From f6ab6622aab00fe7c2f4c3dc41f786ebbe0f0d73 Mon Sep 17 00:00:00 2001 From: Rogan Creswick Date: Fri, 30 Mar 2012 17:07:02 -0700 Subject: initial revision --- tools/.gitignore | 1 + tools/addon-sdk | 1 + tools/addon-sdk-1.3/.version | 1 + tools/addon-sdk-1.3/README.txt | 30 + tools/addon-sdk-1.3/bin/activate | 82 + tools/addon-sdk-1.3/bin/activate.bat | 139 + tools/addon-sdk-1.3/bin/activate.ps1 | 95 + tools/addon-sdk-1.3/bin/cfx | 29 + tools/addon-sdk-1.3/bin/cfx.bat | 3 + tools/addon-sdk-1.3/bin/deactivate.bat | 20 + .../integration-scripts/buildbot-run-cfx-helper | 10 + .../bin/integration-scripts/integration-check | 360 ++ tools/addon-sdk-1.3/examples/annotator/README.md | 42 + .../annotator/data/annotation/annotation.html | 27 + .../annotator/data/annotation/annotation.js | 7 + .../annotator/data/editor/annotation-editor.html | 35 + .../annotator/data/editor/annotation-editor.js | 19 + .../examples/annotator/data/jquery-1.4.2.min.js | 154 + .../annotator/data/list/annotation-list.css | 36 + .../annotator/data/list/annotation-list.html | 22 + .../annotator/data/list/annotation-list.js | 27 + .../examples/annotator/data/matcher.js | 46 + .../examples/annotator/data/selector.js | 56 + .../examples/annotator/data/widget/pencil-off.png | Bin 0 -> 1740 bytes .../examples/annotator/data/widget/pencil-on.png | Bin 0 -> 2054 bytes .../examples/annotator/data/widget/widget.js | 13 + tools/addon-sdk-1.3/examples/annotator/lib/main.js | 290 ++ .../addon-sdk-1.3/examples/annotator/package.json | 10 + .../examples/annotator/tests/test-main.js | 3 + .../examples/library-detector/README.md | 9 + .../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 .../examples/library-detector/data/icons/yui.ico | Bin 0 -> 6598 bytes .../library-detector/data/library-detector.js | 97 + .../examples/library-detector/data/widget.js | 10 + .../examples/library-detector/lib/main.js | 103 + .../examples/library-detector/package.json | 9 + .../examples/library-detector/test/test-main.js | 3 + .../examples/reading-data/data/mom.png | Bin 0 -> 4778 bytes .../examples/reading-data/data/sample.html | 3 + .../examples/reading-data/lib/main.js | 40 + .../examples/reading-data/package.json | 10 + .../examples/reading-data/tests/test-main.js | 20 + .../addon-sdk-1.3/examples/reddit-panel/README.md | 10 + .../examples/reddit-panel/data/jquery-1.4.4.min.js | 167 + .../examples/reddit-panel/data/panel.js | 31 + .../examples/reddit-panel/lib/main.js | 27 + .../examples/reddit-panel/package.json | 10 + .../examples/reddit-panel/tests/test-main.js | 17 + tools/addon-sdk-1.3/packages/addon-kit/README.md | 8 + .../packages/addon-kit/data/moz_favicon.ico | Bin 0 -> 1406 bytes .../packages/addon-kit/data/test-page-mod.html | 8 + .../packages/addon-kit/data/test-page-worker.html | 8 + .../packages/addon-kit/data/test-page-worker.js | 25 + .../packages/addon-kit/data/test.html | 8 + .../packages/addon-kit/docs/clipboard.md | 58 + .../packages/addon-kit/docs/context-menu.md | 702 +++ .../packages/addon-kit/docs/hotkeys.md | 74 + .../packages/addon-kit/docs/notifications.md | 60 + .../packages/addon-kit/docs/page-mod.md | 366 ++ .../packages/addon-kit/docs/page-worker.md | 218 + .../addon-sdk-1.3/packages/addon-kit/docs/panel.md | 301 ++ .../packages/addon-kit/docs/passwords.md | 564 +++ .../packages/addon-kit/docs/private-browsing.md | 46 + .../packages/addon-kit/docs/request.md | 192 + .../packages/addon-kit/docs/selection.md | 86 + .../addon-sdk-1.3/packages/addon-kit/docs/self.md | 73 + .../packages/addon-kit/docs/simple-storage.md | 125 + .../addon-sdk-1.3/packages/addon-kit/docs/tabs.md | 381 ++ .../packages/addon-kit/docs/timers.md | 48 + .../packages/addon-kit/docs/widget.md | 693 +++ .../packages/addon-kit/docs/windows.md | 187 + .../packages/addon-kit/lib/clipboard.js | 266 + .../packages/addon-kit/lib/context-menu.js | 1527 ++++++ .../packages/addon-kit/lib/hotkeys.js | 71 + .../packages/addon-kit/lib/notifications.js | 112 + .../packages/addon-kit/lib/page-mod.js | 229 + .../packages/addon-kit/lib/page-worker.js | 101 + .../addon-sdk-1.3/packages/addon-kit/lib/panel.js | 404 ++ .../packages/addon-kit/lib/passwords.js | 92 + .../packages/addon-kit/lib/private-browsing.js | 102 + .../packages/addon-kit/lib/request.js | 309 ++ .../packages/addon-kit/lib/selection.js | 448 ++ .../packages/addon-kit/lib/simple-storage.js | 263 + tools/addon-sdk-1.3/packages/addon-kit/lib/tabs.js | 62 + .../addon-sdk-1.3/packages/addon-kit/lib/timers.js | 40 + .../addon-sdk-1.3/packages/addon-kit/lib/widget.js | 938 ++++ .../packages/addon-kit/lib/windows.js | 238 + .../addon-sdk-1.3/packages/addon-kit/package.json | 13 + .../addon-kit/tests/pagemod-test-helpers.js | 62 + .../packages/addon-kit/tests/test-clipboard.js | 60 + .../addon-kit/tests/test-context-menu.html | 78 + .../packages/addon-kit/tests/test-context-menu.js | 2074 ++++++++ .../packages/addon-kit/tests/test-hotkeys.js | 155 + .../packages/addon-kit/tests/test-module.js | 33 + .../packages/addon-kit/tests/test-notifications.js | 76 + .../packages/addon-kit/tests/test-page-mod.js | 380 ++ .../packages/addon-kit/tests/test-page-worker.js | 361 ++ .../packages/addon-kit/tests/test-panel.js | 436 ++ .../packages/addon-kit/tests/test-passwords.js | 277 ++ .../addon-kit/tests/test-private-browsing.js | 237 + .../packages/addon-kit/tests/test-request.js | 394 ++ .../packages/addon-kit/tests/test-selection.js | 490 ++ .../addon-kit/tests/test-simple-storage.js | 346 ++ .../packages/addon-kit/tests/test-tabs.js | 904 ++++ .../packages/addon-kit/tests/test-timers.js | 44 + .../packages/addon-kit/tests/test-widget.js | 909 ++++ .../packages/addon-kit/tests/test-windows.js | 354 ++ tools/addon-sdk-1.3/packages/api-utils/README.md | 31 + .../api-utils/data/bootstrap-remote-process.js | 212 + .../api-utils/data/test-content-symbiont.js | 0 .../packages/api-utils/docs/api-utils.md | 153 + .../packages/api-utils/docs/app-strings.md | 61 + .../packages/api-utils/docs/byte-streams.md | 64 + .../packages/api-utils/docs/collection.md | 73 + .../packages/api-utils/docs/content.md | 11 + .../packages/api-utils/docs/content/loader.md | 88 + .../packages/api-utils/docs/content/proxy.md | 237 + .../packages/api-utils/docs/content/symbiont.md | 136 + .../packages/api-utils/docs/content/worker.md | 126 + .../packages/api-utils/docs/cortex.md | 156 + .../packages/api-utils/docs/cuddlefish.md | 5 + .../addon-sdk-1.3/packages/api-utils/docs/e10s.md | 218 + .../packages/api-utils/docs/errors.md | 38 + tools/addon-sdk-1.3/packages/api-utils/docs/es5.md | 52 + .../packages/api-utils/docs/events.md | 74 + .../addon-sdk-1.3/packages/api-utils/docs/file.md | 147 + .../packages/api-utils/docs/hidden-frame.md | 79 + .../packages/api-utils/docs/light-traits.md | 291 ++ .../addon-sdk-1.3/packages/api-utils/docs/list.md | 94 + .../packages/api-utils/docs/match-pattern.md | 242 + .../packages/api-utils/docs/memory.md | 3 + .../packages/api-utils/docs/observer-service.md | 69 + .../packages/api-utils/docs/plain-text-console.md | 3 + .../packages/api-utils/docs/preferences-service.md | 51 + .../packages/api-utils/docs/runtime.md | 71 + .../packages/api-utils/docs/securable-module.md | 93 + .../packages/api-utils/docs/tab-browser.md | 136 + .../packages/api-utils/docs/text-streams.md | 98 + .../packages/api-utils/docs/traceback.md | 62 + .../packages/api-utils/docs/traits.md | 240 + .../packages/api-utils/docs/unit-test.md | 389 ++ .../packages/api-utils/docs/unload.md | 57 + tools/addon-sdk-1.3/packages/api-utils/docs/url.md | 81 + .../packages/api-utils/docs/window-utils.md | 84 + tools/addon-sdk-1.3/packages/api-utils/docs/xhr.md | 91 + .../addon-sdk-1.3/packages/api-utils/docs/xpcom.md | 223 + .../packages/api-utils/docs/xul-app.md | 72 + .../packages/api-utils/lib/api-utils.js | 186 + .../packages/api-utils/lib/app-strings.js | 95 + .../addon-sdk-1.3/packages/api-utils/lib/array.js | 102 + .../packages/api-utils/lib/byte-streams.js | 135 + .../packages/api-utils/lib/collection.js | 141 + .../packages/api-utils/lib/content.js | 44 + .../api-utils/lib/content/content-proxy.js | 798 +++ .../packages/api-utils/lib/content/loader.js | 203 + .../packages/api-utils/lib/content/symbiont.js | 201 + .../packages/api-utils/lib/content/worker.js | 597 +++ .../addon-sdk-1.3/packages/api-utils/lib/cortex.js | 139 + .../packages/api-utils/lib/cuddlefish.js | 182 + .../packages/api-utils/lib/dom/events.js | 169 + .../packages/api-utils/lib/dom/events/keys.js | 93 + tools/addon-sdk-1.3/packages/api-utils/lib/e10s.js | 245 + .../addon-sdk-1.3/packages/api-utils/lib/errors.js | 92 + .../addon-sdk-1.3/packages/api-utils/lib/events.js | 202 + .../packages/api-utils/lib/events/assembler.js | 86 + tools/addon-sdk-1.3/packages/api-utils/lib/file.js | 227 + .../api-utils/lib/find-tests-e10s-adapter.js | 112 + .../packages/api-utils/lib/find-tests.js | 1 + .../packages/api-utils/lib/hidden-frame.js | 200 + .../packages/api-utils/lib/keyboard/hotkeys.js | 141 + .../packages/api-utils/lib/keyboard/observer.js | 86 + .../packages/api-utils/lib/keyboard/utils.js | 216 + .../packages/api-utils/lib/light-traits.js | 626 +++ tools/addon-sdk-1.3/packages/api-utils/lib/list.js | 147 + .../packages/api-utils/lib/match-pattern.js | 137 + .../addon-sdk-1.3/packages/api-utils/lib/memory.js | 146 + .../packages/api-utils/lib/observer-service.js | 211 + .../packages/api-utils/lib/passwords/utils.js | 134 + .../packages/api-utils/lib/plain-text-console.js | 114 + .../packages/api-utils/lib/preferences-service.js | 138 + .../packages/api-utils/lib/runtime.js | 48 + .../packages/api-utils/lib/securable-module.js | 782 +++ .../packages/api-utils/lib/self-e10s-adapter.js | 100 + .../packages/api-utils/lib/self-maker.js | 81 + .../addon-sdk-1.3/packages/api-utils/lib/shims.js | 53 + .../packages/api-utils/lib/tab-browser.js | 761 +++ .../packages/api-utils/lib/tabs/events.js | 56 + .../packages/api-utils/lib/tabs/observer.js | 126 + .../packages/api-utils/lib/tabs/tab.js | 297 ++ .../packages/api-utils/lib/tabs/utils.js | 87 + tools/addon-sdk-1.3/packages/api-utils/lib/test.js | 126 + .../packages/api-utils/lib/test/assert.js | 360 ++ .../packages/api-utils/lib/text-streams.js | 273 + .../packages/api-utils/lib/timer-e10s-adapter.js | 75 + .../addon-sdk-1.3/packages/api-utils/lib/timer.js | 141 + .../packages/api-utils/lib/traceback.js | 155 + .../addon-sdk-1.3/packages/api-utils/lib/traits.js | 215 + .../packages/api-utils/lib/traits/core.js | 339 ++ tools/addon-sdk-1.3/packages/api-utils/lib/type.js | 372 ++ .../packages/api-utils/lib/unit-test-finder.js | 117 + .../packages/api-utils/lib/unit-test.js | 464 ++ .../addon-sdk-1.3/packages/api-utils/lib/unload.js | 59 + .../packages/api-utils/lib/url-e10s-adapter.js | 93 + tools/addon-sdk-1.3/packages/api-utils/lib/url.js | 123 + .../packages/api-utils/lib/utils/data.js | 104 + .../packages/api-utils/lib/utils/function.js | 64 + .../packages/api-utils/lib/utils/registry.js | 90 + .../packages/api-utils/lib/utils/thumbnail.js | 76 + .../packages/api-utils/lib/window-utils.js | 215 + .../packages/api-utils/lib/windows/dom.js | 60 + .../packages/api-utils/lib/windows/loader.js | 151 + .../packages/api-utils/lib/windows/observer.js | 86 + .../packages/api-utils/lib/windows/tabs.js | 207 + tools/addon-sdk-1.3/packages/api-utils/lib/xhr.js | 181 + .../addon-sdk-1.3/packages/api-utils/lib/xpcom.js | 152 + .../packages/api-utils/lib/xul-app.js | 93 + .../addon-sdk-1.3/packages/api-utils/package.json | 15 + .../tests/commonjs-test-adapter/asserts.js | 50 + .../tests/e10s-samples/adapter-only-client.js | 4 + .../e10s-samples/adapter-only-e10s-adapter.js | 11 + .../tests/e10s-samples/bug-617499-e10s-adapter.js | 17 + .../tests/e10s-samples/bug-617499-main.js | 4 + .../api-utils/tests/e10s-samples/bug-617499.js | 1 + .../e10s-samples/chrome-only-module-client.js | 3 + .../tests/e10s-samples/chrome-only-module.js | 5 + .../api-utils/tests/e10s-samples/hello-world.js | 11 + .../tests/e10s-samples/superpower-client.js | 7 + .../tests/e10s-samples/superpower-e10s-adapter.js | 13 + .../api-utils/tests/e10s-samples/superpower.js | 13 + .../api-utils/tests/e10s-samples/syntax-error.js | 1 + .../tests/e10s-samples/thrown-exception.js | 3 + .../packages/api-utils/tests/fixtures/es5.js | 4 + .../tests/interoperablejs-read-only/README.txt | 4 + .../interoperablejs-read-only/compliance/ORACLE | 54 + .../compliance/absolute/b.js | 1 + .../compliance/absolute/program.js | 5 + .../compliance/absolute/submodule/a.js | 3 + .../compliance/absolute/test.js | 13 + .../compliance/cyclic/a.js | 4 + .../compliance/cyclic/b.js | 4 + .../compliance/cyclic/program.js | 10 + .../compliance/cyclic/test.js | 13 + .../compliance/determinism/program.js | 3 + .../compliance/determinism/submodule/a.js | 8 + .../compliance/determinism/submodule/b.js | 2 + .../compliance/determinism/test.js | 13 + .../compliance/exactExports/a.js | 3 + .../compliance/exactExports/b.js | 3 + .../compliance/exactExports/program.js | 3 + .../compliance/exactExports/test.js | 13 + .../compliance/hasOwnProperty/hasOwnProperty.js | 0 .../compliance/hasOwnProperty/program.js | 4 + .../compliance/hasOwnProperty/test.js | 13 + .../compliance/hasOwnProperty/toString.js | 0 .../compliance/method/a.js | 12 + .../compliance/method/program.js | 8 + .../compliance/method/test.js | 13 + .../compliance/missing/program.js | 9 + .../compliance/missing/test.js | 13 + .../compliance/monkeys/a.js | 1 + .../compliance/monkeys/b.js | 0 .../compliance/monkeys/program.js | 5 + .../compliance/monkeys/test.js | 13 + .../compliance/nested/a/b/c/d.js | 3 + .../compliance/nested/program.js | 3 + .../compliance/nested/test.js | 13 + .../compliance/reflexive/a.js | 2 + .../compliance/reflexive/program.js | 3 + .../compliance/reflexive/test.js | 13 + .../compliance/relative/program.js | 5 + .../compliance/relative/submodule/a.js | 1 + .../compliance/relative/submodule/b.js | 2 + .../compliance/relative/test.js | 13 + .../compliance/transitive/a.js | 1 + .../compliance/transitive/b.js | 1 + .../compliance/transitive/c.js | 3 + .../compliance/transitive/program.js | 3 + .../compliance/transitive/test.js | 13 + .../packages/api-utils/tests/modules/add.js | 5 + .../packages/api-utils/tests/modules/async1.js | 10 + .../packages/api-utils/tests/modules/async2.js | 4 + .../api-utils/tests/modules/badExportAndReturn.js | 6 + .../packages/api-utils/tests/modules/badFirst.js | 5 + .../packages/api-utils/tests/modules/badSecond.js | 4 + .../packages/api-utils/tests/modules/blue.js | 5 + .../packages/api-utils/tests/modules/castor.js | 6 + .../packages/api-utils/tests/modules/cheetah.js | 5 + .../packages/api-utils/tests/modules/color.js | 3 + .../packages/api-utils/tests/modules/dupe.js | 11 + .../packages/api-utils/tests/modules/dupeNested.js | 11 + .../api-utils/tests/modules/dupeSetExports.js | 4 + .../api-utils/tests/modules/exportsEquals.js | 1 + .../packages/api-utils/tests/modules/green.js | 6 + .../packages/api-utils/tests/modules/lion.js | 3 + .../packages/api-utils/tests/modules/orange.js | 6 + .../packages/api-utils/tests/modules/pollux.js | 6 + .../packages/api-utils/tests/modules/red.js | 12 + .../packages/api-utils/tests/modules/setExports.js | 1 + .../packages/api-utils/tests/modules/subtract.js | 5 + .../packages/api-utils/tests/modules/tiger.js | 4 + .../api-utils/tests/modules/traditional1.js | 8 + .../api-utils/tests/modules/traditional2.js | 2 + .../packages/api-utils/tests/modules/types/cat.js | 1 + .../packages/api-utils/tests/test-api-utils.js | 274 ++ .../packages/api-utils/tests/test-app-strings.js | 58 + .../packages/api-utils/tests/test-array.js | 36 + .../packages/api-utils/tests/test-byte-streams.js | 202 + .../packages/api-utils/tests/test-collection.js | 160 + .../api-utils/tests/test-commonjs-test-adapter.js | 7 + .../api-utils/tests/test-content-loader.js | 223 + .../packages/api-utils/tests/test-content-proxy.js | 406 ++ .../api-utils/tests/test-content-symbiont.js | 153 + .../api-utils/tests/test-content-worker.js | 347 ++ .../packages/api-utils/tests/test-cortex.js | 118 + .../packages/api-utils/tests/test-cuddlefish.js | 40 + .../packages/api-utils/tests/test-dom.js | 84 + .../packages/api-utils/tests/test-e10s-porting.js | 73 + .../packages/api-utils/tests/test-e10s.js | 268 + .../packages/api-utils/tests/test-errors.js | 66 + .../packages/api-utils/tests/test-events.js | 182 + .../packages/api-utils/tests/test-file.js | 276 ++ .../api-utils/tests/test-function-utils.js | 23 + .../packages/api-utils/tests/test-globals.js | 12 + .../packages/api-utils/tests/test-hidden-frame.js | 47 + .../api-utils/tests/test-keyboard-observer.js | 31 + .../api-utils/tests/test-keyboard-utils.js | 58 + .../packages/api-utils/tests/test-light-traits.js | 7 + .../packages/api-utils/tests/test-list.js | 203 + .../packages/api-utils/tests/test-match-pattern.js | 161 + .../packages/api-utils/tests/test-memory.js | 15 + .../packages/api-utils/tests/test-modules.js | 131 + .../api-utils/tests/test-observer-service.js | 73 + .../api-utils/tests/test-passwords-utils.js | 138 + .../api-utils/tests/test-plain-text-console.js | 64 + .../api-utils/tests/test-preferences-service.js | 87 + .../packages/api-utils/tests/test-registry.js | 76 + .../packages/api-utils/tests/test-require.js | 25 + .../api-utils/tests/test-securable-module.js | 382 ++ .../packages/api-utils/tests/test-self.js | 34 + .../packages/api-utils/tests/test-set-exports.js | 28 + .../packages/api-utils/tests/test-tab-browser.js | 525 ++ .../packages/api-utils/tests/test-tab-observer.js | 35 + .../packages/api-utils/tests/test-tab.js | 106 + .../packages/api-utils/tests/test-text-streams.js | 189 + .../packages/api-utils/tests/test-timer.js | 126 + .../packages/api-utils/tests/test-traceback.js | 114 + .../packages/api-utils/tests/test-traits-core.js | 834 ++++ .../packages/api-utils/tests/test-traits.js | 394 ++ .../packages/api-utils/tests/test-type.js | 88 + .../packages/api-utils/tests/test-unit-test.js | 218 + .../packages/api-utils/tests/test-unload.js | 195 + .../packages/api-utils/tests/test-url.js | 157 + .../packages/api-utils/tests/test-window-loader.js | 152 + .../api-utils/tests/test-window-observer.js | 42 + .../packages/api-utils/tests/test-window-utils.js | 276 ++ .../packages/api-utils/tests/test-xhr.js | 67 + .../packages/api-utils/tests/test-xpcom.js | 106 + .../packages/api-utils/tests/test-xul-app.js | 41 + .../packages/api-utils/tests/traits/assert.js | 94 + .../api-utils/tests/traits/descriptor-tests.js | 331 ++ .../api-utils/tests/traits/inheritance-tests.js | 100 + .../api-utils/tests/traits/object-tests.js | 317 ++ .../packages/api-utils/tests/traits/utils.js | 52 + .../packages/development-mode/README.md | 8 + .../packages/development-mode/docs/bootstrap.md | 6 + .../packages/development-mode/docs/main.md | 5 + .../packages/development-mode/lib/bootstrap.js | 153 + .../packages/development-mode/lib/main.js | 95 + .../packages/development-mode/package.json | 11 + .../addon-sdk-1.3/packages/test-harness/README.md | 8 + .../packages/test-harness/docs/harness.md | 2 + .../packages/test-harness/docs/run-tests.md | 9 + .../packages/test-harness/lib/harness.js | 365 ++ .../packages/test-harness/lib/run-tests.js | 121 + .../packages/test-harness/package.json | 9 + .../packages/test-harness/tests/test-packaging.js | 30 + tools/addon-sdk-1.3/python-lib/.gitignore | 2 + .../python-lib/cuddlefish/__init__.py | 766 +++ .../cuddlefish/app-extension/application.ini | 11 + .../cuddlefish/app-extension/bootstrap.js | 154 + .../cuddlefish/app-extension/components/harness.js | 660 +++ .../cuddlefish/app-extension/install.rdf | 31 + tools/addon-sdk-1.3/python-lib/cuddlefish/bunch.py | 30 + .../python-lib/cuddlefish/docs/__init__.py | 0 .../python-lib/cuddlefish/docs/apiparser.py | 388 ++ .../python-lib/cuddlefish/docs/apirenderer.py | 299 ++ .../python-lib/cuddlefish/docs/generate.py | 230 + .../python-lib/cuddlefish/docs/renderapi.readme.md | 206 + .../python-lib/cuddlefish/docs/webdocs.py | 195 + .../python-lib/cuddlefish/manifest.py | 725 +++ .../cuddlefish/mobile-killer/bootstrap.js | 71 + .../cuddlefish/mobile-killer/install.rdf | 26 + .../python-lib/cuddlefish/packaging.py | 395 ++ .../python-lib/cuddlefish/preflight.py | 73 + tools/addon-sdk-1.3/python-lib/cuddlefish/prefs.py | 111 + tools/addon-sdk-1.3/python-lib/cuddlefish/rdf.py | 172 + .../addon-sdk-1.3/python-lib/cuddlefish/runner.py | 627 +++ .../python-lib/cuddlefish/templates.py | 79 + .../python-lib/cuddlefish/tests/__init__.py | 61 + .../packages/explicit-icon/explicit-icon.png | 0 .../packages/explicit-icon/explicit-icon64.png | 0 .../packages/explicit-icon/lib/main.js | 0 .../packages/explicit-icon/package.json | 5 + .../packages/implicit-icon/icon.png | 0 .../packages/implicit-icon/icon64.png | 0 .../packages/implicit-icon/lib/main.js | 0 .../packages/implicit-icon/package.json | 3 + .../bug-588119-files/packages/no-icon/lib/main.js | 0 .../bug-588119-files/packages/no-icon/package.json | 3 + .../packages/bar/lib/bar-loader.js | 0 .../bug-588661-files/packages/bar/package.json | 3 + .../packages/foo/lib/foo-loader.js | 0 .../bug-588661-files/packages/foo/package.json | 4 + .../tests/bug-611495-files/jspath-one/docs/main.md | 1 + .../tests/bug-611495-files/jspath-one/lib/main.js | 4 + .../tests/bug-611495-files/jspath-one/package.json | 5 + .../packages/commonjs-naming/doc/foo.md | 1 + .../packages/commonjs-naming/lib/foo-loader.js | 1 + .../packages/commonjs-naming/package.json | 3 + .../packages/commonjs-naming/test/test-foo.js | 3 + .../packages/original-naming/docs/foo.md | 1 + .../packages/original-naming/lib/foo-loader.js | 1 + .../packages/original-naming/package.json | 3 + .../packages/original-naming/tests/test-foo.js | 3 + .../packages/default-lib/doc/foo.md | 1 + .../packages/default-lib/lib/foo.js | 1 + .../packages/default-lib/lib/loader.js | 1 + .../packages/default-lib/package.json | 3 + .../packages/default-lib/test/test-foo.js | 3 + .../packages/default-root/doc/foo.md | 1 + .../bug-652227-files/packages/default-root/foo.js | 1 + .../packages/default-root/loader.js | 1 + .../packages/default-root/package.json | 3 + .../packages/default-root/test/test-foo.js | 3 + .../packages/explicit-dir-lib/alt-lib/foo.js | 1 + .../packages/explicit-dir-lib/alt-lib/loader.js | 1 + .../packages/explicit-dir-lib/doc/foo.md | 1 + .../packages/explicit-dir-lib/package.json | 4 + .../packages/explicit-dir-lib/test/test-foo.js | 3 + .../packages/explicit-lib/alt2-lib/foo.js | 1 + .../packages/explicit-lib/alt2-lib/loader.js | 1 + .../packages/explicit-lib/doc/foo.md | 1 + .../packages/explicit-lib/package.json | 4 + .../packages/explicit-lib/test/test-foo.js | 3 + .../packages/extra-options/docs/main.md | 1 + .../packages/extra-options/lib/main.js | 4 + .../packages/extra-options/package.json | 6 + .../packages/foo/lib/bar-e10s-adapter.js | 7 + .../e10s-adapter-files/packages/foo/lib/bar.js | 1 + .../e10s-adapter-files/packages/foo/lib/foo.js | 1 + .../e10s-adapter-files/packages/foo/package.json | 1 + .../cuddlefish/tests/linker-files/five/lib/main.js | 1 + .../tests/linker-files/five/package.json | 3 + .../linker-files/four-deps/four-a/lib/misc.js | 1 + .../linker-files/four-deps/four-a/package.json | 4 + .../linker-files/four-deps/four-a/topfiles/main.js | 1 + .../cuddlefish/tests/linker-files/four/lib/main.js | 1 + .../tests/linker-files/four/package.json | 3 + .../cuddlefish/tests/linker-files/one/lib/main.js | 5 + .../tests/linker-files/one/lib/subdir/three.js | 2 + .../cuddlefish/tests/linker-files/one/lib/two.js | 4 + .../cuddlefish/tests/linker-files/one/package.json | 3 + .../tests/linker-files/seven/data/text.data | 1 + .../tests/linker-files/seven/lib/main.js | 2 + .../tests/linker-files/seven/lib/unused.js | 1 + .../tests/linker-files/seven/package.json | 4 + .../tests/linker-files/six/lib/unused.js | 1 + .../cuddlefish/tests/linker-files/six/package.json | 3 + .../tests/linker-files/six/unreachable.js | 1 + .../linker-files/three-deps/three-a/data/msg.txt | 1 + .../three-deps/three-a/data/subdir/submsg.txt | 1 + .../linker-files/three-deps/three-a/lib/main.js | 4 + .../three-deps/three-a/lib/subdir/subfile.js | 1 + .../linker-files/three-deps/three-a/lib/unused.js | 1 + .../linker-files/three-deps/three-a/package.json | 3 + .../linker-files/three-deps/three-b/lib/main.js | 1 + .../linker-files/three-deps/three-b/package.json | 3 + .../linker-files/three-deps/three-c/lib/main.js | 1 + .../linker-files/three-deps/three-c/lib/sub/foo.js | 2 + .../linker-files/three-deps/three-c/package.json | 3 + .../tests/linker-files/three/lib/main.js | 4 + .../tests/linker-files/three/package.json | 3 + .../static-files/doc/dev-guide-source/no_h1.md | 3 + .../static-files/doc/dev-guide-source/welcome.md | 3 + .../static-files/doc/static-files/another.html | 1 + .../tests/static-files/doc/static-files/base.html | 136 + .../tests/static-files/doc/static-files/index.html | 23 + .../tests/static-files/docs/APIreference.html | 465 ++ .../tests/static-files/docs/APIsample.md | 158 + .../packages/aardvark/doc/aardvark-feeder.md | 8 + .../static-files/packages/aardvark/doc/main.md | 0 .../static-files/packages/aardvark/lib/ignore_me | 3 + .../static-files/packages/aardvark/lib/main.js | 4 + .../aardvark/lib/surprise.js/ignore_me_too | 2 + .../static-files/packages/aardvark/package.json | 7 + .../packages/anteater_files/lib/main.js | 4 + .../packages/anteater_files/package.json | 8 + .../static-files/packages/api-utils/lib/loader.js | 3 + .../static-files/packages/api-utils/package.json | 5 + .../packages/barbeque/lib/bar-module.js | 3 + .../static-files/packages/barbeque/package.json | 4 + .../static-files/packages/minimal/docs/main.md | 1 + .../static-files/packages/minimal/lib/main.js | 4 + .../static-files/packages/minimal/package.json | 4 + .../xpi-template/components/harness.js | 4 + .../python-lib/cuddlefish/tests/test_apiparser.py | 524 ++ .../cuddlefish/tests/test_apirenderer.py | 28 + .../python-lib/cuddlefish/tests/test_generate.py | 76 + .../python-lib/cuddlefish/tests/test_init.py | 85 + .../python-lib/cuddlefish/tests/test_linker.py | 200 + .../python-lib/cuddlefish/tests/test_manifest.py | 256 + .../python-lib/cuddlefish/tests/test_packaging.py | 105 + .../python-lib/cuddlefish/tests/test_preflight.py | 143 + .../python-lib/cuddlefish/tests/test_rdf.py | 15 + .../python-lib/cuddlefish/tests/test_runner.py | 24 + .../python-lib/cuddlefish/tests/test_util.py | 18 + .../python-lib/cuddlefish/tests/test_version.py | 32 + .../python-lib/cuddlefish/tests/test_webdocs.py | 92 + .../python-lib/cuddlefish/tests/test_xpi.py | 253 + tools/addon-sdk-1.3/python-lib/cuddlefish/util.py | 19 + .../addon-sdk-1.3/python-lib/cuddlefish/version.py | 6 + .../python-lib/cuddlefish/version_comparator.py | 202 + tools/addon-sdk-1.3/python-lib/cuddlefish/xpi.py | 105 + tools/addon-sdk-1.3/python-lib/jetpack_sdk_env.py | 62 + .../addon-sdk-1.3/python-lib/markdown/__init__.py | 603 +++ .../python-lib/markdown/blockparser.py | 95 + .../python-lib/markdown/blockprocessors.py | 460 ++ .../python-lib/markdown/commandline.py | 96 + .../addon-sdk-1.3/python-lib/markdown/docs/AUTHORS | 43 + .../addon-sdk-1.3/python-lib/markdown/docs/LICENSE | 30 + .../python-lib/markdown/etree_loader.py | 33 + .../python-lib/markdown/extensions/__init__.py | 0 .../python-lib/markdown/extensions/abbr.py | 95 + .../python-lib/markdown/extensions/codehilite.py | 224 + .../python-lib/markdown/extensions/def_list.py | 104 + .../python-lib/markdown/extensions/extra.py | 49 + .../python-lib/markdown/extensions/fenced_code.py | 117 + .../python-lib/markdown/extensions/footnotes.py | 293 ++ .../python-lib/markdown/extensions/headerid.py | 195 + .../python-lib/markdown/extensions/html_tidy.py | 62 + .../python-lib/markdown/extensions/imagelinks.py | 119 + .../python-lib/markdown/extensions/meta.py | 90 + .../python-lib/markdown/extensions/rss.py | 114 + .../python-lib/markdown/extensions/tables.py | 97 + .../python-lib/markdown/extensions/toc.py | 140 + .../python-lib/markdown/extensions/wikilinks.py | 155 + tools/addon-sdk-1.3/python-lib/markdown/html4.py | 274 ++ .../python-lib/markdown/inlinepatterns.py | 371 ++ tools/addon-sdk-1.3/python-lib/markdown/odict.py | 162 + .../python-lib/markdown/postprocessors.py | 77 + .../python-lib/markdown/preprocessors.py | 214 + .../python-lib/markdown/treeprocessors.py | 329 ++ .../addon-sdk-1.3/python-lib/mozrunner/__init__.py | 589 +++ .../python-lib/mozrunner/killableprocess.py | 316 ++ tools/addon-sdk-1.3/python-lib/mozrunner/qijo.py | 162 + .../python-lib/mozrunner/winprocess.py | 383 ++ tools/addon-sdk-1.3/python-lib/mozrunner/wpk.py | 76 + .../python-lib/simplejson/__init__.py | 376 ++ .../addon-sdk-1.3/python-lib/simplejson/decoder.py | 343 ++ .../addon-sdk-1.3/python-lib/simplejson/encoder.py | 385 ++ .../addon-sdk-1.3/python-lib/simplejson/scanner.py | 67 + tools/addon-sdk-1.3/python-lib/simplejson/tool.py | 44 + .../media/screenshots/widget-panel-clock.png | Bin 0 -> 60823 bytes tools/addon-sdk-1.4/README.txt | 30 + tools/addon-sdk-1.4/bin/activate | 82 + tools/addon-sdk-1.4/bin/activate.bat | 131 + tools/addon-sdk-1.4/bin/activate.ps1 | 95 + tools/addon-sdk-1.4/bin/cfx | 29 + tools/addon-sdk-1.4/bin/cfx.bat | 3 + tools/addon-sdk-1.4/bin/deactivate.bat | 20 + .../integration-scripts/buildbot-run-cfx-helper | 10 + .../bin/integration-scripts/integration-check | 360 ++ tools/addon-sdk-1.4/examples/annotator/README.md | 42 + .../annotator/data/annotation/annotation.html | 27 + .../annotator/data/annotation/annotation.js | 7 + .../annotator/data/editor/annotation-editor.html | 35 + .../annotator/data/editor/annotation-editor.js | 19 + .../examples/annotator/data/jquery-1.4.2.min.js | 154 + .../annotator/data/list/annotation-list.css | 36 + .../annotator/data/list/annotation-list.html | 22 + .../annotator/data/list/annotation-list.js | 27 + .../examples/annotator/data/matcher.js | 46 + .../examples/annotator/data/selector.js | 56 + .../examples/annotator/data/widget/pencil-off.png | Bin 0 -> 1740 bytes .../examples/annotator/data/widget/pencil-on.png | Bin 0 -> 2054 bytes .../examples/annotator/data/widget/widget.js | 13 + tools/addon-sdk-1.4/examples/annotator/lib/main.js | 290 ++ .../addon-sdk-1.4/examples/annotator/package.json | 9 + .../examples/annotator/tests/test-main.js | 3 + .../examples/library-detector/README.md | 9 + .../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 .../examples/library-detector/data/icons/yui.ico | Bin 0 -> 6598 bytes .../library-detector/data/library-detector.js | 97 + .../examples/library-detector/data/widget.js | 10 + .../examples/library-detector/lib/main.js | 103 + .../examples/library-detector/package.json | 9 + .../examples/library-detector/test/test-main.js | 3 + .../examples/reading-data/data/mom.png | Bin 0 -> 4778 bytes .../examples/reading-data/data/sample.html | 3 + .../examples/reading-data/lib/main.js | 40 + .../examples/reading-data/package.json | 9 + .../examples/reading-data/tests/test-main.js | 20 + .../addon-sdk-1.4/examples/reddit-panel/README.md | 10 + .../examples/reddit-panel/data/jquery-1.4.4.min.js | 167 + .../examples/reddit-panel/data/panel.js | 31 + .../examples/reddit-panel/lib/main.js | 27 + .../examples/reddit-panel/package.json | 9 + .../examples/reddit-panel/tests/test-main.js | 17 + tools/addon-sdk-1.4/packages/addon-kit/README.md | 8 + .../packages/addon-kit/data/moz_favicon.ico | Bin 0 -> 1406 bytes .../packages/addon-kit/data/test-page-mod.html | 8 + .../packages/addon-kit/data/test-page-worker.html | 8 + .../packages/addon-kit/data/test-page-worker.js | 25 + .../addon-kit/data/test-request-invalid.json | 1 + .../packages/addon-kit/data/test-request.json | 1 + .../packages/addon-kit/data/test-request.txt | 1 + .../packages/addon-kit/data/test.html | 8 + .../packages/addon-kit/docs/clipboard.md | 58 + .../packages/addon-kit/docs/context-menu.md | 702 +++ .../packages/addon-kit/docs/hotkeys.md | 74 + .../packages/addon-kit/docs/notifications.md | 60 + .../packages/addon-kit/docs/page-mod.md | 367 ++ .../packages/addon-kit/docs/page-worker.md | 218 + .../addon-sdk-1.4/packages/addon-kit/docs/panel.md | 301 ++ .../packages/addon-kit/docs/passwords.md | 564 +++ .../packages/addon-kit/docs/private-browsing.md | 46 + .../packages/addon-kit/docs/request.md | 192 + .../packages/addon-kit/docs/selection.md | 86 + .../addon-sdk-1.4/packages/addon-kit/docs/self.md | 73 + .../packages/addon-kit/docs/simple-prefs.md | 70 + .../packages/addon-kit/docs/simple-storage.md | 125 + .../addon-sdk-1.4/packages/addon-kit/docs/tabs.md | 381 ++ .../packages/addon-kit/docs/timers.md | 48 + .../packages/addon-kit/docs/widget.md | 692 +++ .../packages/addon-kit/docs/windows.md | 187 + .../packages/addon-kit/lib/clipboard.js | 266 + .../packages/addon-kit/lib/context-menu.js | 1526 ++++++ .../packages/addon-kit/lib/hotkeys.js | 71 + .../packages/addon-kit/lib/notifications.js | 112 + .../packages/addon-kit/lib/page-mod.js | 229 + .../packages/addon-kit/lib/page-worker.js | 101 + .../addon-sdk-1.4/packages/addon-kit/lib/panel.js | 403 ++ .../packages/addon-kit/lib/passwords.js | 92 + .../packages/addon-kit/lib/private-browsing.js | 102 + .../packages/addon-kit/lib/request.js | 309 ++ .../packages/addon-kit/lib/selection.js | 448 ++ .../packages/addon-kit/lib/simple-prefs.js | 107 + .../packages/addon-kit/lib/simple-storage.js | 263 + tools/addon-sdk-1.4/packages/addon-kit/lib/tabs.js | 62 + .../addon-sdk-1.4/packages/addon-kit/lib/timers.js | 40 + .../addon-sdk-1.4/packages/addon-kit/lib/widget.js | 947 ++++ .../packages/addon-kit/lib/windows.js | 238 + .../addon-sdk-1.4/packages/addon-kit/package.json | 12 + .../packages/addon-kit/tests/helpers.js | 19 + .../addon-kit/tests/pagemod-test-helpers.js | 63 + .../packages/addon-kit/tests/test-clipboard.js | 60 + .../addon-kit/tests/test-context-menu.html | 78 + .../packages/addon-kit/tests/test-context-menu.js | 2075 ++++++++ .../packages/addon-kit/tests/test-hotkeys.js | 156 + .../packages/addon-kit/tests/test-module.js | 33 + .../packages/addon-kit/tests/test-notifications.js | 79 + .../packages/addon-kit/tests/test-page-mod.js | 416 ++ .../packages/addon-kit/tests/test-page-worker.js | 362 ++ .../packages/addon-kit/tests/test-panel.js | 437 ++ .../packages/addon-kit/tests/test-passwords.js | 277 ++ .../addon-kit/tests/test-private-browsing.js | 238 + .../packages/addon-kit/tests/test-request.js | 358 ++ .../packages/addon-kit/tests/test-selection.js | 490 ++ .../packages/addon-kit/tests/test-simple-prefs.js | 180 + .../addon-kit/tests/test-simple-storage.js | 346 ++ .../packages/addon-kit/tests/test-tabs.js | 905 ++++ .../packages/addon-kit/tests/test-timers.js | 44 + .../packages/addon-kit/tests/test-widget.js | 950 ++++ .../packages/addon-kit/tests/test-windows.js | 342 ++ tools/addon-sdk-1.4/packages/api-utils/README.md | 31 + .../packages/api-utils/data/content-proxy.js | 834 ++++ .../api-utils/data/test-content-symbiont.js | 1 + .../packages/api-utils/data/test-httpd.txt | 1 + .../api-utils/data/test-trusted-document.html | 13 + .../packages/api-utils/docs/api-utils.md | 153 + .../packages/api-utils/docs/app-strings.md | 61 + .../packages/api-utils/docs/byte-streams.md | 64 + .../packages/api-utils/docs/collection.md | 73 + .../packages/api-utils/docs/content.md | 11 + .../packages/api-utils/docs/content/loader.md | 88 + .../packages/api-utils/docs/content/proxy.md | 237 + .../packages/api-utils/docs/content/symbiont.md | 136 + .../packages/api-utils/docs/content/worker.md | 126 + .../packages/api-utils/docs/cortex.md | 156 + .../packages/api-utils/docs/cuddlefish.md | 5 + .../packages/api-utils/docs/environment.md | 39 + .../packages/api-utils/docs/errors.md | 38 + tools/addon-sdk-1.4/packages/api-utils/docs/es5.md | 52 + .../packages/api-utils/docs/events.md | 74 + .../addon-sdk-1.4/packages/api-utils/docs/file.md | 147 + .../packages/api-utils/docs/hidden-frame.md | 79 + .../addon-sdk-1.4/packages/api-utils/docs/httpd.md | 27 + .../packages/api-utils/docs/light-traits.md | 291 ++ .../addon-sdk-1.4/packages/api-utils/docs/list.md | 94 + .../packages/api-utils/docs/match-pattern.md | 242 + .../packages/api-utils/docs/memory.md | 3 + .../packages/api-utils/docs/namespace.md | 66 + .../packages/api-utils/docs/observer-service.md | 69 + .../packages/api-utils/docs/plain-text-console.md | 3 + .../packages/api-utils/docs/preferences-service.md | 80 + .../packages/api-utils/docs/runtime.md | 71 + .../packages/api-utils/docs/securable-module.md | 93 + .../packages/api-utils/docs/tab-browser.md | 136 + .../packages/api-utils/docs/text-streams.md | 98 + .../packages/api-utils/docs/traceback.md | 62 + .../packages/api-utils/docs/traits.md | 240 + .../packages/api-utils/docs/unit-test.md | 389 ++ .../packages/api-utils/docs/unload.md | 57 + tools/addon-sdk-1.4/packages/api-utils/docs/url.md | 81 + .../packages/api-utils/docs/window-utils.md | 84 + tools/addon-sdk-1.4/packages/api-utils/docs/xhr.md | 91 + .../addon-sdk-1.4/packages/api-utils/docs/xpcom.md | 223 + .../packages/api-utils/docs/xul-app.md | 72 + .../packages/api-utils/lib/api-utils.js | 186 + .../packages/api-utils/lib/app-strings.js | 95 + .../addon-sdk-1.4/packages/api-utils/lib/array.js | 102 + .../packages/api-utils/lib/byte-streams.js | 135 + .../packages/api-utils/lib/channel.js | 67 + .../packages/api-utils/lib/collection.js | 141 + .../packages/api-utils/lib/content.js | 44 + .../packages/api-utils/lib/content/loader.js | 203 + .../packages/api-utils/lib/content/symbiont.js | 217 + .../packages/api-utils/lib/content/worker.js | 662 +++ .../addon-sdk-1.4/packages/api-utils/lib/cortex.js | 139 + .../packages/api-utils/lib/cuddlefish.js | 320 ++ .../packages/api-utils/lib/dom/events.js | 169 + .../packages/api-utils/lib/dom/events/keys.js | 93 + tools/addon-sdk-1.4/packages/api-utils/lib/env!.js | 52 + .../packages/api-utils/lib/environment.js | 86 + .../addon-sdk-1.4/packages/api-utils/lib/errors.js | 92 + .../addon-sdk-1.4/packages/api-utils/lib/events.js | 202 + .../packages/api-utils/lib/events/assembler.js | 86 + tools/addon-sdk-1.4/packages/api-utils/lib/file.js | 227 + .../packages/api-utils/lib/find-tests.js | 1 + .../packages/api-utils/lib/globals!.js | 113 + .../packages/api-utils/lib/hidden-frame.js | 200 + .../addon-sdk-1.4/packages/api-utils/lib/httpd.js | 5202 ++++++++++++++++++++ .../packages/api-utils/lib/keyboard/hotkeys.js | 141 + .../packages/api-utils/lib/keyboard/observer.js | 86 + .../packages/api-utils/lib/keyboard/utils.js | 220 + .../packages/api-utils/lib/light-traits.js | 626 +++ tools/addon-sdk-1.4/packages/api-utils/lib/list.js | 147 + .../packages/api-utils/lib/match-pattern.js | 137 + .../addon-sdk-1.4/packages/api-utils/lib/memory.js | 146 + .../packages/api-utils/lib/namespace.js | 55 + .../packages/api-utils/lib/observer-service.js | 212 + .../packages/api-utils/lib/passwords/utils.js | 134 + .../packages/api-utils/lib/plain-text-console.js | 114 + .../packages/api-utils/lib/preferences-service.js | 138 + .../packages/api-utils/lib/process.js | 95 + .../packages/api-utils/lib/runtime.js | 48 + .../addon-sdk-1.4/packages/api-utils/lib/self!.js | 82 + .../addon-sdk-1.4/packages/api-utils/lib/system.js | 131 + .../packages/api-utils/lib/tab-browser.js | 761 +++ .../packages/api-utils/lib/tabs/events.js | 56 + .../packages/api-utils/lib/tabs/observer.js | 126 + .../packages/api-utils/lib/tabs/tab.js | 297 ++ .../packages/api-utils/lib/tabs/utils.js | 87 + tools/addon-sdk-1.4/packages/api-utils/lib/test.js | 140 + .../packages/api-utils/lib/test/assert.js | 360 ++ .../packages/api-utils/lib/text-streams.js | 273 + .../addon-sdk-1.4/packages/api-utils/lib/timer.js | 141 + .../packages/api-utils/lib/traceback.js | 155 + .../addon-sdk-1.4/packages/api-utils/lib/traits.js | 215 + .../packages/api-utils/lib/traits/core.js | 349 ++ tools/addon-sdk-1.4/packages/api-utils/lib/type.js | 372 ++ .../packages/api-utils/lib/unit-test-finder.js | 106 + .../packages/api-utils/lib/unit-test.js | 466 ++ .../addon-sdk-1.4/packages/api-utils/lib/unload.js | 59 + tools/addon-sdk-1.4/packages/api-utils/lib/url.js | 123 + .../packages/api-utils/lib/utils/data.js | 104 + .../packages/api-utils/lib/utils/function.js | 64 + .../packages/api-utils/lib/utils/registry.js | 90 + .../packages/api-utils/lib/utils/thumbnail.js | 76 + .../packages/api-utils/lib/window-utils.js | 270 + .../packages/api-utils/lib/windows/dom.js | 60 + .../packages/api-utils/lib/windows/loader.js | 152 + .../packages/api-utils/lib/windows/observer.js | 86 + .../packages/api-utils/lib/windows/tabs.js | 207 + tools/addon-sdk-1.4/packages/api-utils/lib/xhr.js | 181 + .../addon-sdk-1.4/packages/api-utils/lib/xpcom.js | 152 + .../packages/api-utils/lib/xul-app.js | 95 + .../addon-sdk-1.4/packages/api-utils/package.json | 14 + .../tests/commonjs-test-adapter/asserts.js | 50 + .../packages/api-utils/tests/fixtures/es5.js | 4 + .../packages/api-utils/tests/helpers.js | 19 + .../packages/api-utils/tests/loader/fixture.js | 2 + .../packages/api-utils/tests/modules/add.js | 5 + .../packages/api-utils/tests/modules/async1.js | 10 + .../packages/api-utils/tests/modules/async2.js | 4 + .../api-utils/tests/modules/badExportAndReturn.js | 6 + .../packages/api-utils/tests/modules/badFirst.js | 5 + .../packages/api-utils/tests/modules/badSecond.js | 4 + .../packages/api-utils/tests/modules/blue.js | 5 + .../packages/api-utils/tests/modules/castor.js | 6 + .../packages/api-utils/tests/modules/cheetah.js | 5 + .../packages/api-utils/tests/modules/color.js | 3 + .../packages/api-utils/tests/modules/dupe.js | 11 + .../packages/api-utils/tests/modules/dupeNested.js | 11 + .../api-utils/tests/modules/dupeSetExports.js | 4 + .../api-utils/tests/modules/exportsEquals.js | 1 + .../packages/api-utils/tests/modules/green.js | 6 + .../packages/api-utils/tests/modules/lion.js | 3 + .../packages/api-utils/tests/modules/orange.js | 6 + .../packages/api-utils/tests/modules/pollux.js | 6 + .../packages/api-utils/tests/modules/red.js | 12 + .../packages/api-utils/tests/modules/setExports.js | 1 + .../packages/api-utils/tests/modules/subtract.js | 5 + .../packages/api-utils/tests/modules/tiger.js | 4 + .../api-utils/tests/modules/traditional1.js | 8 + .../api-utils/tests/modules/traditional2.js | 2 + .../packages/api-utils/tests/modules/types/cat.js | 1 + .../packages/api-utils/tests/test-api-utils.js | 274 ++ .../packages/api-utils/tests/test-app-strings.js | 58 + .../packages/api-utils/tests/test-array.js | 36 + .../packages/api-utils/tests/test-byte-streams.js | 203 + .../packages/api-utils/tests/test-collection.js | 160 + .../api-utils/tests/test-commonjs-test-adapter.js | 7 + .../api-utils/tests/test-content-loader.js | 223 + .../packages/api-utils/tests/test-content-proxy.js | 754 +++ .../api-utils/tests/test-content-symbiont.js | 186 + .../api-utils/tests/test-content-worker.js | 374 ++ .../packages/api-utils/tests/test-cortex.js | 118 + .../packages/api-utils/tests/test-dom.js | 84 + .../packages/api-utils/tests/test-e10s-porting.js | 73 + .../packages/api-utils/tests/test-environment.js | 46 + .../packages/api-utils/tests/test-errors.js | 66 + .../packages/api-utils/tests/test-events.js | 182 + .../packages/api-utils/tests/test-file.js | 276 ++ .../api-utils/tests/test-function-utils.js | 23 + .../packages/api-utils/tests/test-globals.js | 21 + .../packages/api-utils/tests/test-hidden-frame.js | 47 + .../packages/api-utils/tests/test-httpd.js | 28 + .../api-utils/tests/test-keyboard-observer.js | 32 + .../api-utils/tests/test-keyboard-utils.js | 58 + .../packages/api-utils/tests/test-light-traits.js | 7 + .../packages/api-utils/tests/test-list.js | 203 + .../packages/api-utils/tests/test-loader.js | 31 + .../packages/api-utils/tests/test-match-pattern.js | 161 + .../packages/api-utils/tests/test-memory.js | 15 + .../packages/api-utils/tests/test-modules.js | 144 + .../packages/api-utils/tests/test-namespace.js | 70 + .../api-utils/tests/test-observer-service.js | 73 + .../api-utils/tests/test-passwords-utils.js | 138 + .../api-utils/tests/test-plain-text-console.js | 64 + .../api-utils/tests/test-preferences-service.js | 87 + .../packages/api-utils/tests/test-registry.js | 76 + .../packages/api-utils/tests/test-require.js | 25 + .../packages/api-utils/tests/test-self.js | 29 + .../packages/api-utils/tests/test-set-exports.js | 31 + .../packages/api-utils/tests/test-tab-browser.js | 525 ++ .../packages/api-utils/tests/test-tab-observer.js | 35 + .../packages/api-utils/tests/test-tab.js | 106 + .../packages/api-utils/tests/test-text-streams.js | 190 + .../packages/api-utils/tests/test-timer.js | 127 + .../packages/api-utils/tests/test-traceback.js | 114 + .../packages/api-utils/tests/test-traits-core.js | 834 ++++ .../packages/api-utils/tests/test-traits.js | 394 ++ .../packages/api-utils/tests/test-type.js | 88 + .../packages/api-utils/tests/test-unit-test.js | 247 + .../packages/api-utils/tests/test-unload.js | 196 + .../packages/api-utils/tests/test-url.js | 157 + .../packages/api-utils/tests/test-window-loader.js | 152 + .../api-utils/tests/test-window-observer.js | 44 + .../packages/api-utils/tests/test-window-utils.js | 271 + .../packages/api-utils/tests/test-xhr.js | 70 + .../packages/api-utils/tests/test-xpcom.js | 107 + .../packages/api-utils/tests/test-xul-app.js | 41 + .../packages/api-utils/tests/traits/assert.js | 94 + .../api-utils/tests/traits/descriptor-tests.js | 331 ++ .../api-utils/tests/traits/inheritance-tests.js | 100 + .../api-utils/tests/traits/object-tests.js | 317 ++ .../packages/api-utils/tests/traits/utils.js | 52 + .../packages/development-mode/README.md | 8 + .../packages/development-mode/docs/bootstrap.md | 6 + .../packages/development-mode/docs/main.md | 5 + .../packages/development-mode/lib/bootstrap.js | 153 + .../packages/development-mode/lib/main.js | 95 + .../packages/development-mode/package.json | 10 + .../addon-sdk-1.4/packages/test-harness/README.md | 8 + .../packages/test-harness/docs/harness.md | 2 + .../packages/test-harness/docs/run-tests.md | 9 + .../packages/test-harness/lib/harness.js | 352 ++ .../packages/test-harness/lib/run-tests.js | 133 + .../packages/test-harness/package.json | 8 + .../packages/test-harness/tests/test-packaging.js | 14 + .../python-lib/cuddlefish/__init__.py | 800 +++ .../python-lib/cuddlefish/_version.py | 129 + .../cuddlefish/app-extension/application.ini | 11 + .../cuddlefish/app-extension/bootstrap.js | 227 + .../cuddlefish/app-extension/install.rdf | 31 + tools/addon-sdk-1.4/python-lib/cuddlefish/bunch.py | 30 + .../python-lib/cuddlefish/docs/__init__.py | 0 .../python-lib/cuddlefish/docs/apiparser.py | 388 ++ .../python-lib/cuddlefish/docs/apirenderer.py | 299 ++ .../python-lib/cuddlefish/docs/generate.py | 230 + .../python-lib/cuddlefish/docs/renderapi.readme.md | 206 + .../python-lib/cuddlefish/docs/webdocs.py | 200 + .../python-lib/cuddlefish/manifest.py | 744 +++ .../cuddlefish/mobile-killer/bootstrap.js | 71 + .../cuddlefish/mobile-killer/install.rdf | 26 + .../python-lib/cuddlefish/options_defaults.py | 22 + .../python-lib/cuddlefish/options_xul.py | 60 + .../python-lib/cuddlefish/packaging.py | 396 ++ .../python-lib/cuddlefish/preflight.py | 73 + tools/addon-sdk-1.4/python-lib/cuddlefish/prefs.py | 111 + tools/addon-sdk-1.4/python-lib/cuddlefish/rdf.py | 178 + .../addon-sdk-1.4/python-lib/cuddlefish/runner.py | 629 +++ .../python-lib/cuddlefish/templates.py | 79 + .../python-lib/cuddlefish/tests/__init__.py | 61 + .../cuddlefish/tests/addons/simplest-test/main.js | 13 + .../tests/addons/simplest-test/package.json | 6 + .../addons/simplest-test/tests/test-minimal.js | 3 + .../packages/explicit-icon/explicit-icon.png | 0 .../packages/explicit-icon/explicit-icon64.png | 0 .../packages/explicit-icon/lib/main.js | 0 .../packages/explicit-icon/package.json | 5 + .../packages/implicit-icon/icon.png | 0 .../packages/implicit-icon/icon64.png | 0 .../packages/implicit-icon/lib/main.js | 0 .../packages/implicit-icon/package.json | 3 + .../bug-588119-files/packages/no-icon/lib/main.js | 0 .../bug-588119-files/packages/no-icon/package.json | 3 + .../packages/bar/lib/bar-loader.js | 0 .../bug-588661-files/packages/bar/package.json | 3 + .../packages/foo/lib/foo-loader.js | 0 .../bug-588661-files/packages/foo/package.json | 4 + .../tests/bug-611495-files/jspath-one/docs/main.md | 1 + .../tests/bug-611495-files/jspath-one/lib/main.js | 4 + .../tests/bug-611495-files/jspath-one/package.json | 5 + .../packages/commonjs-naming/doc/foo.md | 1 + .../packages/commonjs-naming/lib/foo-loader.js | 1 + .../packages/commonjs-naming/package.json | 3 + .../packages/commonjs-naming/test/test-foo.js | 3 + .../packages/original-naming/docs/foo.md | 1 + .../packages/original-naming/lib/foo-loader.js | 1 + .../packages/original-naming/package.json | 3 + .../packages/original-naming/tests/test-foo.js | 3 + .../packages/default-lib/doc/foo.md | 1 + .../packages/default-lib/lib/foo.js | 1 + .../packages/default-lib/lib/loader.js | 1 + .../packages/default-lib/package.json | 3 + .../packages/default-lib/test/test-foo.js | 3 + .../packages/default-root/doc/foo.md | 1 + .../bug-652227-files/packages/default-root/foo.js | 1 + .../packages/default-root/loader.js | 1 + .../packages/default-root/package.json | 3 + .../packages/default-root/test/test-foo.js | 3 + .../packages/explicit-dir-lib/alt-lib/foo.js | 1 + .../packages/explicit-dir-lib/alt-lib/loader.js | 1 + .../packages/explicit-dir-lib/doc/foo.md | 1 + .../packages/explicit-dir-lib/package.json | 4 + .../packages/explicit-dir-lib/test/test-foo.js | 3 + .../packages/explicit-lib/alt2-lib/foo.js | 1 + .../packages/explicit-lib/alt2-lib/loader.js | 1 + .../packages/explicit-lib/doc/foo.md | 1 + .../packages/explicit-lib/package.json | 4 + .../packages/explicit-lib/test/test-foo.js | 3 + .../packages/extra-options/docs/main.md | 1 + .../packages/extra-options/lib/main.js | 4 + .../packages/extra-options/package.json | 6 + .../packages/foo/lib/bar-e10s-adapter.js | 7 + .../e10s-adapter-files/packages/foo/lib/bar.js | 1 + .../e10s-adapter-files/packages/foo/lib/foo.js | 1 + .../e10s-adapter-files/packages/foo/package.json | 1 + .../cuddlefish/tests/linker-files/five/lib/main.js | 1 + .../tests/linker-files/five/package.json | 3 + .../linker-files/four-deps/four-a/lib/misc.js | 1 + .../linker-files/four-deps/four-a/package.json | 4 + .../linker-files/four-deps/four-a/topfiles/main.js | 1 + .../cuddlefish/tests/linker-files/four/lib/main.js | 1 + .../tests/linker-files/four/package.json | 3 + .../cuddlefish/tests/linker-files/one/lib/main.js | 5 + .../tests/linker-files/one/lib/subdir/three.js | 2 + .../cuddlefish/tests/linker-files/one/lib/two.js | 4 + .../cuddlefish/tests/linker-files/one/package.json | 3 + .../tests/linker-files/seven/data/text.data | 1 + .../tests/linker-files/seven/lib/main.js | 2 + .../tests/linker-files/seven/lib/unused.js | 1 + .../tests/linker-files/seven/package.json | 4 + .../tests/linker-files/six/lib/unused.js | 1 + .../cuddlefish/tests/linker-files/six/package.json | 3 + .../tests/linker-files/six/unreachable.js | 1 + .../linker-files/three-deps/three-a/data/msg.txt | 1 + .../three-deps/three-a/data/subdir/submsg.txt | 1 + .../linker-files/three-deps/three-a/lib/main.js | 4 + .../three-deps/three-a/lib/subdir/subfile.js | 1 + .../linker-files/three-deps/three-a/lib/unused.js | 1 + .../linker-files/three-deps/three-a/package.json | 3 + .../linker-files/three-deps/three-b/lib/main.js | 1 + .../linker-files/three-deps/three-b/package.json | 3 + .../linker-files/three-deps/three-c/lib/main.js | 1 + .../linker-files/three-deps/three-c/lib/sub/foo.js | 2 + .../linker-files/three-deps/three-c/package.json | 3 + .../tests/linker-files/three/lib/main.js | 4 + .../tests/linker-files/three/package.json | 3 + .../packages/no-prefs/lib/main.js | 0 .../packages/no-prefs/package.json | 3 + .../packages/simple-prefs/lib/main.js | 0 .../packages/simple-prefs/package.json | 11 + .../static-files/doc/dev-guide-source/no_h1.md | 3 + .../static-files/doc/dev-guide-source/welcome.md | 3 + .../static-files/doc/static-files/another.html | 1 + .../tests/static-files/doc/static-files/base.html | 222 + .../tests/static-files/doc/static-files/index.html | 23 + .../tests/static-files/docs/APIreference.html | 465 ++ .../tests/static-files/docs/APIsample.md | 158 + .../packages/aardvark/doc/aardvark-feeder.md | 8 + .../static-files/packages/aardvark/doc/main.md | 0 .../static-files/packages/aardvark/lib/ignore_me | 3 + .../static-files/packages/aardvark/lib/main.js | 4 + .../aardvark/lib/surprise.js/ignore_me_too | 2 + .../static-files/packages/aardvark/package.json | 7 + .../packages/anteater_files/lib/main.js | 4 + .../packages/anteater_files/package.json | 8 + .../static-files/packages/api-utils/lib/loader.js | 3 + .../static-files/packages/api-utils/package.json | 5 + .../packages/barbeque/lib/bar-module.js | 3 + .../static-files/packages/barbeque/package.json | 4 + .../static-files/packages/minimal/docs/main.md | 1 + .../static-files/packages/minimal/lib/main.js | 4 + .../static-files/packages/minimal/package.json | 4 + .../xpi-template/components/harness.js | 4 + .../python-lib/cuddlefish/tests/test_apiparser.py | 524 ++ .../cuddlefish/tests/test_apirenderer.py | 28 + .../python-lib/cuddlefish/tests/test_generate.py | 137 + .../python-lib/cuddlefish/tests/test_init.py | 153 + .../python-lib/cuddlefish/tests/test_linker.py | 193 + .../python-lib/cuddlefish/tests/test_manifest.py | 250 + .../python-lib/cuddlefish/tests/test_packaging.py | 104 + .../python-lib/cuddlefish/tests/test_preflight.py | 143 + .../python-lib/cuddlefish/tests/test_rdf.py | 15 + .../python-lib/cuddlefish/tests/test_runner.py | 24 + .../python-lib/cuddlefish/tests/test_util.py | 18 + .../python-lib/cuddlefish/tests/test_version.py | 24 + .../python-lib/cuddlefish/tests/test_webdocs.py | 94 + .../python-lib/cuddlefish/tests/test_xpi.py | 284 ++ tools/addon-sdk-1.4/python-lib/cuddlefish/util.py | 19 + .../python-lib/cuddlefish/version_comparator.py | 202 + tools/addon-sdk-1.4/python-lib/cuddlefish/xpi.py | 130 + tools/addon-sdk-1.4/python-lib/jetpack_sdk_env.py | 62 + .../addon-sdk-1.4/python-lib/markdown/__init__.py | 603 +++ .../python-lib/markdown/blockparser.py | 95 + .../python-lib/markdown/blockprocessors.py | 460 ++ .../python-lib/markdown/commandline.py | 96 + .../addon-sdk-1.4/python-lib/markdown/docs/AUTHORS | 43 + .../addon-sdk-1.4/python-lib/markdown/docs/LICENSE | 30 + .../python-lib/markdown/etree_loader.py | 33 + .../python-lib/markdown/extensions/__init__.py | 0 .../python-lib/markdown/extensions/abbr.py | 95 + .../python-lib/markdown/extensions/codehilite.py | 224 + .../python-lib/markdown/extensions/def_list.py | 104 + .../python-lib/markdown/extensions/extra.py | 49 + .../python-lib/markdown/extensions/fenced_code.py | 117 + .../python-lib/markdown/extensions/footnotes.py | 293 ++ .../python-lib/markdown/extensions/headerid.py | 195 + .../python-lib/markdown/extensions/html_tidy.py | 62 + .../python-lib/markdown/extensions/imagelinks.py | 119 + .../python-lib/markdown/extensions/meta.py | 90 + .../python-lib/markdown/extensions/rss.py | 114 + .../python-lib/markdown/extensions/tables.py | 97 + .../python-lib/markdown/extensions/toc.py | 140 + .../python-lib/markdown/extensions/wikilinks.py | 155 + tools/addon-sdk-1.4/python-lib/markdown/html4.py | 274 ++ .../python-lib/markdown/inlinepatterns.py | 371 ++ tools/addon-sdk-1.4/python-lib/markdown/odict.py | 162 + .../python-lib/markdown/postprocessors.py | 77 + .../python-lib/markdown/preprocessors.py | 214 + .../python-lib/markdown/treeprocessors.py | 329 ++ .../addon-sdk-1.4/python-lib/mozrunner/__init__.py | 632 +++ .../python-lib/mozrunner/killableprocess.py | 316 ++ tools/addon-sdk-1.4/python-lib/mozrunner/qijo.py | 162 + .../python-lib/mozrunner/winprocess.py | 383 ++ tools/addon-sdk-1.4/python-lib/mozrunner/wpk.py | 76 + .../python-lib/simplejson/__init__.py | 376 ++ .../addon-sdk-1.4/python-lib/simplejson/decoder.py | 343 ++ .../addon-sdk-1.4/python-lib/simplejson/encoder.py | 385 ++ .../addon-sdk-1.4/python-lib/simplejson/scanner.py | 67 + tools/addon-sdk-1.4/python-lib/simplejson/tool.py | 44 + .../media/screenshots/widget-panel-clock.png | Bin 0 -> 60823 bytes tools/addon-sdk-1.5/LICENSE | 30 + tools/addon-sdk-1.5/README | 30 + tools/addon-sdk-1.5/bin/activate | 86 + tools/addon-sdk-1.5/bin/activate.bat | 135 + tools/addon-sdk-1.5/bin/activate.ps1 | 99 + tools/addon-sdk-1.5/bin/cfx | 33 + tools/addon-sdk-1.5/bin/cfx.bat | 6 + tools/addon-sdk-1.5/bin/deactivate.bat | 23 + .../integration-scripts/buildbot-run-cfx-helper | 14 + .../bin/integration-scripts/integration-check | 364 ++ tools/addon-sdk-1.5/examples/annotator/README.md | 46 + .../annotator/data/annotation/annotation.html | 31 + .../annotator/data/annotation/annotation.js | 11 + .../annotator/data/editor/annotation-editor.html | 39 + .../annotator/data/editor/annotation-editor.js | 23 + .../examples/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 + .../examples/annotator/data/widget/pencil-off.png | Bin 0 -> 1740 bytes .../examples/annotator/data/widget/pencil-on.png | Bin 0 -> 2054 bytes .../examples/annotator/data/widget/widget.js | 17 + tools/addon-sdk-1.5/examples/annotator/lib/main.js | 294 ++ .../addon-sdk-1.5/examples/annotator/package.json | 9 + .../examples/annotator/tests/test-main.js | 7 + .../examples/library-detector/README.md | 9 + .../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 .../examples/library-detector/data/icons/yui.ico | Bin 0 -> 6598 bytes .../library-detector/data/library-detector.js | 97 + .../examples/library-detector/data/widget.js | 14 + .../examples/library-detector/lib/main.js | 107 + .../examples/library-detector/package.json | 9 + .../examples/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 + .../addon-sdk-1.5/examples/reddit-panel/README.md | 14 + .../examples/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 + tools/addon-sdk-1.5/packages/addon-kit/README.md | 12 + .../packages/addon-kit/data/moz_favicon.ico | Bin 0 -> 1406 bytes .../packages/addon-kit/data/test-context-menu.js | 5 + .../packages/addon-kit/data/test-page-mod.html | 12 + .../packages/addon-kit/data/test-page-worker.html | 12 + .../packages/addon-kit/data/test-page-worker.js | 29 + .../packages/addon-kit/data/test.html | 12 + .../packages/addon-kit/docs/clipboard.md | 62 + .../packages/addon-kit/docs/context-menu.md | 715 +++ .../packages/addon-kit/docs/hotkeys.md | 78 + .../packages/addon-kit/docs/notifications.md | 64 + .../packages/addon-kit/docs/page-mod.md | 380 ++ .../packages/addon-kit/docs/page-worker.md | 306 ++ .../addon-sdk-1.5/packages/addon-kit/docs/panel.md | 575 +++ .../packages/addon-kit/docs/passwords.md | 568 +++ .../packages/addon-kit/docs/private-browsing.md | 50 + .../packages/addon-kit/docs/request.md | 196 + .../packages/addon-kit/docs/selection.md | 90 + .../addon-sdk-1.5/packages/addon-kit/docs/self.md | 79 + .../packages/addon-kit/docs/simple-prefs.md | 74 + .../packages/addon-kit/docs/simple-storage.md | 129 + .../addon-sdk-1.5/packages/addon-kit/docs/tabs.md | 385 ++ .../packages/addon-kit/docs/timers.md | 52 + .../packages/addon-kit/docs/widget.md | 907 ++++ .../packages/addon-kit/docs/windows.md | 191 + .../packages/addon-kit/lib/clipboard.js | 230 + .../packages/addon-kit/lib/context-menu.js | 1492 ++++++ .../packages/addon-kit/lib/hotkeys.js | 37 + tools/addon-sdk-1.5/packages/addon-kit/lib/l10n.js | 126 + .../packages/addon-kit/lib/notifications.js | 79 + .../packages/addon-kit/lib/page-mod.js | 196 + .../packages/addon-kit/lib/page-worker.js | 65 + .../addon-sdk-1.5/packages/addon-kit/lib/panel.js | 369 ++ .../packages/addon-kit/lib/passwords.js | 59 + .../packages/addon-kit/lib/private-browsing.js | 67 + .../packages/addon-kit/lib/request.js | 276 ++ .../packages/addon-kit/lib/selection.js | 414 ++ .../packages/addon-kit/lib/simple-prefs.js | 73 + .../packages/addon-kit/lib/simple-storage.js | 228 + tools/addon-sdk-1.5/packages/addon-kit/lib/tabs.js | 28 + .../addon-sdk-1.5/packages/addon-kit/lib/timers.js | 8 + .../addon-sdk-1.5/packages/addon-kit/lib/widget.js | 909 ++++ .../packages/addon-kit/lib/windows.js | 202 + .../packages/addon-kit/locale/en-GB.properties | 5 + .../packages/addon-kit/locale/eo.properties | 5 + .../packages/addon-kit/locale/fr-FR.properties | 17 + .../addon-sdk-1.5/packages/addon-kit/package.json | 12 + .../packages/addon-kit/tests/helpers.js | 23 + .../addon-kit/tests/pagemod-test-helpers.js | 67 + .../packages/addon-kit/tests/test-clipboard.js | 64 + .../addon-kit/tests/test-context-menu.html | 45 + .../packages/addon-kit/tests/test-context-menu.js | 2070 ++++++++ .../packages/addon-kit/tests/test-hotkeys.js | 160 + .../packages/addon-kit/tests/test-l10n.js | 56 + .../packages/addon-kit/tests/test-module.js | 37 + .../packages/addon-kit/tests/test-notifications.js | 46 + .../packages/addon-kit/tests/test-page-mod.js | 420 ++ .../packages/addon-kit/tests/test-page-worker.js | 366 ++ .../packages/addon-kit/tests/test-panel.js | 441 ++ .../packages/addon-kit/tests/test-passwords.js | 281 ++ .../addon-kit/tests/test-private-browsing.js | 204 + .../packages/addon-kit/tests/test-request.js | 340 ++ .../packages/addon-kit/tests/test-selection.js | 458 ++ .../packages/addon-kit/tests/test-simple-prefs.js | 147 + .../addon-kit/tests/test-simple-storage.js | 312 ++ .../packages/addon-kit/tests/test-tabs.js | 873 ++++ .../packages/addon-kit/tests/test-timers.js | 12 + .../packages/addon-kit/tests/test-widget.js | 954 ++++ .../packages/addon-kit/tests/test-windows.js | 309 ++ tools/addon-sdk-1.5/packages/api-utils/README.md | 35 + .../packages/api-utils/data/content-proxy.js | 838 ++++ .../api-utils/data/test-content-symbiont.js | 5 + .../api-utils/data/test-message-manager.js | 6 + .../api-utils/data/test-trusted-document.html | 17 + .../packages/api-utils/docs/api-utils.md | 157 + .../packages/api-utils/docs/app-strings.md | 65 + .../addon-sdk-1.5/packages/api-utils/docs/base.md | 207 + .../packages/api-utils/docs/byte-streams.md | 68 + .../packages/api-utils/docs/collection.md | 77 + .../packages/api-utils/docs/content.md | 15 + .../packages/api-utils/docs/content/loader.md | 92 + .../packages/api-utils/docs/content/proxy.md | 241 + .../packages/api-utils/docs/content/symbiont.md | 140 + .../packages/api-utils/docs/content/worker.md | 130 + .../packages/api-utils/docs/cortex.md | 160 + .../packages/api-utils/docs/cuddlefish.md | 7 + .../packages/api-utils/docs/environment.md | 43 + .../packages/api-utils/docs/errors.md | 42 + .../packages/api-utils/docs/events.md | 78 + .../addon-sdk-1.5/packages/api-utils/docs/file.md | 151 + .../packages/api-utils/docs/hidden-frame.md | 83 + .../addon-sdk-1.5/packages/api-utils/docs/httpd.md | 31 + .../packages/api-utils/docs/light-traits.md | 295 ++ .../addon-sdk-1.5/packages/api-utils/docs/list.md | 98 + .../packages/api-utils/docs/match-pattern.md | 246 + .../packages/api-utils/docs/memory.md | 7 + .../packages/api-utils/docs/message-manager.md | 13 + .../packages/api-utils/docs/namespace.md | 71 + .../packages/api-utils/docs/observer-service.md | 73 + .../packages/api-utils/docs/plain-text-console.md | 7 + .../packages/api-utils/docs/preferences-service.md | 84 + .../packages/api-utils/docs/runtime.md | 75 + .../packages/api-utils/docs/sandbox.md | 51 + .../packages/api-utils/docs/tab-browser.md | 140 + .../packages/api-utils/docs/text-streams.md | 102 + .../packages/api-utils/docs/traceback.md | 66 + .../packages/api-utils/docs/traits.md | 244 + .../packages/api-utils/docs/unit-test.md | 393 ++ .../packages/api-utils/docs/unload.md | 61 + tools/addon-sdk-1.5/packages/api-utils/docs/url.md | 85 + .../packages/api-utils/docs/window-utils.md | 88 + tools/addon-sdk-1.5/packages/api-utils/docs/xhr.md | 95 + .../addon-sdk-1.5/packages/api-utils/docs/xpcom.md | 227 + .../packages/api-utils/docs/xul-app.md | 76 + .../packages/api-utils/lib/api-utils.js | 152 + .../packages/api-utils/lib/app-strings.js | 63 + .../addon-sdk-1.5/packages/api-utils/lib/array.js | 69 + tools/addon-sdk-1.5/packages/api-utils/lib/base.js | 177 + .../packages/api-utils/lib/byte-streams.js | 102 + .../packages/api-utils/lib/channel.js | 46 + .../packages/api-utils/lib/collection.js | 108 + .../packages/api-utils/lib/content.js | 11 + .../packages/api-utils/lib/content/loader.js | 169 + .../packages/api-utils/lib/content/symbiont.js | 183 + .../packages/api-utils/lib/content/worker.js | 614 +++ .../addon-sdk-1.5/packages/api-utils/lib/cortex.js | 109 + .../packages/api-utils/lib/cuddlefish.js | 307 ++ .../packages/api-utils/lib/dom/events.js | 136 + .../packages/api-utils/lib/dom/events/keys.js | 60 + tools/addon-sdk-1.5/packages/api-utils/lib/env!.js | 20 + .../packages/api-utils/lib/environment.js | 54 + .../addon-sdk-1.5/packages/api-utils/lib/errors.js | 60 + .../addon-sdk-1.5/packages/api-utils/lib/events.js | 173 + .../packages/api-utils/lib/events/assembler.js | 53 + tools/addon-sdk-1.5/packages/api-utils/lib/file.js | 192 + .../packages/api-utils/lib/find-tests.js | 5 + .../packages/api-utils/lib/globals!.js | 98 + .../packages/api-utils/lib/hidden-frame.js | 166 + .../addon-sdk-1.5/packages/api-utils/lib/httpd.js | 5166 +++++++++++++++++++ .../packages/api-utils/lib/keyboard/hotkeys.js | 107 + .../packages/api-utils/lib/keyboard/observer.js | 53 + .../packages/api-utils/lib/keyboard/utils.js | 186 + .../packages/api-utils/lib/light-traits.js | 596 +++ tools/addon-sdk-1.5/packages/api-utils/lib/list.js | 114 + .../packages/api-utils/lib/match-pattern.js | 103 + .../addon-sdk-1.5/packages/api-utils/lib/memory.js | 114 + .../packages/api-utils/lib/message-manager.js | 203 + .../packages/api-utils/lib/namespace.js | 27 + .../packages/api-utils/lib/observer-service.js | 178 + .../packages/api-utils/lib/passwords/utils.js | 101 + .../packages/api-utils/lib/plain-text-console.js | 82 + .../packages/api-utils/lib/preferences-service.js | 103 + .../packages/api-utils/lib/process.js | 71 + .../packages/api-utils/lib/runtime.js | 15 + .../packages/api-utils/lib/sandbox.js | 46 + .../addon-sdk-1.5/packages/api-utils/lib/self!.js | 48 + .../addon-sdk-1.5/packages/api-utils/lib/system.js | 120 + .../packages/api-utils/lib/tab-browser.js | 727 +++ .../packages/api-utils/lib/tabs/events.js | 24 + .../packages/api-utils/lib/tabs/observer.js | 93 + .../packages/api-utils/lib/tabs/tab.js | 264 + .../packages/api-utils/lib/tabs/utils.js | 54 + tools/addon-sdk-1.5/packages/api-utils/lib/test.js | 108 + .../packages/api-utils/lib/test/assert.js | 331 ++ .../packages/api-utils/lib/text-streams.js | 240 + .../addon-sdk-1.5/packages/api-utils/lib/timer.js | 106 + .../packages/api-utils/lib/traceback.js | 123 + .../addon-sdk-1.5/packages/api-utils/lib/traits.js | 183 + .../packages/api-utils/lib/traits/core.js | 317 ++ tools/addon-sdk-1.5/packages/api-utils/lib/type.js | 340 ++ .../packages/api-utils/lib/unit-test-finder.js | 74 + .../packages/api-utils/lib/unit-test.js | 438 ++ .../addon-sdk-1.5/packages/api-utils/lib/unload.js | 63 + tools/addon-sdk-1.5/packages/api-utils/lib/url.js | 113 + .../packages/api-utils/lib/utils/data.js | 71 + .../packages/api-utils/lib/utils/function.js | 50 + .../packages/api-utils/lib/utils/object.js | 46 + .../packages/api-utils/lib/utils/registry.js | 57 + .../packages/api-utils/lib/utils/thumbnail.js | 43 + .../packages/api-utils/lib/window-utils.js | 249 + .../packages/api-utils/lib/windows/dom.js | 27 + .../packages/api-utils/lib/windows/loader.js | 120 + .../packages/api-utils/lib/windows/observer.js | 53 + .../packages/api-utils/lib/windows/tabs.js | 174 + tools/addon-sdk-1.5/packages/api-utils/lib/xhr.js | 149 + .../addon-sdk-1.5/packages/api-utils/lib/xpcom.js | 115 + .../packages/api-utils/lib/xul-app.js | 63 + .../addon-sdk-1.5/packages/api-utils/package.json | 14 + .../tests/commonjs-test-adapter/asserts.js | 54 + .../packages/api-utils/tests/fixtures/es5.js | 8 + .../tests/fixtures/sandbox-complex-character.js | 5 + .../api-utils/tests/fixtures/sandbox-normal.js | 7 + .../packages/api-utils/tests/helpers.js | 23 + .../packages/api-utils/tests/loader/fixture.js | 6 + .../packages/api-utils/tests/modules/add.js | 9 + .../packages/api-utils/tests/modules/async1.js | 14 + .../packages/api-utils/tests/modules/async2.js | 8 + .../api-utils/tests/modules/badExportAndReturn.js | 10 + .../packages/api-utils/tests/modules/badFirst.js | 9 + .../packages/api-utils/tests/modules/badSecond.js | 8 + .../packages/api-utils/tests/modules/blue.js | 9 + .../packages/api-utils/tests/modules/castor.js | 10 + .../packages/api-utils/tests/modules/cheetah.js | 9 + .../packages/api-utils/tests/modules/color.js | 7 + .../packages/api-utils/tests/modules/dupe.js | 15 + .../packages/api-utils/tests/modules/dupeNested.js | 15 + .../api-utils/tests/modules/dupeSetExports.js | 8 + .../api-utils/tests/modules/exportsEquals.js | 5 + .../packages/api-utils/tests/modules/green.js | 10 + .../packages/api-utils/tests/modules/lion.js | 7 + .../packages/api-utils/tests/modules/orange.js | 10 + .../packages/api-utils/tests/modules/pollux.js | 10 + .../packages/api-utils/tests/modules/red.js | 16 + .../packages/api-utils/tests/modules/setExports.js | 5 + .../packages/api-utils/tests/modules/subtract.js | 9 + .../packages/api-utils/tests/modules/tiger.js | 8 + .../api-utils/tests/modules/traditional1.js | 12 + .../api-utils/tests/modules/traditional2.js | 6 + .../packages/api-utils/tests/modules/types/cat.js | 5 + .../packages/api-utils/tests/test-api-utils.js | 241 + .../packages/api-utils/tests/test-app-strings.js | 62 + .../packages/api-utils/tests/test-array.js | 40 + .../packages/api-utils/tests/test-base.js | 240 + .../packages/api-utils/tests/test-byte-streams.js | 169 + .../packages/api-utils/tests/test-collection.js | 127 + .../api-utils/tests/test-commonjs-test-adapter.js | 11 + .../api-utils/tests/test-content-loader.js | 229 + .../packages/api-utils/tests/test-content-proxy.js | 758 +++ .../api-utils/tests/test-content-symbiont.js | 190 + .../api-utils/tests/test-content-worker.js | 378 ++ .../packages/api-utils/tests/test-cortex.js | 122 + .../packages/api-utils/tests/test-dom.js | 88 + .../packages/api-utils/tests/test-environment.js | 50 + .../packages/api-utils/tests/test-errors.js | 70 + .../packages/api-utils/tests/test-events.js | 250 + .../packages/api-utils/tests/test-file.js | 273 + .../api-utils/tests/test-function-utils.js | 42 + .../packages/api-utils/tests/test-globals.js | 25 + .../packages/api-utils/tests/test-hidden-frame.js | 51 + .../packages/api-utils/tests/test-httpd.js | 38 + .../api-utils/tests/test-keyboard-observer.js | 36 + .../api-utils/tests/test-keyboard-utils.js | 62 + .../packages/api-utils/tests/test-light-traits.js | 11 + .../packages/api-utils/tests/test-list.js | 207 + .../packages/api-utils/tests/test-loader.js | 35 + .../packages/api-utils/tests/test-match-pattern.js | 127 + .../packages/api-utils/tests/test-memory.js | 19 + .../api-utils/tests/test-message-manager.js | 600 +++ .../packages/api-utils/tests/test-modules.js | 148 + .../packages/api-utils/tests/test-namespace.js | 79 + .../api-utils/tests/test-observer-service.js | 77 + .../api-utils/tests/test-passwords-utils.js | 142 + .../api-utils/tests/test-plain-text-console.js | 68 + .../api-utils/tests/test-preferences-service.js | 91 + .../packages/api-utils/tests/test-registry.js | 80 + .../packages/api-utils/tests/test-require.js | 29 + .../packages/api-utils/tests/test-sandbox.js | 113 + .../packages/api-utils/tests/test-self.js | 33 + .../packages/api-utils/tests/test-set-exports.js | 35 + .../packages/api-utils/tests/test-tab-browser.js | 493 ++ .../packages/api-utils/tests/test-tab-observer.js | 39 + .../packages/api-utils/tests/test-tab.js | 110 + .../packages/api-utils/tests/test-text-streams.js | 156 + .../packages/api-utils/tests/test-timer.js | 131 + .../packages/api-utils/tests/test-traceback.js | 118 + .../packages/api-utils/tests/test-traits-core.js | 838 ++++ .../packages/api-utils/tests/test-traits.js | 398 ++ .../packages/api-utils/tests/test-type.js | 92 + .../packages/api-utils/tests/test-unit-test.js | 251 + .../packages/api-utils/tests/test-unload.js | 161 + .../packages/api-utils/tests/test-url.js | 204 + .../packages/api-utils/tests/test-window-loader.js | 120 + .../api-utils/tests/test-window-observer.js | 48 + .../packages/api-utils/tests/test-window-utils.js | 317 ++ .../packages/api-utils/tests/test-xhr.js | 74 + .../packages/api-utils/tests/test-xpcom.js | 111 + .../packages/api-utils/tests/test-xul-app.js | 45 + .../packages/api-utils/tests/traits/assert.js | 98 + .../api-utils/tests/traits/descriptor-tests.js | 335 ++ .../api-utils/tests/traits/inheritance-tests.js | 104 + .../api-utils/tests/traits/object-tests.js | 321 ++ .../packages/api-utils/tests/traits/utils.js | 56 + .../addon-sdk-1.5/packages/test-harness/README.md | 12 + .../packages/test-harness/docs/harness.md | 6 + .../packages/test-harness/docs/run-tests.md | 13 + .../packages/test-harness/lib/harness.js | 324 ++ .../packages/test-harness/lib/run-tests.js | 101 + .../packages/test-harness/package.json | 8 + .../packages/test-harness/tests/test-packaging.js | 18 + .../python-lib/cuddlefish/__init__.py | 795 +++ .../python-lib/cuddlefish/_version.py | 171 + .../cuddlefish/app-extension/application.ini | 11 + .../cuddlefish/app-extension/bootstrap.js | 216 + .../cuddlefish/app-extension/install.rdf | 35 + tools/addon-sdk-1.5/python-lib/cuddlefish/bunch.py | 34 + .../python-lib/cuddlefish/docs/__init__.py | 0 .../python-lib/cuddlefish/docs/apiparser.py | 392 ++ .../python-lib/cuddlefish/docs/apirenderer.py | 302 ++ .../python-lib/cuddlefish/docs/generate.py | 283 ++ .../python-lib/cuddlefish/docs/renderapi.readme.md | 210 + .../python-lib/cuddlefish/docs/webdocs.py | 198 + .../python-lib/cuddlefish/manifest.py | 750 +++ .../cuddlefish/mobile-killer/bootstrap.js | 52 + .../cuddlefish/mobile-killer/install.rdf | 30 + .../python-lib/cuddlefish/options_defaults.py | 26 + .../python-lib/cuddlefish/options_xul.py | 64 + .../python-lib/cuddlefish/packaging.py | 427 ++ .../python-lib/cuddlefish/preflight.py | 77 + tools/addon-sdk-1.5/python-lib/cuddlefish/prefs.py | 115 + .../python-lib/cuddlefish/property_parser.py | 93 + tools/addon-sdk-1.5/python-lib/cuddlefish/rdf.py | 185 + .../addon-sdk-1.5/python-lib/cuddlefish/runner.py | 651 +++ .../python-lib/cuddlefish/templates.py | 79 + .../python-lib/cuddlefish/tests/__init__.py | 65 + .../cuddlefish/tests/addons/simplest-test/main.js | 17 + .../tests/addons/simplest-test/package.json | 6 + .../addons/simplest-test/tests/test-minimal.js | 7 + .../packages/explicit-icon/explicit-icon.png | 0 .../packages/explicit-icon/explicit-icon64.png | 0 .../packages/explicit-icon/lib/main.js | 0 .../packages/explicit-icon/package.json | 5 + .../packages/implicit-icon/icon.png | 0 .../packages/implicit-icon/icon64.png | 0 .../packages/implicit-icon/lib/main.js | 0 .../packages/implicit-icon/package.json | 3 + .../bug-588119-files/packages/no-icon/lib/main.js | 0 .../bug-588119-files/packages/no-icon/package.json | 3 + .../packages/bar/lib/bar-loader.js | 0 .../bug-588661-files/packages/bar/package.json | 3 + .../packages/foo/lib/foo-loader.js | 0 .../bug-588661-files/packages/foo/package.json | 4 + .../tests/bug-611495-files/jspath-one/docs/main.md | 5 + .../tests/bug-611495-files/jspath-one/lib/main.js | 8 + .../tests/bug-611495-files/jspath-one/package.json | 5 + .../packages/commonjs-naming/doc/foo.md | 5 + .../packages/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 + .../packages/original-naming/lib/foo-loader.js | 5 + .../packages/original-naming/package.json | 3 + .../packages/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 + .../packages/default-locale/locale/emptyFolder | 0 .../packages/default-locale/package.json | 1 + .../packages/default-root/doc/foo.md | 5 + .../bug-652227-files/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 + .../packages/explicit-dir-lib/alt-lib/loader.js | 5 + .../packages/explicit-dir-lib/doc/foo.md | 5 + .../packages/explicit-dir-lib/package.json | 4 + .../packages/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 + .../tests/bug-715340-files/pkg-1-pack/package.json | 10 + .../bug-715340-files/pkg-2-unpack/package.json | 10 + .../tests/bug-715340-files/pkg-3-pack/package.json | 9 + .../packages/foo/lib/bar-e10s-adapter.js | 11 + .../e10s-adapter-files/packages/foo/lib/bar.js | 5 + .../e10s-adapter-files/packages/foo/lib/foo.js | 5 + .../e10s-adapter-files/packages/foo/package.json | 1 + .../cuddlefish/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 + .../linker-files/four-deps/four-a/package.json | 4 + .../linker-files/four-deps/four-a/topfiles/main.js | 5 + .../cuddlefish/tests/linker-files/four/lib/main.js | 5 + .../tests/linker-files/four/package.json | 3 + .../cuddlefish/tests/linker-files/one/lib/main.js | 9 + .../tests/linker-files/one/lib/subdir/three.js | 6 + .../cuddlefish/tests/linker-files/one/lib/two.js | 8 + .../cuddlefish/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 + .../cuddlefish/tests/linker-files/six/package.json | 3 + .../tests/linker-files/six/unreachable.js | 5 + .../linker-files/three-deps/three-a/data/msg.txt | 1 + .../three-deps/three-a/data/subdir/submsg.txt | 1 + .../linker-files/three-deps/three-a/lib/main.js | 8 + .../three-deps/three-a/lib/subdir/subfile.js | 5 + .../linker-files/three-deps/three-a/lib/unused.js | 5 + .../three-deps/three-a/locale/fr-FR.properties | 5 + .../linker-files/three-deps/three-a/package.json | 3 + .../linker-files/three-deps/three-b/lib/main.js | 5 + .../three-deps/three-b/locale/fr-FR.properties | 6 + .../linker-files/three-deps/three-b/package.json | 3 + .../linker-files/three-deps/three-c/lib/main.js | 5 + .../linker-files/three-deps/three-c/lib/sub/foo.js | 6 + .../three-deps/three-c/locale/fr-FR.properties | 9 + .../linker-files/three-deps/three-c/package.json | 3 + .../tests/linker-files/three/lib/main.js | 8 + .../tests/linker-files/three/package.json | 3 + .../tests/linker-files/three/tests/nontest.js | 1 + .../tests/linker-files/three/tests/test-one.js | 1 + .../tests/linker-files/three/tests/test-two.js | 1 + .../packages/no-prefs/lib/main.js | 0 .../packages/no-prefs/package.json | 6 + .../packages/simple-prefs/lib/main.js | 0 .../packages/simple-prefs/package.json | 11 + .../static-files/doc/dev-guide-source/no_h1.md | 7 + .../static-files/doc/dev-guide-source/welcome.md | 7 + .../static-files/doc/static-files/another.html | 5 + .../tests/static-files/doc/static-files/base.html | 222 + .../tests/static-files/doc/static-files/index.html | 27 + .../tests/static-files/docs/APIreference.html | 469 ++ .../tests/static-files/docs/APIsample.md | 162 + .../packages/aardvark/doc/aardvark-feeder.md | 12 + .../static-files/packages/aardvark/doc/main.md | 0 .../static-files/packages/aardvark/lib/ignore_me | 3 + .../static-files/packages/aardvark/lib/main.js | 8 + .../aardvark/lib/surprise.js/ignore_me_too | 2 + .../static-files/packages/aardvark/package.json | 7 + .../packages/anteater_files/lib/main.js | 8 + .../packages/anteater_files/package.json | 8 + .../static-files/packages/api-utils/lib/loader.js | 7 + .../static-files/packages/api-utils/package.json | 5 + .../packages/barbeque/lib/bar-module.js | 7 + .../static-files/packages/barbeque/package.json | 4 + .../static-files/packages/minimal/docs/main.md | 5 + .../static-files/packages/minimal/lib/main.js | 8 + .../static-files/packages/minimal/package.json | 4 + .../xpi-template/components/harness.js | 8 + .../python-lib/cuddlefish/tests/test_apiparser.py | 538 ++ .../cuddlefish/tests/test_apirenderer.py | 31 + .../python-lib/cuddlefish/tests/test_generate.py | 147 + .../python-lib/cuddlefish/tests/test_init.py | 148 + .../python-lib/cuddlefish/tests/test_linker.py | 234 + .../python-lib/cuddlefish/tests/test_manifest.py | 254 + .../python-lib/cuddlefish/tests/test_packaging.py | 116 + .../python-lib/cuddlefish/tests/test_preflight.py | 147 + .../cuddlefish/tests/test_property_parser.py | 56 + .../python-lib/cuddlefish/tests/test_rdf.py | 45 + .../python-lib/cuddlefish/tests/test_runner.py | 27 + .../python-lib/cuddlefish/tests/test_util.py | 22 + .../python-lib/cuddlefish/tests/test_version.py | 28 + .../python-lib/cuddlefish/tests/test_webdocs.py | 97 + .../python-lib/cuddlefish/tests/test_xpi.py | 405 ++ tools/addon-sdk-1.5/python-lib/cuddlefish/util.py | 23 + .../python-lib/cuddlefish/version_comparator.py | 206 + tools/addon-sdk-1.5/python-lib/cuddlefish/xpi.py | 151 + tools/addon-sdk-1.5/python-lib/jetpack_sdk_env.py | 66 + tools/addon-sdk-1.5/python-lib/markdown/AUTHORS | 43 + tools/addon-sdk-1.5/python-lib/markdown/LICENSE | 30 + .../addon-sdk-1.5/python-lib/markdown/__init__.py | 603 +++ .../python-lib/markdown/blockparser.py | 95 + .../python-lib/markdown/blockprocessors.py | 460 ++ .../python-lib/markdown/commandline.py | 96 + .../python-lib/markdown/etree_loader.py | 33 + .../python-lib/markdown/extensions/__init__.py | 0 .../python-lib/markdown/extensions/abbr.py | 95 + .../python-lib/markdown/extensions/codehilite.py | 224 + .../python-lib/markdown/extensions/def_list.py | 104 + .../python-lib/markdown/extensions/extra.py | 49 + .../python-lib/markdown/extensions/fenced_code.py | 117 + .../python-lib/markdown/extensions/footnotes.py | 293 ++ .../python-lib/markdown/extensions/headerid.py | 195 + .../python-lib/markdown/extensions/html_tidy.py | 62 + .../python-lib/markdown/extensions/imagelinks.py | 119 + .../python-lib/markdown/extensions/meta.py | 90 + .../python-lib/markdown/extensions/rss.py | 114 + .../python-lib/markdown/extensions/tables.py | 97 + .../python-lib/markdown/extensions/toc.py | 140 + .../python-lib/markdown/extensions/wikilinks.py | 155 + tools/addon-sdk-1.5/python-lib/markdown/html4.py | 274 ++ .../python-lib/markdown/inlinepatterns.py | 371 ++ tools/addon-sdk-1.5/python-lib/markdown/odict.py | 162 + .../python-lib/markdown/postprocessors.py | 77 + .../python-lib/markdown/preprocessors.py | 214 + .../python-lib/markdown/treeprocessors.py | 329 ++ .../addon-sdk-1.5/python-lib/mozrunner/__init__.py | 654 +++ .../python-lib/mozrunner/killableprocess.py | 316 ++ tools/addon-sdk-1.5/python-lib/mozrunner/qijo.py | 166 + .../python-lib/mozrunner/winprocess.py | 383 ++ tools/addon-sdk-1.5/python-lib/mozrunner/wpk.py | 80 + .../python-lib/simplejson/LICENSE.txt | 19 + .../python-lib/simplejson/__init__.py | 376 ++ .../addon-sdk-1.5/python-lib/simplejson/decoder.py | 343 ++ .../addon-sdk-1.5/python-lib/simplejson/encoder.py | 385 ++ .../addon-sdk-1.5/python-lib/simplejson/scanner.py | 67 + tools/addon-sdk-1.5/python-lib/simplejson/tool.py | 44 + tools/closure_compiler/COPYING | 202 + tools/closure_compiler/README | 292 ++ tools/closure_compiler/compiler.jar | Bin 0 -> 5266129 bytes tools/closure_linter-2.3.4/.gitignore | 1 + tools/closure_linter-2.3.4/PKG-INFO | 10 + tools/closure_linter-2.3.4/README | 9 + .../closure_linter/__init__.py | 1 + .../closure_linter-2.3.4/closure_linter/checker.py | 142 + .../closure_linter/checkerbase.py | 308 ++ .../closure_linter/closurizednamespacesinfo.py | 498 ++ .../closurizednamespacesinfo_test.py | 451 ++ .../closure_linter/common/__init__.py | 1 + .../closure_linter/common/error.py | 65 + .../closure_linter/common/erroraccumulator.py | 46 + .../closure_linter/common/errorhandler.py | 61 + .../closure_linter/common/errorprinter.py | 203 + .../closure_linter/common/filetestcase.py | 105 + .../closure_linter/common/htmlutil.py | 170 + .../closure_linter/common/lintrunner.py | 39 + .../closure_linter/common/matcher.py | 60 + .../closure_linter/common/position.py | 126 + .../closure_linter/common/simplefileflags.py | 190 + .../closure_linter/common/tokenizer.py | 184 + .../closure_linter/common/tokens.py | 139 + .../closure_linter/ecmalintrules.py | 786 +++ .../closure_linter/ecmametadatapass.py | 521 ++ .../closure_linter/error_check.py | 87 + .../closure_linter/error_fixer.py | 414 ++ .../closure_linter/errorrules.py | 42 + .../closure_linter-2.3.4/closure_linter/errors.py | 133 + .../closure_linter/fixjsstyle.py | 57 + .../closure_linter/fixjsstyle_test.py | 188 + .../closure_linter/full_test.py | 111 + .../closure_linter-2.3.4/closure_linter/gjslint.py | 148 + .../closure_linter/indentation.py | 543 ++ .../closure_linter/javascriptlintrules.py | 487 ++ .../closure_linter/javascriptstatetracker.py | 116 + .../closure_linter/javascripttokenizer.py | 367 ++ .../closure_linter/javascripttokens.py | 147 + .../closure_linter/not_strict_test.py | 74 + .../closure_linter/requireprovidesorter.py | 262 + .../closure_linter/statetracker.py | 1007 ++++ .../closure_linter/tokenutil.py | 359 ++ tools/closure_linter-2.3.4/setup.py | 38 + tools/downloads/addon-sdk-1.3.zip | Bin 0 -> 3638182 bytes tools/downloads/addon-sdk-1.4.tar.gz | Bin 0 -> 3309260 bytes tools/jsdoc-toolkit | 1 + tools/jsdoc-toolkit-2.4.0/README.txt | 183 + tools/jsdoc-toolkit-2.4.0/app/frame.js | 33 + tools/jsdoc-toolkit-2.4.0/app/frame/Chain.js | 102 + tools/jsdoc-toolkit-2.4.0/app/frame/Dumper.js | 144 + tools/jsdoc-toolkit-2.4.0/app/frame/Hash.js | 84 + tools/jsdoc-toolkit-2.4.0/app/frame/Link.js | 173 + tools/jsdoc-toolkit-2.4.0/app/frame/Namespace.js | 10 + tools/jsdoc-toolkit-2.4.0/app/frame/Opt.js | 134 + tools/jsdoc-toolkit-2.4.0/app/frame/Reflection.js | 26 + tools/jsdoc-toolkit-2.4.0/app/frame/String.js | 93 + tools/jsdoc-toolkit-2.4.0/app/frame/Testrun.js | 129 + tools/jsdoc-toolkit-2.4.0/app/handlers/FOODOC.js | 26 + tools/jsdoc-toolkit-2.4.0/app/handlers/XMLDOC.js | 26 + .../app/handlers/XMLDOC/DomReader.js | 159 + .../app/handlers/XMLDOC/XMLDoc.js | 16 + .../app/handlers/XMLDOC/XMLParse.js | 292 ++ tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC.js | 106 + .../app/lib/JSDOC/DocComment.js | 204 + tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/DocTag.js | 294 ++ tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/JsDoc.js | 140 + tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/JsPlate.js | 109 + tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/Lang.js | 144 + tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/Parser.js | 146 + .../app/lib/JSDOC/PluginManager.js | 33 + tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/Symbol.js | 644 +++ .../jsdoc-toolkit-2.4.0/app/lib/JSDOC/SymbolSet.js | 243 + .../app/lib/JSDOC/TextStream.js | 41 + tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/Token.js | 18 + .../app/lib/JSDOC/TokenReader.js | 332 ++ .../app/lib/JSDOC/TokenStream.js | 133 + tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/Util.js | 32 + tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/Walker.js | 507 ++ tools/jsdoc-toolkit-2.4.0/app/main.js | 111 + .../app/plugins/commentSrcJson.js | 20 + .../app/plugins/frameworkPrototype.js | 16 + .../app/plugins/functionCall.js | 10 + .../app/plugins/publishSrcHilite.js | 62 + .../jsdoc-toolkit-2.4.0/app/plugins/symbolLink.js | 10 + .../app/plugins/tagParamConfig.js | 31 + .../jsdoc-toolkit-2.4.0/app/plugins/tagSynonyms.js | 43 + tools/jsdoc-toolkit-2.4.0/app/run.js | 348 ++ tools/jsdoc-toolkit-2.4.0/app/t/TestDoc.js | 144 + tools/jsdoc-toolkit-2.4.0/app/t/runner.js | 13 + tools/jsdoc-toolkit-2.4.0/app/test.js | 342 ++ tools/jsdoc-toolkit-2.4.0/app/test/addon.js | 24 + tools/jsdoc-toolkit-2.4.0/app/test/anon_inner.js | 14 + tools/jsdoc-toolkit-2.4.0/app/test/augments.js | 31 + tools/jsdoc-toolkit-2.4.0/app/test/augments2.js | 26 + tools/jsdoc-toolkit-2.4.0/app/test/borrows.js | 46 + tools/jsdoc-toolkit-2.4.0/app/test/borrows2.js | 23 + tools/jsdoc-toolkit-2.4.0/app/test/config.js | 22 + tools/jsdoc-toolkit-2.4.0/app/test/constructs.js | 18 + tools/jsdoc-toolkit-2.4.0/app/test/encoding.js | 10 + .../jsdoc-toolkit-2.4.0/app/test/encoding_other.js | 12 + tools/jsdoc-toolkit-2.4.0/app/test/event.js | 54 + tools/jsdoc-toolkit-2.4.0/app/test/exports.js | 14 + .../jsdoc-toolkit-2.4.0/app/test/functions_anon.js | 39 + .../app/test/functions_nested.js | 33 + tools/jsdoc-toolkit-2.4.0/app/test/global.js | 13 + tools/jsdoc-toolkit-2.4.0/app/test/globals.js | 25 + tools/jsdoc-toolkit-2.4.0/app/test/ignore.js | 10 + tools/jsdoc-toolkit-2.4.0/app/test/inner.js | 16 + tools/jsdoc-toolkit-2.4.0/app/test/jsdoc_test.js | 477 ++ tools/jsdoc-toolkit-2.4.0/app/test/lend.js | 33 + tools/jsdoc-toolkit-2.4.0/app/test/memberof.js | 19 + tools/jsdoc-toolkit-2.4.0/app/test/memberof2.js | 38 + tools/jsdoc-toolkit-2.4.0/app/test/memberof3.js | 33 + .../app/test/memberof_constructor.js | 17 + tools/jsdoc-toolkit-2.4.0/app/test/module.js | 17 + .../jsdoc-toolkit-2.4.0/app/test/multi_methods.js | 25 + tools/jsdoc-toolkit-2.4.0/app/test/name.js | 19 + .../app/test/namespace_nested.js | 23 + tools/jsdoc-toolkit-2.4.0/app/test/nocode.js | 13 + tools/jsdoc-toolkit-2.4.0/app/test/oblit_anon.js | 20 + tools/jsdoc-toolkit-2.4.0/app/test/overview.js | 20 + tools/jsdoc-toolkit-2.4.0/app/test/param_inline.js | 37 + .../app/test/params_optional.js | 8 + tools/jsdoc-toolkit-2.4.0/app/test/prototype.js | 17 + .../app/test/prototype_nested.js | 9 + .../app/test/prototype_oblit.js | 13 + .../app/test/prototype_oblit_constructor.js | 24 + tools/jsdoc-toolkit-2.4.0/app/test/public.js | 10 + tools/jsdoc-toolkit-2.4.0/app/test/scripts/code.js | 5 + .../app/test/scripts/notcode.txt | 5 + tools/jsdoc-toolkit-2.4.0/app/test/shared.js | 42 + tools/jsdoc-toolkit-2.4.0/app/test/shared2.js | 2 + tools/jsdoc-toolkit-2.4.0/app/test/shortcuts.js | 22 + tools/jsdoc-toolkit-2.4.0/app/test/static_this.js | 13 + tools/jsdoc-toolkit-2.4.0/app/test/synonyms.js | 31 + tools/jsdoc-toolkit-2.4.0/app/test/tosource.js | 23 + .../app/test/variable_redefine.js | 14 + tools/jsdoc-toolkit-2.4.0/changes.txt | 124 + tools/jsdoc-toolkit-2.4.0/conf/sample.conf | 31 + tools/jsdoc-toolkit-2.4.0/java/build.xml | 36 + tools/jsdoc-toolkit-2.4.0/java/build_1.4.xml | 36 + tools/jsdoc-toolkit-2.4.0/java/classes/js.jar | Bin 0 -> 819369 bytes tools/jsdoc-toolkit-2.4.0/java/src/JsDebugRun.java | 21 + tools/jsdoc-toolkit-2.4.0/java/src/JsRun.java | 21 + tools/jsdoc-toolkit-2.4.0/jsdebug.jar | Bin 0 -> 1307 bytes tools/jsdoc-toolkit-2.4.0/jsrun.jar | Bin 0 -> 1303 bytes tools/jsdoc-toolkit-2.4.0/jsrun.sh | 52 + .../templates/fiveui/allclasses.tmpl | 17 + .../templates/fiveui/allfiles.tmpl | 56 + .../templates/fiveui/class.tmpl | 649 +++ .../templates/fiveui/index.tmpl | 38 + .../templates/fiveui/publish.js | 201 + .../templates/fiveui/static/.gitignore | 2 + .../templates/fiveui/static/default.css | 162 + .../templates/fiveui/static/header.html | 2 + .../templates/fiveui/static/index.html | 19 + .../templates/fiveui/static/preludeIntro.md | 86 + .../templates/fiveui/symbol.tmpl | 35 + .../templates/jsdoc/allclasses.tmpl | 17 + .../templates/jsdoc/allfiles.tmpl | 56 + .../jsdoc-toolkit-2.4.0/templates/jsdoc/class.tmpl | 649 +++ .../jsdoc-toolkit-2.4.0/templates/jsdoc/index.tmpl | 39 + .../jsdoc-toolkit-2.4.0/templates/jsdoc/publish.js | 201 + .../templates/jsdoc/static/default.css | 162 + .../templates/jsdoc/static/header.html | 2 + .../templates/jsdoc/static/index.html | 19 + .../templates/jsdoc/symbol.tmpl | 35 + tools/seleniumChromeDrivers/lin32/chromedriver | Bin 0 -> 15841644 bytes tools/seleniumChromeDrivers/lin64/chromedriver | Bin 0 -> 16294128 bytes tools/seleniumChromeDrivers/mac/chromedriver | Bin 0 -> 12716216 bytes tools/seleniumChromeDrivers/win/chromedriver.exe | Bin 0 -> 2224640 bytes 1811 files changed, 218660 insertions(+) create mode 100644 tools/.gitignore create mode 120000 tools/addon-sdk create mode 100644 tools/addon-sdk-1.3/.version create mode 100644 tools/addon-sdk-1.3/README.txt create mode 100644 tools/addon-sdk-1.3/bin/activate create mode 100644 tools/addon-sdk-1.3/bin/activate.bat create mode 100644 tools/addon-sdk-1.3/bin/activate.ps1 create mode 100755 tools/addon-sdk-1.3/bin/cfx create mode 100644 tools/addon-sdk-1.3/bin/cfx.bat create mode 100644 tools/addon-sdk-1.3/bin/deactivate.bat create mode 100755 tools/addon-sdk-1.3/bin/integration-scripts/buildbot-run-cfx-helper create mode 100644 tools/addon-sdk-1.3/bin/integration-scripts/integration-check create mode 100644 tools/addon-sdk-1.3/examples/annotator/README.md create mode 100644 tools/addon-sdk-1.3/examples/annotator/data/annotation/annotation.html create mode 100644 tools/addon-sdk-1.3/examples/annotator/data/annotation/annotation.js create mode 100644 tools/addon-sdk-1.3/examples/annotator/data/editor/annotation-editor.html create mode 100644 tools/addon-sdk-1.3/examples/annotator/data/editor/annotation-editor.js create mode 100644 tools/addon-sdk-1.3/examples/annotator/data/jquery-1.4.2.min.js create mode 100644 tools/addon-sdk-1.3/examples/annotator/data/list/annotation-list.css create mode 100644 tools/addon-sdk-1.3/examples/annotator/data/list/annotation-list.html create mode 100644 tools/addon-sdk-1.3/examples/annotator/data/list/annotation-list.js create mode 100644 tools/addon-sdk-1.3/examples/annotator/data/matcher.js create mode 100644 tools/addon-sdk-1.3/examples/annotator/data/selector.js create mode 100644 tools/addon-sdk-1.3/examples/annotator/data/widget/pencil-off.png create mode 100644 tools/addon-sdk-1.3/examples/annotator/data/widget/pencil-on.png create mode 100644 tools/addon-sdk-1.3/examples/annotator/data/widget/widget.js create mode 100644 tools/addon-sdk-1.3/examples/annotator/lib/main.js create mode 100644 tools/addon-sdk-1.3/examples/annotator/package.json create mode 100644 tools/addon-sdk-1.3/examples/annotator/tests/test-main.js create mode 100755 tools/addon-sdk-1.3/examples/library-detector/README.md create mode 100755 tools/addon-sdk-1.3/examples/library-detector/data/icons/closure.ico create mode 100755 tools/addon-sdk-1.3/examples/library-detector/data/icons/jquery.ico create mode 100755 tools/addon-sdk-1.3/examples/library-detector/data/icons/jquery_ui.ico create mode 100755 tools/addon-sdk-1.3/examples/library-detector/data/icons/modernizr.ico create mode 100755 tools/addon-sdk-1.3/examples/library-detector/data/icons/mootools.png create mode 100755 tools/addon-sdk-1.3/examples/library-detector/data/icons/yui.ico create mode 100755 tools/addon-sdk-1.3/examples/library-detector/data/library-detector.js create mode 100755 tools/addon-sdk-1.3/examples/library-detector/data/widget.js create mode 100755 tools/addon-sdk-1.3/examples/library-detector/lib/main.js create mode 100755 tools/addon-sdk-1.3/examples/library-detector/package.json create mode 100644 tools/addon-sdk-1.3/examples/library-detector/test/test-main.js create mode 100644 tools/addon-sdk-1.3/examples/reading-data/data/mom.png create mode 100644 tools/addon-sdk-1.3/examples/reading-data/data/sample.html create mode 100644 tools/addon-sdk-1.3/examples/reading-data/lib/main.js create mode 100644 tools/addon-sdk-1.3/examples/reading-data/package.json create mode 100644 tools/addon-sdk-1.3/examples/reading-data/tests/test-main.js create mode 100644 tools/addon-sdk-1.3/examples/reddit-panel/README.md create mode 100644 tools/addon-sdk-1.3/examples/reddit-panel/data/jquery-1.4.4.min.js create mode 100644 tools/addon-sdk-1.3/examples/reddit-panel/data/panel.js create mode 100644 tools/addon-sdk-1.3/examples/reddit-panel/lib/main.js create mode 100644 tools/addon-sdk-1.3/examples/reddit-panel/package.json create mode 100644 tools/addon-sdk-1.3/examples/reddit-panel/tests/test-main.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/README.md create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/data/moz_favicon.ico create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/data/test-page-mod.html create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/data/test-page-worker.html create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/data/test-page-worker.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/data/test.html create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/docs/clipboard.md create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/docs/context-menu.md create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/docs/hotkeys.md create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/docs/notifications.md create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/docs/page-mod.md create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/docs/page-worker.md create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/docs/panel.md create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/docs/passwords.md create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/docs/private-browsing.md create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/docs/request.md create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/docs/selection.md create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/docs/self.md create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/docs/simple-storage.md create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/docs/tabs.md create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/docs/timers.md create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/docs/widget.md create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/docs/windows.md create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/lib/clipboard.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/lib/context-menu.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/lib/hotkeys.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/lib/notifications.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/lib/page-mod.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/lib/page-worker.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/lib/panel.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/lib/passwords.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/lib/private-browsing.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/lib/request.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/lib/selection.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/lib/simple-storage.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/lib/tabs.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/lib/timers.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/lib/widget.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/lib/windows.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/package.json create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/tests/pagemod-test-helpers.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/tests/test-clipboard.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/tests/test-context-menu.html create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/tests/test-context-menu.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/tests/test-hotkeys.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/tests/test-module.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/tests/test-notifications.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/tests/test-page-mod.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/tests/test-page-worker.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/tests/test-panel.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/tests/test-passwords.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/tests/test-private-browsing.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/tests/test-request.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/tests/test-selection.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/tests/test-simple-storage.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/tests/test-tabs.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/tests/test-timers.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/tests/test-widget.js create mode 100644 tools/addon-sdk-1.3/packages/addon-kit/tests/test-windows.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/README.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/data/bootstrap-remote-process.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/data/test-content-symbiont.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/api-utils.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/app-strings.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/byte-streams.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/collection.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/content.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/content/loader.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/content/proxy.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/content/symbiont.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/content/worker.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/cortex.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/cuddlefish.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/e10s.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/errors.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/es5.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/events.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/file.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/hidden-frame.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/light-traits.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/list.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/match-pattern.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/memory.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/observer-service.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/plain-text-console.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/preferences-service.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/runtime.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/securable-module.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/tab-browser.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/text-streams.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/traceback.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/traits.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/unit-test.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/unload.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/url.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/window-utils.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/xhr.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/xpcom.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/docs/xul-app.md create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/api-utils.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/app-strings.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/array.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/byte-streams.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/collection.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/content.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/content/content-proxy.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/content/loader.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/content/symbiont.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/content/worker.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/cortex.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/cuddlefish.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/dom/events.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/dom/events/keys.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/e10s.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/errors.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/events.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/events/assembler.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/file.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/find-tests-e10s-adapter.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/find-tests.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/hidden-frame.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/keyboard/hotkeys.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/keyboard/observer.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/keyboard/utils.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/light-traits.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/list.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/match-pattern.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/memory.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/observer-service.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/passwords/utils.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/plain-text-console.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/preferences-service.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/runtime.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/securable-module.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/self-e10s-adapter.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/self-maker.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/shims.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/tab-browser.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/tabs/events.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/tabs/observer.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/tabs/tab.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/tabs/utils.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/test.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/test/assert.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/text-streams.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/timer-e10s-adapter.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/timer.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/traceback.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/traits.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/traits/core.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/type.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/unit-test-finder.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/unit-test.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/unload.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/url-e10s-adapter.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/url.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/utils/data.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/utils/function.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/utils/registry.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/utils/thumbnail.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/window-utils.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/windows/dom.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/windows/loader.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/windows/observer.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/windows/tabs.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/xhr.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/xpcom.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/lib/xul-app.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/package.json create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/commonjs-test-adapter/asserts.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/e10s-samples/adapter-only-client.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/e10s-samples/adapter-only-e10s-adapter.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/e10s-samples/bug-617499-e10s-adapter.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/e10s-samples/bug-617499-main.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/e10s-samples/bug-617499.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/e10s-samples/chrome-only-module-client.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/e10s-samples/chrome-only-module.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/e10s-samples/hello-world.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/e10s-samples/superpower-client.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/e10s-samples/superpower-e10s-adapter.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/e10s-samples/superpower.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/e10s-samples/syntax-error.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/e10s-samples/thrown-exception.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/fixtures/es5.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/README.txt create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/ORACLE create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/absolute/b.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/absolute/program.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/absolute/submodule/a.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/absolute/test.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/cyclic/a.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/cyclic/b.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/cyclic/program.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/cyclic/test.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/determinism/program.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/determinism/submodule/a.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/determinism/submodule/b.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/determinism/test.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/exactExports/a.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/exactExports/b.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/exactExports/program.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/exactExports/test.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/hasOwnProperty/hasOwnProperty.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/hasOwnProperty/program.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/hasOwnProperty/test.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/hasOwnProperty/toString.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/method/a.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/method/program.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/method/test.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/missing/program.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/missing/test.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/monkeys/a.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/monkeys/b.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/monkeys/program.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/monkeys/test.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/nested/a/b/c/d.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/nested/program.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/nested/test.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/reflexive/a.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/reflexive/program.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/reflexive/test.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/relative/program.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/relative/submodule/a.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/relative/submodule/b.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/relative/test.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/transitive/a.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/transitive/b.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/transitive/c.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/transitive/program.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/interoperablejs-read-only/compliance/transitive/test.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/add.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/async1.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/async2.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/badExportAndReturn.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/badFirst.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/badSecond.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/blue.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/castor.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/cheetah.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/color.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/dupe.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/dupeNested.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/dupeSetExports.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/exportsEquals.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/green.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/lion.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/orange.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/pollux.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/red.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/setExports.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/subtract.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/tiger.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/traditional1.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/traditional2.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/modules/types/cat.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-api-utils.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-app-strings.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-array.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-byte-streams.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-collection.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-commonjs-test-adapter.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-content-loader.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-content-proxy.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-content-symbiont.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-content-worker.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-cortex.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-cuddlefish.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-dom.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-e10s-porting.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-e10s.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-errors.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-events.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-file.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-function-utils.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-globals.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-hidden-frame.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-keyboard-observer.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-keyboard-utils.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-light-traits.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-list.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-match-pattern.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-memory.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-modules.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-observer-service.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-passwords-utils.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-plain-text-console.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-preferences-service.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-registry.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-require.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-securable-module.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-self.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-set-exports.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-tab-browser.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-tab-observer.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-tab.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-text-streams.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-timer.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-traceback.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-traits-core.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-traits.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-type.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-unit-test.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-unload.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-url.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-window-loader.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-window-observer.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-window-utils.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-xhr.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-xpcom.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/test-xul-app.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/traits/assert.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/traits/descriptor-tests.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/traits/inheritance-tests.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/traits/object-tests.js create mode 100644 tools/addon-sdk-1.3/packages/api-utils/tests/traits/utils.js create mode 100644 tools/addon-sdk-1.3/packages/development-mode/README.md create mode 100644 tools/addon-sdk-1.3/packages/development-mode/docs/bootstrap.md create mode 100644 tools/addon-sdk-1.3/packages/development-mode/docs/main.md create mode 100644 tools/addon-sdk-1.3/packages/development-mode/lib/bootstrap.js create mode 100644 tools/addon-sdk-1.3/packages/development-mode/lib/main.js create mode 100644 tools/addon-sdk-1.3/packages/development-mode/package.json create mode 100644 tools/addon-sdk-1.3/packages/test-harness/README.md create mode 100644 tools/addon-sdk-1.3/packages/test-harness/docs/harness.md create mode 100644 tools/addon-sdk-1.3/packages/test-harness/docs/run-tests.md create mode 100644 tools/addon-sdk-1.3/packages/test-harness/lib/harness.js create mode 100644 tools/addon-sdk-1.3/packages/test-harness/lib/run-tests.js create mode 100644 tools/addon-sdk-1.3/packages/test-harness/package.json create mode 100644 tools/addon-sdk-1.3/packages/test-harness/tests/test-packaging.js create mode 100644 tools/addon-sdk-1.3/python-lib/.gitignore create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/__init__.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/app-extension/application.ini create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/app-extension/bootstrap.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/app-extension/components/harness.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/app-extension/install.rdf create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/bunch.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/docs/__init__.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/docs/apiparser.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/docs/apirenderer.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/docs/generate.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/docs/renderapi.readme.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/docs/webdocs.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/manifest.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/mobile-killer/bootstrap.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/mobile-killer/install.rdf create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/packaging.py create mode 100755 tools/addon-sdk-1.3/python-lib/cuddlefish/preflight.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/prefs.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/rdf.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/runner.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/templates.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/__init__.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/explicit-icon.png create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/explicit-icon64.png create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/icon.png create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/icon64.png create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/lib/bar-loader.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/lib/foo-loader.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/docs/main.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/doc/foo.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/lib/foo-loader.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/test/test-foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/docs/foo.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/lib/foo-loader.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/tests/test-foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/doc/foo.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/loader.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/test/test-foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/doc/foo.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/loader.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/test/test-foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/loader.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/doc/foo.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/test/test-foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/loader.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/doc/foo.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/test/test-foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/docs/main.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar-e10s-adapter.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/five/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/five/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/lib/misc.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/topfiles/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/one/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/one/lib/subdir/three.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/one/lib/two.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/one/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/seven/data/text.data create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/seven/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/seven/lib/unused.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/seven/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/six/lib/unused.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/six/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/six/unreachable.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/data/msg.txt create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/data/subdir/submsg.txt create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/subdir/subfile.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/unused.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/sub/foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/dev-guide-source/no_h1.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/dev-guide-source/welcome.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/static-files/another.html create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/static-files/base.html create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/static-files/index.html create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/docs/APIreference.html create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/docs/APIsample.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/doc/aardvark-feeder.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/doc/main.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/ignore_me create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/surprise.js/ignore_me_too create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/anteater_files/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/anteater_files/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/api-utils/lib/loader.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/api-utils/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/barbeque/lib/bar-module.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/barbeque/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/minimal/docs/main.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/minimal/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/minimal/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/xpi-template/components/harness.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_apiparser.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_apirenderer.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_generate.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_init.py create mode 100755 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_linker.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_manifest.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_packaging.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_preflight.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_rdf.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_runner.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_util.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_version.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_webdocs.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_xpi.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/util.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/version.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/version_comparator.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/xpi.py create mode 100644 tools/addon-sdk-1.3/python-lib/jetpack_sdk_env.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/__init__.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/blockparser.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/blockprocessors.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/commandline.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/docs/AUTHORS create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/docs/LICENSE create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/etree_loader.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/extensions/__init__.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/extensions/abbr.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/extensions/codehilite.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/extensions/def_list.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/extensions/extra.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/extensions/fenced_code.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/extensions/footnotes.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/extensions/headerid.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/extensions/html_tidy.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/extensions/imagelinks.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/extensions/meta.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/extensions/rss.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/extensions/tables.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/extensions/toc.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/extensions/wikilinks.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/html4.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/inlinepatterns.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/odict.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/postprocessors.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/preprocessors.py create mode 100644 tools/addon-sdk-1.3/python-lib/markdown/treeprocessors.py create mode 100644 tools/addon-sdk-1.3/python-lib/mozrunner/__init__.py create mode 100644 tools/addon-sdk-1.3/python-lib/mozrunner/killableprocess.py create mode 100644 tools/addon-sdk-1.3/python-lib/mozrunner/qijo.py create mode 100644 tools/addon-sdk-1.3/python-lib/mozrunner/winprocess.py create mode 100644 tools/addon-sdk-1.3/python-lib/mozrunner/wpk.py create mode 100644 tools/addon-sdk-1.3/python-lib/simplejson/__init__.py create mode 100644 tools/addon-sdk-1.3/python-lib/simplejson/decoder.py create mode 100644 tools/addon-sdk-1.3/python-lib/simplejson/encoder.py create mode 100644 tools/addon-sdk-1.3/python-lib/simplejson/scanner.py create mode 100644 tools/addon-sdk-1.3/python-lib/simplejson/tool.py create mode 100644 tools/addon-sdk-1.3/static-files/media/screenshots/widget-panel-clock.png create mode 100644 tools/addon-sdk-1.4/README.txt create mode 100644 tools/addon-sdk-1.4/bin/activate create mode 100644 tools/addon-sdk-1.4/bin/activate.bat create mode 100644 tools/addon-sdk-1.4/bin/activate.ps1 create mode 100755 tools/addon-sdk-1.4/bin/cfx create mode 100644 tools/addon-sdk-1.4/bin/cfx.bat create mode 100644 tools/addon-sdk-1.4/bin/deactivate.bat create mode 100755 tools/addon-sdk-1.4/bin/integration-scripts/buildbot-run-cfx-helper create mode 100644 tools/addon-sdk-1.4/bin/integration-scripts/integration-check create mode 100644 tools/addon-sdk-1.4/examples/annotator/README.md create mode 100644 tools/addon-sdk-1.4/examples/annotator/data/annotation/annotation.html create mode 100644 tools/addon-sdk-1.4/examples/annotator/data/annotation/annotation.js create mode 100644 tools/addon-sdk-1.4/examples/annotator/data/editor/annotation-editor.html create mode 100644 tools/addon-sdk-1.4/examples/annotator/data/editor/annotation-editor.js create mode 100644 tools/addon-sdk-1.4/examples/annotator/data/jquery-1.4.2.min.js create mode 100644 tools/addon-sdk-1.4/examples/annotator/data/list/annotation-list.css create mode 100644 tools/addon-sdk-1.4/examples/annotator/data/list/annotation-list.html create mode 100644 tools/addon-sdk-1.4/examples/annotator/data/list/annotation-list.js create mode 100644 tools/addon-sdk-1.4/examples/annotator/data/matcher.js create mode 100644 tools/addon-sdk-1.4/examples/annotator/data/selector.js create mode 100644 tools/addon-sdk-1.4/examples/annotator/data/widget/pencil-off.png create mode 100644 tools/addon-sdk-1.4/examples/annotator/data/widget/pencil-on.png create mode 100644 tools/addon-sdk-1.4/examples/annotator/data/widget/widget.js create mode 100644 tools/addon-sdk-1.4/examples/annotator/lib/main.js create mode 100644 tools/addon-sdk-1.4/examples/annotator/package.json create mode 100644 tools/addon-sdk-1.4/examples/annotator/tests/test-main.js create mode 100755 tools/addon-sdk-1.4/examples/library-detector/README.md create mode 100755 tools/addon-sdk-1.4/examples/library-detector/data/icons/closure.ico create mode 100755 tools/addon-sdk-1.4/examples/library-detector/data/icons/jquery.ico create mode 100755 tools/addon-sdk-1.4/examples/library-detector/data/icons/jquery_ui.ico create mode 100755 tools/addon-sdk-1.4/examples/library-detector/data/icons/modernizr.ico create mode 100755 tools/addon-sdk-1.4/examples/library-detector/data/icons/mootools.png create mode 100755 tools/addon-sdk-1.4/examples/library-detector/data/icons/yui.ico create mode 100755 tools/addon-sdk-1.4/examples/library-detector/data/library-detector.js create mode 100755 tools/addon-sdk-1.4/examples/library-detector/data/widget.js create mode 100755 tools/addon-sdk-1.4/examples/library-detector/lib/main.js create mode 100755 tools/addon-sdk-1.4/examples/library-detector/package.json create mode 100644 tools/addon-sdk-1.4/examples/library-detector/test/test-main.js create mode 100644 tools/addon-sdk-1.4/examples/reading-data/data/mom.png create mode 100644 tools/addon-sdk-1.4/examples/reading-data/data/sample.html create mode 100644 tools/addon-sdk-1.4/examples/reading-data/lib/main.js create mode 100644 tools/addon-sdk-1.4/examples/reading-data/package.json create mode 100644 tools/addon-sdk-1.4/examples/reading-data/tests/test-main.js create mode 100644 tools/addon-sdk-1.4/examples/reddit-panel/README.md create mode 100644 tools/addon-sdk-1.4/examples/reddit-panel/data/jquery-1.4.4.min.js create mode 100644 tools/addon-sdk-1.4/examples/reddit-panel/data/panel.js create mode 100644 tools/addon-sdk-1.4/examples/reddit-panel/lib/main.js create mode 100644 tools/addon-sdk-1.4/examples/reddit-panel/package.json create mode 100644 tools/addon-sdk-1.4/examples/reddit-panel/tests/test-main.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/README.md create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/data/moz_favicon.ico create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/data/test-page-mod.html create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/data/test-page-worker.html create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/data/test-page-worker.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/data/test-request-invalid.json create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/data/test-request.json create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/data/test-request.txt create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/data/test.html create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/docs/clipboard.md create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/docs/context-menu.md create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/docs/hotkeys.md create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/docs/notifications.md create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/docs/page-mod.md create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/docs/page-worker.md create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/docs/panel.md create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/docs/passwords.md create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/docs/private-browsing.md create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/docs/request.md create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/docs/selection.md create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/docs/self.md create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/docs/simple-prefs.md create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/docs/simple-storage.md create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/docs/tabs.md create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/docs/timers.md create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/docs/widget.md create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/docs/windows.md create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/lib/clipboard.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/lib/context-menu.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/lib/hotkeys.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/lib/notifications.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/lib/page-mod.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/lib/page-worker.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/lib/panel.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/lib/passwords.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/lib/private-browsing.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/lib/request.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/lib/selection.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/lib/simple-prefs.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/lib/simple-storage.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/lib/tabs.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/lib/timers.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/lib/widget.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/lib/windows.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/package.json create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/tests/helpers.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/tests/pagemod-test-helpers.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/tests/test-clipboard.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/tests/test-context-menu.html create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/tests/test-context-menu.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/tests/test-hotkeys.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/tests/test-module.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/tests/test-notifications.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/tests/test-page-mod.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/tests/test-page-worker.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/tests/test-panel.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/tests/test-passwords.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/tests/test-private-browsing.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/tests/test-request.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/tests/test-selection.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/tests/test-simple-prefs.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/tests/test-simple-storage.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/tests/test-tabs.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/tests/test-timers.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/tests/test-widget.js create mode 100644 tools/addon-sdk-1.4/packages/addon-kit/tests/test-windows.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/README.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/data/content-proxy.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/data/test-content-symbiont.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/data/test-httpd.txt create mode 100644 tools/addon-sdk-1.4/packages/api-utils/data/test-trusted-document.html create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/api-utils.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/app-strings.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/byte-streams.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/collection.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/content.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/content/loader.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/content/proxy.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/content/symbiont.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/content/worker.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/cortex.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/cuddlefish.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/environment.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/errors.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/es5.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/events.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/file.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/hidden-frame.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/httpd.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/light-traits.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/list.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/match-pattern.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/memory.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/namespace.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/observer-service.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/plain-text-console.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/preferences-service.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/runtime.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/securable-module.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/tab-browser.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/text-streams.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/traceback.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/traits.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/unit-test.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/unload.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/url.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/window-utils.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/xhr.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/xpcom.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/docs/xul-app.md create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/api-utils.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/app-strings.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/array.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/byte-streams.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/channel.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/collection.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/content.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/content/loader.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/content/symbiont.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/content/worker.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/cortex.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/cuddlefish.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/dom/events.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/dom/events/keys.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/env!.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/environment.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/errors.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/events.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/events/assembler.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/file.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/find-tests.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/globals!.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/hidden-frame.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/httpd.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/keyboard/hotkeys.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/keyboard/observer.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/keyboard/utils.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/light-traits.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/list.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/match-pattern.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/memory.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/namespace.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/observer-service.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/passwords/utils.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/plain-text-console.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/preferences-service.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/process.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/runtime.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/self!.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/system.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/tab-browser.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/tabs/events.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/tabs/observer.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/tabs/tab.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/tabs/utils.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/test.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/test/assert.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/text-streams.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/timer.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/traceback.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/traits.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/traits/core.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/type.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/unit-test-finder.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/unit-test.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/unload.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/url.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/utils/data.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/utils/function.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/utils/registry.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/utils/thumbnail.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/window-utils.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/windows/dom.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/windows/loader.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/windows/observer.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/windows/tabs.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/xhr.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/xpcom.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/lib/xul-app.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/package.json create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/commonjs-test-adapter/asserts.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/fixtures/es5.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/helpers.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/loader/fixture.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/add.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/async1.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/async2.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/badExportAndReturn.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/badFirst.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/badSecond.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/blue.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/castor.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/cheetah.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/color.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/dupe.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/dupeNested.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/dupeSetExports.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/exportsEquals.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/green.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/lion.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/orange.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/pollux.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/red.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/setExports.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/subtract.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/tiger.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/traditional1.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/traditional2.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/modules/types/cat.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-api-utils.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-app-strings.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-array.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-byte-streams.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-collection.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-commonjs-test-adapter.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-content-loader.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-content-proxy.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-content-symbiont.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-content-worker.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-cortex.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-dom.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-e10s-porting.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-environment.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-errors.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-events.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-file.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-function-utils.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-globals.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-hidden-frame.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-httpd.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-keyboard-observer.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-keyboard-utils.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-light-traits.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-list.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-loader.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-match-pattern.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-memory.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-modules.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-namespace.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-observer-service.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-passwords-utils.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-plain-text-console.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-preferences-service.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-registry.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-require.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-self.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-set-exports.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-tab-browser.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-tab-observer.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-tab.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-text-streams.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-timer.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-traceback.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-traits-core.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-traits.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-type.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-unit-test.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-unload.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-url.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-window-loader.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-window-observer.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-window-utils.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-xhr.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-xpcom.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/test-xul-app.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/traits/assert.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/traits/descriptor-tests.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/traits/inheritance-tests.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/traits/object-tests.js create mode 100644 tools/addon-sdk-1.4/packages/api-utils/tests/traits/utils.js create mode 100644 tools/addon-sdk-1.4/packages/development-mode/README.md create mode 100644 tools/addon-sdk-1.4/packages/development-mode/docs/bootstrap.md create mode 100644 tools/addon-sdk-1.4/packages/development-mode/docs/main.md create mode 100644 tools/addon-sdk-1.4/packages/development-mode/lib/bootstrap.js create mode 100644 tools/addon-sdk-1.4/packages/development-mode/lib/main.js create mode 100644 tools/addon-sdk-1.4/packages/development-mode/package.json create mode 100644 tools/addon-sdk-1.4/packages/test-harness/README.md create mode 100644 tools/addon-sdk-1.4/packages/test-harness/docs/harness.md create mode 100644 tools/addon-sdk-1.4/packages/test-harness/docs/run-tests.md create mode 100644 tools/addon-sdk-1.4/packages/test-harness/lib/harness.js create mode 100644 tools/addon-sdk-1.4/packages/test-harness/lib/run-tests.js create mode 100644 tools/addon-sdk-1.4/packages/test-harness/package.json create mode 100644 tools/addon-sdk-1.4/packages/test-harness/tests/test-packaging.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/__init__.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/_version.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/app-extension/application.ini create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/app-extension/bootstrap.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/app-extension/install.rdf create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/bunch.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/docs/__init__.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/docs/apiparser.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/docs/apirenderer.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/docs/generate.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/docs/renderapi.readme.md create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/docs/webdocs.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/manifest.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/mobile-killer/bootstrap.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/mobile-killer/install.rdf create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/options_defaults.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/options_xul.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/packaging.py create mode 100755 tools/addon-sdk-1.4/python-lib/cuddlefish/preflight.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/prefs.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/rdf.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/runner.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/templates.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/__init__.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/addons/simplest-test/main.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/addons/simplest-test/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/addons/simplest-test/tests/test-minimal.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/explicit-icon.png create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/explicit-icon64.png create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/lib/main.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/icon.png create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/icon64.png create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/lib/main.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/lib/main.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/lib/bar-loader.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/lib/foo-loader.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/docs/main.md create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/lib/main.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/doc/foo.md create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/lib/foo-loader.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/test/test-foo.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/docs/foo.md create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/lib/foo-loader.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/tests/test-foo.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/doc/foo.md create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/foo.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/loader.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/test/test-foo.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/doc/foo.md create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/foo.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/loader.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/test/test-foo.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/foo.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/loader.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/doc/foo.md create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/test/test-foo.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/foo.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/loader.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/doc/foo.md create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/test/test-foo.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/docs/main.md create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/lib/main.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar-e10s-adapter.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/foo.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/five/lib/main.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/five/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/lib/misc.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/topfiles/main.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/four/lib/main.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/four/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/one/lib/main.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/one/lib/subdir/three.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/one/lib/two.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/one/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/seven/data/text.data create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/seven/lib/main.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/seven/lib/unused.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/seven/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/six/lib/unused.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/six/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/six/unreachable.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/data/msg.txt create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/data/subdir/submsg.txt create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/main.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/subdir/subfile.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/unused.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/lib/main.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/main.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/sub/foo.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/three/lib/main.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/linker-files/three/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/preferences-files/packages/no-prefs/lib/main.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/preferences-files/packages/no-prefs/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/preferences-files/packages/simple-prefs/lib/main.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/preferences-files/packages/simple-prefs/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/doc/dev-guide-source/no_h1.md create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/doc/dev-guide-source/welcome.md create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/doc/static-files/another.html create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/doc/static-files/base.html create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/doc/static-files/index.html create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/docs/APIreference.html create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/docs/APIsample.md create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/packages/aardvark/doc/aardvark-feeder.md create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/packages/aardvark/doc/main.md create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/ignore_me create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/main.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/surprise.js/ignore_me_too create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/packages/aardvark/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/packages/anteater_files/lib/main.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/packages/anteater_files/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/packages/api-utils/lib/loader.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/packages/api-utils/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/packages/barbeque/lib/bar-module.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/packages/barbeque/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/packages/minimal/docs/main.md create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/packages/minimal/lib/main.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/packages/minimal/package.json create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/static-files/xpi-template/components/harness.js create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/test_apiparser.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/test_apirenderer.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/test_generate.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/test_init.py create mode 100755 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/test_linker.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/test_manifest.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/test_packaging.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/test_preflight.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/test_rdf.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/test_runner.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/test_util.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/test_version.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/test_webdocs.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/tests/test_xpi.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/util.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/version_comparator.py create mode 100644 tools/addon-sdk-1.4/python-lib/cuddlefish/xpi.py create mode 100644 tools/addon-sdk-1.4/python-lib/jetpack_sdk_env.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/__init__.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/blockparser.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/blockprocessors.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/commandline.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/docs/AUTHORS create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/docs/LICENSE create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/etree_loader.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/extensions/__init__.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/extensions/abbr.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/extensions/codehilite.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/extensions/def_list.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/extensions/extra.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/extensions/fenced_code.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/extensions/footnotes.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/extensions/headerid.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/extensions/html_tidy.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/extensions/imagelinks.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/extensions/meta.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/extensions/rss.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/extensions/tables.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/extensions/toc.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/extensions/wikilinks.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/html4.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/inlinepatterns.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/odict.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/postprocessors.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/preprocessors.py create mode 100644 tools/addon-sdk-1.4/python-lib/markdown/treeprocessors.py create mode 100644 tools/addon-sdk-1.4/python-lib/mozrunner/__init__.py create mode 100644 tools/addon-sdk-1.4/python-lib/mozrunner/killableprocess.py create mode 100644 tools/addon-sdk-1.4/python-lib/mozrunner/qijo.py create mode 100644 tools/addon-sdk-1.4/python-lib/mozrunner/winprocess.py create mode 100644 tools/addon-sdk-1.4/python-lib/mozrunner/wpk.py create mode 100644 tools/addon-sdk-1.4/python-lib/simplejson/__init__.py create mode 100644 tools/addon-sdk-1.4/python-lib/simplejson/decoder.py create mode 100644 tools/addon-sdk-1.4/python-lib/simplejson/encoder.py create mode 100644 tools/addon-sdk-1.4/python-lib/simplejson/scanner.py create mode 100644 tools/addon-sdk-1.4/python-lib/simplejson/tool.py create mode 100644 tools/addon-sdk-1.4/static-files/media/screenshots/widget-panel-clock.png create mode 100644 tools/addon-sdk-1.5/LICENSE create mode 100644 tools/addon-sdk-1.5/README create mode 100644 tools/addon-sdk-1.5/bin/activate create mode 100644 tools/addon-sdk-1.5/bin/activate.bat create mode 100644 tools/addon-sdk-1.5/bin/activate.ps1 create mode 100755 tools/addon-sdk-1.5/bin/cfx create mode 100644 tools/addon-sdk-1.5/bin/cfx.bat create mode 100644 tools/addon-sdk-1.5/bin/deactivate.bat create mode 100755 tools/addon-sdk-1.5/bin/integration-scripts/buildbot-run-cfx-helper create mode 100644 tools/addon-sdk-1.5/bin/integration-scripts/integration-check create mode 100644 tools/addon-sdk-1.5/examples/annotator/README.md create mode 100644 tools/addon-sdk-1.5/examples/annotator/data/annotation/annotation.html create mode 100644 tools/addon-sdk-1.5/examples/annotator/data/annotation/annotation.js create mode 100644 tools/addon-sdk-1.5/examples/annotator/data/editor/annotation-editor.html create mode 100644 tools/addon-sdk-1.5/examples/annotator/data/editor/annotation-editor.js create mode 100644 tools/addon-sdk-1.5/examples/annotator/data/jquery-1.4.2.min.js create mode 100644 tools/addon-sdk-1.5/examples/annotator/data/list/annotation-list.css create mode 100644 tools/addon-sdk-1.5/examples/annotator/data/list/annotation-list.html create mode 100644 tools/addon-sdk-1.5/examples/annotator/data/list/annotation-list.js create mode 100644 tools/addon-sdk-1.5/examples/annotator/data/matcher.js create mode 100644 tools/addon-sdk-1.5/examples/annotator/data/selector.js create mode 100644 tools/addon-sdk-1.5/examples/annotator/data/widget/pencil-off.png create mode 100644 tools/addon-sdk-1.5/examples/annotator/data/widget/pencil-on.png create mode 100644 tools/addon-sdk-1.5/examples/annotator/data/widget/widget.js create mode 100644 tools/addon-sdk-1.5/examples/annotator/lib/main.js create mode 100644 tools/addon-sdk-1.5/examples/annotator/package.json create mode 100644 tools/addon-sdk-1.5/examples/annotator/tests/test-main.js create mode 100755 tools/addon-sdk-1.5/examples/library-detector/README.md create mode 100755 tools/addon-sdk-1.5/examples/library-detector/data/icons/closure.ico create mode 100755 tools/addon-sdk-1.5/examples/library-detector/data/icons/jquery.ico create mode 100755 tools/addon-sdk-1.5/examples/library-detector/data/icons/jquery_ui.ico create mode 100755 tools/addon-sdk-1.5/examples/library-detector/data/icons/modernizr.ico create mode 100755 tools/addon-sdk-1.5/examples/library-detector/data/icons/mootools.png create mode 100755 tools/addon-sdk-1.5/examples/library-detector/data/icons/yui.ico create mode 100755 tools/addon-sdk-1.5/examples/library-detector/data/library-detector.js create mode 100755 tools/addon-sdk-1.5/examples/library-detector/data/widget.js create mode 100755 tools/addon-sdk-1.5/examples/library-detector/lib/main.js create mode 100755 tools/addon-sdk-1.5/examples/library-detector/package.json create mode 100644 tools/addon-sdk-1.5/examples/library-detector/test/test-main.js create mode 100644 tools/addon-sdk-1.5/examples/reading-data/data/mom.png create mode 100644 tools/addon-sdk-1.5/examples/reading-data/data/sample.html create mode 100644 tools/addon-sdk-1.5/examples/reading-data/lib/main.js create mode 100644 tools/addon-sdk-1.5/examples/reading-data/package.json create mode 100644 tools/addon-sdk-1.5/examples/reading-data/tests/test-main.js create mode 100644 tools/addon-sdk-1.5/examples/reddit-panel/README.md create mode 100644 tools/addon-sdk-1.5/examples/reddit-panel/data/jquery-1.4.4.min.js create mode 100644 tools/addon-sdk-1.5/examples/reddit-panel/data/panel.js create mode 100644 tools/addon-sdk-1.5/examples/reddit-panel/lib/main.js create mode 100644 tools/addon-sdk-1.5/examples/reddit-panel/package.json create mode 100644 tools/addon-sdk-1.5/examples/reddit-panel/tests/test-main.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/README.md create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/data/moz_favicon.ico create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/data/test-context-menu.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/data/test-page-mod.html create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/data/test-page-worker.html create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/data/test-page-worker.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/data/test.html create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/docs/clipboard.md create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/docs/context-menu.md create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/docs/hotkeys.md create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/docs/notifications.md create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/docs/page-mod.md create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/docs/page-worker.md create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/docs/panel.md create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/docs/passwords.md create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/docs/private-browsing.md create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/docs/request.md create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/docs/selection.md create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/docs/self.md create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/docs/simple-prefs.md create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/docs/simple-storage.md create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/docs/tabs.md create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/docs/timers.md create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/docs/widget.md create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/docs/windows.md create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/lib/clipboard.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/lib/context-menu.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/lib/hotkeys.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/lib/l10n.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/lib/notifications.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/lib/page-mod.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/lib/page-worker.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/lib/panel.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/lib/passwords.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/lib/private-browsing.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/lib/request.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/lib/selection.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/lib/simple-prefs.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/lib/simple-storage.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/lib/tabs.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/lib/timers.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/lib/widget.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/lib/windows.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/locale/en-GB.properties create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/locale/eo.properties create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/locale/fr-FR.properties create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/package.json create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/tests/helpers.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/tests/pagemod-test-helpers.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/tests/test-clipboard.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/tests/test-context-menu.html create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/tests/test-context-menu.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/tests/test-hotkeys.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/tests/test-l10n.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/tests/test-module.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/tests/test-notifications.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/tests/test-page-mod.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/tests/test-page-worker.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/tests/test-panel.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/tests/test-passwords.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/tests/test-private-browsing.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/tests/test-request.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/tests/test-selection.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/tests/test-simple-prefs.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/tests/test-simple-storage.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/tests/test-tabs.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/tests/test-timers.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/tests/test-widget.js create mode 100644 tools/addon-sdk-1.5/packages/addon-kit/tests/test-windows.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/README.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/data/content-proxy.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/data/test-content-symbiont.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/data/test-message-manager.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/data/test-trusted-document.html create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/api-utils.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/app-strings.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/base.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/byte-streams.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/collection.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/content.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/content/loader.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/content/proxy.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/content/symbiont.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/content/worker.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/cortex.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/cuddlefish.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/environment.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/errors.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/events.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/file.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/hidden-frame.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/httpd.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/light-traits.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/list.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/match-pattern.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/memory.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/message-manager.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/namespace.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/observer-service.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/plain-text-console.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/preferences-service.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/runtime.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/sandbox.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/tab-browser.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/text-streams.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/traceback.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/traits.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/unit-test.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/unload.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/url.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/window-utils.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/xhr.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/xpcom.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/docs/xul-app.md create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/api-utils.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/app-strings.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/array.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/base.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/byte-streams.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/channel.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/collection.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/content.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/content/loader.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/content/symbiont.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/content/worker.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/cortex.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/cuddlefish.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/dom/events.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/dom/events/keys.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/env!.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/environment.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/errors.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/events.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/events/assembler.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/file.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/find-tests.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/globals!.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/hidden-frame.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/httpd.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/keyboard/hotkeys.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/keyboard/observer.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/keyboard/utils.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/light-traits.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/list.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/match-pattern.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/memory.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/message-manager.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/namespace.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/observer-service.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/passwords/utils.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/plain-text-console.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/preferences-service.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/process.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/runtime.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/sandbox.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/self!.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/system.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/tab-browser.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/tabs/events.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/tabs/observer.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/tabs/tab.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/tabs/utils.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/test.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/test/assert.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/text-streams.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/timer.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/traceback.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/traits.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/traits/core.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/type.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/unit-test-finder.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/unit-test.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/unload.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/url.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/utils/data.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/utils/function.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/utils/object.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/utils/registry.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/utils/thumbnail.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/window-utils.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/windows/dom.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/windows/loader.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/windows/observer.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/windows/tabs.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/xhr.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/xpcom.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/lib/xul-app.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/package.json create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/commonjs-test-adapter/asserts.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/fixtures/es5.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/fixtures/sandbox-complex-character.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/fixtures/sandbox-normal.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/helpers.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/loader/fixture.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/add.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/async1.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/async2.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/badExportAndReturn.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/badFirst.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/badSecond.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/blue.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/castor.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/cheetah.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/color.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/dupe.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/dupeNested.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/dupeSetExports.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/exportsEquals.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/green.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/lion.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/orange.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/pollux.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/red.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/setExports.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/subtract.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/tiger.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/traditional1.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/traditional2.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/modules/types/cat.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-api-utils.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-app-strings.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-array.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-base.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-byte-streams.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-collection.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-commonjs-test-adapter.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-content-loader.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-content-proxy.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-content-symbiont.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-content-worker.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-cortex.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-dom.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-environment.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-errors.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-events.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-file.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-function-utils.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-globals.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-hidden-frame.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-httpd.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-keyboard-observer.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-keyboard-utils.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-light-traits.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-list.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-loader.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-match-pattern.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-memory.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-message-manager.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-modules.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-namespace.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-observer-service.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-passwords-utils.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-plain-text-console.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-preferences-service.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-registry.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-require.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-sandbox.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-self.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-set-exports.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-tab-browser.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-tab-observer.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-tab.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-text-streams.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-timer.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-traceback.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-traits-core.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-traits.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-type.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-unit-test.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-unload.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-url.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-window-loader.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-window-observer.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-window-utils.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-xhr.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-xpcom.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/test-xul-app.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/traits/assert.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/traits/descriptor-tests.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/traits/inheritance-tests.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/traits/object-tests.js create mode 100644 tools/addon-sdk-1.5/packages/api-utils/tests/traits/utils.js create mode 100644 tools/addon-sdk-1.5/packages/test-harness/README.md create mode 100644 tools/addon-sdk-1.5/packages/test-harness/docs/harness.md create mode 100644 tools/addon-sdk-1.5/packages/test-harness/docs/run-tests.md create mode 100644 tools/addon-sdk-1.5/packages/test-harness/lib/harness.js create mode 100644 tools/addon-sdk-1.5/packages/test-harness/lib/run-tests.js create mode 100644 tools/addon-sdk-1.5/packages/test-harness/package.json create mode 100644 tools/addon-sdk-1.5/packages/test-harness/tests/test-packaging.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/__init__.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/_version.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/app-extension/application.ini create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/app-extension/bootstrap.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/app-extension/install.rdf create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/bunch.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/docs/__init__.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/docs/apiparser.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/docs/apirenderer.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/docs/generate.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/docs/renderapi.readme.md create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/docs/webdocs.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/manifest.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/mobile-killer/bootstrap.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/mobile-killer/install.rdf create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/options_defaults.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/options_xul.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/packaging.py create mode 100755 tools/addon-sdk-1.5/python-lib/cuddlefish/preflight.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/prefs.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/property_parser.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/rdf.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/runner.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/templates.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/__init__.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/addons/simplest-test/main.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/addons/simplest-test/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/addons/simplest-test/tests/test-minimal.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/explicit-icon.png create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/explicit-icon64.png create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/lib/main.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/icon.png create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/icon64.png create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/lib/main.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/lib/main.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/lib/bar-loader.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/lib/foo-loader.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/docs/main.md create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/lib/main.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/doc/foo.md create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/lib/foo-loader.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/test/test-foo.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/docs/foo.md create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/lib/foo-loader.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/tests/test-foo.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/doc/foo.md create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/foo.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/loader.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/test/test-foo.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-652227-files/packages/default-locale/locale/emptyFolder create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-652227-files/packages/default-locale/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/doc/foo.md create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/foo.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/loader.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/test/test-foo.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/foo.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/loader.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/doc/foo.md create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/test/test-foo.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/foo.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/loader.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/doc/foo.md create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/test/test-foo.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/docs/main.md create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/lib/main.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-715340-files/pkg-1-pack/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-715340-files/pkg-2-unpack/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/bug-715340-files/pkg-3-pack/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar-e10s-adapter.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/foo.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/five/lib/main.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/five/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/lib/misc.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/topfiles/main.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/four/lib/main.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/four/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/one/lib/main.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/one/lib/subdir/three.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/one/lib/two.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/one/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/seven/data/text.data create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/seven/lib/main.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/seven/lib/unused.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/seven/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/six/lib/unused.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/six/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/six/unreachable.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/data/msg.txt create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/data/subdir/submsg.txt create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/main.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/subdir/subfile.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/unused.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/locale/fr-FR.properties create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/lib/main.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/locale/fr-FR.properties create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/main.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/sub/foo.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/locale/fr-FR.properties create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/three/lib/main.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/three/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/three/tests/nontest.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/three/tests/test-one.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/linker-files/three/tests/test-two.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/preferences-files/packages/no-prefs/lib/main.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/preferences-files/packages/no-prefs/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/preferences-files/packages/simple-prefs/lib/main.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/preferences-files/packages/simple-prefs/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/doc/dev-guide-source/no_h1.md create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/doc/dev-guide-source/welcome.md create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/doc/static-files/another.html create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/doc/static-files/base.html create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/doc/static-files/index.html create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/docs/APIreference.html create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/docs/APIsample.md create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/packages/aardvark/doc/aardvark-feeder.md create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/packages/aardvark/doc/main.md create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/ignore_me create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/main.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/surprise.js/ignore_me_too create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/packages/aardvark/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/packages/anteater_files/lib/main.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/packages/anteater_files/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/packages/api-utils/lib/loader.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/packages/api-utils/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/packages/barbeque/lib/bar-module.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/packages/barbeque/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/packages/minimal/docs/main.md create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/packages/minimal/lib/main.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/packages/minimal/package.json create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/static-files/xpi-template/components/harness.js create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/test_apiparser.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/test_apirenderer.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/test_generate.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/test_init.py create mode 100755 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/test_linker.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/test_manifest.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/test_packaging.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/test_preflight.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/test_property_parser.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/test_rdf.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/test_runner.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/test_util.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/test_version.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/test_webdocs.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/tests/test_xpi.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/util.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/version_comparator.py create mode 100644 tools/addon-sdk-1.5/python-lib/cuddlefish/xpi.py create mode 100644 tools/addon-sdk-1.5/python-lib/jetpack_sdk_env.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/AUTHORS create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/LICENSE create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/__init__.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/blockparser.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/blockprocessors.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/commandline.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/etree_loader.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/extensions/__init__.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/extensions/abbr.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/extensions/codehilite.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/extensions/def_list.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/extensions/extra.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/extensions/fenced_code.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/extensions/footnotes.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/extensions/headerid.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/extensions/html_tidy.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/extensions/imagelinks.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/extensions/meta.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/extensions/rss.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/extensions/tables.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/extensions/toc.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/extensions/wikilinks.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/html4.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/inlinepatterns.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/odict.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/postprocessors.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/preprocessors.py create mode 100644 tools/addon-sdk-1.5/python-lib/markdown/treeprocessors.py create mode 100644 tools/addon-sdk-1.5/python-lib/mozrunner/__init__.py create mode 100644 tools/addon-sdk-1.5/python-lib/mozrunner/killableprocess.py create mode 100644 tools/addon-sdk-1.5/python-lib/mozrunner/qijo.py create mode 100644 tools/addon-sdk-1.5/python-lib/mozrunner/winprocess.py create mode 100644 tools/addon-sdk-1.5/python-lib/mozrunner/wpk.py create mode 100644 tools/addon-sdk-1.5/python-lib/simplejson/LICENSE.txt create mode 100644 tools/addon-sdk-1.5/python-lib/simplejson/__init__.py create mode 100644 tools/addon-sdk-1.5/python-lib/simplejson/decoder.py create mode 100644 tools/addon-sdk-1.5/python-lib/simplejson/encoder.py create mode 100644 tools/addon-sdk-1.5/python-lib/simplejson/scanner.py create mode 100644 tools/addon-sdk-1.5/python-lib/simplejson/tool.py create mode 100644 tools/closure_compiler/COPYING create mode 100644 tools/closure_compiler/README create mode 100644 tools/closure_compiler/compiler.jar create mode 100644 tools/closure_linter-2.3.4/.gitignore create mode 100644 tools/closure_linter-2.3.4/PKG-INFO create mode 100644 tools/closure_linter-2.3.4/README create mode 100755 tools/closure_linter-2.3.4/closure_linter/__init__.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/checker.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/checkerbase.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/closurizednamespacesinfo.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/closurizednamespacesinfo_test.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/common/__init__.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/common/error.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/common/erroraccumulator.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/common/errorhandler.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/common/errorprinter.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/common/filetestcase.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/common/htmlutil.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/common/lintrunner.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/common/matcher.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/common/position.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/common/simplefileflags.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/common/tokenizer.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/common/tokens.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/ecmalintrules.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/ecmametadatapass.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/error_check.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/error_fixer.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/errorrules.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/errors.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/fixjsstyle.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/fixjsstyle_test.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/full_test.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/gjslint.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/indentation.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/javascriptlintrules.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/javascriptstatetracker.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/javascripttokenizer.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/javascripttokens.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/not_strict_test.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/requireprovidesorter.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/statetracker.py create mode 100755 tools/closure_linter-2.3.4/closure_linter/tokenutil.py create mode 100755 tools/closure_linter-2.3.4/setup.py create mode 100644 tools/downloads/addon-sdk-1.3.zip create mode 100644 tools/downloads/addon-sdk-1.4.tar.gz create mode 120000 tools/jsdoc-toolkit create mode 100644 tools/jsdoc-toolkit-2.4.0/README.txt create mode 100644 tools/jsdoc-toolkit-2.4.0/app/frame.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/frame/Chain.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/frame/Dumper.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/frame/Hash.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/frame/Link.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/frame/Namespace.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/frame/Opt.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/frame/Reflection.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/frame/String.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/frame/Testrun.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/handlers/FOODOC.js create mode 100755 tools/jsdoc-toolkit-2.4.0/app/handlers/XMLDOC.js create mode 100755 tools/jsdoc-toolkit-2.4.0/app/handlers/XMLDOC/DomReader.js create mode 100755 tools/jsdoc-toolkit-2.4.0/app/handlers/XMLDOC/XMLDoc.js create mode 100755 tools/jsdoc-toolkit-2.4.0/app/handlers/XMLDOC/XMLParse.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/DocComment.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/DocTag.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/JsDoc.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/JsPlate.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/Lang.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/Parser.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/PluginManager.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/Symbol.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/SymbolSet.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/TextStream.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/Token.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/TokenReader.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/TokenStream.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/Util.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/lib/JSDOC/Walker.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/main.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/plugins/commentSrcJson.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/plugins/frameworkPrototype.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/plugins/functionCall.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/plugins/publishSrcHilite.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/plugins/symbolLink.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/plugins/tagParamConfig.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/plugins/tagSynonyms.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/run.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/t/TestDoc.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/t/runner.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/addon.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/anon_inner.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/augments.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/augments2.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/borrows.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/borrows2.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/config.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/constructs.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/encoding.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/encoding_other.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/event.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/exports.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/functions_anon.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/functions_nested.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/global.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/globals.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/ignore.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/inner.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/jsdoc_test.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/lend.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/memberof.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/memberof2.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/memberof3.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/memberof_constructor.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/module.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/multi_methods.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/name.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/namespace_nested.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/nocode.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/oblit_anon.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/overview.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/param_inline.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/params_optional.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/prototype.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/prototype_nested.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/prototype_oblit.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/prototype_oblit_constructor.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/public.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/scripts/code.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/scripts/notcode.txt create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/shared.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/shared2.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/shortcuts.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/static_this.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/synonyms.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/tosource.js create mode 100644 tools/jsdoc-toolkit-2.4.0/app/test/variable_redefine.js create mode 100644 tools/jsdoc-toolkit-2.4.0/changes.txt create mode 100644 tools/jsdoc-toolkit-2.4.0/conf/sample.conf create mode 100644 tools/jsdoc-toolkit-2.4.0/java/build.xml create mode 100644 tools/jsdoc-toolkit-2.4.0/java/build_1.4.xml create mode 100644 tools/jsdoc-toolkit-2.4.0/java/classes/js.jar create mode 100755 tools/jsdoc-toolkit-2.4.0/java/src/JsDebugRun.java create mode 100644 tools/jsdoc-toolkit-2.4.0/java/src/JsRun.java create mode 100644 tools/jsdoc-toolkit-2.4.0/jsdebug.jar create mode 100644 tools/jsdoc-toolkit-2.4.0/jsrun.jar create mode 100755 tools/jsdoc-toolkit-2.4.0/jsrun.sh create mode 100644 tools/jsdoc-toolkit-2.4.0/templates/fiveui/allclasses.tmpl create mode 100644 tools/jsdoc-toolkit-2.4.0/templates/fiveui/allfiles.tmpl create mode 100644 tools/jsdoc-toolkit-2.4.0/templates/fiveui/class.tmpl create mode 100644 tools/jsdoc-toolkit-2.4.0/templates/fiveui/index.tmpl create mode 100644 tools/jsdoc-toolkit-2.4.0/templates/fiveui/publish.js create mode 100644 tools/jsdoc-toolkit-2.4.0/templates/fiveui/static/.gitignore create mode 100644 tools/jsdoc-toolkit-2.4.0/templates/fiveui/static/default.css create mode 100644 tools/jsdoc-toolkit-2.4.0/templates/fiveui/static/header.html create mode 100644 tools/jsdoc-toolkit-2.4.0/templates/fiveui/static/index.html create mode 100644 tools/jsdoc-toolkit-2.4.0/templates/fiveui/static/preludeIntro.md create mode 100644 tools/jsdoc-toolkit-2.4.0/templates/fiveui/symbol.tmpl create mode 100644 tools/jsdoc-toolkit-2.4.0/templates/jsdoc/allclasses.tmpl create mode 100644 tools/jsdoc-toolkit-2.4.0/templates/jsdoc/allfiles.tmpl create mode 100644 tools/jsdoc-toolkit-2.4.0/templates/jsdoc/class.tmpl create mode 100644 tools/jsdoc-toolkit-2.4.0/templates/jsdoc/index.tmpl create mode 100644 tools/jsdoc-toolkit-2.4.0/templates/jsdoc/publish.js create mode 100644 tools/jsdoc-toolkit-2.4.0/templates/jsdoc/static/default.css create mode 100644 tools/jsdoc-toolkit-2.4.0/templates/jsdoc/static/header.html create mode 100644 tools/jsdoc-toolkit-2.4.0/templates/jsdoc/static/index.html create mode 100644 tools/jsdoc-toolkit-2.4.0/templates/jsdoc/symbol.tmpl create mode 100755 tools/seleniumChromeDrivers/lin32/chromedriver create mode 100755 tools/seleniumChromeDrivers/lin64/chromedriver create mode 100755 tools/seleniumChromeDrivers/mac/chromedriver create mode 100755 tools/seleniumChromeDrivers/win/chromedriver.exe (limited to 'tools') diff --git a/tools/.gitignore b/tools/.gitignore new file mode 100644 index 0000000..0e6e7ab --- /dev/null +++ b/tools/.gitignore @@ -0,0 +1 @@ +addon-sdk-*/doc diff --git a/tools/addon-sdk b/tools/addon-sdk new file mode 120000 index 0000000..23c4d91 --- /dev/null +++ b/tools/addon-sdk @@ -0,0 +1 @@ +addon-sdk-1.5/ \ No newline at end of file diff --git a/tools/addon-sdk-1.3/.version b/tools/addon-sdk-1.3/.version new file mode 100644 index 0000000..7e32cd5 --- /dev/null +++ b/tools/addon-sdk-1.3/.version @@ -0,0 +1 @@ +1.3 diff --git a/tools/addon-sdk-1.3/README.txt b/tools/addon-sdk-1.3/README.txt new file mode 100644 index 0000000..4cd0648 --- /dev/null +++ b/tools/addon-sdk-1.3/README.txt @@ -0,0 +1,30 @@ +Add-on SDK README +================== + +Before proceeding, please make sure you've installed Python 2.5 +or 2.6 if it's not already on your system: + + http://python.org/download/ + +Note that Python 3.0 and 3.1 are not supported in this release. + +For Windows users, MozillaBuild (https://wiki.mozilla.org/MozillaBuild) +will install the correct version of Python and the MSYS package, which +will make it easier to work with the SDK. + +To get started, first enter the same directory that this README file +is in (the SDK's root directory) using a shell program. On Unix systems +or on Windows with MSYS, you can execute the following command: + + source bin/activate + +Windows users using cmd.exe should instead run: + + bin\activate.bat + +Then run: + + cfx docs + +This should start a documentation server and open a web browser +with further instructions. diff --git a/tools/addon-sdk-1.3/bin/activate b/tools/addon-sdk-1.3/bin/activate new file mode 100644 index 0000000..83fef4d --- /dev/null +++ b/tools/addon-sdk-1.3/bin/activate @@ -0,0 +1,82 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + +deactivate () { + if [ -n "$_OLD_VIRTUAL_PATH" ] ; then + PATH="$_OLD_VIRTUAL_PATH" + export PATH + unset _OLD_VIRTUAL_PATH + fi + + # This should detect bash and zsh, which have a hash command that must + # be called to get it to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then + hash -r + fi + + if [ -n "$_OLD_VIRTUAL_PS1" ] ; then + PS1="$_OLD_VIRTUAL_PS1" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + if [ -n "$_OLD_PYTHONPATH" ] ; then + PYTHONPATH="$_OLD_PYTHONPATH" + export PYTHONPATH + unset _OLD_PYTHONPATH + fi + + unset CUDDLEFISH_ROOT + + unset VIRTUAL_ENV + if [ ! "$1" = "nondestructive" ] ; then + # Self destruct! + unset deactivate + fi +} + +# unset irrelavent variables +deactivate nondestructive + +_OLD_PYTHONPATH="$PYTHONPATH" +_OLD_VIRTUAL_PATH="$PATH" + +VIRTUAL_ENV="`pwd`" + +if [ "x$OSTYPE" = "xmsys" ] ; then + CUDDLEFISH_ROOT="`pwd -W | sed s,/,\\\\\\\\,g`" + PATH="`pwd`/bin:$PATH" + # msys will convert any env vars with PATH in it to use msys + # form and will unconvert before launching + PYTHONPATH="`pwd -W`/python-lib;$PYTHONPATH" +else + CUDDLEFISH_ROOT="$VIRTUAL_ENV" + PYTHONPATH="$VIRTUAL_ENV/python-lib:$PYTHONPATH" + PATH="$VIRTUAL_ENV/bin:$PATH" +fi + +VIRTUAL_ENV="`pwd`" + +export CUDDLEFISH_ROOT +export PYTHONPATH +export PATH + +_OLD_VIRTUAL_PS1="$PS1" +if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1" +else + PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1" +fi +export PS1 + +# This should detect bash and zsh, which have a hash command that must +# be called to get it to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then + hash -r +fi + +python -c "from jetpack_sdk_env import welcome; welcome()" diff --git a/tools/addon-sdk-1.3/bin/activate.bat b/tools/addon-sdk-1.3/bin/activate.bat new file mode 100644 index 0000000..20b80bc --- /dev/null +++ b/tools/addon-sdk-1.3/bin/activate.bat @@ -0,0 +1,139 @@ +@echo off +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 + +SET PYTHONVERSION=2.4 +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 + +SET PYTHONVERSION=2.4 +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= +cd "%VIRTUAL_ENV%" +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/tools/addon-sdk-1.3/bin/activate.ps1 b/tools/addon-sdk-1.3/bin/activate.ps1 new file mode 100644 index 0000000..7bbef70 --- /dev/null +++ b/tools/addon-sdk-1.3/bin/activate.ps1 @@ -0,0 +1,95 @@ +$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/tools/addon-sdk-1.3/bin/cfx b/tools/addon-sdk-1.3/bin/cfx new file mode 100755 index 0000000..d7155d4 --- /dev/null +++ b/tools/addon-sdk-1.3/bin/cfx @@ -0,0 +1,29 @@ +#! /usr/bin/env python + +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.append(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/tools/addon-sdk-1.3/bin/cfx.bat b/tools/addon-sdk-1.3/bin/cfx.bat new file mode 100644 index 0000000..eb35853 --- /dev/null +++ b/tools/addon-sdk-1.3/bin/cfx.bat @@ -0,0 +1,3 @@ +@echo off + +python "%VIRTUAL_ENV%\bin\cfx" %* diff --git a/tools/addon-sdk-1.3/bin/deactivate.bat b/tools/addon-sdk-1.3/bin/deactivate.bat new file mode 100644 index 0000000..885e951 --- /dev/null +++ b/tools/addon-sdk-1.3/bin/deactivate.bat @@ -0,0 +1,20 @@ +@echo off + +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/tools/addon-sdk-1.3/bin/integration-scripts/buildbot-run-cfx-helper b/tools/addon-sdk-1.3/bin/integration-scripts/buildbot-run-cfx-helper new file mode 100755 index 0000000..6d1fc14 --- /dev/null +++ b/tools/addon-sdk-1.3/bin/integration-scripts/buildbot-run-cfx-helper @@ -0,0 +1,10 @@ +#!/bin/bash + +source ./bin/activate +if [ type -P xvfb-run ] +then + xvfb-run cfx $* +else + cfx $* +fi +deactivate diff --git a/tools/addon-sdk-1.3/bin/integration-scripts/integration-check b/tools/addon-sdk-1.3/bin/integration-scripts/integration-check new file mode 100644 index 0000000..7ac0e6e --- /dev/null +++ b/tools/addon-sdk-1.3/bin/integration-scripts/integration-check @@ -0,0 +1,360 @@ +#!/usr/bin/env python +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/tools/addon-sdk-1.3/examples/annotator/README.md b/tools/addon-sdk-1.3/examples/annotator/README.md new file mode 100644 index 0000000..cfd7fb1 --- /dev/null +++ b/tools/addon-sdk-1.3/examples/annotator/README.md @@ -0,0 +1,42 @@ +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/tools/addon-sdk-1.3/examples/annotator/data/annotation/annotation.html b/tools/addon-sdk-1.3/examples/annotator/data/annotation/annotation.html new file mode 100644 index 0000000..d642d0b --- /dev/null +++ b/tools/addon-sdk-1.3/examples/annotator/data/annotation/annotation.html @@ -0,0 +1,27 @@ + + + + + Annotation + + + + + + +
+
+ + + diff --git a/tools/addon-sdk-1.3/examples/annotator/data/annotation/annotation.js b/tools/addon-sdk-1.3/examples/annotator/data/annotation/annotation.js new file mode 100644 index 0000000..169ed0e --- /dev/null +++ b/tools/addon-sdk-1.3/examples/annotator/data/annotation/annotation.js @@ -0,0 +1,7 @@ +/* +Initialize annotation content. +*/ + +self.on('message', function(message) { + $('#annotation').text(message); +}); diff --git a/tools/addon-sdk-1.3/examples/annotator/data/editor/annotation-editor.html b/tools/addon-sdk-1.3/examples/annotator/data/editor/annotation-editor.html new file mode 100644 index 0000000..760d1f6 --- /dev/null +++ b/tools/addon-sdk-1.3/examples/annotator/data/editor/annotation-editor.html @@ -0,0 +1,35 @@ + + + + + Annotation + + + + + + + + + + + diff --git a/tools/addon-sdk-1.3/examples/annotator/data/editor/annotation-editor.js b/tools/addon-sdk-1.3/examples/annotator/data/editor/annotation-editor.js new file mode 100644 index 0000000..5a762fc --- /dev/null +++ b/tools/addon-sdk-1.3/examples/annotator/data/editor/annotation-editor.js @@ -0,0 +1,19 @@ +/* +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/tools/addon-sdk-1.3/examples/annotator/data/jquery-1.4.2.min.js b/tools/addon-sdk-1.3/examples/annotator/data/jquery-1.4.2.min.js new file mode 100644 index 0000000..7c24308 --- /dev/null +++ b/tools/addon-sdk-1.3/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/tools/addon-sdk-1.3/examples/annotator/data/list/annotation-list.css b/tools/addon-sdk-1.3/examples/annotator/data/list/annotation-list.css new file mode 100644 index 0000000..6a2b2ee --- /dev/null +++ b/tools/addon-sdk-1.3/examples/annotator/data/list/annotation-list.css @@ -0,0 +1,36 @@ +#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/tools/addon-sdk-1.3/examples/annotator/data/list/annotation-list.html b/tools/addon-sdk-1.3/examples/annotator/data/list/annotation-list.html new file mode 100644 index 0000000..6b97153 --- /dev/null +++ b/tools/addon-sdk-1.3/examples/annotator/data/list/annotation-list.html @@ -0,0 +1,22 @@ + + + + Saved annotations + + + + +
+
+ +
+
+ +
+
+
+
+ + + + diff --git a/tools/addon-sdk-1.3/examples/annotator/data/list/annotation-list.js b/tools/addon-sdk-1.3/examples/annotator/data/list/annotation-list.js new file mode 100644 index 0000000..1a0e8f5 --- /dev/null +++ b/tools/addon-sdk-1.3/examples/annotator/data/list/annotation-list.js @@ -0,0 +1,27 @@ +/* +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/tools/addon-sdk-1.3/examples/annotator/data/matcher.js b/tools/addon-sdk-1.3/examples/annotator/data/matcher.js new file mode 100644 index 0000000..5dbd967 --- /dev/null +++ b/tools/addon-sdk-1.3/examples/annotator/data/matcher.js @@ -0,0 +1,46 @@ +/* +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); + annotationAnchor = $(annotationAnchorAncestor).parent().find( + ':contains(' + annotation.anchorText + ')').last(); + $(annotationAnchor).addClass('annotated'); + $(annotationAnchor).attr('annotation', annotation.annotationText); +} diff --git a/tools/addon-sdk-1.3/examples/annotator/data/selector.js b/tools/addon-sdk-1.3/examples/annotator/data/selector.js new file mode 100644 index 0000000..4c42bb1 --- /dev/null +++ b/tools/addon-sdk-1.3/examples/annotator/data/selector.js @@ -0,0 +1,56 @@ +/* +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(); + } +}); + +$('*').mouseenter(function() { + if (!active || $(this).hasClass('annotated')) { + return; + } + resetMatchedElement(); + ancestor = $(this).closest("[id]"); + matchedElement = $(this).first(); + originalBgColor = $(matchedElement).css('background-color'); + $(matchedElement).css('background-color', 'yellow'); + $(matchedElement).bind('click.annotator', function(event) { + event.stopPropagation(); + event.preventDefault(); + self.port.emit('show', + [ + document.location.toString(), + $(ancestor).attr("id"), + $(matchedElement).text() + ] + ); + }); +}); + +$('*').mouseout(function() { + resetMatchedElement(); +}); diff --git a/tools/addon-sdk-1.3/examples/annotator/data/widget/pencil-off.png b/tools/addon-sdk-1.3/examples/annotator/data/widget/pencil-off.png new file mode 100644 index 0000000..b14d961 Binary files /dev/null and b/tools/addon-sdk-1.3/examples/annotator/data/widget/pencil-off.png differ diff --git a/tools/addon-sdk-1.3/examples/annotator/data/widget/pencil-on.png b/tools/addon-sdk-1.3/examples/annotator/data/widget/pencil-on.png new file mode 100644 index 0000000..71a609f Binary files /dev/null and b/tools/addon-sdk-1.3/examples/annotator/data/widget/pencil-on.png differ diff --git a/tools/addon-sdk-1.3/examples/annotator/data/widget/widget.js b/tools/addon-sdk-1.3/examples/annotator/data/widget/widget.js new file mode 100644 index 0000000..f9714db --- /dev/null +++ b/tools/addon-sdk-1.3/examples/annotator/data/widget/widget.js @@ -0,0 +1,13 @@ +/* +Listen for left and right click events and send the corresponding message +to the content script. +*/ + +this.addEventListener('click', function(event) { + if(event.button == 0 && event.shiftKey == false) + self.port.emit('left-click'); + + if(event.button == 2 || (event.button == 0 && event.shiftKey == true)) + self.port.emit('right-click'); + event.preventDefault(); +}, true); diff --git a/tools/addon-sdk-1.3/examples/annotator/lib/main.js b/tools/addon-sdk-1.3/examples/annotator/lib/main.js new file mode 100644 index 0000000..9c9fba3 --- /dev/null +++ b/tools/addon-sdk-1.3/examples/annotator/lib/main.js @@ -0,0 +1,290 @@ +var widgets = require('widget'); +var pageMod = require('page-mod'); +var data = require('self').data; +var panels = require('panel'); +var simpleStorage = require('simple-storage'); +var notifications = require("notifications"); +var privateBrowsing = require('private-browsing'); + +/* +Global variables +* Boolean to indicate whether the add-on is switched on or not +* Array for all workers associated with the 'selector' page mod +* Array for all workers associated with the 'matcher' page mod +*/ +var annotatorIsOn = false; +var selectors = []; +var matchers = []; + +if (!simpleStorage.storage.annotations) + simpleStorage.storage.annotations = []; + +/* +Update the matchers: call this whenever the set of annotations changes +*/ +function updateMatchers() { + matchers.forEach(function (matcher) { + matcher.postMessage(simpleStorage.storage.annotations); + }); +} + +/* +You can add annotations iff the add-on is on AND private browsing is off +*/ +function canEnterAnnotations() { + return (annotatorIsOn && !privateBrowsing.isActive); +} + +/* +Constructor for an Annotation object +*/ +function Annotation(annotationText, anchor) { + this.annotationText = annotationText; + this.url = anchor[0]; + this.ancestorId = anchor[1]; + this.anchorText = anchor[2]; +} + +/* +Function to deal with a new annotation. +Create a new annotation object, store it, and +notify all the annotators of the change. +*/ +function handleNewAnnotation(annotationText, anchor) { + var newAnnotation = new Annotation(annotationText, anchor); + simpleStorage.storage.annotations.push(newAnnotation); + updateMatchers(); +} + +/* +Function to tell the selector page mod that the add-on has become (in)active +*/ +function activateSelectors() { + selectors.forEach( + function (selector) { + selector.postMessage(canEnterAnnotations()); + }); +} + +/* +Toggle activation: update the on/off state and notify the selectors. +Toggling activation is disabled when private browsing is on. +*/ +function toggleActivation() { + if (privateBrowsing.isActive) { + return false; + } + annotatorIsOn = !annotatorIsOn; + activateSelectors(); + return canEnterAnnotations(); +} + +function detachWorker(worker, workerArray) { + var index = workerArray.indexOf(worker); + if(index != -1) { + workerArray.splice(index, 1); + } +} + +exports.main = function() { + +/* +The widget provides a mechanism to switch the selector on or off, and to +view the list of annotations. + +The selector is switched on/off with a left-click, and the list of annotations +is displayed on a right-click. +*/ + var widget = widgets.Widget({ + id: 'toggle-switch', + label: 'Annotator', + contentURL: data.url('widget/pencil-off.png'), + contentScriptWhen: 'ready', + contentScriptFile: data.url('widget/widget.js') + }); + + widget.port.on('left-click', function() { + console.log('activate/deactivate'); + widget.contentURL = toggleActivation() ? + data.url('widget/pencil-on.png') : + data.url('widget/pencil-off.png'); + }); + + widget.port.on('right-click', function() { + console.log('show annotation list'); + annotationList.show(); + }); + +/* +The selector page-mod enables the user to select page elements to annotate. + +It is attached to all pages but only operates if the add-on is active. + +The content script highlights any page elements which can be annotated. If the +user clicks a highlighted element it sends a message to the add-on containing +information about the element clicked, which is called the anchor of the +annotation. + +When we receive this message we assign the anchor to the annotationEditor and +display it. +*/ + var selector = pageMod.PageMod({ + include: ['*'], + contentScriptWhen: 'ready', + contentScriptFile: [data.url('jquery-1.4.2.min.js'), + data.url('selector.js')], + onAttach: function(worker) { + worker.postMessage(canEnterAnnotations()); + selectors.push(worker); + worker.port.on('show', function(data) { + annotationEditor.annotationAnchor = data; + annotationEditor.show(); + }); + worker.on('detach', function () { + detachWorker(this, selectors); + }); + } + }); + +/* +The annotationEditor panel is the UI component used for creating +new annotations. It contains a text area for the user to +enter the annotation. + +When we are ready to display the editor we assign its 'anchor' property +and call its show() method. + +Its content script sends the content of the text area to the add-on +when the user presses the return key. + +When we receives this message we create a new annotation using the anchor +and the text the user entered, store it, and hide the panel. +*/ + var annotationEditor = panels.Panel({ + width: 220, + height: 220, + contentURL: data.url('editor/annotation-editor.html'), + contentScriptFile: data.url('editor/annotation-editor.js'), + onMessage: function(annotationText) { + if (annotationText) + handleNewAnnotation(annotationText, this.annotationAnchor); + annotationEditor.hide(); + }, + onShow: function() { + this.postMessage('focus'); + } + }); + +/* +The annotationList panel is the UI component that lists all the annotations +the user has entered. + +On its 'show' event we pass it the array of annotations. + +The content script creates the HTML elements for the annotations, and +intercepts clicks on the links, passing them back to the add-on to open them +in the browser. +*/ + var annotationList = panels.Panel({ + width: 420, + height: 200, + contentURL: data.url('list/annotation-list.html'), + contentScriptFile: [data.url('jquery-1.4.2.min.js'), + data.url('list/annotation-list.js')], + contentScriptWhen: 'ready', + onShow: function() { + this.postMessage(simpleStorage.storage.annotations); + }, + onMessage: function(message) { + require('tabs').open(message); + } + }); + +/* +We listen for the OverQuota event from simple-storage. +If it fires we just notify the user and delete the most +recent annotations until we are back in quota. +*/ + simpleStorage.on("OverQuota", function () { + notifications.notify({ + title: 'Storage space exceeded', + text: 'Removing recent annotations'}); + while (simpleStorage.quotaUsage > 1) + simpleStorage.storage.annotations.pop(); + }); + +/* +We listen for private browsing start/stop events to change the widget icon +and to notify the selectors of the change in state. +*/ + privateBrowsing.on('start', function() { + widget.contentURL = data.url('widget/pencil-off.png'); + activateSelectors(); + }); + + privateBrowsing.on('stop', function() { + if (canEnterAnnotations()) { + widget.contentURL = data.url('widget/pencil-on.png'); + activateSelectors(); + } + }); + +/* +The matcher page-mod locates anchors on web pages and prepares for the +annotation to be displayed. + +It is attached to all pages, and when it is attached we pass it the complete +list of annotations. It looks for anchors in its page. If it finds one it +highlights the anchor and binds mouseenter/mouseout events to 'show' and 'hide' +messages to the add-on. + +When the add-on receives the 'show' message it assigns the annotation text to +the annotation panel and shows it. + +Note that the matcher is active whether or not the add-on is active: +'inactive' only means that the user can't create new add-ons, they can still +see old ones. +*/ + var matcher = pageMod.PageMod({ + include: ['*'], + contentScriptWhen: 'ready', + contentScriptFile: [data.url('jquery-1.4.2.min.js'), + data.url('matcher.js')], + onAttach: function(worker) { + if(simpleStorage.storage.annotations) { + worker.postMessage(simpleStorage.storage.annotations); + } + worker.port.on('show', function(data) { + annotation.content = data; + annotation.show(); + }); + worker.port.on('hide', function() { + annotation.content = null; + annotation.hide(); + }); + worker.on('detach', function () { + detachWorker(this, matchers); + }); + matchers.push(worker); + } + }); + +/* +The annotation panel is the UI component that displays an annotation. + +When we are ready to show it we assign its 'content' attribute to contain +the annotation text, and that gets sent to the content process in onShow(). +*/ + var annotation = panels.Panel({ + width: 200, + height: 180, + contentURL: data.url('annotation/annotation.html'), + contentScriptFile: [data.url('jquery-1.4.2.min.js'), + data.url('annotation/annotation.js')], + contentScriptWhen: 'ready', + onShow: function() { + this.postMessage(this.content); + } + }); + +} diff --git a/tools/addon-sdk-1.3/examples/annotator/package.json b/tools/addon-sdk-1.3/examples/annotator/package.json new file mode 100644 index 0000000..1dacd49 --- /dev/null +++ b/tools/addon-sdk-1.3/examples/annotator/package.json @@ -0,0 +1,10 @@ +{ + "license": "MPL 1.1/GPL 2.0/LGPL 2.1", + "name": "annotator", + "contributors": [], + "author": "Will Bamberg", + "version": "1.3", + "keywords": [], + "id": "anonid0-annotator", + "description": "Add notes to Web pages" +} diff --git a/tools/addon-sdk-1.3/examples/annotator/tests/test-main.js b/tools/addon-sdk-1.3/examples/annotator/tests/test-main.js new file mode 100644 index 0000000..2915364 --- /dev/null +++ b/tools/addon-sdk-1.3/examples/annotator/tests/test-main.js @@ -0,0 +1,3 @@ +exports.testMain = function(test) { + test.pass("TODO: Write some tests."); +}; diff --git a/tools/addon-sdk-1.3/examples/library-detector/README.md b/tools/addon-sdk-1.3/examples/library-detector/README.md new file mode 100755 index 0000000..e222fdc --- /dev/null +++ b/tools/addon-sdk-1.3/examples/library-detector/README.md @@ -0,0 +1,9 @@ +This is a port to the SDK of the +[Library Detector add-on](https://addons.mozilla.org/en-US/firefox/addon/library-detector/). +The original Library Detector is written by +[Paul Bakaus](http://paulbakaus.com/) and made available under the +[MIT License](http://www.opensource.org/licenses/mit-license.php). + +It only recognizes a subset of the libraries recognized by the original, +just to keep the SDK download package size and on-disk footprint as small +as possible. diff --git a/tools/addon-sdk-1.3/examples/library-detector/data/icons/closure.ico b/tools/addon-sdk-1.3/examples/library-detector/data/icons/closure.ico new file mode 100755 index 0000000..a8c91a0 Binary files /dev/null and b/tools/addon-sdk-1.3/examples/library-detector/data/icons/closure.ico differ diff --git a/tools/addon-sdk-1.3/examples/library-detector/data/icons/jquery.ico b/tools/addon-sdk-1.3/examples/library-detector/data/icons/jquery.ico new file mode 100755 index 0000000..8df8f38 Binary files /dev/null and b/tools/addon-sdk-1.3/examples/library-detector/data/icons/jquery.ico differ diff --git a/tools/addon-sdk-1.3/examples/library-detector/data/icons/jquery_ui.ico b/tools/addon-sdk-1.3/examples/library-detector/data/icons/jquery_ui.ico new file mode 100755 index 0000000..77e3bd2 Binary files /dev/null and b/tools/addon-sdk-1.3/examples/library-detector/data/icons/jquery_ui.ico differ diff --git a/tools/addon-sdk-1.3/examples/library-detector/data/icons/modernizr.ico b/tools/addon-sdk-1.3/examples/library-detector/data/icons/modernizr.ico new file mode 100755 index 0000000..c37d438 Binary files /dev/null and b/tools/addon-sdk-1.3/examples/library-detector/data/icons/modernizr.ico differ diff --git a/tools/addon-sdk-1.3/examples/library-detector/data/icons/mootools.png b/tools/addon-sdk-1.3/examples/library-detector/data/icons/mootools.png new file mode 100755 index 0000000..2a5df7d Binary files /dev/null and b/tools/addon-sdk-1.3/examples/library-detector/data/icons/mootools.png differ diff --git a/tools/addon-sdk-1.3/examples/library-detector/data/icons/yui.ico b/tools/addon-sdk-1.3/examples/library-detector/data/icons/yui.ico new file mode 100755 index 0000000..06acd8a Binary files /dev/null and b/tools/addon-sdk-1.3/examples/library-detector/data/icons/yui.ico differ diff --git a/tools/addon-sdk-1.3/examples/library-detector/data/library-detector.js b/tools/addon-sdk-1.3/examples/library-detector/data/library-detector.js new file mode 100755 index 0000000..af3a88e --- /dev/null +++ b/tools/addon-sdk-1.3/examples/library-detector/data/library-detector.js @@ -0,0 +1,97 @@ +/* +The code in this file is adapted from the original +Library Detector add-on +(https://addons.mozilla.org/en-US/firefox/addon/library-detector/) written by +Paul Bakaus (http://paulbakaus.com/) and made available under the +MIT License (http://www.opensource.org/licenses/mit-license.php). +*/ + +var LD_tests = { + + 'jQuery': { + test: function(win) { + var jq = win.jQuery || win.$ || win.$jq || win.$j; + if(jq && jq.fn && jq.fn.jquery) { + return { version: jq.fn.jquery }; + } else { + return false; + } + } + }, + + 'jQuery UI': { + //phonehome: 'http://jqueryui.com/phone_home', + test: function(win) { + + var jq = win.jQuery || win.$ || win.$jq || win.$j; + if(jq && jq.fn && jq.fn.jquery && jq.ui) { + + var plugins = 'accordion,datepicker,dialog,draggable,droppable,progressbar,resizable,selectable,slider,menu,grid,tabs'.split(','), concat = []; + for (var i=0; i < plugins.length; i++) { if(jq.ui[plugins[i]]) concat.push(plugins[i].substr(0,1).toUpperCase() + plugins[i].substr(1)); }; + + return { version: jq.ui.version, details: concat.length ? 'Plugins used: '+concat.join(',') : '' }; + } else { + return false; + } + + } + }, + + 'MooTools': { + test: function(win) { + if(win.MooTools && win.MooTools.version) { + return { version: win.MooTools.version }; + } else { + return false; + } + } + }, + + 'YUI': { + test: function(win) { + if(win.YAHOO && win.YAHOO.VERSION) { + return { version: win.YAHOO.VERSION }; + } else { + return false; + } + } + }, + + 'Closure': { + test: function(win) { + if(win.goog) { + return { version: '2.0' }; + } + return false; + } + }, + + 'Modernizr': { + test: function(win) { + if(win.Modernizr) { + return { version: win.Modernizr._version }; + } + return false; + } + }, + + +}; + +function testLibraries() { + var win = unsafeWindow; + var libraryList = []; + for(var i in LD_tests) { + var passed = LD_tests[i].test(win); + if (passed) { + let libraryInfo = { + name: i, + version: passed.version + }; + libraryList.push(libraryInfo); + } + } + self.postMessage(libraryList); +} + +testLibraries(); \ No newline at end of file diff --git a/tools/addon-sdk-1.3/examples/library-detector/data/widget.js b/tools/addon-sdk-1.3/examples/library-detector/data/widget.js new file mode 100755 index 0000000..70c59c5 --- /dev/null +++ b/tools/addon-sdk-1.3/examples/library-detector/data/widget.js @@ -0,0 +1,10 @@ + +function setLibraryInfo(element) { + self.port.emit('setLibraryInfo', element.target.title); +} + +var elements = document.getElementsByTagName('img'); + +for (var i = 0; i < elements.length; i++) { + elements[i].addEventListener('mouseover', setLibraryInfo, false); +} diff --git a/tools/addon-sdk-1.3/examples/library-detector/lib/main.js b/tools/addon-sdk-1.3/examples/library-detector/lib/main.js new file mode 100755 index 0000000..346b5f8 --- /dev/null +++ b/tools/addon-sdk-1.3/examples/library-detector/lib/main.js @@ -0,0 +1,103 @@ +const tabs = require('tabs'); +const widgets = require('widget'); +const data = require('self').data; +const pageMod = require('page-mod'); +const panel = require('panel'); + +const htmlContentPreamble = + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + +const htmlContentPostamble = + ' ' + + '' + +const icons = { + 'jQuery' : 'jquery.ico', + 'jQuery UI' : 'jquery_ui.ico', + 'MooTools' : 'mootools.png', + 'YUI' : 'yui.ico', + 'Closure' : 'closure.ico', + 'Modernizr': 'modernizr.ico', +} + +const ICON_WIDTH = 32; + +function buildIconHtml(imageName, libraryInfo) { + return ''; +} + +function buildWidgetViewContent(libraryList) { + widgetContent = htmlContentPreamble; + libraryList.forEach(function(library) { + widgetContent += buildIconHtml(icons[library.name], library.name + "
Version: " + library.version); + }); + widgetContent += htmlContentPostamble; + return widgetContent; +} + +function updateWidgetView(tab) { + let widgetView = widget.getView(tab.window); + if (!tab.libraries) { + tab.libraries = []; + } + widgetView.content = buildWidgetViewContent(tab.libraries); + widgetView.width = tab.libraries.length * ICON_WIDTH; +} + +var widget = widgets.Widget({ + id: "library-detector", + label: "Library Detector", + content: "", + contentScriptFile: data.url("widget.js"), + panel: panel.Panel({ + width: 240, + height: 60, + contentScript: 'self.on("message", function(libraryInfo) {' + + ' window.document.body.innerHTML = libraryInfo;' + + '});' + }), +}); + +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/tools/addon-sdk-1.3/examples/library-detector/package.json b/tools/addon-sdk-1.3/examples/library-detector/package.json new file mode 100755 index 0000000..7bd9eaa --- /dev/null +++ b/tools/addon-sdk-1.3/examples/library-detector/package.json @@ -0,0 +1,9 @@ +{ + "name": "library-detector-sdk", + "license": "MPL 1.1/GPL 2.0/LGPL 2.1", + "author": "", + "version": "0.1", + "fullName": "library-detector-sdk", + "id": "jid1-R4rSVNkBANnvGQ", + "description": "a basic add-on" +} diff --git a/tools/addon-sdk-1.3/examples/library-detector/test/test-main.js b/tools/addon-sdk-1.3/examples/library-detector/test/test-main.js new file mode 100644 index 0000000..2915364 --- /dev/null +++ b/tools/addon-sdk-1.3/examples/library-detector/test/test-main.js @@ -0,0 +1,3 @@ +exports.testMain = function(test) { + test.pass("TODO: Write some tests."); +}; diff --git a/tools/addon-sdk-1.3/examples/reading-data/data/mom.png b/tools/addon-sdk-1.3/examples/reading-data/data/mom.png new file mode 100644 index 0000000..4ba89a2 Binary files /dev/null and b/tools/addon-sdk-1.3/examples/reading-data/data/mom.png differ diff --git a/tools/addon-sdk-1.3/examples/reading-data/data/sample.html b/tools/addon-sdk-1.3/examples/reading-data/data/sample.html new file mode 100644 index 0000000..f9a89db --- /dev/null +++ b/tools/addon-sdk-1.3/examples/reading-data/data/sample.html @@ -0,0 +1,3 @@ + +

Hello World

+ diff --git a/tools/addon-sdk-1.3/examples/reading-data/lib/main.js b/tools/addon-sdk-1.3/examples/reading-data/lib/main.js new file mode 100644 index 0000000..5d97b0d --- /dev/null +++ b/tools/addon-sdk-1.3/examples/reading-data/lib/main.js @@ -0,0 +1,40 @@ +var self = require("self"); +var panels = require("addon-kit/panel"); +var widgets = require("addon-kit/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/tools/addon-sdk-1.3/examples/reading-data/package.json b/tools/addon-sdk-1.3/examples/reading-data/package.json new file mode 100644 index 0000000..7511687 --- /dev/null +++ b/tools/addon-sdk-1.3/examples/reading-data/package.json @@ -0,0 +1,10 @@ +{ + "name": "reading-data", + "description": "A demonstration of reading bundled data.", + "keywords": [], + "author": "Brian Warner", + "contributors": [], + "version": "1.3", + "license": "MPL 1.1/GPL 2.0/LGPL 2.1", + "id": "reading-data-example@jetpack.mozillalabs.com" +} diff --git a/tools/addon-sdk-1.3/examples/reading-data/tests/test-main.js b/tools/addon-sdk-1.3/examples/reading-data/tests/test-main.js new file mode 100644 index 0000000..7447dea --- /dev/null +++ b/tools/addon-sdk-1.3/examples/reading-data/tests/test-main.js @@ -0,0 +1,20 @@ +var m = require("main"); +var self = require("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/tools/addon-sdk-1.3/examples/reddit-panel/README.md b/tools/addon-sdk-1.3/examples/reddit-panel/README.md new file mode 100644 index 0000000..7b92e39 --- /dev/null +++ b/tools/addon-sdk-1.3/examples/reddit-panel/README.md @@ -0,0 +1,10 @@ +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/tools/addon-sdk-1.3/examples/reddit-panel/data/jquery-1.4.4.min.js b/tools/addon-sdk-1.3/examples/reddit-panel/data/jquery-1.4.4.min.js new file mode 100644 index 0000000..8f3ca2e --- /dev/null +++ b/tools/addon-sdk-1.3/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/tools/addon-sdk-1.3/examples/reddit-panel/data/panel.js b/tools/addon-sdk-1.3/examples/reddit-panel/data/panel.js new file mode 100644 index 0000000..7c48a39 --- /dev/null +++ b/tools/addon-sdk-1.3/examples/reddit-panel/data/panel.js @@ -0,0 +1,31 @@ +// 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/tools/addon-sdk-1.3/examples/reddit-panel/lib/main.js b/tools/addon-sdk-1.3/examples/reddit-panel/lib/main.js new file mode 100644 index 0000000..b6a696b --- /dev/null +++ b/tools/addon-sdk-1.3/examples/reddit-panel/lib/main.js @@ -0,0 +1,27 @@ +var data = require("self").data; + +var reddit_panel = require("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("tabs").open(url); +}); + +require("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/tools/addon-sdk-1.3/examples/reddit-panel/package.json b/tools/addon-sdk-1.3/examples/reddit-panel/package.json new file mode 100644 index 0000000..74dd78f --- /dev/null +++ b/tools/addon-sdk-1.3/examples/reddit-panel/package.json @@ -0,0 +1,10 @@ +{ + "license": "MPL 1.1/GPL 2.0/LGPL 2.1", + "name": "reddit-panel", + "contributors": [], + "author": "Myk Melez", + "version": "1.3", + "keywords": [], + "description": "Displays Reddit in a panel.", + "id": "anonid0-reddit-panel" +} diff --git a/tools/addon-sdk-1.3/examples/reddit-panel/tests/test-main.js b/tools/addon-sdk-1.3/examples/reddit-panel/tests/test-main.js new file mode 100644 index 0000000..69865d9 --- /dev/null +++ b/tools/addon-sdk-1.3/examples/reddit-panel/tests/test-main.js @@ -0,0 +1,17 @@ +var m = require("main"); +var self = require("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/tools/addon-sdk-1.3/packages/addon-kit/README.md b/tools/addon-sdk-1.3/packages/addon-kit/README.md new file mode 100644 index 0000000..9a68554 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/README.md @@ -0,0 +1,8 @@ +The addon-kit package provides high-level APIs for add-on developers. +Most of the needs of most add-on developers should be served by the modules +found here. Modules in this packages don't require any special privileges to +run. + +The modules in the addon-kit package are relatively stable. We intend to add +new APIs here and extend existing ones, but will avoid making incompatible +changes to them unless absolutely necessary. diff --git a/tools/addon-sdk-1.3/packages/addon-kit/data/moz_favicon.ico b/tools/addon-sdk-1.3/packages/addon-kit/data/moz_favicon.ico new file mode 100644 index 0000000..d444389 Binary files /dev/null and b/tools/addon-sdk-1.3/packages/addon-kit/data/moz_favicon.ico differ diff --git a/tools/addon-sdk-1.3/packages/addon-kit/data/test-page-mod.html b/tools/addon-sdk-1.3/packages/addon-kit/data/test-page-mod.html new file mode 100644 index 0000000..9211698 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/data/test-page-mod.html @@ -0,0 +1,8 @@ + + + Page Mod test + + +

Lorem ipsum dolor sit amet.

+ + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/data/test-page-worker.html b/tools/addon-sdk-1.3/packages/addon-kit/data/test-page-worker.html new file mode 100644 index 0000000..e89a283 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/data/test-page-worker.html @@ -0,0 +1,8 @@ + + + Page Worker test + + +

Lorem ipsum dolor sit amet.

+ + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/data/test-page-worker.js b/tools/addon-sdk-1.3/packages/addon-kit/data/test-page-worker.js new file mode 100644 index 0000000..d8effb9 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/data/test-page-worker.js @@ -0,0 +1,25 @@ + +// get title directly +self.postMessage(["assertEqual", document.title, "Page Worker test", + "Correct page title accessed directly"]); + +// get

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

can be accessed directly"]); +self.postMessage(["assertEqual", 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(["assert", !!div, "

can be accessed directly"]); +self.postMessage(["assertEqual", div.firstChild.nodeValue, + "Test text created", "Correct text node expected"]); +self.postMessage(["done"]); + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/data/test.html b/tools/addon-sdk-1.3/packages/addon-kit/data/test.html new file mode 100644 index 0000000..39de477 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/data/test.html @@ -0,0 +1,8 @@ + + + foo + + +

bar

+ + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/docs/clipboard.md b/tools/addon-sdk-1.3/packages/addon-kit/docs/clipboard.md new file mode 100644 index 0000000..7292017 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/docs/clipboard.md @@ -0,0 +1,58 @@ + + +The `clipboard` module allows callers to interact with the system clipboard, +setting and retrieving its contents. + +You can optionally specify the type of the data to set and retrieve. +The following types are supported: + +* `text` (plain text) +* `html` (a string of HTML) + +If no data type is provided, then the module will detect it for you. + +Examples +-------- + +Set and get the contents of the clipboard. + + let clipboard = require("clipboard"); + clipboard.set("Lorem ipsum dolor sit amet"); + let contents = clipboard.get(); + +Set the clipboard contents to some HTML. + + let clipboard = require("clipboard"); + clipboard.set("Lorem ipsum dolor sit amet", "html"); + +If the clipboard contains HTML content, open it in a new tab. + + let clipboard = require("clipboard"); + if (clipboard.currentFlavors.indexOf("html") != -1) + require("tabs").open("data:text/html," + clipboard.get("html")); + + +@function + Replace the contents of the user's clipboard with the provided data. +@param data {string} + The data to put on the clipboard. +@param [datatype] {string} + The type of the data (optional). + + + +@function + Get the contents of the user's clipboard. +@param [datatype] {string} + Retrieve the clipboard contents only if matching this type (optional). + The function will return null if the contents of the clipboard do not match + the supplied type. + + + +@property {array} + Data on the clipboard is sometimes available in multiple types. For example, + HTML data might be available as both a string of HTML (the `html` type) + and a string of plain text (the `text` type). This function returns an array + of all types in which the data currently on the clipboard is available. + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/docs/context-menu.md b/tools/addon-sdk-1.3/packages/addon-kit/docs/context-menu.md new file mode 100644 index 0000000..f2bb5b3 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/docs/context-menu.md @@ -0,0 +1,702 @@ + + + +The `context-menu` module lets you add items to Firefox's page context menu. + + +Introduction +------------ + +The `context-menu` API provides a simple, declarative way to add items to the +page's context menu. You can add items that perform an action when clicked, +submenus, and menu separators. + +Instead of manually adding items when particular contexts occur and then +removing them when those contexts go away, you *bind* items to contexts, and the +adding and removing is automatically handled for you. Items are bound to +contexts in much the same way that event listeners are bound to events. When +the user invokes the context menu, all of the items bound to the current context +are automatically added to the menu. If no items are bound, none are added. +Likewise, any items that were previously in the menu but are not bound to the +current context are automatically removed from the menu. You never need to +manually remove your items from the menu unless you want them to never appear +again. + +For example, if your add-on needs to add a context menu item whenever the +user visits a certain page, don't create the item when that page loads, and +don't remove it when the page unloads. Rather, create your item only once and +supply a context that matches the target URL. + + +Specifying Contexts +------------------- + +As its name implies, the context menu should be reserved for the occurrence of +specific contexts. Contexts can be related to page content or the page itself, +but they should never be external to the page. + +For example, a good use of the menu would be to show an "Edit Image" item when +the user right-clicks an image in the page. A bad use would be to show a +submenu that listed all the user's tabs, since tabs aren't related to the page +or the node the user clicked to open the menu. + +### The Page Context + +First of all, you may not need to specify a context at all. When an item does +not specify a context, the page context applies. + +The *page context* occurs when the user invokes the context menu on a +non-interactive portion of the page. Try right-clicking a blank spot in this +page, or on text. Make sure that no text is selected. The menu that appears +should contain the items "Back", "Forward", "Reload", "Stop", and so on. This +is the page context. + +The page context is appropriate when your item acts on the page as a whole. It +does not occur when the user invokes the context menu on a link, image, or other +non-text node, or while a selection exists. + +### Declarative Contexts + +You can specify some simple, declarative contexts when you create a menu item by +setting the `context` property of the options object passed to its constructor, +like this: + + var cm = require("context-menu"); + cm.Item({ + label: "My Menu Item", + context: cm.URLContext("*.mozilla.org") + }); + +These contexts may be specified by calling the following constructors. Each is +exported by the `context-menu` module. + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
ConstructorDescription
+ PageContext() + + The page context. +
+ SelectionContext() + + This context occurs when the menu is invoked on a page in which the user + has made a selection. +
+ SelectorContext(selector) + + This context occurs when the menu is invoked on a node that either matches + selector, a CSS selector, or has an ancestor that matches. + selector may include multiple selectors separated by commas, + e.g., "a[href], img". +
+ URLContext(matchPattern) + + This context occurs when the menu is invoked on pages with particular + URLs. matchPattern is a match pattern string or an array of + match pattern strings. When matchPattern is an array, the + context occurs when the menu is invoked on a page whose URL matches any of + the patterns. These are the same match pattern strings that you use with + the page-mod + include property. + Read more about patterns. +
+ array + + An array of any of the other types. This context occurs when all contexts + in the array occur. +
+ +Menu items also have a `context` property that can be used to add and remove +declarative contexts after construction. For example: + + var context = require("context-menu").SelectorContext("img"); + myMenuItem.context.add(context); + myMenuItem.context.remove(context); + +When a menu item is bound to more than one context, it appears in the menu when +all of those contexts occur. + +### In Content Scripts + +The declarative contexts are handy but not very powerful. For instance, you +might want your menu item to appear for any page that has at least one image, +but declarative contexts won't help you there. + +When you need more control control over the context in which your menu items are +shown, you can use content scripts. Like other APIs in the SDK, the +`context-menu` API uses +[content scripts](dev-guide/addon-development/web-content.html) to let your +add-on interact with pages in the browser. Each menu item you create in the +top-level context menu can have a content script. + +A special event named `"context"` is emitted in your content scripts whenever +the context menu is about to be shown. If you register a listener function for +this event and it returns true, the menu item associated with the listener's +content script is shown in the menu. + +For example, this item appears whenever the context menu is invoked on a page +that contains at least one image: + + require("context-menu").Item({ + label: "This Page Has Images", + contentScript: 'self.on("context", function (node) {' + + ' return !!document.querySelector("img");' + + '});' + }); + +Note that the listener function has a parameter called `node`. This is the node +in the page that the user context-clicked to invoke the menu. You can use it to +determine whether your item should be shown. + +You can both specify declarative contexts and listen for contexts in a content +script. In that case, the declarative contexts are evaluated first. If they +are not current, then your context listener is never called. + +This example takes advantage of that fact. The listener can be assured that +`node` will always be an image: + + require("context-menu").Item({ + label: "A Mozilla Image", + context: contextMenu.SelectorContext("img"), + contentScript: 'self.on("context", function (node) {' + + ' return /mozilla/.test(node.src);' + + '});' + }); + +Your item is shown only when all declarative contexts are current and your +context listener returns true. + + +Handling Menu Item Clicks +------------------------- + +In addition to using content scripts to listen for the `"context"` event as +described above, you can use content scripts to handle item clicks. When the +user clicks your menu item, an event named `"click"` is emitted in the item's +content script. + +Therefore, to handle an item click, listen for the `"click"` event in that +item's content script like so: + + require("context-menu").Item({ + label: "My Item", + contentScript: 'self.on("click", function (node, data) {' + + ' console.log("Item clicked!");' + + '});' + }); + +Note that the listener function has parameters called `node` and `data`. `node` +is the node that the user context-clicked to invoke the menu. You can use it +when performing some action. `data` is the `data` property of the menu item +that was clicked. Since only top-level menu items have content scripts, this +comes in handy for determining which item in a `Menu` was clicked: + + var cm = require("context-menu"); + cm.Menu({ + label: "My Menu", + contentScript: 'self.on("click", function (node, data) {' + + ' console.log("You clicked " + data);' + + '});', + items: [ + cm.Item({ label: "Item 1", data: "item1" }), + cm.Item({ label: "Item 2", data: "item2" }), + cm.Item({ label: "Item 3", data: "item3" }) + ] + }); + +Often you will need to collect some kind of information in the click listener +and perform an action unrelated to content. To communicate to the menu item +associated with the content script, the content script can call the +`postMessage` function attached to the global `self` object, passing it some +JSON-able data. The menu item's `"message"` event listener will be called with +that data. + + require("context-menu").Item({ + label: "Edit Image", + context: contextMenu.SelectorContext("img"), + contentScript: 'self.on("click", function (node, data) {' + + ' self.postMessage(node.src);' + + '});', + onMessage: function (imgSrc) { + openImageEditor(imgSrc); + } + }); + + +Updating a Menu Item's Label +---------------------------- + +Each menu item must be created with a label, but you can change its label later +using a couple of methods. + +The simplest method is to set the menu item's `label` property. This example +updates the item's label based on the number of times it's been clicked: + + var numClicks = 0; + var myItem = require("context-menu").Item({ + label: "Click Me: " + numClicks, + contentScript: 'self.on("click", self.postMessage);', + onMessage: function () { + numClicks++; + this.label = "Click Me: " + numClicks; + // Setting myItem.label is equivalent. + } + }); + +Sometimes you might want to update the label based on the context. For +instance, if your item performs a search with the user's selected text, it would +be nice to display the text in the item to provide feedback to the user. In +these cases you can use the second method. Recall that your content scripts can +listen for the `"context"` event and if your listeners return true, the items +associated with the content scripts are shown in the menu. In addition to +returning true, your `"context"` listeners can also return strings. When a +`"context"` listener returns a string, it becomes the item's new label. + +This item implements the aforementioned search example: + + var cm = require("context-menu"); + cm.Item({ + label: "Search Google", + context: cm.SelectionContext(), + contentScript: 'self.on("context", function () {' + + ' var text = window.getSelection().toString();' + + ' if (text.length > 20)' + + ' text = text.substr(0, 20) + "...";' + + ' return "Search Google for " + text;' + + '});' + }); + +The `"context"` listener gets the window's current selection, truncating it if +it's too long, and includes it in the returned string. When the item is shown, +its label will be "Search Google for `text`", where `text` is the truncated +selection. + + +More Examples +------------- + +For conciseness, these examples create their content scripts as strings and use +the `contentScript` property. In your own add-on, you will probably want to +create your content scripts in separate files and pass their URLs using the +`contentScriptFile` property. See +[Working with Content Scripts](dev-guide/addon-development/web-content.html) +for more information. + +Show an "Edit Page Source" item when the user right-clicks a non-interactive +part of the page: + + require("context-menu").Item({ + label: "Edit Page Source", + contentScript: 'self.on("click", function (node, data) {' + + ' self.postMessage(document.URL);' + + '});', + onMessage: function (pageURL) { + editSource(pageURL); + } + }); + +Show an "Edit Image" item when the menu is invoked on an image: + + require("context-menu").Item({ + label: "Edit Image", + context: contextMenu.SelectorContext("img"), + contentScript: 'self.on("click", function (node, data) {' + + ' self.postMessage(node.src);' + + '});', + onMessage: function (imgSrc) { + openImageEditor(imgSrc); + } + }); + +Show an "Edit Mozilla Image" item when the menu is invoked on an image in a +mozilla.org or mozilla.com page: + + var cm = require("context-menu"); + cm.Item({ + label: "Edit Mozilla Image", + context: [ + cm.URLContext(["*.mozilla.org", "*.mozilla.com"]), + cm.SelectorContext("img") + ], + contentScript: 'self.on("click", function (node, data) {' + + ' self.postMessage(node.src);' + + '});', + onMessage: function (imgSrc) { + openImageEditor(imgSrc); + } + }); + +Show an "Edit Page Images" item when the page contains at least one image: + + require("context-menu").Item({ + label: "Edit Page Images", + // This ensures the item only appears during the page context. + context: contextMenu.PageContext(), + contentScript: 'self.on("context", function (node) {' + + ' var pageHasImgs = !!document.querySelector("img");' + + ' return pageHasImgs;' + + '});' + + 'self.on("click", function (node, data) {' + + ' var imgs = document.querySelectorAll("img");' + + ' var imgSrcs = [];' + + ' for (var i = 0 ; i < imgs.length; i++)' + + ' imgSrcs.push(imgs[i].src);' + + ' self.postMessage(imgSrcs);' + + '});', + onMessage: function (imgSrcs) { + openImageEditor(imgSrcs); + } + }); + +Show a "Search With" menu when the user right-clicks an anchor that searches +Google or Wikipedia with the text contained in the anchor: + + var cm = require("context-menu"); + var googleItem = cm.Item({ + label: "Google", + data: "http://www.google.com/search?q=" + }); + var wikipediaItem = cm.Item({ + label: "Wikipedia", + data: "http://en.wikipedia.org/wiki/Special:Search?search=" + }); + var searchMenu = cm.Menu({ + label: "Search With", + context: contextMenu.SelectorContext("a[href]"), + contentScript: 'self.on("click", function (node, data) {' + + ' var searchURL = data + node.textContent;' + + ' window.location.href = searchURL;' + + '});', + items: [googleItem, wikipediaItem] + }); + + + +@class +A labeled menu item that can perform an action when clicked. + +@constructor + Creates a labeled menu item that can perform an action when clicked. +@param options {object} + An object with the following keys: + @prop label {string} + The item's label. It must either be a string or an object that implements + `toString()`. + @prop [image] {string} + The item's icon, a string URL. The URL can be remote, a reference to an + image in the add-on's `data` directory, or a data URI. + @prop [data] {string} + An optional arbitrary value to associate with the item. It must be either a + string or an object that implements `toString()`. It will be passed to + click listeners. + @prop [context] {value} + If the item is contained in the top-level context menu, this declaratively + specifies the context under which the item will appear; see Specifying + Contexts above. Ignored if the item is contained in a submenu. + @prop [contentScript] {string,array} + If the item is contained in the top-level context menu, this is the content + script or an array of content scripts that the item can use to interact with + the page. Ignored if the item is contained in a submenu. + @prop [contentScriptFile] {string,array} + If the item is contained in the top-level context menu, this is the local + file URL of the content script or an array of such URLs that the item can + use to interact with the page. Ignored if the item is contained in a + submenu. + @prop [onMessage] {function} + If the item is contained in the top-level context menu, this function will + be called when the content script calls `self.postMessage`. It will be + passed the data that was passed to `postMessage`. Ignored if the item is + contained in a submenu. + + + +@property {string} + The menu item's label. You can set this after creating the item to update its + label later. + + + +@property {string} + The item's icon, a string URL. The URL can be remote, a reference to an image + in the add-on's `data` directory, or a data URI. You can set this after + creating the item to update its image later. To remove the item's image, set + it to `null`. + + + +@property {string} + An optional arbitrary value to associate with the item. It must be either a + string or an object that implements `toString()`. It will be passed to + click listeners. You can set this after creating the item to update its data + later. + + + +@property {list} + A list of declarative contexts for which the menu item will appear in the + context menu. Contexts can be added by calling `context.add()` and removed by + called `context.remove()`. This property is meaningful only for items + contained in the top-level context menu. + + + +@property {Menu} + The item's parent `Menu`, or `null` if the item is contained in the top-level + context menu. This property is read-only. To add the item to a new menu, + call that menu's `addItem()` method. + + + +@property {string,array} + The content script or the array of content scripts associated with the menu + item during creation. This property is meaningful only for items contained in + the top-level context menu. + + + +@property {string,array} + The URL of a content script or the array of such URLs associated with the menu + item during creation. This property is meaningful only for items contained in + the top-level context menu. + + + +@method + Permanently removes the item from its parent menu and frees its resources. + The item must not be used afterward. If you need to remove the item from its + parent menu but use it afterward, call `removeItem()` on the parent menu + instead. + + + +@event +If you listen to this event you can receive message events from content +scripts associated with this menu item. When a content script posts a +message using `self.postMessage()`, the message is delivered to the add-on +code in the menu item's `message` event. + +@argument {value} +Listeners are passed a single argument which is the message posted +from the content script. The message can be any +JSON-serializable value. + + + + + +@class +A labeled menu item that expands into a submenu. + + +@constructor + Creates a labeled menu item that expands into a submenu. +@param options {object} + An object with the following keys: + @prop label {string} + The item's label. It must either be a string or an object that implements + `toString()`. + @prop items {array} + An array of menu items that the menu will contain. Each must be an `Item`, + `Menu`, or `Separator`. + @prop [image] {string} + The menu's icon, a string URL. The URL can be remote, a reference to an + image in the add-on's `data` directory, or a data URI. + @prop [context] {value} + If the menu is contained in the top-level context menu, this declaratively + specifies the context under which the menu will appear; see Specifying + Contexts above. Ignored if the menu is contained in a submenu. + @prop [contentScript] {string,array} + If the menu is contained in the top-level context menu, this is the content + script or an array of content scripts that the menu can use to interact with + the page. Ignored if the menu is contained in a submenu. + @prop [contentScriptFile] {string,array} + If the menu is contained in the top-level context menu, this is the local + file URL of the content script or an array of such URLs that the menu can + use to interact with the page. Ignored if the menu is contained in a + submenu. + @prop [onMessage] {function} + If the menu is contained in the top-level context menu, this function will + be called when the content script calls `self.postMessage`. It will be + passed the data that was passed to `postMessage`. Ignored if the item is + contained in a submenu. + + + +@property {string} + The menu's label. You can set this after creating the menu to update its + label later. + + + +@property {array} + An array containing the items in the menu. The array is read-only, meaning + that modifications to it will not affect the menu. However, setting this + property to a new array will replace all the items currently in the menu with + the items in the new array. + + + +@property {string} + The menu's icon, a string URL. The URL can be remote, a reference to an image + in the add-on's `data` directory, or a data URI. You can set this after + creating the menu to update its image later. To remove the menu's image, set + it to `null`. + + + +@property {list} + A list of declarative contexts for which the menu will appear in the context + menu. Contexts can be added by calling `context.add()` and removed by called + `context.remove()`. This property is meaningful only for menus contained in + the top-level context menu. + + + +@property {Menu} + The menu's parent `Menu`, or `null` if the menu is contained in the top-level + context menu. This property is read-only. To add the menu to a new menu, + call that menu's `addItem()` method. + + + +@property {string,array} + The content script or the array of content scripts associated with the menu + during creation. This property is meaningful only for menus contained in the + top-level context menu. + + + +@property {string,array} + The URL of a content script or the array of such URLs associated with the menu + during creation. This property is meaningful only for menus contained in the + top-level context menu. + + + +@method + Appends a menu item to the end of the menu. If the item is already contained + in another menu or in the top-level context menu, it's automatically removed + first. +@param item {Item,Menu,Separator} + The `Item`, `Menu`, or `Separator` to add to the menu. + + + +@method + Removes the given menu item from the menu. If the menu does not contain the + item, this method does nothing. +@param item {Item,Menu,Separator} + The menu item to remove from the menu. + + + +@method + Permanently removes the menu from its parent menu and frees its resources. + The menu must not be used afterward. If you need to remove the menu from its + parent menu but use it afterward, call `removeItem()` on the parent menu + instead. + + + +@event +If you listen to this event you can receive message events from content +scripts associated with this menu item. When a content script posts a +message using `self.postMessage()`, the message is delivered to the add-on +code in the menu item's `message` event. + +@argument {value} +Listeners are passed a single argument which is the message posted +from the content script. The message can be any +JSON-serializable value. + + + + + +@class +A menu separator. Separators can be contained only in `Menu`s, not in the +top-level context menu. + + +@constructor + Creates a menu separator. + + + +@property {Menu} + The separator's parent `Menu`. This property is read-only. To add the + separator to a new menu, call that menu's `addItem()` method. + + + +@method + Permanently removes the separator from its parent menu and frees its + resources. The separator must not be used afterward. If you need to remove + the separator from its parent menu but use it afterward, call `removeItem()` + on the parent menu instead. + + + + + +@class + +@constructor + Creates a page context. See Specifying Contexts above. + + + + +@class + +@constructor + Creates a context that occurs when a page contains a selection. See + Specifying Contexts above. + + + + +@class + +@constructor + Creates a context that matches a given CSS selector. See Specifying Contexts + above. +@param selector {string} + A CSS selector. + + + + +@class + +@constructor + Creates a context that matches pages with particular URLs. See Specifying + Contexts above. +@param matchPattern {string,array} + A [match pattern](packages/api-utils/docs/match-pattern.html) string or an array of + match pattern strings. + + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/docs/hotkeys.md b/tools/addon-sdk-1.3/packages/addon-kit/docs/hotkeys.md new file mode 100644 index 0000000..dfbe572 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/docs/hotkeys.md @@ -0,0 +1,74 @@ + + +Some add-ons may wish to define keyboard shortcuts for certain operations. This +module exposes an API to create those. + + +@class + +Module exports `Hotkey` constructor allowing users to create a `hotkey` for the +host application. + + +@constructor +Creates a hotkey who's `onPress` listener method is invoked when key combination +defined by `hotkey` is pressed. + +Please note: If more than one `hotkey` is created for the same key +combination, the listener is executed only on the last one created + +@param options {Object} + Options for the hotkey, with the following keys: + +@prop combo {String} +Any function key: `"f1, f2, ..., f24"` or key combination in the format +of `'modifier-key'`: + + "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. + +@prop onPress {Function} +Function that is invoked when the key combination `hotkey` is pressed. + + + +@method +Stops this instance of `Hotkey` from reacting on the key combinations. Once +destroyed it can no longer be used. + + + +## Example ## + + // Define keyboard shortcuts for showing and hiding a custom panel. + var { Hotkey } = require("hotkeys"); + + var showHotKey = Hotkey({ + combo: "accel-shift-o", + onPress: function() { + showMyPanel(); + } + }); + var hideHotKey = Hotkey({ + combo: "accel-alt-shift-o", + onPress: function() { + hideMyPanel(); + } + }); + +[Mozilla keyboard planning FAQ]:http://www.mozilla.org/access/keyboard/ +[keyboard shortcuts]:https://developer.mozilla.org/en/XUL_Tutorial/Keyboard_Shortcuts diff --git a/tools/addon-sdk-1.3/packages/addon-kit/docs/notifications.md b/tools/addon-sdk-1.3/packages/addon-kit/docs/notifications.md new file mode 100644 index 0000000..5e76c1b --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/docs/notifications.md @@ -0,0 +1,60 @@ + + +The `notifications` module allows you to display transient, +[toaster](http://en.wikipedia.org/wiki/Toast_%28computing%29)-style +desktop messages to the user. + +This API supports desktop notifications on Windows, OS X using +[Growl](http://growl.info/), and Linux using libnotify. If the user's system +does not support desktop notifications or if its notifications service is not +running, then notifications made with this API are logged to Firefox's error +console and, if the user launched Firefox from the command line, the terminal. + +Examples +-------- + +Here's a typical example. When the message is clicked, a string is logged to +the console. + + var notifications = require("notifications"); + notifications.notify({ + title: "Jabberwocky", + text: "'Twas brillig, and the slithy toves", + data: "did gyre and gimble in the wabe", + onClick: function (data) { + console.log(data); + // console.log(this.data) would produce the same result. + } + }); + +This one displays an icon that's stored in the add-on's `data` directory. (See +the [`self`](packages/addon-kit/docs/self.html) module documentation for more information.) + + var notifications = require("notifications"); + var self = require("self"); + var myIconURL = self.data.url("myIcon.png"); + notifications.notify({ + text: "I have an icon!", + iconURL: myIconURL + }); + + + +@function + Displays a transient notification to the user. +@param options {object} + An object with the following keys. Each is optional. + @prop [title] {string} + A string to display as the message's title. + @prop [text] {string} + A string to display as the body of the message. + @prop [iconURL] {string} + The URL of an icon to display inside the message. It may be a remote URL, + a data URI, or a URL returned by the [`self`](packages/addon-kit/docs/self.html) + module. + @prop [onClick] {function} + A function to be called when the user clicks the message. It will be passed + the value of `data`. + @prop [data] {string} + A string that will be passed to `onClick`. + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/docs/page-mod.md b/tools/addon-sdk-1.3/packages/addon-kit/docs/page-mod.md new file mode 100644 index 0000000..aa046d4 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/docs/page-mod.md @@ -0,0 +1,366 @@ + + + + +Overview +-------- +The page-mod module enables add-on developers to execute scripts in the context +of specific web pages. Most obviously you could use page-mod to dynamically +modify the content of certain pages. + +The module exports a constructor function `PageMod` which creates a new page +modification (or "mod" for short). + +A page mod does not modify its pages until those pages are loaded or reloaded. +In other words, if your add-on is loaded while the user's browser is open, the +user will have to reload any open pages that match the mod for the mod to affect +them. + +To stop a page mod from making any more modifications, call its `destroy` +method. + +Like all modules that interact with web content, page-mod uses content +scripts that execute in the content process and defines a messaging API to +communicate between the content scripts and the main add-on script. For more +details on content scripting see the tutorial on [interacting with web +content](dev-guide/addon-development/web-content.html). + +To create a PageMod the add-on developer supplies: + +* a set of rules to select the desired subset of web pages based on their URL. +Each rule is specified using the +[match-pattern](packages/api-utils/docs/match-pattern.html) syntax. + +* a set of content scripts to execute in the context of the desired pages. + +* a value for the onAttach option: this value is a function which will be +called when a page is loaded that matches the ruleset. This is used to set up a +communication channel between the add-on code and the content script. + +All these parameters are optional except for the ruleset, which must include +at least one rule. + +The following add-on displays an alert whenever a page matching the ruleset is +loaded: + + var pageMod = require("page-mod"); + pageMod.PageMod({ + include: "*.org", + contentScript: 'window.alert("Page matches ruleset");' + }); + +If you specify a value of "ready" or "end" for `contentScriptWhen`, +then the content script can interact with the DOM itself: + + var pageMod = require("page-mod"); + pageMod.PageMod({ + include: "*.org", + contentScriptWhen: 'end', + contentScript: 'document.body.innerHTML = ' + + ' "

Page matches ruleset

";' + }); + +### Using `contentScriptFile` ### + +Most of the examples in this page define content scripts as strings, +and use the `contentScript` option to assign them to page mods. + +In your code you will more often create content scripts in separate files +under your add-on's `data` directory. Then you can use the +[`self`](packages/addon-kit/docs/self.html) module to retrieve a URL pointing +to the file, and assign this to the page-mod's `contentScriptFile` +property. + +For example, if you save the content script +file in your `data` directory as "myScript.js", you would assign it using +code like: + + var data = require("self").data; + + var pageMod = require("page-mod"); + pageMod.PageMod({ + include: "*.org", + contentScriptWhen: 'end', + contentScriptFile: data.url("myScript.js") + }); + +## Communicating With Content Scripts ## + +When a matching page is loaded the `PageMod` will call the function that the +add-on code supplied to `onAttach`. The `PageMod` supplies one argument to +this function: a `worker` object. + +The worker can be thought of as the add-on's end of +a communication channel between the add-on code and the content scripts that +have been attached to this page. + +Thus the add-on can pass messages to the content scripts by calling the +worker's `postMessage` function and can receive messages from the content +scripts by registering a function as a listener to the worker's `on` function. + +Note that if multiple matching pages are loaded simultaneously then each page +is loaded into its own execution context with its own copy of the content +scripts. In this case `onAttach` is called once for each loaded page, and the +add-on code will have a separate worker for each page: + +![Multiple workers](static-files/media/multiple-workers.jpg) + +This is demonstrated in the following example: + + var pageMod = require("page-mod"); + var tabs = require("tabs"); + + var workers = []; + + pageMod.PageMod({ + include: ["http://www.mozilla*"], + contentScriptWhen: 'end', + contentScript: "onMessage = function onMessage(message) {" + + " window.alert(message);};", + onAttach: function onAttach(worker) { + if (workers.push(worker) == 3) { + workers[0].postMessage("The first worker!"); + workers[1].postMessage("The second worker!"); + workers[2].postMessage("The third worker!"); + } + } + }); + + tabs.open("http://www.mozilla.com"); + tabs.open("http://www.mozilla.org"); + tabs.open("http://www.mozilla-europe.org"); + +Here we specify a ruleset to match any URLs starting with +"http://www.mozilla". When a page matches we add the supplied worker to +an array, and when we have three workers in the array we send a message to +each worker in turn, telling it the order in which it was attached. The +worker just displays the message in an alert box. + +This shows that separate pages execute in separate contexts and that each +context has its own communication channel with the add-on script. + +Note though that while there is a separate worker for each execution context, +the worker is shared across all the content scripts associated with a single +execution context. In the following example we pass two content scripts into +the `PageMod`: these content scripts will share a worker instance. + +In the example each content script identifies itself to the add-on script +by sending it a message using the global `postMessage` function. In the +`onAttach` function the add-on code logs the fact that a new page is +attached and registers a listener function that simply logs the message: + + + var pageMod = require("page-mod"); + var data = require("self").data; + var tabs = require("tabs"); + + pageMod.PageMod({ + include: ["http://www.mozilla*"], + contentScriptWhen: 'end', + contentScript: ["postMessage('Content script 1 is attached to '+ " + + "document.URL);", + "postMessage('Content script 2 is attached to '+ " + + "document.URL);"], + onAttach: function onAttach(worker) { + console.log("Attaching content scripts") + worker.on('message', function(data) { + console.log(data); + }); + } + }); + + tabs.open("http://www.mozilla.com"); + +The console output of this add-on is: + +
+  info: Attaching content scripts
+  info: Content script 1 is attached to http://www.mozilla.com/en-US/
+  info: Content script 2 is attached to http://www.mozilla.com/en-US/
+
+ +### Mapping workers to tabs ### + +The [`worker`](packages/api-utils/docs/content/worker.html) has a `tab` +property which returns the tab associated with this worker. You can use this +to access the [`tabs API`](packages/addon-kit/docs/tabs.html) for the tab +associated with a specific page: + + var pageMod = require("page-mod"); + var tabs = require("tabs"); + + pageMod.PageMod({ + include: ["*"], + onAttach: function onAttach(worker) { + console.log(worker.tab.title); + } + }); + +### Attaching content scripts to tabs ### + +We've seen that the page mod API attaches content scripts to pages based on +their URL. Sometimes, though, we don't care about the URL: we just want +to execute a script on demand in the context of a particular tab. + +For example, we might want to run a script in the context of the currently +active tab when the user clicks a widget: to block certain content, to +change the font style, or to display the page's DOM structure. + +Using the `attach` method of the [`tab`](packages/addon-kit/docs/tabs.html) +object, you can attach a set of content scripts to a particular tab. The +scripts are executed immediately. + +The following add-on creates a widget which, when clicked, highlights all the +`div` elements in the page loaded into the active tab: + + var widgets = require("widget"); + var tabs = require("tabs"); + + var widget = widgets.Widget({ + id: "div-show", + label: "Show divs", + contentURL: "http://www.mozilla.org/favicon.ico", + onClick: function() { + tabs.activeTab.attach({ + contentScript: + 'var divs = document.getElementsByTagName("div");' + + 'for (var i = 0; i < divs.length; ++i) {' + + 'divs[i].setAttribute("style", "border: solid red 1px;");' + + '}' + }); + } + }); + +## Destroying Workers ## + +Workers generate a `detach` event when their associated page is closed: that +is, when the tab is closed or the tab's location changes. If +you are maintaining a list of workers belonging to a page mod, you can use +this event to remove workers that are no longer valid. + +For example, if you maintain a list of workers attached to a page mod: + + var workers = []; + + var pageMod = require("page-mod").PageMod({ + include: ['*'], + contentScriptWhen: 'ready', + contentScriptFile: data.url('pagemod.js'), + onAttach: function(worker) { + workers.push(worker); + } + }); + +You can remove workers when they are no longer valid by listening to `detach`: + + var workers = []; + + function detachWorker(worker, workerArray) { + var index = workerArray.indexOf(worker); + if(index != -1) { + workerArray.splice(index, 1); + } + } + + var pageMod = require("page-mod").PageMod({ + include: ['*'], + contentScriptWhen: 'ready', + contentScriptFile: data.url('pagemod.js'), + onAttach: function(worker) { + workers.push(worker); + worker.on('detach', function () { + detachWorker(this, workers); + }); + } + }); + + +@class +A PageMod object. Once activated a page mod will execute the supplied content +scripts in the context of any pages matching the pattern specified by the +'include' property. + +@constructor +Creates a PageMod. +@param options {object} + Options for the PageMod, with the following keys: + @prop include {string,array} + A match pattern string or an array of match pattern strings. These define + the pages to which the PageMod applies. See the + [match-pattern](packages/api-utils/docs/match-pattern.html) module for + a description of match pattern syntax. + At least one match pattern must be supplied. + + @prop [contentScriptFile] {string,array} + The local file URLs of content scripts to load. Content scripts specified + by this option are loaded *before* those specified by the `contentScript` + option. Optional. + @prop [contentScript] {string,array} + The texts of content scripts to load. Content scripts specified by this + option are loaded *after* those specified by the `contentScriptFile` option. + Optional. + @prop [contentScriptWhen="end"] {string} + When to load the content scripts. This may take one of the following + values: + + * "start": load content scripts immediately after the document + element for the page is inserted into the DOM, but before the DOM content + itself has been loaded + * "ready": load content scripts once DOM content has been loaded, + corresponding to the + [DOMContentLoaded](https://developer.mozilla.org/en/Gecko-Specific_DOM_Events) + event + * "end": load content scripts once all the content (DOM, JS, CSS, + images) for the page has been loaded, at the time the + [window.onload event](https://developer.mozilla.org/en/DOM/window.onload) + fires + + This property is optional and defaults to "end". + + @prop [onAttach] {function} +A function to call when the PageMod attaches content scripts to +a matching page. The function will be called with one argument, a `worker` +object which the add-on script can use to communicate with the content scripts +attached to the page in question. + + + + +@property {List} +A [list](packages/api-utils/docs/list.html) of match pattern strings. These +define the pages to which the page mod applies. See the +[match-pattern](packages/api-utils/docs/match-pattern.html) module for a +description of match patterns. Rules can be added to the list by calling its +`add` method and removed by calling its `remove` method. + + + + +@method +Stops the page mod from making any more modifications. Once destroyed the page +mod can no longer be used. Note that modifications already made to open pages +will not be undone. + + + +@event +This event is emitted this event when the page-mod's content scripts are +attached to a page whose URL matches the page-mod's `include` filter. + +@argument {Worker} +The listener function is passed a [`Worker`](packages/api-utils/docs/content/worker.html) object that can be used to communicate +with any content scripts attached to this page. + + + +@event +This event is emitted when an uncaught runtime error occurs in one of the page +mod's content scripts. + +@argument {Error} +Listeners are passed a single argument, the +[Error](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error) +object. + + + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/docs/page-worker.md b/tools/addon-sdk-1.3/packages/addon-kit/docs/page-worker.md new file mode 100644 index 0000000..06097ae --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/docs/page-worker.md @@ -0,0 +1,218 @@ + + +The `page-worker` module provides a way to create a permanent, invisible page +and access its DOM. + +Introduction +------------ + +The module exports a constructor function `Page`, which constructs a new page +worker. A page worker may be destroyed, after which its memory is freed, and +you must create a new instance to load another page. + +Page workers have associated content scripts, which are JavaScript scripts that +have access to the content loaded into the pages. You can specify scripts to +load for a page worker, and you communicate with those scripts over an +asynchronous JSON pipe. For more information on content scripts, see +[Working with Content Scripts](dev-guide/addon-development/web-content.html). + +Examples +-------- + +For conciseness, these examples create their content scripts as strings and use +the `contentScript` property. In your own add-ons, you will probably want to +create your content scripts in separate files and pass their URLs using the +`contentScriptFile` property. See +[Working with Content Scripts](dev-guide/addon-development/web-content.html) +for more information. + +### Print all header titles from a Wikipedia article ### + + var pageWorkers = require("page-worker"); + + // This content script sends header titles from the page to the add-on: + var script = "var elements = document.querySelectorAll('h2 > span'); " + + "for (var i = 0; i < elements.length; i++) { " + + " postMessage(elements[i].textContent) " + + "}"; + + // Create a page worker that loads Wikipedia: + pageWorkers.Page({ + contentURL: "http://en.wikipedia.org/wiki/Internet", + contentScript: script, + contentScriptWhen: "ready", + onMessage: function(message) { + console.log(message); + } + }); + +The page worker's "message" event listener, specified by `onMessage`, will print +all the titles it receives from the content script. + + +@class +A `Page` object loads the page specified by its `contentURL` option and +executes any content scripts that have been supplied to it in the +`contentScript` and `contentScriptFile` options. + +The page is not displayed to the user. + +The page worker is loaded as soon as the `Page` object is created and stays +loaded until its `destroy` method is called or the add-on is unloaded. + + +@constructor + Creates an uninitialized page worker instance. +@param [options] {object} + The *`options`* parameter is optional, and if given it should be an object + with any of the following keys: + @prop [contentURL] {string} + The URL of the content to load in the panel. + @prop [allow] {object} + An object with keys to configure the permissions on the page worker. The + boolean key `script` controls if scripts from the page are allowed to run. + `script` defaults to true. + @prop [contentScriptFile] {string,array} + A local file URL or an array of local file URLs of content scripts to load. + Content scripts specified by this option are loaded *before* those specified + by the `contentScript` option. See + [Working with Content Scripts](dev-guide/addon-development/web-content.html) + for help on setting this property. + @prop [contentScript] {string,array} + A string or an array of strings containing the texts of content scripts to + load. Content scripts specified by this option are loaded *after* those + specified by the `contentScriptFile` option. + @prop [contentScriptWhen="end"] {string} + When to load the content scripts. This may take one of the following + values: + + * "start": load content scripts immediately after the document + element for the page is inserted into the DOM, but before the DOM content + itself has been loaded + * "ready": load content scripts once DOM content has been loaded, + corresponding to the + [DOMContentLoaded](https://developer.mozilla.org/en/Gecko-Specific_DOM_Events) + event + * "end": load content scripts once all the content (DOM, JS, CSS, + images) for the page has been loaded, at the time the + [window.onload event](https://developer.mozilla.org/en/DOM/window.onload) + fires + + This property is optional and defaults to "end". + + @prop [onMessage] {function} + Use this to add a listener to the page worker's `message` event. + + + +@property {EventEmitter} +[EventEmitter](packages/api-utils/docs/events.html) object that allows you to: + +* send events to the content script using the `port.emit` function +* receive events from the content script using the `port.on` function + +See the guide to + +communicating using port for details. + + + +@property {string} +The URL of the content loaded. + + + +@property {object} + A object describing permissions for the content. It contains a single key + named `script` whose value is a boolean that indicates whether or not to + execute script in the content. `script` defaults to true. + + + +@property {string,array} +A local file URL or an array of local file URLs of content scripts to load. + + + +@property {string,array} +A string or an array of strings containing the texts of content scripts to +load. + + + +@property {string} + When to load the content scripts. This may have one of the following + values: + + * "start": load content scripts immediately after the document + element for the page is inserted into the DOM, but before the DOM content + itself has been loaded + * "ready": load content scripts once DOM content has been loaded, + corresponding to the + [DOMContentLoaded](https://developer.mozilla.org/en/Gecko-Specific_DOM_Events) + event + * "end": load content scripts once all the content (DOM, JS, CSS, + images) for the page has been loaded, at the time the + [window.onload event](https://developer.mozilla.org/en/DOM/window.onload) + fires + + + + +@method +Unloads the page worker. After you destroy a page worker, its memory is freed +and you must create a new instance if you need to load another page. + + + +@method +Sends a message to the content scripts. +@param message {value} +The message to send. Must be JSON-able. + + + +@method +Registers an event listener with the page worker. See +[Working with Events](dev-guide/addon-development/events.html) for help with +events. +@param type {string} +The type of event to listen for. +@param listener {function} +The listener function that handles the event. + + + +@method +Unregisters an event listener from the page worker. +@param type {string} +The type of event for which `listener` was registered. +@param listener {function} +The listener function that was registered. + + + +@event +If you listen to this event you can receive message events from content +scripts associated with this page worker. When a content script posts a +message using `self.postMessage()`, the message is delivered to the add-on +code in the page worker's `message` event. + +@argument {value} +Listeners are passed a single argument which is the message posted +from the content script. The message can be any +JSON-serializable value + + + +@event +This event is emitted when an uncaught runtime error occurs in one of the +page worker's content scripts. + +@argument {Error} +Listeners are passed a single argument, the +[Error](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error) +object. + + + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/docs/panel.md b/tools/addon-sdk-1.3/packages/addon-kit/docs/panel.md new file mode 100644 index 0000000..45eea2f --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/docs/panel.md @@ -0,0 +1,301 @@ + + + +The `panel` module creates floating modal "popup dialogs" that appear on top of +web content and browser chrome and persist until dismissed by users or programs. +Panels are useful for presenting temporary interfaces to users in a way that is +easier for users to ignore and dismiss than a modal dialog, since panels are +hidden the moment users interact with parts of the application interface outside +them. + +The module exports a single constructor function `Panel` which constructs a +new panel. + +A panel's content is loaded as soon as it is created, before the panel is shown, +and the content remains loaded when a panel is hidden, so it is possible +to keep a panel around in the background, updating its content as appropriate +in preparation for the next time it is shown. + +Your add-on can receive notifications when a panel is shown or hidden by +listening to its `show` and `hide` events. + +Panels have associated content scripts, which are JavaScript scripts that have +access to the content loaded into the panels. An add-on can specify one or +more content scripts to load for a panel, and the add-on can communicate with +those scripts either using the `message` event or by using user-defined +events. See +[Working with Content Scripts](dev-guide/addon-development/web-content.html) +for more information. + +The panel's default style is different for each operating system. +For example, suppose a panel's content is specified with the following HTML: + + + +On OS X it will look like this: + + +
+ +On Windows 7 it will look like this: + + +
+ +On Ubuntu it will look like this: + + +
+ +This helps to ensure that the panel's style is consistent with the dialogs +displayed by Firefox and other applications, but means you need to take care +when applying your own styles. For example, if you set the panel's +`background-color` property to `white` and do not set the `color` property, +then the panel's text will be invisible on OS X although it looks fine on Ubuntu. + +Examples +-------- + +Create and show a simple panel with content from the `data/` directory: + + var data = require("self").data; + var panel = require("panel").Panel({ + contentURL: data.url("foo.html") + }); + + panel.show(); + +The tutorial section on +[web content](dev-guide/addon-development/web-content.html) has +a more complex example using panels. + + +@class +The Panel object represents a floating modal dialog that can by an add-on to +present user interface content. + +Once a panel object has been created it can be shown and hidden using its +`show()` and `hide()` methods. Once a panel is no longer needed it can be +deactivated using `destroy()`. + +The content of a panel is specified using the `contentURL` option. An add-on +can interact with the content of a panel using content scripts which it +supplies in the `contentScript` and/or `contentScriptFile` options. For example, +a content script could create a menu and send the user's selection to the +add-on. + + +@constructor +Creates a panel. +@param options {object} + Options for the panel, with the following keys: + @prop [width] {number} + The width of the panel in pixels. Optional. + @prop [height] {number} + The height of the panel in pixels. Optional. + @prop [contentURL] {string} + The URL of the content to load in the panel. + @prop [allow] {object} + An optional object describing permissions for the content. It should + contain a single key named `script` whose value is a boolean that indicates + whether or not to execute script in the content. `script` defaults to true. + @prop [contentScriptFile] {string,array} + A local file URL or an array of local file URLs of content scripts to load. + Content scripts specified by this property are loaded *before* those + specified by the `contentScript` property. + @prop [contentScript] {string,array} + A string or an array of strings containing the texts of content scripts to + load. Content scripts specified by this property are loaded *after* those + specified by the `contentScriptFile` property. + @prop [contentScriptWhen="end"] {string} + When to load the content scripts. This may take one of the following + values: + + * "start": load content scripts immediately after the document + element for the panel is inserted into the DOM, but before the DOM content + itself has been loaded + * "ready": load content scripts once DOM content has been loaded, + corresponding to the + [DOMContentLoaded](https://developer.mozilla.org/en/Gecko-Specific_DOM_Events) + event + * "end": load content scripts once all the content (DOM, JS, CSS, + images) for the panel has been loaded, at the time the + [window.onload event](https://developer.mozilla.org/en/DOM/window.onload) + fires + + This property is optional and defaults to "end". + + @prop [onMessage] {function} + Include this to listen to the panel's `message` event. + @prop [onShow] {function} + Include this to listen to the panel's `show` event. + @prop [onHide] {function} + Include this to listen to the panel's `hide` event. + + + +@property {EventEmitter} +[EventEmitter](packages/api-utils/docs/events.html) object that allows you to: + +* send events to the content script using the `port.emit` function +* receive events from the content script using the `port.on` function + +See the guide to + +communicating using port for details. + + + +@property {boolean} +Tells if the panel is currently shown or not. This property is read-only. + + + +@property {number} +The height of the panel in pixels. + + + +@property {number} +The width of the panel in pixels. + + + +@property {string} +The URL of the content loaded in the panel. + + + +@property {object} +An object describing permissions for the content. It contains a single key +named `script` whose value is a boolean that indicates whether or not to execute +script in the content. + + + +@property {string,array} +A local file URL or an array of local file URLs of content scripts to load. +Content scripts specified by this property are loaded *before* those +specified by the `contentScript` property. + + + +@property {string,array} +A string or an array of strings containing the texts of content scripts to +load. Content scripts specified by this property are loaded *after* those +specified by the `contentScriptFile` property. + + + +@property {string} +When to load the content scripts. This may have one of the following +values: + +* "start": load content scripts immediately after the document +element for the panel is inserted into the DOM, but before the DOM content +itself has been loaded +* "ready": load content scripts once DOM content has been loaded, +corresponding to the +[DOMContentLoaded](https://developer.mozilla.org/en/Gecko-Specific_DOM_Events) +event +* "end": load content scripts once all the content (DOM, JS, CSS, +images) for the panel has been loaded, at the time the +[window.onload event](https://developer.mozilla.org/en/DOM/window.onload) +fires + + + + +@method +Destroys the panel, unloading any content that was loaded in it. Once +destroyed, the panel can no longer be used. If you just want to hide +the panel and might show it later, use `hide` instead. + + + +@method +Sends a message to the content scripts. +@param message {value} +The message to send. Must be stringifiable to JSON. + + + +@method +Displays the panel. + + + +@method +Stops displaying the panel. + + + +@method +Resizes the panel. +@param width {number} +The new width of the panel in pixels. +@param height {number} +The new height of the panel in pixels. + + + +@method + Registers an event listener with the panel. +@param type {string} + The type of event to listen for. +@param listener {function} + The listener function that handles the event. + + + +@method + Unregisters an event listener from the panel. +@param type {string} + The type of event for which `listener` was registered. +@param listener {function} + The listener function that was registered. + + + +@event +This event is emitted when the panel is shown. + + + +@event +This event is emitted when the panel is hidden. + + + +@event +If you listen to this event you can receive message events from content +scripts associated with this panel. When a content script posts a +message using `self.postMessage()`, the message is delivered to the add-on +code in the panel's `message` event. + +@argument {value} +Listeners are passed a single argument which is the message posted +from the content script. The message can be any +JSON-serializable value. + + + +@event +This event is emitted when an uncaught runtime error occurs in one of the +panel's content scripts. + +@argument {Error} +Listeners are passed a single argument, the +[Error](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error) +object. + + + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/docs/passwords.md b/tools/addon-sdk-1.3/packages/addon-kit/docs/passwords.md new file mode 100644 index 0000000..6a4b246 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/docs/passwords.md @@ -0,0 +1,564 @@ + + +The `passwords` module allows add-ons to interact with Firefox's +[Password Manager](http://support.mozilla.com/en-US/kb/Remembering%20passwords) +to add, retrieve and remove stored credentials. + +A _credential_ is the set of information a user supplies to authenticate +herself with a service. Typically a credential consists of a username and a +password. + +Using this module you can: + +1. **Search** for credentials which have been stored in the Password Manager. + You can then use the credentials to access their related service (for + example, by logging into a web site). + +2. **Store** credentials in the Password Manager. You can store different sorts + of credentials, as outlined in the "Credentials" section below. + +3. **Remove** stored credentials from the Password Manager. + +## Credentials ## + +In this API, credentials are represented by objects. + +You create credential objects to pass into the API, and the API also returns +credential objects to you. The sections below explain both the properties you +should define on credential objects and the properties you can expect on +credential objects returned by the API. + +All credential objects include `username` and `password` properties. Different +sorts of stored credentials include various additional properties, as +outlined in this section. + +You can use the Passwords API with three sorts of credentials: + +* Add-on credentials +* HTML form credentials +* HTTP Authentication credentials + +### Add-on Credential ### + +These are associated with your add-on rather than a particular web site. +They contain the following properties: + + +++ + + + + + + + + + + + + + + + + + + + + +
+ username + + The username. +
+ password + + The password. +
+ url + +

For an add-on credential, this property is of the form:
+ addon:<addon-id>, where <addon-id> + is the add-on's + + Program ID.

+

You don't supply this value when storing an add-on credential: it is + automatically generated for you. However, you can use it to work out + which stored credentials belong to your add-on by comparing it with the + uri property of the + self + module.

+
+ realm + +

You can use this as a name for the credential, to distinguish + it from any other credentials you've stored.

+

The realm is displayed + in Firefox's Password Manager, under "Site", in brackets after the URL. + For example, if the realm for a credential is "User Registration", then + its "Site" field will look something like:

+ addon:jid0-01mBBFyu0ZAXCFuB1JYKooSTKIc (User Registration) +
+ +### HTML Form Credential ### + +If a web service uses HTML forms to authenticate its users, then the +corresponding credential is an HTML Form credential. + +It contains the following properties: + + +++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ username + + The username. +
+ password + + The password. +
+ url + + The URL for the web service which requires the credential. + You should omit anything after the hostname and (optional) port. +
+ formSubmitURL + + The value of the form's "action" attribute. + You should omit anything after the hostname and (optional) port. + If the form doesn't contain an "action" attribute, this property should + match the url property. +
+ usernameField + + The value of the "name" attribute for the form's username field. +
+ passwordField + + The value of the "name" attribute for the form's password field. +
+ +So: given a form at `http://www.example.com/login` +with the following HTML: + + + +The corresponding values for the credential (excluding username and password) +should be: + +
+  url: "http://www.example.com"
+  formSubmitURL: "http://login.example.com"
+  usernameField: "uname"
+  passwordField: "pword"
+
+ +Note that for both `url` and `formSubmitURL`, the portion of the URL after the +hostname is omitted. + +### HTTP Authentication Credential ### + +These are used to authenticate the user to a web site +which uses HTTP Authentication, as detailed in +[RFC 2617](http://tools.ietf.org/html/rfc2617). +They contain the following properties: + + +++ + + + + + + + + + + + + + + + + + + + + +
+ username + + The username. +
+ password + + The password. +
+ url + + The URL for the web service which requires the credential. + You should omit anything after the hostname and (optional) port. +
+ realm + +

The WWW-Authenticate response header sent by the server may include a + "realm" field as detailed in + RFC 2617. If it does, + this property contains the value for the "realm" field. Otherwise, it is + omitted.

+

The realm is displayed in Firefox's Password Manager, under "Site", + in brackets after the URL.

+
+ +So: if a web server at `http://www.example.com` requested authentication with +a response code like this: + +
+  HTTP/1.0 401 Authorization Required
+  Server: Apache/1.3.27
+  WWW-Authenticate: Basic realm="ExampleCo Login"
+
+ +The corresponding values for the credential (excluding username and password) +should be: + +
+  url: "http://www.example.com"
+  realm: "ExampleCo Login"
+
+ +## onComplete and onError ## + +This API is explicitly asynchronous, so all its functions take two callback +functions as additional options: `onComplete` and `onError`. + +`onComplete` is called when the operation has completed successfully and +`onError` is called when the function encounters an error. + +Because the `search` function is expected to return a list of matching +credentials, its `onComplete` option is mandatory. Because the other functions +don't return a value in case of success their `onComplete` options are +optional. + +For all functions, `onError` is optional. + + +@function + +This function is used to retrieve a credential, or a list of credentials, +stored in the Password Manager. + +You pass it any subset of the possible properties a credential can contain. +Credentials which match all the properties you supplied are returned as an +argument to the `onComplete` callback. + +So if you pass in an empty set of properties, all stored credentials are +returned: + + function show_all_passwords() { + require("passwords").search({ + onComplete: function onComplete(credentials) { + credentials.forEach(function(credential) { + console.log(credential.username); + console.log(credential.password); + }); + } + }); + } + +If you pass it a single property, only credentials matching that property are +returned: + + function show_passwords_for_joe() { + require("passwords").search({ + username: "joe", + onComplete: function onComplete(credentials) { + credentials.forEach(function(credential) { + console.log(credential.username); + console.log(credential.password); + }); + } + }); + } + +If you pass more than one property, returned credentials must match all of +them: + + function show_google_password_for_joe() { + require("passwords").search({ + username: "joe", + url: "https://www.google.com", + onComplete: function onComplete(credentials) { + credentials.forEach(function(credential) { + console.log(credential.username); + console.log(credential.password); + }); + } + }); + } + +To retrieve only credentials associated with your add-on, use the `url` +property, initialized from `self.uri`: + + function show_my_addon_passwords() { + require("passwords").search({ + url: require("self").uri, + onComplete: function onComplete(credentials) { + credentials.forEach(function(credential) { + console.log(credential.username); + console.log(credential.password); + }); + } + }); + } + +@param options {object} +The `options` object may contain any credential properties. It is used to +restrict the set of credentials returned by the `search` function. + +See "Credentials" above for details on what these properties should be. + +Additionally, `options` must contain a function assigned to its `onComplete` +property: this is called when the function completes and is passed the set of +credentials retrieved. + +`options` may contain a function assigned to its `onError` property, which is +called if the function encounters an error. `onError` is passed the error as an +[nsIException](https://developer.mozilla.org/en/nsIException) object. + +@prop [username] {string} +The username for the credential. + +@prop [password] {string} +The password for the credential. + +@prop [url] {string} +The URL associated with the credential. + +@prop [formSubmitURL] {string} +The URL an HTML form credential is submitted to. + +@prop [realm] {string} +For HTTP Authentication credentials, the realm for which the credential was +requested. For add-on credentials, a name for the credential. + +@prop [usernameField] {string} +The value of the `name` attribute for the user name input field in a form. + +@prop [passwordField] {string} +The value of the `name` attribute for the password input field in a form. + +@prop onComplete {function} +The callback function that is called once the function completes successfully. +It is passed all the matching credentials as a list. This is the only +mandatory option. + +@prop [onError] {function} +The callback function that is called if the function failed. The +callback is passed an `error` containing a reason of a failure: this is an +[nsIException](https://developer.mozilla.org/en/nsIException) object. + + + + +@function + +This function is used to store a credential in the Password Manager. + +It takes an `options` object as an argument: this contains all the properties +for the new credential. + +As different sorts of credentials contain different properties, the +appropriate options differ depending on the sort of credential being stored. + +To store an add-on credential: + + require("passwords").store({ + realm: "User Registration", + username: "joe", + password: "SeCrEt123", + }); + +To store an HTML form credential: + + require("passwords").store({ + url: "http://www.example.com", + formSubmitURL: "http://login.example.com", + username: "joe", + usernameField: "uname", + password: "SeCrEt123", + passwordField: "pword" + }); + +To store an HTTP Authentication credential: + + require("passwords").store({ + url: "http://www.example.com", + realm: "ExampleCo Login", + username: "joe", + password: "SeCrEt123", + }); + +See "Credentials" above for more details on how to set these properties. + +The options parameter may also include `onComplete` and `onError` +callback functions, which are called when the function has completed +successfully and when it encounters an error, respectively. These options +are both optional. + +@param options {object} +An object containing the properties of the credential to be stored, and +optional `onComplete` and `onError` callback functions. + +@prop username {string} +The username for the credential. + +@prop password {string} +The password for the credential. + +@prop [url] {string} +The URL to which the credential applies. Omitted for add-on +credentials. + +@prop [formSubmitURL] {string} +The URL a form-based credential was submitted to. Omitted for add-on +credentials and HTTP Authentication credentials. + +@prop [realm] {string} +For HTTP Authentication credentials, the realm for which the credential was +requested. For add-on credentials, a name for the credential. + +@prop [usernameField] {string} +The value of the `name` attribute for the username input in a form. +Omitted for add-on credentials and HTTP Authentication credentials. + +@prop [passwordField] {string} +The value of the `name` attribute for the password input in a form. +Omitted for add-on credentials and HTTP Authentication credentials. + +@prop [onComplete] {function} +The callback function that is called once the function completes successfully. + +@prop [onError] {function} +The callback function that is called if the function failed. The +callback is passed an `error` argument: this is an +[nsIException](https://developer.mozilla.org/en/nsIException) object. + + + + +@function + +Removes a stored credential. You supply it all the properties of the credential +to remove, along with optional `onComplete` and `onError` callbacks. + +Because you must supply all the credential's properties, it may be convenient +to call `search` first, and use its output as the input to `remove`. For +example, to remove all of joe's stored credentials: + + require("passwords").search({ + username: "joe", + onComplete: function onComplete(credentials) { + credentials.forEach(require("passwords").remove); + }) + }); + +To change an existing credential just call `store` after `remove` succeeds: + + require("passwords").remove({ + realm: "User Registration", + username: "joe", + password: "SeCrEt123" + onComplete: function onComplete() { + require("passwords").store({ + realm: "User Registration", + username: "joe", + password: "{{new password}}" + }) + } + }); + +@param options {object} + +An object containing all the properties of the credential to be removed, +and optional `onComplete` and `onError` callback functions. + +@prop username {string} +The username for the credential. + +@prop password {string} +The password for the credential. + +@prop [url] {string} +The URL to which the credential applies. Omitted for add-on +credentials. + +@prop [formSubmitURL] {string} +The URL a form-based credential was submitted to. Omitted for add-on +credentials and HTTP Authentication credentials. + +@prop [realm] {string} +For HTTP Authentication credentials, the realm for which the credential was +requested. For add-on credentials, a name for the credential. + +@prop [usernameField] {string} +The value of the `name` attribute for the username input in a form. +Omitted for add-on credentials and HTTP Authentication credentials. + +@prop [passwordField] {string} +The value of the `name` attribute for the password input in a form. +Omitted for add-on credentials and HTTP Authentication credentials. + +@prop [onComplete] {function} +The callback function that is called once the function has completed +successfully. + +@prop [onError] {function} +The callback function that is called if the function failed. The +callback is passed an `error` argument: this is an +[nsIException](https://developer.mozilla.org/en/nsIException) object. + + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/docs/private-browsing.md b/tools/addon-sdk-1.3/packages/addon-kit/docs/private-browsing.md new file mode 100644 index 0000000..cad6166 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/docs/private-browsing.md @@ -0,0 +1,46 @@ + + + + +The `private-browsing` module allows you to access Firefox's private browsing +mode, detecting if it is active and when its state changes. + +This module is available in all applications. However, only Firefox will ever +transition into or out of private browsing mode. For all other applications, +`pb.isActive` will always be `false`, and none of the events will be emitted. + + +@property {boolean} + This read-only boolean is true if private browsing mode is turned on. + + + +@function + Turns on private browsing mode. + + + +@function + Turns off private browsing mode. + + + +@event +Emitted immediately after the browser enters private browsing mode. + + var pb = require("private-browsing"); + pb.on("start", function() { + // Do something when the browser starts private browsing mode. + }); + + + + +@event +Emitted immediately after the browser exits private browsing mode. + + var pb = require("private-browsing"); + pb.on("stop", function() { + // Do something when the browser stops private browsing mode. + }); + \ No newline at end of file diff --git a/tools/addon-sdk-1.3/packages/addon-kit/docs/request.md b/tools/addon-sdk-1.3/packages/addon-kit/docs/request.md new file mode 100644 index 0000000..77bea43 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/docs/request.md @@ -0,0 +1,192 @@ +The `request` module lets you make simple yet powerful network requests. + + +@class +The `Request` object is used to make `GET` or `POST` network requests. It is +constructed with a URL to which the request is sent. Optionally the user may +specify a collection of headers and content to send alongside the request and +a callback which will be executed once the request completes. + +Once a `Request` object has been created a `GET` request can be executed by +calling its `get()` method, or a `POST` request by calling its `post()` method. + +When the server completes the request, the `Request` object emits a "complete" +event. Registered event listeners are passed a `Response` object. + +Each `Request` object is designed to be used once. Once `GET` or `POST` are +called, attempting to call either will throw an error. + +Since the request is not being made by any particular website, requests made +here are not subject to the same-domain restriction that requests made in web +pages are subject to. + +With the exception of `response`, all of a `Request` object's properties +correspond with the options in the constructor. Each can be set by simply +performing an assignment. However, keep in mind that the same validation rules +that apply to `options` in the constructor will apply during assignment. Thus, +each can throw if given an invalid value. + +The example below shows how to use Request to get the most recent public tweet. + + var Request = require("request").Request; + var latestTweetRequest = Request({ + url: "http://api.twitter.com/1/statuses/public_timeline.json", + onComplete: function (response) { + var tweet = response.json[0]; + console.log("User: " + tweet.user.screen_name); + console.log("Tweet: " + tweet.text); + } + }); + + // Be a good consumer and check for rate limiting before doing more. + Request({ + url: "http://api.twitter.com/1/account/rate_limit_status.json", + onComplete: function (response) { + if (response.json.remaining_hits) { + latestTweetRequest.get(); + } else { + console.log("You have been rate limited!"); + } + } + }).get(); + + +@constructor +This constructor creates a request object that can be used to make network +requests. The constructor takes a single parameter `options` which is used to +set several properties on the resulting `Request`. +@param options {object} + @prop url {string} + This is the url to which the request will be made. + + @prop [onComplete] {function} + This function will be called when the request has received a response (or in + terms of XHR, when `readyState == 4`). The function is passed a `Response` + object. + + @prop [headers] {object} + An unordered collection of name/value pairs representing headers to send + with the request. + + @prop [content] {string,object} + The content to send to the server. If `content` is a string, it should be + URL-encoded (use `encodeURIComponent`). If `content` is an object, it + should be a collection of name/value pairs. Nested objects & arrays should + encode safely. + + For `GET` requests, the query string (`content`) will be appended to the + URL. For `POST` requests, the query string will be sent as the body of the + request. + + @prop [contentType] {string} + The type of content to send to the server. This explicitly sets the + `Content-Type` header. The default value is `application/x-www-form-urlencoded`. + + @prop [overrideMimeType] {string} + Use this string to override the MIME type returned by the server in the + response's Content-Type header. You can use this to treat the content as a + different MIME type, or to force text to be interpreted using a specific + character. + + For example, if you're retrieving text content which was encoded as + ISO-8859-1 (Latin 1), it will be given a content type of "utf-8" and + certain characters will not display correctly. To force the response to + be interpreted as Latin-1, use `overrideMimeType`: + + var Request = require("request").Request; + var quijote = Request({ + url: "http://www.latin1files.org/quijote.txt", + overrideMimeType: "text/plain; charset=latin1", + onComplete: function (response) { + console.log(response.text); + } + }); + + quijote.get(); + + + + +@property {string} + + + +@property {object} + + + +@property {string,object} + + + +@property {string} + + + +@property {Response} + + + +@method +Make a `GET` request. +@returns {Request} + + + +@method +Make a `POST` request. +@returns {Request} + + + +@event +The `Request` object emits this event when the request has completed and a +response has been received. + +@argument {Response} +Listener functions are passed the response to the request as a `Response` object. + + + + + + +@class +The Response object contains the response to a network request issued using a +`Request` object. It is returned by the `get()` or `post()` method of a +`Request` object. + +All members of a `Response` object are read-only. + +@property {string} +The content of the response as plain text. + + + +@property {object} +The content of the response as a JavaScript object. The value will be `null` +if the document cannot be processed by `JSON.parse`. + + + +@property {string} +The HTTP response status code (e.g. *200*). + + + +@property {string} +The HTTP response status line (e.g. *OK*). + + + +@property {object} +The HTTP response headers represented as key/value pairs. + +To print all the headers you can do something like this: + + for (var headerName in response.headers) { + console.log(headerName + " : " + response.headers[headerName]); + } + + + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/docs/selection.md b/tools/addon-sdk-1.3/packages/addon-kit/docs/selection.md new file mode 100644 index 0000000..62a3d64 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/docs/selection.md @@ -0,0 +1,86 @@ + + + +The `selection` module provides a means to get and set text and HTML selections +in the current Firefox page. It can also observe new selections. + +Registering for Selection Notifications +--------------------------------------- + +To be notified when the user makes a selection, register a listener for the +"select" event. Each listener will be called after a selection is made. + + function myListener() { + console.log("A selection has been made."); + } + var selection = require("selection"); + selection.on('select', myListener); + + // You can remove listeners too. + selection.removeListener('select', myListener); + +Iterating Over Discontiguous Selections +--------------------------------------- + +Discontiguous selections can be accessed by iterating over the `selection` +module itself. Each iteration yields a `Selection` object from which `text`, +`html`, and `isContiguous` properties can be accessed. + + +Examples +-------- + +Log the current contiguous selection as text: + + var selection = require("selection"); + if (selection.text) + console.log(selection.text); + +Log the current discontiguous selections as HTML: + + var selection = require("selection"); + if (!selection.isContiguous) { + for (var subselection in selection) { + console.log(subselection.html); + } + } + +Surround HTML selections with delimiters: + + var selection = require("selection"); + selection.on('select', function () { + selection.html = "\\\" + selection.html + "///"; + }); + + +@property {string} + Gets or sets the current selection as plain text. Setting the selection + removes all current selections, inserts the specified text at the location of + the first selection, and selects the new text. Getting the selection when + there is no current selection returns `null`. Setting the selection when there + is no current selection throws an exception. Getting the selection when + `isContiguous` is `true` returns the text of the first selection. + + + +@property {string} + Gets or sets the current selection as HTML. Setting the selection removes all + current selections, inserts the specified text at the location of the first + selection, and selects the new text. Getting the selection when there is no + current selection returns `null`. Setting the selection when there is no + current selection throws an exception. Getting the selection when + `isContiguous` is `true` returns the text of the first selection. + + + +@property {boolean} + `true` if the current selection is a single, contiguous selection, and `false` + if there are two or more discrete selections, each of which may or may not be + spatially adjacent. (Discontiguous selections can be created by the user with + Ctrl+click-and-drag.) + + + +@event + This event is emitted whenever the user makes a new selection in a page. + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/docs/self.md b/tools/addon-sdk-1.3/packages/addon-kit/docs/self.md new file mode 100644 index 0000000..e76ad50 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/docs/self.md @@ -0,0 +1,73 @@ +The `self` module provides access to data that is bundled with the add-on +as a whole. It also provides access to the +[Program ID](dev-guide/addon-development/program-id.html), a value which is +unique for each add-on. + +Note that the `self` module is completely different from the global `self` +object accessible to content scripts, which is used by a content script to +[communicate with the add-on code](dev-guide/addon-development/content-scripts/using-port.html). + + +@property {string} +This property is a printable string that is unique for each add-on. It comes +from the `id` property set in the `package.json` file in the main package +(i.e. the package in which you run `cfx xpi`). While not generally of use to +add-on code directly, it can be used by internal API code to index local +storage and other resources that are associated with a particular add-on. +Eventually, this ID will be unspoofable (see +[JEP 118](https://wiki.mozilla.org/Labs/Jetpack/Reboot/JEP/118) for details). + + + +@property {string} +This property contains the add-on's short name. It comes from the `name` +property in the main package's `package.json` file. + + + +@property {string} +This property contains the add-on's version string. It comes from the +`version` property set in the `package.json` file in the main package. + + + +@property {object} +The `data` object is used to access data that was bundled with the add-on. +This data lives in the main package's `data/` directory, immediately below +the `package.json` file. All files in this directory will be copied into the +XPI and made available through the `data` object. + +The [Package Specification](dev-guide/addon-development/package-spec.html) +section explains the `package.json` file. + + +@method +The `data.load(NAME)` method returns the contents of an embedded data file, +as a string. It is most useful for data that will be modified or parsed in +some way, such as JSON, XML, plain text, or perhaps an HTML template. For +data that can be displayed directly in a content frame, use `data.url(NAME)`. +@param name {string} The filename to be read, relative to the + package's `data` directory. Each package that uses the `self` module + will see its own `data` directory. +@returns {string} + + + +@method +The `data.url(NAME)` method returns a URL instance that points at an embedded +data file. It is most useful for data that can be displayed directly in a +content frame. The URL instance can be passed to a content frame constructor, +such as the Panel: + + var self = require("self"); + var myPanel = require("panel").Panel({ + contentURL: self.data.url("my-panel-content.html") + }); + myPanel.show(); + +@param name {string} The filename to be read, relative to the + package's `data` directory. Each package that uses the `self` module + will see its own `data` directory. +@returns {URL} + + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/docs/simple-storage.md b/tools/addon-sdk-1.3/packages/addon-kit/docs/simple-storage.md new file mode 100644 index 0000000..e34e333 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/docs/simple-storage.md @@ -0,0 +1,125 @@ +The `simple-storage` module lets you easily and persistently store data across +application restarts. If you're familiar with [DOM storage][] on the Web, it's +kind of like that, but for add-ons. + +[DOM storage]: https://developer.mozilla.org/en/DOM/Storage + + +Introduction +------------ + +The simple storage module exports an object called `storage` that is persistent +and private to your add-on. It's a normal JavaScript object, and you can treat +it as you would any other. + +To store a value, just assign it to a property on `storage`: + + var ss = require("simple-storage"); + ss.storage.myArray = [1, 1, 2, 3, 5, 8, 13]; + ss.storage.myBoolean = true; + ss.storage.myNull = null; + ss.storage.myNumber = 3.1337; + ss.storage.myObject = { a: "foo", b: { c: true }, d: null }; + ss.storage.myString = "O frabjous day!"; + +You can store array, boolean, number, object, null, and string values. If you'd +like to store other types of values, you'll first have to convert them to +strings or another one of these types. + +Be careful to set properties on the `storage` object and not the module itself: + + // This is no good! + var ss = require("simple-storage"); + ss.foo = "I will not be saved! :("; + + +Quotas +------ + +The simple storage available to your add-on is limited. Currently this limit is +about five megabytes (5,242,880 bytes). You can choose to be notified when you +go over quota, and you should respond by reducing the amount of data in storage. +If the user quits the application while you are over quota, all data stored +since the last time you were under quota will not be persisted. You should not +let that happen. + +To listen for quota notifications, register a listener for the `"OverQuota"` +event. It will be called when your storage goes over quota. + + function myOnOverQuotaListener() { + console.log("Uh oh."); + } + ss.on("OverQuota", myOnOverQuotaListener); + +Listeners can also be removed: + + ss.removeListener("OverQuota", myOnOverQuotaListener); + +To find out how much of your quota you're using, check the module's `quotaUsage` +property. It indicates the percentage of quota your storage occupies. If +you're within your quota, it's a number from 0 to 1, inclusive, and if you're +over, it's a number greater than 1. + +Therefore, when you're notified that you're over quota, respond by removing +storage until your `quotaUsage` is less than or equal to 1. Which particular +data you remove is up to you. For example: + + ss.storage.myList = [ /* some long array */ ]; + ss.on("OverQuota", function () { + while (ss.quotaUsage > 1) + ss.storage.myList.pop(); + }); + + +Private Browsing +---------------- + +*This section applies only to add-ons running on Firefox.* + +If your storage is related to your users' Web history, personal information, or +other sensitive data, your add-on should respect [private browsing mode][SUMO]. +While private browsing mode is active, you should not store any sensitive data. + +Because any kind of data can be placed into simple storage, support for private +browsing is not built into the module. Instead, use the +[`private-browsing`](packages/addon-kit/docs/private-browsing.html) module to +check private browsing status and respond accordingly. + +For example, the URLs your users visit should not be stored during private +browsing. If your add-on records the URL of the selected tab, here's how you +might handle that: + + ss.storage.history = []; + var privateBrowsing = require("private-browsing"); + if (!privateBrowsing.active) { + var url = getSelectedTabURL(); + ss.storage.history.push(url); + } + +For more information on supporting private browsing, see its [Mozilla Developer +Network documentation][MDN]. While that page does not apply specifically to +SDK-based add-ons (and its code samples don't apply at all), you should follow +its guidance on best practices and policies. + +[SUMO]: http://support.mozilla.com/en-US/kb/Private+Browsing +[MDN]: https://developer.mozilla.org/En/Supporting_private_browsing_mode + + + +@property {object} + A persistent object private to your add-on. Properties with array, boolean, + number, object, null, and string values will be persisted. + + + +@property {number} + A number in the range [0, Infinity) that indicates the percentage of quota + occupied by storage. A value in the range [0, 1] indicates that the storage + is within quota. A value greater than 1 indicates that the storage exceeds + quota. + + + +@event +The module emits this event when your add-on's storage goes over its quota. + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/docs/tabs.md b/tools/addon-sdk-1.3/packages/addon-kit/docs/tabs.md new file mode 100644 index 0000000..a6a858f --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/docs/tabs.md @@ -0,0 +1,381 @@ + + + +The `tabs` module provides easy access to tabs and tab-related events. + +The module itself can be used like a basic list of all opened +tabs across all windows. In particular, you can enumerate it: + + var tabs = require('tabs'); + for each (var tab in tabs) + console.log(tab.title); + +You can also access individual tabs by index: + + var tabs = require('tabs'); + + tabs.on('ready', function () { + console.log('first: ' + tabs[0].title); + console.log('last: ' + tabs[tabs.length-1].title); + }); + +You can open a new tab, specifying various properties including location: + + var tabs = require("tabs"); + tabs.open("http://www.example.com"); + +You can register event listeners to be notified when tabs open, close, finish +loading DOM content, or are made active or inactive: + + var tabs = require("tabs"); + + // Listen for tab openings. + tabs.on('open', function onOpen(tab) { + myOpenTabs.push(tab); + }); + + // Listen for tab content loads. + tabs.on('ready', function(tab) { + console.log('tab is loaded', tab.title, tab.url) + }); + +You can get and set various properties of tabs (but note that properties + relating to the tab's content, such as the URL, will not contain valid +values until after the tab's `ready` event fires). By setting the `url` +property you can load a new page in the tab: + + var tabs = require("tabs"); + tabs.on('activate', function(tab) { + tab.url = "http://www.example.com"; + }); + +You can attach a [content script](dev-guide/addon-development/web-content.html) +to the page hosted in a tab, and use that to access and manipulate the page's +content: + + var tabs = require("tabs"); + + tabs.on('activate', function(tab) { + tab.attach({ + contentScript: 'self.postMessage(document.body.innerHTML);', + onMessage: function (message) { + console.log(message); + } + }); + }); + + +@property {Tab} + +The currently active tab in the active window. This property is read-only. To +activate a `Tab` object, call its `activate` method. + +**Example** + + // Get the active tab's title. + var tabs = require("tabs"); + console.log("title of active tab is " + tabs.activeTab.title); + + + +@property {number} +The number of open tabs across all windows. + + + +@function +Opens a new tab. The new tab will open in the active window or in a new window, +depending on the `inNewWindow` option. + +**Example** + + var tabs = require("tabs"); + + // Open a new tab on active window and make tab active. + tabs.open("http://www.mysite.com"); + + // Open a new tab in a new window and make it active. + tabs.open({ + url: "http://www.mysite.com", + inNewWindow: true + }); + + // Open a new tab on active window in the background. + tabs.open({ + url: "http://www.mysite.com", + inBackground: true + }); + + // Open a new tab as an app tab and do something once it's open. + tabs.open({ + url: "http://www.mysite.com", + isPinned: true, + onOpen: function onOpen(tab) { + // do stuff like listen for content + // loading. + } + }); + +@param options {object} +An object containing configurable options for how and where the tab will be +opened, as well as a listeners for the tab events. + +If the only option being used is `url`, then a bare string URL can be passed to +`open` instead of adding at a property of the `options` object. + +@prop [url] {string} +String URL to be opened in the new tab. +This is a required property. + +@prop [inNewWindow] {boolean} +If present and true, a new browser window will be opened and the URL will be +opened in the first tab in that window. This is an optional property. + +@prop [inBackground] {boolean} +If present and true, the new tab will be opened to the right of the active tab +and will not be active. This is an optional property. + +@prop [isPinned] {boolean} +If present and true, then the new tab will be pinned as an +[app tab](http://support.mozilla.com/en-US/kb/what-are-app-tabs). + +@prop [onOpen] {function} +A callback function that will be registered for 'open' event. +This is an optional property. +@prop [onClose] {function} +A callback function that will be registered for 'close' event. +This is an optional property. +@prop [onReady] {function} +A callback function that will be registered for 'ready' event. +This is an optional property. +@prop [onActivate] {function} +A callback function that will be registered for 'activate' event. +This is an optional property. +@prop [onDeactivate] {function} +A callback function that will be registered for 'deactivate' event. +This is an optional property. + + + +@class +A `Tab` instance represents a single open tab. It contains various tab +properties, several methods for manipulation, as well as per-tab event +registration. + +Tabs emit all the events described in the Events section. Listeners are +passed the `Tab` object that triggered the event. + + +@property {string} +The title of the page currently loaded in the tab. +This property can be set to change the tab title. + + + +@property {String} +The URL of the page currently loaded in the tab. +This property can be set to load a different URL in the tab. + + + +@property {string} +The URL of the favicon for the page currently loaded in the tab. +This property is read-only. + + + +@property {integer} +The index of the tab relative to other tabs in the application window. +This property can be set to change its relative position. + + + +@property {boolean} +Whether or not tab is pinned as an [app tab][]. +This property is read-only. +[app tab]:http://support.mozilla.com/en-US/kb/what-are-app-tabs + + + +@property {method} +Returns thumbnail data URI of the page currently loaded in this tab. + + + +@method +Pins this tab as an [app tab][]. +[app tab]:http://support.mozilla.com/en-US/kb/what-are-app-tabs + + + +@method +Unpins this tab. + + + +@method +Closes this tab. + +@param [callback] {function} +A function to be called when the tab finishes its closing process. +This is an optional argument. + + + +@method +Reloads this tab. + + + +@method +Makes this tab active, which will bring this tab to the foreground. + + + +@method + Create a page mod and attach it to the document in the tab. + +**Example** + + var tabs = require("tabs"); + + tabs.on('ready', function(tab) { + tab.attach({ + contentScript: + 'document.body.style.border = "5px solid red";' + }); + }); + +@param options {object} + Options for the page mod, with the following keys: + +@prop [contentScriptFile] {string,array} + The local file URLs of content scripts to load. Content scripts specified + by this option are loaded *before* those specified by the `contentScript` + option. Optional. +@prop [contentScript] {string,array} + The texts of content scripts to load. Content scripts specified by this + option are loaded *after* those specified by the `contentScriptFile` option. + Optional. +@prop [onMessage] {function} + A function called when the page mod receives a message from content scripts. + Listeners are passed a single argument, the message posted from the + content script. + +@returns {Worker} + See [Content Scripts guide](dev-guide/addon-development/web-content.html) + to learn how to use the `Worker` object to communicate with the content script. + + + + +@event + +This event is emitted when the tab is closed. It's also emitted when the +tab's window is closed. + +@argument {Tab} +Listeners are passed the tab object. + + + +@event + +This event is emitted when the DOM for the tab's content is ready. It is +equivalent to the `DOMContentLoaded` event for the given content page. + +A single tab will emit this event every time the DOM is loaded: so it will be +emitted again if the tab's location changes or the content is reloaded. + +After this event has been emitted, all properties relating to the tab's +content can be used. + +@argument {Tab} +Listeners are passed the tab object. + + + +@event + +This event is emitted when the tab is made active. + +@argument {Tab} +Listeners are passed the tab object. + + + +@event + +This event is emitted when the tab is made inactive. + +@argument {Tab} +Listeners are passed the tab object. + + + + + +@event + +This event is emitted when a new tab is opened. This does not mean that +the content has loaded, only that the browser tab itself is fully visible +to the user. + +Properties relating to the tab's content (for example: `title`, `favicon`, +and `url`) will not be correct at this point. If you need to access these +properties, listen for the `ready` event: + + var tabs = require("tabs"); + tabs.on('open', function(tab){ + tab.on('ready', function(tab){ + console.log(tab.url); + }); + }); + +@argument {Tab} +Listeners are passed the tab object that just opened. + + + +@event + +This event is emitted when a tab is closed. When a window is closed +this event will be emitted for each of the open tabs in that window. + +@argument {Tab} +Listeners are passed the tab object that has closed. + + + +@event + +This event is emitted when the DOM for a tab's content is ready. +It is equivalent to the `DOMContentLoaded` event for the given content page. + +A single tab will emit this event every time the DOM is loaded: so it will be +emitted again if the tab's location changes or the content is reloaded. + +After this event has been emitted, all properties relating to the tab's +content can be used. + +@argument {Tab} +Listeners are passed the tab object that has loaded. + + + +@event + +This event is emitted when an inactive tab is made active. + +@argument {Tab} +Listeners are passed the tab object that has become active. + + + +@event + +This event is emitted when the active tab is made inactive. + +@argument {Tab} +Listeners are passed the tab object that has become inactive. + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/docs/timers.md b/tools/addon-sdk-1.3/packages/addon-kit/docs/timers.md new file mode 100644 index 0000000..bc36750 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/docs/timers.md @@ -0,0 +1,48 @@ + + + + + +The `timers` module provides access to web-like timing functionality. + + +@function + Schedules `callback` to be called in `ms` milliseconds. Any additional + arguments are passed straight through to the callback. +@returns {integer} + An ID that can later be used to undo this scheduling, if `callback` hasn't yet + been called. +@param callback {function} + Function to be called. +@param ms {integer} + Interval in milliseconds after which the function will be called. + + + +@function + Given an ID returned from `setTimeout()`, prevents the callback with the ID + from being called (if it hasn't yet been called). +@param ID {integer} + An ID returned from `setTimeout()`. + + + +@function + Schedules `callback` to be called repeatedly every `ms` milliseconds. Any + additional arguments are passed straight through to the callback. +@returns {integer} + An ID that can later be used to unschedule the callback. +@param callback {function} + Function to be called. +@param ms {integer} + Interval in milliseconds at which the function will be called. + + + +@function + Given an ID returned from `setInterval()`, prevents the callback with the ID + from being called again. +@param ID {integer} + An ID returned from `setInterval()`. + + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/docs/widget.md b/tools/addon-sdk-1.3/packages/addon-kit/docs/widget.md new file mode 100644 index 0000000..852ce55 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/docs/widget.md @@ -0,0 +1,693 @@ + + + + +The `widget` module provides your add-on with a simple user interface that is +consistent with other add-ons and blends in well with Firefox. + +## Introduction ## + +"Widgets" are small pieces of content that live in the Firefox 4 +[add-on bar](https://developer.mozilla.org/en/The_add-on_bar). +They can be simple icons or complex web pages. You can attach +[panels](packages/addon-kit/docs/panel.html) to them that open when they're +clicked, or you can define a custom click handler to perform some other action, +like opening a web page in a tab. + +There are a few advantages to using widgets over an ad hoc user interface. +First, your users will be accustomed to interacting with add-ons via widgets and +the add-on bar. Second, it allows Firefox to treat your interface as a +first-class citizen. For example, in the future Firefox may allow the user to +drag widgets from the add-on bar to other toolbars. By exposing your interface +as a widget, your add-on would automatically inherit such functionality. + +## Creation and Content ## + +Widgets can contain images or arbitrary web content. You can include this +content inline as a string by using the `content` property, or point to content +using a URL with the `contentURL` property. + +For example, this widget contains an image, so it looks like a simple icon: + + require("widget").Widget({ + id: "mozilla-icon", + label: "My Mozilla Widget", + contentURL: "http://www.mozilla.org/favicon.ico" + }); + +Upon creation, the widget is automatically added to the add-on bar. +You can set the width of a widget, but the height is fixed so as to fit in the +add-on bar. If the content is an image, it is automatically scaled to be 16x16 +pixels. + +This widget contains an entire web page: + + require("widget").Widget({ + id: "hello-display", + label: "My Hello Widget", + content: "Hello!", + width: 50 + }); + +Widgets are quite small by default, so this example used the `width` property to +grow it in order to show all the text. + +As with many SDK APIs, communication with the content inside your widgets is +handled by [content scripts](dev-guide/addon-development/web-content.html). +So, for example, to be notified when your widget's content has loaded, you can +make a small script that calls back to the widget when it finishes loading. + +## Attaching Panels to Widgets ## + +You can supply a [panel](packages/addon-kit/docs/panel.html) to the widget's +constructor: if you do this, the panel is automatically displayed when the +user clicks the widget. + + data = require("self").data + + var clockPanel = require("panel").Panel({ + width:215, + height:160, + contentURL: data.url("clock.html") + }); + + require("widget").Widget({ + id: "open-clock-btn", + label: "Clock", + contentURL: data.url("History.png"), + panel: clockPanel + }); + + + + +
+ +Note that this is, at the moment, the only way you can attach a panel to a widget. + +You must supply the panel in the widget's constructor for it to work. If you +assign the panel to the widget after construction, the panel can still be shown +but will not be anchored to the widget: + + data = require("self").data + + var clockPanel = require("panel").Panel({ + width:215, + height:160, + contentURL: data.url("clock.html") + }); + + widget = require("widget").Widget({ + id: "open-clock-btn", + label: "Clock", + contentURL: data.url("History.png") + }); + + widget.panel = clockPanel; + + // Will not be anchored + widget.panel.show(); + +Also, if you try to call `panel.show()` inside your widget's `click` event +listener, the panel will not be anchored: + + data = require("self").data + + var clockPanel = require("panel").Panel({ + width:215, + height:160, + contentURL: data.url("clock.html") + }); + + require("widget").Widget({ + id: "open-clock-btn", + label: "Clock", + contentURL: data.url("History.png"), + panel: clockPanel, + onClick: function() { + // Will not be anchored + this.panel.show(); + } + }); + +See [bug 638142](https://bugzilla.mozilla.org/show_bug.cgi?id=638142). + +## Examples ## + +For conciseness, these examples create their content scripts as strings and use +the `contentScript` property. In your own add-ons, you will probably want to +create your content scripts in separate files and pass their URLs using the +`contentScriptFile` property. See +[Working with Content Scripts](dev-guide/addon-development/web-content.html) for more +information. + + var widgets = require("widget"); + + // A basic click-able image widget. + widgets.Widget({ + id: "google-link", + label: "Widget with an image and a click handler", + contentURL: "http://www.google.com/favicon.ico", + onClick: function() { + require("tabs").activeTab.url = "http://www.google.com/"; + } + }); +
+ + // A widget that changes display on mouseover. + widgets.Widget({ + id: "mouseover-effect", + label: "Widget with changing image on mouseover", + contentURL: "http://www.yahoo.com/favicon.ico", + onMouseover: function() { + this.contentURL = "http://www.bing.com/favicon.ico"; + }, + onMouseout: function() { + this.contentURL = "http://www.yahoo.com/favicon.ico"; + } + }); +
+ + // A widget that updates content on a timer. + widgets.Widget({ + id: "auto-update-widget", + label: "Widget that updates content on a timer", + content: "0", + contentScript: 'setTimeout(function() {' + + ' document.body.innerHTML++;' + + '}, 2000)', + contentScriptWhen: "ready" + }); +
+ + // A widget that loads a random Flickr photo every 5 minutes. + widgets.Widget({ + id: "random-flickr", + label: "Random Flickr Photo Widget", + contentURL: "http://www.flickr.com/explore/", + contentScriptWhen: "ready", + contentScript: 'postMessage(document.querySelector(".pc_img").src);' + + 'setTimeout(function() {' + + ' document.location = "http://www.flickr.com/explore/";' + + '}, 5 * 60 * 1000);', + onMessage: function(imgSrc) { + this.contentURL = imgSrc; + }, + onClick: function() { + require("tabs").activeTab.url = this.contentURL; + } + }); +
+ + // A widget created with a specified width, that grows. + let myWidget = widgets.Widget({ + id: "widget-effect", + label: "Wide widget that grows wider on a timer", + content: "I'm getting longer.", + width: 50, + }); + require("timers").setInterval(function() { + myWidget.width += 10; + }, 1000); +
+ + // A widget communicating bi-directionally with a content script. + let widget = widgets.Widget({ + id: "message-test", + label: "Bi-directional communication!", + content: "bar", + contentScriptWhen: "ready", + contentScript: 'on("message", function(message) {' + + ' alert("Got message: " + message);' + + '});' + + 'postMessage("ready");', + onMessage: function(message) { + if (message == "ready") + widget.postMessage("me too"); + } + }); + + +@class +Represents a widget object. + + +@constructor {options} + Creates a new widget. The widget is immediately added to the add-on bar. + +@param options {object} + An object with the following keys: + + @prop label {string} + A required string description of the widget used for accessibility, + title bars, and error reporting. + + @prop id {string} + Mandatory string used to identify your widget in order to save its + location when the user moves it in the browser. + This string has to be unique and must not be changed over time. + + @prop [content] {string} + An optional string value containing the displayed content of the widget. + It may contain HTML. Widgets must have either the `content` property or the + `contentURL` property set. + + If the content is an image, it is automatically scaled to be 16x16 pixels. + + @prop [contentURL] {string} + An optional string URL to content to load into the widget. This can be + [local content](dev-guide/addon-development/web-content.html) or remote + content, an image or web content. Widgets must have either the `content` + property or the `contentURL` property set. + + If the content is an image, it is automatically scaled to be 16x16 pixels. + + @prop [panel] {Panel} + An optional [panel](packages/addon-kit/docs/panel.html) to open when the + user clicks on the widget. Note: If you also register a "click" listener, + it will be called instead of the panel being opened. However, you can show + the panel from the listener by calling `this.panel.show()`. + + @prop [width] {integer} + Optional width in pixels of the widget. If not given, a default width is + used. + + @prop [onClick] {function} + Include this to listen to the widget's `click` event. + + @prop [onMessage] {function} + Include this to listen to the widget's `message` event. + + @prop [onMouseover] {function} + Include this to listen to the widget's `mouseover` event. + + @prop [onMouseout] {function} + Include this to listen to the widget's `mouseout` event. + + @prop [onAttach] {function} + Include this to listen to the widget's `attach` event. + + @prop [tooltip] {string} + Optional text to show when the user's mouse hovers over the widget. If not + given, the `label` is used. + + @prop [allow] {object} + An optional object describing permissions for the content. It should + contain a single key named `script` whose value is a boolean that indicates + whether or not to execute script in the content. `script` defaults to true. + + @prop [contentScriptFile] {string,array} + A local file URL or an array of local file URLs of content scripts to load. + Content scripts specified by this property are loaded *before* those + specified by the `contentScript` property. + + @prop [contentScript] {string,array} + A string or an array of strings containing the texts of content scripts to + load. Content scripts specified by this property are loaded *after* those + specified by the `contentScriptFile` property. + + @prop [contentScriptWhen="end"] {string} + When to load the content scripts. This may take one of the following + values: + + * "start": load content scripts immediately after the document + element for the widget is inserted into the DOM, but before the DOM content + itself has been loaded + * "ready": load content scripts once DOM content has been loaded, + corresponding to the + [DOMContentLoaded](https://developer.mozilla.org/en/Gecko-Specific_DOM_Events) + event + * "end": load content scripts once all the content (DOM, JS, CSS, + images) for the widget has been loaded, at the time the + [window.onload event](https://developer.mozilla.org/en/DOM/window.onload) + fires + + This property is optional and defaults to "end". + + + + +@method + Removes the widget from the add-on bar. + + + +@method + Sends a message to the widget's content scripts. +@param data {value} + The message to send. + The message can be any +JSON-serializable value. + + + +@method + Registers an event listener with the widget. +@param type {string} + The type of event to listen for. +@param listener {function} + The listener function that handles the event. + + + +@method + Unregisters an event listener from the widget. +@param type {string} + The type of event for which `listener` was registered. +@param listener {function} + The listener function that was registered. + + + +@method + Retrieve a `WidgetView` instance of this widget relative to a browser window. +@param window {BrowserWindow} + The [BrowserWindow](packages/addon-kit/docs/windows.html) instance to match. +@returns {WidgetView} + A `WidgetView` instance associated with the browser window. Any changes + subsequently applied to this object will only be applied to the widget + attached to that window. + + + +@property {string} + The widget's label. Read-only. + + + +@property {string} + A string containing the widget's content. It can contain HTML. Setting it + updates the widget's appearance immediately. However, if the widget was + created using `contentURL`, then this property is meaningless, and setting it + has no effect. + + + +@property {string} + The URL of content to load into the widget. This can be + [local content](dev-guide/addon-development/web-content.html) or remote + content, an image or web content. Setting it updates the widget's appearance + immediately. However, if the widget was created using `content`, then this + property is meaningless, and setting it has no effect. + + + +@property {Panel} + A [panel](packages/addon-kit/docs/panel.html) to open when the user clicks on + the widget. + + + +@property {number} + The widget's width in pixels. Setting it updates the widget's appearance + immediately. + + + +@property {string} + The text of the tooltip that appears when the user hovers over the widget. + + + +@property {object} + A object describing permissions for the content. It contains a single key + named `script` whose value is a boolean that indicates whether or not to + execute script in the content. + + + +@property {string,array} + A local file URL or an array of local file URLs of content scripts to load. + + + +@property {string,array} + A string or an array of strings containing the texts of content scripts to + load. + + + +@property {string} + When to load the content scripts. This may have one of the following + values: + + * "start": load content scripts immediately after the document + element for the widget is inserted into the DOM, but before the DOM content + itself has been loaded + * "ready": load content scripts once DOM content has been loaded, + corresponding to the + [DOMContentLoaded](https://developer.mozilla.org/en/Gecko-Specific_DOM_Events) + event + * "end": load content scripts once all the content (DOM, JS, CSS, + images) for the widget has been loaded, at the time the + [window.onload event](https://developer.mozilla.org/en/DOM/window.onload) + fires + + + + +@property {EventEmitter} +[EventEmitter](packages/api-utils/docs/events.html) object that allows you to: + +* send events to the content script using the `port.emit` function +* receive events from the content script using the `port.on` function + +See the guide to + +communicating using port for details. + + + +@event +This event is emitted when a new `WidgetView` object is created using the +`getView()` function. + + + +@event +This event is emitted when the widget is clicked. + + + +@event +If you listen to this event you can receive message events from content +scripts associated with this widget. When a content script posts a +message using `self.postMessage()`, the message is delivered to the add-on +code in the widget's `message` event. + +@argument {value} +Listeners are passed a single argument which is the message posted +from the content script. The message can be any +JSON-serializable value. + + + +@event +This event is emitted when the user moves the mouse over the widget. + + + +@event +This event is emitted when the user moves the mouse away from the widget. + + + + + + +@class +Represents a widget instance specific to one browser window. + +Anything you do to an instance of this object will only be applied to the +instance attached to its browser window: widget instances attached to other +browser windows will be unaffected. + +By contrast, any changes you make to an instance of the normal `Widget` class +will be applied across all browser windows. + +This class has all the same methods, attributes and events as the `Widget` +class except for the `getView` method and the `attach` event. + +In this example `WidgetView` is used to display different content for +`http` and `https` schemes: + + // A widget that update its content specifically to each window. + let tabs = require("tabs"); + let windows = require("windows").browserWindows; + let widget = widgets.Widget({ + id: "window-specific-test", + label: "Widget with content specific to each window", + content: " ", + width: 50 + }); + // Observe tab switch or document changes in each existing tab: + function updateWidgetState(tab) { + let view = widget.getView(tab.window); + if (!view) return; + // Update widget displayed text: + view.content = tab.url.match(/^https/) ? "Secured" : "Unsafe"; + } + tabs.on('ready', updateWidgetState); + tabs.on('activate', updateWidgetState); + + +@method + Removes the widget view from the add-on bar. + + + +@method + Sends a message to the widget view's content scripts. +@param data {value} + The message to send. The message can be any +JSON-serializable value. + + + +@method + Registers an event listener with the widget view. +@param type {string} + The type of event to listen for. +@param listener {function} + The listener function that handles the event. + + + +@method + Unregisters an event listener from the widget view. +@param type {string} + The type of event for which `listener` was registered. +@param listener {function} + The listener function that was registered. + + + +@property {string} + The widget view's label. Read-only. + + + +@property {string} + A string containing the widget view's content. It can contain HTML. + Setting it updates the widget view's appearance immediately. However, + if the widget view was created using `contentURL`, then this property + is meaningless, and setting it has no effect. + + + +@property {string} + The URL of content to load into the widget view. This can be + [local content](dev-guide/addon-development/web-content.html) or remote + content, an image or web content. Setting it updates the widget view's + appearance immediately. However, if the widget view was created using + `content`, then this property is meaningless, and setting it has no effect. + + + +@property {Panel} + A [panel](packages/addon-kit/docs/panel.html) to open when the user clicks on + the widget view. + + + +@property {number} + The widget view's width in pixels. Setting it updates the widget view's + appearance immediately. + + + +@property {string} + The text of the tooltip that appears when the user hovers over the widget + view. + + + +@property {object} + A object describing permissions for the content. It contains a single key + named `script` whose value is a boolean that indicates whether or not to + execute script in the content. + + + +@property {string,array} + A local file URL or an array of local file URLs of content scripts to load. + + + +@property {string,array} + A string or an array of strings containing the texts of content scripts to + load. + + + +@property {string} + When to load the content scripts. This may have one of the following + values: + + * "start": load content scripts immediately after the document + element for the widget view is inserted into the DOM, but before the DOM + content itself has been loaded + * "ready": load content scripts once DOM content has been loaded, + corresponding to the + [DOMContentLoaded](https://developer.mozilla.org/en/Gecko-Specific_DOM_Events) + event + * "end": load content scripts once all the content (DOM, JS, CSS, + images) for the widget view has been loaded, at the time the + [window.onload event](https://developer.mozilla.org/en/DOM/window.onload) + fires + + + + +@property {EventEmitter} +[EventEmitter](packages/api-utils/docs/events.html) object that allows you to: + +* send events to the content script using the `port.emit` function +* receive events from the content script using the `port.on` + +See the guide to + +communicating using port for details. + + + +@event +The `detach` event is fired when the widget view is removed from its related +window. +This can occur if the window is closed, Firefox exits, or the add-on is +disabled. + + + +@event +This event is emitted when the widget view is clicked. + + + +@event +If you listen to this event you can receive message events from content +scripts associated with this widget view. When a content script posts a +message using `self.postMessage()`, the message is delivered to the add-on +code in the widget view's `message` event. + +@argument {value} +Listeners are passed a single argument which is the message posted +from the content script. The message can be any +JSON-serializable value. + + + +@event +This event is emitted when the user moves the mouse over the widget view. + + + +@event +This event is emitted when the user moves the mouse away from the widget view. + + + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/docs/windows.md b/tools/addon-sdk-1.3/packages/addon-kit/docs/windows.md new file mode 100644 index 0000000..a5bed95 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/docs/windows.md @@ -0,0 +1,187 @@ + + + +The `windows` module provides easy access to browser windows, their +tabs, and open/close related functions and events. + +This module currently only supports browser windows and does not provide +access to non-browser windows such as the Bookmarks Library, preferences +or other non-browser windows created via add-ons. + + +@property {List} +An object that contains various properties and methods to access +functionality from browser windows, such as opening new windows, accessing +their tabs or switching the current active window. + +`browserWindows` provides access to all the currently open browser windows: + + var windows = require("windows"); + for each (var window in windows.browserWindows) { + console.log(window.title); + } + + console.log(windows.browserWindows.length); + +Object emits all the events listed under "Events" section. + +####Examples#### + + var windows = require("windows").browserWindows; + + // add a listener to the 'open' event + windows.on('open', function(window) { + myOpenWindows.push(window); + }); + + // add a listener to the 'close' event + windows.on('close', function(window) { + console.log("A window was closed."); + }); + + +@property {BrowserWindow} + +The currently active window. This property is read-only. + +**Example** + + // get + var windows = require("windows"); + console.log("title of active window is " + + windows.browserWindows.activeWindow.title); + + anotherWindow.activate(); + // set + windows.activeWindow == anotherWindow // true + + + + + +@function +Open a new window. + + var windows = require("windows").browserWindows; + + // Open a new window. + windows.open("http://www.example.com"); + + // Open a new window and set a listener for "open" event. + windows.open({ + url: "http://www.example.com", + onOpen: function(window) { + // do stuff like listen for content + // loading. + } + }); + +Returns the window that was opened: + + var widgets = require("widget"); + var windows = require("windows").browserWindows; + + var example = windows.open("http://www.example.com"); + + var widget = widgets.Widget({ + id: "close-window", + label: "Close window", + contentURL: "http://www.mozilla.org/favicon.ico", + onClick: function() { + example.close(); + } + }); + +@param options {object} +An object containing configurable options for how this window will be opened, +as well as a callback for being notified when the window has fully opened. + +If the only option being used is `url`, then a bare string URL can be passed to +`open` instead of specifying it as a property of the `options` object. + +@prop url {string} +String URL to be opened in the new window. +This is a required property. + +@prop [onOpen] {function} +A callback function that is called when the window has opened. This does not +mean that the URL content has loaded, only that the window itself is fully +functional and its properties can be accessed. This is an optional property. + +@prop [onClose] {function} +A callback function that is called when the window will be called. +This is an optional property. + +@returns {BrowserWindow} + + + +@class +A `BrowserWindow` instance represents a single open window. They can be +retrieved from the `browserWindows` property exported by this module. + + var windows = require("windows").browserWindows; + + //Print how many tabs the current window has + console.log("The active window has " + + windows.activeWindow.tabs.length + + " tabs."); + + // Print the title of all browser windows + for each (var window in windows) { + console.log(window.title); + } + + // close the active window + windows.activeWindow.close(); + + windows.activeWindow.close(function() { + console.log("The active window was closed"); + }); + + +@property {string} +The current title of the window. Usually the title of the active tab, +plus an app identifier. +This property is read-only. + + + +@property {TabList} +A live list of tabs in this window. This object has the same interface as the +[`tabs` API](packages/addon-kit/docs/tabs.html), except it contains only the +tabs in this window, not all tabs in all windows. This property is read-only. + + + +@method +Makes window active, which will focus that window and bring it to the +foreground. + + + +@method +Close the window. + +@param [callback] {function} +A function to be called when the window finishes its closing process. +This is an optional argument. + + + + + +@event +Event emitted when a new window is open. +This does not mean that the content has loaded, only that the browser window +itself is fully visible to the user. +@argument {Window} +Listeners are passed the `window` object that triggered the event. + + + +@event +Event emitted when a window is closed. +@argument {Window} +Listeners are passed the `window` object that triggered the event. + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/lib/clipboard.js b/tools/addon-sdk-1.3/packages/addon-kit/lib/clipboard.js new file mode 100644 index 0000000..24f4641 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/lib/clipboard.js @@ -0,0 +1,266 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Paul O’Shannessy (Original Author) + * Dietrich Ayala + * Myk Melez + * Erik Vold + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +"use strict"; + +const {Cc,Ci} = require("chrome"); +const errors = require("api-utils/errors"); +const apiUtils = require("api-utils/api-utils"); + +/* +While these data flavors resemble Internet media types, they do +no directly map to them. +*/ +const kAllowableFlavors = [ + "text/unicode", + "text/html" + /* CURRENTLY UNSUPPORTED FLAVORS + "text/plain", + "image/png", + "image/jpg", + "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" } + // Images are currently unsupported. + //{ 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); + + +exports.set = function(aData, aDataType) { + let options = { + data: aData, + datatype: aDataType || "text" + }; + options = apiUtils.validateOptions(options, { + data: { + is: ["string"] + }, + datatype: { + is: ["string"] + } + }); + + var flavor = fromJetpackFlavor(options.datatype); + + if (!flavor) + throw new Error("Invalid flavor"); + + // 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)."); + + 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; + // TODO: images! + 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 || "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)."); + + 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; + default: + data = null; + } + + return data; +}; + +exports.__defineGetter__("currentFlavors", function() { + // 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; +}); + +// 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/tools/addon-sdk-1.3/packages/addon-kit/lib/context-menu.js b/tools/addon-sdk-1.3/packages/addon-kit/lib/context-menu.js new file mode 100644 index 0000000..3c43e35 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/lib/context-menu.js @@ -0,0 +1,1527 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Drew Willcoxon (Original Author) + * Irakli Gozalishvili + * Matteo Ferretti + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +"use strict"; + +const {Ci} = require("chrome"); + +if (!require("api-utils/xul-app").is("Firefox")) { + throw new Error([ + "The context-menu module currently supports only Firefox. In the future ", + "we would like it to support other applications, however. Please see ", + "https://bugzilla.mozilla.org/show_bug.cgi?id=560716 for more information." + ].join("")); +} + +const apiUtils = require("api-utils/api-utils"); +const collection = require("api-utils/collection"); +const { Worker } = require("api-utils/content"); +const url = require("api-utils/url"); +const { MatchPattern } = require("api-utils/match-pattern"); +const { EventEmitterTrait: EventEmitter } = require("api-utils/events"); +const observerServ = require("api-utils/observer-service"); +const jpSelf = require("self"); +const winUtils = require("api-utils/window-utils"); +const { Trait } = require("api-utils/light-traits"); +const { Cortex } = require("api-utils/cortex"); +const timer = require("timer"); + +// All user items we add have this class name. +const ITEM_CLASS = "jetpack-context-menu-item"; + +// Items in the top-level context menu also have this class. +const TOPLEVEL_ITEM_CLASS = "jetpack-context-menu-item-toplevel"; + +// Items in the overflow submenu also have this class. +const OVERFLOW_ITEM_CLASS = "jetpack-context-menu-item-overflow"; + +// The ID of the menu separator that separates standard context menu items from +// our user items. +const SEPARATOR_ID = "jetpack-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 ID of the overflow sub-xul:menu. +const OVERFLOW_MENU_ID = "jetpack-content-menu-overflow-menu"; + +// The ID of the overflow submenu's xul:menupopup. +const OVERFLOW_POPUP_ID = "jetpack-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, +]; + +// This is used to access private properties of Item and Menu instances. +const PRIVATE_PROPS_KEY = { + valueOf: function valueOf() "private properties key" +}; + +// Used as an internal ID for items and as part of a public ID for item DOM +// elements. Careful: This number is not necessarily unique to any one instance +// of the module. For each module instance, when the first item is created this +// number will be 0, when the second is created it will be 1, and so on. +let nextItemID = 0; + +// The number of items that haven't finished initializing yet. See +// AIT__finishActiveItemInit(). +let numItemsWithUnfinishedInit = 0; + +exports.Item = Item; +exports.Menu = Menu; +exports.Separator = Separator; + + +// A word about traits and privates. `this` inside of traits methods is an +// object private to the implementation. It should never be publicly leaked. +// We use Cortex in the exported menu item constructors to create public +// reflections of the private objects that hide private properties -- those +// prefixed with an underscore. Public reflections are attached to the private +// objects via the `_public` property. +// +// All item objects passed into the implementation by the client will be public +// reflections, not private objects. Likewise, all item objects passed out of +// the implementation to the client must be public, not private. Mixing up +// public and private is bad and easy to do, so not only are private objects +// restricted to the implementation, but as much as possible we try to restrict +// them to the Item, Menu, and Separator traits and constructors. Everybody +// else in the implementation should expect to be passed public reflections, and +// they must specifically request private objects via privateItem(). + +// Item, Menu, and Separator are composed of this trait. +const ItemBaseTrait = Trait({ + + _initBase: function IBT__initBase(opts, optRules, optsToNotSet) { + this._optRules = optRules; + for (let optName in optRules) + if (optsToNotSet.indexOf(optName) < 0) + this[optName] = opts[optName]; + optsToNotSet.forEach(function (opt) validateOpt(opts[opt], optRules[opt])); + this._isInited = true; + + this._id = nextItemID++; + this._parentMenu = null; + + // This makes the private properties accessible to anyone with access to + // PRIVATE_PROPS_KEY. Barring loader tricks, only this file has has access + // to it, so only this file has access to the private properties. + const self = this; + this.valueOf = function IBT_valueOf(key) { + return key === PRIVATE_PROPS_KEY ? self : self._public; + }; + }, + + destroy: function IBT_destroy() { + if (this._wasDestroyed) + return; + if (this.parentMenu) + this.parentMenu.removeItem(this._public); + else if (!(this instanceof Separator) && this._hasFinishedInit) + browserManager.removeTopLevelItem(this._public); + browserManager.unregisterItem(this._public); + this._wasDestroyed = true; + }, + + get parentMenu() { + return this._parentMenu; + }, + + set parentMenu(val) { + throw new Error("The 'parentMenu' property is not intended to be set. " + + "Use menu.addItem(item) instead."); + }, + + set _isTopLevel(val) { + if (val) + this._workerReg = new WorkerRegistry(this._public); + else { + this._workerReg.destroy(); + delete this._workerReg; + } + }, + + get _topLevelItem() { + let topLevelItem = this._public; + let parentMenu = this.parentMenu; + while (parentMenu) { + topLevelItem = parentMenu; + parentMenu = parentMenu.parentMenu; + } + return topLevelItem; + } +}); + +// Item and Menu are composed of this trait. +const ActiveItemTrait = Trait.compose(ItemBaseTrait, EventEmitter, Trait({ + + _initActiveItem: function AIT__initActiveItem(opts, optRules, optsToNotSet) { + this._initBase(opts, optRules, + optsToNotSet.concat(["onMessage", "context"])); + + if ("onMessage" in opts) + this.on("message", opts.onMessage); + + // When a URL context is removed (by calling context.remove(urlContext)), we + // may need to create workers for windows containing pages that the item now + // matches. Likewise, when a URL context is added, we need to destroy + // workers for windows containing pages that the item now does not match. + // + // collection doesn't provide a way to listen for removals. utils/registry + // does, but it doesn't allow its elements to be enumerated. So as a hack, + // use a collection for item.context and replace its add and remove methods. + collection.addCollectionProperty(this, "context"); + if (opts.context) + this.context.add(opts.context); + + const self = this; + + let add = this.context.add; + this.context.add = function itemContextAdd() { + let args = Array.slice(arguments); + add.apply(self.context, args); + if (self._workerReg && args.some(function (a) a instanceof URLContext)) + self._workerReg.destroyUnneededWorkers(); + }; + + let remove = this.context.remove; + this.context.remove = function itemContextRemove() { + let args = Array.slice(arguments); + remove.apply(self.context, args); + if (self._workerReg && args.some(function (a) a instanceof URLContext)) + self._workerReg.createNeededWorkers(); + }; + }, + + // Workers are only created for top-level menu items. When a top-level item + // is later added to a Menu, its workers are destroyed. Well, all items start + // out as top-level because there is, unfortunately, no contextMenu.add(). So + // when an item is created and immediately added to a Menu, workers for it are + // needlessly created and destroyed. The point of this timeout is to avoid + // that. Items that are created and added to Menus in the same turn of the + // event loop won't have workers created for them. + _finishActiveItemInit: function AIT__finishActiveItemInit() { + numItemsWithUnfinishedInit++; + const self = this; + timer.setTimeout(function AIT__finishActiveItemInitTimeout() { + if (!self.parentMenu && !self._wasDestroyed) + browserManager.addTopLevelItem(self._public); + self._hasFinishedInit = true; + numItemsWithUnfinishedInit--; + }, 0); + }, + + get label() { + return this._label; + }, + + set label(val) { + this._label = validateOpt(val, this._optRules.label); + if (this._isInited) + browserManager.setItemLabel(this, this._label); + return this._label; + }, + + get image() { + return this._image; + }, + + set image(val) { + this._image = validateOpt(val, this._optRules.image); + if (this._isInited) + browserManager.setItemImage(this, this._image); + return this._image; + }, + + get contentScript() { + return this._contentScript; + }, + + set contentScript(val) { + this._contentScript = validateOpt(val, this._optRules.contentScript); + return this._contentScript; + }, + + get contentScriptFile() { + return this._contentScriptFile; + }, + + set contentScriptFile(val) { + this._contentScriptFile = + validateOpt(val, this._optRules.contentScriptFile); + return this._contentScriptFile; + } +})); + +// Item is composed of this trait. +const ItemTrait = Trait.compose(ActiveItemTrait, Trait({ + + _initItem: function IT__initItem(opts, optRules) { + this._initActiveItem(opts, optRules, []); + }, + + get data() { + return this._data; + }, + + set data(val) { + this._data = validateOpt(val, this._optRules.data); + if (this._isInited) + browserManager.setItemData(this, this._data); + return this._data; + }, + + toString: function IT_toString() { + return '[object Item "' + this.label + '"]'; + } +})); + +// The exported Item constructor. +function Item(options) { + let optRules = optionsRules(); + optRules.data = { + map: function (v) v.toString(), + is: ["string", "undefined"] + }; + + let item = ItemTrait.create(Item.prototype); + item._initItem(options, optRules); + + item._public = Cortex(item); + browserManager.registerItem(item._public); + item._finishActiveItemInit(); + + return item._public; +} + +// Menu is composed of this trait. +const MenuTrait = Trait.compose( + ActiveItemTrait.resolve({ destroy: "_destroyThisItem" }), + Trait({ + + _initMenu: function MT__initMenu(opts, optRules, optsToNotSet) { + this._items = []; + this._initActiveItem(opts, optRules, optsToNotSet); + }, + + destroy: function MT_destroy() { + while (this.items.length) + this.items[0].destroy(); + this._destroyThisItem(); + }, + + get items() { + return this._items; + }, + + set items(val) { + let newItems = validateOpt(val, this._optRules.items); + while (this.items.length) + this.items[0].destroy(); + newItems.forEach(function (i) this.addItem(i), this); + return newItems; + }, + + addItem: function MT_addItem(item) { + // First, remove the item from its current parent. + let privates = privateItem(item); + if (item.parentMenu) + item.parentMenu.removeItem(item); + else if (!(item instanceof Separator) && privates._hasFinishedInit) + browserManager.removeTopLevelItem(item); + + // Now add the item to this menu. + this._items.push(item); + privates._parentMenu = this._public; + browserManager.addItemToMenu(item, this._public); + }, + + removeItem: function MT_removeItem(item) { + let idx = this._items.indexOf(item); + if (idx < 0) + return; + this._items.splice(idx, 1); + privateItem(item)._parentMenu = null; + browserManager.removeItemFromMenu(item, this._public); + }, + + toString: function MT_toString() { + return '[object Menu "' + this.label + '"]'; + } +})); + +// The exported Menu constructor. +function Menu(options) { + let optRules = optionsRules(); + optRules.items = { + is: ["array"], + ok: function (v) { + return v.every(function (item) { + return (item instanceof Item) || + (item instanceof Menu) || + (item instanceof Separator); + }); + }, + msg: "items must be an array, and each element in the array must be an " + + "Item, Menu, or Separator." + }; + + let menu = MenuTrait.create(Menu.prototype); + + // We can't rely on _initBase to set the `items` property, because the menu + // needs to be registered with and added to the browserManager before any + // child items are added to it. + menu._initMenu(options, optRules, ["items"]); + + menu._public = Cortex(menu); + browserManager.registerItem(menu._public); + menu.items = options.items; + menu._finishActiveItemInit(); + + return menu._public; +} + +// The exported Separator constructor. +function Separator() { + let sep = ItemBaseTrait.create(Separator.prototype); + sep._initBase({}, {}, []); + + sep._public = Cortex(sep); + browserManager.registerItem(sep._public); + sep._hasFinishedInit = true; + return sep._public; +} + + +function Context() {} + +function PageContext() { + this.isCurrent = function PageContext_isCurrent(popupNode) { + let win = popupNode.ownerDocument.defaultView; + if (win && !win.getSelection().isCollapsed) + return false; + + let cursor = popupNode; + while (cursor && !(cursor instanceof Ci.nsIDOMHTMLHtmlElement)) { + if (NON_PAGE_CONTEXT_ELTS.some(function (iface) cursor instanceof iface)) + return false; + cursor = cursor.parentNode; + } + return true; + }; +} + +PageContext.prototype = new Context(); + +function SelectorContext(selector) { + let opts = apiUtils.validateOptions({ selector: selector }, { + selector: { + is: ["string"], + msg: "selector must be a string." + } + }); + + this.adjustPopupNode = function SelectorContext_adjustPopupNode(node) { + return closestMatchingAncestor(node); + }; + + this.isCurrent = function SelectorContext_isCurrent(popupNode) { + return !!closestMatchingAncestor(popupNode); + }; + + // Returns node if it matches selector, or the closest ancestor of node that + // matches, or null if node and none of its ancestors matches. + function closestMatchingAncestor(node) { + let cursor = node; + while (cursor) { + if (cursor.mozMatchesSelector(selector)) + return cursor; + if (cursor instanceof Ci.nsIDOMHTMLHtmlElement) + break; + cursor = cursor.parentNode; + } + return null; + } +} + +SelectorContext.prototype = new Context(); + +function SelectionContext() { + this.isCurrent = function SelectionContext_isCurrent(popupNode) { + let win = popupNode.ownerDocument.defaultView; + if (!win) + return false; + + let hasSelection = !win.getSelection().isCollapsed; + if (!hasSelection) { + // window.getSelection doesn't return a selection for text selected in a + // form field (see bug 85686), so before returning false we want to check + // if the popupNode is a text field. + let { selectionStart, selectionEnd } = popupNode; + hasSelection = !isNaN(selectionStart) && + !isNaN(selectionEnd) && + selectionStart !== selectionEnd; + } + return hasSelection; + }; +} + +SelectionContext.prototype = new Context(); + +function URLContext(patterns) { + let opts = apiUtils.validateOptions({ patterns: patterns }, { + patterns: { + map: function (v) apiUtils.getTypeOf(v) === "array" ? v : [v], + ok: function (v) v.every(function (p) typeof(p) === "string"), + msg: "patterns must be a string or an array of strings." + } + }); + try { + patterns = opts.patterns.map(function (p) new MatchPattern(p)); + } + catch (err) { + console.error("Error creating URLContext match pattern:"); + throw err; + } + + const self = this; + + this.isCurrent = function URLContext_isCurrent(popupNode) { + return self.isCurrentForURL(popupNode.ownerDocument.URL); + }; + + this.isCurrentForURL = function URLContext_isCurrentForURL(url) { + return patterns.some(function (p) p.test(url)); + }; +} + +URLContext.prototype = new Context(); + +exports.PageContext = apiUtils.publicConstructor(PageContext); +exports.SelectorContext = apiUtils.publicConstructor(SelectorContext); +exports.SelectionContext = apiUtils.publicConstructor(SelectionContext); +exports.URLContext = apiUtils.publicConstructor(URLContext); + + +// Returns a version of opt validated against the given rule. +function validateOpt(opt, rule) { + let { opt } = apiUtils.validateOptions({ opt: opt }, { opt: rule }); + return opt; +} + +// Returns rules for apiUtils.validateOptions() common to Item and Menu. +function optionsRules() { + return { + context: { + is: ["undefined", "object", "array"], + ok: function (v) { + if (!v) + return true; + let arr = apiUtils.getTypeOf(v) === "array" ? 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." + }, + label: { + map: function (v) v.toString(), + is: ["string"], + ok: function (v) !!v, + msg: "The item must have a non-empty string label." + }, + image: { + map: function (v) v.toString(), + is: ["string", "undefined", "null"] + }, + contentScript: { + is: ["string", "array", "undefined"], + ok: function (v) { + return apiUtils.getTypeOf(v) !== "array" || + v.every(function (s) typeof(s) === "string"); + } + }, + contentScriptFile: { + is: ["string", "array", "undefined"], + ok: function (v) { + if (!v) + return true; + let arr = apiUtils.getTypeOf(v) === "array" ? v : [v]; + try { + return arr.every(function (s) { + return apiUtils.getTypeOf(s) === "string" && url.toFilename(s); + }); + } + catch (err) {} + return false; + }, + msg: "The 'contentScriptFile' option must be a local file URL or " + + "an array of local file URLs." + }, + onMessage: { + is: ["function", "undefined"] + } + }; +} + +// Does a binary search on elts, a NodeList, and returns the DOM element +// before which an item with targetLabel should be inserted. null is returned +// if the new item should be inserted at the end. +function insertionPoint(targetLabel, elts) { + let from = 0; + let to = elts.length - 1; + + while (from <= to) { + let i = Math.floor((from + to) / 2); + let comp = targetLabel.localeCompare(elts[i].getAttribute("label")); + if (comp < 0) + to = i - 1; + else if (comp > 0) + from = i + 1; + else + return elts[i]; + } + return elts[from] || null; +} + +// Builds an ID suitable for a DOM element from the given item ID. +// isInOverflowSubtree should be true if the returned element will be inserted +// into the DOM subtree rooted at the overflow menu. +function domEltIDFromItemID(itemID, isInOverflowSubtree) { + let suffix = isInOverflowSubtree ? "-overflow" : ""; + return jpSelf.id + "-context-menu-item-" + itemID + suffix; +} + +// Parses the item ID out of the given DOM element ID and returns it. If the +// element's ID is malformed or it indicates that the element was not created by +// the instance of the module calling this function, returns -1. +function itemIDFromDOMEltID(domEltID) { + let match = /^(.+?)-context-menu-item-([0-9]+)[-a-z]*$/.exec(domEltID); + return !match || match[1] !== jpSelf.id ? -1 : match[2]; +} + +// Returns the private version of the given public reflection. +function privateItem(publicItem) { + return publicItem.valueOf(PRIVATE_PROPS_KEY); +} + + +// A type of Worker tailored to our uses. +const ContextMenuWorker = Worker.compose({ + destroy: Worker.required, + + // Returns true if any context listeners are defined in the worker's port. + anyContextListeners: function CMW_anyContextListeners() { + return this._contentWorker._listeners("context").length > 0; + }, + + // Returns the first string or truthy value returned by a context listener in + // the worker's port. If none return a string or truthy value or if there are + // no context listeners, returns false. popupNode is the node that was + // context-clicked. + isAnyContextCurrent: function CMW_isAnyContextCurrent(popupNode) { + let listeners = this._contentWorker._listeners("context"); + for (let i = 0; i < listeners.length; i++) { + try { + let val = listeners[i].call(this._contentWorker._sandbox, popupNode); + if (typeof(val) === "string" || val) + return val; + } + catch (err) { + console.exception(err); + } + } + return false; + }, + + // 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 CMW_fireClick(popupNode, clickedItemData) { + this._contentWorker._asyncEmit("click", popupNode, clickedItemData); + } +}); + + +// This class creates and stores content workers for pairs of menu items and +// content windows. Use one instance for each item. Not all pairs need a +// worker: if an item has a URL context that does not match a window's page, +// then no worker is created for the pair. +function WorkerRegistry(item) { + this.item = item; + + // inner window ID => { win, worker } + this.winWorkers = {}; + + // inner window ID => content window + this.winsWithoutWorkers = {}; +} + +WorkerRegistry.prototype = { + + // Registers a content window, creating a worker for it if it needs one. + registerContentWin: function WR_registerContentWin(win) { + let innerWinID = winUtils.getInnerId(win); + if ((innerWinID in this.winWorkers) || + (innerWinID in this.winsWithoutWorkers)) + return; + if (this._doesURLNeedWorker(win.document.URL)) + this.winWorkers[innerWinID] = { win: win, worker: this._makeWorker(win) }; + else + this.winsWithoutWorkers[innerWinID] = win; + }, + + // Unregisters a content window, destroying its related worker if it has one. + unregisterContentWin: function WR_unregisterContentWin(innerWinID) { + if (innerWinID in this.winWorkers) { + let winWorker = this.winWorkers[innerWinID]; + winWorker.worker.destroy(); + delete winWorker.worker; + delete winWorker.win; + delete this.winWorkers[innerWinID]; + } + else + delete this.winsWithoutWorkers[innerWinID]; + }, + + // Creates a worker for each window that needs a worker but doesn't have one. + createNeededWorkers: function WR_createNeededWorkers() { + for (let [innerWinID, win] in Iterator(this.winsWithoutWorkers)) { + delete this.winsWithoutWorkers[innerWinID]; + this.registerContentWin(win); + } + }, + + // Destroys the worker for each window that has a worker but doesn't need it. + destroyUnneededWorkers: function WR_destroyUnneededWorkers() { + for (let [innerWinID, winWorker] in Iterator(this.winWorkers)) { + if (!this._doesURLNeedWorker(winWorker.win.document.URL)) { + this.unregisterContentWin(innerWinID); + this.winsWithoutWorkers[innerWinID] = winWorker.win; + } + } + }, + + // Returns the worker for the item-window pair or null if none exists. + find: function WR_find(contentWin) { + let innerWinID = winUtils.getInnerId(contentWin); + return (innerWinID in this.winWorkers) ? + this.winWorkers[innerWinID].worker : + null; + }, + + // Unregisters all content windows from the registry, which destroys all + // workers. + destroy: function WR_destroy() { + for (let innerWinID in this.winWorkers) + this.unregisterContentWin(innerWinID); + for (let innerWinID in this.winsWithoutWorkers) + this.unregisterContentWin(innerWinID); + }, + + // Returns false if the item has a URL context that does not match the given + // URL. + _doesURLNeedWorker: function WR__doesURLNeedWorker(url) { + for (let ctxt in this.item.context) + if ((ctxt instanceof URLContext) && !ctxt.isCurrentForURL(url)) + return false; + return true; + }, + + _makeWorker: function WR__makeWorker(win) { + let worker = ContextMenuWorker({ + window: win, + contentScript: this.item.contentScript, + contentScriptFile: this.item.contentScriptFile, + onError: function (err) console.exception(err) + }); + let item = this.item; + worker.on("message", function workerOnMessage(msg) { + try { + privateItem(item)._emitOnObject(item, "message", msg); + } + catch (err) { + console.exception(err); + } + }); + return worker; + } +}; + + +// Mirrors state across all browser windows. Also responsible for detecting +// all content window loads and unloads. +let browserManager = { + topLevelItems: [], + browserWins: [], + + // inner window ID => content window + contentWins: {}, + + // Call this when a new item is created, top-level or not. + registerItem: function BM_registerItem(item) { + this.browserWins.forEach(function (w) w.registerItem(item)); + }, + + // Call this when an item is destroyed and won't be used again, top-level or + // not. + unregisterItem: function BM_unregisterItem(item) { + this.browserWins.forEach(function (w) w.unregisterItem(item)); + }, + + addTopLevelItem: function BM_addTopLevelItem(item) { + this.topLevelItems.push(item); + this.browserWins.forEach(function (w) w.addTopLevelItem(item)); + + // Create the item's worker registry and register all currently loaded + // content windows with it. + let privates = privateItem(item); + privates._isTopLevel = true; + for each (let win in this.contentWins) + privates._workerReg.registerContentWin(win); + }, + + removeTopLevelItem: function BM_removeTopLevelItem(item) { + let idx = this.topLevelItems.indexOf(item); + if (idx < 0) + throw new Error("Internal error: item not in top-level menu: " + item); + this.topLevelItems.splice(idx, 1); + this.browserWins.forEach(function (w) w.removeTopLevelItem(item)); + privateItem(item)._isTopLevel = false; + }, + + addItemToMenu: function BM_addItemToMenu(item, parentMenu) { + this.browserWins.forEach(function (w) w.addItemToMenu(item, parentMenu)); + }, + + removeItemFromMenu: function BM_removeItemFromMenu(item, parentMenu) { + this.browserWins.forEach(function (w) w.removeItemFromMenu(item, + parentMenu)); + }, + + setItemLabel: function BM_setItemLabel(item, label) { + this.browserWins.forEach(function (w) w.setItemLabel(item, label)); + }, + + setItemImage: function BM_setItemImage(item, imageURL) { + this.browserWins.forEach(function (w) w.setItemImage(item, imageURL)); + }, + + setItemData: function BM_setItemData(item, data) { + this.browserWins.forEach(function (w) w.setItemData(item, data)); + }, + + // Note that calling this method will cause onTrack to be called immediately + // for each currently open browser window. + init: function BM_init() { + require("api-utils/unload").ensure(this); + let windowTracker = new winUtils.WindowTracker(this); + + // Register content windows on content-document-global-created and + // unregister them on inner-window-destroyed. For rationale, see bug 667957 + // for the former and bug 642004 for the latter. + observerServ.add("content-document-global-created", + this._onDocGlobalCreated, this); + observerServ.add("inner-window-destroyed", + this._onInnerWinDestroyed, this); + }, + + _onDocGlobalCreated: function BM__onDocGlobalCreated(contentWin) { + let doc = contentWin.document; + if (doc.readyState == "loading") { + const self = this; + doc.addEventListener("readystatechange", function onReadyStateChange(e) { + if (e.target != doc || doc.readyState != "complete") + return; + doc.removeEventListener("readystatechange", onReadyStateChange, false); + self._registerContentWin(contentWin); + }, false); + } + else if (doc.readyState == "complete") + this._registerContentWin(contentWin); + }, + + _onInnerWinDestroyed: function BM__onInnerWinDestroyed(subj) { + this._unregisterContentWin( + subj.QueryInterface(Ci.nsISupportsPRUint64).data); + }, + + // Stores the given content window with the manager and registers it with each + // top-level item's worker registry. + _registerContentWin: function BM__registerContentWin(win) { + let innerID = winUtils.getInnerId(win); + + // It's an error to call this method for the same window more than once, but + // we allow it in one case: when onTrack races _onDocGlobalCreated. (See + // the comment in onTrack.) Make sure the window is registered only once. + if (innerID in this.contentWins) + return; + + this.contentWins[innerID] = win; + this.topLevelItems.forEach(function (item) { + privateItem(item)._workerReg.registerContentWin(win); + }); + }, + + // Removes the given content window from the manager and unregisters it from + // each top-level item's worker registry. + _unregisterContentWin: function BM__unregisterContentWin(innerID) { + delete this.contentWins[innerID]; + this.topLevelItems.forEach(function (item) { + privateItem(item)._workerReg.unregisterContentWin(innerID); + }); + }, + + unload: function BM_unload() { + // The window tracker is unloaded at the same time this method is called, + // which causes onUntrack to be called for each open browser window, so + // there's no need to clean up browser windows here. + + while (this.topLevelItems.length) { + let item = this.topLevelItems[0]; + this.removeTopLevelItem(item); + this.unregisterItem(item); + } + delete this.contentWins; + }, + + // Registers a browser window with the manager. This is a WindowTracker + // callback. Note that this is called in two cases: for each newly opened + // chrome window, and for each chrome window that is open when the loader + // loads this module. + onTrack: function BM_onTrack(window) { + if (!this._isBrowserWindow(window)) + return; + + let browserWin = new BrowserWindow(window); + this.browserWins.push(browserWin); + + // Register all loaded content windows in the browser window. Be sure to + // include frames and iframes. If onTrack is called as a result of a new + // browser window being opened, as opposed to the module being loaded, then + // this will race the content-document-global-created notification. That's + // OK, since _registerContentWin will not register the same content window + // more than once. + window.gBrowser.browsers.forEach(function (browser) { + let topContentWin = browser.contentWindow; + let allContentWins = Array.slice(topContentWin.frames); + allContentWins.push(topContentWin); + allContentWins.forEach(function (contentWin) { + if (contentWin.document.readyState == "complete") + this._registerContentWin(contentWin); + }, this); + }, this); + + // Add all top-level items and, recursively, their child items to the new + // browser window. + function addItemTree(item, parentMenu) { + browserWin.registerItem(item); + if (parentMenu) + browserWin.addItemToMenu(item, parentMenu); + else + browserWin.addTopLevelItem(item); + if (item instanceof Menu) + item.items.forEach(function (subitem) addItemTree(subitem, item)); + } + this.topLevelItems.forEach(function (item) addItemTree(item, null)); + }, + + // Unregisters a browser window from the manager. This is a WindowTracker + // callback. Note that this is called in two cases: for each newly closed + // chrome window, and for each chrome window that is open when this module is + // unloaded. + onUntrack: function BM_onUntrack(window) { + if (!this._isBrowserWindow(window)) + return; + + // Remove the window from the window list. + let idx = 0; + for (; idx < this.browserWins.length; idx++) + if (this.browserWins[idx].window == window) + break; + if (idx == this.browserWins.length) + throw new Error("Internal error: browser window not found"); + let browserWin = this.browserWins.splice(idx, 1)[0]; + + // Remove all top-level items from the window. + this.topLevelItems.forEach(function (i) browserWin.removeTopLevelItem(i)); + browserWin.destroy(); + }, + + _isBrowserWindow: function BM__isBrowserWindow(win) { + let winType = win.document.documentElement.getAttribute("windowtype"); + return winType === "navigator:browser"; + } +}; + + +// Responsible for creating and managing context menu item DOM elements for a +// browser window. Also responsible for providing a description of the window's +// current context and determining whether an item matches the current context. +// +// TODO: If other apps besides Firefox want to support the context menu in +// whatever way is appropriate for them, plugging in a substitute for or an +// adapter to this class should be the way to do it. Make it easy for them. +// See bug 560716. +function BrowserWindow(window) { + this.window = window; + this.doc = window.document; + + let popupDOMElt = this.doc.getElementById("contentAreaContextMenu"); + if (!popupDOMElt) + throw new Error("Internal error: Context menu popup not found."); + this.contextMenuPopup = new ContextMenuPopup(popupDOMElt, this); + + // item ID => { item, domElt, overflowDOMElt, popup, overflowPopup } + // item may or may not be top-level. domElt is the item's DOM element + // contained in the subtree rooted in the top-level context menu. + // overflowDOMElt is the item's DOM element contained in the subtree rooted in + // the overflow submenu. popup and overflowPopup are only defined if the item + // is a Menu; they're the Popup instances containing the Menu's child items, + // with the aforementioned distinction between top-level and overflow + // subtrees. + this.items = {}; +} + +BrowserWindow.prototype = { + + // Creates and stores DOM elements for the given item, top-level or not. + registerItem: function BW_registerItem(item) { + // this.items[id] is referenced by _makeMenu, so it needs to be defined + // before _makeDOMElt is called. + let props = { item: item }; + this.items[privateItem(item)._id] = props; + props.domElt = this._makeDOMElt(item, false); + props.overflowDOMElt = this._makeDOMElt(item, true); + }, + + // Removes the given item's DOM elements from the store. + unregisterItem: function BW_unregisterItem(item) { + delete this.items[privateItem(item)._id]; + }, + + addTopLevelItem: function BW_addTopLevelItem(item) { + this.contextMenuPopup.addItem(item); + }, + + removeTopLevelItem: function BW_removeTopLevelItem(item) { + this.contextMenuPopup.removeItem(item); + }, + + addItemToMenu: function BW_addItemToMenu(item, parentMenu) { + let { popup, overflowPopup } = this.items[privateItem(parentMenu)._id]; + popup.addItem(item); + overflowPopup.addItem(item); + }, + + removeItemFromMenu: function BW_removeItemFromMenu(item, parentMenu) { + let { popup, overflowPopup } = this.items[privateItem(parentMenu)._id]; + popup.removeItem(item); + overflowPopup.removeItem(item); + }, + + setItemLabel: function BW_setItemLabel(item, label) { + let privates = privateItem(item); + let { domElt, overflowDOMElt } = this.items[privates._id]; + this._setDOMEltLabel(domElt, label); + this._setDOMEltLabel(overflowDOMElt, label); + if (!item.parentMenu && privates._hasFinishedInit) + this.contextMenuPopup.itemLabelDidChange(item); + }, + + _setDOMEltLabel: function BW__setDOMEltLabel(domElt, label) { + domElt.setAttribute("label", label); + }, + + setItemImage: function BW_setItemImage(item, imageURL) { + let { domElt, overflowDOMElt } = this.items[privateItem(item)._id]; + let isMenu = item instanceof Menu; + this._setDOMEltImage(domElt, imageURL, isMenu); + this._setDOMEltImage(overflowDOMElt, imageURL, isMenu); + }, + + _setDOMEltImage: function BW__setDOMEltImage(domElt, imageURL, isMenu) { + if (!imageURL) { + domElt.removeAttribute("image"); + domElt.classList.remove("menu-iconic"); + domElt.classList.remove("menuitem-iconic"); + } + else { + domElt.setAttribute("image", imageURL); + domElt.classList.add(isMenu ? "menu-iconic" : "menuitem-iconic"); + } + }, + + setItemData: function BW_setItemData(item, data) { + let { domElt, overflowDOMElt } = this.items[privateItem(item)._id]; + this._setDOMEltData(domElt, data); + this._setDOMEltData(overflowDOMElt, data); + }, + + _setDOMEltData: function BW__setDOMEltData(domElt, data) { + domElt.setAttribute("value", data); + }, + + // The context specified for a top-level item may not match exactly the real + // context that triggers it. For example, if the user context-clicks a span + // inside an anchor, we want items that specify an anchor context to be + // triggered, but the real context will indicate that the span was clicked, + // not the anchor. Where the real context and an item's context conflict, + // clients should be given the item's context, and this method can be used to + // make such adjustments. Returns an adjusted popupNode. + adjustPopupNode: function BW_adjustPopupNode(popupNode, topLevelItem) { + for (let ctxt in topLevelItem.context) { + if (typeof(ctxt.adjustPopupNode) === "function") { + let ctxtNode = ctxt.adjustPopupNode(popupNode); + if (ctxtNode) { + popupNode = ctxtNode; + break; + } + } + } + return popupNode; + }, + + // Returns true if all of item's contexts are current in the window. + areAllContextsCurrent: function BW_areAllContextsCurrent(item, popupNode) { + let win = popupNode.ownerDocument.defaultView; + let worker = privateItem(item)._workerReg.find(win); + + // If the worker for the item-window pair doesn't exist (e.g., because the + // page hasn't loaded yet), we can't really make a good decision since the + // content script may have a context listener. So just don't show the item + // at all. + if (!worker) + return false; + + // If there are no contexts given at all, the page context applies. + let hasContentContext = worker.anyContextListeners(); + if (!hasContentContext && !item.context.length) + return new PageContext().isCurrent(popupNode); + + // Otherwise, determine if all given contexts are current. Evaluate the + // declarative contexts first and the worker's context listeners last. That + // way the listener might be able to avoid some work. + let curr = true; + for (let ctxt in item.context) { + curr = curr && ctxt.isCurrent(popupNode); + if (!curr) + return false; + } + return !hasContentContext || worker.isAnyContextCurrent(popupNode); + }, + + // Sets this.popupNode to the node the user context-clicked to invoke the + // context menu. For Gecko 2.0 and later, triggerNode is this node; if it's + // falsey, document.popupNode is used. Returns the popupNode. + capturePopupNode: function BW_capturePopupNode(triggerNode) { + this.popupNode = triggerNode || this.doc.popupNode; + if (!this.popupNode) + console.warn("popupNode is null."); + return this.popupNode; + }, + + destroy: function BW_destroy() { + this.contextMenuPopup.destroy(); + delete this.window; + delete this.doc; + delete this.items; + }, + + // Emits a click event in the port of the content worker related to given top- + // level item and popupNode's content window. Listeners will be passed + // popupNode and clickedItemData. + fireClick: function BW_fireClick(topLevelItem, popupNode, clickedItemData) { + let win = popupNode.ownerDocument.defaultView; + let worker = privateItem(topLevelItem)._workerReg.find(win); + if (worker) + worker.fireClick(popupNode, clickedItemData); + }, + + _makeDOMElt: function BW__makeDOMElt(item, isInOverflowSubtree) { + let elt = item instanceof Item ? this._makeMenuitem(item) : + item instanceof Menu ? this._makeMenu(item, isInOverflowSubtree) : + item instanceof Separator ? this._makeSeparator() : + null; + if (!elt) + throw new Error("Internal error: can't make element, unknown item type"); + + elt.id = domEltIDFromItemID(privateItem(item)._id, isInOverflowSubtree); + elt.classList.add(ITEM_CLASS); + return elt; + }, + + // Returns a new xul:menu representing the menu. + _makeMenu: function BW__makeMenu(menu, isInOverflowSubtree) { + let menuElt = this.doc.createElement("menu"); + this._setDOMEltLabel(menuElt, menu.label); + if (menu.image) + this._setDOMEltImage(menuElt, menu.image, true); + let popupElt = this.doc.createElement("menupopup"); + menuElt.appendChild(popupElt); + + let popup = new Popup(popupElt, this, isInOverflowSubtree); + let props = this.items[privateItem(menu)._id]; + if (isInOverflowSubtree) + props.overflowPopup = popup; + else + props.popup = popup; + + return menuElt; + }, + + // Returns a new xul:menuitem representing the item. + _makeMenuitem: function BW__makeMenuitem(item) { + let elt = this.doc.createElement("menuitem"); + this._setDOMEltLabel(elt, item.label); + if (item.image) + this._setDOMEltImage(elt, item.image, false); + if (item.data) + this._setDOMEltData(elt, item.data); + return elt; + }, + + // Returns a new xul:menuseparator. + _makeSeparator: function BW__makeSeparator() { + return this.doc.createElement("menuseparator"); + } +}; + + +// Responsible for adding DOM elements to and removing them from poupDOMElt. +function Popup(popupDOMElt, browserWin, isInOverflowSubtree) { + this.popupDOMElt = popupDOMElt; + this.browserWin = browserWin; + this.isInOverflowSubtree = isInOverflowSubtree; +} + +Popup.prototype = { + + addItem: function Popup_addItem(item) { + let props = this.browserWin.items[privateItem(item)._id]; + let elt = this.isInOverflowSubtree ? props.overflowDOMElt : props.domElt; + this.popupDOMElt.appendChild(elt); + }, + + removeItem: function Popup_removeItem(item) { + let props = this.browserWin.items[privateItem(item)._id]; + let elt = this.isInOverflowSubtree ? props.overflowDOMElt : props.domElt; + this.popupDOMElt.removeChild(elt); + } +}; + + +// Represents a browser window's context menu popup. Responsible for hiding and +// showing items according to the browser window's current context and for +// handling item clicks. +function ContextMenuPopup(popupDOMElt, browserWin) { + this.popupDOMElt = popupDOMElt; + this.browserWin = browserWin; + this.doc = popupDOMElt.ownerDocument; + + // item ID => item + // Calling this variable "topLevelItems" is redundant, since Popup and + // ContextMenuPopup are only responsible for their child items, not all their + // descendant items. But calling it "items" might encourage one to believe + // otherwise, so topLevelItems it is. + this.topLevelItems = {}; + + popupDOMElt.addEventListener("popupshowing", this, false); + popupDOMElt.addEventListener("command", this, false); +} + +ContextMenuPopup.prototype = { + + addItem: function CMP_addItem(item) { + this._ensureStaticEltsExist(); + let itemID = privateItem(item)._id; + this.topLevelItems[itemID] = item; + let props = this.browserWin.items[itemID]; + props.domElt.classList.add(TOPLEVEL_ITEM_CLASS); + props.overflowDOMElt.classList.add(OVERFLOW_ITEM_CLASS); + this._insertItemInSortedOrder(item); + }, + + removeItem: function CMP_removeItem(item) { + let itemID = privateItem(item)._id; + delete this.topLevelItems[itemID]; + let { domElt, overflowDOMElt } = this.browserWin.items[itemID]; + domElt.classList.remove(TOPLEVEL_ITEM_CLASS); + overflowDOMElt.classList.remove(OVERFLOW_ITEM_CLASS); + this.popupDOMElt.removeChild(domElt); + this._overflowPopup.removeChild(overflowDOMElt); + }, + + // Call this after the item's label changes. This re-inserts the item into + // the popup so that it remains in sorted order. + itemLabelDidChange: function CMP_itemLabelDidChange(item) { + let itemID = privateItem(item)._id; + let { domElt, overflowDOMElt } = this.browserWin.items[itemID]; + this.popupDOMElt.removeChild(domElt); + this._overflowPopup.removeChild(overflowDOMElt); + this._insertItemInSortedOrder(item); + }, + + destroy: function CMP_destroy() { + // If there are no more items from any instance of the module, remove the + // separator and overflow submenu, if they exist. + let elts = this._topLevelElts; + if (!elts.length) { + let submenu = this._overflowMenu; + if (submenu) + this.popupDOMElt.removeChild(submenu); + + let sep = this._separator; + if (sep) + this.popupDOMElt.removeChild(sep); + } + + this.popupDOMElt.removeEventListener("popupshowing", this, false); + this.popupDOMElt.removeEventListener("command", this, false); + }, + + handleEvent: function CMP_handleEvent(event) { + try { + if (event.type === "command") + this._handleClick(event.target); + else if (event.type === "popupshowing" && + event.target === this.popupDOMElt) + this._handlePopupShowing(); + } + catch (err) { + console.exception(err); + } + }, + + // command events bubble to the context menu's top-level xul:menupopup and are + // caught here. + _handleClick: function CMP__handleClick(clickedDOMElt) { + if (!clickedDOMElt.classList.contains(ITEM_CLASS)) + return; + let itemID = itemIDFromDOMEltID(clickedDOMElt.id); + if (itemID < 0) + return; + let { item, domElt, overflowDOMElt } = this.browserWin.items[itemID]; + + // Bail if the DOM element was not created by this module instance. In + // real-world add-ons, the itemID < 0 check above is sufficient, but for the + // unit test the JID never changes, making this necessary. + if (clickedDOMElt != domElt && clickedDOMElt != overflowDOMElt) + return; + + let topLevelItem = privateItem(item)._topLevelItem; + let popupNode = this.browserWin.adjustPopupNode(this.browserWin.popupNode, + topLevelItem); + this.browserWin.fireClick(topLevelItem, popupNode, item.data); + }, + + // popupshowing is used to show top-level items that match the browser + // window's current context and hide items that don't. Each module instance + // is responsible for showing and hiding the items it owns. + _handlePopupShowing: function CMP__handlePopupShowing() { + // If there are items queued up to finish initializing, let them go first. + // Otherwise the overflow submenu and menu separator may end up in an + // inappropriate state when those items are later added to the menu. + if (numItemsWithUnfinishedInit) { + const self = this; + timer.setTimeout(function popupShowingTryAgain() { + self._handlePopupShowing(); + }, 0); + return; + } + + // popupDOMElt.triggerNode was added in Gecko 2.0 by bug 383930. The || is + // to avoid a Spidermonkey strict warning on earlier versions. + let triggerNode = this.popupDOMElt.triggerNode || undefined; + let popupNode = this.browserWin.capturePopupNode(triggerNode); + + // Show and hide items. Set a "jetpackContextCurrent" property on the + // DOM elements to signal which of our items match the current context. + for (let [itemID, item] in Iterator(this.topLevelItems)) { + let areContextsCurr = + this.browserWin.areAllContextsCurrent(item, popupNode); + + // Change the item's label if the return value was a string. + if (typeof(areContextsCurr) === "string") { + item.label = areContextsCurr; + areContextsCurr = true; + } + + let { domElt, overflowDOMElt } = this.browserWin.items[itemID]; + domElt.jetpackContextCurrent = areContextsCurr; + domElt.hidden = !areContextsCurr; + overflowDOMElt.jetpackContextCurrent = areContextsCurr; + overflowDOMElt.hidden = !areContextsCurr; + } + + // Get the total number of items that match the current context. It's a + // little tricky: There may be other instances of this module loaded, + // each hiding and showing their items. So we can't base this number on + // only our items, or on the hidden state of items. That's why we set + // the jetpackContextCurrent property above. The last instance to run + // will leave the menupopup in the correct state. + let elts = this._topLevelElts; + let numShown = Array.reduce(elts, function (total, elt) { + return total + (elt.jetpackContextCurrent ? 1 : 0); + }, 0); + + // If too many items are shown, show the submenu and hide the top-level + // items. Otherwise, hide the submenu and show the top-level items. + let overflow = numShown > this._overflowThreshold; + if (overflow) + Array.forEach(elts, function (e) e.hidden = true); + + let submenu = this._overflowMenu; + if (submenu) + submenu.hidden = !overflow; + + // If no items are shown, hide the menu separator. + let sep = this._separator; + if (sep) + sep.hidden = numShown === 0; + }, + + // Adds the menu separator and overflow submenu if they don't exist. + _ensureStaticEltsExist: function CMP__ensureStaticEltsExist() { + let sep = this._separator; + if (!sep) { + sep = this._makeSeparator(); + this.popupDOMElt.appendChild(sep); + } + + let submenu = this._overflowMenu; + if (!submenu) { + submenu = this._makeOverflowMenu(); + submenu.hidden = true; + this.popupDOMElt.insertBefore(submenu, sep.nextSibling); + } + }, + + // Inserts the given item's DOM element into the popup in sorted order. + _insertItemInSortedOrder: function CMP__insertItemInSortedOrder(item) { + let props = this.browserWin.items[privateItem(item)._id]; + this.popupDOMElt.insertBefore( + props.domElt, insertionPoint(item.label, this._topLevelElts)); + this._overflowPopup.insertBefore( + props.overflowDOMElt, insertionPoint(item.label, this._overflowElts)); + }, + + // Creates and returns the xul:menu that's shown when too many items are added + // to the popup. + _makeOverflowMenu: function CMP__makeOverflowMenu() { + let submenu = this.doc.createElement("menu"); + submenu.id = OVERFLOW_MENU_ID; + submenu.setAttribute("label", OVERFLOW_MENU_LABEL); + let popup = this.doc.createElement("menupopup"); + popup.id = OVERFLOW_POPUP_ID; + submenu.appendChild(popup); + return submenu; + }, + + // Creates and returns the xul:menuseparator that separates the standard + // context menu items from our items. + _makeSeparator: function CMP__makeSeparator() { + let elt = this.doc.createElement("menuseparator"); + elt.id = SEPARATOR_ID; + return elt; + }, + + // Returns the item elements contained in the overflow menu, a NodeList. + get _overflowElts() { + return this._overflowPopup.getElementsByClassName(OVERFLOW_ITEM_CLASS); + }, + + // Returns the overflow xul:menu. + get _overflowMenu() { + return this.doc.getElementById(OVERFLOW_MENU_ID); + }, + + // Returns the overflow xul:menupopup. + get _overflowPopup() { + return this.doc.getElementById(OVERFLOW_POPUP_ID); + }, + + // Returns the OVERFLOW_THRESH_PREF pref value if it exists or + // OVERFLOW_THRESH_DEFAULT if it doesn't. + get _overflowThreshold() { + let prefs = require("api-utils/preferences-service"); + return prefs.get(OVERFLOW_THRESH_PREF, OVERFLOW_THRESH_DEFAULT); + }, + + // Returns the xul:menuseparator. + get _separator() { + return this.doc.getElementById(SEPARATOR_ID); + }, + + // Returns the item elements contained in the top-level menu, a NodeList. + get _topLevelElts() { + return this.popupDOMElt.getElementsByClassName(TOPLEVEL_ITEM_CLASS); + } +}; + + +// Init the browserManager only after setting prototypes and such above, because +// it will cause browserManager.onTrack to be called immediately if there are +// open windows. +browserManager.init(); diff --git a/tools/addon-sdk-1.3/packages/addon-kit/lib/hotkeys.js b/tools/addon-sdk-1.3/packages/addon-kit/lib/hotkeys.js new file mode 100644 index 0000000..f8c6434 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/lib/hotkeys.js @@ -0,0 +1,71 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Irakli Gozalishvili (Original Author) + * Henri Wiechers + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +"use strict"; + +const INVALID_HOTKEY = "Hotkey must have at least one modifier."; + +const { toJSON: jsonify, toString: stringify, + isFunctionKey } = require("api-utils/keyboard/utils"); +const { register, unregister } = require("api-utils/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; + 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/tools/addon-sdk-1.3/packages/addon-kit/lib/notifications.js b/tools/addon-sdk-1.3/packages/addon-kit/lib/notifications.js new file mode 100644 index 0000000..f2a4ea2 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/lib/notifications.js @@ -0,0 +1,112 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim:set ts=2 sw=2 sts=2 et filetype=javascript + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Drew Willcoxon (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +"use strict"; + +const { Cc, Ci, Cr } = require("chrome"); +const apiUtils = require("api-utils/api-utils"); +const errors = require("api-utils/errors"); + +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); + } + 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"] + }, + onClick: { + is: ["function", "undefined"] + }, + text: { + is: ["string", "undefined"] + }, + title: { + is: ["string", "undefined"] + } + }); +} diff --git a/tools/addon-sdk-1.3/packages/addon-kit/lib/page-mod.js b/tools/addon-sdk-1.3/packages/addon-kit/lib/page-mod.js new file mode 100644 index 0000000..d511464 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/lib/page-mod.js @@ -0,0 +1,229 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack Packages. + * + * The Initial Developer of the Original Code is Nickolay Ponomarev. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Nickolay Ponomarev (Original Author) + * Irakli Gozalishvili + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +"use strict"; + +const observers = require("api-utils/observer-service"); +const { Worker, Loader } = require('api-utils/content'); +const { EventEmitter } = require('api-utils/events'); +const { List } = require('api-utils/list'); +const { Registry } = require('api-utils/utils/registry'); +const xulApp = require("api-utils/xul-app"); +const { MatchPattern } = require('api-utils/match-pattern'); + +// Whether or not the host application dispatches a document-element-inserted +// notification when the document element is inserted into the DOM of a page. +// The notification was added in Gecko 2.0b6, it's a better time to attach +// scripts with contentScriptWhen "start" than content-document-global-created, +// since libraries like jQuery assume the presence of the document element. +const HAS_DOCUMENT_ELEMENT_INSERTED = + xulApp.versionInRange(xulApp.platformVersion, "2.0b6", "*"); +const ON_CONTENT = HAS_DOCUMENT_ELEMENT_INSERTED ? 'document-element-inserted' : + 'content-document-global-created'; + +// Workaround bug 642145: document-element-inserted is fired multiple times. +// This bug is fixed in Firefox 4.0.1, but we want to keep FF 4.0 compatibility +// Tracking bug 641457. To be removed when 4.0 has disappeared from earth. +const HAS_BUG_642145_FIXED = + xulApp.versionInRange(xulApp.platformVersion, "2.0.1", "*"); + +// rules registry +const RULES = {}; + +const Rules = EventEmitter.resolve({ toString: null }).compose(List, { + add: function() Array.slice(arguments).forEach(function onAdd(rule) { + if (this._has(rule)) + return; + // registering rule to the rules registry + if (!(rule in RULES)) + RULES[rule] = new MatchPattern(rule); + this._add(rule); + this._emit('add', rule); + }.bind(this)), + remove: function() Array.slice(arguments).forEach(function onRemove(rule) { + if (!this._has(rule)) + return; + this._remove(rule); + this._emit('remove', rule); + }.bind(this)), +}); + +/** + * PageMod constructor (exported below). + * @constructor + */ +const PageMod = Loader.compose(EventEmitter, { + on: EventEmitter.required, + _listeners: EventEmitter.required, + contentScript: Loader.required, + contentScriptFile: Loader.required, + contentScriptWhen: Loader.required, + include: null, + constructor: function PageMod(options) { + this._onContent = this._onContent.bind(this); + options = options || {}; + + if ('contentScript' in options) + this.contentScript = options.contentScript; + if ('contentScriptFile' in options) + this.contentScriptFile = options.contentScriptFile; + 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); + + let include = options.include; + let rules = this.include = Rules(); + rules.on('add', this._onRuleAdd = this._onRuleAdd.bind(this)); + rules.on('remove', this._onRuleRemove = this._onRuleRemove.bind(this)); + + if (Array.isArray(include)) + rules.add.apply(null, include); + else + rules.add(include); + + this.on('error', this._onUncaughtError = this._onUncaughtError.bind(this)); + pageModManager.add(this._public); + + this._loadingWindows = []; + }, + + destroy: function destroy() { + for each (let rule in this.include) + this.include.remove(rule); + pageModManager.remove(this._public); + this._loadingWindows = []; + }, + + _loadingWindows: [], + + _onContent: function _onContent(window) { + // not registered yet + if (!pageModManager.has(this)) + return; + + if (!HAS_BUG_642145_FIXED) { + if (this._loadingWindows.indexOf(window) != -1) + return; + this._loadingWindows.push(window); + } + + if ('start' == this.contentScriptWhen) { + 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, + onError: this._onUncaughtError + }); + this._emit('attach', worker); + let self = this; + worker.once('detach', function detach() { + worker.destroy(); + + if (!HAS_BUG_642145_FIXED) { + let idx = self._loadingWindows.indexOf(window); + if (idx != -1) + self._loadingWindows.splice(idx, 1); + } + }); + }, + _onRuleAdd: function _onRuleAdd(url) { + pageModManager.on(url, this._onContent); + }, + _onRuleRemove: function _onRuleRemove(url) { + pageModManager.off(url, this._onContent); + }, + _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.add( + ON_CONTENT, this._onContentWindow = this._onContentWindow.bind(this) + ); + }, + _destructor: function _destructor() { + observers.remove(ON_CONTENT, this._onContentWindow); + for (let rule in RULES) { + this._removeAllListeners(rule); + delete RULES[rule]; + } + this._registryDestructor(); + }, + _onContentWindow: function _onContentWindow(domObj) { + let window = HAS_DOCUMENT_ELEMENT_INSERTED ? domObj.defaultView : domObj; + // XML documents don't have windows, and we don't yet support them. + if (!window) + return; + for (let rule in RULES) + if (RULES[rule].test(window.document.URL)) + this._emit(rule, window); + }, + off: function off(topic, listener) { + this.removeListener(topic, listener); + if (!this._listeners(topic).length) + delete RULES[topic]; + } +}); +const pageModManager = PageModManager(); diff --git a/tools/addon-sdk-1.3/packages/addon-kit/lib/page-worker.js b/tools/addon-sdk-1.3/packages/addon-kit/lib/page-worker.js new file mode 100644 index 0000000..93750a1 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/lib/page-worker.js @@ -0,0 +1,101 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Felipe Gomes (Original Author) + * Myk Melez + * Irakli Gozalishvili + * Drew Willcoxon + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +"use strict"; + +const { Symbiont } = require("api-utils/content"); +const { Trait } = require("api-utils/traits"); + +if (!require("api-utils/xul-app").isOneOf(["Firefox", "Thunderbird"])) { + throw new Error([ + "The page-worker module currently supports only Firefox and Thunderbird. ", + "In the future, we would like it to support other applications, however. ", + "Please see https://bugzilla.mozilla.org/show_bug.cgi?id=546740 for more ", + "information." + ].join("")); +} + +const Page = Trait.compose( + Symbiont.resolve({ + constructor: '_initSymbiont' + }), + { + _frame: Trait.required, + _initFrame: Trait.required, + postMessage: Symbiont.required, + on: Symbiont.required, + destroy: Symbiont.required, + + constructor: function Page(options) { + options = options || {}; + + this.contentURL = 'contentURL' in options ? options.contentURL + : 'about:blank'; + if ('contentScriptWhen' in options) + this.contentScriptWhen = options.contentScriptWhen; + 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); + + this.on('propertyChange', this._onChange.bind(this)); + + this._initSymbiont(); + }, + + _onChange: function _onChange(e) { + if ('contentURL' in e && this._frame) { + // Cleanup the worker before injecting the content script in the new + // document + this._workerCleanup(); + this._initFrame(this._frame); + } + } + } +); +exports.Page = function(options) Page(options); +exports.Page.prototype = Page.prototype; diff --git a/tools/addon-sdk-1.3/packages/addon-kit/lib/panel.js b/tools/addon-sdk-1.3/packages/addon-kit/lib/panel.js new file mode 100644 index 0000000..6fbe4fc --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/lib/panel.js @@ -0,0 +1,404 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Myk Melez (Original Author) + * Irakli Gozalishvili + * Mihai Sucan + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +"use strict"; + +if (!require("api-utils/xul-app").is("Firefox")) { + throw new Error([ + "The panel module currently supports only Firefox. In the future ", + "we would like it to support other applications, however. Please see ", + "https://bugzilla.mozilla.org/show_bug.cgi?id=jetpack-panel-apps ", + "for more information." + ].join("")); +} + +const { Ci } = require("chrome"); +const { validateOptions: valid } = require("api-utils/api-utils"); +const { Symbiont } = require("api-utils/content"); +const { EventEmitter } = require('api-utils/events'); +const timer = require("api-utils/timer"); +const runtime = require("api-utils/runtime"); + +require("api-utils/xpcom").utils.defineLazyServiceGetter( + this, + "windowMediator", + "@mozilla.org/appshell/window-mediator;1", + "nsIWindowMediator" +); + +const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", + ON_SHOW = 'popupshown', + ON_HIDE = 'popuphidden', + validNumber = { is: ['number', 'undefined', 'null'] }; + +/** + * Emits show and hide events. + */ +const Panel = Symbiont.resolve({ + constructor: '_init', + _onInit: '_onSymbiontInit', + destroy: '_symbiontDestructor', + _documentUnload: '_workerDocumentUnload' +}).compose({ + _frame: Symbiont.required, + _init: Symbiont.required, + _onSymbiontInit: Symbiont.required, + _symbiontDestructor: Symbiont.required, + _emit: Symbiont.required, + _asyncEmit: Symbiont.required, + on: Symbiont.required, + removeListener: Symbiont.required, + + _inited: false, + + /** + * If set to `true` frame loaders between xul panel frame and + * hidden frame are swapped. If set to `false` frame loaders are + * set back to normal. Setting the value that was already set will + * have no effect. + */ + set _frameLoadersSwapped(value) { + if (this.__frameLoadersSwapped == value) return; + this._frame.QueryInterface(Ci.nsIFrameLoaderOwner) + .swapFrameLoaders(this._viewFrame); + this.__frameLoadersSwapped = value; + }, + __frameLoadersSwapped: false, + + constructor: function Panel(options) { + this._onShow = this._onShow.bind(this); + this._onHide = this._onHide.bind(this); + this.on('inited', this._onSymbiontInit.bind(this)); + + options = options || {}; + if ('onShow' in options) + this.on('show', options.onShow); + if ('onHide' in options) + this.on('hide', options.onHide); + if ('width' in options) + this.width = options.width; + if ('height' in options) + this.height = options.height; + if ('contentURL' in options) + this.contentURL = options.contentURL; + + this._init(options); + }, + _destructor: function _destructor() { + this.hide(); + this._removeAllListeners('show'); + // defer cleanup to be performed after panel gets hidden + this._xulPanel = null; + this._symbiontDestructor(this); + this._removeAllListeners(this, 'hide'); + }, + destroy: function destroy() { + this._destructor(); + }, + /* Public API: Panel.width */ + get width() this._width, + set width(value) + this._width = valid({ $: value }, { $: validNumber }).$ || this._width, + _width: 320, + /* Public API: Panel.height */ + get height() this._height, + set height(value) + this._height = valid({ $: value }, { $: validNumber }).$ || this._height, + _height: 240, + + /* Public API: Panel.isShowing */ + get isShowing() !!this._xulPanel && this._xulPanel.state == "open", + + /* Public API: Panel.show */ + show: function show(anchor) { + anchor = anchor || null; + let document = getWindow(anchor).document; + let xulPanel = this._xulPanel; + if (!xulPanel) { + xulPanel = this._xulPanel = document.createElementNS(XUL_NS, 'panel'); + xulPanel.setAttribute("type", "arrow"); + + // One anonymous node has a big padding that doesn't work well with + // Jetpack, as we would like to display an iframe that completely fills + // the panel. + // -> Use a XBL wrapper with inner stylesheet to remove this padding. + let css = ".panel-inner-arrowcontent, .panel-arrowcontent {padding: 0;}"; + let originalXBL = "chrome://global/content/bindings/popup.xml#arrowpanel"; + let binding = + '' + + '' + + '' + + '' + + '' + + '' + + ''; + xulPanel.style.MozBinding = 'url("data:text/xml,' + + document.defaultView.encodeURIComponent(binding) + '")'; + + let frame = document.createElementNS(XUL_NS, 'iframe'); + frame.setAttribute('type', 'content'); + frame.setAttribute('flex', '1'); + frame.setAttribute('transparent', 'transparent'); + if (runtime.OS === "Darwin") { + frame.style.borderRadius = "6px"; + frame.style.padding = "1px"; + } + + // Load an empty document in order to have an immediatly loaded iframe, + // so swapFrameLoaders is going to work without having to wait for load. + frame.setAttribute("src","data:,"); + + xulPanel.appendChild(frame); + document.getElementById("mainPopupSet").appendChild(xulPanel); + } + let { width, height } = this, x, y, position; + + if (!anchor) { + // Open the popup in the middle of the window. + x = document.documentElement.clientWidth / 2 - width / 2; + y = document.documentElement.clientHeight / 2 - height / 2; + position = null; + } + else { + // Open the popup by the anchor. + let rect = anchor.getBoundingClientRect(); + + let window = anchor.ownerDocument.defaultView; + + let zoom = window.mozScreenPixelsPerCSSPixel; + 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"; + position = 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) + xulPanel.setAttribute("flip","both"); + } + + // Resize the iframe instead of using panel.sizeTo + // because sizeTo doesn't work with arrow panels + xulPanel.firstChild.style.width = width + "px"; + xulPanel.firstChild.style.height = height + "px"; + + // Wait for the XBL binding to be constructed + function waitForBinding() { + if (!xulPanel.openPopup) { + timer.setTimeout(waitForBinding, 50); + return; + } + xulPanel.openPopup(anchor, position, x, y); + } + waitForBinding(); + + return this._public; + }, + /* Public API: Panel.hide */ + hide: function hide() { + // The popuphiding handler takes care of swapping back the frame loaders + // and removing the XUL panel from the application window, we just have to + // trigger it by hiding the popup. + // XXX Sometimes I get "TypeError: xulPanel.hidePopup is not a function" + // when quitting the host application while a panel is visible. To suppress + // them, this now checks for "hidePopup" in xulPanel before calling it. + // It's not clear if there's an actual issue or the error is just normal. + let xulPanel = this._xulPanel; + if (xulPanel && "hidePopup" in xulPanel) + xulPanel.hidePopup(); + return this._public; + }, + + /* Public API: Panel.resize */ + resize: function resize(width, height) { + this.width = width; + this.height = height; + // Resize the iframe instead of using panel.sizeTo + // because sizeTo doesn't work with arrow panels + let xulPanel = this._xulPanel; + if (xulPanel) { + xulPanel.firstChild.style.width = width + "px"; + xulPanel.firstChild.style.height = height + "px"; + } + }, + + // While the panel is visible, this is the XUL we use to display it. + // Otherwise, it's null. + get _xulPanel() this.__xulPanel, + set _xulPanel(value) { + let xulPanel = this.__xulPanel; + if (value === xulPanel) return; + if (xulPanel) { + xulPanel.removeEventListener(ON_HIDE, this._onHide, false); + xulPanel.removeEventListener(ON_SHOW, this._onShow, false); + xulPanel.parentNode.removeChild(xulPanel); + } + if (value) { + value.addEventListener(ON_HIDE, this._onHide, false); + value.addEventListener(ON_SHOW, this._onShow, false); + } + this.__xulPanel = value; + }, + __xulPanel: null, + get _viewFrame() this.__xulPanel.children[0], + /** + * When the XUL panel becomes hidden, we swap frame loaders back to move + * the content of the panel to the hidden frame & remove panel element. + */ + _onHide: function _onHide() { + try { + this._frameLoadersSwapped = false; + this._xulPanel = null; + this._emit('hide'); + } catch(e) { + this._emit('error', e); + } + }, + /** + * When the XUL panel becomes shown, we swap frame loaders between panel + * frame and hidden frame to preserve state of the content dom. + */ + _onShow: function _onShow() { + try { + if (!this._inited) // defer if not initialized yet + return this.on('inited', this._onShow.bind(this)); + this._frameLoadersSwapped = true; + + // Retrieve computed text color style in order to apply to the iframe + // document. As MacOS background is dark gray, we need to use skin's text + // color. + let win = this._xulPanel.ownerDocument.defaultView; + let node = win.document.getAnonymousElementByAttribute(this._xulPanel, + "class", "panel-inner-arrowcontent"); + let textColor = win.getComputedStyle(node).getPropertyValue("color"); + let doc = this._xulPanel.firstChild.contentDocument; + let style = doc.createElement("style"); + style.textContent = "body { color: " + textColor + "; }"; + let container = doc.head ? doc.head : doc.documentElement; + if (container.firstChild) + container.insertBefore(style, container.firstChild); + else + container.appendChild(style); + + + this._emit('show'); + } catch(e) { + this._emit('error', e); + } + }, + /** + * Notification that panel was fully initialized. + */ + _onInit: function _onInit() { + this._inited = true; + + // Avoid panel document from resizing the browser window + // New platform capability added through bug 635673 + if ("allowWindowControl" in this._frame.docShell) + this._frame.docShell.allowWindowControl = false; + + // perform all deferred tasks like initSymbiont, show, hide ... + // TODO: We're publicly exposing a private event here; this + // 'inited' event should really be made private, somehow. + this._emit('inited'); + }, + + // Catch document unload event in order to rebind load event listener with + // Symbiont._initFrame if Worker._documentUnload destroyed the worker + _documentUnload: function(subject, topic, data) { + if (this._workerDocumentUnload(subject, topic, data)) { + this._initFrame(this._frame); + return true; + } + return false; + } +}); +exports.Panel = function(options) Panel(options) +exports.Panel.prototype = Panel.prototype; + +function getWindow(anchor) { + let window; + + if (anchor) { + let anchorWindow = anchor.ownerDocument.defaultView.top; + let anchorDocument = anchorWindow.document; + + let enumerator = windowMediator.getEnumerator("navigator:browser"); + while (enumerator.hasMoreElements()) { + let enumWindow = enumerator.getNext(); + + // Check if the anchor is in this browser window. + if (enumWindow == anchorWindow) { + window = anchorWindow; + break; + } + + // Check if the anchor is in a browser tab in this browser window. + let browser = enumWindow.gBrowser.getBrowserForDocument(anchorDocument); + if (browser) { + window = enumWindow; + break; + } + + // Look in other subdocuments (sidebar, etc.)? + } + } + + // If we didn't find the anchor's window (or we have no anchor), + // return the most recent browser window. + if (!window) + window = windowMediator.getMostRecentWindow("navigator:browser"); + + return window; +} + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/lib/passwords.js b/tools/addon-sdk-1.3/packages/addon-kit/lib/passwords.js new file mode 100644 index 0000000..8225da9 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/lib/passwords.js @@ -0,0 +1,92 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Irakli Gozalishvili (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +"use strict"; +const { Trait } = require("api-utils/light-traits"); +const utils = require("api-utils/passwords/utils"); +const defer = require("api-utils/utils/function").Enqueued; + +/** + * Utility function that returns `onComplete` and `onError` callbacks form the + * given `options` objects. Also properties are removed from the passed + * `options` objects. + * @param {Object} options + * Object that is passed to the exported functions of this module. + * @returns {Function[]} + * Array with two elements `onComplete` and `onError` functions. + */ +function getCallbacks(options) { + let value = [ + 'onComplete' in options ? options.onComplete : null, + 'onError' in options ? defer(options.onError) : console.exception + ]; + + delete options.onComplete; + delete options.onError; + + return value; +}; + +/** + * Creates a wrapper function that tries to call `onComplete` with a return + * value of the wrapped function or falls back to `onError` if wrapped function + * throws an exception. + */ +function createWrapperMethod(wrapped) { + return function (options) { + let [ onComplete, onError ] = getCallbacks(options); + try { + let value = wrapped(options); + if (onComplete) { + defer(function() { + try { + onComplete(value); + } catch (exception) { + onError(exception); + } + })(); + } + } catch (exception) { + onError(exception); + } + }; +} + +exports.search = createWrapperMethod(utils.search); +exports.store = createWrapperMethod(utils.store); +exports.remove = createWrapperMethod(utils.remove); diff --git a/tools/addon-sdk-1.3/packages/addon-kit/lib/private-browsing.js b/tools/addon-sdk-1.3/packages/addon-kit/lib/private-browsing.js new file mode 100644 index 0000000..8336e7b --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/lib/private-browsing.js @@ -0,0 +1,102 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Paul O’Shannessy + * Irakli Gozalishvili + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +"use strict"; + +const {Cc,Ci} = require("chrome"); +const observers = require("api-utils/observer-service"); +const { EventEmitter } = require("api-utils/events"); +const { setTimeout } = require("api-utils/timer"); +const unload = require("api-utils/unload"); + +const ON_START = "start"; +const ON_STOP = "stop"; +const ON_TRANSITION = "private-browsing-transition-complete"; + +let pbService; +// Currently, only Firefox implements the private browsing service. +if (require("api-utils/xul-app").is("Firefox")) { + pbService = Cc["@mozilla.org/privatebrowsing;1"]. + getService(Ci.nsIPrivateBrowsingService); +} + +function toggleMode(value) pbService.privateBrowsingEnabled = !!value + +const privateBrowsing = EventEmitter.compose({ + constructor: function PrivateBrowsing() { + // Binding method to instance since it will be used with `setTimeout`. + this._emitOnObject = this._emitOnObject.bind(this); + this.unload = this.unload.bind(this); + // Report unhandled errors from listeners + this.on("error", console.exception.bind(console)); + unload.ensure(this); + // We only need to add observers if `pbService` exists. + if (pbService) { + observers.add(ON_TRANSITION, this.onTransition.bind(this)); + this._isActive = pbService.privateBrowsingEnabled; + } + }, + unload: function _destructor() { + this._removeAllListeners(ON_START); + this._removeAllListeners(ON_STOP); + }, + // We don't need to do anything with cancel here. + onTransition: function onTransition() { + let isActive = this._isActive = pbService.privateBrowsingEnabled; + setTimeout(this._emitOnObject, 0, exports, isActive ? ON_START : ON_STOP); + }, + get isActive() this._isActive, + set isActive(value) { + if (pbService) + // We toggle private browsing mode asynchronously in order to work around + // bug 659629. Since private browsing transitions are asynchronous + // anyway, this doesn't significantly change the behavior of the API. + setTimeout(toggleMode, 0, value); + }, + _isActive: false +})() + +Object.defineProperty(exports, "isActive", { + get: function() privateBrowsing.isActive +}); +exports.activate = function activate() privateBrowsing.isActive = true; +exports.deactivate = function deactivate() privateBrowsing.isActive = false; +exports.on = privateBrowsing.on; +exports.once = privateBrowsing.once; +exports.removeListener = privateBrowsing.removeListener; + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/lib/request.js b/tools/addon-sdk-1.3/packages/addon-kit/lib/request.js new file mode 100644 index 0000000..a72b28c --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/lib/request.js @@ -0,0 +1,309 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Paul O’Shannessy (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +"use strict"; +const xpcom = require("api-utils/xpcom"); +const xhr = require("api-utils/xhr"); +const errors = require("api-utils/errors"); +const apiUtils = require("api-utils/api-utils"); + +// Ugly but will fix with: https://bugzilla.mozilla.org/show_bug.cgi?id=596248 +const EventEmitter = require('api-utils/events').EventEmitter.compose({ + constructor: function EventEmitter() this +}); + +// Instead of creating a new validator for each request, just make one and reuse it. +const validator = new OptionsValidator({ + url: { + //XXXzpao should probably verify that url is a valid url as well + is: ["string"] + }, + headers: { + map: function (v) v || {}, + is: ["object"], + }, + content: { + map: function (v) v || null, + is: ["string", "object", "null"], + }, + contentType: { + map: function (v) v || "application/x-www-form-urlencoded", + is: ["string"], + }, + overrideMimeType: { + map: function(v) v || null, + is: ["string", "null"], + } +}); + +const REUSE_ERROR = "This request object has been used already. You must " + + "create a new one to make a new request." + +function Request(options) { + const self = EventEmitter(), + _public = self._public; + // request will hold the actual XHR object + let request; + let response; + + if ('onComplete' in options) + self.on('complete', options.onComplete) + options = validator.validateOptions(options); + + // function to prep the request since it's the same between GET and POST + function makeRequest(mode) { + // If this request has already been used, then we can't reuse it. Throw an error. + if (request) { + throw new Error(REUSE_ERROR); + } + + request = new xhr.XMLHttpRequest(); + + let url = options.url; + // Build the data to be set. For GET requests, we want to append that to + // the URL before opening the request. + let data = makeQueryString(options.content); + if (mode == "GET" && data) { + // If the URL already has ? in it, then we want to just use & + url = url + (/\?/.test(url) ? "&" : "?") + data; + } + + // open the request + request.open(mode, url); + + // request header must be set after open, but before send + request.setRequestHeader("Content-Type", options.contentType); + + // set other headers + for (let k in options.headers) { + request.setRequestHeader(k, options.headers[k]); + } + + // set overrideMimeType + if (options.overrideMimeType) { + request.overrideMimeType(options.overrideMimeType); + } + + // handle the readystate, create the response, and call the callback + request.onreadystatechange = function () { + if (request.readyState == 4) { + response = new Response(request); + errors.catchAndLog(function () { + self._emit('complete', response); + })(); + } + } + + // actually send the request. we only want to send data on POST requests + request.send(mode == "POST" ? data : null); + } + + // Map these setters/getters to the options + ["url", "headers", "content", "contentType"].forEach(function (k) { + _public.__defineGetter__(k, function () options[k]); + _public.__defineSetter__(k, function (v) { + // This will automatically rethrow errors from apiUtils.validateOptions. + return options[k] = validator.validateSingleOption(k, v); + }); + }); + + // response should be available as a getter + _public.__defineGetter__("response", function () response); + + _public.get = function () { + makeRequest("GET"); + return this; + }; + + _public.post = function () { + makeRequest("POST"); + return this; + }; + + return _public; +} +exports.Request = Request; + +// Converts an object of unordered key-vals to a string that can be passed +// as part of a request +function makeQueryString(content) { + // Explicitly return null if we have null, and empty string, or empty object. + if (!content) { + return null; + } + + // If content is already a string, just return it as is. + if (typeof(content) == "string") { + return content; + } + + // At this point we have a k:v object. Iterate over it and encode each value. + // Arrays and nested objects will get encoded as needed. For example... + // + // { foo: [1, 2, { omg: "bbq", "all your base!": "are belong to us" }], bar: "baz" } + // + // will be encoded as + // + // foo[0]=1&foo[1]=2&foo[2][omg]=bbq&foo[2][all+your+base!]=are+belong+to+us&bar=baz + // + // Keys (including "[" and "]") and values will be encoded with + // fixedEncodeURIComponent before returning. + // + // Execution was inspired by jQuery, but some details have changed and numeric + // array keys are included (whereas they are not in jQuery). + + let encodedContent = []; + function add(key, val) { + encodedContent.push(fixedEncodeURIComponent(key) + "=" + + fixedEncodeURIComponent(val)); + } + + function make(key, val) { + if (typeof(val) === "object" && val !== null) { + for ([k, v] in Iterator(val)) { + make(key + "[" + k + "]", v); + } + } + else { + add(key, val) + } + } + for ([k, v] in Iterator(content)) { + make(k, v); + } + return encodedContent.join("&"); + + //XXXzpao In theory, we can just use a FormData object on 1.9.3, but I had + // trouble getting that working. It would also be nice to stay + // backwards-compat as long as possible. Keeping this in for now... + // let formData = Cc["@mozilla.org/files/formdata;1"]. + // createInstance(Ci.nsIDOMFormData); + // for ([k, v] in Iterator(content)) { + // formData.append(k, v); + // } + // return formData; +} + + +// encodes a string safely for application/x-www-form-urlencoded +// adheres to RFC 3986 +// see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Functions/encodeURIComponent +function fixedEncodeURIComponent (str) { + return encodeURIComponent(str).replace(/%20/g, "+").replace(/!/g, "%21"). + replace(/'/g, "%27").replace(/\(/g, "%28"). + replace(/\)/g, "%29").replace(/\*/g, "%2A"); +} + +function Response(request) { + // Define the straight mappings of our value to original request value + xpcom.utils.defineLazyGetter(this, "text", function () request.responseText); + xpcom.utils.defineLazyGetter(this, "xml", function () { + throw new Error("Sorry, the 'xml' property is no longer available. " + + "see bug 611042 for more information."); + }); + xpcom.utils.defineLazyGetter(this, "status", function () request.status); + xpcom.utils.defineLazyGetter(this, "statusText", function () request.statusText); + + // this.json should be the JS object, so we need to attempt to parse it. + xpcom.utils.defineLazyGetter(this, "json", function () { + let _json = null; + try { + _json = JSON.parse(this.text); + } + catch (e) {} + return _json; + }); + + // this.headers also should be a JS object, so we need to split up the raw + // headers string provided by the request. + xpcom.utils.defineLazyGetter(this, "headers", function () { + let _headers = {}; + let lastKey; + // Since getAllResponseHeaders() will return null if there are no headers, + // defend against it by defaulting to "" + let rawHeaders = request.getAllResponseHeaders() || ""; + rawHeaders.split("\n").forEach(function (h) { + // According to the HTTP spec, the header string is terminated by an empty + // line, so we can just skip it. + if (!h.length) { + return; + } + + let index = h.indexOf(":"); + // The spec allows for leading spaces, so instead of assuming a single + // leading space, just trim the values. + let key = h.substring(0, index).trim(), + val = h.substring(index + 1).trim(); + + // For empty keys, that means that the header value spanned multiple lines. + // In that case we should append the value to the value of lastKey with a + // new line. We'll assume lastKey will be set because there should never + // be an empty key on the first pass. + if (key) { + _headers[key] = val; + lastKey = key; + } + else { + _headers[lastKey] += "\n" + val; + } + }); + return _headers; + }) +} + +// apiUtils.validateOptions doesn't give the ability to easily validate single +// options, so this is a wrapper that provides that ability. +function OptionsValidator(rules) { + this.rules = rules; + + this.validateOptions = function (options) { + return apiUtils.validateOptions(options, this.rules); + } + + this.validateSingleOption = function (field, value) { + // We need to create a single rule object from our listed rules. To avoid + // JavaScript String warnings, check for the field & default to an empty object. + let singleRule = {}; + if (field in this.rules) { + singleRule[field] = this.rules[field]; + } + let singleOption = {}; + singleOption[field] = value; + // This should throw if it's invalid, which will bubble up & out. + return apiUtils.validateOptions(singleOption, singleRule)[field]; + } +} diff --git a/tools/addon-sdk-1.3/packages/addon-kit/lib/selection.js b/tools/addon-sdk-1.3/packages/addon-kit/lib/selection.js new file mode 100644 index 0000000..48db7fc --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/lib/selection.js @@ -0,0 +1,448 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Eric H. Jung + * Irakli Gozalishivili + * Matteo Ferretti + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +"use strict"; + +if (!require("api-utils/xul-app").is("Firefox")) { + throw new Error([ + "The selection module currently supports only Firefox. In the future ", + "we would like it to support other applications, however. Please see ", + "https://bugzilla.mozilla.org/show_bug.cgi?id=560716 for more information." + ].join("")); +} + +let { Ci } = require("chrome"), + { setTimeout } = require("api-utils/timer"), + { EventEmitter } = require("api-utils/events"); + +// The selection type HTML +const HTML = 0x01; + +// The selection type TEXT +const TEXT = 0x02; + +// The selection type DOM (internal use only) +const DOM = 0x03; + +// A more developer-friendly message than the caught exception when is not +// possible change a selection. +const ERR_CANNOT_CHANGE_SELECTION = + "It isn't possible to change the selection, as there isn't currently a selection"; + +/** + * Creates an object from which a selection can be set, get, etc. Each + * object has an associated with a range number. Range numbers are the + * 0-indexed counter of selection ranges as explained at + * https://developer.mozilla.org/en/DOM/Selection. + * + * @param rangeNumber + * The zero-based range index into the selection + */ +function Selection(rangeNumber) { + + // In order to hide the private rangeNumber argument from API consumers while + // still enabling Selection getters/setters to access it, the getters/setters + // are defined as lexical closures in the Selector constructor. + + this.__defineGetter__("text", function () getSelection(TEXT, rangeNumber)); + this.__defineSetter__("text", function (str) setSelection(str, rangeNumber)); + + this.__defineGetter__("html", function () getSelection(HTML, rangeNumber)); + this.__defineSetter__("html", function (str) setSelection(str, rangeNumber)); + + this.__defineGetter__("isContiguous", function () { + let sel = getSelection(DOM); + + // If there are multiple ranges, the selection is definitely discontiguous. + // It returns `false` also if there are no selection; and `true` if there is + // a single non empty range, or a selection in a text field - contiguous or + // not (text field selection APIs doesn't support multiple selections). + + if (sel.rangeCount > 1) + return false; + + return !!(safeGetRange(sel, 0) || getElementWithSelection()); + }); +} + +require("api-utils/xpcom").utils.defineLazyServiceGetter(this, "windowMediator", + "@mozilla.org/appshell/window-mediator;1", "nsIWindowMediator"); + +/** + * Returns the most recent content window + */ +function context() { + // Overlay names should probably go into the xul-app module instead of here + return windowMediator.getMostRecentWindow("navigator:browser").document. + commandDispatcher.focusedWindow; +} + +/** + * Returns the current selection from most recent content window. Depending on + * the specified |type|, the value returned can be a string of text, stringified + * HTML, or a DOM selection object as described at + * https://developer.mozilla.org/en/DOM/Selection. + * + * @param type + * Specifies the return type of the selection. Valid values are the one + * of the constants HTML, TEXT, or DOM. + * + * @param rangeNumber + * Specifies the zero-based range index of the returned selection. + */ +function getSelection(type, rangeNumber) { + let window, selection; + try { + window = context(); + selection = window.getSelection(); + } + catch (e) { + return null; + } + + // Get the selected content as the specified type + if (type == DOM) + return selection; + else if (type == TEXT) { + let range = safeGetRange(selection, rangeNumber); + + if (range) + return range.toString(); + + let node = getElementWithSelection(window); + + if (!node) + return null; + + return node.value.substring(node.selectionStart, node.selectionEnd); + } + else if (type == HTML) { + let range = safeGetRange(selection, rangeNumber); + // Another way, but this includes the xmlns attribute for all elements in + // Gecko 1.9.2+ : + // return Cc["@mozilla.org/xmlextras/xmlserializer;1"]. + // createInstance(Ci.nsIDOMSerializer).serializeToSTring(range. + // cloneContents()); + if (!range) + return null; + let node = window.document.createElement("span"); + node.appendChild(range.cloneContents()); + return node.innerHTML; + } + throw new Error("Type " + type + " is unrecognized."); +} + +/** + * Returns the specified range in a selection without throwing an exception. + * + * @param selection + * A selection object as described at + * https://developer.mozilla.org/en/DOM/Selection + * + * @param rangeNumber + * Specifies the zero-based range index of the returned selection. + */ +function safeGetRange(selection, rangeNumber) { + try { + let range = selection.getRangeAt(rangeNumber); + if (!range || range.toString() == "") + return null; + return range; + } + catch (e) { + return null; + } +} + +/** + * Returns a reference of the DOM's active element for the window given, if it + * supports the text field selection API and has a text selected. + * + * Note: + * we need this method because window.getSelection doesn't return a selection + * for text selected in a form field (see bug 85686) + * + * @param {nsIWindow} [window] + * A reference to a window + */ +function getElementWithSelection(window) { + let element; + + try { + element = (window || context()).document.activeElement; + } + catch (e) { + element = null; + } + + if (!element) + return null; + + let { value, selectionStart, selectionEnd } = element; + + let hasSelection = typeof value === "string" && + !isNaN(selectionStart) && + !isNaN(selectionEnd) && + selectionStart !== selectionEnd; + + return hasSelection ? element : null; +} +/** + * Sets the current selection of the most recent content document by changing + * the existing selected text/HTML range to the specified value. + * + * @param val + * The value for the new selection + * + * @param rangeNumber + * The zero-based range index of the selection to be set + * + */ +function setSelection(val, rangeNumber) { + // Make sure we have a window context & that there is a current selection. + // Selection cannot be set unless there is an existing selection. + let window, selection; + + try { + window = context(); + selection = window.getSelection(); + } + catch (e) { + throw new Error(ERR_CANNOT_CHANGE_SELECTION); + } + + let range = safeGetRange(selection, rangeNumber); + + if (range) { + // Get rid of the current selection and insert our own + range.deleteContents(); + let node = window.document.createElement("span"); + range.surroundContents(node); + + // Some relevant JEP-111 requirements: + + // Setting the text property replaces the selection with the value to + // which the property is set and sets the html property to the same value + // to which the text property is being set. + + // Setting the html property replaces the selection with the value to + // which the property is set and sets the text property to the text version + // of the HTML value. + + // This sets both the HTML and text properties. + node.innerHTML = val; + } else { + let node = getElementWithSelection(window); + + if (!node) + throw new Error(ERR_CANNOT_CHANGE_SELECTION); + + let { value, selectionStart, selectionEnd } = node; + + let newSelectionEnd = selectionStart + val.length; + + node.value = value.substring(0, selectionStart) + + val + + value.substring(selectionEnd, value.length); + + node.setSelectionRange(selectionStart, newSelectionEnd); + } +} + +function onLoad(event) { + SelectionListenerManager.onLoad(event); +} + +function onUnload(event) { + SelectionListenerManager.onUnload(event); +} + +function onSelect() { + SelectionListenerManager.onSelect(); +} + +let SelectionListenerManager = { + QueryInterface: require("api-utils/xpcom").utils. + generateQI([Ci.nsISelectionListener]), + + // The collection of listeners wanting to be notified of selection changes + listeners: EventEmitter.compose({ + emit: function emit(type) this._emitOnObject(exports, type), + off: function() this._removeAllListeners.apply(this, arguments) + })(), + /** + * This is the nsISelectionListener implementation. This function is called + * by Gecko when a selection is changed interactively. + * + * We only pay attention to the SELECTALL, KEYPRESS, and MOUSEUP selection + * reasons. All reasons are listed here: + * + * http://mxr.mozilla.org/mozilla1.9.2/source/content/base/public/ + * nsISelectionListener.idl + * + * The other reasons (NO_REASON, DRAG_REASON, MOUSEDOWN_REASON) aren't + * applicable to us. + */ + notifySelectionChanged: function notifySelectionChanged(document, selection, + reason) { + if (!["SELECTALL", "KEYPRESS", "MOUSEUP"].some(function(type) reason & + Ci.nsISelectionListener[type + "_REASON"]) || selection.toString() == "") + return; + + this.onSelect(); + }, + + onSelect : function onSelect() { + setTimeout(this.listeners.emit, 0, "select"); + }, + + /** + * Part of the Tracker implementation. This function is called by the + * tabs module when a browser is being tracked. Often, that means a new tab + * has been opened, but it can also mean an addon has been installed while + * tabs are already opened. In that case, this function is called for those + * already-opened tabs. + * + * @param browser + * The browser being tracked + */ + onTrack: function onTrack(browser) { + browser.addEventListener("load", onLoad, true); + browser.addEventListener("unload", onUnload, true); + }, + + onLoad: function onLoad(event) { + // Nothing to do without a useful window + let window = event.target.defaultView; + if (!window) + return; + + // Wrap the add selection call with some number of setTimeout 0 because some + // reason it's possible to add a selection listener "too early". 2 sometimes + // works for gmail, and more consistently with 3, so make it 5 to be safe. + let count = 0; + let self = this; + function wrap(count, func) { + if (count-- > 0) + require("api-utils/timer").setTimeout(wrap, 0); + else + self.addSelectionListener(window); + } + wrap(); + }, + + addSelectionListener: function addSelectionListener(window) { + if (window.jetpack_core_selection_listener) + return; + let selection = window.getSelection(); + if (selection instanceof Ci.nsISelectionPrivate) + selection.addSelectionListener(this); + + // nsISelectionListener implementation seems not fire a notification if + // a selection is in a text field, therefore we need to add a listener to + // window.onselect, that is fired only for text fields. + // https://developer.mozilla.org/en/DOM/window.onselect + window.addEventListener("select", onSelect, true); + + window.jetpack_core_selection_listener = true; + }, + + onUnload: function onUnload(event) { + // Nothing to do without a useful window + let window = event.target.defaultView; + if (!window) + return; + this.removeSelectionListener(window); + this.listeners.off('error'); + this.listeners.off('selection'); + }, + + removeSelectionListener: function removeSelectionListener(window) { + if (!window.jetpack_core_selection_listener) + return; + let selection = window.getSelection(); + if (selection instanceof Ci.nsISelectionPrivate) + selection.removeSelectionListener(this); + + window.removeEventListener("select", onSelect); + + window.jetpack_core_selection_listener = false; + }, + + /** + * Part of the TabTracker implementation. This function is called by the + * tabs module when a browser is being untracked. Usually, that means a tab + * has been closed. + * + * @param browser + * The browser being untracked + */ + onUntrack: function onUntrack(browser) { + browser.removeEventListener("load", onLoad, true); + browser.removeEventListener("unload", onUnload, true); + } +}; +SelectionListenerManager.listeners.on('error', console.error); + +/** + * Install |SelectionListenerManager| as tab tracker in order to watch + * tab opening/closing + */ +require("api-utils/tab-browser").Tracker(SelectionListenerManager); + +/** + * Exports an iterator so that discontiguous selections can be iterated. + * + * If discontiguous selections are in a text field, only the first one + * is returned because the text field selection APIs doesn't support + * multiple selections. + */ +exports.__iterator__ = function __iterator__() { + let sel = getSelection(DOM); + let rangeCount = sel.rangeCount || (getElementWithSelection() ? 1 : 0); + + for (let i = 0; i < rangeCount; i++) + yield new Selection(i); +}; + +exports.on = SelectionListenerManager.listeners.on; +exports.removeListener = SelectionListenerManager.listeners.removeListener; + +// Export the Selection singleton. Its rangeNumber is always zero. +Selection.call(exports, 0); + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/lib/simple-storage.js b/tools/addon-sdk-1.3/packages/addon-kit/lib/simple-storage.js new file mode 100644 index 0000000..b719e27 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/lib/simple-storage.js @@ -0,0 +1,263 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim:set ts=2 sw=2 sts=2 et filetype=javascript + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Drew Willcoxon (Original Author) + * Irakli Gozalishvili + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +"use strict"; + +const {Cc,Ci} = require("chrome"); +const file = require("api-utils/file"); +const prefs = require("api-utils/preferences-service"); +const jpSelf = require("self"); +const timer = require("api-utils/timer"); +const unload = require("api-utils/unload"); +const { EventEmitter } = require("api-utils/events"); +const { Trait } = require("api-utils/traits"); + +const WRITE_PERIOD_PREF = "extensions.addon-sdk.simple-storage.writePeriod"; +const WRITE_PERIOD_DEFAULT = 300000; // 5 minutes + +const QUOTA_PREF = "extensions.addon-sdk.simple-storage.quota"; +const QUOTA_DEFAULT = 5242880; // 5 MiB + +const JETPACK_DIR_BASENAME = "jetpack"; + + +// simpleStorage.storage +exports.__defineGetter__("storage", function () manager.root); +exports.__defineSetter__("storage", function (val) manager.root = val); + +// simpleStorage.quotaUsage +exports.__defineGetter__("quotaUsage", function () manager.quotaUsage); + +// A generic JSON store backed by a file on disk. This should be isolated +// enough to move to its own module if need be... +function JsonStore(options) { + this.filename = options.filename; + this.quota = options.quota; + this.writePeriod = options.writePeriod; + this.onOverQuota = options.onOverQuota; + this.onWrite = options.onWrite; + + unload.ensure(this); + + this.writeTimer = timer.setInterval(this.write.bind(this), + this.writePeriod); +} + +JsonStore.prototype = { + // The store's root. + get root() { + return this.isRootInited ? this._root : {}; + }, + + // Performs some type checking. + set root(val) { + let types = ["array", "boolean", "null", "number", "object", "string"]; + if (types.indexOf(typeof(val)) < 0) { + throw new Error("storage must be one of the following types: " + + types.join(", ")); + } + this._root = val; + return val; + }, + + // True if the root has ever been set (either via the root setter or by the + // backing file's having been read). + get isRootInited() { + return this._root !== undefined; + }, + + // Percentage of quota used, as a number [0, Inf). > 1 implies over quota. + // Undefined if there is no quota. + get quotaUsage() { + return this.quota > 0 ? + JSON.stringify(this.root).length / this.quota : + undefined; + }, + + // Removes the backing file and all empty subdirectories. + purge: function JsonStore_purge() { + try { + // This'll throw if the file doesn't exist. + file.remove(this.filename); + let parentPath = this.filename; + do { + parentPath = file.dirname(parentPath); + // This'll throw if the dir isn't empty. + file.rmdir(parentPath); + } while (file.basename(parentPath) !== JETPACK_DIR_BASENAME); + } + catch (err) {} + }, + + // Initializes the root by reading the backing file. + read: function JsonStore_read() { + try { + let str = file.read(this.filename); + + // Ideally we'd log the parse error with console.error(), but logged + // errors cause tests to fail. Supporting "known" errors in the test + // harness appears to be non-trivial. Maybe later. + this.root = JSON.parse(str); + } + catch (err) { + this.root = {}; + } + }, + + // If the store is under quota, writes the root to the backing file. + // Otherwise quota observers are notified and nothing is written. + write: function JsonStore_write() { + if (this.quotaUsage > 1) + this.onOverQuota(this); + else + this._write(); + }, + + // Cleans up on unload. If unloading because of uninstall, the store is + // purged; otherwise it's written. + unload: function JsonStore_unload(reason) { + timer.clearInterval(this.writeTimer); + this.writeTimer = null; + + if (reason === "uninstall") + this.purge(); + else + this._write(); + }, + + // True if the root is an empty object. + get _isEmpty() { + if (this.root && typeof(this.root) === "object") { + let empty = true; + for (let key in this.root) { + empty = false; + break; + } + return empty; + } + return false; + }, + + // Writes the root to the backing file, notifying write observers when + // complete. If the store is over quota or if it's empty and the store has + // never been written, nothing is written and write observers aren't notified. + _write: function JsonStore__write() { + // Don't write if the root is uninitialized or if the store is empty and the + // backing file doesn't yet exist. + if (!this.isRootInited || (this._isEmpty && !file.exists(this.filename))) + return; + + // If the store is over quota, don't write. The current under-quota state + // should persist. + if (this.quotaUsage > 1) + return; + + // Finally, write. + let stream = file.open(this.filename, "w"); + try { + stream.writeAsync(JSON.stringify(this.root), function writeAsync(err) { + if (err) + console.error("Error writing simple storage file: " + this.filename); + else if (this.onWrite) + this.onWrite(this); + }.bind(this)); + } + catch (err) { + // writeAsync closes the stream after it's done, so only close on error. + stream.close(); + } + } +}; + + +// This manages a JsonStore singleton and tailors its use to simple storage. +// The root of the JsonStore is lazy-loaded: The backing file is only read the +// first time the root's gotten. +let manager = Trait.compose(EventEmitter, Trait.compose({ + jsonStore: null, + + // The filename of the store, based on the profile dir and extension ID. + get filename() { + let storeFile = Cc["@mozilla.org/file/directory_service;1"]. + getService(Ci.nsIProperties). + get("ProfD", Ci.nsIFile); + storeFile.append(JETPACK_DIR_BASENAME); + storeFile.append(jpSelf.id); + storeFile.append("simple-storage"); + file.mkpath(storeFile.path); + storeFile.append("store.json"); + return storeFile.path; + }, + + get quotaUsage() { + return this.jsonStore.quotaUsage; + }, + + get root() { + if (!this.jsonStore.isRootInited) + this.jsonStore.read(); + return this.jsonStore.root; + }, + + set root(val) { + return this.jsonStore.root = val; + }, + + unload: function manager_unload() { + this._removeAllListeners("OverQuota"); + this._removeAllListeners("error"); + }, + + constructor: function manager_constructor() { + // Log unhandled errors. + this.on("error", console.exception.bind(console)); + unload.ensure(this); + + this.jsonStore = new JsonStore({ + filename: this.filename, + writePeriod: prefs.get(WRITE_PERIOD_PREF, WRITE_PERIOD_DEFAULT), + quota: prefs.get(QUOTA_PREF, QUOTA_DEFAULT), + onOverQuota: this._emitOnObject.bind(this, exports, "OverQuota") + }); + } +}))(); + +exports.on = manager.on; +exports.removeListener = manager.removeListener; diff --git a/tools/addon-sdk-1.3/packages/addon-kit/lib/tabs.js b/tools/addon-sdk-1.3/packages/addon-kit/lib/tabs.js new file mode 100644 index 0000000..81681d6 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/lib/tabs.js @@ -0,0 +1,62 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dietrich Ayala (Original author) + * Felipe Gomes + * Irakli Gozalishvili + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +"use strict"; + +if (!require("api-utils/xul-app").is("Firefox")) { + throw new Error([ + "The tabs module currently supports only Firefox. In the future ", + "we would like it to support other applications, however. Please see ", + "https://bugzilla.mozilla.org/show_bug.cgi?id=560716 for more information." + ].join("")); +} + +const { browserWindows } = require("./windows"); +const { tabs } = require("api-utils/windows/tabs"); + +Object.defineProperties(tabs, { + open: { value: function open(options) { + if (options.inNewWindow) + // `tabs` option is under review and may be removed. + return browserWindows.open({ tabs: [ options ] }); + // Open in active window if new window was not required. + return browserWindows.activeWindow.tabs.open(options); + }} +}); +// It's a hack but we will be able to remove it once will implement CommonJS +// feature that would allow us to override exports. +exports.__proto__ = tabs; diff --git a/tools/addon-sdk-1.3/packages/addon-kit/lib/timers.js b/tools/addon-sdk-1.3/packages/addon-kit/lib/timers.js new file mode 100644 index 0000000..9373dea --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/lib/timers.js @@ -0,0 +1,40 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Irakli Gozalishvili + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +"use strict"; + +// This module just proxies to the low level equivalent "timer" in "api-utils". +module.exports = require("api-utils/timer"); diff --git a/tools/addon-sdk-1.3/packages/addon-kit/lib/widget.js b/tools/addon-sdk-1.3/packages/addon-kit/lib/widget.js new file mode 100644 index 0000000..39f825a --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/lib/widget.js @@ -0,0 +1,938 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dietrich Ayala (Original Author) + * Drew Willcoxon + * Irakli Gozalishvili + * Alexandre Poirot + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +"use strict"; + +const {Cc, Ci} = require("chrome"); + +// Widget content types +const CONTENT_TYPE_URI = 1; +const CONTENT_TYPE_HTML = 2; +const CONTENT_TYPE_IMAGE = 3; + +const ERR_CONTENT = "No content or contentURL property found. Widgets must " + + "have one or the other.", + ERR_LABEL = "The widget must have a non-empty label property.", + ERR_ID = "You have to specify a unique value for the id property of " + + "your widget in order for the application to remember its " + + "position.", + ERR_DESTROYED = "The widget has been destroyed and can no longer be used."; + +// Supported events, mapping from DOM event names to our event names +const EVENTS = { + "click": "click", + "mouseover": "mouseover", + "mouseout": "mouseout", +}; + +if (!require("api-utils/xul-app").is("Firefox")) { + throw new Error([ + "The widget module currently supports only Firefox. In the future ", + "it will support other applications. Please see ", + "https://bugzilla.mozilla.org/show_bug.cgi?id=560716 for more information." + ].join("")); +} + +const { validateOptions } = require("api-utils/api-utils"); +const panels = require("./panel"); +const { EventEmitter, EventEmitterTrait } = require("api-utils/events"); +const { Trait } = require("api-utils/traits"); +const LightTrait = require('api-utils/light-traits').Trait; +const { Loader, Symbiont } = require("api-utils/content"); +const timer = require("api-utils/timer"); +const { Cortex } = require('api-utils/cortex'); +const windowsAPI = require("./windows"); +const unload = require("api-utils/unload"); + +// Data types definition +const valid = { + number: { is: ["null", "undefined", "number"] }, + string: { is: ["null", "undefined", "string"] }, + id: { + is: ["string"], + ok: function (v) v.length > 0, + msg: ERR_ID, + readonly: true + }, + label: { + is: ["string"], + ok: function (v) v.length > 0, + msg: ERR_LABEL + }, + panel: { + is: ["null", "undefined", "object"], + ok: function(v) !v || v instanceof panels.Panel + }, + width: { + is: ["null", "undefined", "number"], + map: function (v) { + if (null === v || undefined === v) v = 16; + return v; + }, + defaultValue: 16 + }, +}; + +// Widgets attributes definition +let widgetAttributes = { + label: valid.label, + id: valid.id, + tooltip: valid.string, + width: valid.width, + content: valid.string, + panel: valid.panel +}; + +// Import data definitions from loader, but don't compose with it as Model +// functions allow us to recreate easily all Loader code. +let loaderAttributes = require("api-utils/content/loader").validationAttributes; +for (let i in loaderAttributes) + widgetAttributes[i] = loaderAttributes[i]; + +widgetAttributes.contentURL.optional = true; + +// Widgets public events list, that are automatically binded in options object +const WIDGET_EVENTS = [ + "click", + "mouseover", + "mouseout", + "error", + "message", + "attach" +]; + +// `Model` utility functions that help creating these various Widgets objects +let model = { + + // Validate one attribute using api-utils.js:validateOptions function + _validate: function _validate(name, suspect, validation) { + let $1 = {}; + $1[name] = suspect; + let $2 = {}; + $2[name] = validation; + return validateOptions($1, $2)[name]; + }, + + /** + * This method has two purposes: + * 1/ Validate and define, on a given object, a set of attribute + * 2/ Emit a "change" event on this object when an attribute is changed + * + * @params {Object} object + * Object on which we can bind attributes on and watch for their changes. + * This object must have an EventEmitter interface, or, at least `_emit` + * method + * @params {Object} attrs + * Dictionary of attributes definition following api-utils:validateOptions + * scheme + * @params {Object} values + * Dictionary of attributes default values + */ + setAttributes: function setAttributes(object, attrs, values) { + let properties = {}; + for (let name in attrs) { + let value = values[name]; + let req = attrs[name]; + + // Retrieve default value from typedef if the value is not defined + if ((typeof value == "undefined" || value == null) && req.defaultValue) + value = req.defaultValue; + + // Check for valid value if value is defined or mandatory + if (!req.optional || typeof value != "undefined") + value = model._validate(name, value, req); + + // In any case, define this property on `object` + let property = null; + if (req.readonly) { + property = { + value: value, + writable: false, + enumerable: true, + configurable: false + }; + } + else { + property = model._createWritableProperty(name, value); + } + + properties[name] = property; + } + Object.defineProperties(object, properties); + }, + + // Generate ES5 property definition for a given attribute + _createWritableProperty: function _createWritableProperty(name, value) { + return { + get: function () { + return value; + }, + set: function (newValue) { + value = newValue; + // The main goal of all this Model stuff is here: + // We want to forward all changes to some listeners + this._emit("change", name, value); + }, + enumerable: true, + configurable: false + }; + }, + + /** + * Automagically register listeners in options dictionary + * by detecting listener attributes with name starting with `on` + * + * @params {Object} object + * Target object that need to follow EventEmitter interface, or, at least, + * having `on` method. + * @params {Array} events + * List of events name to automatically bind. + * @params {Object} listeners + * Dictionary of event listener functions to register. + */ + setEvents: function setEvents(object, events, listeners) { + for (let i = 0, l = events.length; i < l; i++) { + let name = events[i]; + let onName = "on" + name[0].toUpperCase() + name.substr(1); + if (!listeners[onName]) + continue; + object.on(name, listeners[onName].bind(object)); + } + } + +}; + + +/** + * Main Widget class: entry point of the widget API + * + * Allow to control all widget across all existing windows with a single object. + * Widget.getView allow to retrieve a WidgetView instance to control a widget + * specific to one window. + */ +const WidgetTrait = LightTrait.compose(EventEmitterTrait, LightTrait({ + + _initWidget: function _initWidget(options) { + model.setAttributes(this, widgetAttributes, options); + + browserManager.validate(this); + + // We must have at least content or contentURL defined + if (!(this.content || this.contentURL)) + throw new Error(ERR_CONTENT); + + this._views = []; + + // Set tooltip to label value if we don't have tooltip defined + if (!this.tooltip) + this.tooltip = this.label; + + model.setEvents(this, WIDGET_EVENTS, options); + + this.on('change', this._onChange.bind(this)); + + let self = this; + this._port = EventEmitterTrait.create({ + emit: function () { + let args = arguments; + self._views.forEach(function(v) v.port.emit.apply(v.port, args)); + } + }); + // expose wrapped port, that exposes only public properties. + this._port._public = Cortex(this._port); + + // Register this widget to browser manager in order to create new widget on + // all new windows + browserManager.addItem(this); + }, + + _onChange: function _onChange(name, value) { + // Set tooltip to label value if we don't have tooltip defined + if (name == 'tooltip' && !value) { + // we need to change tooltip again in order to change the value of the + // attribute itself + this.tooltip = this.label; + return; + } + + // Forward attributes changes to WidgetViews + if (['width', 'tooltip', 'content', 'contentURL'].indexOf(name) != -1) { + this._views.forEach(function(v) v[name] = value); + } + }, + + _onEvent: function _onEvent(type, eventData) { + this._emit(type, eventData); + }, + + _createView: function _createView() { + // Create a new WidgetView instance + let view = WidgetView(this); + + // Keep a reference to it + this._views.push(view); + + // Emit an `attach` event with a WidgetView instance without private attrs + this._emit("attach", view._public); + + return view; + }, + + // a WidgetView instance is destroyed + _onViewDestroyed: function _onViewDestroyed(view) { + let idx = this._views.indexOf(view); + this._views.splice(idx, 1); + }, + + /** + * Called on browser window closed, to destroy related WidgetViews + * @params {ChromeWindow} window + * Window that has been closed + */ + _onWindowClosed: function _onWindowClosed(window) { + for each (let view in this._views) { + if (view._isInChromeWindow(window)) { + view.destroy(); + break; + } + } + }, + + /** + * Get the WidgetView instance related to a BrowserWindow instance + * @params {BrowserWindow} window + * BrowserWindow reference from "windows" module + */ + getView: function getView(window) { + for each (let view in this._views) { + if (view._isInWindow(window)) { + return view._public; + } + } + return null; + }, + + get port() this._port._public, + set port(v) {}, // Work around Cortex failure with getter without setter + // See bug 653464 + _port: null, + + postMessage: function postMessage(message) { + this._views.forEach(function(v) v.postMessage(message)); + }, + + destroy: function destroy() { + if (this.panel) + this.panel.destroy(); + + // Dispatch destroy calls to views + // we need to go backward as we remove items from this array in + // _onViewDestroyed + for (let i = this._views.length - 1; i >= 0; i--) + this._views[i].destroy(); + + // Unregister widget to stop creating it over new windows + // and allow creation of new widget with same id + browserManager.removeItem(this); + } + +})); + +// Widget constructor +const Widget = function Widget(options) { + let w = WidgetTrait.create(Widget.prototype); + w._initWidget(options); + + // Return a Cortex of widget in order to hide private attributes like _onEvent + let _public = Cortex(w); + unload.ensure(_public, "destroy"); + return _public; +} +exports.Widget = Widget; + + + +/** + * WidgetView is an instance of a widget for a specific window. + * + * This is an external API that can be retrieved by calling Widget.getView or + * by watching `attach` event on Widget. + */ +const WidgetViewTrait = LightTrait.compose(EventEmitterTrait, LightTrait({ + + // Reference to the matching WidgetChrome + // set right after constructor call + _chrome: null, + + // Public interface of the WidgetView, passed in `attach` event or in + // Widget.getView + _public: null, + + _initWidgetView: function WidgetView__initWidgetView(baseWidget) { + this._baseWidget = baseWidget; + + model.setAttributes(this, widgetAttributes, baseWidget); + + this.on('change', this._onChange.bind(this)); + + let self = this; + this._port = EventEmitterTrait.create({ + emit: function () { + if (!self._chrome) + throw new Error(ERR_DESTROYED); + self._chrome.update(self._baseWidget, "emit", arguments); + } + }); + // expose wrapped port, that exposes only public properties. + this._port._public = Cortex(this._port); + + this._public = Cortex(this); + }, + + _onChange: function WidgetView__onChange(name, value) { + if (name == 'tooltip' && !value) { + this.tooltip = this.label; + return; + } + + // Forward attributes changes to WidgetChrome instance + if (['width', 'tooltip', 'content', 'contentURL'].indexOf(name) != -1) { + this._chrome.update(this._baseWidget, name, value); + } + }, + + _onEvent: function WidgetView__onEvent(type, eventData, domNode) { + // Dispatch event in view + this._emit(type, eventData); + + // And forward it to the main Widget object + if ("click" == type || type.indexOf("mouse") == 0) + this._baseWidget._onEvent(type, this._public); + else + this._baseWidget._onEvent(type, eventData); + + // Special case for click events: if the widget doesn't have a click + // handler, but it does have a panel, display the panel. + if ("click" == type && !this._listeners("click").length && this.panel) + this.panel.show(domNode); + }, + + _isInWindow: function WidgetView__isInWindow(window) { + return windowsAPI.BrowserWindow({ + window: this._chrome.window + }) == window; + }, + + _isInChromeWindow: function WidgetView__isInChromeWindow(window) { + return this._chrome.window == window; + }, + + _onPortEvent: function WidgetView__onPortEvent(args) { + let port = this._port; + port._emit.apply(port, args); + let basePort = this._baseWidget._port; + basePort._emit.apply(basePort, args); + }, + + get port() this._port._public, + set port(v) {}, // Work around Cortex failure with getter without setter + // See bug 653464 + _port: null, + + postMessage: function WidgetView_postMessage(message) { + if (!this._chrome) + throw new Error(ERR_DESTROYED); + this._chrome.update(this._baseWidget, "postMessage", message); + }, + + destroy: function WidgetView_destroy() { + this._chrome.destroy(); + delete this._chrome; + this._baseWidget._onViewDestroyed(this); + this._emit("detach"); + } + +})); + +const WidgetView = function WidgetView(baseWidget) { + let w = WidgetViewTrait.create(WidgetView.prototype); + w._initWidgetView(baseWidget); + return w; +} + + + +/** + * Keeps track of all browser windows. + * Exposes methods for adding/removing widgets + * across all open windows (and future ones). + * Create a new instance of BrowserWindow per window. + */ +let browserManager = { + items: [], + windows: [], + + // Registers the manager to listen for window openings and closings. Note + // that calling this method can cause onTrack to be called immediately if + // there are open windows. + init: function () { + let windowTracker = new (require("api-utils/window-utils").WindowTracker)(this); + unload.ensure(windowTracker); + }, + + // Registers a window with the manager. This is a WindowTracker callback. + onTrack: function browserManager_onTrack(window) { + if (this._isBrowserWindow(window)) { + let win = new BrowserWindow(window); + win.addItems(this.items); + this.windows.push(win); + } + }, + + // Unregisters a window from the manager. It's told to undo all + // modifications. This is a WindowTracker callback. Note that when + // WindowTracker is unloaded, it calls onUntrack for every currently opened + // window. The browserManager therefore doesn't need to specially handle + // unload itself, since unloading the browserManager means untracking all + // currently opened windows. + onUntrack: function browserManager_onUntrack(window) { + if (this._isBrowserWindow(window)) { + this.items.forEach(function(i) i._onWindowClosed(window)); + for (let i = 0; i < this.windows.length; i++) { + if (this.windows[i].window == window) { + this.windows.splice(i, 1)[0]; + return; + } + } + + } + }, + + // Used to validate widget by browserManager before adding it, + // in order to check input very early in widget constructor + validate : function (item) { + let idx = this.items.indexOf(item); + if (idx > -1) + throw new Error("The widget " + item + " has already been added."); + if (item.id) { + let sameId = this.items.filter(function(i) i.id == item.id); + if (sameId.length > 0) + throw new Error("This widget ID is already used: " + item.id); + } else { + item.id = this.items.length; + } + }, + + // Registers an item with the manager. It's added to all currently registered + // windows, and when new windows are registered it will be added to them, too. + addItem: function browserManager_addItem(item) { + this.items.push(item); + this.windows.forEach(function (w) w.addItems([item])); + }, + + // Unregisters an item from the manager. It's removed from all windows that + // are currently registered. + removeItem: function browserManager_removeItem(item) { + let idx = this.items.indexOf(item); + if (idx > -1) + this.items.splice(idx, 1); + }, + + _isBrowserWindow: function browserManager__isBrowserWindow(win) { + let winType = win.document.documentElement.getAttribute("windowtype"); + return winType === "navigator:browser"; + } +}; + + + +/** + * Keeps track of a single browser window. + * + * This is where the core of how a widget's content is added to a window lives. + */ +function BrowserWindow(window) { + this.window = window; + this.doc = window.document; +} + +BrowserWindow.prototype = { + + // Adds an array of items to the window. + addItems: function BW_addItems(items) { + items.forEach(this._addItemToWindow, this); + }, + + _addItemToWindow: function BW__addItemToWindow(baseWidget) { + // Create a WidgetView instance + let widget = baseWidget._createView(); + + // Create a WidgetChrome instance + let item = new WidgetChrome({ + widget: widget, + doc: this.doc, + window: this.window + }); + + widget._chrome = item; + + this._insertNodeInToolbar(item.node); + + // We need to insert Widget DOM Node before finishing widget view creation + // (because fill creates an iframe and tries to access its docShell) + item.fill(); + }, + + _insertNodeInToolbar: function BW__insertNodeInToolbar(node) { + // Add to the customization palette + let toolbox = this.doc.getElementById("navigator-toolbox"); + let palette = toolbox.palette; + palette.appendChild(node); + + // Search for widget toolbar by reading toolbar's currentset attribute + let container = null; + let toolbars = this.doc.getElementsByTagName("toolbar"); + let id = node.getAttribute("id"); + for (let i = 0, l = toolbars.length; i < l; i++) { + let toolbar = toolbars[i]; + if (toolbar.getAttribute("currentset").indexOf(id) == -1) + continue; + container = toolbar; + } + + // if widget isn't in any toolbar, add it to the addon-bar + // TODO: we may want some "first-launch" module to do this only on very + // first execution + if (!container) { + container = this.doc.getElementById("addon-bar"); + // TODO: find a way to make the following code work when we use "cfx run": + // http://mxr.mozilla.org/mozilla-central/source/browser/base/content/browser.js#8586 + // until then, force display of addon bar directly from sdk code + // https://bugzilla.mozilla.org/show_bug.cgi?id=627484 + if (container.collapsed) + this.window.toggleAddonBar(); + } + + // Now retrieve a reference to the next toolbar item + // by reading currentset attribute on the toolbar + let nextNode = null; + let currentSet = container.getAttribute("currentset"); + let ids = (currentSet == "__empty") ? [] : currentSet.split(","); + let idx = ids.indexOf(id); + if (idx != -1) { + for (let i = idx; i < ids.length; i++) { + nextNode = this.doc.getElementById(ids[i]); + if (nextNode) + break; + } + } + + // Finally insert our widget in the right toolbar and in the right position + container.insertItem(id, nextNode, null, false); + + // Update DOM in order to save position if we remove/readd the widget + container.setAttribute("currentset", container.currentSet); + // Save DOM attribute in order to save position on new window opened + this.window.document.persist(container.id, "currentset"); + } +} + + +/** + * Final Widget class that handles chrome DOM Node: + * - create initial DOM nodes + * - receive instruction from WidgetView through update method and update DOM + * - watch for DOM events and forward them to WidgetView + */ +function WidgetChrome(options) { + this.window = options.window; + this._doc = options.doc; + this._widget = options.widget; + this._symbiont = null; // set later + this.node = null; // set later + + this._createNode(); +} + +// Update a property of a widget. +WidgetChrome.prototype.update = function WC_update(updatedItem, property, value) { + switch(property) { + case "contentURL": + case "content": + this.setContent(); + break; + case "width": + this.node.style.minWidth = value + "px"; + this.node.querySelector("iframe").style.width = value + "px"; + break; + case "tooltip": + this.node.setAttribute("tooltiptext", value); + break; + case "postMessage": + this._symbiont.postMessage(value); + break; + case "emit": + let port = this._symbiont.port; + port.emit.apply(port, value); + break; + } +} + +// Add a widget to this window. +WidgetChrome.prototype._createNode = function WC__createNode() { + // XUL element container for widget + let node = this._doc.createElement("toolbaritem"); + let guid = require("api-utils/xpcom").makeUuid().toString(); + + // Temporary work around require("self") failing on unit-test execution ... + let jetpackID = "testID"; + try { + jetpackID = require("self").id; + } catch(e) {} + + // Compute an unique and stable widget id with jetpack id and widget.id + let id = "widget:" + jetpackID + "-" + this._widget.id; + node.setAttribute("id", id); + node.setAttribute("label", this._widget.label); + node.setAttribute("tooltiptext", this._widget.tooltip); + node.setAttribute("align", "center"); + + // TODO move into a stylesheet, configurable by consumers. + // Either widget.style, exposing the style object, or a URL + // (eg, can load local stylesheet file). + node.setAttribute("style", [ + "overflow: hidden; margin: 1px 2px 1px 2px; padding: 0px;", + "min-height: 16px;", + ].join("")); + + node.style.minWidth = this._widget.width + "px"; + + this.node = node; +} + +// Initial population of a widget's content. +WidgetChrome.prototype.fill = function WC_fill() { + // Create element + var iframe = this._doc.createElement("iframe"); + iframe.setAttribute("type", "content"); + iframe.setAttribute("transparent", "transparent"); + iframe.style.overflow = "hidden"; + iframe.style.height = "16px"; + iframe.style.maxHeight = "16px"; + iframe.style.width = this._widget.width + "px"; + iframe.setAttribute("flex", "1"); + iframe.style.border = "none"; + iframe.style.padding = "0px"; + + // Do this early, because things like contentWindow are null + // until the node is attached to a document. + this.node.appendChild(iframe); + + // add event handlers + this.addEventHandlers(); + + // set content + this.setContent(); +} + +// Get widget content type. +WidgetChrome.prototype.getContentType = function WC_getContentType() { + if (this._widget.content) + return CONTENT_TYPE_HTML; + return (this._widget.contentURL && /\.(jpg|gif|png|ico)$/.test(this._widget.contentURL)) + ? CONTENT_TYPE_IMAGE : CONTENT_TYPE_URI; +} + +// Set widget content. +WidgetChrome.prototype.setContent = function WC_setContent() { + let type = this.getContentType(); + let contentURL = null; + + switch (type) { + case CONTENT_TYPE_HTML: + contentURL = "data:text/html," + encodeURIComponent(this._widget.content); + break; + case CONTENT_TYPE_URI: + contentURL = this._widget.contentURL; + break; + case CONTENT_TYPE_IMAGE: + let imageURL = this._widget.contentURL; + contentURL = "data:text/html,"; + break; + default: + throw new Error("The widget's type cannot be determined."); + } + + let iframe = this.node.firstElementChild; + + let self = this; + // Cleanup previously created symbiont (in case we are update content) + if (this._symbiont) + this._symbiont.destroy(); + + this._symbiont = Trait.compose(Symbiont.resolve({ + _onContentScriptEvent: "_onContentScriptEvent-not-used" + }), { + _onContentScriptEvent: function () { + // Redirect events to WidgetView + self._widget._onPortEvent(arguments); + } + })({ + frame: iframe, + contentURL: contentURL, + contentScriptFile: this._widget.contentScriptFile, + contentScript: this._widget.contentScript, + contentScriptWhen: this._widget.contentScriptWhen, + allow: this._widget.allow, + onMessage: function(message) { + timer.setTimeout(function() { + self._widget._onEvent("message", message); + }, 0); + } + }); +} + +// Detect if document consists of a single image. +WidgetChrome._isImageDoc = function WC__isImageDoc(doc) { + return doc.body.childNodes.length == 1 && + doc.body.firstElementChild && + doc.body.firstElementChild.tagName == "IMG"; +} + +// Set up all supported events for a widget. +WidgetChrome.prototype.addEventHandlers = function WC_addEventHandlers() { + let contentType = this.getContentType(); + + let self = this; + let listener = function(e) { + // Ignore event firings that target the iframe. + if (e.target == self.node.firstElementChild) + return; + + // The widget only supports left-click for now, + // so ignore right-clicks. + if (e.type == "click" && e.button == 2) + return; + + // Proxy event to the widget + timer.setTimeout(function() { + self._widget._onEvent(EVENTS[e.type], null, self.node); + }, 0); + }; + + this.eventListeners = {}; + let iframe = this.node.firstElementChild; + for (let [type, method] in Iterator(EVENTS)) { + iframe.addEventListener(type, listener, true, true); + + // Store listeners for later removal + this.eventListeners[type] = listener; + } + + // On document load, make modifications required for nice default + // presentation. + let self = this; + function loadListener(e) { + // Ignore event firings that target the iframe + if (e.target == iframe) + return; + // Ignore about:blank loads + if (e.type == "load" && e.target.location == "about:blank") + return; + + // We may have had an unload event before that cleaned up the symbiont + if (!self._symbiont) + self.setContent(); + + let doc = e.target; + if (contentType == CONTENT_TYPE_IMAGE || WidgetChrome._isImageDoc(doc)) { + // Force image content to size. + // Add-on authors must size their images correctly. + doc.body.firstElementChild.style.width = self._widget.width + "px"; + doc.body.firstElementChild.style.height = "16px"; + } + + // Allow all content to fill the box by default. + doc.body.style.margin = "0"; + } + iframe.addEventListener("load", loadListener, true); + this.eventListeners["load"] = loadListener; + + // Register a listener to unload symbiont if the toolbaritem is moved + // on user toolbars customization + function unloadListener(e) { + if (e.target.location == "about:blank") + return; + self._symbiont.destroy(); + self._symbiont = null; + // This may fail but not always, it depends on how the node is + // moved or removed + try { + self.setContent(); + } catch(e) {} + + } + + iframe.addEventListener("unload", unloadListener, true); + this.eventListeners["unload"] = unloadListener; +} + +// Remove and unregister the widget from everything +WidgetChrome.prototype.destroy = function WC_destroy(removedItems) { + // remove event listeners + for (let [type, listener] in Iterator(this.eventListeners)) + this.node.firstElementChild.removeEventListener(type, listener, true); + // remove dom node + this.node.parentNode.removeChild(this.node); + // cleanup symbiont + this._symbiont.destroy(); + // cleanup itself + this.eventListeners = null; + this._widget = null; + this._symbiont = null; +} + +// Init the browserManager only after setting prototypes and such above, because +// it will cause browserManager.onTrack to be called immediately if there are +// open windows. +browserManager.init(); diff --git a/tools/addon-sdk-1.3/packages/addon-kit/lib/windows.js b/tools/addon-sdk-1.3/packages/addon-kit/lib/windows.js new file mode 100644 index 0000000..e9e097b --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/lib/windows.js @@ -0,0 +1,238 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Felipe Gomes (Original author) + * Irakli Gozalishvili + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +"use strict"; + +if (!require("api-utils/xul-app").is("Firefox")) { + throw new Error([ + "The windows module currently supports only Firefox. In the future", + " we would like it to support other applications, however. Please see ", + "https://bugzilla.mozilla.org/show_bug.cgi?id=571449 for more information." + ].join("")); +} + +const { Cc, Ci } = require('chrome'), + { Trait } = require('api-utils/traits'), + { List } = require('api-utils/list'), + { EventEmitter } = require('api-utils/events'), + { WindowTabs, WindowTabTracker } = require('api-utils/windows/tabs'), + { WindowDom } = require('api-utils/windows/dom'), + { WindowLoader } = require('api-utils/windows/loader'), + { WindowTrackerTrait } = require('api-utils/window-utils'), + { Options } = require('api-utils/tabs/tab'), + { utils } = require('api-utils/xpcom'), + apiUtils = require('api-utils/api-utils'), + unload = require('api-utils/unload'), + + WM = Cc['@mozilla.org/appshell/window-mediator;1']. + getService(Ci.nsIWindowMediator), + + BROWSER = 'navigator:browser'; + +/** + * Window trait composes safe wrappers for browser window that are E10S + * compatible. + */ +const BrowserWindowTrait = Trait.compose( + EventEmitter, + WindowDom.resolve({ close: '_close' }), + WindowTabs, + WindowTabTracker, + WindowLoader, + /* WindowSidebars, */ + Trait.compose({ + _emit: Trait.required, + _close: Trait.required, + _load: Trait.required, + /** + * Constructor returns wrapper of the specified chrome window. + * @param {nsIWindow} window + */ + constructor: function BrowserWindow(options) { + // Register this window ASAP, in order to avoid loop that would try + // to create this window instance over and over (see bug 648244) + windows.push(this); + + // make sure we don't have unhandled errors + this.on('error', console.exception.bind(console)); + + if ('onOpen' in options) + this.on('open', options.onOpen); + if ('onClose' in options) + this.on('close', options.onClose); + if ('window' in options) + this._window = options.window; + if ('tabs' in options) { + this._tabOptions = Array.isArray(options.tabs) ? + options.tabs.map(Options) : + [ Options(options.tabs) ]; + } + else if ('url' in options) { + this._tabOptions = [ Options(options.url) ]; + } + this._load(); + return this; + }, + _tabOptions: [], + _onLoad: function() { + try { + this._initWindowTabTracker(); + } catch(e) { + this._emit('error', e) + } + this._emitOnObject(browserWindows, 'open', this._public); + }, + _onUnload: function() { + this._destroyWindowTabTracker(); + this._emitOnObject(browserWindows, 'close', this._public); + this._window = null; + // Removing reference from the windows array. + windows.splice(windows.indexOf(this), 1); + this._removeAllListeners('close'); + this._removeAllListeners('open'); + this._removeAllListeners('ready'); + }, + close: function close(callback) { + // maybe we should deprecate this with message ? + if (callback) this.on('close', callback); + return this._close(); + } + }) +); +/** + * Wrapper for `BrowserWindowTrait`. Creates new instance if wrapper for + * window doesn't exists yet. If wrapper already exists then returns it + * instead. + * @params {Object} options + * Options that are passed to the the `BrowserWindowTrait` + * @returns {BrowserWindow} + * @see BrowserWindowTrait + */ +function BrowserWindow(options) { + let chromeWindow = options.window; + for each (let window in windows) { + if (chromeWindow == window._window) + return window._public + } + let window = BrowserWindowTrait(options); + return window._public; +} +// to have proper `instanceof` behavior will go away when #596248 is fixed. +BrowserWindow.prototype = BrowserWindowTrait.prototype; +exports.BrowserWindow = BrowserWindow +const windows = []; +/** + * `BrowserWindows` trait is composed out of `List` trait and it represents + * "live" list of currently open browser windows. Instance mutates itself + * whenever new browser window gets opened / closed. + */ +// Very stupid to resolve all `toStrings` but this will be fixed by #596248 +const browserWindows = Trait.resolve({ toString: null }).compose( + List.resolve({ constructor: '_initList' }), + EventEmitter.resolve({ toString: null }), + WindowTrackerTrait.resolve({ constructor: '_initTracker', toString: null }), + Trait.compose({ + _emit: Trait.required, + _add: Trait.required, + _remove: Trait.required, + + // public API + + /** + * Constructor creates instance of `Windows` that represents live list of open + * windows. + */ + constructor: function BrowserWindows() { + this._trackedWindows = []; + this._initList(); + this._initTracker(); + unload.ensure(this, "_destructor"); + }, + _destructor: function _destructor() { + this._removeAllListeners('open'); + this._removeAllListeners('close'); + }, + /** + * This property represents currently active window. + * Property is non-enumerable, in order to preserve array like enumeration. + * @type {Window|null} + */ + get activeWindow() { + let window = WM.getMostRecentWindow(BROWSER); + return this._isBrowser(window) ? BrowserWindow({ window: window }) : null; + }, + open: function open(options) { + if (typeof options === "string") + // `tabs` option is under review and may be removed. + options = { tabs: [Options(options)] }; + return BrowserWindow(options); + }, + /** + * Returns true if specified window is a browser window. + * @param {nsIWindow} window + * @returns {Boolean} + */ + _isBrowser: function _isBrowser(window) + BROWSER === window.document.documentElement.getAttribute("windowtype") + , + /** + * Internal listener which is called whenever new window gets open. + * Creates wrapper and adds to this list. + * @param {nsIWindow} chromeWindow + */ + _onTrack: function _onTrack(chromeWindow) { + if (!this._isBrowser(chromeWindow)) return; + let window = BrowserWindow({ window: chromeWindow }); + this._add(window); + this._emit('open', window); + }, + /** + * Internal listener which is called whenever window gets closed. + * Cleans up references and removes wrapper from this list. + * @param {nsIWindow} window + */ + _onUntrack: function _onUntrack(chromeWindow) { + if (!this._isBrowser(chromeWindow)) return; + let window = BrowserWindow({ window: chromeWindow }); + // `_onUnload` method of the `BrowserWindow` will remove `chromeWindow` + // from the `windows` array. + this._remove(window); + this._emit('close', window); + } + }).resolve({ toString: null }) +)(); +exports.browserWindows = browserWindows; + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/package.json b/tools/addon-sdk-1.3/packages/addon-kit/package.json new file mode 100644 index 0000000..6507ecb --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/package.json @@ -0,0 +1,13 @@ +{ + "name": "addon-kit", + "description": "Add-on development made easy.", + "keywords": ["javascript", "engine", "platform", "xulrunner"], + "author": "Atul Varma (http://toolness.com/) ", + "contributors": [ + "Myk Melez (http://melez.com/) ", + "Daniel Aquino " + ], + "version": "1.3", + "license": "MPL 1.1/GPL 2.0/LGPL 2.1", + "dependencies": ["api-utils"] +} diff --git a/tools/addon-sdk-1.3/packages/addon-kit/tests/pagemod-test-helpers.js b/tools/addon-sdk-1.3/packages/addon-kit/tests/pagemod-test-helpers.js new file mode 100644 index 0000000..01799ec --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/tests/pagemod-test-helpers.js @@ -0,0 +1,62 @@ +"use strict"; + +const {Cc,Ci} = require("chrome"); +const timer = require("timer"); + +/** + * 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(test, testURL, pageModOptions, + testCallback, timeout) { + var xulApp = require("xul-app"); + if (!xulApp.versionInRange(xulApp.platformVersion, "1.9.3a3", "*") && + !xulApp.versionInRange(xulApp.platformVersion, "1.9.2.7", "1.9.2.*")) { + test.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) { + test.pass("page-mod tests: could not find the browser window, so " + + "will not run. Use -a firefox to run the pagemod tests.") + return null; + } + + if (timeout !== undefined) + test.waitUntilDone(timeout); + else + test.waitUntilDone(); + + let loader = test.makeSandboxedLoader(); + let pageMod = loader.require("page-mod"); + + var pageMods = [new pageMod.PageMod(opts) for each(opts in pageModOptions)]; + + var tabBrowser = browserWindow.gBrowser; + var newTab = tabBrowser.addTab(testURL); + tabBrowser.selectedTab = newTab; + var b = tabBrowser.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 done() { + pageMods.forEach(function(mod) mod.destroy()); + // XXX leaks reported if we don't close the tab? + tabBrowser.removeTab(newTab); + loader.unload(); + test.done(); + }); + } + b.addEventListener("load", onPageLoad, true); + + return pageMods; +} diff --git a/tools/addon-sdk-1.3/packages/addon-kit/tests/test-clipboard.js b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-clipboard.js new file mode 100644 index 0000000..5f61d48 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-clipboard.js @@ -0,0 +1,60 @@ + +// Test the typical use case, setting & getting with no flavors specified +exports.testWithNoFlavor = function(test) { + var contents = "hello there"; + var flavor = "text"; + var fullFlavor = "text/unicode"; + var clip = require("clipboard"); + // Confirm we set the clipboard + test.assert(clip.set(contents)); + // Confirm flavor is set + test.assertEqual(clip.currentFlavors[0], flavor); + // Confirm we set the clipboard + test.assertEqual(clip.get(), contents); + // Confirm we can get the clipboard using the flavor + test.assertEqual(clip.get(flavor), contents); + // Confirm we can still get the clipboard using the full flavor + test.assertEqual(clip.get(fullFlavor), contents); +}; + +// Test the slightly less common case where we specify the flavor +exports.testWithFlavor = function(test) { + 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("clipboard"); + test.assert(clip.set(contents, flavor)); + test.assertEqual(clip.currentFlavors[0], unicodeFlavor); + test.assertEqual(clip.currentFlavors[1], flavor); + test.assertEqual(clip.get(), contentsText); + test.assertEqual(clip.get(flavor), contents); + test.assertEqual(clip.get(fullFlavor), contents); + test.assertEqual(clip.get(unicodeFlavor), contentsText); + test.assertEqual(clip.get(unicodeFullFlavor), contentsText); +}; + +// Test that the typical case still works when we specify the flavor to set +exports.testWithRedundantFlavor = function(test) { + var contents = "hello there"; + var flavor = "text"; + var fullFlavor = "text/unicode"; + var clip = require("clipboard"); + test.assert(clip.set(contents, flavor)); + test.assertEqual(clip.currentFlavors[0], flavor); + test.assertEqual(clip.get(), contents); + test.assertEqual(clip.get(flavor), contents); + test.assertEqual(clip.get(fullFlavor), contents); +}; + +exports.testNotInFlavor = function(test) { + var contents = "hello there"; + var flavor = "html"; + var clip = require("clipboard"); + test.assert(clip.set(contents)); + // If there's nothing on the clipboard with this flavor, should return null + test.assertEqual(clip.get(flavor), null); +}; +// TODO: Test error cases. diff --git a/tools/addon-sdk-1.3/packages/addon-kit/tests/test-context-menu.html b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-context-menu.html new file mode 100644 index 0000000..a0fb5cb --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-context-menu.html @@ -0,0 +1,78 @@ + + + + + Context menu test + + +

+ +

+ +

+ + A simple link. + +

+ +

+ + + A span inside a link. + + +

+ +

+ Some text. +

+ +

+ +

+ +

+ +

+ + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/tests/test-context-menu.js b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-context-menu.js new file mode 100644 index 0000000..e0f7c2f --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-context-menu.js @@ -0,0 +1,2074 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Drew Willcoxon (Original Author) + * Matteo Ferretti + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +let {Cc,Ci} = require("chrome"); + +// These should match the same constants in the module. +const ITEM_CLASS = "jetpack-context-menu-item"; +const SEPARATOR_ID = "jetpack-context-menu-separator"; +const OVERFLOW_THRESH_DEFAULT = 10; +const OVERFLOW_THRESH_PREF = + "extensions.addon-sdk.context-menu.overflowThreshold"; +const OVERFLOW_MENU_ID = "jetpack-content-menu-overflow-menu"; +const OVERFLOW_POPUP_ID = "jetpack-content-menu-overflow-popup"; + +const TEST_DOC_URL = __url__.replace(/\.js$/, ".html"); + + +// Destroying items that were previously created should cause them to be absent +// from the menu. +exports.testConstructDestroy = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + // Create an item. + let item = new loader.cm.Item({ label: "item" }); + test.assertEqual(item.parentMenu, null, "item's parent menu should be null"); + + 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]); + test.done(); + }); + }); +}; + + +// Destroying an item twice should not cause an error. +exports.testDestroyTwice = function (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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], []); + 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 (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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, []); + test.done(); + }); + }); +}; + + +// Selection contexts should cause items to appear when a selection exists. +exports.testSelectionContextMatch = function (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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], []); + test.done(); + }); + }); +}; + + +// Selection contexts should not cause items to appear when a selection does +// not exist. +exports.testSelectionContextNoMatch = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let item = loader.cm.Item({ + label: "item", + context: loader.cm.SelectionContext() + }); + + test.showMenu(null, function (popup) { + test.checkMenu([], [item], []); + test.done(); + }); +}; + + +// URL contexts should cause items to appear on pages that match. +exports.testURLContextMatch = function (test) { + test = new TestHelper(test); + 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"]) + }) + ]; + + 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 (test) { + test = new TestHelper(test); + 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"]) + }) + ]; + + test.withTestDoc(function (window, doc) { + test.showMenu(null, function (popup) { + test.checkMenu([], 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. +exports.testURLContextRemove = function (test) { + test = new TestHelper(test); + 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");', + onMessage: function (msg) { + test.assert(shouldBeEvaled, + "content script should be evaluated when expected"); + shouldBeEvaled = false; + test.done(); + } + }); + + test.withTestDoc(function (window, doc) { + shouldBeEvaled = true; + item.context.remove(context); + }); +}; + + +// Adding a non-matching URL context after its item is created and the page is +// loaded should cause the item's worker to be destroyed. +exports.testURLContextAdd = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let item = loader.cm.Item({ label: "item" }); + + test.withTestDoc(function (window, doc) { + let privatePropsKey = loader.globalScope.PRIVATE_PROPS_KEY; + let workerReg = item.valueOf(privatePropsKey)._workerReg; + + let found = false; + for each (let winWorker in workerReg.winWorkers) { + if (winWorker.win === window) { + found = true; + break; + } + } + this.test.assert(found, "window should be present in worker registry"); + + item.context.add(loader.cm.URLContext("*.bogus.com")); + + for each (let winWorker in workerReg.winWorkers) + this.test.assertNotEqual(winWorker.win, window, + "window should not be present in worker registry"); + + test.done(); + }); +}; + + +// Content contexts that return true should cause their items to be present +// in the menu. +exports.testContentContextMatch = function (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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], []); + 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 (test) { + test = new TestHelper(test); + 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], [], []); + test.assertEqual(item.label, "second label", + "item's label should be updated"); + test.done(); + }); +}; + + +// The args passed to context listeners should be correct. +exports.testContentContextArgs = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "item", + contentScript: 'self.on("context", function (node) {' + + ' let Ci = Components.interfaces;' + + ' self.postMessage(node instanceof Ci.nsIDOMHTMLElement);' + + ' return false;' + + '});', + onMessage: function (isElt) { + test.assert(isElt, "node should be an HTML element"); + test.done(); + } + }); + + test.showMenu(null, function () {}); +}; + + +// Multiple contexts imply intersection, not union, and content context +// listeners should not be called if all declarative contexts are not current. +exports.testMultipleContexts = function (test) { + test = new TestHelper(test); + 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], []); + test.done(); + }); + }); +}; + + +// Once a context is removed, it should no longer cause its item to appear. +exports.testRemoveContext = function (test) { + test = new TestHelper(test); + 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], []); + test.done(); + }); + }); + }); +}; + + +// Lots of items should overflow into the overflow submenu. +exports.testOverflow = function (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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]); + test.done(); + }); + }); +}; + + +// Using multiple module instances to add items without causing overflow should +// work OK. Assumes OVERFLOW_THRESH_DEFAULT <= 2. +exports.testMultipleModulesAdd = function (test) { + test = new TestHelper(test); + 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([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]); + test.done(); + }); + }); + }); +}; + + +// Using multiple module instances to add items causing overflow should work OK. +exports.testMultipleModulesAddOverflow = function (test) { + test = new TestHelper(test); + 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([item1], [], 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); + 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 (test) { + test = new TestHelper(test); + 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([item1], [item0], []); + popup.hidePopup(); + + // Unload module 0. + loader0.unload(); + test.showMenu(null, function (popup) { + + // item0 should be removed from the menu. + test.checkMenu([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]); + 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 (test) { + test = new TestHelper(test); + 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([item1], [item0], []); + popup.hidePopup(); + + // Unload module 0. + loader0.unload(); + test.showMenu(null, function (popup) { + + // item0 should be removed from the menu. + test.checkMenu([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]); + 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 (test) { + test = new TestHelper(test); + 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([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]); + popup.hidePopup(); + + // Unload module 0. + loader0.unload(); + test.showMenu(null, function (popup) { + + // Both items should be removed from the menu. + test.checkMenu([], [], [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 (test) { + test = new TestHelper(test); + 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([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]); + popup.hidePopup(); + + // Unload module 0. + loader0.unload(); + test.showMenu(null, function (popup) { + + // Both items should be removed from the menu. + test.checkMenu([], [], [item0, item1]); + test.done(); + }); + }); + }); +}; + + +// Test interactions between a loaded module, unloading another module, and the +// menu separator and overflow submenu. +exports.testMultipleModulesAddRemove = function (test) { + test = new TestHelper(test); + 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]); + 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]); + test.done(); + }); + }); + }); +}; + + +// An item's click listener should work. +exports.testItemClick = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "item", + data: "item data", + contentScript: 'self.on("click", function (node, data) {' + + ' let Ci = Components.interfaces;' + + ' self.postMessage({' + + ' isElt: node instanceof Ci.nsIDOMHTMLElement,' + + ' data: data' + + ' });' + + '});', + onMessage: function (data) { + test.assertEqual(this, item, "`this` inside onMessage should be item"); + test.assert(data.isElt, "node should be an HTML element"); + test.assertEqual(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 (test) { + // 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(test); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "submenu item", + data: "submenu item data" + }); + + let submenu = new loader.cm.Menu({ + label: "submenu", + items: [item] + }); + + let topMenu = new loader.cm.Menu({ + label: "top menu", + contentScript: 'self.on("click", function (node, data) {' + + ' let Ci = Components.interfaces;' + + ' self.postMessage({' + + ' isAnchor: node instanceof Ci.nsIDOMHTMLAnchorElement,' + + ' data: data' + + ' });' + + '});', + onMessage: function (data) { + test.assertEqual(this, topMenu, "`this` inside top menu should be menu"); + test.assert(data.isAnchor, "Clicked node should be anchor"); + test.assertEqual(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 (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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(); + }); +}; + + +// Existing context menu modifications should apply to new windows. +exports.testNewWindow = function (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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([], [], []); + test.done(); + }); + }); + }); +}; + + +// Items in the context menu should be sorted according to locale. +exports.testSorting = function (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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); + test.done(); + }); + }); +}; + + +// The binary search of insertionPoint should work OK. +exports.testInsertionPoint = function (test) { + function mockElts(labels) { + return labels.map(function (label) { + return { label: label, getAttribute: function (l) label }; + }); + } + + test = new TestHelper(test); + let loader = test.newLoader(); + let insertionPoint = loader.globalScope.insertionPoint; + + let ip = insertionPoint("a", []); + test.assertStrictEqual(ip, null, "Insertion point should be null"); + + ip = insertionPoint("a", mockElts(["b"])); + test.assertEqual(ip.label, "b", "Insertion point should be 'b'"); + + ip = insertionPoint("c", mockElts(["b"])); + test.assertStrictEqual(ip, null, "Insertion point should be null"); + + ip = insertionPoint("b", mockElts(["a", "c"])); + test.assertEqual(ip.label, "c", "Insertion point should be 'c'"); + + ip = insertionPoint("c", mockElts(["a", "b", "d"])); + test.assertEqual(ip.label, "d", "Insertion point should be 'd'"); + + ip = insertionPoint("a", mockElts(["b", "c", "d"])); + test.assertEqual(ip.label, "b", "Insertion point should be 'b'"); + + ip = insertionPoint("d", mockElts(["a", "b", "c"])); + test.assertStrictEqual(ip, null, "Insertion point should be null"); + + 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 (test) { + test = new TestHelper(test); + 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) { + test.assertEqual(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 (test) { + test = new TestHelper(test); + 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(); + }); + }); +}; + + +// Setting an item's label before the menu is ever shown should correctly change +// its label and, if necessary, its order within the menu. +exports.testSetLabelBeforeShow = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let items = [ + new loader.cm.Item({ label: "a" }), + new loader.cm.Item({ label: "b" }) + ] + items[0].label = "z"; + test.assertEqual(items[0].label, "z"); + + test.showMenu(null, function (popup) { + test.checkMenu([items[1], items[0]], [], []); + test.done(); + }); +}; + + +// Setting an item's label after the menu is shown should correctly change its +// label and, if necessary, its order within the menu. +exports.testSetLabelAfterShow = function (test) { + test = new TestHelper(test); + 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"; + test.assertEqual(items[0].label, "z"); + test.showMenu(null, function (popup) { + test.checkMenu([items[1], items[0]], [], []); + test.done(); + }); + }); +}; + + +// Setting an item's label before the menu is ever shown should correctly change +// its label and, if necessary, its order within the menu if the item is in the +// overflow submenu. +exports.testSetLabelBeforeShowOverflow = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let prefs = loader.loader.require("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"; + test.assertEqual(items[0].label, "z"); + + test.showMenu(null, function (popup) { + test.checkMenu([items[1], items[0]], [], []); + prefs.set(OVERFLOW_THRESH_PREF, OVERFLOW_THRESH_DEFAULT); + test.done(); + }); +}; + + +// Setting an item's label after the menu is shown should correctly change its +// label and, if necessary, its order within the menu if the item is in the +// overflow submenu. +exports.testSetLabelAfterShowOverflow = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let prefs = loader.loader.require("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"; + test.assertEqual(items[0].label, "z"); + test.showMenu(null, function (popup) { + test.checkMenu([items[1], items[0]], [], []); + prefs.set(OVERFLOW_THRESH_PREF, OVERFLOW_THRESH_DEFAULT); + test.done(); + }); + }); +}; + + +// Setting the label of an item in a Menu should work. +exports.testSetLabelMenuItem = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let menu = loader.cm.Menu({ + label: "menu", + items: [loader.cm.Item({ label: "a" })] + }); + menu.items[0].label = "z"; + + test.assertEqual(menu.items[0].label, "z"); + + test.showMenu(null, function (popup) { + test.checkMenu([menu], [], []); + test.done(); + }); +}; + + +// Menu.addItem() should work. +exports.testMenuAddItem = function (test) { + test = new TestHelper(test); + 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" })); + + test.assertEqual(menu.items.length, 3, + "menu should have correct number of items"); + for (let i = 0; i < 3; i++) { + test.assertEqual(menu.items[i].label, "item " + i, + "item label should be correct"); + test.assertEqual(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 (test) { + test = new TestHelper(test); + 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); + + test.assertEqual(menu.items.length, 2, + "menu should have correct number of items"); + for (let i = 0; i < 2; i++) { + test.assertEqual(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 (test) { + test = new TestHelper(test); + 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); + + test.assertEqual(subitem.parentMenu, null, + "item's parent menu should be correct"); + + test.assertEqual(menu.items.length, 2, + "menu should have correct number of items"); + test.assertEqual(menu.items[0].label, "item 0", + "item label should be correct"); + test.assertEqual(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 (test) { + test = new TestHelper(test); + 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); + + test.assertEqual(menu0.items.length, 0, + "menu should have correct number of items"); + + test.assertEqual(menu1.items.length, 1, + "menu should have correct number of items"); + test.assertEqual(menu1.items[0].label, "item", + "item label should be correct"); + + test.assertEqual(subitem.parentMenu, menu1, + "item's parent menu should be correct"); + + test.showMenu(null, function (popup) { + test.checkMenu([menu0, menu1], [], []); + test.done(); + }); +}; + + +// Destroying an item should remove it from its parent menu. +exports.testMenuItemDestroy = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let subitem = loader.cm.Item({ label: "item" }); + let menu = loader.cm.Menu({ + label: "menu", + items: [subitem] + }); + subitem.destroy(); + + test.assertEqual(menu.items.length, 0, + "menu should have correct number of items"); + test.assertEqual(subitem.parentMenu, null, + "item's parent menu should be correct"); + + test.showMenu(null, function (popup) { + test.checkMenu([menu], [], []); + test.done(); + }); +}; + + +// Setting Menu.items should work. +exports.testMenuItemsSetter = function (test) { + test = new TestHelper(test); + 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" }) + ]; + + test.assertEqual(menu.items.length, 3, + "menu should have correct number of items"); + for (let i = 0; i < 3; i++) { + test.assertEqual(menu.items[i].label, "new item " + i, + "item label should be correct"); + test.assertEqual(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 (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let item = loader.cm.Item({ label: "old item 0", data: "old" }); + item.data = "new"; + + test.assertEqual(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 (test) { + test = new TestHelper(test); + 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(); + }); + }); +}; + + +// Test image support. +exports.testItemImage = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let imageURL = require("self").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: [] }); + + test.showMenu(null, function (popup) { + test.checkMenu([item, menu], [], []); + + let imageURL2 = require("self").data.url("dummy.ico"); + item.image = imageURL2; + menu.image = imageURL2; + test.checkMenu([item, menu], [], []); + + item.image = null; + menu.image = null; + test.checkMenu([item, menu], [], []); + + test.done(); + }); +}; + + +// Menu.destroy should destroy the item tree rooted at that menu. +exports.testMenuDestroy = function (test) { + test = new TestHelper(test); + 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++; + }); + test.assertEqual(numRegistryEntries, 0, "All items should be unregistered."); + + test.showMenu(null, function (popup) { + test.checkMenu([], [], [menu]); + test.done(); + }); +}; + + +// NO TESTS BELOW THIS LINE! /////////////////////////////////////////////////// + +// Run only a dummy test if context-menu doesn't support the host app. +if (!require("xul-app").is("Firefox")) { + for (let [prop, val] in Iterator(exports)) + if (/^test/.test(prop) && typeof(val) === "function") + delete exports[prop]; + exports.testAppNotSupported = function (test) { + test.pass("context-menu does not support this application."); + }; +} + + +// 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(test) { + // default waitUntilDone timeout is 10s, which is too short on the win7 + // buildslave + test.waitUntilDone(30*1000); + this.test = test; + this.loaders = []; + this.browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"]. + getService(Ci.nsIWindowMediator). + getMostRecentWindow("navigator:browser"); +} + +TestHelper.prototype = { + get contextMenuPopup() { + return this.browserWindow.document.getElementById("contentAreaContextMenu"); + }, + + get contextMenuSeparator() { + return this.browserWindow.document.getElementById(SEPARATOR_ID); + }, + + get overflowPopup() { + return this.browserWindow.document.getElementById(OVERFLOW_POPUP_ID); + }, + + get overflowSubmenu() { + return this.browserWindow.document.getElementById(OVERFLOW_MENU_ID); + }, + + get tabBrowser() { + return this.browserWindow.gBrowser; + }, + + // Methods on the wrapped test can be called on this object. + __noSuchMethod__: function (methodName, args) { + this.test[methodName].apply(this.test, args); + }, + + // Asserts that absentItems -- an array of items that should not match the + // current context -- aren't present in the menu. + checkAbsentItems: function (presentItems, absentItems) { + for (let i = 0; i < absentItems.length; i++) { + let item = absentItems[i]; + let elt = this.getItemElt(this.contextMenuPopup, item); + + // The implementation actually hides items rather than removing or not + // adding them in the first place, but that's an implementation detail. + this.test.assert(!elt || elt.hidden, + "Item should not be present in top-level menu"); + + if (this.shouldOverflow(presentItems)) { + elt = getItemElt(this.overflowPopup, item); + this.test.assert(!elt || elt.hidden, + "Item should not be present in overflow submenu"); + } + } + }, + + // Asserts that elt, a DOM element representing item, looks OK. + checkItemElt: function (elt, item) { + let itemType = this.getItemType(item); + + switch (itemType) { + case "Item": + this.test.assertEqual(elt.localName, "menuitem", + "Item DOM element should be a xul:menuitem"); + if (typeof(item.data) === "string") { + this.test.assertEqual(elt.getAttribute("value"), item.data, + "Item should have correct data"); + } + break + case "Menu": + this.test.assertEqual(elt.localName, "menu", + "Menu DOM element should be a xul:menu"); + let subPopup = elt.firstChild; + this.test.assert(subPopup, "xul:menu should have a child"); + this.test.assertEqual(subPopup.localName, "menupopup", + "xul:menu's first child should be a menupopup"); + break; + case "Separator": + this.test.assertEqual(elt.localName, "menuseparator", + "Separator DOM element should be a xul:menuseparator"); + break; + } + + if (itemType === "Item" || itemType === "Menu") { + this.test.assertEqual(elt.getAttribute("label"), item.label, + "Item should have correct title"); + if (typeof(item.image) === "string") + this.test.assertEqual(elt.getAttribute("image"), item.image, + "Item should have correct image"); + else + this.test.assert(!elt.hasAttribute("image"), + "Item should not have image"); + } + }, + + // Asserts that the context menu looks OK given the arguments. presentItems + // are items that should match the current context. absentItems are items + // that shouldn't. removedItems are items that have been removed from the + // menu. + checkMenu: function (presentItems, absentItems, removedItems) { + this.checkSeparator(presentItems); + this.checkOverflow(presentItems); + this.checkPresentItems(presentItems); + this.checkAbsentItems(presentItems, absentItems); + this.checkRemovedItems(removedItems); + this.checkSort(presentItems); + }, + + // Asserts that the overflow submenu is present or absent as appropriate for + // presentItems. + checkOverflow: function (presentItems) { + let submenu = this.overflowSubmenu; + if (this.shouldOverflow(presentItems)) { + this.test.assert(submenu && !submenu.hidden, + "Overflow submenu should be present"); + this.test.assert(submenu.localName, "menu", + "Overflow submenu should be a "); + let overflowPopup = this.overflowPopup; + this.test.assert(overflowPopup, + "Overflow submenu popup should be present"); + this.test.assert(overflowPopup.localName, "menupopup", + "Overflow submenu popup should be a "); + } + else { + this.test.assert(!submenu || submenu.hidden, + "Overflow submenu should be absent"); + } + }, + + // Asserts that the items that are present in the menu because they match the + // current context look OK. + checkPresentItems: function (presentItems) { + function recurse(popup, items, isTopLevel) { + items.forEach(function (item) { + let elt = this.getItemElt(popup, item); + + if (isTopLevel) { + if (this.shouldOverflow(items)) { + this.test.assert(!elt || elt.hidden, + "Item should not be present in top-level menu"); + + let overflowPopup = this.overflowPopup; + this.test.assert(overflowPopup, + "Overflow submenu should be present"); + + elt = this.getItemElt(overflowPopup, item); + this.test.assert(elt && !elt.hidden, + "Item should be present in overflow submenu"); + } + else { + this.test.assert(elt && !elt.hidden, + "Item should be present in top-level menu"); + } + } + else { + this.test.assert(elt && !elt.hidden, + "Item should be present in menu"); + } + + this.checkItemElt(elt, item); + if (this.getItemType(item) === "Menu") + recurse.call(this, elt.firstChild, item.items, false); + }, this); + } + + recurse.call(this, this.contextMenuPopup, presentItems, true); + }, + + // Asserts that items that have been removed from the menu are really removed. + checkRemovedItems: function (removedItems) { + for (let i = 0; i < removedItems.length; i++) { + let item = removedItems[i]; + + let elt = this.getItemElt(this.contextMenuPopup, item); + this.test.assert(!elt, "Item should be removed from top-level menu"); + + let overflowPopup = this.overflowPopup; + if (overflowPopup) { + elt = this.getItemElt(overflowPopup, item); + this.test.assert(!elt, "Item should be removed from overflow submenu"); + } + } + }, + + // Asserts that the menu separator separating standard items from our items + // looks OK. + checkSeparator: function (presentItems) { + let sep = this.contextMenuSeparator; + if (presentItems.length) { + this.test.assert(sep && !sep.hidden, "Menu separator should be present"); + this.test.assertEqual(sep.localName, "menuseparator", + "Menu separator should be a "); + } + else { + this.test.assert(!sep || sep.hidden, "Menu separator should be absent"); + } + }, + + // Asserts that our items are sorted. + checkSort: function (presentItems) { + // Get the first item in sorted order, get its elt, walk the nextSibling + // chain, making sure each is greater than the previous. + if (presentItems.length) { + let sorted = presentItems.slice(0). + sort(function (a, b) a.label.localeCompare(b.label)); + let elt = this.shouldOverflow(presentItems) ? + this.getItemElt(this.overflowPopup, sorted[0]) : + this.getItemElt(this.contextMenuPopup, sorted[0]); + let numElts = 1; + while (elt.nextSibling && + elt.nextSibling.className.split(/\s+/).indexOf(ITEM_CLASS) >= 0) { + let eltLabel = elt.getAttribute("label"); + let nextLabel = elt.nextSibling.getAttribute("label"); + this.test.assert(eltLabel.localeCompare(nextLabel) < 0, + "Item label should be < next item's label"); + elt = elt.nextSibling; + numElts++; + } + this.test.assertEqual(numElts, presentItems.length, + "The first item in sorted order should have the " + + "first element in sorted order"); + } + }, + + // 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. + delayedEventListener: function (node, event, callback, useCapture) { + const self = this; + node.addEventListener(event, function handler(evt) { + node.removeEventListener(event, handler, useCapture); + require("timer").setTimeout(function () { + try { + callback.call(self, evt); + } + catch (err) { + self.test.exception(err); + self.test.done(); + } + }, 20); + }, useCapture); + }, + + // Call to finish the test. + done: function () { + function commonDone() { + if (this.tab) { + this.tabBrowser.removeTab(this.tab); + this.tabBrowser.selectedTab = this.oldSelectedTab; + } + while (this.loaders.length) { + let browserManager = this.loaders[0].globalScope.browserManager; + let topLevelItems = browserManager.topLevelItems.slice(); + let privatePropsKey = this.loaders[0].globalScope.PRIVATE_PROPS_KEY; + let workerRegs = topLevelItems.map(function (item) { + return item.valueOf(privatePropsKey)._workerReg; + }); + + this.loaders[0].unload(); + + // Make sure the browser manager is cleaned up. + this.test.assertEqual(browserManager.browserWins.length, 0, + "browserManager should have no windows left"); + this.test.assertEqual(browserManager.topLevelItems.length, 0, + "browserManager should have no items left"); + this.test.assert(!("contentWins" in browserManager), + "browserManager should have no content windows left"); + + // Make sure the items' worker registries are cleaned up. + topLevelItems.forEach(function (item) { + this.test.assert(!("_workerReg" in item.valueOf(privatePropsKey)), + "item's worker registry should be removed"); + }, this); + workerRegs.forEach(function (workerReg) { + this.test.assertEqual(Object.keys(workerReg.winWorkers).length, 0, + "worker registry should be empty"); + this.test.assertEqual( + Object.keys(workerReg.winsWithoutWorkers).length, 0, + "worker registry list of windows without workers should be empty"); + }, this); + } + this.test.done(); + } + + 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(); + } + }, + + // 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 = this.test.makeSandboxedLoader({ + globals: { packaging: packaging } + }); + let wrapper = { + loader: loader, + cm: loader.require("context-menu"), + globalScope: loader.findSandboxForModule("context-menu").globalScope, + 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 number of presentItems crosses the overflow threshold. + shouldOverflow: function (presentItems) { + return presentItems.length > + (this.loaders.length ? + this.loaders[0].loader.require("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 = 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); + }, + + // 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 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); + } +}; diff --git a/tools/addon-sdk-1.3/packages/addon-kit/tests/test-hotkeys.js b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-hotkeys.js new file mode 100644 index 0000000..b4c6b37 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-hotkeys.js @@ -0,0 +1,155 @@ +"use strict"; + +const { Hotkey } = require("hotkeys"); +const { keyDown } = require("dom/events/keys"); + +exports["test hotkey: function key"] = function(assert, done) { + var element = require("window-utils").activeBrowserWindow.document.documentElement; + var showHotKey = Hotkey({ + combo: "f1", + onPress: function() { + assert.pass("first callback is called"); + 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 = require("window-utils").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 = require("window-utils").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 = require("window-utils").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 = require("window-utils").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 = assert._log.makeSandboxedLoader(); + + var called = false; + var element = loader.require("window-utils").activeBrowserWindow.document.documentElement; + var hotkey = loader.require("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"); + + require("timer").setTimeout(function () { + assert.ok(!called, "Hotkey is destroyed and not called."); + done(); + }, 0); +}; + +require("test").run(exports); diff --git a/tools/addon-sdk-1.3/packages/addon-kit/tests/test-module.js b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-module.js new file mode 100644 index 0000000..c62f923 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-module.js @@ -0,0 +1,33 @@ +"use strict"; + +/** Disabled because of Bug 672199 +exports["test module exports are frozen"] = function(assert) { + assert.ok(Object.isFrozen(require("addon-kit/hotkeys")), + "module exports are frozen"); +}; + +exports["test redefine exported property"] = function(assert) { + let hotkeys = require("addon-kit/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("addon-kit/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("addon-kit/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/tools/addon-sdk-1.3/packages/addon-kit/tests/test-notifications.js b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-notifications.js new file mode 100644 index 0000000..9d20822 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-notifications.js @@ -0,0 +1,76 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim:set ts=2 sw=2 sts=2 et filetype=javascript + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Drew Willcoxon (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +exports.testOnClick = function (test) { + let [loader, mockAlertServ] = makeLoader(test); + let notifs = loader.require("notifications"); + let data = "test data"; + let opts = { + onClick: function (clickedData) { + test.assertEqual(this, notifs, "|this| should be notifications module"); + test.assertEqual(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(); +}; + +// Returns [loader, mockAlertService]. +function makeLoader(test) { + let loader = test.makeSandboxedLoader(); + 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); + } + }; + let scope = loader.findSandboxForModule("notifications").globalScope; + scope.notify = mockAlertServ.showAlertNotification.bind(mockAlertServ); + return [loader, mockAlertServ]; +}; diff --git a/tools/addon-sdk-1.3/packages/addon-kit/tests/test-page-mod.js b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-page-mod.js new file mode 100644 index 0000000..68f0a95 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-page-mod.js @@ -0,0 +1,380 @@ +"use strict"; + +var pageMod = require("page-mod"); +var testPageMod = require("pagemod-test-helpers").testPageMod; + +/* XXX This can be used to delay closing the test Firefox instance for interactive + * testing or visual inspection. This test is registered first so that it runs + * the last. */ +exports.delay = function(test) { + if (false) { + test.waitUntilDone(60000); + require("timer").setTimeout(function() {test.done();}, 4000); + } else + test.pass(); +} + +/* Tests for the PageMod APIs */ + +exports.testPageMod1 = function(test) { + let mods = testPageMod(test, "about:", [{ + include: /about:/, + contentScriptWhen: 'end', + contentScript: 'new ' + function WorkerScope() { + window.document.body.setAttribute("JEP-107", "worked"); + }, + onAttach: function() { + test.assertEqual(this, mods[0], "The 'this' object is the page mod."); + } + }], + function(win, done) { + test.assertEqual( + win.document.body.getAttribute("JEP-107"), + "worked", + "PageMod.onReady test" + ); + done(); + } + ); +}; + +exports.testPageMod2 = function(test) { + testPageMod(test, "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) { + test.assertEqual(win.document.documentElement.getAttribute("first"), + "true", + "PageMod test #2: first script has run"); + test.assertEqual(win.document.documentElement.getAttribute("second"), + "true", + "PageMod test #2: second script has run"); + test.assertEqual("AUQLUE" in win, false, + "PageMod test #2: scripts get a wrapped window"); + done(); + }); +}; + +exports.testPageModIncludes = function(test) { + var asserts = []; + function createPageModTest(include, expectedMatch) { + // Create an 'onload' test function... + asserts.push(function(test, win) { + var matches = include in win.localStorage; + test.assert(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(test, "about:buildconfig", [ + createPageModTest("*", false), + createPageModTest("*.google.com", false), + createPageModTest("about:*", true), + createPageModTest("about:", false), + createPageModTest("about:buildconfig", true) + ], + function (win, done) { + test.waitUntil(function () win.localStorage["about:buildconfig"], + "about:buildconfig page-mod to be executed") + .then(function () { + asserts.forEach(function(fn) { + fn(test, win); + }); + done(); + }); + } + ); +}; + +exports.testPageModErrorHandling = function(test) { + test.assertRaises(function() { + new pageMod.PageMod(); + }, + 'pattern is undefined', + "PageMod() throws when 'include' option is not specified."); +}; + +/* Tests for internal functions. */ +exports.testCommunication1 = function(test) { + let workerDone = false, + callbackDone = null; + + testPageMod(test, "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) { + test.fail('Errors where reported'); + }); + worker.on('message', function(value) { + test.assertEqual( + "worked", + value, + "test comunication" + ); + workerDone = true; + if (callbackDone) + callbackDone(); + }); + worker.postMessage('do it!') + } + }], + function(win, done) { + (callbackDone = function() { + if (workerDone) { + test.assertEqual( + 'worked', + win.document.body.getAttribute('JEP-107'), + 'attribute should be modified' + ); + done(); + } + })(); + } + ); +}; + +exports.testCommunication2 = function(test) { + let callbackDone = null, + window; + + testPageMod(test, "about:", [{ + 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) { + test.fail('Errors where reported'); + }); + worker.on('message', function(msg) { + if ('onload' == msg) { + test.assertEqual( + '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 { + test.assertEqual( + 'changes in window', + msg, + 'PageMod test #2: second script has run' + ) + callbackDone(); + } + }); + } + }], + function(win, done) { + window = win; + callbackDone = done; + } + ); +}; + +exports.testEventEmitter = function(test) { + let workerDone = false, + callbackDone = null; + + testPageMod(test, "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) { + test.fail('Errors were reported : '+e); + }); + worker.port.on('content-to-addon', function(value) { + test.assertEqual( + "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; + } + ); +}; + +exports.testHistory = function(test) { + // 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 = require("self").data.url("test-page-mod.html"); + let callbackDone = null; + testPageMod(test, 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) { + test.assertEqual(JSON.stringify(data), JSON.stringify({foo: "bar"}), + "History API works!"); + callbackDone(); + }); + } + }], + function(win, done) { + callbackDone = done; + } + ); +}; + +exports.testRelatedTab = function(test) { + test.waitUntilDone(); + + let tabs = require("tabs"); + let tab; + let pageMod = new require("page-mod").PageMod({ + include: "about:*", + onAttach: function(worker) { + test.assertEqual(tab, worker.tab, "Worker.tab is valid"); + pageMod.destroy(); + tab.close(); + test.done(); + } + }); + + tabs.open({ + url: "about:", + onOpen: function onOpen(t) { + tab = t; + } + }); + +}; + +exports['test tab worker on message'] = function(test) { + test.waitUntilDone(); + + let { browserWindows } = require("windows"); + let tabs = require("tabs"); + let { PageMod } = require("page-mod"); + + let url1 = "data:text/html,tab1

worker1.tab

"; + let url2 = "data:text/html,tab2

worker2.tab

"; + let worker1 = null; + + let mod = PageMod({ + include: "data:text/html,*", + contentScriptWhen: "ready", + contentScript: "self.postMessage('#1');", + onAttach: function onAttach(worker) { + worker.on("message", function onMessage() { + this.tab.attach({ + contentScriptWhen: "ready", + contentScript: "self.postMessage({ url: window.location.href, title: document.title });", + onMessage: function onMessage(data) { + test.assertEqual(this.tab.url, data.url, "location is correct"); + test.assertEqual(this.tab.title, data.title, "title is correct"); + if (this.tab.url === url1) { + worker1 = this; + tabs.open({ url: url2, inBackground: true }); + } + else if (this.tab.url === url2) { + mod.destroy(); + worker1.tab.close(); + worker1.destroy(); + worker.tab.close(); + worker.destroy(); + test.done(); + } + } + }); + }); + } + }); + + tabs.open(url1); +}; + +exports.testAutomaticDestroy = function(test) { + test.waitUntilDone(); + let loader = test.makeSandboxedLoader(); + + let pageMod = loader.require("page-mod").PageMod({ + include: "about:*", + contentScriptWhen: "start", + onAttach: function(w) { + test.fail("Page-mod should have been detroyed during module unload"); + } + }); + + // Unload the page-mod module so that our page mod is destroyed + loader.unload(); + + // Then create a second tab to ensure that it is correctly destroyed + let tabs = require("tabs"); + tabs.open({ + url: "about:", + onReady: function onReady(tab) { + test.pass("check automatic destroy"); + tab.close(); + test.done(); + } + }); + +} diff --git a/tools/addon-sdk-1.3/packages/addon-kit/tests/test-page-worker.js b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-page-worker.js new file mode 100644 index 0000000..8ea0ad4 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-page-worker.js @@ -0,0 +1,361 @@ +let tests = {}, Pages, Page; + +const ERR_DESTROYED = + "The page has been destroyed and can no longer be used."; + +tests.testSimplePageCreation = function(test) { + test.waitUntilDone(); + + let page = new Page({ + contentScript: "self.postMessage(window.location.href)", + contentScriptWhen: "end", + onMessage: function (message) { + test.assertEqual(message, "about:blank", + "Page Worker should start with a blank page by default"); + test.assertEqual(this, page, "The 'this' object is the page itself."); + test.done(); + } + }); +} + +/* + * Tests that we can't be tricked by document overloads as we have access + * to wrapped nodes + */ +tests.testWrappedDOM = function(test) { + test.waitUntilDone(); + + let page = Page({ + allow: { script: true }, + contentURL: "data:text/html,", + contentScript: "window.addEventListener('load', function () " + + "self.postMessage([typeof(document.getElementById), " + + "typeof(window.scrollTo)]), true)", + onMessage: function (message) { + test.assertEqual(message[0], + "function", + "getElementById from content script is the native one"); + + test.assertEqual(message[1], + "function", + "scrollTo from content script is the native one"); + + test.done(); + } + }); +} + +/* +// We do not offer unwrapped access to DOM since bug 601295 landed +// See 660780 to track progress of unwrap feature +tests.testUnwrappedDOM = function(test) { + test.waitUntilDone(); + + let page = Page({ + allow: { script: true }, + contentURL: "data:text/html,", + contentScript: "window.addEventListener('load', function () " + + "self.postMessage([typeof(unsafeWindow.document.getElementById), " + + "typeof(unsafeWindow.scrollTo)]), true)", + onMessage: function (message) { + test.assertEqual(message[0], + "number", + "document inside page is free to be changed"); + + test.assertEqual(message[1], + "number", + "window inside page is free to be changed"); + + test.done(); + } + }); +} +*/ + +tests.testPageProperties = function(test) { + let page = new Page(); + + for each (let prop in ['contentURL', 'allow', 'contentScriptFile', + 'contentScript', 'contentScriptWhen', 'on', + 'postMessage', 'removeListener']) { + test.assert(prop in page, prop + " property is defined on page."); + } + + test.assert(function () page.postMessage("foo") || true, + "postMessage doesn't throw exception on page."); +} + +tests.testConstructorAndDestructor = function(test) { + test.waitUntilDone(); + + let loader = test.makeSandboxedLoader(); + let Pages = loader.require("page-worker"); + let global = loader.findSandboxForModule("page-worker").globalScope; + + let pagesReady = 0; + + let page1 = Pages.Page({ + contentScript: "self.postMessage('')", + contentScriptWhen: "end", + onMessage: pageReady + }); + let page2 = Pages.Page({ + contentScript: "self.postMessage('')", + contentScriptWhen: "end", + onMessage: pageReady + }); + + test.assertNotEqual(page1, page2, + "Page 1 and page 2 should be different objects."); + + function pageReady() { + if (++pagesReady == 2) { + page1.destroy(); + page2.destroy(); + + test.assert(isDestroyed(page1), "page1 correctly unloaded."); + test.assert(isDestroyed(page2), "page2 correctly unloaded."); + + loader.unload(); + test.done(); + } + } +} + +tests.testAutoDestructor = function(test) { + test.waitUntilDone(); + + let loader = test.makeSandboxedLoader(); + let Pages = loader.require("page-worker"); + + let page = Pages.Page({ + contentScript: "self.postMessage('')", + contentScriptWhen: "end", + onMessage: function() { + loader.unload(); + test.assert(isDestroyed(page), "Page correctly unloaded."); + test.done(); + } + }); +} + +tests.testValidateOptions = function(test) { + test.assertRaises( + function () Page({ contentURL: 'home' }), + "The `contentURL` option must be a valid URL.", + "Validation correctly denied a non-URL contentURL" + ); + + test.assertRaises( + function () Page({ onMessage: "This is not a function."}), + "The event listener must be a function.", + "Validation correctly denied a non-function onMessage." + ); + + test.pass("Options validation is working."); +} + +tests.testContentAndAllowGettersAndSetters = function(test) { + test.waitUntilDone(); + let content = "data:text/html,"; + let page = Page({ + contentURL: content, + contentScript: "self.postMessage(window.localStorage.allowScript)", + contentScriptWhen: "end", + onMessage: step0 + }); + + function step0(message) { + test.assertEqual(message, "3", + "Correct value expected for allowScript - 3"); + test.assertEqual(page.contentURL, content, + "Correct content expected"); + page.removeListener('message', step0); + page.on('message', step1); + page.allow = { script: false }; + page.contentURL = content = + "data:text/html,"; + } + + function step1(message) { + test.assertEqual(message, "3", + "Correct value expected for allowScript - 3"); + test.assertEqual(page.contentURL, content, "Correct content expected"); + page.removeListener('message', step1); + page.on('message', step2); + page.allow = { script: true }; + page.contentURL = content = + "data:text/html,"; + } + + function step2(message) { + test.assertEqual(message, "g", + "Correct value expected for allowScript - g"); + test.assertEqual(page.contentURL, content, "Correct content expected"); + page.removeListener('message', step2); + page.on('message', step3); + page.allow.script = false; + page.contentURL = content = + "data:text/html,"; + } + + function step3(message) { + test.assertEqual(message, "g", + "Correct value expected for allowScript - g"); + test.assertEqual(page.contentURL, content, "Correct content expected"); + page.removeListener('message', step3); + page.on('message', step4); + page.allow.script = true; + page.contentURL = content = + "data:text/html,"; + } + + function step4(message) { + test.assertEqual(message, "4", + "Correct value expected for allowScript - 4"); + test.assertEqual(page.contentURL, content, "Correct content expected"); + test.done(); + } + +} + +tests.testOnMessageCallback = function(test) { + test.waitUntilDone(); + + Page({ + contentScript: "self.postMessage('')", + contentScriptWhen: "end", + onMessage: function() { + test.pass("onMessage callback called"); + test.done(); + } + }); +} + +tests.testMultipleOnMessageCallbacks = function(test) { + test.waitUntilDone(); + + let count = 0; + let page = Page({ + contentScript: "self.postMessage('')", + contentScriptWhen: "end", + onMessage: function() count += 1 + }); + page.on('message', function() count += 2); + page.on('message', function() count *= 3); + page.on('message', function() + test.assertEqual(count, 9, "All callbacks were called, in order.")); + page.on('message', function() test.done()); + +} + +tests.testLoadContentPage = function(test) { + + test.waitUntilDone(); + + let page = Page({ + onMessage: function(message) { + // The message is an array whose first item is the test method to call + // and the rest of whose items are arguments to pass it. + test[message.shift()].apply(test, message); + }, + contentURL: require("self").data.url("test-page-worker.html"), + contentScriptFile: require("self").data.url("test-page-worker.js"), + contentScriptWhen: "ready" + }); + +} + +tests.testAllowScriptDefault = function(test) { + + test.waitUntilDone(); + + let page = Page({ + onMessage: function(message) { + test.assert(message, "Script is allowed to run by default."); + test.done(); + }, + contentURL: "data:text/html,", + contentScript: "self.postMessage(document.documentElement.getAttribute('foo'))", + contentScriptWhen: "ready" + }); +} + +tests.testAllowScript = function(test) { + + test.waitUntilDone(); + + let page = Page({ + onMessage: function(message) { + test.assert(message, "Script runs when allowed to do so."); + test.done(); + }, + allow: { script: true }, + contentURL: "data:text/html,", + contentScript: "self.postMessage(document.documentElement.hasAttribute('foo') && " + + " document.documentElement.getAttribute('foo') == 3)", + contentScriptWhen: "ready" + }); +} + +tests.testPingPong = function(test) { + test.waitUntilDone(); + let page = Page({ + contentURL: 'data:text/html,ping-pong', + contentScript: 'self.on("message", function(message) self.postMessage("pong"));' + + 'self.postMessage("ready");', + onMessage: function(message) { + if ('ready' == message) { + return page.postMessage('ping'); + } + else { + test.assert(message, 'pong', 'Callback from contentScript'); + test.done(); + } + } + }); +}; + +tests.testMultipleDestroys = function(test) { + let page = Page(); + page.destroy(); + page.destroy(); + test.pass("Multiple destroys should not cause an error"); +}; + + +function isDestroyed(page) { + try { + page.postMessage("foo"); + } + catch (err if err.message == ERR_DESTROYED) { + return true; + } + return false; +} + + +let pageWorkerSupported = true; + +try { + Pages = require("page-worker"); + Page = Pages.Page; +} +catch (ex if ex.message == [ + "The page-worker module currently supports only Firefox and Thunderbird. ", + "In the future, we would like it to support other applications, however. ", + "Please see https://bugzilla.mozilla.org/show_bug.cgi?id=546740 for more ", + "information." + ].join("")) { + pageWorkerSupported = false; +} + +if (pageWorkerSupported) { + for (let test in tests) { + exports[test] = tests[test]; + } +} else { + exports.testPageWorkerNotSupported = function(test) { + test.pass("The page-worker module is not supported on this app."); + } +} diff --git a/tools/addon-sdk-1.3/packages/addon-kit/tests/test-panel.js b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-panel.js new file mode 100644 index 0000000..ac58044 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-panel.js @@ -0,0 +1,436 @@ +let { Cc, Ci } = require("chrome"); +let panels = require('panel'); +let tests = {}, panels, Panel; + +tests.testPanel = function(test) { + test.waitUntilDone(); + let panel = Panel({ + contentURL: "about:buildconfig", + contentScript: "self.postMessage(1); self.on('message', function() self.postMessage(2));", + onMessage: function (message) { + test.assertEqual(this, panel, "The 'this' object is the panel."); + switch(message) { + case 1: + test.pass("The panel was loaded."); + panel.postMessage(''); + break; + case 2: + test.pass("The panel posted a message and received a response."); + panel.destroy(); + test.done(); + break; + } + } + }); +}; + +tests.testPanelEmit = function(test) { + test.waitUntilDone(); + let panel = Panel({ + contentURL: "about:buildconfig", + contentScript: "self.port.emit('loaded');" + + "self.port.on('addon-to-content', " + + " function() self.port.emit('received'));", + }); + panel.port.on("loaded", function () { + test.pass("The panel was loaded and sent a first event."); + panel.port.emit("addon-to-content"); + }); + panel.port.on("received", function () { + test.pass("The panel posted a message and received a response."); + panel.destroy(); + test.done(); + }); +}; + +tests.testPanelEmitEarly = function(test) { + test.waitUntilDone(); + let panel = Panel({ + contentURL: "about:buildconfig", + contentScript: "self.port.on('addon-to-content', " + + " function() self.port.emit('received'));", + }); + panel.port.on("received", function () { + test.pass("The panel posted a message early and received a response."); + panel.destroy(); + test.done(); + }); + panel.port.emit("addon-to-content"); +}; + +tests.testShowHidePanel = function(test) { + test.waitUntilDone(); + let panel = Panel({ + contentScript: "self.postMessage('')", + contentScriptWhen: "end", + onMessage: function (message) { + panel.show(); + }, + onShow: function () { + test.pass("The panel was shown."); + test.assertEqual(this, panel, "The 'this' object is the panel."); + test.assertEqual(this.isShowing, true, "panel.isShowing == true."); + panel.hide(); + }, + onHide: function () { + test.pass("The panel was hidden."); + test.assertEqual(this, panel, "The 'this' object is the panel."); + test.assertEqual(this.isShowing, false, "panel.isShowing == false."); + panel.destroy(); + test.done(); + } + }); +}; + +tests.testDocumentReload = function(test) { + test.waitUntilDone(); + let content = + ""; + let messageCount = 0; + let panel = Panel({ + contentURL: "data:text/html," + encodeURIComponent(content), + contentScript: "self.postMessage(window.location.href)", + onMessage: function (message) { + messageCount++; + if (messageCount == 1) { + test.assertMatches(message, /data:text\/html,/, "First document had a content script"); + } + else if (messageCount == 2) { + test.assertEqual(message, "about:blank", "Second document too"); + panel.destroy(); + test.done(); + } + } + }); +}; + +tests.testParentResizeHack = function(test) { + let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"]. + getService(Ci.nsIWindowMediator). + getMostRecentWindow("navigator:browser"); + let docShell = browserWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell); + if (!("allowWindowControl" in docShell)) { + // bug 635673 is not fixed in this firefox build + test.pass("allowWindowControl attribute that allow to fix browser window " + + "resize is not available on this build."); + return; + } + + test.waitUntilDone(30000); + + let previousWidth = browserWindow.outerWidth, previousHeight = browserWindow.outerHeight; + + let content = "" + + "Try to resize browser window"; + let panel = Panel({ + contentURL: "data:text/html," + encodeURIComponent(content), + contentScript: "self.on('message', function(message){" + + " if (message=='resize') " + + " unsafeWindow.contentResize();" + + "});", + contentScriptWhen: "ready", + onMessage: function (message) { + + }, + onShow: function () { + panel.postMessage('resize'); + require("timer").setTimeout(function () { + test.assertEqual(previousWidth,browserWindow.outerWidth,"Size doesn't change by calling resizeTo/By/..."); + test.assertEqual(previousHeight,browserWindow.outerHeight,"Size doesn't change by calling resizeTo/By/..."); + panel.destroy(); + test.done(); + },0); + } + }); + panel.show(); +} + +tests.testResizePanel = function(test) { + test.waitUntilDone(); + + // These tests fail on Linux if the browser window in which the panel + // is displayed is not active. And depending on what other tests have run + // before this one, it might not be (the untitled window in which the test + // runner executes is often active). So we make sure the browser window + // is focused by focusing it before running the tests. Then, to be the best + // possible test citizen, we refocus whatever window was focused before we + // started running these tests. + + let activeWindow = Cc["@mozilla.org/embedcomp/window-watcher;1"]. + getService(Ci.nsIWindowWatcher). + activeWindow; + let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"]. + getService(Ci.nsIWindowMediator). + getMostRecentWindow("navigator:browser"); + + + function onFocus() { + browserWindow.removeEventListener("focus", onFocus, true); + + let panel = Panel({ + contentScript: "self.postMessage('')", + contentScriptWhen: "end", + height: 10, + width: 10, + onMessage: function (message) { + panel.show(); + }, + onShow: function () { + panel.resize(100,100); + panel.hide(); + }, + onHide: function () { + test.assert((panel.width == 100) && (panel.height == 100), + "The panel was resized."); + if (activeWindow) + activeWindow.focus(); + test.done(); + } + }); + } + + if (browserWindow === activeWindow) { + onFocus(); + } + else { + browserWindow.addEventListener("focus", onFocus, true); + browserWindow.focus(); + } +}; + +tests.testHideBeforeShow = function(test) { + test.waitUntilDone(); + let showCalled = false; + let panel = Panel({ + onShow: function () { + showCalled = true; + }, + onHide: function () { + test.assert(!showCalled, 'must not emit show if was hidden before'); + test.done(); + } + }); + panel.show(); + panel.hide(); +}; + +tests.testSeveralShowHides = function(test) { + test.waitUntilDone(); + let hideCalled = 0; + let panel = panels.Panel({ + contentURL: "about:buildconfig", + onShow: function () { + panel.hide(); + }, + onHide: function () { + hideCalled++; + if (hideCalled < 3) + panel.show(); + else { + test.pass("onHide called three times as expected"); + test.done(); + } + } + }); + panel.on('error', function(e) { + test.fail('error was emitted:' + e.message + '\n' + e.stack); + }); + panel.show(); +}; + +tests.testAnchorAndArrow = function(test) { + test.waitUntilDone(20000); + let count = 0; + function newPanel(tab, anchor) { + let panel = panels.Panel({ + contentURL: "data:text/html,Anchor: " + + anchor.id + "", + width: 200, + height: 100, + onShow: function () { + count++; + panel.destroy(); + if (count==5) { + test.pass("All anchored panel test displayed"); + tab.close(function () { + test.done(); + }); + } + } + }); + panel.show(anchor); + } + + let tabs= require("tabs"); + let url = 'data:text/html,' + + 'foo' + + '' + + '
Top Left
' + + '
Top Right
' + + '
Bottom Left
' + + '
Bottom right
' + + ''; + + tabs.open({ + url: url, + onReady: function(tab) { + let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"]. + getService(Ci.nsIWindowMediator). + getMostRecentWindow("navigator:browser"); + let window = browserWindow.content; + newPanel(tab, window.document.getElementById('tl')); + newPanel(tab, window.document.getElementById('tr')); + newPanel(tab, window.document.getElementById('bl')); + newPanel(tab, window.document.getElementById('br')); + let anchor = browserWindow.document.getElementById("identity-box"); + newPanel(tab, anchor); + } + }); + + + +}; + +tests.testPanelTextColor = function(test) { + test.waitUntilDone(); + let html = "" + + "

Foo

"; + let panel = Panel({ + contentURL: "data:text/html," + encodeURI(html), + contentScript: "self.port.emit('color', " + + "window.getComputedStyle(document.body.firstChild, null). " + + " getPropertyValue('color'));" + }); + panel.port.on("color", function (color) { + test.assertEqual(color, "rgb(255, 255, 0)", + "The panel text color style is preserved when a style exists."); + panel.destroy(); + test.done(); + }); +}; + +function makeEventOrderTest(options) { + let expectedEvents = []; + + return function(test) { + let panel = panels.Panel({ contentURL: "about:buildconfig" }); + + function expect(event, cb) { + expectedEvents.push(event); + panel.on(event, function() { + test.assertEqual(event, expectedEvents.shift()); + if (cb) + require("timer").setTimeout(cb, 1); + }); + return {then: expect}; + } + + test.waitUntilDone(); + options.test(test, expect, panel); + } +} + +tests.testAutomaticDestroy = function(test) { + let loader = test.makeSandboxedLoader(); + let panel = loader.require("panel").Panel({ + contentURL: "about:buildconfig", + contentScript: + "self.port.on('event', function() self.port.emit('event-back'));" + }); + + loader.unload(); + + panel.port.on("event-back", function () { + test.fail("Panel should have been destroyed on module unload"); + }); + panel.port.emit("event"); + test.pass("check automatic destroy"); +}; + +tests.testWaitForInitThenShowThenDestroy = makeEventOrderTest({ + test: function(test, expect, panel) { + expect('inited', function() { panel.show(); }). + then('show', function() { panel.destroy(); }). + then('hide', function() { test.done(); }); + } +}); + +tests.testShowThenWaitForInitThenDestroy = makeEventOrderTest({ + test: function(test, expect, panel) { + panel.show(); + expect('inited'). + then('show', function() { panel.destroy(); }). + then('hide', function() { test.done(); }); + } +}); + +tests.testShowThenHideThenDestroy = makeEventOrderTest({ + test: function(test, expect, panel) { + panel.show(); + expect('show', function() { panel.hide(); }). + then('hide', function() { panel.destroy(); test.done(); }); + } +}); + +tests.testContentURLOption = function(test) { + const URL_STRING = "about:buildconfig"; + const HTML_CONTENT = "Test

This is a test.

"; + + let (panel = Panel({ contentURL: URL_STRING })) { + test.pass("contentURL accepts a string URL."); + test.assertEqual(panel.contentURL, URL_STRING, + "contentURL is the string to which it was set."); + } + + let dataURL = "data:text/html," + encodeURIComponent(HTML_CONTENT); + let (panel = Panel({ contentURL: dataURL })) { + test.pass("contentURL accepts a data: URL."); + } + + let (panel = Panel({})) { + test.assert(panel.contentURL == null, + "contentURL is undefined."); + } + + test.assertRaises(function () Panel({ contentURL: "foo" }), + "The `contentURL` option must be a valid URL.", + "Panel throws an exception if contentURL is not a URL."); +}; + +let panelSupported = true; + +try { + panels = require("panel"); + Panel = panels.Panel; +} +catch(ex if ex.message == [ + "The panel module currently supports only Firefox. In the future ", + "we would like it to support other applications, however. Please see ", + "https://bugzilla.mozilla.org/show_bug.cgi?id=jetpack-panel-apps ", + "for more information." + ].join("")) { + panelSupported = false; +} + +if (panelSupported) { + for (let test in tests) + exports[test] = tests[test]; +} +else { + exports.testPanelNotSupported = function(test) { + test.pass("The panel module is not supported on this app."); + } +} diff --git a/tools/addon-sdk-1.3/packages/addon-kit/tests/test-passwords.js b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-passwords.js new file mode 100644 index 0000000..e799b48 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-passwords.js @@ -0,0 +1,277 @@ +"use strict"; + +const { store, search, remove } = require("passwords"); + +exports["test store requires `password` field"] = function(assert, done) { + store({ + username: "foo", + realm: "bar", + onComplete: function onComplete() { + assert.fail("onComplete should not be called"); + }, + onError: function onError() { + assert.pass("'`password` is required"); + done(); + } + }); +}; + +exports["test store requires `username` field"] = function(assert, done) { + store({ + password: "foo", + realm: "bar", + onComplete: function onComplete() { + assert.fail("onComplete should not be called"); + }, + onError: function onError() { + assert.pass("'`username` is required"); + done(); + } + }); +}; + +exports["test onComplete is optional"] = function(assert, done) { + store({ + realm: "bla", + username: "bla", + password: "bla", + onError: function onError() { + assert.fail("onError was called"); + } + }); + assert.pass("exception is not thrown if `onComplete is missing") + done(); +}; + +exports["test exceptions in onComplete are reported"] = function(assert, done) { + store({ + realm: "throws", + username: "error", + password: "boom!", + onComplete: function onComplete(error) { + throw new Error("Boom!") + }, + onError: function onError(error) { + assert.equal(error.message, "Boom!", "Error thrown is reported"); + done(); + } + }); +}; + +exports["test store requires `realm` field"] = function(assert, done) { + store({ + username: "foo", + password: "bar", + onComplete: function onComplete() { + assert.fail("onComplete should not be called"); + }, + onError: function onError() { + assert.pass("'`realm` is required"); + done(); + } + }); +}; + +exports["test can't store same login twice"] = function(assert, done) { + store({ + username: "user", + password: "pass", + realm: "realm", + onComplete: function onComplete() { + assert.pass("credential saved"); + + store({ + username: "user", + password: "pass", + realm: "realm", + onComplete: function onComplete() { + assert.fail("onComplete should not be called"); + }, + onError: function onError() { + assert.pass("re-saving credential failed"); + + remove({ + username: "user", + password: "pass", + realm: "realm", + onComplete: function onComplete() { + assert.pass("credential was removed"); + done(); + }, + onError: function onError() { + assert.fail("remove should not fail"); + } + }); + } + }); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); +}; + +exports["test remove fails if no login found"] = function(assert, done) { + remove({ + username: "foo", + password: "bar", + realm: "baz", + onComplete: function onComplete() { + assert.fail("should not be able to remove unstored credentials"); + }, + onError: function onError() { + assert.pass("can't remove unstored credentials"); + done(); + } + }); +}; + +exports["test addon associated credentials"] = function(assert, done) { + store({ + username: "foo", + password: "bar", + realm: "baz", + onComplete: function onComplete() { + search({ + username: "foo", + password: "bar", + realm: "baz", + onComplete: function onComplete([credential]) { + assert.equal(credential.url.indexOf("addon:"), 0, + "`addon:` uri is used for add-on credentials"); + assert.equal(credential.username, "foo", + "username matches"); + assert.equal(credential.password, "bar", + "password matches"); + assert.equal(credential.realm, "baz", "realm matches"); + assert.equal(credential.formSubmitURL, null, + "`formSubmitURL` is `null` for add-on credentials"); + assert.equal(credential.usernameField, "", "usernameField is empty"); + assert.equal(credential.passwordField, "", "passwordField is empty"); + + remove({ + username: credential.username, + password: credential.password, + realm: credential.realm, + onComplete: function onComplete() { + assert.pass("credential is removed"); + done(); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); +}; + +exports["test web page associated credentials"] = function(assert, done) { + store({ + url: "http://bar.foo.com/authentication/?login", + formSubmitURL: "http://login.foo.com/authenticate.cgi", + username: "user", + password: "pass", + usernameField: "user-f", + passwordField: "pass-f", + onComplete: function onComplete() { + search({ + username: "user", + password: "pass", + url: "http://bar.foo.com", + formSubmitURL: "http://login.foo.com", + onComplete: function onComplete([credential]) { + assert.equal(credential.url, "http://bar.foo.com", "url matches"); + assert.equal(credential.username, "user", "username matches"); + assert.equal(credential.password, "pass", "password matches"); + assert.equal(credential.realm, null, "realm is null"); + assert.equal(credential.formSubmitURL, "http://login.foo.com", + "formSubmitURL matches"); + assert.equal(credential.usernameField, "user-f", + "usernameField is matches"); + assert.equal(credential.passwordField, "pass-f", + "passwordField matches"); + + remove({ + url: credential.url, + formSubmitURL: credential.formSubmitURL, + username: credential.username, + password: credential.password, + usernameField: credential.usernameField, + passwordField: credential.passwordField, + + onComplete: function onComplete() { + assert.pass("credential is removed"); + done(); + }, + onError: function onError(e) { + assert.fail("onError should not be called"); + } + }); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); +}; + +exports["test site authentication credentials"] = function(assert, done) { + store({ + url: "http://authentication.com", + username: "U", + password: "P", + realm: "R", + onComplete: function onComplete() { + search({ + url: "http://authentication.com", + username: "U", + password: "P", + realm: "R", + onComplete: function onComplete([credential]) { + assert.equal(credential.url,"http://authentication.com", + "url matches"); + assert.equal(credential.username, "U", "username matches"); + assert.equal(credential.password, "P", "password matches"); + assert.equal(credential.realm, "R", "realm matches"); + assert.equal(credential.formSubmitURL, null, "formSubmitURL is null"); + assert.equal(credential.usernameField, "", "usernameField is empty"); + assert.equal(credential.passwordField, "", "passwordField is empty"); + + remove({ + url: credential.url, + username: credential.username, + password: credential.password, + realm: credential.realm, + onComplete: function onComplete() { + assert.pass("credential is removed"); + done(); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); +}; + +require("test").run(exports); diff --git a/tools/addon-sdk-1.3/packages/addon-kit/tests/test-private-browsing.js b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-private-browsing.js new file mode 100644 index 0000000..268e933 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-private-browsing.js @@ -0,0 +1,237 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Paul O’Shannessy + * Irakli Gozalishvili + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +let pb = require("private-browsing"); +let {Cc,Ci} = require("chrome"); + +let pbService; +// Currently, only Firefox implements the private browsing service. +if (require("xul-app").is("Firefox")) { + pbService = Cc["@mozilla.org/privatebrowsing;1"]. + getService(Ci.nsIPrivateBrowsingService); +} + +if (pbService) { + + // tests that isActive has the same value as the private browsing service + // expects + exports.testGetIsActive = function (test) { + test.assertEqual(pb.isActive, false, + "private-browsing.isActive is correct without modifying PB service"); + + pbService.privateBrowsingEnabled = true; + test.assert(pb.isActive, + "private-browsing.isActive is correct after modifying PB service"); + + // Switch back to normal mode. + pbService.privateBrowsingEnabled = false; + }; + + // tests that activating does put the browser into private browsing mode + exports.testActivateDeactivate = function (test) { + test.waitUntilDone(); + pb.once("start", function onStart() { + test.assertEqual(pbService.privateBrowsingEnabled, true, + "private browsing mode was activated"); + pb.deactivate(); + }); + pb.once("stop", function onStop() { + test.assertEqual(pbService.privateBrowsingEnabled, false, + "private browsing mode was deactivate"); + test.done(); + }); + pb.activate(); + }; + + exports.testStart = function(test) { + test.waitUntilDone(); + pb.on("start", function onStart() { + test.assertEqual(this, pb, "`this` should be private-browsing module"); + test.assert(pbService.privateBrowsingEnabled, + 'private mode is active when "start" event is emitted'); + test.assert(pb.isActive, + '`isActive` is `true` when "start" event is emitted'); + pb.removeListener("start", onStart); + test.done(); + }); + pb.activate(); + }; + + exports.testStop = function(test) { + test.waitUntilDone(); + pb.on("stop", function onStop() { + test.assertEqual(this, pb, "`this` should be private-browsing module"); + test.assertEqual(pbService.privateBrowsingEnabled, false, + "private mode is disabled when stop event is emitted"); + test.assertEqual(pb.isActive, false, + "`isActive` is `false` when stop event is emitted"); + pb.removeListener("stop", onStop); + test.done(); + }); + pb.activate(); + pb.deactivate(); + }; + + exports.testAutomaticUnload = function(test) { + test.waitUntilDone(); + // Create another private browsing instance and unload it + let loader = test.makeSandboxedLoader(); + let pb2 = loader.require("private-browsing"); + let called = false; + pb2.on("start", function onStart() { + called = true; + test.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.activate(); + pb.once("start", function onStart() { + require("timer").setTimeout(function () { + test.assert(!called, + "First private browsing instance is destroyed and inactive"); + + // Must reset to normal mode, so that next test starts with it. + pb.deactivate(); + test.done(); + }, 0); + }); + }; + + exports.testBothListeners = function(test) { + test.waitUntilDone(); + let stop = false; + let start = false; + + function onStop() { + test.assertEqual(stop, false, + "stop callback must be called only once"); + test.assertEqual(pbService.privateBrowsingEnabled, false, + "private mode is disabled when stop event is emitted"); + test.assertEqual(pb.isActive, false, + "`isActive` 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() { + test.assertEqual(false, start, + "stop callback must be called only once"); + test.assert(pbService.privateBrowsingEnabled, + "private mode is active when start event is emitted"); + test.assert(pb.isActive, + "`isActive` is `true` when start event is emitted"); + + pb.on("stop", onStop); + pb.deactivate(); + start = true; + } + + function onStart2() { + test.assert(start, "start listener must be called already"); + test.assertEqual(false, stop, "stop callback must not be called yet"); + } + + function finish() { + test.assert(pbService.privateBrowsingEnabled, true, + "private mode is active when start event is emitted"); + test.assert(pb.isActive, + "`isActive` is `true` when start event is emitted"); + + pb.removeListener("start", finish); + pb.removeListener("stop", onStop); + + pb.deactivate(); + pb.once("stop", function () { + test.assertEqual(pbService.privateBrowsingEnabled, false); + test.assertEqual(pb.isActive, false); + + test.done(); + }); + } + + pb.on("start", onStart); + pb.on("start", onStart2); + pbService.privateBrowsingEnabled = true; + }; + + exports["test activate private mode via handler"] = function(test) { + const tabs = require("tabs"); + + test.waitUntilDone(); + 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() { + test.done(); + }); + } + } + + tabs.on("ready", onReady); + pb.once("start", function onStart() { + test.pass("private mode was activated"); + pb.deactivate(); + }); + pb.once("stop", function onStop() { + test.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:"); + }; +} +else { + // tests for the case where private browsing doesn't exist + exports.testNoImpl = function (test) { + test.assertEqual(pb.isActive, false, + "pb.isActive returns false when private browsing isn't supported"); + }; +} diff --git a/tools/addon-sdk-1.3/packages/addon-kit/tests/test-request.js b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-request.js new file mode 100644 index 0000000..0bc7dbb --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-request.js @@ -0,0 +1,394 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Paul O’Shannessy (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const Request = require("request").Request; + +exports.testOptionsValidator = function(test) { + // First, a simple test to make sure we didn't break normal functionality. + test.assertRaises(function () { + Request({ + url: null + }); + }, 'The option "url" must be one of the following types: string'); + + // Next we'll have a Request that doesn't throw from c'tor, but from a setter. + let req = Request({ + url: "http://playground.zpao.com/jetpack/request/text.php", + onComplete: function () {} + }); + test.assertRaises(function () { + req.url = null; + }, 'The option "url" must be one of the following types: string'); + // The url shouldn't have changed, so check that + test.assertEqual(req.url, "http://playground.zpao.com/jetpack/request/text.php"); +} + +exports.testContentValidator = function(test) { + test.waitUntilDone(); + Request({ + url: "data:text/html,response", + content: { 'key1' : null, 'key2' : 'some value' }, + onComplete: function(response) { + test.assertEqual(response.text, "response?key1=null&key2=some+value"); + test.done(); + } + }).get(); +}; + +// All tests below here require a network connection. They will be commented out +// when checked in. If you'd like to run them, simply uncomment them. +// +// When we have the means, these tests will be converted so that they don't +// require an external server nor a network connection. + +/* +// This is request to a file that exists +exports.testStatus_200 = function (test) { + test.waitUntilDone(); + var req = Request({ + url: "http://playground.zpao.com/jetpack/request/text.php", + onComplete: function (response) { + test.assertEqual(this, req, "`this` should be request"); + test.assertEqual(response.status, 200); + test.assertEqual(response.statusText, "OK"); + test.done(); + } + }).get(); +} + +// This tries to get a file that doesn't exist +exports.testStatus_404 = function (test) { + test.waitUntilDone(); + Request({ + // the following URL doesn't exist + url: "http://playground.zpao.com/jetpack/request/nonexistent.php", + onComplete: function (response) { + test.assertEqual(response.status, 404); + test.assertEqual(response.statusText, "Not Found"); + test.done(); + } + }).get(); +} + +exports.testSimpleXML = function (test) { + test.waitUntilDone(); + Request({ + // File originally available at http://www.w3schools.com/xml/note.xml + url: "http://playground.zpao.com/jetpack/request/note.xml", + onComplete: function (response) { + // response.xml should be a document, so lets use it + test.assertRaises(function() { response.xml }, + "Sorry, the 'xml' property is no longer available. " + + "see bug 611042 for more information."); + test.done(); + return; + let xml = response.xml; + let notes = xml.getElementsByTagName("note"); + // Notes should have length of 1 + test.assertEqual(notes.length, 1, "Should be 1 in the XML"); + let note = notes[0]; + + // Silly whitespace text nodes... + let text = note.childNodes[0]; + test.assertEqual(note.childNodes[0].nodeName, "#text"); + + // Just test the next real node + let to = note.childNodes[1]; + test.assertEqual(to.nodeName, "to"); + test.assertEqual(to.textContent, "Tove"); + test.assertEqual(to.childNodes[0].nodeValue, "Tove"); + test.done(); + } + }).get(); +} + +// a simple file with known contents +exports.testSimpleText = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/text.php", + onComplete: function (response) { + test.assertEqual(response.text, "Look ma, no hands!\n"); + test.done(); + } + }).get(); +} + +// a simple file with a known header +exports.testKnownHeader = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/headers.php", + onComplete: function (response) { + test.assertEqual(response.headers["x-zpao-header"], "Jamba Juice"); + test.done(); + } + }).get(); +} + +// complex headers +exports.testKnownHeader = function (test) { + let headers = { + "x-zpao-header": "Jamba Juice is: delicious", + "x-zpao-header-2": "foo, bar", + "Set-Cookie": "foo=bar\nbaz=foo" + } + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/complex_headers.php", + onComplete: function (response) { + for (k in headers) { + test.assertEqual(response.headers[k], headers[k]); + } + test.done(); + } + }).get(); +} + +exports.testContentTypeHeader = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/text.txt", + onComplete: function (response) { + test.assertEqual(response.headers["Content-Type"], "text/plain"); + test.done(); + } + }).get(); +} + +exports.testSimpleJSON = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/json.php", + onComplete: function (response) { + assertDeepEqual(test, response.json, { foo: "bar" }); + test.done(); + } + }).get(); +} + +exports.testInvalidJSON = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/invalid_json.php", + onComplete: function (response) { + test.assertEqual(response.json, null); + test.done(); + } + }).get(); +} + +exports.testGetWithParamsNotContent = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php?foo=bar", + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : { foo: "bar" } + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} + +exports.testGetWithContent = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php", + content: { foo: "bar" }, + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : { foo: "bar" } + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} + +exports.testGetWithParamsAndContent = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php?foo=bar", + content: { baz: "foo" }, + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : { foo: "bar", baz: "foo" } + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} + +exports.testSimplePost = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php", + content: { foo: "bar" }, + onComplete: function (response) { + let expected = { + "POST": { foo: "bar" }, + "GET" : [] + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).post(); +} + +exports.testEncodedContent = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php", + content: "foo=bar&baz=foo", + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : { foo: "bar", baz: "foo" } + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} + +exports.testEncodedContentWithSpaces = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php", + content: "foo=bar+hop!&baz=foo", + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : { foo: "bar hop!", baz: "foo" } + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} + +exports.testGetWithArray = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php", + content: { foo: [1, 2], baz: "foo" }, + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : { foo: [1, 2], baz: "foo" } + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} + +exports.testGetWithNestedArray = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php", + content: { foo: [1, 2, [3, 4]], bar: "baz" }, + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : this.content + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} + +exports.testGetWithNestedArray = function (test) { + test.waitUntilDone(); + let request = Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php", + content: { + foo: [1, 2, { + omg: "bbq", + "all your base!": "are belong to us" + }], + bar: "baz" + }, + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : request.content + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} +*/ + +// This is not a proper testing for deep equal, but it's good enough for my uses +// here. It will do type coercion to check equality, but that's good here. Data +// coming from the server will be stringified and so "0" should be equal to 0. +function assertDeepEqual(test, obj1, obj2, msg) { + function equal(o1, o2) { + // cover our non-object cases well enough + if (o1 == o2) + return true; + if (typeof(o1) != typeof(o2)) + return false; + if (typeof(o1) != "object") + return o1 == o2; + + let e = true; + for (let [key, val] in Iterator(o1)) { + e = e && key in o2 && equal(o2[key], val); + if (!e) + break; + } + for (let [key, val] in Iterator(o2)) { + e = e && key in o1 && equal(o1[key], val); + if (!e) + break; + } + return e; + } + msg = msg || "objects not equal - " + JSON.stringify(obj1) + " != " + + JSON.stringify(obj2); + test.assert(equal(obj1, obj2), msg); +} diff --git a/tools/addon-sdk-1.3/packages/addon-kit/tests/test-selection.js b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-selection.js new file mode 100644 index 0000000..cd96072 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-selection.js @@ -0,0 +1,490 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Eric H. Jung + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +let timer = require("timer"); +let {Cc,Ci} = require("chrome"); + +// Arbitrary delay needed to avoid weird behavior. +// TODO: We need to find all uses of this and replace them +// with more deterministic solutions. +const ARB_DELAY = 100; + +// Select all divs elements in an HTML document +function selectAllDivs(window) { + let divs = window.document.getElementsByTagName("div"); + let s = window.getSelection(); + if (s.rangeCount > 0) + s.removeAllRanges(); + for (let i = 0; i < divs.length; i++) { + let range = window.document.createRange(); + range.selectNode(divs[i]); + s.addRange(range); + } +} + +function selectTextarea(window, from, to) { + let textarea = window.document.getElementsByTagName("textarea")[0]; + + from = from || 0; + to = to || textarea.value.length; + + textarea.setSelectionRange(from, to); + textarea.focus(); +} + +function primeTestCase(html, test, callback) { + let tabBrowser = require("tab-browser"); + let dataURL = "data:text/html," + encodeURI(html); + let tracker = tabBrowser.whenContentLoaded( + function(window) { + if (window.document.location.href != dataURL) + return; + callback(window, test); + timer.setTimeout(function() { + tracker.unload(); + test.done(); + window.close(); + }, + ARB_DELAY); + } + ); + tabBrowser.addTab(dataURL); +} + +const DIV1 = '
bar
'; +const DIV2 = '
noodles
'; +const HTML_MULTIPLE = '' + DIV1 + DIV2 + ''; +const HTML_SINGLE = '' + DIV1 + ''; + +// Tests of contiguous + +exports.testContiguousMultiple = function testContiguousMultiple(test) { + let selection = require("selection"); + primeTestCase(HTML_MULTIPLE, test, function(window, test) { + selectAllDivs(window); + test.assertEqual(selection.isContiguous, false, + "selection.isContiguous multiple works."); + }); + + test.waitUntilDone(5000); +}; + +exports.testContiguousSingle = function testContiguousSingle(test) { + let selection = require("selection"); + primeTestCase(HTML_SINGLE, test, function(window, test) { + selectAllDivs(window); + test.assertEqual(selection.isContiguous, true, + "selection.isContiguous single works."); + }); + + test.waitUntilDone(5000); +}; + +exports.testContiguousWithoutSelection = + function testContiguousWithoutSelection(test) { + let selection = require("selection"); + primeTestCase(HTML_SINGLE, test, function(window, test) { + test.assertEqual(selection.isContiguous, false, + "selection.isContiguous without selection works."); + }); + + test.waitUntilDone(5000); +}; + +/** + * Test that setting the contiguous property has no effect. + */ +/*exports.testSetContiguous = function testSetContiguous(test) { + let selection = require("selection"); + primeTestCase(HTML_MULTIPLE, test, function(window, test) { + selectAllDivs(window); + try { + selection.isContiguous = true; + test.assertEqual(selection.isContiguous, false, + "setting selection.isContiguous doesn't work (as expected)."); + } + catch (e) { + test.pass("setting selection.isContiguous doesn't work (as expected)."); + } + }); + + test.waitUntilDone(5000); +};*/ + + +// HTML tests + +exports.testGetHTMLSingleSelection = function testGetHTMLSingleSelection(test) { + let selection = require("selection"); + primeTestCase(HTML_SINGLE, test, function(window, test) { + selectAllDivs(window); + test.assertEqual(selection.html, DIV1, "get html selection works"); + }); + + test.waitUntilDone(5000); +}; + +/* Myk's comments: This is fine. However, it reminds me to figure out and + specify whether iteration is ordered. If so, we'll want to change this + test in the future to test that the discontiguous selections are returned in + the appropriate order. In the meantime, add a comment to that effect here */ +exports.testGetHTMLMultipleSelection = + function testGetHTMLMultipleSelection(test) { + let selection = require("selection"); + primeTestCase(HTML_MULTIPLE, test, function(window, test) { + selectAllDivs(window); + let assertions = false; + for each (let i in selection) { + test.assertEqual(true, [DIV1, DIV2].some(function(t) t == i.html), + "get multiple selection html works"); + assertions = true; + } + // Ensure we ran at least one assertEqual() + test.assert(assertions, "No assertions were called"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetHTMLNull = function testGetHTMLNull(test) { + let selection = require("selection"); + primeTestCase(HTML_SINGLE, test, function(window, test) { + test.assertEqual(selection.html, null, "get html null works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetHTMLWeird = function testGetHTMLWeird(test) { + let selection = require("selection"); + // If the getter is used when there are contiguous selections, the first + // selection should be returned + primeTestCase(HTML_MULTIPLE, test, function(window, test) { + selectAllDivs(window); + test.assertEqual(selection.html, DIV1, "get html weird works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetHTMLNullInTextareaSelection = + function testGetHTMLNullInTextareaSelection(test) { + let selection = require("selection"); + + primeTestCase(TEXT_FIELD, test, function(window, test) { + selectTextarea(window); + + test.assertEqual(selection.html, null, "get html null in textarea works") + }); + + test.waitUntilDone(5000); +}; + +const REPLACEMENT_HTML = "Lorem ipsum dolor sit amet"; + +exports.testSetHTMLSelection = function testSetHTMLSelection(test) { + let selection = require("selection"); + primeTestCase(HTML_SINGLE, test, function(window, test) { + selectAllDivs(window); + selection.html = REPLACEMENT_HTML; + test.assertEqual(selection.html, "" + REPLACEMENT_HTML + + "", "selection html works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testSetHTMLException = function testSetHTMLException(test) { + let selection = require("selection"); + primeTestCase(HTML_SINGLE, test, function(window, test) { + try { + selection.html = REPLACEMENT_HTML; + test.fail("set HTML throws when there's no selection."); + } + catch (e) { + test.pass("set HTML throws when there's no selection."); + } + }); + + test.waitUntilDone(5000); +}; + +const TEXT1 = "foo"; +const TEXT2 = "noodles"; +const TEXT_MULTIPLE = "
" + TEXT1 + "
" + TEXT2 + + "
"; +const TEXT_SINGLE = "
" + TEXT1 + "
"; +const TEXT_FIELD = ""; + +// Text tests + +exports.testSetHTMLinTextareaSelection = + function testSetHTMLinTextareaSelection(test) { + let selection = require("selection"); + + primeTestCase(TEXT_FIELD, test, function(window, test) { + selectTextarea(window); + + // HTML string is set as plain text in textareas, that's because + // `selection.html` and `selection.text` are basically aliases when a + // value is set. See bug 677269 + selection.html = REPLACEMENT_HTML; + + test.assertEqual(selection.text, REPLACEMENT_HTML, + "set selection html in textarea works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetTextSingleSelection = + function testGetTextSingleSelection(test) { + let selection = require("selection"); + primeTestCase(TEXT_SINGLE, test, function(window, test) { + selectAllDivs(window); + test.assertEqual(selection.text, TEXT1, "get text selection works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetTextMultipleSelection = + function testGetTextMultipleSelection(test) { + let selection = require("selection"); + primeTestCase(TEXT_MULTIPLE, test, function(window, test) { + selectAllDivs(window); + let assertions = false; + for each (let i in selection) { + test.assertEqual(true, [TEXT1, TEXT2].some(function(t) t == i.text), + "get multiple selection text works"); + assertions = true; + } + // Ensure we ran at least one assertEqual() + test.assert(assertions, "No assertions were called"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetTextNull = function testGetTextNull(test) { + let selection = require("selection"); + primeTestCase(TEXT_SINGLE, test, function(window, test) { + test.assertEqual(selection.text, null, "get text null works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetTextWeird = function testGetTextWeird(test) { + let selection = require("selection"); + // If the getter is used when there are contiguous selections, the first + // selection should be returned + primeTestCase(TEXT_MULTIPLE, test, function(window, test) { + selectAllDivs(window); + test.assertEqual(selection.text, TEXT1, "get text weird works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetTextNullInTextareaSelection = + function testGetTextInTextareaSelection(test) { + let selection = require("selection"); + + primeTestCase(TEXT_FIELD, test, function(window, test) { + test.assertEqual(selection.text, null, "get text null in textarea works") + }); + + test.waitUntilDone(5000); +}; + +exports.testGetTextInTextareaSelection = + function testGetTextInTextareaSelection(test) { + let selection = require("selection"); + + primeTestCase(TEXT_FIELD, test, function(window, test) { + selectTextarea(window); + + test.assertEqual(selection.text, TEXT1, "get text null in textarea works") + }); + + test.waitUntilDone(5000); +}; + +const REPLACEMENT_TEXT = "Lorem ipsum dolor sit amet"; + +exports.testSetTextSelection = function testSetTextSelection(test) { + let selection = require("selection"); + primeTestCase(TEXT_SINGLE, test, function(window, test) { + selectAllDivs(window); + selection.text = REPLACEMENT_TEXT; + test.assertEqual(selection.text, REPLACEMENT_TEXT, "selection text works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testSetHTMLException = function testSetHTMLException(test) { + let selection = require("selection"); + primeTestCase(TEXT_SINGLE, test, function(window, test) { + try { + selection.text = REPLACEMENT_TEXT; + test.fail("set HTML throws when there's no selection."); + } + catch (e) { + test.pass("set HTML throws when there's no selection."); + } + }); + + test.waitUntilDone(5000); +}; + +exports.testSetTextInTextareaSelection = + function testSetTextInTextareaSelection(test) { + let selection = require("selection"); + + primeTestCase(TEXT_FIELD, test, function(window, test) { + selectTextarea(window); + + selection.text = REPLACEMENT_TEXT; + + test.assertEqual(selection.text, REPLACEMENT_TEXT, + "set selection text in textarea works"); + }); + + test.waitUntilDone(5000); +}; + +// Iterator tests + +exports.testIterator = function testIterator(test) { + let selection = require("selection"); + let selectionCount = 0; + primeTestCase(TEXT_MULTIPLE, test, function(window, test) { + selectAllDivs(window); + for each (let i in selection) + selectionCount++; + test.assertEqual(2, selectionCount, "iterator works."); + }); + + test.waitUntilDone(5000); +}; + +exports.testIteratorWithTextareaSelection = + function testIteratorWithTextareaSelection(test) { + let selection = require("selection"); + let selectionCount = 0; + + primeTestCase(TEXT_FIELD, test, function(window, test) { + selectTextarea(window); + + for each (let i in selection) + selectionCount++; + + test.assertEqual(1, selectionCount, "iterator works in textarea."); + }); + + test.waitUntilDone(5000); +}; + +/* onSelect tests */ + +/* +function sendSelectionSetEvent(window) { + const Ci = Components.interfaces; + let utils = window.QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIDOMWindowUtils); + if (!utils.sendSelectionSetEvent(0, 1, false)) + dump("**** sendSelectionSetEvent did not select anything\n"); + else + dump("**** sendSelectionSetEvent succeeded\n"); +} + +// testOnSelect() requires nsIDOMWindowUtils, which is only available in +// Firefox 3.7+. +exports.testOnSelect = function testOnSelect(test) { + let selection = require("selection"); + let callbackCount = 0; + primeTestCase(TEXT_SINGLE, test, function(window, test) { + selection.onSelect = function() {callbackCount++}; + // Now simulate the user selecting stuff + sendSelectionSetEvent(window); + selection.text = REPLACEMENT_TEXT; + test.assertEqual(1, callbackCount, "onSelect text listener works."); + //test.pass(); + //test.done(); + }); + + test.waitUntilDone(5000); +}; + +// testOnSelectExceptionNoBubble() requires nsIDOMWindowUtils, which is only +// available in Firefox 3.7+. +exports.testOnSelectExceptionNoBubble = + function testOnSelectTextSelection(test) { + let selection = require("selection"); + primeTestCase(HTML_SINGLE, test, function(window, test) { + selection.onSelect = function() { + throw new Error("Exception thrown in testOnSelectExceptionNoBubble"); + }; + // Now simulate the user selecting stuff + sendSelectionSetEvent(window); + test.pass("onSelect catches exceptions."); + }); + + test.waitUntilDone(5000); +}; +*/ + +// If the module doesn't support the app we're being run in, require() will +// throw. In that case, remove all tests above from exports, and add one dummy +// test that passes. +try { + require("selection"); +} +catch (err) { + // This bug should be mentioned in the error message. + let bug = "https://bugzilla.mozilla.org/show_bug.cgi?id=560716"; + if (err.message.indexOf(bug) < 0) + throw err; + for (let [prop, val] in Iterator(exports)) { + if (/^test/.test(prop) && typeof(val) === "function") + delete exports[prop]; + } + exports.testAppNotSupported = function (test) { + test.pass("The selection module does not support this application."); + }; +} diff --git a/tools/addon-sdk-1.3/packages/addon-kit/tests/test-simple-storage.js b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-simple-storage.js new file mode 100644 index 0000000..6ec2507 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-simple-storage.js @@ -0,0 +1,346 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim:set ts=2 sw=2 sts=2 et filetype=javascript + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Drew Willcoxon (Original Author) + * Irakli Gozalishvili + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const file = require("file"); +const prefs = require("preferences-service"); + +const QUOTA_PREF = "extensions.addon-sdk.simple-storage.quota"; + +let {Cc,Ci} = require("chrome"); +let storeFile = Cc["@mozilla.org/file/directory_service;1"]. + getService(Ci.nsIProperties). + get("ProfD", Ci.nsIFile); +storeFile.append("jetpack"); +storeFile.append(packaging.jetpackID); +storeFile.append("simple-storage"); +storeFile.append("store.json"); +let storeFilename = storeFile.path; + +exports.testSetGet = function (test) { + test.waitUntilDone(); + + // Load the module once, set a value. + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function (storage) { + test.assert(file.exists(storeFilename), "Store file should exist"); + + // Load the module again and make sure the value stuck. + loader = newLoader(test); + ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function (storage) { + file.remove(storeFilename); + test.done(); + }; + test.assertEqual(ss.storage.foo, val, "Value should persist"); + loader.unload(); + }; + let val = "foo"; + ss.storage.foo = val; + test.assertEqual(ss.storage.foo, val, "Value read should be value set"); + loader.unload(); +}; + +exports.testSetGetRootArray = function (test) { + setGetRoot(test, [1, 2, 3], function (arr1, arr2) { + if (arr1.length !== arr2.length) + return false; + for (let i = 0; i < arr1.length; i++) { + if (arr1[i] !== arr2[i]) + return false; + } + return true; + }); +}; + +exports.testSetGetRootBool = function (test) { + setGetRoot(test, true); +}; + +exports.testSetGetRootFunction = function (test) { + setGetRootError(test, function () {}, + "Setting storage to a function should fail"); +}; + +exports.testSetGetRootNull = function (test) { + setGetRoot(test, null); +}; + +exports.testSetGetRootNumber = function (test) { + setGetRoot(test, 3.14); +}; + +exports.testSetGetRootObject = function (test) { + setGetRoot(test, { foo: 1, bar: 2 }, function (obj1, obj2) { + for (let [prop, val] in Iterator(obj1)) { + if (!(prop in obj2) || obj2[prop] !== val) + return false; + } + for (let [prop, val] in Iterator(obj2)) { + if (!(prop in obj1) || obj1[prop] !== val) + return false; + } + return true; + }); +}; + +exports.testSetGetRootString = function (test) { + setGetRoot(test, "sho' 'nuff"); +}; + +exports.testSetGetRootUndefined = function (test) { + setGetRootError(test, undefined, "Setting storage to undefined should fail"); +}; + +exports.testEmpty = function (test) { + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + loader.unload(); + test.assert(!file.exists(storeFilename), "Store file should not exist"); +}; + +exports.testMalformed = function (test) { + let stream = file.open(storeFilename, "w"); + stream.write("i'm not json"); + stream.close(); + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + let empty = true; + for (let key in ss.storage) { + empty = false; + break; + } + test.assert(empty, "Malformed storage should cause root to be empty"); + loader.unload(); +}; + +// Go over quota and handle it by listener. +exports.testQuotaExceededHandle = function (test) { + test.waitUntilDone(); + prefs.set(QUOTA_PREF, 18); + + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + ss.on("OverQuota", function () { + test.pass("OverQuota was emitted as expected"); + test.assertEqual(this, ss, "`this` should be simple storage"); + ss.storage = { x: 4, y: 5 }; + + manager(loader).jsonStore.onWrite = function () { + loader = newLoader(test); + ss = loader.require("simple-storage"); + let numProps = 0; + for (let prop in ss.storage) + numProps++; + test.assert(numProps, 2, + "Store should contain 2 values: " + ss.storage.toSource()); + test.assertEqual(ss.storage.x, 4, "x value should be correct"); + test.assertEqual(ss.storage.y, 5, "y value should be correct"); + manager(loader).jsonStore.onWrite = function (storage) { + prefs.reset(QUOTA_PREF); + test.done(); + }; + loader.unload(); + }; + loader.unload(); + }); + // This will be JSON.stringify()ed to: {"a":1,"b":2,"c":3} (19 bytes) + ss.storage = { a: 1, b: 2, c: 3 }; + manager(loader).jsonStore.write(); +}; + +// Go over quota but don't handle it. The last good state should still persist. +exports.testQuotaExceededNoHandle = function (test) { + test.waitUntilDone(); + prefs.set(QUOTA_PREF, 5); + + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + + manager(loader).jsonStore.onWrite = function (storage) { + loader = newLoader(test); + ss = loader.require("simple-storage"); + test.assertEqual(ss.storage, val, + "Value should have persisted: " + ss.storage); + ss.storage = "some very long string that is very long"; + ss.on("OverQuota", function () { + test.pass("OverQuota emitted as expected"); + manager(loader).jsonStore.onWrite = function () { + test.fail("Over-quota value should not have been written"); + }; + loader.unload(); + + loader = newLoader(test); + ss = loader.require("simple-storage"); + test.assertEqual(ss.storage, val, + "Over-quota value should not have been written, " + + "old value should have persisted: " + ss.storage); + loader.unload(); + prefs.reset(QUOTA_PREF); + test.done(); + }); + manager(loader).jsonStore.write(); + }; + + let val = "foo"; + ss.storage = val; + loader.unload(); +}; + +exports.testQuotaUsage = function (test) { + test.waitUntilDone(); + + let quota = 21; + prefs.set(QUOTA_PREF, quota); + + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + + // {"a":1} (7 bytes) + ss.storage = { a: 1 }; + test.assertEqual(ss.quotaUsage, 7 / quota, "quotaUsage should be correct"); + + // {"a":1,"bb":2} (14 bytes) + ss.storage = { a: 1, bb: 2 }; + test.assertEqual(ss.quotaUsage, 14 / quota, "quotaUsage should be correct"); + + // {"a":1,"bb":2,"cc":3} (21 bytes) + ss.storage = { a: 1, bb: 2, cc: 3 }; + test.assertEqual(ss.quotaUsage, 21 / quota, "quotaUsage should be correct"); + + manager(loader).jsonStore.onWrite = function () { + prefs.reset(QUOTA_PREF); + test.done(); + }; + loader.unload(); +}; + +exports.testUninstall = function (test) { + test.waitUntilDone(); + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function () { + test.assert(file.exists(storeFilename), "Store file should exist"); + + loader = newLoader(test); + ss = loader.require("simple-storage"); + loader.unload("uninstall"); + test.assert(!file.exists(storeFilename), "Store file should be removed"); + test.done(); + }; + ss.storage.foo = "foo"; + loader.unload(); +}; + +exports.testSetNoSetRead = function (test) { + test.waitUntilDone(); + + // Load the module, set a value. + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function (storage) { + test.assert(file.exists(storeFilename), "Store file should exist"); + + // Load the module again but don't access ss.storage. + loader = newLoader(test); + ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function (storage) { + test.fail("Nothing should be written since `storage` was not accessed."); + }; + loader.unload(); + + // Load the module a third time and make sure the value stuck. + loader = newLoader(test); + ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function (storage) { + file.remove(storeFilename); + test.done(); + }; + test.assertEqual(ss.storage.foo, val, "Value should persist"); + loader.unload(); + }; + let val = "foo"; + ss.storage.foo = val; + test.assertEqual(ss.storage.foo, val, "Value read should be value set"); + loader.unload(); +}; + +function manager(loader) { + return loader.findSandboxForModule("simple-storage").globalScope.manager; +} + +function newLoader(test) { + return test.makeSandboxedLoader({ globals: { packaging: packaging } }); +} + +function setGetRoot(test, val, compare) { + test.waitUntilDone(); + + compare = compare || function (a, b) a === b; + + // Load the module once, set a value. + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function () { + test.assert(file.exists(storeFilename), "Store file should exist"); + + // Load the module again and make sure the value stuck. + loader = newLoader(test); + ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function () { + file.remove(storeFilename); + test.done(); + }; + test.assert(compare(ss.storage, val), "Value should persist"); + loader.unload(); + }; + ss.storage = val; + test.assert(compare(ss.storage, val), "Value read should be value set"); + loader.unload(); +} + +function setGetRootError(test, val, msg) { + let pred = "storage must be one of the following types: " + + "array, boolean, null, number, object, string"; + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + test.assertRaises(function () ss.storage = val, pred, msg); + loader.unload(); +} diff --git a/tools/addon-sdk-1.3/packages/addon-kit/tests/test-tabs.js b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-tabs.js new file mode 100644 index 0000000..cbd6808 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-tabs.js @@ -0,0 +1,904 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dietrich Ayala (Original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +"use strict"; + +var {Cc,Ci} = require("chrome"); + +// test tab.activeTab getter +exports.testActiveTab_getter = function(test) { + test.waitUntilDone(); + + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + + let url = "data:text/html,foo"; + require("tab-browser").addTab( + url, + { + onLoad: function(e) { + test.assert(tabs.activeTab); + test.assertEqual(tabs.activeTab.url, url); + test.assertEqual(tabs.activeTab.title, "foo"); + closeBrowserWindow(window, function() test.done()); + } + } + ); + }); +}; + +// test 'BrowserWindow' instance creation on tab 'activate' event +// See bug 648244: there was a infinite loop. +exports.testBrowserWindowCreationOnActivate = function(test) { + test.waitUntilDone(); + + let windows = require("windows").browserWindows; + let tabs = require("tabs"); + + let gotActivate = false; + + tabs.once('activate', function onActivate(eventTab) { + test.assert(windows.activeWindow, "Is able to fetch activeWindow"); + gotActivate = true; + }); + + openBrowserWindow(function(window, browser) { + test.assert(gotActivate, "Received activate event before openBrowserWindow's callback is called"); + closeBrowserWindow(window, function () test.done()); + }); +} + +// test tab.activeTab setter +exports.testActiveTab_setter = function(test) { + test.waitUntilDone(); + + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url = "data:text/html,foo"; + + tabs.on('ready', function onReady(tab) { + tabs.removeListener('ready', onReady); + test.assertEqual(tabs.activeTab.url, "about:blank", "activeTab url has not changed"); + test.assertEqual(tab.url, url, "url of new background tab matches"); + tabs.on('activate', function onActivate(eventTab) { + tabs.removeListener('activate', onActivate); + test.assertEqual(tabs.activeTab.url, url, "url after activeTab setter matches"); + test.assertEqual(eventTab, tab, "event argument is the activated tab"); + test.assertEqual(eventTab, tabs.activeTab, "the tab is the active one"); + closeBrowserWindow(window, function() test.done()); + }); + tab.activate(); + }) + + tabs.open({ + url: url, + inBackground: true + }); + }); +}; + +exports.testAutomaticDestroy = function(test) { + test.waitUntilDone(); + + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + + // Create a second tab instance that we will destroy + let called = false; + + let loader = test.makeSandboxedLoader(); + let tabs2 = loader.require("tabs"); + tabs2.on('open', function onOpen(tab) { + called = true; + }); + + loader.unload(); + + // Fire a tab event an ensure that this destroyed tab is inactive + tabs.once('open', function () { + require("timer").setTimeout(function () { + test.assert(!called, "Unloaded tab module is destroyed and inactive"); + closeBrowserWindow(window, function() test.done()); + }, 0); + }); + + tabs.open("data:text/html,foo"); + + }); +}; + +// test tab properties +exports.testTabProperties = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs= require("tabs"); + let url = "data:text/html,foofoo"; + tabs.open({ + url: url, + onReady: function(tab) { + test.assertEqual(tab.title, "foo", "title of the new tab matches"); + test.assertEqual(tab.url, url, "URL of the new tab matches"); + test.assert(tab.favicon, "favicon of the new tab is not empty"); + test.assertEqual(tab.style, null, "style of the new tab matches"); + test.assertEqual(tab.index, 1, "index of the new tab matches"); + test.assertNotEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); + closeBrowserWindow(window, function() test.done()); + } + }); + }); +}; + +// test tabs iterator and length property +exports.testTabsIteratorAndLength = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let startCount = 0; + for each (let t in tabs) startCount++; + test.assertEqual(startCount, tabs.length, "length property is correct"); + let url = "data:text/html,default"; + tabs.open(url); + tabs.open(url); + tabs.open({ + url: url, + onOpen: function(tab) { + let count = 0; + for each (let t in tabs) count++; + test.assertEqual(count, startCount + 3, "iterated tab count matches"); + test.assertEqual(startCount + 3, tabs.length, "iterated tab count matches length property"); + closeBrowserWindow(window, function() test.done()); + } + }); + }); +}; + +// test tab.url setter +exports.testTabLocation = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url1 = "data:text/html,foo"; + let url2 = "data:text/html,bar"; + + tabs.on('ready', function onReady(tab) { + if (tab.url != url2) + return; + tabs.removeListener('ready', onReady); + test.pass("tab.load() loaded the correct url"); + closeBrowserWindow(window, function() test.done()); + }); + + tabs.open({ + url: url1, + onOpen: function(tab) { + tab.url = url2 + } + }); + }); +}; + +// test tab.close() +exports.testTabClose = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url = "data:text/html,foo"; + + test.assertNotEqual(tabs.activeTab.url, url, "tab is now the active tab"); + tabs.on('ready', function onReady(tab) { + tabs.removeListener('ready', onReady); + test.assertEqual(tabs.activeTab.url, tab.url, "tab is now the active tab"); + tab.close(function() { + closeBrowserWindow(window, function() test.done()); + }); + test.assertNotEqual(tabs.activeTab.url, url, "tab is no longer the active tab"); + }); + + tabs.open(url); + }); +}; + +// test tab.reload() +exports.testTabReload = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url = "data:text/html,"; + + tabs.open({ url: url, onReady: function onReady(tab) { + tab.removeListener("ready", onReady); + + browser.addEventListener( + "load", + function onLoad() { + browser.removeEventListener("load", onLoad, true); + + browser.addEventListener( + "load", + function onReload() { + browser.removeEventListener("load", onReload, true); + test.pass("the tab was loaded again"); + test.assertEqual(tab.url, url, "the tab has the same URL"); + closeBrowserWindow(window, function() test.done()); + }, + true + ); + tab.reload(); + }, + true + ); + }}); + }); +}; + +// test tab.move() +exports.testTabMove = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url = "data:text/html,foo"; + + tabs.open({ + url: url, + onOpen: function(tab) { + test.assertEqual(tab.index, 1, "tab index before move matches"); + tab.index = 0; + test.assertEqual(tab.index, 0, "tab index after move matches"); + closeBrowserWindow(window, function() test.done()); + } + }); + }); +}; + +// open tab with default options +exports.testOpen = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url = "data:text/html,default"; + tabs.open({ + url: url, + onReady: function(tab) { + test.assertEqual(tab.url, url, "URL of the new tab matches"); + test.assertEqual(window.content.location, url, "URL of active tab in the current window matches"); + closeBrowserWindow(window, function() test.done()); + } + }); + }); +}; + +// open pinned tab +exports.testOpenPinned = function(test) { + const xulApp = require("xul-app"); + if (xulApp.versionInRange(xulApp.platformVersion, "2.0b2", "*")) { + // test tab pinning + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url = "data:text/html,default"; + tabs.open({ + url: url, + isPinned: true, + onOpen: function(tab) { + test.assertEqual(tab.isPinned, true, "The new tab is pinned"); + closeBrowserWindow(window, function() test.done()); + } + }); + }); + } + else { + test.pass("Pinned tabs are not supported in this application."); + } +}; + +// pin/unpin opened tab +exports.testPinUnpin = function(test) { + const xulApp = require("xul-app"); + if (xulApp.versionInRange(xulApp.platformVersion, "2.0b2", "*")) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url = "data:text/html,default"; + tabs.open({ + url: url, + onOpen: function(tab) { + tab.pin(); + test.assertEqual(tab.isPinned, true, "The tab was pinned correctly"); + tab.unpin(); + test.assertEqual(tab.isPinned, false, "The tab was unpinned correctly"); + closeBrowserWindow(window, function() test.done()); + } + }); + }); + } + else { + test.pass("Pinned tabs are not supported in this application."); + } +}; + +// open tab in background +exports.testInBackground = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let activeUrl = tabs.activeTab.url; + let url = "data:text/html,background"; + test.assertEqual(activeWindow, window, "activeWindow matches this window"); + tabs.on('ready', function onReady(tab) { + tabs.removeListener('ready', onReady); + test.assertEqual(tabs.activeTab.url, activeUrl, "URL of active tab has not changed"); + test.assertEqual(tab.url, url, "URL of the new background tab matches"); + test.assertEqual(activeWindow, window, "a new window was not opened"); + test.assertNotEqual(tabs.activeTab.url, url, "URL of active tab is not the new URL"); + closeBrowserWindow(window, function() test.done()); + }); + tabs.open({ + url: url, + inBackground: true + }); + }); +}; + +// open tab in new window +exports.testOpenInNewWindow = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + + let cache = []; + let windowUtils = require("window-utils"); + let wt = new windowUtils.WindowTracker({ + onTrack: function(win) { + cache.push(win); + }, + onUntrack: function(win) { + cache.splice(cache.indexOf(win), 1) + } + }); + let startWindowCount = cache.length; + + let url = "data:text/html,newwindow"; + tabs.open({ + url: url, + inNewWindow: true, + onReady: function(tab) { + let newWindow = cache[cache.length - 1]; + test.assertEqual(cache.length, startWindowCount + 1, "a new window was opened"); + test.assertEqual(activeWindow, newWindow, "new window is active"); + test.assertEqual(tab.url, url, "URL of the new tab matches"); + test.assertEqual(newWindow.content.location, url, "URL of new tab in new window matches"); + test.assertEqual(tabs.activeTab.url, url, "URL of activeTab matches"); + for (var i in cache) cache[i] = null; + wt.unload(); + closeBrowserWindow(newWindow, function() { + closeBrowserWindow(window, function() test.done()); + }); + } + }); + }); +}; + +// onOpen event handler +exports.testTabsEvent_onOpen = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + var tabs = require("tabs"); + let url = "data:text/html,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) { + test.assertEqual(++eventCount, 2, "both listeners notified"); + tabs.removeListener('open', listener1); + tabs.removeListener('open', listener2); + closeBrowserWindow(window, function() test.done()); + }); + + tabs.open(url); + }); +}; + +// onClose event handler +exports.testTabsEvent_onClose = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + var tabs = require("tabs"); + let url = "data:text/html,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) { + test.assertEqual(++eventCount, 2, "both listeners notified"); + tabs.removeListener('close', listener1); + tabs.removeListener('close', listener2); + closeBrowserWindow(window, function() test.done()); + }); + + tabs.on('ready', function onReady(tab) { + tabs.removeListener('ready', onReady); + tab.close(); + }); + + tabs.open(url); + }); +}; + +// onClose event handler when a window is closed +exports.testTabsEvent_onCloseWindow = function(test) { + test.waitUntilDone(); + + openBrowserWindow(function(window, browser) { + var tabs = require("tabs"); + + let closeCount = 0, individualCloseCount = 0; + function listener() { + closeCount++; + } + tabs.on('close', listener); + + // One tab is already open with the window + let openTabs = 1; + function testCasePossiblyLoaded() { + if (++openTabs == 4) { + beginCloseWindow(); + } + } + + tabs.open({ + url: "data:text/html,tab2", + onOpen: function() testCasePossiblyLoaded(), + onClose: function() individualCloseCount++ + }); + + tabs.open({ + url: "data:text/html,tab3", + onOpen: function() testCasePossiblyLoaded(), + onClose: function() individualCloseCount++ + }); + + tabs.open({ + url: "data:text/html,tab4", + onOpen: function() testCasePossiblyLoaded(), + onClose: function() individualCloseCount++ + }); + + function beginCloseWindow() { + closeBrowserWindow(window, function testFinished() { + tabs.removeListener("close", listener); + + test.assertEqual(closeCount, 4, "Correct number of close events received"); + test.assertEqual(individualCloseCount, 3, + "Each tab with an attached onClose listener received a close " + + "event when the window was closed"); + + test.done(); + }); + } + + }); +} + +// onReady event handler +exports.testTabsEvent_onReady = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + var tabs = require("tabs"); + let url = "data:text/html,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) { + test.assertEqual(++eventCount, 2, "both listeners notified"); + tabs.removeListener('ready', listener1); + tabs.removeListener('ready', listener2); + closeBrowserWindow(window, function() test.done()); + }); + + tabs.open(url); + }); +}; + +// onActivate event handler +exports.testTabsEvent_onActivate = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + var tabs = require("tabs"); + let url = "data:text/html,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) { + test.assertEqual(++eventCount, 2, "both listeners notified"); + tabs.removeListener('activate', listener1); + tabs.removeListener('activate', listener2); + closeBrowserWindow(window, function() test.done()); + }); + + tabs.open(url); + }); +}; + +// onDeactivate event handler +exports.testTabsEvent_onDeactivate = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + var tabs = require("tabs"); + let url = "data:text/html,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) { + test.assertEqual(++eventCount, 2, "both listeners notified"); + tabs.removeListener('deactivate', listener1); + tabs.removeListener('deactivate', listener2); + closeBrowserWindow(window, function() test.done()); + }); + + tabs.on('open', function onOpen(tab) { + tabs.removeListener('open', onOpen); + tabs.open("data:text/html,foo"); + }); + + tabs.open(url); + }); +}; + +// per-tab event handlers +exports.testPerTabEvents = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + var tabs = require("tabs"); + let eventCount = 0; + + tabs.open({ + url: "data:text/html,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() { + test.assertEqual(eventCount, 1, "both listeners notified"); + tab.removeListener('ready', listener1); + tab.removeListener('ready', listener2); + closeBrowserWindow(window, function() test.done()); + }); + } + }); + }); +}; + +exports.testAttachOnOpen = function (test) { + // Take care that attach has to be called on tab ready and not on tab open. + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + + tabs.open({ + url: "data:text/html,foobar", + onOpen: function (tab) { + let worker = tab.attach({ + contentScript: 'self.postMessage(document.location.href); ', + onMessage: function (msg) { + test.assertEqual(msg, "about:blank", + "Worker document url is about:blank on open"); + worker.destroy(); + closeBrowserWindow(window, function() test.done()); + } + }); + } + }); + + }); +} + +exports.testAttachOnMultipleDocuments = function (test) { + // Example of attach that process multiple tab documents + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let firstLocation = "data:text/html,foobar"; + let secondLocation = "data:text/html,bar"; + let thirdLocation = "data:text/html,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) { + test.assertEqual(msg, firstLocation, + "Worker url is equal to the 1st document"); + tab.url = secondLocation; + }, + onDetach: function () { + detachEventCount++; + test.pass("Got worker1 detach event"); + test.assertRaises(function () { + worker1.postMessage("ex-1"); + }, + /The page has been destroyed/, + "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) { + test.assertEqual(msg, secondLocation, + "Worker url is equal to the 2nd document"); + tab.url = thirdLocation; + }, + onDetach: function () { + detachEventCount++; + test.pass("Got worker2 detach event"); + test.assertRaises(function () { + worker2.postMessage("ex-2"); + }, + /The page has been destroyed/, + "postMessage throw because worker2 is destroyed"); + checkEnd(); + } + }); + worker2.postMessage("new-doc-2"); + } + else if (onReadyCount == 3) { + + tab.close(); + + } + + } + }); + + function checkEnd() { + if (detachEventCount != 2) + return; + + test.pass("Got all detach events"); + + closeBrowserWindow(window, function() test.done()); + } + + }); +} + + +exports.testAttachWrappers = function (test) { + // Check that content script has access to wrapped values by default + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let document = "data:text/html,"; + 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) { + test.assertEqual(msg, true, "Worker has wrapped objects ("+count+")"); + if (count++ == 1) + closeBrowserWindow(window, function() test.done()); + } + }); + } + }); + + }); +} + +/* +// We do not offer unwrapped access to DOM since bug 601295 landed +// See 660780 to track progress of unwrap feature +exports.testAttachUnwrapped = function (test) { + // Check that content script has access to unwrapped values through unsafeWindow + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let document = "data:text/html,"; + 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) { + test.assertEqual(msg, true, "Worker has access to javascript content globals ("+count+")"); + closeBrowserWindow(window, function() test.done()); + } + }); + } + }); + + }); +} +*/ + +exports['test window focus changes active tab'] = function(test) { + test.waitUntilDone(); + let win1 = openBrowserWindow(function() { + let win2 = openBrowserWindow(function() { + let tabs = require("tabs"); + tabs.on("activate", function onActivate() { + tabs.removeListener("activate", onActivate); + test.pass("activate was called on windows focus change."); + closeBrowserWindow(win1, function() { + closeBrowserWindow(win2, function() { test.done(); }); + }); + }); + win1.focus(); + }, "data:text/html,test window focus changes active tab

Window #2"); + }, "data:text/html,test window focus changes active tab

Window #1"); +}; + +exports['test ready event on new window tab'] = function(test) { + test.waitUntilDone(); + let uri = encodeURI("data:text/html,Waiting for ready event!"); + + require("tabs").on("ready", function onReady(tab) { + if (tab.url === uri) { + require("tabs").removeListener("ready", onReady); + test.pass("ready event was emitted"); + closeBrowserWindow(window, function() { + test.done(); + }); + } + }); + + let window = openBrowserWindow(function(){}, uri); +}; +/******************* 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 { + require("timer").setTimeout(function () { + callback(window, browsers[0]); + }, 10); + } catch (e) { console.exception(e); } + } + }, true); + } + + return window; +} + +// Helper for calling code at window close +function closeBrowserWindow(window, callback) { + window.addEventListener("unload", function unload() { + window.removeEventListener("unload", unload, false); + callback(); + }, false); + window.close(); +} + +// If the module doesn't support the app we're being run in, require() will +// throw. In that case, remove all tests above from exports, and add one dummy +// test that passes. +try { + require("tabs"); +} +catch (err) { + // This bug should be mentioned in the error message. + let bug = "https://bugzilla.mozilla.org/show_bug.cgi?id=560716"; + if (err.message.indexOf(bug) < 0) + throw err; + for (let [prop, val] in Iterator(exports)) { + if (/^test/.test(prop) && typeof(val) === "function") + delete exports[prop]; + } + exports.testAppNotSupported = function (test) { + test.pass("the tabs module does not support this application."); + }; +} diff --git a/tools/addon-sdk-1.3/packages/addon-kit/tests/test-timers.js b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-timers.js new file mode 100644 index 0000000..98ea613 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-timers.js @@ -0,0 +1,44 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Shane Tomlinson (Original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +const timers = require("timers"); + +exports.testTimeout = function (test) { + test.waitUntilDone(); + timers.setTimeout(function () { + test.pass("timers.setTimeout works"); + test.done(); + }, 0); +} diff --git a/tools/addon-sdk-1.3/packages/addon-kit/tests/test-widget.js b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-widget.js new file mode 100644 index 0000000..0845517 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-widget.js @@ -0,0 +1,909 @@ +const {Cc,Ci} = require("chrome"); + +exports.testConstructor = function(test) { + + const tabBrowser = require("tab-browser"); + + test.waitUntilDone(30000); + + const widgets = require("widget"); + const url = require("url"); + const windowUtils = require("window-utils"); + + let browserWindow = windowUtils.activeBrowserWindow; + let doc = browserWindow.document; + let AddonsMgrListener = browserWindow.AddonsMgrListener; + + function container() doc.getElementById("addon-bar"); + function widgetCount() container() ? container().getElementsByTagName("toolbaritem").length : 0; + let widgetStartCount = widgetCount(); + function widgetNode(index) container() ? container().getElementsByTagName("toolbaritem")[index] : null; + + // Test basic construct/destroy + AddonsMgrListener.onInstalling(); + let w = widgets.Widget({ id: "fooID", label: "foo", content: "bar" }); + AddonsMgrListener.onInstalled(); + test.assertEqual(widgetCount(), widgetStartCount + 1, "panel has correct number of child elements after widget construction"); + + // test widget height + test.assertEqual(widgetNode(0).firstChild.boxObject.height, 16, "widget has correct default height"); + + AddonsMgrListener.onUninstalling(); + w.destroy(); + AddonsMgrListener.onUninstalled(); + w.destroy(); + test.pass("Multiple destroys do not cause an error"); + test.assertEqual(widgetCount(), widgetStartCount, "panel has correct number of child elements after destroy"); + + // Test automatic widget destroy on unload + let loader = test.makeSandboxedLoader(); + let widgetsFromLoader = loader.require("widget"); + let widgetStartCount = widgetCount(); + let w = widgetsFromLoader.Widget({ id: "fooID", label: "foo", content: "bar" }); + test.assertEqual(widgetCount(), widgetStartCount + 1, "widget has been correctly added"); + loader.unload(); + test.assertEqual(widgetCount(), widgetStartCount, "widget has been destroyed on module unload"); + + // Test nothing + test.assertRaises( + function() widgets.Widget({}), + "The widget must have a non-empty label property.", + "throws on no properties"); + + // Test no label + test.assertRaises( + function() widgets.Widget({content: "foo"}), + "The widget must have a non-empty label property.", + "throws on no label"); + + // Test empty label + test.assertRaises( + function() widgets.Widget({label: "", content: "foo"}), + "The widget must have a non-empty label property.", + "throws on empty label"); + + // Test no content or image + test.assertRaises( + function() widgets.Widget({id: "fooID", label: "foo"}), + "No content or contentURL property found. Widgets must have one or the other.", + "throws on no content"); + + // Test empty content, no image + test.assertRaises( + function() widgets.Widget({id:"fooID", label: "foo", content: ""}), + "No content or contentURL property found. Widgets must have one or the other.", + "throws on empty content"); + + // Test empty image, no content + test.assertRaises( + function() widgets.Widget({id:"fooID", label: "foo", image: ""}), + "No content or contentURL property found. Widgets must have one or the other.", + "throws on empty content"); + + // Test empty content, empty image + test.assertRaises( + function() widgets.Widget({id:"fooID", label: "foo", content: "", image: ""}), + "No content or contentURL property found. Widgets must have one or the other.", + "throws on empty content"); + + // Test duplicated ID + let duplicateID = widgets.Widget({id: "foo", label: "foo", content: "bar"}); + test.assertRaises( + function() widgets.Widget({id: "foo", label: "bar", content: "bar"}), + /This widget ID is already used:/, + "throws on duplicated id"); + duplicateID.destroy(); + + // Test duplicate label, different ID + let w1 = widgets.Widget({id: "id1", label: "foo", content: "bar"}); + let w2 = widgets.Widget({id: "id2", label: "foo", content: "bar"}); + w1.destroy(); + w2.destroy(); + + // Test position restore on create/destroy/create + // Create 3 ordered widgets + let w1 = widgets.Widget({id: "first", label:"first", content: "bar"}); + let w2 = widgets.Widget({id: "second", label:"second", content: "bar"}); + let w3 = widgets.Widget({id: "third", label:"third", content: "bar"}); + // Remove the middle widget + test.assertEqual(widgetNode(1).getAttribute("label"), "second", "second widget is the second widget inserted"); + w2.destroy(); + test.assertEqual(widgetNode(1).getAttribute("label"), "third", "second widget is removed, so second widget is now the third one"); + w2 = widgets.Widget({id: "second", label:"second", content: "bar"}); + test.assertEqual(widgetNode(1).getAttribute("label"), "second", "second widget is created again, at the same location"); + // Cleanup this testcase + AddonsMgrListener.onUninstalling(); + w1.destroy(); + w2.destroy(); + w3.destroy(); + AddonsMgrListener.onUninstalled(); + + // Test concurrent widget module instances on addon-bar hiding + let loader = test.makeSandboxedLoader(); + let anotherWidgetsInstance = loader.require("widget"); + test.assert(container().collapsed, "UI is hidden when no widgets"); + AddonsMgrListener.onInstalling(); + let w1 = widgets.Widget({id: "foo", label: "foo", content: "bar"}); + // Ideally we would let AddonsMgrListener display the addon bar + // But, for now, addon bar is immediatly displayed by sdk code + // https://bugzilla.mozilla.org/show_bug.cgi?id=627484 + test.assert(!container().collapsed, "UI is already visible when we just added the widget"); + AddonsMgrListener.onInstalled(); + test.assert(!container().collapsed, "UI become visible when we notify AddonsMgrListener about end of addon installation"); + let w2 = anotherWidgetsInstance.Widget({id: "bar", label: "bar", content: "foo"}); + test.assert(!container().collapsed, "UI still visible when we add a second widget"); + AddonsMgrListener.onUninstalling(); + w1.destroy(); + AddonsMgrListener.onUninstalled(); + test.assert(!container().collapsed, "UI still visible when we remove one of two widgets"); + AddonsMgrListener.onUninstalling(); + w2.destroy(); + test.assert(!container().collapsed, "UI is still visible when we have removed all widget but still not called onUninstalled"); + AddonsMgrListener.onUninstalled(); + test.assert(container().collapsed, "UI is hidden when we have removed all widget and called onUninstalled"); + + // Helper for testing a single widget. + // Confirms proper addition and content setup. + function testSingleWidget(widgetOptions) { + // We have to display which test is being run, because here we do not + // use the regular test framework but rather a custom one that iterates + // the `tests` array. + console.info("executing: " + widgetOptions.id); + + let startCount = widgetCount(); + let widget = widgets.Widget(widgetOptions); + let node = widgetNode(startCount); + test.assert(node, "widget node at index"); + test.assertEqual(node.tagName, "toolbaritem", "widget element is correct"); + test.assertEqual(widget.width + "px", node.style.minWidth, "widget width is correct"); + test.assertEqual(widgetCount(), startCount + 1, "container has correct number of child elements"); + let content = node.firstElementChild; + test.assert(content, "found content"); + test.assertMatches(content.tagName, /iframe|image/, "content is iframe or image"); + return widget; + } + + // Array of widgets to test + // and a function to test them. + let tests = []; + function nextTest() { + test.assertEqual(widgetCount(), 0, "widget in last test property cleaned itself up"); + if (!tests.length) + test.done(); + else + require("timer").setTimeout(tests.shift(), 0); + } + function doneTest() nextTest(); + + // text widget + tests.push(function testTextWidget() testSingleWidget({ + id: "text", + label: "text widget", + content: "oh yeah", + contentScript: "self.postMessage(document.body.innerHTML);", + contentScriptWhen: "end", + onMessage: function (message) { + test.assertEqual(this.content, message, "content matches"); + this.destroy(); + doneTest(); + } + })); + + // html widget + tests.push(function testHTMLWidget() testSingleWidget({ + id: "html", + label: "html widget", + content: "
oh yeah
", + contentScript: "self.postMessage(document.body.innerHTML);", + contentScriptWhen: "end", + onMessage: function (message) { + test.assertEqual(this.content, message, "content matches"); + this.destroy(); + doneTest(); + } + })); + + // image url widget + tests.push(function testImageURLWidget() testSingleWidget({ + id: "image", + label: "image url widget", + contentURL: require("self").data.url("test.html"), + contentScript: "self.postMessage({title: document.title, " + + "tag: document.body.firstElementChild.tagName, " + + "content: document.body.firstElementChild.innerHTML});", + contentScriptWhen: "end", + onMessage: function (message) { + test.assertEqual(message.title, "foo", "title matches"); + test.assertEqual(message.tag, "P", "element matches"); + test.assertEqual(message.content, "bar", "element content matches"); + this.destroy(); + doneTest(); + } + })); + + // web uri widget + tests.push(function testWebURIWidget() testSingleWidget({ + id: "web", + label: "web uri widget", + contentURL: require("self").data.url("test.html"), + contentScript: "self.postMessage({title: document.title, " + + "tag: document.body.firstElementChild.tagName, " + + "content: document.body.firstElementChild.innerHTML});", + contentScriptWhen: "end", + onMessage: function (message) { + test.assertEqual(message.title, "foo", "title matches"); + test.assertEqual(message.tag, "P", "element matches"); + test.assertEqual(message.content, "bar", "element content matches"); + this.destroy(); + doneTest(); + } + })); + + // event: onclick + content + tests.push(function testOnclickEventContent() testSingleWidget({ + id: "click", + label: "click test widget - content", + content: "
foo
", + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('click', true, true ); " + + "document.getElementById('me').dispatchEvent(evt);", + contentScriptWhen: "end", + onClick: function() { + test.pass("onClick called"); + this.destroy(); + doneTest(); + } + })); + + // event: onmouseover + content + tests.push(function testOnmouseoverEventContent() testSingleWidget({ + id: "mouseover", + label: "mouseover test widget - content", + content: "
foo
", + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('mouseover', true, true ); " + + "document.getElementById('me').dispatchEvent(evt);", + contentScriptWhen: "end", + onMouseover: function() { + test.pass("onMouseover called"); + this.destroy(); + doneTest(); + } + })); + + // event: onmouseout + content + tests.push(function testOnmouseoutEventContent() testSingleWidget({ + id: "mouseout", + label: "mouseout test widget - content", + content: "
foo
", + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('mouseout', true, true ); " + + "document.getElementById('me').dispatchEvent(evt);", + contentScriptWhen: "end", + onMouseout: function() { + test.pass("onMouseout called"); + this.destroy(); + doneTest(); + } + })); + + // event: onclick + image + tests.push(function testOnclickEventImage() testSingleWidget({ + id: "click", + label: "click test widget - image", + contentURL: require("self").data.url("moz_favicon.ico"), + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('click', true, true ); " + + "document.body.firstElementChild.dispatchEvent(evt);", + contentScriptWhen: "end", + onClick: function() { + test.pass("onClick called"); + this.destroy(); + doneTest(); + } + })); + + // event: onmouseover + image + tests.push(function testOnmouseoverEventImage() testSingleWidget({ + id: "mouseover", + label: "mouseover test widget - image", + contentURL: require("self").data.url("moz_favicon.ico"), + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('mouseover', true, true ); " + + "document.body.firstElementChild.dispatchEvent(evt);", + contentScriptWhen: "end", + onMouseover: function() { + test.pass("onMouseover called"); + this.destroy(); + doneTest(); + } + })); + + // event: onmouseout + image + tests.push(function testOnmouseoutEventImage() testSingleWidget({ + id: "mouseout", + label: "mouseout test widget - image", + contentURL: require("self").data.url("moz_favicon.ico"), + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('mouseout', true, true ); " + + "document.body.firstElementChild.dispatchEvent(evt);", + contentScriptWhen: "end", + onMouseout: function() { + test.pass("onMouseout called"); + this.destroy(); + doneTest(); + } + })); + + // test multiple widgets + tests.push(function testMultipleWidgets() { + let w1 = widgets.Widget({id: "first", label: "first widget", content: "first content"}); + let w2 = widgets.Widget({id: "second", label: "second widget", content: "second content"}); + + w1.destroy(); + w2.destroy(); + + doneTest(); + }); + + // test updating widget content + let loads = 0; + tests.push(function testUpdatingWidgetContent() testSingleWidget({ + id: "content", + label: "content update test widget", + content: "
foo
", + contentScript: "self.postMessage(1)", + contentScriptWhen: "ready", + onMessage: function(message) { + if (!this.flag) { + this.content = "
bar
"; + this.flag = 1; + } + else { + test.assertEqual(this.content, "
bar
"); + this.destroy(); + doneTest(); + } + } + })); + + // test updating widget contentURL + let url1 = "data:text/html,foodle"; + let url2 = "data:text/html,nistel"; + + tests.push(function testUpdatingContentURL() testSingleWidget({ + id: "content", + label: "content update test widget", + contentURL: url1, + contentScript: "self.postMessage(document.location.href);", + contentScriptWhen: "end", + onMessage: function(message) { + if (!this.flag) { + test.assertEqual(this.contentURL.toString(), url1); + test.assertEqual(message, url1); + this.contentURL = url2; + this.flag = 1; + } + else { + test.assertEqual(this.contentURL.toString(), url2); + test.assertEqual(message, url2); + this.destroy(); + doneTest(); + } + } + })); + + // test tooltip + tests.push(function testTooltip() testSingleWidget({ + id: "text", + label: "text widget", + content: "oh yeah", + tooltip: "foo", + contentScript: "self.postMessage(1)", + contentScriptWhen: "ready", + onMessage: function(message) { + test.assertEqual(this.tooltip, "foo", "tooltip matches"); + this.destroy(); + doneTest(); + } + })); + + // test tooltip fallback to label + tests.push(function testTooltipFallback() testSingleWidget({ + id: "fallback", + label: "fallback", + content: "oh yeah", + contentScript: "self.postMessage(1)", + contentScriptWhen: "ready", + onMessage: function(message) { + test.assertEqual(this.tooltip, this.label, "tooltip fallbacks to label"); + this.destroy(); + doneTest(); + } + })); + + // test updating widget tooltip + let updated = false; + tests.push(function testUpdatingTooltip() testSingleWidget({ + id: "tooltip", + label: "tooltip update test widget", + tooltip: "foo", + content: "
foo
", + contentScript: "self.postMessage(1)", + contentScriptWhen: "ready", + onMessage: function(message) { + this.tooltip = "bar"; + test.assertEqual(this.tooltip, "bar", "tooltip gets updated"); + this.destroy(); + doneTest(); + } + })); + + // test multiple windows + tests.push(function testMultipleWindows() { + tabBrowser.addTab("about:blank", { inNewWindow: true, onLoad: function(e) { + let browserWindow = e.target.defaultView; + let doc = browserWindow.document; + function container() doc.getElementById("addon-bar"); + function widgetCount2() container() ? container().childNodes.length : 0; + let widgetStartCount2 = widgetCount2(); + + let w1Opts = {id:"first", label: "first widget", content: "first content"}; + let w1 = testSingleWidget(w1Opts); + test.assertEqual(widgetCount2(), widgetStartCount2 + 1, "2nd window has correct number of child elements after first widget"); + + let w2Opts = {id:"second", label: "second widget", content: "second content"}; + let w2 = testSingleWidget(w2Opts); + test.assertEqual(widgetCount2(), widgetStartCount2 + 2, "2nd window has correct number of child elements after second widget"); + + w1.destroy(); + test.assertEqual(widgetCount2(), widgetStartCount2 + 1, "2nd window has correct number of child elements after first destroy"); + w2.destroy(); + test.assertEqual(widgetCount2(), widgetStartCount2, "2nd window has correct number of child elements after second destroy"); + + closeBrowserWindow(browserWindow, function() { + doneTest(); + }); + }}); + }); + + // test window closing + tests.push(function testWindowClosing() { + // 1/ Create a new widget + let w1Opts = { + id:"first", + label: "first widget", + content: "first content", + contentScript: "self.port.on('event', function () self.port.emit('event'))" + }; + let widget = testSingleWidget(w1Opts); + let windows = require("windows").browserWindows; + + // 2/ Retrieve a WidgetView for the initial browser window + let acceptDetach = false; + let mainView = widget.getView(windows.activeWindow); + test.assert(mainView, "Got first widget view"); + mainView.on("detach", function () { + // 8/ End of our test. Accept detach event only when it occurs after + // widget.destroy() + if (acceptDetach) + doneTest(); + else + test.fail("View on initial window should not be destroyed"); + }); + mainView.port.on("event", function () { + // 7/ Receive event sent during 6/ and cleanup our test + acceptDetach = true; + widget.destroy(); + }); + + // 3/ First: open a new browser window + windows.open({ + url: "about:blank", + onOpen: function(window) { + // 4/ Retrieve a WidgetView for this new window + let view = widget.getView(window); + test.assert(view, "Got second widget view"); + view.port.on("event", function () { + test.fail("We should not receive event on the detach view"); + }); + view.on("detach", function () { + // The related view is destroyed + // 6/ Send a custom event + test.assertRaises(function () { + view.port.emit("event"); + }, + /The widget has been destroyed and can no longer be used./, + "emit on a destroyed view should throw"); + widget.port.emit("event"); + }); + + // 5/ Destroy this window + window.close(); + } + }); + }); + + tests.push(function testAddonBarHide() { + // Hide the addon-bar + browserWindow.setToolbarVisibility(container(), false); + + // Then open a browser window and verify that the addon-bar remains hidden + tabBrowser.addTab("about:blank", { inNewWindow: true, onLoad: function(e) { + let browserWindow = e.target.defaultView; + let doc = browserWindow.document; + function container2() doc.getElementById("addon-bar"); + function widgetCount2() container2() ? container2().childNodes.length : 0; + let widgetStartCount2 = widgetCount2(); + + let w1Opts = {id:"first", label: "first widget", content: "first content"}; + let w1 = testSingleWidget(w1Opts); + test.assertEqual(widgetCount2(), widgetStartCount2 + 1, "2nd window has correct number of child elements after widget creation"); + + w1.destroy(); + test.assertEqual(widgetCount2(), widgetStartCount2, "2nd window has correct number of child elements after widget destroy"); + + test.assert(container().collapsed, "1st window has an hidden addon-bar"); + test.assert(container2().collapsed, "2nd window has an hidden addon-bar"); + + browserWindow.setToolbarVisibility(container(), true); + + closeBrowserWindow(browserWindow, function() { + doneTest(); + }); + }}); + }); + + // test widget.width + tests.push(function testWidgetWidth() testSingleWidget({ + id: "text", + label: "test widget.width", + content: "test width", + width: 200, + contentScript: "self.postMessage(1)", + contentScriptWhen: "ready", + onMessage: function(message) { + test.assertEqual(this.width, 200); + + let node = widgetNode(0); + test.assertEqual(this.width, node.style.minWidth.replace("px", "")); + test.assertEqual(this.width, node.firstElementChild.style.width.replace("px", "")); + this.width = 300; + test.assertEqual(this.width, node.style.minWidth.replace("px", "")); + test.assertEqual(this.width, node.firstElementChild.style.width.replace("px", "")); + + this.destroy(); + doneTest(); + } + })); + + // test click handler not respond to right-click + let clickCount = 0; + tests.push(function testNoRightClick() testSingleWidget({ + id: "click-content", + label: "click test widget - content", + content: "
foo
", + contentScript: "var evt = document.createEvent('MouseEvents'); " + + "evt.initMouseEvent('click', true, true, window, " + + " 0, 0, 0, 0, 0, false, false, false, false, 2, null); " + + "document.getElementById('me').dispatchEvent(evt); " + + "evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('click', true, true ); " + + "document.getElementById('me').dispatchEvent(evt); " + + "evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('mouseover', true, true ); " + + "document.getElementById('me').dispatchEvent(evt);", + contentScriptWhen: "end", + onClick: function() clickCount++, + onMouseover: function() { + test.assertEqual(clickCount, 1, "right click wasn't sent to click handler"); + this.destroy(); + doneTest(); + } + })); + + // kick off test execution + doneTest(); +}; + +exports.testPanelWidget1 = function testPanelWidget1(test) { + const widgets = require("widget"); + + let widget1 = widgets.Widget({ + id: "panel1", + label: "panel widget 1", + content: "
foo
", + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('click', true, true ); " + + "document.body.dispatchEvent(evt);", + contentScriptWhen: "end", + panel: require("panel").Panel({ + contentURL: "data:text/html,Look ma, a panel!", + onShow: function() { + widget1.destroy(); + test.pass("panel displayed on click"); + test.done(); + } + }) + }); + test.waitUntilDone(); +}; + +exports.testPanelWidget2 = function testPanelWidget2(test) { + const widgets = require("widget"); + test.assertRaises( + function() { + widgets.Widget({ + id: "panel2", + label: "panel widget 2", + panel: {} + }); + }, + "The option \"panel\" must be one of the following types: null, undefined, object", + "widget.panel must be a Panel object" + ); +}; + +exports.testPanelWidget3 = function testPanelWidget3(test) { + const widgets = require("widget"); + let onClickCalled = false; + let widget3 = widgets.Widget({ + id: "panel3", + label: "panel widget 3", + content: "
foo
", + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('click', true, true ); " + + "document.body.firstElementChild.dispatchEvent(evt);", + contentScriptWhen: "end", + onClick: function() { + onClickCalled = true; + this.panel.show(); + }, + panel: require("panel").Panel({ + contentURL: "data:text/html,Look ma, a panel!", + onShow: function() { + test.assert( + onClickCalled, + "onClick called on click for widget with both panel and onClick" + ); + widget3.destroy(); + test.done(); + } + }) + }); + test.waitUntilDone(); +}; + +exports.testWidgetMessaging = function testWidgetMessaging(test) { + test.waitUntilDone(); + let origMessage = "foo"; + const widgets = require("widget"); + let widget = widgets.Widget({ + id: "foo", + label: "foo", + content: "baz", + contentScriptWhen: "end", + contentScript: "self.on('message', function(data) { self.postMessage(data); }); self.postMessage('ready');", + onMessage: function(message) { + if (message == "ready") + widget.postMessage(origMessage); + else { + test.assertEqual(origMessage, message); + widget.destroy(); + test.done(); + } + } + }); +}; + +exports.testWidgetViews = function testWidgetViews(test) { + test.waitUntilDone(); + const widgets = require("widget"); + let widget = widgets.Widget({ + id: "foo", + label: "foo", + content: "baz", + contentScriptWhen: "ready", + contentScript: "self.on('message', function(data) self.postMessage(data)); self.postMessage('ready')", + onAttach: function(view) { + test.pass("WidgetView created"); + view.on("message", function () { + test.pass("Got message in WidgetView"); + widget.destroy(); + }); + view.on("detach", function () { + test.pass("WidgetView destroyed"); + test.done(); + }); + } + }); + +}; + +exports.testWidgetViewsUIEvents = function testWidgetViewsUIEvents(test) { + test.waitUntilDone(); + const widgets = require("widget"); + let view = null; + let widget = widgets.Widget({ + id: "foo", + label: "foo", + content: "
foo
", + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('click', true, true ); " + + "document.getElementById('me').dispatchEvent(evt);", + contentScriptWhen: "ready", + onAttach: function(attachView) { + view = attachView; + test.pass("Got attach event"); + }, + onClick: function (eventView) { + test.assertEqual(view, eventView, + "event first argument is equal to the WidgetView"); + let view2 = widget.getView(require("windows").browserWindows.activeWindow); + test.assertEqual(view, view2, + "widget.getView return the same WidgetView"); + widget.destroy(); + test.done(); + } + }); +}; + +exports.testWidgetViewsCustomEvents = function testWidgetViewsCustomEvents(test) { + test.waitUntilDone(); + const widgets = require("widget"); + let widget = widgets.Widget({ + id: "foo", + label: "foo", + content: "
foo
", + contentScript: "self.port.emit('event', 'ok');", + contentScriptWhen: "ready", + onAttach: function(view) { + view.port.on("event", function (data) { + test.assertEqual(data, "ok", + "event argument is valid on WidgetView"); + }); + }, + }); + widget.port.on("event", function (data) { + test.assertEqual(data, "ok", + "event argument is valid on Widget"); + widget.destroy(); + test.done(); + }); +}; + +exports.testWidgetViewsTooltip = function testWidgetViewsTooltip(test) { + test.waitUntilDone(); + const widgets = require("widget"); + + let widget = new widgets.Widget({ + id: "foo", + label: "foo", + content: "foo" + }); + let view = widget.getView(require("windows").browserWindows.activeWindow); + widget.tooltip = null; + test.assertEqual(view.tooltip, "foo", + "view tooltip defaults to base widget label"); + test.assertEqual(widget.tooltip, "foo", + "tooltip defaults to base widget label"); + widget.destroy(); + test.done(); +}; + +exports.testWidgetMove = function testWidgetMove(test) { + test.waitUntilDone(); + + let windowUtils = require("window-utils"); + let widgets = require("widget"); + + let browserWindow = windowUtils.activeBrowserWindow; + let doc = browserWindow.document; + + let label = "unique-widget-label"; + let origMessage = "message after node move"; + let gotFirstReady = false; + + let widget = widgets.Widget({ + id: "foo", + label: label, + content: "baz", + contentScriptWhen: "ready", + contentScript: "self.on('message', function(data) { self.postMessage(data); }); self.postMessage('ready');", + onMessage: function(message) { + if (message == "ready") { + if (!gotFirstReady) { + test.pass("Got first ready event"); + let widgetNode = doc.querySelector('toolbaritem[label="' + label + '"]'); + let parent = widgetNode.parentNode; + parent.insertBefore(widgetNode, parent.firstChild); + gotFirstReady = true; + } else { + test.pass("Got second ready event"); + widget.postMessage(origMessage); + } + } + else { + test.assertEqual(origMessage, message, "Got message after node move"); + widget.destroy(); + test.done(); + } + } + }); +}; + +/* +The bug is exhibited when a widget with HTML content has it's content +changed to new HTML content with a pound in it. Because the src of HTML +content is converted to a data URI, the underlying iframe doesn't +consider the content change a navigation change, so doesn't load +the new content. +*/ +exports.testWidgetWithPound = function testWidgetWithPound(test) { + test.waitUntilDone(); + + function getWidgetContent(widget) { + let windowUtils = require("window-utils"); + let browserWindow = windowUtils.activeBrowserWindow; + let doc = browserWindow.document; + let widgetNode = doc.querySelector('toolbaritem[label="' + widget.label + '"]'); + test.assert(widgetNode, 'found widget node in the front-end'); + return widgetNode.firstChild.contentDocument.body.innerHTML; + } + + let widgets = require("widget"); + let count = 0; + let widget = widgets.Widget({ + id: "1", + label: "foo", + content: "foo", + contentScript: "window.addEventListener('load', self.postMessage, false);", + onMessage: function() { + count++; + if (count == 1) { + widget.content = "foo#"; + } + else { + test.assertEqual(getWidgetContent(widget), "foo#", "content updated to pound?"); + widget.destroy(); + test.done(); + } + } + }); +}; + +/******************* helpers *********************/ + +// Helper for calling code at window close +function closeBrowserWindow(window, callback) { + require("timer").setTimeout(function() { + window.addEventListener("unload", function onUnload() { + window.removeEventListener("unload", onUnload, false); + callback(); + }, false); + window.close(); + }, 0); +} + +// ADD NO TESTS BELOW THIS LINE! /////////////////////////////////////////////// + +// If the module doesn't support the app we're being run in, require() will +// throw. In that case, remove all tests above from exports, and add one dummy +// test that passes. +try { + require("widget"); +} +catch (err) { + // This bug should be mentioned in the error message. + let bug = "https://bugzilla.mozilla.org/show_bug.cgi?id=560716"; + if (err.message.indexOf(bug) < 0) + throw err; + for (let [prop, val] in Iterator(exports)) { + if (/^test/.test(prop) && typeof(val) === "function") + delete exports[prop]; + } + exports.testAppNotSupported = function (test) { + test.pass("context-menu does not support this application."); + }; +} + diff --git a/tools/addon-sdk-1.3/packages/addon-kit/tests/test-windows.js b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-windows.js new file mode 100644 index 0000000..befe353 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/addon-kit/tests/test-windows.js @@ -0,0 +1,354 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Felipe Gomes (Original author) + * Irakli Gozalishvili + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const {Cc, Ci} = require("chrome"); + +exports.testOpenAndCloseWindow = function(test) { + + test.waitUntilDone(); + let windows = require("windows").browserWindows; + + test.assertEqual(windows.length, 1, "Only one window open"); + + windows.open({ + url: "data:text/html,windows API test", + onOpen: function(window) { + test.assertEqual(this, windows, + "The 'this' object is the windows object."); + test.assertEqual(window.tabs.length, 1, "Only one tab open"); + test.assertEqual(windows.length, 2, "Two windows open"); + window.tabs.activeTab.on('ready', function onReady(tab) { + tab.removeListener('ready', onReady); + test.assert(window.title.indexOf("windows API test") != -1, + "URL correctly loaded"); + window.close(); + }); + }, + onClose: function(window) { + test.assertEqual(window.tabs.length, 0, "Tabs were cleared"); + test.assertEqual(windows.length, 1, "Only one window open"); + test.done(); + } + }); +}; + +exports.testAutomaticDestroy = function(test) { + + test.waitUntilDone(); + let windows = require("windows").browserWindows; + + // Create a second windows instance that we will unload + let called = false; + let loader = test.makeSandboxedLoader(); + let windows2 = loader.require("windows").browserWindows; + windows2.on("open", function() { + called = true; + }); + + loader.unload(); + + // Fire a windows event and check that this unloaded instance is inactive + windows.open({ + url: "data:text/html,foo", + onOpen: function(window) { + require("timer").setTimeout(function () { + test.assert(!called, + "Unloaded windows instance is destroyed and inactive"); + window.close(function () { + test.done(); + }); + }); + } + }); + +}; + +exports.testOnOpenOnCloseListeners = function(test) { + test.waitUntilDone(); + let windows = require("windows").browserWindows; + + test.assertEqual(windows.length, 1, "Only one window open"); + + let received = { + listener1: false, + listener2: false, + listener3: false, + listener4: false + } + + function listener1() { + test.assertEqual(this, windows, "The 'this' object is the windows object."); + if (received.listener1) + test.fail("Event received twice"); + received.listener1 = true; + } + + function listener2() { + if (received.listener2) + test.fail("Event received twice"); + received.listener2 = true; + } + + function listener3() { + test.assertEqual(this, windows, "The 'this' object is the windows object."); + if (received.listener3) + test.fail("Event received twice"); + received.listener3 = true; + } + + function listener4() { + if (received.listener4) + test.fail("Event received twice"); + received.listener4 = true; + } + + windows.on('open', listener1); + windows.on('open', listener2); + windows.on('close', listener3); + windows.on('close', listener4); + + function verify() { + test.assert(received.listener1, "onOpen handler called"); + test.assert(received.listener2, "onOpen handler called"); + test.assert(received.listener3, "onClose handler called"); + test.assert(received.listener4, "onClose handler called"); + + windows.removeListener('open', listener1); + windows.removeListener('open', listener2); + windows.removeListener('close', listener3); + windows.removeListener('close', listener4); + test.done(); + } + + + windows.open({ + url: "data:text/html,foo", + onOpen: function(window) { + window.close(verify); + } + }); +}; + +exports.testWindowTabsObject = function(test) { + test.waitUntilDone(); + let windows = require("windows").browserWindows; + + windows.open({ + url: "data:text/html,tab 1", + onOpen: function onOpen(window) { + test.assertEqual(window.tabs.length, 1, "Only 1 tab open"); + + window.tabs.open({ + url: "data:text/html,tab 2", + inBackground: true, + onReady: function onReady(newTab) { + test.assertEqual(window.tabs.length, 2, "New tab open"); + test.assertEqual(newTab.title, "tab 2", "Correct new tab title"); + test.assertEqual(window.tabs.activeTab.title, "tab 1", "Correct active tab"); + + let i = 1; + for each (let tab in window.tabs) + test.assertEqual(tab.title, "tab " + i++, "Correct title"); + + window.close(); + } + }); + }, + onClose: function onClose(window) { + test.assertEqual(window.tabs.length, 0, "No more tabs on closed window"); + test.done(); + } + }); +}; + +exports.testActiveWindow = function(test) { + const xulApp = require("xul-app"); + if (xulApp.versionInRange(xulApp.platformVersion, "1.9.2", "1.9.2.*")) { + test.pass("This test is disabled on 3.6. For more information, see bug 598525"); + return; + } + + let windows = require("windows").browserWindows; + + // API window objects + let window2, window3; + + // Raw window objects + let nonBrowserWindow, rawWindow2, rawWindow3; + + // Find the first non-browser window: probably the test runner window + let wm = Cc["@mozilla.org/appshell/window-mediator;1"] + .getService(Ci.nsIWindowMediator); + let winEnum = wm.getEnumerator(""); + while (winEnum.hasMoreElements()) { + let win = winEnum.getNext(); + if (win.document.documentElement.getAttribute("windowtype") != "navigator:browser") { + nonBrowserWindow = win; + break; + } + } + if (!nonBrowserWindow) { + test.fail("This test can't proceed without a non-browser window"); + return; + } + + test.waitUntilDone(); + + let testSteps = [ + function() { + test.assertEqual(windows.length, 3, "Correct number of browser windows"); + let count = 0; + for (let window in windows) + count++; + test.assertEqual(count, 3, "Correct number of windows returned by iterator"); + + rawWindow2.focus(); + continueAfterFocus(rawWindow2); + }, + function() { + nonBrowserWindow.focus(); + continueAfterFocus(nonBrowserWindow); + }, + function() { + /** + * Bug 614079: This test fails intermittently on some specific linux + * environnements, without being able to reproduce it in same + * distribution with same window manager. + * Disable it until being able to reproduce it easily. + + // On linux, focus is not consistent, so we can't be sure + // what window will be on top. + // Here when we focus "non-browser" window, + // Any Browser window may be selected as "active". + test.assert(windows.activeWindow == window2 || windows.activeWindow == window3, + "Non-browser windows aren't handled by this module"); + */ + window2.activate(); + continueAfterFocus(rawWindow2); + }, + function() { + test.assertEqual(windows.activeWindow.title, window2.title, "Correct active window - 2"); + window3.activate(); + continueAfterFocus(rawWindow3); + }, + function() { + test.assertEqual(windows.activeWindow.title, window3.title, "Correct active window - 3"); + nonBrowserWindow.focus(); + finishTest(); + } + ]; + + windows.open({ + url: "data:text/html,window 2", + onOpen: function(window) { + window2 = window; + rawWindow2 = wm.getMostRecentWindow("navigator:browser"); + + windows.open({ + url: "data:text/html,window 3", + onOpen: function(window) { + window.tabs.activeTab.on('ready', function onReady() { + window3 = window; + rawWindow3 = wm.getMostRecentWindow("navigator:browser"); + nextStep() + }); + } + }); + } + }); + + function nextStep() { + if (testSteps.length > 0) + testSteps.shift()(); + } + + function continueAfterFocus(targetWindow) { + + // Based on SimpleTest.waitForFocus + var fm = Cc["@mozilla.org/focus-manager;1"]. + getService(Ci.nsIFocusManager); + + var childTargetWindow = {}; + fm.getFocusedElementForWindow(targetWindow, true, childTargetWindow); + childTargetWindow = childTargetWindow.value; + + var focusedChildWindow = {}; + if (fm.activeWindow) { + fm.getFocusedElementForWindow(fm.activeWindow, true, focusedChildWindow); + focusedChildWindow = focusedChildWindow.value; + } + + var focused = (focusedChildWindow == childTargetWindow); + if (focused) { + nextStep(); + } else { + childTargetWindow.addEventListener("focus", function focusListener() { + childTargetWindow.removeEventListener("focus", focusListener, true); + nextStep(); + }, true); + } + + } + + function finishTest() { + window3.close(function() { + window2.close(function() { + test.done(); + }); + }); + } +}; + +// If the module doesn't support the app we're being run in, require() will +// throw. In that case, remove all tests above from exports, and add one dummy +// test that passes. +try { + require("windows"); +} +catch (err) { + // This bug should be mentioned in the error message. + let bug = "https://bugzilla.mozilla.org/show_bug.cgi?id=571449"; + if (err.message.indexOf(bug) < 0) + throw err; + for (let [prop, val] in Iterator(exports)) { + if (/^test/.test(prop) && typeof(val) === "function") + delete exports[prop]; + } + exports.testAppNotSupported = function (test) { + test.pass("the windows module does not support this application."); + }; +} diff --git a/tools/addon-sdk-1.3/packages/api-utils/README.md b/tools/addon-sdk-1.3/packages/api-utils/README.md new file mode 100644 index 0000000..e973e4c --- /dev/null +++ b/tools/addon-sdk-1.3/packages/api-utils/README.md @@ -0,0 +1,31 @@ +API Utils provides a basic CommonJS infrastructure for +developing traditional XULRunner add-ons and applications. It is +the basis for the Add-on SDK. + +To address issues present in traditional add-on development, +API Utils provides mechanisms for: + +* writing and executing test cases, inspired by Python's [nose][] + package, +* tracking JS objects of interest to aid in memory profiling and leak + detection, +* registering callbacks that perform cleanup tasks when modules are + unloaded, +* easily reporting errors with full stack tracebacks. + +API Utils also has the following characteristics: + +* Beautiful, concise documentation. +* A rigorous test suite ensuring that the library doesn't break as the + Mozilla platform evolves. +* Solid developer ergonomics ensuring that developers can easily find + out why something they're doing isn't working. + +API Utils is intended to be very small and only contain the bare +minimum of functionality that all add-ons need. + +Note that the API Utils package has not fully stabilized yet, meaning that +we do still expect to make incompatible changes to its APIs in future releases +of the SDK. + + [nose]: http://code.google.com/p/python-nose/ diff --git a/tools/addon-sdk-1.3/packages/api-utils/data/bootstrap-remote-process.js b/tools/addon-sdk-1.3/packages/api-utils/data/bootstrap-remote-process.js new file mode 100644 index 0000000..6fb17eb --- /dev/null +++ b/tools/addon-sdk-1.3/packages/api-utils/data/bootstrap-remote-process.js @@ -0,0 +1,212 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Atul Varma + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +// This is the first code that's ever run in a Jetpack process. It sets up +// infrastructure and receivers needed to start a Jetpack-based addon +// in a separate process. + +// A list of scripts to inject into all new CommonJS module sandboxes. +var injectedSandboxScripts = []; + +// A table of all CommonJS modules currently loaded. +var modules = {}; + +// This object represents the chrome process, and can be used to +// communicate with it. +var chrome = { + createHandle: function() { + return createHandle(); + }, + on: function(type, listener) { + registerReceiver(type, listener); + }, + removeListener: function(type, listener) { + unregisterReceiver(type, listener); + }, + send: function(type) { + sendMessage.apply(this, arguments); + }, + call: function(name) { + var result = callMessage.apply(this, arguments); + + if (result.length > 1) + throw new Error("More than one result received for call '" + name + + "': " + result.length); + + if (result.length == 0) + throw new Error("No receiver registered for call '" + name + "'"); + + if (result[0].exception) { + throw Object.create(Error.prototype, { + message: { value: result[0].exception.message, enumerable: true }, + fileName: { value: result[0].exception.fileName, enumerable: true }, + lineNumber: { value: result[0].exception.lineNumber, enumerable: true }, + // Concatenate the stack from the other process with one from this + // process, so callers have access to the full stack. + stack: { value: result[0].exception.stack + (new Error()).stack, + enumerable: true } + }); + } + + return result[0].returnValue; + } +}; + +// Use this for really low-level debugging of this script. +function dump(msg) { + // Don't use chrome.send() to avoid infinite recursion when + // debugging chrome.send() itself. + sendMessage("dump", msg); +} + +// Taken from plain-text-console.js. +function stringify(arg) { + try { + return String(arg); + } + catch(ex) { + return ""; + } +} + +// Set up our "proxy" objects that just send messages to our parent +// process to do the real work. +var console = { + exception: function(e) { + chrome.send('console:exception', e); + }, + trace: function() { + chrome.send('console:trace', new Error()); + } +}; + +['log', 'debug', 'info', 'warn', 'error'].forEach(function(method) { + console[method] = function() { + chrome.send('console:' + method, Array.map(arguments, stringify)); + } +}); + +var memory = { + track: function() { + /* TODO */ + } +}; + +function makeRequire(base) { + var resolvedNames = {}; + + function require(name) { + // first, have we already require()d this name from this base? Just + // re-use the module + if (name && name in resolvedNames) + return resolvedNames[name].exports; + + // if not, resolve relative import names by asking the browser-process + // side for the URL/filename of the module this points to + var response = chrome.call("require", base, name); + switch (response.code) { + case "not-found": + throw new Error("Unknown module '" + name + "'."); + case "access-denied": + throw new Error("Module '" + name + "' requires chrome privileges " + + "and has no e10s adapter."); + case "error": + throw new Error("An unexpected error occurred in the chrome " + + "process."); + case "ok": + break; + default: + throw new Error("Internal error: unknown response code '" + + response.code + "'"); + }; + + // do we already have a module for this filename? + if (response.script.filename in modules) { + module = resolvedNames[name] = modules[response.script.filename]; + return module.exports; + } + + var module = createSandbox(); + + function injectScript(script) { + evalInSandbox(module, '//@line 1 "' + script.filename + + '"\n' + script.contents); + } + + injectedSandboxScripts.forEach(injectScript); + + modules[response.script.filename] = resolvedNames[name] = module; + + // Set up the globals of the sandbox. + module.exports = {}; + module.console = console; + module.memory = memory; + module.require = makeRequire(response.script.filename); + module.__url__ = response.script.filename; + + if (response.needsMessaging) + module.chrome = chrome; + + injectScript(response.script); + + return module.exports; + }; + return require; +} + +chrome.on( + "addInjectedSandboxScript", + function(name, script) { + injectedSandboxScripts.push(script); + }); + +chrome.on( + "startMain", + function(name, mainName, options) { + var mainRequire = makeRequire(null); + var main = mainRequire(mainName); + + var callbacks = { + quit: function quit(status) { + if (status === undefined) + status = "OK"; + chrome.send("quit", status); + } + }; + + if ('main' in main) + main.main(options, callbacks); + }); diff --git a/tools/addon-sdk-1.3/packages/api-utils/data/test-content-symbiont.js b/tools/addon-sdk-1.3/packages/api-utils/data/test-content-symbiont.js new file mode 100644 index 0000000..e69de29 diff --git a/tools/addon-sdk-1.3/packages/api-utils/docs/api-utils.md b/tools/addon-sdk-1.3/packages/api-utils/docs/api-utils.md new file mode 100644 index 0000000..d87acdb --- /dev/null +++ b/tools/addon-sdk-1.3/packages/api-utils/docs/api-utils.md @@ -0,0 +1,153 @@ + + + +The `api-utils` module provides some helpers useful to the SDK's high-level API +implementations. + +Introduction +------------ + +The SDK high-level API design guidelines make a number of recommendations. +This module implements some of those patterns so that your own implementations +don't need to reinvent them. + +For example, public constructors should be callable both with and without the +`new` keyword. Your module can implement this recommendation using the +`publicConstructor` function. + +Options objects or "dictionaries" are also common throughout the high-level +APIs. The guidelines recommend that public constructors should generally define +a single `options` parameter rather than defining many parameters. Since one of +the SDK's principles is to be friendly to developers, ideally all properties on +options dictionaries should be checked for correct type, and informative error +messages should be generated when clients make mistakes. With the +`validateOptions` function, your module can easily do so. + +And objects are sometimes iterable over a custom set of key/value pairs. +Such objects should have custom iterators that let consumers iterate keys, +values, or [key, value] pairs. The `addIterator` function makes it easy to do +so in a way that is consistent with the behavior of default iterators during +`for...in`, `for each...in`, and `for...in Iterator()` loops. + + +@function +Returns a function *C* that creates an instance of `privateConstructor`. *C* +may be called with or without the `new` keyword. + +The prototype of each instance returned from *C* is *C*.`prototype`, and +*C*.`prototype` is an object whose prototype is +`privateConstructor.prototype`. Instances returned from *C* are therefore +instances of both *C* and `privateConstructor`. + +Additionally, the constructor of each instance returned from *C* is *C*. + +Instances returned from *C* are automatically memory tracked using +`memory.track` under the bin name `privateConstructor.name`. + +**Example** + + function MyObject() {} + exports.MyObject = apiUtils.publicConstructor(MyObject); + +@returns {function} +A function that makes new instances of `privateConstructor`. + +@param privateConstructor {constructor} + + + +@function +A function to validate an options dictionary according to the specified +constraints. + +`map`, `is`, and `ok` are used in that order. + +The return value is 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. + +**Examples** + +A typical use: + + var opts = { foo: 1337 }; + var requirements = { + foo: { + map: function (val) val.toString(), + is: ["string"], + ok: function (val) val.length > 0, + msg: "foo must be a non-empty string." + } + }; + var validatedOpts = apiUtils.validateOptions(opts, requirements); + // validatedOpts == { foo: "1337" } + +If the key `foo` is optional and doesn't need to be mapped: + + var opts = { foo: 1337 }; + var validatedOpts = apiUtils.validateOptions(opts, { foo: {} }); + // validatedOpts == { foo: 1337 } + + opts = {}; + validatedOpts = apiUtils.validateOptions(opts, { foo: {} }); + // validatedOpts == {} + +@returns {object} +A validated options dictionary given some requirements. If any of the +requirements are not met, an exception is thrown. + +@param options {object} +The options dictionary to validate. It's not modified. If it's null or +otherwise falsey, an empty object is assumed. + +@param requirements {object} +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. The keys of that object are the following, and each is optional: + +@prop [map] {function} +A function that's passed the value of the key in the `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 is caught and discarded, +and the key's value is its value in `options`. + +@prop [is] {array} +An array containing the number of `typeof` type names. If the key's value is +none of these types it fails validation. Arrays and nulls are identified by +the special type names "array" and "null"; "object" will not match either. +No type coercion is done. + +@prop [ok] {function} +A function that is passed the key's value. If it returns false, the value +fails validation. + +@prop [msg] {string} +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. + + + +@function +Adds an iterator to the specified object that iterates keys, values, +or [key, value] pairs depending on how it is invoked, i.e.: + + for (var key in obj) { ... } // iterate keys + for each (var val in obj) { ... } // iterate values + for (var [key, val] in Iterator(obj)) { ... } // iterate pairs + +If your object only iterates either keys or values, you don't need this +function. Simply assign a generator function that iterates the keys/values +to your object's `__iterator__` property instead, f.e.: + + obj.__iterator__ = function () { for each (var i in items) yield i; } + +@param obj {object} +the object to which to add the iterator + +@param keysValsGen {function} +a generator function that yields [key, value] pairs + diff --git a/tools/addon-sdk-1.3/packages/api-utils/docs/app-strings.md b/tools/addon-sdk-1.3/packages/api-utils/docs/app-strings.md new file mode 100644 index 0000000..3e4d358 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/api-utils/docs/app-strings.md @@ -0,0 +1,61 @@ +The `app-strings` module gives you access to the host application's localized +string bundles (`.properties` files). + +The module exports the `StringBundle` constructor function. To access a string +bundle, construct an instance of `StringBundle`, passing it the bundle's URL: + + var StringBundle = require("app-strings").StringBundle; + var bundle = StringBundle("chrome://browser/locale/browser.properties"); + +To get the value of a string, call the object's `get` method, passing it +the name of the string: + + var accessKey = bundle.get("contextMenuSearchText.accesskey"); + // "S" in the en-US locale + +To get the formatted value of a string that accepts arguments, call the object's +`get` method, passing it the name of the string and an array of arguments +with which to format the string: + + var searchText = bundle.get("contextMenuSearchText", + ["universe", "signs of intelligent life"]); + // 'Search universe for "signs of intelligent life"' in the en-US locale + +To get all strings in the bundle, iterate the object, which returns arrays +of the form [name, value]: + + for (var [name, value] in Iterator(bundle)) + console.log(name + " = " + value); + +Iteration +--------- + +for (var name in bundle) { ... } + +Iterate the names of strings in the bundle. + +for each (var val in bundle) { ... } + +Iterate the values of strings in the bundle. + +for (var [name, value] in Iterator(bundle)) { ... } + +Iterate the names and values of strings in the bundle. + + + +@class +The `StringBundle` object represents a string bundle. + +@constructor +Creates a StringBundle object that gives you access to a string bundle. +@param url {string} the URL of the string bundle +@returns {StringBundle} the string bundle + + +@method Get the value of the string with the given name. +@param [name] {string} the name of the string to get +@param [args] {array} (optional) strings that replace placeholders in the string +@returns {string} the value of the string + + diff --git a/tools/addon-sdk-1.3/packages/api-utils/docs/byte-streams.md b/tools/addon-sdk-1.3/packages/api-utils/docs/byte-streams.md new file mode 100644 index 0000000..f7cffe8 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/api-utils/docs/byte-streams.md @@ -0,0 +1,64 @@ + + + +The `byte-streams` module provides streams for reading and writing bytes. + + +@class + +@constructor + Creates a binary input stream that reads bytes from a backing stream. +@param inputStream {stream} + The backing stream, an nsIInputStream. + + +@property {boolean} + True if the stream is closed. + + + +@method + Closes both the stream and its backing stream. If the stream is already + closed, an exception is thrown. + + + +@method + Reads a string from the stream. If the stream is closed, an exception is + thrown. +@param [numBytes] {number} + The number of bytes to read. If not given, the remainder of the entire stream + is read. +@returns {string} + A string containing the bytes read. If the stream is at the end, returns the + empty string. + + + + +@class + +@constructor + Creates a binary output stream that writes bytes to a backing stream. +@param outputStream {stream} + The backing stream, an nsIOutputStream. + + +@property {boolean} + True if the stream is closed. + + +@method + Closes both the stream and its backing stream. If the stream is already + closed, an exception is thrown. + + +@method + Writes a string to the stream. If the stream is closed, an exception is + thrown. +@param str {string} + The string to write. + + diff --git a/tools/addon-sdk-1.3/packages/api-utils/docs/collection.md b/tools/addon-sdk-1.3/packages/api-utils/docs/collection.md new file mode 100644 index 0000000..796289a --- /dev/null +++ b/tools/addon-sdk-1.3/packages/api-utils/docs/collection.md @@ -0,0 +1,73 @@ + + + +The `collection` module provides a simple list-like class and utilities for +using it. A collection is ordered, like an array, but its items are unique, +like a set. + + +@class +A collection object provides for...in-loop iteration. Items are yielded in the +order they were added. For example, the following code... + + var collection = require("collection"); + var c = new collection.Collection(); + c.add(1); + c.add(2); + c.add(3); + for (item in c) + console.log(item); + +... would print this to the console: + +
+  1
+  2
+  3
+
+ +Iteration proceeds over a copy of the collection made before iteration begins, +so it is safe to mutate the collection during iteration; doing so does not +affect the results of the iteration. + + +@constructor +Creates a new collection. The collection is backed by an array. +@param [array] {array} +If *array* is given, it will be used as the backing array. This way the caller +can fully control the collection. Otherwise a new empty array will be used, and +no one but the collection will have access to it. + + +@property {number} +The number of items in the collection array. + + +@method +Adds a single item or an array of items to the collection. Any items already +contained in the collection are ignored. +@param itemOrItems {object} An item or array of items. +@returns {Collection} The Collection. + + +@method +Removes a single item or an array of items from the collection. Any items not +contained in the collection are ignored. +@param itemOrItems {object} An item or array of items. +@returns {Collection} The Collection. + +
+ + +@function +Adds a collection property to the given object. Setting the property to a +scalar value empties the collection and adds the value. Setting it to an array +empties the collection and adds all the items in the array. +@param object {object} +The property will be defined on this object. +@param propName {string} +The name of the property. +@param [backingArray] {array} +If given, this will be used as the collection's backing array. + + diff --git a/tools/addon-sdk-1.3/packages/api-utils/docs/content.md b/tools/addon-sdk-1.3/packages/api-utils/docs/content.md new file mode 100644 index 0000000..b258d02 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/api-utils/docs/content.md @@ -0,0 +1,11 @@ + + +The `content` module exports three different traits [Loader][], [Worker][] and +[Symbiont][]. None of this traits is intended to be used directly by programs. +Rather, they are intended to be used by other modules that provide high +level APIs to programs or libraries. + +[Loader]:packages/api-utils/docs/content/loader.html +[Worker]:packages/api-utils/docs/content/worker.html +[Symbiont]:packages/api-utils/docs/content/symbiont.html + diff --git a/tools/addon-sdk-1.3/packages/api-utils/docs/content/loader.md b/tools/addon-sdk-1.3/packages/api-utils/docs/content/loader.md new file mode 100644 index 0000000..83b6276 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/api-utils/docs/content/loader.md @@ -0,0 +1,88 @@ + + +Loader is base trait and it provides set of core properties and associated +validations. Trait is useful for all the compositions providing high level +APIs for creating JavaScript contexts that can access web content. + +Loader is composed from the +[EventEmitter](packages/api-utils/docs/events.html) trait, therefore +instances of Loader and their descendants expose all the public properties +exposed by EventEmitter along with additional public properties: + +Value changes on all of the above mentioned properties emit `propertyChange` +events on an instances. + +**Example:** + +The following code creates a wrapper on hidden frame that reloads a web page +in frame every time `contentURL` property is changed: + + var hiddenFrames = require("hidden-frame"); + var { Loader } = require("content"); + var PageLoader = Loader.compose({ + constructor: function PageLoader(options) { + options = options || {}; + if (options.contentURL) + this.contentURL = options.contentURL; + this.on('propertyChange', this._onChange = this._onChange.bind(this)); + let self = this; + hiddenFrames.add(hiddenFrames.HiddenFrame({ + onReady: function onReady() { + let frame = self._frame = this.element; + self._emit('propertyChange', { contentURL: self.contentURL }); + } + })); + }, + _onChange: function _onChange(e) { + if ('contentURL' in e) + this._frame.setAttribute('src', this._contentURL); + } + }); + + +@class + +@property {array} +The local file URLs of content scripts to load. Content scripts specified by +this property are loaded *before* those specified by the `contentScript` +property. + + + +@property {array} +The texts of content scripts to load. Content scripts specified by this +property are loaded *after* those specified by the `contentScriptFile` property. + + + +@property {string} +When to load the content scripts. This may take one of the following +values: + +* "start": load content scripts immediately after the document +element for the page is inserted into the DOM, but before the DOM content +itself has been loaded +* "ready": load content scripts once DOM content has been loaded, +corresponding to the +[DOMContentLoaded](https://developer.mozilla.org/en/Gecko-Specific_DOM_Events) +event +* "end": load content scripts once all the content (DOM, JS, CSS, +images) for the page has been loaded, at the time the +[window.onload event](https://developer.mozilla.org/en/DOM/window.onload) +fires + + + + +@property {string} +The URL of the content loaded. + + + +@property {object} +Permissions for the content, with the following keys: +@prop script {boolean} + Whether or not to execute script in the content. Defaults to true. + + + diff --git a/tools/addon-sdk-1.3/packages/api-utils/docs/content/proxy.md b/tools/addon-sdk-1.3/packages/api-utils/docs/content/proxy.md new file mode 100644 index 0000000..012fbc7 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/api-utils/docs/content/proxy.md @@ -0,0 +1,237 @@ + + +Content scripts need access to the DOM of the pages they are attached to. +However, those pages should be considered to be hostile environments: we +have no control over any other scripts loaded by the web page that may be +executing in the same context. If the content scripts and scripts loaded +by the web page were to access the same DOM objects, there are two possible +security problems: + +First, a malicious page might redefine functions and properties of DOM +objects so they don't do what the add-on expects. For example, if a +content script calls `document.getElementById()` to retrieve a DOM +element, then a malicious page could redefine its behavior to return +something unexpected: + +

+// If the web document contains the following script:
+document.getElementById = function (str) {
+  // Overload indexOf method of all string instances
+  str.__proto__.indexOf = function () {return -1;};
+  // Overload toString method of all object instances
+  str.__proto__.__proto__.toString = function () {return "evil";};
+};
+// After the following line, the content script will be compromised:
+var node = document.getElementById("element");
+// Then your content script is totally out of control.
+
+ +Second, changes the content script made to the DOM objects would be visible +to the page, leaking information to it. + +The general approach to fixing these problems is to wrap DOM objects in +[`XrayWrappers`](https://developer.mozilla.org/en/XPCNativeWrapper) +(also know as `XPCNativeWrapper`). This guarantees that: + +* when the content script accesses DOM properties and functions it gets the +original native version of them, ignoring any modifications made by the web +page +* changes to the DOM made by the content script are not visible to scripts +running in the page. + +However, `XrayWrapper` has some limitations and bugs, which break many +popular web frameworks. In particular, you can't: + +* define attributes like `onclick`: you have to use `addEventListener` syntax +* overload native methods on DOM objects, like this: +

+proxy.addEventListener = function () {};
+
+* access named elements using properties like `window[framename]` or +`document[formname]` +* use some other features that have bugs in the `XrayWrapper` +implementation, like `mozMatchesSelector` + +The `proxy` module uses `XrayWrapper` in combination with the +experimental +[Proxy API](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Proxy) +to address both the security vulnerabilities of content scripts and the +limitations of `XrayWrapper`. + +
+  /--------------------\                           /------------------------\
+  |    Web document    |                           | Content script sandbox |
+  | http://mozilla.org |                           |     data/worker.js     |
+  |                    | require('content-proxy'). |                        |
+  | window >-----------|-     create(window)      -|-> window               |
+  \--------------------/                           \------------------------/
+
+ + +## The Big Picture ## + +The implementation defines two different kinds of proxy: + + 1. Content script proxies that wrap DOM objects that are exposed to + content scripts as described above. + 2. XrayWrapper proxies that wrap objects from content scripts before handing + them over to XrayWrapper functions. These proxies are internal + and are not exposed to content scripts or document content. + +
+  /--------------------\                           /------------------------\
+  |    Web document    |                           | Content script sandbox |
+  | http://mozilla.org |                           |     data/worker.js     |
+  |                    |                   /-------|-> myObject = {}        |
+  |                    |  /----------------v--\    |                        |
+  |                    |  | XrayWrapper Proxy |    | - document             |
+  |                    |  \---------v---------/    \----^-------------------/
+  |                    |            v                   |
+  |                    |  /-------------\  /----------\ |
+  | - document >-------|->| XrayWrapper |<-| CS proxy |-/
+  \--------------------/  \-------------/  \----------/
+
+ +Everything begins with a single call to the `create` function exported by the +content-proxy module: + + // Retrieve the unwrapped reference to the current web page window object + var win = gBrowser.contentDocument.defaultView.wrappedJSObject; + // Or in addon sdk style + var win = require("tab-browser").activeTab.linkedBrowser.contentWindow.wrappedJSObject; + // Now create a content script proxy for the window object + var windowProxy = require("api-utils/content/content-proxy").create(win); + + // We finally use this window object as sandbox prototype, + // so that all web page globals are accessible in CS too: + var contentScriptSandbox = new Cu.Sandbox(win, { + sandboxPrototype: windowProxy + }); + +Then all other proxies are created from this one. Attempts to access DOM +attributes of this proxy are trapped, and the proxy constructs and returns +content script proxies for those attributes: + + // For example, if you simply do this: + var document = window.document; + // accessing the `document` attribute will be trapped by the `window` content script + // proxy, and that proxy will that create another content script proxy for `document` + +So the main responsibility of the content script proxy implementation is to +ensure that we always return content script proxies to the content script. + +## Internal Implementation ## + +Each content script proxy keeps a reference to the `XrayWrapper` that enables +it to be sure of calling native DOM methods. + +There are two internal functions to convert between content script proxy +values and `XrayWrapper` values. + +1. __`wrap`__ takes an XrayWrapper value and wraps it in a content script +proxy if needed. + This method is called when: + * a content script accesses an attribute of a content script proxy. + * XrayWrapper code calls a callback function defined in the content +script, so that arguments passed into the function by the XrayWrapper are +converted into content script proxies. For example, if a content script +calls `addEventListener`, then the listener function will expect any arguments +to be content script proxies. +

+2. __`unwrap`__ takes an object coming from the content script context and: + * if the object is a content script proxy, unwraps it back to an +XrayWrapper reference + * if the object is not a content script proxy, wraps it in an XrayWrapper +proxy. +

+This means we can call a XrayWrapper method either with: + + * a raw XrayWrapper object. + + // The following line doesn't work if child is a content script proxy, + // it has to be a raw XrayWrapper reference + xrayWrapper.appendChild(child) + + * an XrayWrapper proxy when you pass a custom object from the content +script context. + + var myListener = { + handleEvent: function(event) { + // `event` should be a content script proxy + } + }; + // `myListener` has to be another kind of Proxy: XrayWrapper proxy, + // that aims to catch the call to `handleEvent` in order to wrap its + // arguments in a content script proxy. + xrayWrapper.addEventListener("click", myListener, false); + + +## Stack Traces ## + +The following code: + + function listener(event) { + + } + csProxy.addEventListener("message", listener, false); + +generates the following internal calls: + + -> CS Proxy:: get("addEventListener") + -> wrap(xrayWrapper.addEventListener) + -> NativeFunctionWrapper(xrayWrapper.addEventListener) + // NativeFunctionWrapper generates: + function ("message", listener, false) { + return xraywrapper.addEventListener("message", unwrap(listener), false); + } + -> unwrap(listener) + -> ContentScriptFunctionWrapper(listener) + // ContentScriptFunctionWrapper generates: + function (event) { + return listener(wrap(event)); + } + +
+ + // First, create an object from content script context + var myListener = { + handleEvent: function (event) { + + } + }; + // Then, pass this object as an argument to a CS proxy method + window.addEventListener("message", myListener, false); + + // Generates the following internal calls: + -> CS Proxy:: get("addEventListener") + -> wrap(xrayWrapper.addEventListener) + -> NativeFunctionWrapper(xrayWrapper.addEventListener) + // Generate the following function: + function ("message", myListener, false) { + return xraywrapper.addEventListener("message", unwrap(myListener), false); + } + -> unwrap(myListener) + -> ContentScriptObjectWrapper(myListener) + // Generate an XrayWrapper proxy and give it to xrayWrapper method. + // Then when native code fires an event, the proxy will catch it: + -> XrayWrapper Proxy:: get("handleEvent") + -> unwrap(myListener.handleEvent) + -> ContentScriptFunctionWrapper(myListener.handleEvent) + // Generate following function: + function (event) { + return myListener.handleEvent(wrap(event)); + } + + + +@function + Create a content script proxy.
+ Doesn't create a proxy if we are not able to create a XrayWrapper for + this object: for example, if the object comes from system principal. + +@param object {Object} + The object to proxify. + +@returns {Object} + A content script proxy that wraps `object`. +
diff --git a/tools/addon-sdk-1.3/packages/api-utils/docs/content/symbiont.md b/tools/addon-sdk-1.3/packages/api-utils/docs/content/symbiont.md new file mode 100644 index 0000000..7519e52 --- /dev/null +++ b/tools/addon-sdk-1.3/packages/api-utils/docs/content/symbiont.md @@ -0,0 +1,136 @@ + + + + +This module is not intended to be used directly by programs. Rather, it is +intended to be used by other modules that provide APIs to programs. + + +This module exports `Symbiont` trait that can be used for creating JavaScript +contexts that can access web content in host application frames (i.e. XUL +` +

+ + diff --git a/tools/addon-sdk-1.4/packages/addon-kit/tests/test-context-menu.js b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-context-menu.js new file mode 100644 index 0000000..157edf1 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-context-menu.js @@ -0,0 +1,2075 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Drew Willcoxon (Original Author) + * Matteo Ferretti + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +let {Cc,Ci} = require("chrome"); +const { Loader } = require('./helpers'); + +// These should match the same constants in the module. +const ITEM_CLASS = "jetpack-context-menu-item"; +const SEPARATOR_ID = "jetpack-context-menu-separator"; +const OVERFLOW_THRESH_DEFAULT = 10; +const OVERFLOW_THRESH_PREF = + "extensions.addon-sdk.context-menu.overflowThreshold"; +const OVERFLOW_MENU_ID = "jetpack-content-menu-overflow-menu"; +const OVERFLOW_POPUP_ID = "jetpack-content-menu-overflow-popup"; + +const TEST_DOC_URL = module.uri.replace(/\.js$/, ".html"); + + +// Destroying items that were previously created should cause them to be absent +// from the menu. +exports.testConstructDestroy = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + // Create an item. + let item = new loader.cm.Item({ label: "item" }); + test.assertEqual(item.parentMenu, null, "item's parent menu should be null"); + + 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]); + test.done(); + }); + }); +}; + + +// Destroying an item twice should not cause an error. +exports.testDestroyTwice = function (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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], []); + 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 (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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, []); + test.done(); + }); + }); +}; + + +// Selection contexts should cause items to appear when a selection exists. +exports.testSelectionContextMatch = function (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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], []); + test.done(); + }); + }); +}; + + +// Selection contexts should not cause items to appear when a selection does +// not exist. +exports.testSelectionContextNoMatch = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let item = loader.cm.Item({ + label: "item", + context: loader.cm.SelectionContext() + }); + + test.showMenu(null, function (popup) { + test.checkMenu([], [item], []); + test.done(); + }); +}; + + +// URL contexts should cause items to appear on pages that match. +exports.testURLContextMatch = function (test) { + test = new TestHelper(test); + 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"]) + }) + ]; + + 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 (test) { + test = new TestHelper(test); + 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"]) + }) + ]; + + test.withTestDoc(function (window, doc) { + test.showMenu(null, function (popup) { + test.checkMenu([], 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. +exports.testURLContextRemove = function (test) { + test = new TestHelper(test); + 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");', + onMessage: function (msg) { + test.assert(shouldBeEvaled, + "content script should be evaluated when expected"); + shouldBeEvaled = false; + test.done(); + } + }); + + test.withTestDoc(function (window, doc) { + shouldBeEvaled = true; + item.context.remove(context); + }); +}; + + +// Adding a non-matching URL context after its item is created and the page is +// loaded should cause the item's worker to be destroyed. +exports.testURLContextAdd = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let item = loader.cm.Item({ label: "item" }); + + test.withTestDoc(function (window, doc) { + let privatePropsKey = loader.globalScope.PRIVATE_PROPS_KEY; + let workerReg = item.valueOf(privatePropsKey)._workerReg; + + let found = false; + for each (let winWorker in workerReg.winWorkers) { + if (winWorker.win === window) { + found = true; + break; + } + } + this.test.assert(found, "window should be present in worker registry"); + + item.context.add(loader.cm.URLContext("*.bogus.com")); + + for each (let winWorker in workerReg.winWorkers) + this.test.assertNotEqual(winWorker.win, window, + "window should not be present in worker registry"); + + test.done(); + }); +}; + + +// Content contexts that return true should cause their items to be present +// in the menu. +exports.testContentContextMatch = function (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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], []); + 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 (test) { + test = new TestHelper(test); + 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], [], []); + test.assertEqual(item.label, "second label", + "item's label should be updated"); + test.done(); + }); +}; + + +// The args passed to context listeners should be correct. +exports.testContentContextArgs = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + let callbacks = 0; + + let item = new loader.cm.Item({ + label: "item", + contentScript: 'self.on("context", function (node) {' + + ' let Ci = Components.interfaces;' + + ' self.postMessage(node instanceof Ci.nsIDOMHTMLElement);' + + ' return false;' + + '});', + onMessage: function (isElt) { + test.assert(isElt, "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 (test) { + test = new TestHelper(test); + 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], []); + test.done(); + }); + }); +}; + + +// Once a context is removed, it should no longer cause its item to appear. +exports.testRemoveContext = function (test) { + test = new TestHelper(test); + 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], []); + test.done(); + }); + }); + }); +}; + + +// Lots of items should overflow into the overflow submenu. +exports.testOverflow = function (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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]); + test.done(); + }); + }); +}; + + +// Using multiple module instances to add items without causing overflow should +// work OK. Assumes OVERFLOW_THRESH_DEFAULT <= 2. +exports.testMultipleModulesAdd = function (test) { + test = new TestHelper(test); + 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([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]); + test.done(); + }); + }); + }); +}; + + +// Using multiple module instances to add items causing overflow should work OK. +exports.testMultipleModulesAddOverflow = function (test) { + test = new TestHelper(test); + 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([item1], [], 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); + 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 (test) { + test = new TestHelper(test); + 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([item1], [item0], []); + popup.hidePopup(); + + // Unload module 0. + loader0.unload(); + test.showMenu(null, function (popup) { + + // item0 should be removed from the menu. + test.checkMenu([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]); + 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 (test) { + test = new TestHelper(test); + 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([item1], [item0], []); + popup.hidePopup(); + + // Unload module 0. + loader0.unload(); + test.showMenu(null, function (popup) { + + // item0 should be removed from the menu. + test.checkMenu([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]); + 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 (test) { + test = new TestHelper(test); + 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([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]); + popup.hidePopup(); + + // Unload module 0. + loader0.unload(); + test.showMenu(null, function (popup) { + + // Both items should be removed from the menu. + test.checkMenu([], [], [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 (test) { + test = new TestHelper(test); + 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([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]); + popup.hidePopup(); + + // Unload module 0. + loader0.unload(); + test.showMenu(null, function (popup) { + + // Both items should be removed from the menu. + test.checkMenu([], [], [item0, item1]); + test.done(); + }); + }); + }); +}; + + +// Test interactions between a loaded module, unloading another module, and the +// menu separator and overflow submenu. +exports.testMultipleModulesAddRemove = function (test) { + test = new TestHelper(test); + 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]); + 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]); + test.done(); + }); + }); + }); +}; + + +// An item's click listener should work. +exports.testItemClick = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "item", + data: "item data", + contentScript: 'self.on("click", function (node, data) {' + + ' let Ci = Components.interfaces;' + + ' self.postMessage({' + + ' isElt: node instanceof Ci.nsIDOMHTMLElement,' + + ' data: data' + + ' });' + + '});', + onMessage: function (data) { + test.assertEqual(this, item, "`this` inside onMessage should be item"); + test.assert(data.isElt, "node should be an HTML element"); + test.assertEqual(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 (test) { + // Create a top-level menu, submenu, and item, like this: + // topMenu -> submenu -> item + // Click the item and make sure the click bubbles. + test = new TestHelper(test); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "submenu item", + data: "submenu item data" + }); + + let submenu = new loader.cm.Menu({ + label: "submenu", + items: [item] + }); + + let topMenu = new loader.cm.Menu({ + label: "top menu", + contentScript: 'self.on("click", function (node, data) {' + + ' let Ci = Components.interfaces;' + + ' self.postMessage({' + + ' isAnchor: node instanceof Ci.nsIDOMHTMLAnchorElement,' + + ' data: data' + + ' });' + + '});', + onMessage: function (data) { + test.assertEqual(this, topMenu, "`this` inside top menu should be menu"); + test.assert(data.isAnchor, "Clicked node should be anchor"); + test.assertEqual(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 (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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(); + }); +}; + + +// Existing context menu modifications should apply to new windows. +exports.testNewWindow = function (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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([], [], []); + test.done(); + }); + }); + }); +}; + + +// Items in the context menu should be sorted according to locale. +exports.testSorting = function (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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); + test.done(); + }); + }); +}; + + +// The binary search of insertionPoint should work OK. +exports.testInsertionPoint = function (test) { + function mockElts(labels) { + return labels.map(function (label) { + return { label: label, getAttribute: function (l) label }; + }); + } + + test = new TestHelper(test); + let loader = test.newLoader(); + let insertionPoint = loader.globalScope.insertionPoint; + + let ip = insertionPoint("a", []); + test.assertStrictEqual(ip, null, "Insertion point should be null"); + + ip = insertionPoint("a", mockElts(["b"])); + test.assertEqual(ip.label, "b", "Insertion point should be 'b'"); + + ip = insertionPoint("c", mockElts(["b"])); + test.assertStrictEqual(ip, null, "Insertion point should be null"); + + ip = insertionPoint("b", mockElts(["a", "c"])); + test.assertEqual(ip.label, "c", "Insertion point should be 'c'"); + + ip = insertionPoint("c", mockElts(["a", "b", "d"])); + test.assertEqual(ip.label, "d", "Insertion point should be 'd'"); + + ip = insertionPoint("a", mockElts(["b", "c", "d"])); + test.assertEqual(ip.label, "b", "Insertion point should be 'b'"); + + ip = insertionPoint("d", mockElts(["a", "b", "c"])); + test.assertStrictEqual(ip, null, "Insertion point should be null"); + + 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 (test) { + test = new TestHelper(test); + 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) { + test.assertEqual(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 (test) { + test = new TestHelper(test); + 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(); + }); + }); +}; + + +// Setting an item's label before the menu is ever shown should correctly change +// its label and, if necessary, its order within the menu. +exports.testSetLabelBeforeShow = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let items = [ + new loader.cm.Item({ label: "a" }), + new loader.cm.Item({ label: "b" }) + ] + items[0].label = "z"; + test.assertEqual(items[0].label, "z"); + + test.showMenu(null, function (popup) { + test.checkMenu([items[1], items[0]], [], []); + test.done(); + }); +}; + + +// Setting an item's label after the menu is shown should correctly change its +// label and, if necessary, its order within the menu. +exports.testSetLabelAfterShow = function (test) { + test = new TestHelper(test); + 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"; + test.assertEqual(items[0].label, "z"); + test.showMenu(null, function (popup) { + test.checkMenu([items[1], items[0]], [], []); + test.done(); + }); + }); +}; + + +// Setting an item's label before the menu is ever shown should correctly change +// its label and, if necessary, its order within the menu if the item is in the +// overflow submenu. +exports.testSetLabelBeforeShowOverflow = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let prefs = loader.loader.require("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"; + test.assertEqual(items[0].label, "z"); + + test.showMenu(null, function (popup) { + test.checkMenu([items[1], items[0]], [], []); + prefs.set(OVERFLOW_THRESH_PREF, OVERFLOW_THRESH_DEFAULT); + test.done(); + }); +}; + + +// Setting an item's label after the menu is shown should correctly change its +// label and, if necessary, its order within the menu if the item is in the +// overflow submenu. +exports.testSetLabelAfterShowOverflow = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let prefs = loader.loader.require("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"; + test.assertEqual(items[0].label, "z"); + test.showMenu(null, function (popup) { + test.checkMenu([items[1], items[0]], [], []); + prefs.set(OVERFLOW_THRESH_PREF, OVERFLOW_THRESH_DEFAULT); + test.done(); + }); + }); +}; + + +// Setting the label of an item in a Menu should work. +exports.testSetLabelMenuItem = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let menu = loader.cm.Menu({ + label: "menu", + items: [loader.cm.Item({ label: "a" })] + }); + menu.items[0].label = "z"; + + test.assertEqual(menu.items[0].label, "z"); + + test.showMenu(null, function (popup) { + test.checkMenu([menu], [], []); + test.done(); + }); +}; + + +// Menu.addItem() should work. +exports.testMenuAddItem = function (test) { + test = new TestHelper(test); + 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" })); + + test.assertEqual(menu.items.length, 3, + "menu should have correct number of items"); + for (let i = 0; i < 3; i++) { + test.assertEqual(menu.items[i].label, "item " + i, + "item label should be correct"); + test.assertEqual(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 (test) { + test = new TestHelper(test); + 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); + + test.assertEqual(menu.items.length, 2, + "menu should have correct number of items"); + for (let i = 0; i < 2; i++) { + test.assertEqual(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 (test) { + test = new TestHelper(test); + 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); + + test.assertEqual(subitem.parentMenu, null, + "item's parent menu should be correct"); + + test.assertEqual(menu.items.length, 2, + "menu should have correct number of items"); + test.assertEqual(menu.items[0].label, "item 0", + "item label should be correct"); + test.assertEqual(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 (test) { + test = new TestHelper(test); + 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); + + test.assertEqual(menu0.items.length, 0, + "menu should have correct number of items"); + + test.assertEqual(menu1.items.length, 1, + "menu should have correct number of items"); + test.assertEqual(menu1.items[0].label, "item", + "item label should be correct"); + + test.assertEqual(subitem.parentMenu, menu1, + "item's parent menu should be correct"); + + test.showMenu(null, function (popup) { + test.checkMenu([menu0, menu1], [], []); + test.done(); + }); +}; + + +// Destroying an item should remove it from its parent menu. +exports.testMenuItemDestroy = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let subitem = loader.cm.Item({ label: "item" }); + let menu = loader.cm.Menu({ + label: "menu", + items: [subitem] + }); + subitem.destroy(); + + test.assertEqual(menu.items.length, 0, + "menu should have correct number of items"); + test.assertEqual(subitem.parentMenu, null, + "item's parent menu should be correct"); + + test.showMenu(null, function (popup) { + test.checkMenu([menu], [], []); + test.done(); + }); +}; + + +// Setting Menu.items should work. +exports.testMenuItemsSetter = function (test) { + test = new TestHelper(test); + 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" }) + ]; + + test.assertEqual(menu.items.length, 3, + "menu should have correct number of items"); + for (let i = 0; i < 3; i++) { + test.assertEqual(menu.items[i].label, "new item " + i, + "item label should be correct"); + test.assertEqual(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 (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let item = loader.cm.Item({ label: "old item 0", data: "old" }); + item.data = "new"; + + test.assertEqual(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 (test) { + test = new TestHelper(test); + 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(); + }); + }); +}; + + +// Test image support. +exports.testItemImage = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let imageURL = require("self").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: [] }); + + test.showMenu(null, function (popup) { + test.checkMenu([item, menu], [], []); + + let imageURL2 = require("self").data.url("dummy.ico"); + item.image = imageURL2; + menu.image = imageURL2; + test.checkMenu([item, menu], [], []); + + item.image = null; + menu.image = null; + test.checkMenu([item, menu], [], []); + + test.done(); + }); +}; + + +// Menu.destroy should destroy the item tree rooted at that menu. +exports.testMenuDestroy = function (test) { + test = new TestHelper(test); + 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++; + }); + test.assertEqual(numRegistryEntries, 0, "All items should be unregistered."); + + test.showMenu(null, function (popup) { + test.checkMenu([], [], [menu]); + test.done(); + }); +}; + + +// NO TESTS BELOW THIS LINE! /////////////////////////////////////////////////// + +// Run only a dummy test if context-menu doesn't support the host app. +if (!require("xul-app").is("Firefox")) { + for (let [prop, val] in Iterator(exports)) + if (/^test/.test(prop) && typeof(val) === "function") + delete exports[prop]; + exports.testAppNotSupported = function (test) { + test.pass("context-menu does not support this application."); + }; +} + + +// 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(test) { + // default waitUntilDone timeout is 10s, which is too short on the win7 + // buildslave + test.waitUntilDone(30*1000); + this.test = test; + this.loaders = []; + this.browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"]. + getService(Ci.nsIWindowMediator). + getMostRecentWindow("navigator:browser"); +} + +TestHelper.prototype = { + get contextMenuPopup() { + return this.browserWindow.document.getElementById("contentAreaContextMenu"); + }, + + get contextMenuSeparator() { + return this.browserWindow.document.getElementById(SEPARATOR_ID); + }, + + get overflowPopup() { + return this.browserWindow.document.getElementById(OVERFLOW_POPUP_ID); + }, + + get overflowSubmenu() { + return this.browserWindow.document.getElementById(OVERFLOW_MENU_ID); + }, + + get tabBrowser() { + return this.browserWindow.gBrowser; + }, + + // Methods on the wrapped test can be called on this object. + __noSuchMethod__: function (methodName, args) { + this.test[methodName].apply(this.test, args); + }, + + // Asserts that absentItems -- an array of items that should not match the + // current context -- aren't present in the menu. + checkAbsentItems: function (presentItems, absentItems) { + for (let i = 0; i < absentItems.length; i++) { + let item = absentItems[i]; + let elt = this.getItemElt(this.contextMenuPopup, item); + + // The implementation actually hides items rather than removing or not + // adding them in the first place, but that's an implementation detail. + this.test.assert(!elt || elt.hidden, + "Item should not be present in top-level menu"); + + if (this.shouldOverflow(presentItems)) { + elt = getItemElt(this.overflowPopup, item); + this.test.assert(!elt || elt.hidden, + "Item should not be present in overflow submenu"); + } + } + }, + + // Asserts that elt, a DOM element representing item, looks OK. + checkItemElt: function (elt, item) { + let itemType = this.getItemType(item); + + switch (itemType) { + case "Item": + this.test.assertEqual(elt.localName, "menuitem", + "Item DOM element should be a xul:menuitem"); + if (typeof(item.data) === "string") { + this.test.assertEqual(elt.getAttribute("value"), item.data, + "Item should have correct data"); + } + break + case "Menu": + this.test.assertEqual(elt.localName, "menu", + "Menu DOM element should be a xul:menu"); + let subPopup = elt.firstChild; + this.test.assert(subPopup, "xul:menu should have a child"); + this.test.assertEqual(subPopup.localName, "menupopup", + "xul:menu's first child should be a menupopup"); + break; + case "Separator": + this.test.assertEqual(elt.localName, "menuseparator", + "Separator DOM element should be a xul:menuseparator"); + break; + } + + if (itemType === "Item" || itemType === "Menu") { + this.test.assertEqual(elt.getAttribute("label"), item.label, + "Item should have correct title"); + if (typeof(item.image) === "string") + this.test.assertEqual(elt.getAttribute("image"), item.image, + "Item should have correct image"); + else + this.test.assert(!elt.hasAttribute("image"), + "Item should not have image"); + } + }, + + // Asserts that the context menu looks OK given the arguments. presentItems + // are items that should match the current context. absentItems are items + // that shouldn't. removedItems are items that have been removed from the + // menu. + checkMenu: function (presentItems, absentItems, removedItems) { + this.checkSeparator(presentItems); + this.checkOverflow(presentItems); + this.checkPresentItems(presentItems); + this.checkAbsentItems(presentItems, absentItems); + this.checkRemovedItems(removedItems); + this.checkSort(presentItems); + }, + + // Asserts that the overflow submenu is present or absent as appropriate for + // presentItems. + checkOverflow: function (presentItems) { + let submenu = this.overflowSubmenu; + if (this.shouldOverflow(presentItems)) { + this.test.assert(submenu && !submenu.hidden, + "Overflow submenu should be present"); + this.test.assert(submenu.localName, "menu", + "Overflow submenu should be a "); + let overflowPopup = this.overflowPopup; + this.test.assert(overflowPopup, + "Overflow submenu popup should be present"); + this.test.assert(overflowPopup.localName, "menupopup", + "Overflow submenu popup should be a "); + } + else { + this.test.assert(!submenu || submenu.hidden, + "Overflow submenu should be absent"); + } + }, + + // Asserts that the items that are present in the menu because they match the + // current context look OK. + checkPresentItems: function (presentItems) { + function recurse(popup, items, isTopLevel) { + items.forEach(function (item) { + let elt = this.getItemElt(popup, item); + + if (isTopLevel) { + if (this.shouldOverflow(items)) { + this.test.assert(!elt || elt.hidden, + "Item should not be present in top-level menu"); + + let overflowPopup = this.overflowPopup; + this.test.assert(overflowPopup, + "Overflow submenu should be present"); + + elt = this.getItemElt(overflowPopup, item); + this.test.assert(elt && !elt.hidden, + "Item should be present in overflow submenu"); + } + else { + this.test.assert(elt && !elt.hidden, + "Item should be present in top-level menu"); + } + } + else { + this.test.assert(elt && !elt.hidden, + "Item should be present in menu"); + } + + this.checkItemElt(elt, item); + if (this.getItemType(item) === "Menu") + recurse.call(this, elt.firstChild, item.items, false); + }, this); + } + + recurse.call(this, this.contextMenuPopup, presentItems, true); + }, + + // Asserts that items that have been removed from the menu are really removed. + checkRemovedItems: function (removedItems) { + for (let i = 0; i < removedItems.length; i++) { + let item = removedItems[i]; + + let elt = this.getItemElt(this.contextMenuPopup, item); + this.test.assert(!elt, "Item should be removed from top-level menu"); + + let overflowPopup = this.overflowPopup; + if (overflowPopup) { + elt = this.getItemElt(overflowPopup, item); + this.test.assert(!elt, "Item should be removed from overflow submenu"); + } + } + }, + + // Asserts that the menu separator separating standard items from our items + // looks OK. + checkSeparator: function (presentItems) { + let sep = this.contextMenuSeparator; + if (presentItems.length) { + this.test.assert(sep && !sep.hidden, "Menu separator should be present"); + this.test.assertEqual(sep.localName, "menuseparator", + "Menu separator should be a "); + } + else { + this.test.assert(!sep || sep.hidden, "Menu separator should be absent"); + } + }, + + // Asserts that our items are sorted. + checkSort: function (presentItems) { + // Get the first item in sorted order, get its elt, walk the nextSibling + // chain, making sure each is greater than the previous. + if (presentItems.length) { + let sorted = presentItems.slice(0). + sort(function (a, b) a.label.localeCompare(b.label)); + let elt = this.shouldOverflow(presentItems) ? + this.getItemElt(this.overflowPopup, sorted[0]) : + this.getItemElt(this.contextMenuPopup, sorted[0]); + let numElts = 1; + while (elt.nextSibling && + elt.nextSibling.className.split(/\s+/).indexOf(ITEM_CLASS) >= 0) { + let eltLabel = elt.getAttribute("label"); + let nextLabel = elt.nextSibling.getAttribute("label"); + this.test.assert(eltLabel.localeCompare(nextLabel) < 0, + "Item label should be < next item's label"); + elt = elt.nextSibling; + numElts++; + } + this.test.assertEqual(numElts, presentItems.length, + "The first item in sorted order should have the " + + "first element in sorted order"); + } + }, + + // 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. + delayedEventListener: function (node, event, callback, useCapture) { + const self = this; + node.addEventListener(event, function handler(evt) { + node.removeEventListener(event, handler, useCapture); + require("timer").setTimeout(function () { + try { + callback.call(self, evt); + } + catch (err) { + self.test.exception(err); + self.test.done(); + } + }, 20); + }, useCapture); + }, + + // Call to finish the test. + done: function () { + function commonDone() { + if (this.tab) { + this.tabBrowser.removeTab(this.tab); + this.tabBrowser.selectedTab = this.oldSelectedTab; + } + while (this.loaders.length) { + let browserManager = this.loaders[0].globalScope.browserManager; + let topLevelItems = browserManager.topLevelItems.slice(); + let privatePropsKey = this.loaders[0].globalScope.PRIVATE_PROPS_KEY; + let workerRegs = topLevelItems.map(function (item) { + return item.valueOf(privatePropsKey)._workerReg; + }); + + this.loaders[0].unload(); + + // Make sure the browser manager is cleaned up. + this.test.assertEqual(browserManager.browserWins.length, 0, + "browserManager should have no windows left"); + this.test.assertEqual(browserManager.topLevelItems.length, 0, + "browserManager should have no items left"); + this.test.assert(!("contentWins" in browserManager), + "browserManager should have no content windows left"); + + // Make sure the items' worker registries are cleaned up. + topLevelItems.forEach(function (item) { + this.test.assert(!("_workerReg" in item.valueOf(privatePropsKey)), + "item's worker registry should be removed"); + }, this); + workerRegs.forEach(function (workerReg) { + this.test.assertEqual(Object.keys(workerReg.winWorkers).length, 0, + "worker registry should be empty"); + this.test.assertEqual( + Object.keys(workerReg.winsWithoutWorkers).length, 0, + "worker registry list of windows without workers should be empty"); + }, this); + } + this.test.done(); + } + + 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(); + } + }, + + // 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("context-menu"), + globalScope: loader.sandbox("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 number of presentItems crosses the overflow threshold. + shouldOverflow: function (presentItems) { + return presentItems.length > + (this.loaders.length ? + this.loaders[0].loader.require("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 = 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); + }, + + // 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 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); + } +}; diff --git a/tools/addon-sdk-1.4/packages/addon-kit/tests/test-hotkeys.js b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-hotkeys.js new file mode 100644 index 0000000..3b69a28 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-hotkeys.js @@ -0,0 +1,156 @@ +"use strict"; + +const { Hotkey } = require("hotkeys"); +const { keyDown } = require("dom/events/keys"); +const { Loader } = require('./helpers'); + +exports["test hotkey: function key"] = function(assert, done) { + var element = require("window-utils").activeBrowserWindow.document.documentElement; + var showHotKey = Hotkey({ + combo: "f1", + onPress: function() { + assert.pass("first callback is called"); + 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 = require("window-utils").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 = require("window-utils").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 = require("window-utils").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 = require("window-utils").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("window-utils").activeBrowserWindow.document.documentElement; + var hotkey = loader.require("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"); + + require("timer").setTimeout(function () { + assert.ok(!called, "Hotkey is destroyed and not called."); + done(); + }, 0); +}; + +require("test").run(exports); diff --git a/tools/addon-sdk-1.4/packages/addon-kit/tests/test-module.js b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-module.js new file mode 100644 index 0000000..c62f923 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-module.js @@ -0,0 +1,33 @@ +"use strict"; + +/** Disabled because of Bug 672199 +exports["test module exports are frozen"] = function(assert) { + assert.ok(Object.isFrozen(require("addon-kit/hotkeys")), + "module exports are frozen"); +}; + +exports["test redefine exported property"] = function(assert) { + let hotkeys = require("addon-kit/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("addon-kit/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("addon-kit/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/tools/addon-sdk-1.4/packages/addon-kit/tests/test-notifications.js b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-notifications.js new file mode 100644 index 0000000..1eb6190 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-notifications.js @@ -0,0 +1,79 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim:set ts=2 sw=2 sts=2 et filetype=javascript + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Drew Willcoxon (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const { Loader } = require('./helpers'); + +exports.testOnClick = function (test) { + let [loader, mockAlertServ] = makeLoader(module); + let notifs = loader.require("notifications"); + let data = "test data"; + let opts = { + onClick: function (clickedData) { + test.assertEqual(this, notifs, "|this| should be notifications module"); + test.assertEqual(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(); +}; + +// Returns [loader, mockAlertService]. +function makeLoader(test) { + 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("notifications"); + let scope = loader.sandbox("notifications"); + scope.notify = mockAlertServ.showAlertNotification.bind(mockAlertServ); + return [loader, mockAlertServ]; +}; diff --git a/tools/addon-sdk-1.4/packages/addon-kit/tests/test-page-mod.js b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-page-mod.js new file mode 100644 index 0000000..2b38946 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-page-mod.js @@ -0,0 +1,416 @@ +"use strict"; + +var pageMod = require("page-mod"); +var testPageMod = require("pagemod-test-helpers").testPageMod; +const { Loader } = require('./helpers'); +const tabs = require("tabs"); + +/* XXX This can be used to delay closing the test Firefox instance for interactive + * testing or visual inspection. This test is registered first so that it runs + * the last. */ +exports.delay = function(test) { + if (false) { + test.waitUntilDone(60000); + require("timer").setTimeout(function() {test.done();}, 4000); + } else + test.pass(); +} + +/* Tests for the PageMod APIs */ + +exports.testPageMod1 = function(test) { + let mods = testPageMod(test, "about:", [{ + include: /about:/, + contentScriptWhen: 'end', + contentScript: 'new ' + function WorkerScope() { + window.document.body.setAttribute("JEP-107", "worked"); + }, + onAttach: function() { + test.assertEqual(this, mods[0], "The 'this' object is the page mod."); + } + }], + function(win, done) { + test.assertEqual( + win.document.body.getAttribute("JEP-107"), + "worked", + "PageMod.onReady test" + ); + done(); + } + ); +}; + +exports.testPageMod2 = function(test) { + testPageMod(test, "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) { + test.assertEqual(win.document.documentElement.getAttribute("first"), + "true", + "PageMod test #2: first script has run"); + test.assertEqual(win.document.documentElement.getAttribute("second"), + "true", + "PageMod test #2: second script has run"); + test.assertEqual("AUQLUE" in win, false, + "PageMod test #2: scripts get a wrapped window"); + done(); + }); +}; + +exports.testPageModIncludes = function(test) { + var asserts = []; + function createPageModTest(include, expectedMatch) { + // Create an 'onload' test function... + asserts.push(function(test, win) { + var matches = include in win.localStorage; + test.assert(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(test, "about:buildconfig", [ + createPageModTest("*", false), + createPageModTest("*.google.com", false), + createPageModTest("about:*", true), + createPageModTest("about:", false), + createPageModTest("about:buildconfig", true) + ], + function (win, done) { + test.waitUntil(function () win.localStorage["about:buildconfig"], + "about:buildconfig page-mod to be executed") + .then(function () { + asserts.forEach(function(fn) { + fn(test, win); + }); + done(); + }); + } + ); +}; + +exports.testPageModErrorHandling = function(test) { + test.assertRaises(function() { + new pageMod.PageMod(); + }, + 'pattern is undefined', + "PageMod() throws when 'include' option is not specified."); +}; + +/* Tests for internal functions. */ +exports.testCommunication1 = function(test) { + let workerDone = false, + callbackDone = null; + + testPageMod(test, "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) { + test.fail('Errors where reported'); + }); + worker.on('message', function(value) { + test.assertEqual( + "worked", + value, + "test comunication" + ); + workerDone = true; + if (callbackDone) + callbackDone(); + }); + worker.postMessage('do it!') + } + }], + function(win, done) { + (callbackDone = function() { + if (workerDone) { + test.assertEqual( + 'worked', + win.document.body.getAttribute('JEP-107'), + 'attribute should be modified' + ); + done(); + } + })(); + } + ); +}; + +exports.testCommunication2 = function(test) { + let callbackDone = null, + window; + + testPageMod(test, "about:", [{ + 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) { + test.fail('Errors where reported'); + }); + worker.on('message', function(msg) { + if ('onload' == msg) { + test.assertEqual( + '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 { + test.assertEqual( + 'changes in window', + msg, + 'PageMod test #2: second script has run' + ) + callbackDone(); + } + }); + } + }], + function(win, done) { + window = win; + callbackDone = done; + } + ); +}; + +exports.testEventEmitter = function(test) { + let workerDone = false, + callbackDone = null; + + testPageMod(test, "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) { + test.fail('Errors were reported : '+e); + }); + worker.port.on('content-to-addon', function(value) { + test.assertEqual( + "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(test) { + let doneCallback = null; + let messages = 0; + let modObject = { + include: "data:text/html,", + 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) { + test.fail("Page mod contexts are mixed."); + doneCallback(); + } + else if (++messages == 2) { + test.pass("Page mod contexts are different."); + doneCallback(); + } + }); + } + }; + testPageMod(test, "data:text/html,", [modObject, modObject], + function(win, done) { + doneCallback = done; + } + ); +}; + +exports.testHistory = function(test) { + // 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 = require("self").data.url("test-page-mod.html"); + let callbackDone = null; + testPageMod(test, 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) { + test.assertEqual(JSON.stringify(data), JSON.stringify({foo: "bar"}), + "History API works!"); + callbackDone(); + }); + } + }], + function(win, done) { + callbackDone = done; + } + ); +}; + +exports.testRelatedTab = function(test) { + test.waitUntilDone(); + + let tab; + let { PageMod } = require("page-mod"); + let pageMod = new PageMod({ + include: "about:*", + onAttach: function(worker) { + test.assertEqual(tab, worker.tab, "Worker.tab is valid"); + pageMod.destroy(); + tab.close(); + test.done(); + } + }); + + tabs.open({ + url: "about:", + onOpen: function onOpen(t) { + tab = t; + } + }); + +}; + +exports['test tab worker on message'] = function(test) { + test.waitUntilDone(); + + let { browserWindows } = require("windows"); + let tabs = require("tabs"); + let { PageMod } = require("page-mod"); + + let url1 = "data:text/html,tab1

worker1.tab

"; + let url2 = "data:text/html,tab2

worker2.tab

"; + let worker1 = null; + + let mod = PageMod({ + include: "data:text/html,*", + contentScriptWhen: "ready", + contentScript: "self.postMessage('#1');", + onAttach: function onAttach(worker) { + worker.on("message", function onMessage() { + this.tab.attach({ + contentScriptWhen: "ready", + contentScript: "self.postMessage({ url: window.location.href, title: document.title });", + onMessage: function onMessage(data) { + test.assertEqual(this.tab.url, data.url, "location is correct"); + test.assertEqual(this.tab.title, data.title, "title is correct"); + if (this.tab.url === url1) { + worker1 = this; + tabs.open({ url: url2, inBackground: true }); + } + else if (this.tab.url === url2) { + mod.destroy(); + worker1.tab.close(); + worker1.destroy(); + worker.tab.close(); + worker.destroy(); + test.done(); + } + } + }); + }); + } + }); + + tabs.open(url1); +}; + +exports.testAutomaticDestroy = function(test) { + test.waitUntilDone(); + let loader = Loader(module); + + let pageMod = loader.require("page-mod").PageMod({ + include: "about:*", + contentScriptWhen: "start", + onAttach: function(w) { + test.fail("Page-mod should have been detroyed during module unload"); + } + }); + + // Unload the page-mod module so that our page mod is destroyed + loader.unload(); + + // Then create a second tab to ensure that it is correctly destroyed + let tabs = require("tabs"); + tabs.open({ + url: "about:", + onReady: function onReady(tab) { + test.pass("check automatic destroy"); + tab.close(); + test.done(); + } + }); + +} diff --git a/tools/addon-sdk-1.4/packages/addon-kit/tests/test-page-worker.js b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-page-worker.js new file mode 100644 index 0000000..89cdf45 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-page-worker.js @@ -0,0 +1,362 @@ +let tests = {}, Pages, Page; +const { Loader } = require('./helpers'); + +const ERR_DESTROYED = + "The page has been destroyed and can no longer be used."; + +tests.testSimplePageCreation = function(test) { + test.waitUntilDone(); + + let page = new Page({ + contentScript: "self.postMessage(window.location.href)", + contentScriptWhen: "end", + onMessage: function (message) { + test.assertEqual(message, "about:blank", + "Page Worker should start with a blank page by default"); + test.assertEqual(this, page, "The 'this' object is the page itself."); + test.done(); + } + }); +} + +/* + * Tests that we can't be tricked by document overloads as we have access + * to wrapped nodes + */ +tests.testWrappedDOM = function(test) { + test.waitUntilDone(); + + let page = Page({ + allow: { script: true }, + contentURL: "data:text/html,", + contentScript: "window.addEventListener('load', function () " + + "self.postMessage([typeof(document.getElementById), " + + "typeof(window.scrollTo)]), true)", + onMessage: function (message) { + test.assertEqual(message[0], + "function", + "getElementById from content script is the native one"); + + test.assertEqual(message[1], + "function", + "scrollTo from content script is the native one"); + + test.done(); + } + }); +} + +/* +// We do not offer unwrapped access to DOM since bug 601295 landed +// See 660780 to track progress of unwrap feature +tests.testUnwrappedDOM = function(test) { + test.waitUntilDone(); + + let page = Page({ + allow: { script: true }, + contentURL: "data:text/html,", + contentScript: "window.addEventListener('load', function () " + + "self.postMessage([typeof(unsafeWindow.document.getElementById), " + + "typeof(unsafeWindow.scrollTo)]), true)", + onMessage: function (message) { + test.assertEqual(message[0], + "number", + "document inside page is free to be changed"); + + test.assertEqual(message[1], + "number", + "window inside page is free to be changed"); + + test.done(); + } + }); +} +*/ + +tests.testPageProperties = function(test) { + let page = new Page(); + + for each (let prop in ['contentURL', 'allow', 'contentScriptFile', + 'contentScript', 'contentScriptWhen', 'on', + 'postMessage', 'removeListener']) { + test.assert(prop in page, prop + " property is defined on page."); + } + + test.assert(function () page.postMessage("foo") || true, + "postMessage doesn't throw exception on page."); +} + +tests.testConstructorAndDestructor = function(test) { + test.waitUntilDone(); + + let loader = Loader(module); + let Pages = loader.require("page-worker"); + let global = loader.sandbox("page-worker"); + + let pagesReady = 0; + + let page1 = Pages.Page({ + contentScript: "self.postMessage('')", + contentScriptWhen: "end", + onMessage: pageReady + }); + let page2 = Pages.Page({ + contentScript: "self.postMessage('')", + contentScriptWhen: "end", + onMessage: pageReady + }); + + test.assertNotEqual(page1, page2, + "Page 1 and page 2 should be different objects."); + + function pageReady() { + if (++pagesReady == 2) { + page1.destroy(); + page2.destroy(); + + test.assert(isDestroyed(page1), "page1 correctly unloaded."); + test.assert(isDestroyed(page2), "page2 correctly unloaded."); + + loader.unload(); + test.done(); + } + } +} + +tests.testAutoDestructor = function(test) { + test.waitUntilDone(); + + let loader = Loader(module); + let Pages = loader.require("page-worker"); + + let page = Pages.Page({ + contentScript: "self.postMessage('')", + contentScriptWhen: "end", + onMessage: function() { + loader.unload(); + test.assert(isDestroyed(page), "Page correctly unloaded."); + test.done(); + } + }); +} + +tests.testValidateOptions = function(test) { + test.assertRaises( + function () Page({ contentURL: 'home' }), + "The `contentURL` option must be a valid URL.", + "Validation correctly denied a non-URL contentURL" + ); + + test.assertRaises( + function () Page({ onMessage: "This is not a function."}), + "The event listener must be a function.", + "Validation correctly denied a non-function onMessage." + ); + + test.pass("Options validation is working."); +} + +tests.testContentAndAllowGettersAndSetters = function(test) { + test.waitUntilDone(); + let content = "data:text/html,"; + let page = Page({ + contentURL: content, + contentScript: "self.postMessage(window.localStorage.allowScript)", + contentScriptWhen: "end", + onMessage: step0 + }); + + function step0(message) { + test.assertEqual(message, "3", + "Correct value expected for allowScript - 3"); + test.assertEqual(page.contentURL, content, + "Correct content expected"); + page.removeListener('message', step0); + page.on('message', step1); + page.allow = { script: false }; + page.contentURL = content = + "data:text/html,"; + } + + function step1(message) { + test.assertEqual(message, "3", + "Correct value expected for allowScript - 3"); + test.assertEqual(page.contentURL, content, "Correct content expected"); + page.removeListener('message', step1); + page.on('message', step2); + page.allow = { script: true }; + page.contentURL = content = + "data:text/html,"; + } + + function step2(message) { + test.assertEqual(message, "g", + "Correct value expected for allowScript - g"); + test.assertEqual(page.contentURL, content, "Correct content expected"); + page.removeListener('message', step2); + page.on('message', step3); + page.allow.script = false; + page.contentURL = content = + "data:text/html,"; + } + + function step3(message) { + test.assertEqual(message, "g", + "Correct value expected for allowScript - g"); + test.assertEqual(page.contentURL, content, "Correct content expected"); + page.removeListener('message', step3); + page.on('message', step4); + page.allow.script = true; + page.contentURL = content = + "data:text/html,"; + } + + function step4(message) { + test.assertEqual(message, "4", + "Correct value expected for allowScript - 4"); + test.assertEqual(page.contentURL, content, "Correct content expected"); + test.done(); + } + +} + +tests.testOnMessageCallback = function(test) { + test.waitUntilDone(); + + Page({ + contentScript: "self.postMessage('')", + contentScriptWhen: "end", + onMessage: function() { + test.pass("onMessage callback called"); + test.done(); + } + }); +} + +tests.testMultipleOnMessageCallbacks = function(test) { + test.waitUntilDone(); + + let count = 0; + let page = Page({ + contentScript: "self.postMessage('')", + contentScriptWhen: "end", + onMessage: function() count += 1 + }); + page.on('message', function() count += 2); + page.on('message', function() count *= 3); + page.on('message', function() + test.assertEqual(count, 9, "All callbacks were called, in order.")); + page.on('message', function() test.done()); + +} + +tests.testLoadContentPage = function(test) { + + test.waitUntilDone(); + + let page = Page({ + onMessage: function(message) { + // The message is an array whose first item is the test method to call + // and the rest of whose items are arguments to pass it. + test[message.shift()].apply(test, message); + }, + contentURL: require("self").data.url("test-page-worker.html"), + contentScriptFile: require("self").data.url("test-page-worker.js"), + contentScriptWhen: "ready" + }); + +} + +tests.testAllowScriptDefault = function(test) { + + test.waitUntilDone(); + + let page = Page({ + onMessage: function(message) { + test.assert(message, "Script is allowed to run by default."); + test.done(); + }, + contentURL: "data:text/html,", + contentScript: "self.postMessage(document.documentElement.getAttribute('foo'))", + contentScriptWhen: "ready" + }); +} + +tests.testAllowScript = function(test) { + + test.waitUntilDone(); + + let page = Page({ + onMessage: function(message) { + test.assert(message, "Script runs when allowed to do so."); + test.done(); + }, + allow: { script: true }, + contentURL: "data:text/html,", + contentScript: "self.postMessage(document.documentElement.hasAttribute('foo') && " + + " document.documentElement.getAttribute('foo') == 3)", + contentScriptWhen: "ready" + }); +} + +tests.testPingPong = function(test) { + test.waitUntilDone(); + let page = Page({ + contentURL: 'data:text/html,ping-pong', + contentScript: 'self.on("message", function(message) self.postMessage("pong"));' + + 'self.postMessage("ready");', + onMessage: function(message) { + if ('ready' == message) { + page.postMessage('ping'); + } + else { + test.assert(message, 'pong', 'Callback from contentScript'); + test.done(); + } + } + }); +}; + +tests.testMultipleDestroys = function(test) { + let page = Page(); + page.destroy(); + page.destroy(); + test.pass("Multiple destroys should not cause an error"); +}; + + +function isDestroyed(page) { + try { + page.postMessage("foo"); + } + catch (err if err.message == ERR_DESTROYED) { + return true; + } + return false; +} + + +let pageWorkerSupported = true; + +try { + Pages = require("page-worker"); + Page = Pages.Page; +} +catch (ex if ex.message == [ + "The page-worker module currently supports only Firefox and Thunderbird. ", + "In the future, we would like it to support other applications, however. ", + "Please see https://bugzilla.mozilla.org/show_bug.cgi?id=546740 for more ", + "information." + ].join("")) { + pageWorkerSupported = false; +} + +if (pageWorkerSupported) { + for (let test in tests) { + exports[test] = tests[test]; + } +} else { + exports.testPageWorkerNotSupported = function(test) { + test.pass("The page-worker module is not supported on this app."); + } +} diff --git a/tools/addon-sdk-1.4/packages/addon-kit/tests/test-panel.js b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-panel.js new file mode 100644 index 0000000..3e240db --- /dev/null +++ b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-panel.js @@ -0,0 +1,437 @@ +let { Cc, Ci } = require("chrome"); +let panels = require('panel'); +let tests = {}, panels, Panel; +const { Loader } = require('./helpers'); + +tests.testPanel = function(test) { + test.waitUntilDone(); + let panel = Panel({ + contentURL: "about:buildconfig", + contentScript: "self.postMessage(1); self.on('message', function() self.postMessage(2));", + onMessage: function (message) { + test.assertEqual(this, panel, "The 'this' object is the panel."); + switch(message) { + case 1: + test.pass("The panel was loaded."); + panel.postMessage(''); + break; + case 2: + test.pass("The panel posted a message and received a response."); + panel.destroy(); + test.done(); + break; + } + } + }); +}; + +tests.testPanelEmit = function(test) { + test.waitUntilDone(); + let panel = Panel({ + contentURL: "about:buildconfig", + contentScript: "self.port.emit('loaded');" + + "self.port.on('addon-to-content', " + + " function() self.port.emit('received'));", + }); + panel.port.on("loaded", function () { + test.pass("The panel was loaded and sent a first event."); + panel.port.emit("addon-to-content"); + }); + panel.port.on("received", function () { + test.pass("The panel posted a message and received a response."); + panel.destroy(); + test.done(); + }); +}; + +tests.testPanelEmitEarly = function(test) { + test.waitUntilDone(); + let panel = Panel({ + contentURL: "about:buildconfig", + contentScript: "self.port.on('addon-to-content', " + + " function() self.port.emit('received'));", + }); + panel.port.on("received", function () { + test.pass("The panel posted a message early and received a response."); + panel.destroy(); + test.done(); + }); + panel.port.emit("addon-to-content"); +}; + +tests.testShowHidePanel = function(test) { + test.waitUntilDone(); + let panel = Panel({ + contentScript: "self.postMessage('')", + contentScriptWhen: "end", + onMessage: function (message) { + panel.show(); + }, + onShow: function () { + test.pass("The panel was shown."); + test.assertEqual(this, panel, "The 'this' object is the panel."); + test.assertEqual(this.isShowing, true, "panel.isShowing == true."); + panel.hide(); + }, + onHide: function () { + test.pass("The panel was hidden."); + test.assertEqual(this, panel, "The 'this' object is the panel."); + test.assertEqual(this.isShowing, false, "panel.isShowing == false."); + panel.destroy(); + test.done(); + } + }); +}; + +tests.testDocumentReload = function(test) { + test.waitUntilDone(); + let content = + ""; + let messageCount = 0; + let panel = Panel({ + contentURL: "data:text/html," + encodeURIComponent(content), + contentScript: "self.postMessage(window.location.href)", + onMessage: function (message) { + messageCount++; + if (messageCount == 1) { + test.assertMatches(message, /data:text\/html,/, "First document had a content script"); + } + else if (messageCount == 2) { + test.assertEqual(message, "about:blank", "Second document too"); + panel.destroy(); + test.done(); + } + } + }); +}; + +tests.testParentResizeHack = function(test) { + let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"]. + getService(Ci.nsIWindowMediator). + getMostRecentWindow("navigator:browser"); + let docShell = browserWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell); + if (!("allowWindowControl" in docShell)) { + // bug 635673 is not fixed in this firefox build + test.pass("allowWindowControl attribute that allow to fix browser window " + + "resize is not available on this build."); + return; + } + + test.waitUntilDone(30000); + + let previousWidth = browserWindow.outerWidth, previousHeight = browserWindow.outerHeight; + + let content = "" + + "Try to resize browser window"; + let panel = Panel({ + contentURL: "data:text/html," + encodeURIComponent(content), + contentScript: "self.on('message', function(message){" + + " if (message=='resize') " + + " unsafeWindow.contentResize();" + + "});", + contentScriptWhen: "ready", + onMessage: function (message) { + + }, + onShow: function () { + panel.postMessage('resize'); + require("timer").setTimeout(function () { + test.assertEqual(previousWidth,browserWindow.outerWidth,"Size doesn't change by calling resizeTo/By/..."); + test.assertEqual(previousHeight,browserWindow.outerHeight,"Size doesn't change by calling resizeTo/By/..."); + panel.destroy(); + test.done(); + },0); + } + }); + panel.show(); +} + +tests.testResizePanel = function(test) { + test.waitUntilDone(); + + // These tests fail on Linux if the browser window in which the panel + // is displayed is not active. And depending on what other tests have run + // before this one, it might not be (the untitled window in which the test + // runner executes is often active). So we make sure the browser window + // is focused by focusing it before running the tests. Then, to be the best + // possible test citizen, we refocus whatever window was focused before we + // started running these tests. + + let activeWindow = Cc["@mozilla.org/embedcomp/window-watcher;1"]. + getService(Ci.nsIWindowWatcher). + activeWindow; + let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"]. + getService(Ci.nsIWindowMediator). + getMostRecentWindow("navigator:browser"); + + + function onFocus() { + browserWindow.removeEventListener("focus", onFocus, true); + + let panel = Panel({ + contentScript: "self.postMessage('')", + contentScriptWhen: "end", + height: 10, + width: 10, + onMessage: function (message) { + panel.show(); + }, + onShow: function () { + panel.resize(100,100); + panel.hide(); + }, + onHide: function () { + test.assert((panel.width == 100) && (panel.height == 100), + "The panel was resized."); + if (activeWindow) + activeWindow.focus(); + test.done(); + } + }); + } + + if (browserWindow === activeWindow) { + onFocus(); + } + else { + browserWindow.addEventListener("focus", onFocus, true); + browserWindow.focus(); + } +}; + +tests.testHideBeforeShow = function(test) { + test.waitUntilDone(); + let showCalled = false; + let panel = Panel({ + onShow: function () { + showCalled = true; + }, + onHide: function () { + test.assert(!showCalled, 'must not emit show if was hidden before'); + test.done(); + } + }); + panel.show(); + panel.hide(); +}; + +tests.testSeveralShowHides = function(test) { + test.waitUntilDone(); + let hideCalled = 0; + let panel = panels.Panel({ + contentURL: "about:buildconfig", + onShow: function () { + panel.hide(); + }, + onHide: function () { + hideCalled++; + if (hideCalled < 3) + panel.show(); + else { + test.pass("onHide called three times as expected"); + test.done(); + } + } + }); + panel.on('error', function(e) { + test.fail('error was emitted:' + e.message + '\n' + e.stack); + }); + panel.show(); +}; + +tests.testAnchorAndArrow = function(test) { + test.waitUntilDone(20000); + let count = 0; + function newPanel(tab, anchor) { + let panel = panels.Panel({ + contentURL: "data:text/html,Anchor: " + + anchor.id + "", + width: 200, + height: 100, + onShow: function () { + count++; + panel.destroy(); + if (count==5) { + test.pass("All anchored panel test displayed"); + tab.close(function () { + test.done(); + }); + } + } + }); + panel.show(anchor); + } + + let tabs= require("tabs"); + let url = 'data:text/html,' + + 'foo' + + '' + + '
Top Left
' + + '
Top Right
' + + '
Bottom Left
' + + '
Bottom right
' + + ''; + + tabs.open({ + url: url, + onReady: function(tab) { + let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"]. + getService(Ci.nsIWindowMediator). + getMostRecentWindow("navigator:browser"); + let window = browserWindow.content; + newPanel(tab, window.document.getElementById('tl')); + newPanel(tab, window.document.getElementById('tr')); + newPanel(tab, window.document.getElementById('bl')); + newPanel(tab, window.document.getElementById('br')); + let anchor = browserWindow.document.getElementById("identity-box"); + newPanel(tab, anchor); + } + }); + + + +}; + +tests.testPanelTextColor = function(test) { + test.waitUntilDone(); + let html = "" + + "

Foo

"; + let panel = Panel({ + contentURL: "data:text/html," + encodeURI(html), + contentScript: "self.port.emit('color', " + + "window.getComputedStyle(document.body.firstChild, null). " + + " getPropertyValue('color'));" + }); + panel.port.on("color", function (color) { + test.assertEqual(color, "rgb(255, 255, 0)", + "The panel text color style is preserved when a style exists."); + panel.destroy(); + test.done(); + }); +}; + +function makeEventOrderTest(options) { + let expectedEvents = []; + + return function(test) { + let panel = panels.Panel({ contentURL: "about:buildconfig" }); + + function expect(event, cb) { + expectedEvents.push(event); + panel.on(event, function() { + test.assertEqual(event, expectedEvents.shift()); + if (cb) + require("timer").setTimeout(cb, 1); + }); + return {then: expect}; + } + + test.waitUntilDone(); + options.test(test, expect, panel); + } +} + +tests.testAutomaticDestroy = function(test) { + let loader = Loader(module); + let panel = loader.require("panel").Panel({ + contentURL: "about:buildconfig", + contentScript: + "self.port.on('event', function() self.port.emit('event-back'));" + }); + + loader.unload(); + + panel.port.on("event-back", function () { + test.fail("Panel should have been destroyed on module unload"); + }); + panel.port.emit("event"); + test.pass("check automatic destroy"); +}; + +tests.testWaitForInitThenShowThenDestroy = makeEventOrderTest({ + test: function(test, expect, panel) { + expect('inited', function() { panel.show(); }). + then('show', function() { panel.destroy(); }). + then('hide', function() { test.done(); }); + } +}); + +tests.testShowThenWaitForInitThenDestroy = makeEventOrderTest({ + test: function(test, expect, panel) { + panel.show(); + expect('inited'). + then('show', function() { panel.destroy(); }). + then('hide', function() { test.done(); }); + } +}); + +tests.testShowThenHideThenDestroy = makeEventOrderTest({ + test: function(test, expect, panel) { + panel.show(); + expect('show', function() { panel.hide(); }). + then('hide', function() { panel.destroy(); test.done(); }); + } +}); + +tests.testContentURLOption = function(test) { + const URL_STRING = "about:buildconfig"; + const HTML_CONTENT = "Test

This is a test.

"; + + let (panel = Panel({ contentURL: URL_STRING })) { + test.pass("contentURL accepts a string URL."); + test.assertEqual(panel.contentURL, URL_STRING, + "contentURL is the string to which it was set."); + } + + let dataURL = "data:text/html," + encodeURIComponent(HTML_CONTENT); + let (panel = Panel({ contentURL: dataURL })) { + test.pass("contentURL accepts a data: URL."); + } + + let (panel = Panel({})) { + test.assert(panel.contentURL == null, + "contentURL is undefined."); + } + + test.assertRaises(function () Panel({ contentURL: "foo" }), + "The `contentURL` option must be a valid URL.", + "Panel throws an exception if contentURL is not a URL."); +}; + +let panelSupported = true; + +try { + panels = require("panel"); + Panel = panels.Panel; +} +catch(ex if ex.message == [ + "The panel module currently supports only Firefox. In the future ", + "we would like it to support other applications, however. Please see ", + "https://bugzilla.mozilla.org/show_bug.cgi?id=jetpack-panel-apps ", + "for more information." + ].join("")) { + panelSupported = false; +} + +if (panelSupported) { + for (let test in tests) + exports[test] = tests[test]; +} +else { + exports.testPanelNotSupported = function(test) { + test.pass("The panel module is not supported on this app."); + } +} diff --git a/tools/addon-sdk-1.4/packages/addon-kit/tests/test-passwords.js b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-passwords.js new file mode 100644 index 0000000..e799b48 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-passwords.js @@ -0,0 +1,277 @@ +"use strict"; + +const { store, search, remove } = require("passwords"); + +exports["test store requires `password` field"] = function(assert, done) { + store({ + username: "foo", + realm: "bar", + onComplete: function onComplete() { + assert.fail("onComplete should not be called"); + }, + onError: function onError() { + assert.pass("'`password` is required"); + done(); + } + }); +}; + +exports["test store requires `username` field"] = function(assert, done) { + store({ + password: "foo", + realm: "bar", + onComplete: function onComplete() { + assert.fail("onComplete should not be called"); + }, + onError: function onError() { + assert.pass("'`username` is required"); + done(); + } + }); +}; + +exports["test onComplete is optional"] = function(assert, done) { + store({ + realm: "bla", + username: "bla", + password: "bla", + onError: function onError() { + assert.fail("onError was called"); + } + }); + assert.pass("exception is not thrown if `onComplete is missing") + done(); +}; + +exports["test exceptions in onComplete are reported"] = function(assert, done) { + store({ + realm: "throws", + username: "error", + password: "boom!", + onComplete: function onComplete(error) { + throw new Error("Boom!") + }, + onError: function onError(error) { + assert.equal(error.message, "Boom!", "Error thrown is reported"); + done(); + } + }); +}; + +exports["test store requires `realm` field"] = function(assert, done) { + store({ + username: "foo", + password: "bar", + onComplete: function onComplete() { + assert.fail("onComplete should not be called"); + }, + onError: function onError() { + assert.pass("'`realm` is required"); + done(); + } + }); +}; + +exports["test can't store same login twice"] = function(assert, done) { + store({ + username: "user", + password: "pass", + realm: "realm", + onComplete: function onComplete() { + assert.pass("credential saved"); + + store({ + username: "user", + password: "pass", + realm: "realm", + onComplete: function onComplete() { + assert.fail("onComplete should not be called"); + }, + onError: function onError() { + assert.pass("re-saving credential failed"); + + remove({ + username: "user", + password: "pass", + realm: "realm", + onComplete: function onComplete() { + assert.pass("credential was removed"); + done(); + }, + onError: function onError() { + assert.fail("remove should not fail"); + } + }); + } + }); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); +}; + +exports["test remove fails if no login found"] = function(assert, done) { + remove({ + username: "foo", + password: "bar", + realm: "baz", + onComplete: function onComplete() { + assert.fail("should not be able to remove unstored credentials"); + }, + onError: function onError() { + assert.pass("can't remove unstored credentials"); + done(); + } + }); +}; + +exports["test addon associated credentials"] = function(assert, done) { + store({ + username: "foo", + password: "bar", + realm: "baz", + onComplete: function onComplete() { + search({ + username: "foo", + password: "bar", + realm: "baz", + onComplete: function onComplete([credential]) { + assert.equal(credential.url.indexOf("addon:"), 0, + "`addon:` uri is used for add-on credentials"); + assert.equal(credential.username, "foo", + "username matches"); + assert.equal(credential.password, "bar", + "password matches"); + assert.equal(credential.realm, "baz", "realm matches"); + assert.equal(credential.formSubmitURL, null, + "`formSubmitURL` is `null` for add-on credentials"); + assert.equal(credential.usernameField, "", "usernameField is empty"); + assert.equal(credential.passwordField, "", "passwordField is empty"); + + remove({ + username: credential.username, + password: credential.password, + realm: credential.realm, + onComplete: function onComplete() { + assert.pass("credential is removed"); + done(); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); +}; + +exports["test web page associated credentials"] = function(assert, done) { + store({ + url: "http://bar.foo.com/authentication/?login", + formSubmitURL: "http://login.foo.com/authenticate.cgi", + username: "user", + password: "pass", + usernameField: "user-f", + passwordField: "pass-f", + onComplete: function onComplete() { + search({ + username: "user", + password: "pass", + url: "http://bar.foo.com", + formSubmitURL: "http://login.foo.com", + onComplete: function onComplete([credential]) { + assert.equal(credential.url, "http://bar.foo.com", "url matches"); + assert.equal(credential.username, "user", "username matches"); + assert.equal(credential.password, "pass", "password matches"); + assert.equal(credential.realm, null, "realm is null"); + assert.equal(credential.formSubmitURL, "http://login.foo.com", + "formSubmitURL matches"); + assert.equal(credential.usernameField, "user-f", + "usernameField is matches"); + assert.equal(credential.passwordField, "pass-f", + "passwordField matches"); + + remove({ + url: credential.url, + formSubmitURL: credential.formSubmitURL, + username: credential.username, + password: credential.password, + usernameField: credential.usernameField, + passwordField: credential.passwordField, + + onComplete: function onComplete() { + assert.pass("credential is removed"); + done(); + }, + onError: function onError(e) { + assert.fail("onError should not be called"); + } + }); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); +}; + +exports["test site authentication credentials"] = function(assert, done) { + store({ + url: "http://authentication.com", + username: "U", + password: "P", + realm: "R", + onComplete: function onComplete() { + search({ + url: "http://authentication.com", + username: "U", + password: "P", + realm: "R", + onComplete: function onComplete([credential]) { + assert.equal(credential.url,"http://authentication.com", + "url matches"); + assert.equal(credential.username, "U", "username matches"); + assert.equal(credential.password, "P", "password matches"); + assert.equal(credential.realm, "R", "realm matches"); + assert.equal(credential.formSubmitURL, null, "formSubmitURL is null"); + assert.equal(credential.usernameField, "", "usernameField is empty"); + assert.equal(credential.passwordField, "", "passwordField is empty"); + + remove({ + url: credential.url, + username: credential.username, + password: credential.password, + realm: credential.realm, + onComplete: function onComplete() { + assert.pass("credential is removed"); + done(); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); +}; + +require("test").run(exports); diff --git a/tools/addon-sdk-1.4/packages/addon-kit/tests/test-private-browsing.js b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-private-browsing.js new file mode 100644 index 0000000..a3c1519 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-private-browsing.js @@ -0,0 +1,238 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Paul O’Shannessy + * Irakli Gozalishvili + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +let pb = require("private-browsing"); +let {Cc,Ci} = require("chrome"); +const { Loader } = require('./helpers'); + +let pbService; +// Currently, only Firefox implements the private browsing service. +if (require("xul-app").is("Firefox")) { + pbService = Cc["@mozilla.org/privatebrowsing;1"]. + getService(Ci.nsIPrivateBrowsingService); +} + +if (pbService) { + + // tests that isActive has the same value as the private browsing service + // expects + exports.testGetIsActive = function (test) { + test.assertEqual(pb.isActive, false, + "private-browsing.isActive is correct without modifying PB service"); + + pbService.privateBrowsingEnabled = true; + test.assert(pb.isActive, + "private-browsing.isActive is correct after modifying PB service"); + + // Switch back to normal mode. + pbService.privateBrowsingEnabled = false; + }; + + // tests that activating does put the browser into private browsing mode + exports.testActivateDeactivate = function (test) { + test.waitUntilDone(); + pb.once("start", function onStart() { + test.assertEqual(pbService.privateBrowsingEnabled, true, + "private browsing mode was activated"); + pb.deactivate(); + }); + pb.once("stop", function onStop() { + test.assertEqual(pbService.privateBrowsingEnabled, false, + "private browsing mode was deactivate"); + test.done(); + }); + pb.activate(); + }; + + exports.testStart = function(test) { + test.waitUntilDone(); + pb.on("start", function onStart() { + test.assertEqual(this, pb, "`this` should be private-browsing module"); + test.assert(pbService.privateBrowsingEnabled, + 'private mode is active when "start" event is emitted'); + test.assert(pb.isActive, + '`isActive` is `true` when "start" event is emitted'); + pb.removeListener("start", onStart); + test.done(); + }); + pb.activate(); + }; + + exports.testStop = function(test) { + test.waitUntilDone(); + pb.on("stop", function onStop() { + test.assertEqual(this, pb, "`this` should be private-browsing module"); + test.assertEqual(pbService.privateBrowsingEnabled, false, + "private mode is disabled when stop event is emitted"); + test.assertEqual(pb.isActive, false, + "`isActive` is `false` when stop event is emitted"); + pb.removeListener("stop", onStop); + test.done(); + }); + pb.activate(); + pb.deactivate(); + }; + + exports.testAutomaticUnload = function(test) { + test.waitUntilDone(); + // Create another private browsing instance and unload it + let loader = Loader(module); + let pb2 = loader.require("private-browsing"); + let called = false; + pb2.on("start", function onStart() { + called = true; + test.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.activate(); + pb.once("start", function onStart() { + require("timer").setTimeout(function () { + test.assert(!called, + "First private browsing instance is destroyed and inactive"); + + // Must reset to normal mode, so that next test starts with it. + pb.deactivate(); + test.done(); + }, 0); + }); + }; + + exports.testBothListeners = function(test) { + test.waitUntilDone(); + let stop = false; + let start = false; + + function onStop() { + test.assertEqual(stop, false, + "stop callback must be called only once"); + test.assertEqual(pbService.privateBrowsingEnabled, false, + "private mode is disabled when stop event is emitted"); + test.assertEqual(pb.isActive, false, + "`isActive` 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() { + test.assertEqual(false, start, + "stop callback must be called only once"); + test.assert(pbService.privateBrowsingEnabled, + "private mode is active when start event is emitted"); + test.assert(pb.isActive, + "`isActive` is `true` when start event is emitted"); + + pb.on("stop", onStop); + pb.deactivate(); + start = true; + } + + function onStart2() { + test.assert(start, "start listener must be called already"); + test.assertEqual(false, stop, "stop callback must not be called yet"); + } + + function finish() { + test.assert(pbService.privateBrowsingEnabled, true, + "private mode is active when start event is emitted"); + test.assert(pb.isActive, + "`isActive` is `true` when start event is emitted"); + + pb.removeListener("start", finish); + pb.removeListener("stop", onStop); + + pb.deactivate(); + pb.once("stop", function () { + test.assertEqual(pbService.privateBrowsingEnabled, false); + test.assertEqual(pb.isActive, false); + + test.done(); + }); + } + + pb.on("start", onStart); + pb.on("start", onStart2); + pbService.privateBrowsingEnabled = true; + }; + + exports["test activate private mode via handler"] = function(test) { + const tabs = require("tabs"); + + test.waitUntilDone(); + 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() { + test.done(); + }); + } + } + + tabs.on("ready", onReady); + pb.once("start", function onStart() { + test.pass("private mode was activated"); + pb.deactivate(); + }); + pb.once("stop", function onStop() { + test.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:"); + }; +} +else { + // tests for the case where private browsing doesn't exist + exports.testNoImpl = function (test) { + test.assertEqual(pb.isActive, false, + "pb.isActive returns false when private browsing isn't supported"); + }; +} diff --git a/tools/addon-sdk-1.4/packages/addon-kit/tests/test-request.js b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-request.js new file mode 100644 index 0000000..8b8300e --- /dev/null +++ b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-request.js @@ -0,0 +1,358 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Paul O’Shannessy (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const Request = require("request").Request; + +var port = 8080; +var data = require("self").data; +var testFilePath = require("url").toFilename(data.url("test-request.txt")); +var basePath = require("file").dirname(testFilePath); + +var {startServerAsync} = require("httpd"); + +exports.testOptionsValidator = function(test) { + // First, a simple test to make sure we didn't break normal functionality. + test.assertRaises(function () { + Request({ + url: null + }); + }, 'The option "url" must be one of the following types: string'); + + // Next we'll have a Request that doesn't throw from c'tor, but from a setter. + let req = Request({ + url: "http://playground.zpao.com/jetpack/request/text.php", + onComplete: function () {} + }); + test.assertRaises(function () { + req.url = null; + }, 'The option "url" must be one of the following types: string'); + // The url shouldn't have changed, so check that + test.assertEqual(req.url, "http://playground.zpao.com/jetpack/request/text.php"); +} + +exports.testContentValidator = function(test) { + test.waitUntilDone(); + Request({ + url: "data:text/html,response", + content: { 'key1' : null, 'key2' : 'some value' }, + onComplete: function(response) { + test.assertEqual(response.text, "response?key1=null&key2=some+value"); + test.done(); + } + }).get(); +}; + +// All tests below here require a network connection. They will be commented out +// when checked in. If you'd like to run them, simply uncomment them. +// +// When we have the means, these tests will be converted so that they don't +// require an external server nor a network connection. + +// This is a request to a file that exists. +exports.testStatus200 = function (test) { + var srv = startServerAsync(port, basePath); + + test.waitUntilDone(); + var req = Request({ + url: "http://localhost:" + port + "/test-request.txt", + onComplete: function (response) { + test.assertEqual(this, req, "`this` should be request"); + test.assertEqual(response.status, 200); + test.assertEqual(response.statusText, "OK"); + test.assertEqual(response.headers["Content-Type"], "text/plain"); + test.assertEqual(response.text, "Look ma, no hands!\n"); + srv.stop(function() test.done()); + } + }).get(); +} + +// This tries to get a file that doesn't exist +exports.testStatus404 = function (test) { + var srv = startServerAsync(port, basePath); + + test.waitUntilDone(); + Request({ + // the following URL doesn't exist + url: "http://localhost:" + port + "/test-request-404.txt", + onComplete: function (response) { + test.assertEqual(response.status, 404); + test.assertEqual(response.statusText, "Not Found"); + srv.stop(function() test.done()); + } + }).get(); +} + +/* +// a simple file with a known header +exports.testKnownHeader = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/headers.php", + onComplete: function (response) { + test.assertEqual(response.headers["x-zpao-header"], "Jamba Juice"); + test.done(); + } + }).get(); +} + +// complex headers +exports.testKnownHeader = function (test) { + let headers = { + "x-zpao-header": "Jamba Juice is: delicious", + "x-zpao-header-2": "foo, bar", + "Set-Cookie": "foo=bar\nbaz=foo" + } + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/complex_headers.php", + onComplete: function (response) { + for (k in headers) { + test.assertEqual(response.headers[k], headers[k]); + } + test.done(); + } + }).get(); +} +*/ + +exports.testSimpleJSON = function (test) { + var srv = startServerAsync(port, basePath); + + test.waitUntilDone(); + Request({ + url: "http://localhost:" + port + "/test-request.json", + onComplete: function (response) { + assertDeepEqual(test, response.json, { foo: "bar" }); + srv.stop(function() test.done()); + } + }).get(); +} + +exports.testInvalidJSON = function (test) { + var srv = startServerAsync(port, basePath); + + test.waitUntilDone(); + Request({ + url: "http://localhost:" + port + "/test-request-invalid.json", + onComplete: function (response) { + test.assertEqual(response.json, null); + srv.stop(function() test.done()); + } + }).get(); +} + +/* +exports.testGetWithParamsNotContent = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php?foo=bar", + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : { foo: "bar" } + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} + +exports.testGetWithContent = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php", + content: { foo: "bar" }, + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : { foo: "bar" } + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} + +exports.testGetWithParamsAndContent = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php?foo=bar", + content: { baz: "foo" }, + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : { foo: "bar", baz: "foo" } + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} + +exports.testSimplePost = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php", + content: { foo: "bar" }, + onComplete: function (response) { + let expected = { + "POST": { foo: "bar" }, + "GET" : [] + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).post(); +} + +exports.testEncodedContent = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php", + content: "foo=bar&baz=foo", + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : { foo: "bar", baz: "foo" } + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} + +exports.testEncodedContentWithSpaces = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php", + content: "foo=bar+hop!&baz=foo", + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : { foo: "bar hop!", baz: "foo" } + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} + +exports.testGetWithArray = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php", + content: { foo: [1, 2], baz: "foo" }, + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : { foo: [1, 2], baz: "foo" } + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} + +exports.testGetWithNestedArray = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php", + content: { foo: [1, 2, [3, 4]], bar: "baz" }, + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : this.content + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} + +exports.testGetWithNestedArray = function (test) { + test.waitUntilDone(); + let request = Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php", + content: { + foo: [1, 2, { + omg: "bbq", + "all your base!": "are belong to us" + }], + bar: "baz" + }, + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : request.content + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} +*/ + +// This is not a proper testing for deep equal, but it's good enough for my uses +// here. It will do type coercion to check equality, but that's good here. Data +// coming from the server will be stringified and so "0" should be equal to 0. +function assertDeepEqual(test, obj1, obj2, msg) { + function equal(o1, o2) { + // cover our non-object cases well enough + if (o1 == o2) + return true; + if (typeof(o1) != typeof(o2)) + return false; + if (typeof(o1) != "object") + return o1 == o2; + + let e = true; + for (let [key, val] in Iterator(o1)) { + e = e && key in o2 && equal(o2[key], val); + if (!e) + break; + } + for (let [key, val] in Iterator(o2)) { + e = e && key in o1 && equal(o1[key], val); + if (!e) + break; + } + return e; + } + msg = msg || "objects not equal - " + JSON.stringify(obj1) + " != " + + JSON.stringify(obj2); + test.assert(equal(obj1, obj2), msg); +} diff --git a/tools/addon-sdk-1.4/packages/addon-kit/tests/test-selection.js b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-selection.js new file mode 100644 index 0000000..cd96072 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-selection.js @@ -0,0 +1,490 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Eric H. Jung + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +let timer = require("timer"); +let {Cc,Ci} = require("chrome"); + +// Arbitrary delay needed to avoid weird behavior. +// TODO: We need to find all uses of this and replace them +// with more deterministic solutions. +const ARB_DELAY = 100; + +// Select all divs elements in an HTML document +function selectAllDivs(window) { + let divs = window.document.getElementsByTagName("div"); + let s = window.getSelection(); + if (s.rangeCount > 0) + s.removeAllRanges(); + for (let i = 0; i < divs.length; i++) { + let range = window.document.createRange(); + range.selectNode(divs[i]); + s.addRange(range); + } +} + +function selectTextarea(window, from, to) { + let textarea = window.document.getElementsByTagName("textarea")[0]; + + from = from || 0; + to = to || textarea.value.length; + + textarea.setSelectionRange(from, to); + textarea.focus(); +} + +function primeTestCase(html, test, callback) { + let tabBrowser = require("tab-browser"); + let dataURL = "data:text/html," + encodeURI(html); + let tracker = tabBrowser.whenContentLoaded( + function(window) { + if (window.document.location.href != dataURL) + return; + callback(window, test); + timer.setTimeout(function() { + tracker.unload(); + test.done(); + window.close(); + }, + ARB_DELAY); + } + ); + tabBrowser.addTab(dataURL); +} + +const DIV1 = '
bar
'; +const DIV2 = '
noodles
'; +const HTML_MULTIPLE = '' + DIV1 + DIV2 + ''; +const HTML_SINGLE = '' + DIV1 + ''; + +// Tests of contiguous + +exports.testContiguousMultiple = function testContiguousMultiple(test) { + let selection = require("selection"); + primeTestCase(HTML_MULTIPLE, test, function(window, test) { + selectAllDivs(window); + test.assertEqual(selection.isContiguous, false, + "selection.isContiguous multiple works."); + }); + + test.waitUntilDone(5000); +}; + +exports.testContiguousSingle = function testContiguousSingle(test) { + let selection = require("selection"); + primeTestCase(HTML_SINGLE, test, function(window, test) { + selectAllDivs(window); + test.assertEqual(selection.isContiguous, true, + "selection.isContiguous single works."); + }); + + test.waitUntilDone(5000); +}; + +exports.testContiguousWithoutSelection = + function testContiguousWithoutSelection(test) { + let selection = require("selection"); + primeTestCase(HTML_SINGLE, test, function(window, test) { + test.assertEqual(selection.isContiguous, false, + "selection.isContiguous without selection works."); + }); + + test.waitUntilDone(5000); +}; + +/** + * Test that setting the contiguous property has no effect. + */ +/*exports.testSetContiguous = function testSetContiguous(test) { + let selection = require("selection"); + primeTestCase(HTML_MULTIPLE, test, function(window, test) { + selectAllDivs(window); + try { + selection.isContiguous = true; + test.assertEqual(selection.isContiguous, false, + "setting selection.isContiguous doesn't work (as expected)."); + } + catch (e) { + test.pass("setting selection.isContiguous doesn't work (as expected)."); + } + }); + + test.waitUntilDone(5000); +};*/ + + +// HTML tests + +exports.testGetHTMLSingleSelection = function testGetHTMLSingleSelection(test) { + let selection = require("selection"); + primeTestCase(HTML_SINGLE, test, function(window, test) { + selectAllDivs(window); + test.assertEqual(selection.html, DIV1, "get html selection works"); + }); + + test.waitUntilDone(5000); +}; + +/* Myk's comments: This is fine. However, it reminds me to figure out and + specify whether iteration is ordered. If so, we'll want to change this + test in the future to test that the discontiguous selections are returned in + the appropriate order. In the meantime, add a comment to that effect here */ +exports.testGetHTMLMultipleSelection = + function testGetHTMLMultipleSelection(test) { + let selection = require("selection"); + primeTestCase(HTML_MULTIPLE, test, function(window, test) { + selectAllDivs(window); + let assertions = false; + for each (let i in selection) { + test.assertEqual(true, [DIV1, DIV2].some(function(t) t == i.html), + "get multiple selection html works"); + assertions = true; + } + // Ensure we ran at least one assertEqual() + test.assert(assertions, "No assertions were called"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetHTMLNull = function testGetHTMLNull(test) { + let selection = require("selection"); + primeTestCase(HTML_SINGLE, test, function(window, test) { + test.assertEqual(selection.html, null, "get html null works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetHTMLWeird = function testGetHTMLWeird(test) { + let selection = require("selection"); + // If the getter is used when there are contiguous selections, the first + // selection should be returned + primeTestCase(HTML_MULTIPLE, test, function(window, test) { + selectAllDivs(window); + test.assertEqual(selection.html, DIV1, "get html weird works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetHTMLNullInTextareaSelection = + function testGetHTMLNullInTextareaSelection(test) { + let selection = require("selection"); + + primeTestCase(TEXT_FIELD, test, function(window, test) { + selectTextarea(window); + + test.assertEqual(selection.html, null, "get html null in textarea works") + }); + + test.waitUntilDone(5000); +}; + +const REPLACEMENT_HTML = "Lorem ipsum dolor sit amet"; + +exports.testSetHTMLSelection = function testSetHTMLSelection(test) { + let selection = require("selection"); + primeTestCase(HTML_SINGLE, test, function(window, test) { + selectAllDivs(window); + selection.html = REPLACEMENT_HTML; + test.assertEqual(selection.html, "" + REPLACEMENT_HTML + + "", "selection html works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testSetHTMLException = function testSetHTMLException(test) { + let selection = require("selection"); + primeTestCase(HTML_SINGLE, test, function(window, test) { + try { + selection.html = REPLACEMENT_HTML; + test.fail("set HTML throws when there's no selection."); + } + catch (e) { + test.pass("set HTML throws when there's no selection."); + } + }); + + test.waitUntilDone(5000); +}; + +const TEXT1 = "foo"; +const TEXT2 = "noodles"; +const TEXT_MULTIPLE = "
" + TEXT1 + "
" + TEXT2 + + "
"; +const TEXT_SINGLE = "
" + TEXT1 + "
"; +const TEXT_FIELD = ""; + +// Text tests + +exports.testSetHTMLinTextareaSelection = + function testSetHTMLinTextareaSelection(test) { + let selection = require("selection"); + + primeTestCase(TEXT_FIELD, test, function(window, test) { + selectTextarea(window); + + // HTML string is set as plain text in textareas, that's because + // `selection.html` and `selection.text` are basically aliases when a + // value is set. See bug 677269 + selection.html = REPLACEMENT_HTML; + + test.assertEqual(selection.text, REPLACEMENT_HTML, + "set selection html in textarea works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetTextSingleSelection = + function testGetTextSingleSelection(test) { + let selection = require("selection"); + primeTestCase(TEXT_SINGLE, test, function(window, test) { + selectAllDivs(window); + test.assertEqual(selection.text, TEXT1, "get text selection works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetTextMultipleSelection = + function testGetTextMultipleSelection(test) { + let selection = require("selection"); + primeTestCase(TEXT_MULTIPLE, test, function(window, test) { + selectAllDivs(window); + let assertions = false; + for each (let i in selection) { + test.assertEqual(true, [TEXT1, TEXT2].some(function(t) t == i.text), + "get multiple selection text works"); + assertions = true; + } + // Ensure we ran at least one assertEqual() + test.assert(assertions, "No assertions were called"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetTextNull = function testGetTextNull(test) { + let selection = require("selection"); + primeTestCase(TEXT_SINGLE, test, function(window, test) { + test.assertEqual(selection.text, null, "get text null works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetTextWeird = function testGetTextWeird(test) { + let selection = require("selection"); + // If the getter is used when there are contiguous selections, the first + // selection should be returned + primeTestCase(TEXT_MULTIPLE, test, function(window, test) { + selectAllDivs(window); + test.assertEqual(selection.text, TEXT1, "get text weird works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetTextNullInTextareaSelection = + function testGetTextInTextareaSelection(test) { + let selection = require("selection"); + + primeTestCase(TEXT_FIELD, test, function(window, test) { + test.assertEqual(selection.text, null, "get text null in textarea works") + }); + + test.waitUntilDone(5000); +}; + +exports.testGetTextInTextareaSelection = + function testGetTextInTextareaSelection(test) { + let selection = require("selection"); + + primeTestCase(TEXT_FIELD, test, function(window, test) { + selectTextarea(window); + + test.assertEqual(selection.text, TEXT1, "get text null in textarea works") + }); + + test.waitUntilDone(5000); +}; + +const REPLACEMENT_TEXT = "Lorem ipsum dolor sit amet"; + +exports.testSetTextSelection = function testSetTextSelection(test) { + let selection = require("selection"); + primeTestCase(TEXT_SINGLE, test, function(window, test) { + selectAllDivs(window); + selection.text = REPLACEMENT_TEXT; + test.assertEqual(selection.text, REPLACEMENT_TEXT, "selection text works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testSetHTMLException = function testSetHTMLException(test) { + let selection = require("selection"); + primeTestCase(TEXT_SINGLE, test, function(window, test) { + try { + selection.text = REPLACEMENT_TEXT; + test.fail("set HTML throws when there's no selection."); + } + catch (e) { + test.pass("set HTML throws when there's no selection."); + } + }); + + test.waitUntilDone(5000); +}; + +exports.testSetTextInTextareaSelection = + function testSetTextInTextareaSelection(test) { + let selection = require("selection"); + + primeTestCase(TEXT_FIELD, test, function(window, test) { + selectTextarea(window); + + selection.text = REPLACEMENT_TEXT; + + test.assertEqual(selection.text, REPLACEMENT_TEXT, + "set selection text in textarea works"); + }); + + test.waitUntilDone(5000); +}; + +// Iterator tests + +exports.testIterator = function testIterator(test) { + let selection = require("selection"); + let selectionCount = 0; + primeTestCase(TEXT_MULTIPLE, test, function(window, test) { + selectAllDivs(window); + for each (let i in selection) + selectionCount++; + test.assertEqual(2, selectionCount, "iterator works."); + }); + + test.waitUntilDone(5000); +}; + +exports.testIteratorWithTextareaSelection = + function testIteratorWithTextareaSelection(test) { + let selection = require("selection"); + let selectionCount = 0; + + primeTestCase(TEXT_FIELD, test, function(window, test) { + selectTextarea(window); + + for each (let i in selection) + selectionCount++; + + test.assertEqual(1, selectionCount, "iterator works in textarea."); + }); + + test.waitUntilDone(5000); +}; + +/* onSelect tests */ + +/* +function sendSelectionSetEvent(window) { + const Ci = Components.interfaces; + let utils = window.QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIDOMWindowUtils); + if (!utils.sendSelectionSetEvent(0, 1, false)) + dump("**** sendSelectionSetEvent did not select anything\n"); + else + dump("**** sendSelectionSetEvent succeeded\n"); +} + +// testOnSelect() requires nsIDOMWindowUtils, which is only available in +// Firefox 3.7+. +exports.testOnSelect = function testOnSelect(test) { + let selection = require("selection"); + let callbackCount = 0; + primeTestCase(TEXT_SINGLE, test, function(window, test) { + selection.onSelect = function() {callbackCount++}; + // Now simulate the user selecting stuff + sendSelectionSetEvent(window); + selection.text = REPLACEMENT_TEXT; + test.assertEqual(1, callbackCount, "onSelect text listener works."); + //test.pass(); + //test.done(); + }); + + test.waitUntilDone(5000); +}; + +// testOnSelectExceptionNoBubble() requires nsIDOMWindowUtils, which is only +// available in Firefox 3.7+. +exports.testOnSelectExceptionNoBubble = + function testOnSelectTextSelection(test) { + let selection = require("selection"); + primeTestCase(HTML_SINGLE, test, function(window, test) { + selection.onSelect = function() { + throw new Error("Exception thrown in testOnSelectExceptionNoBubble"); + }; + // Now simulate the user selecting stuff + sendSelectionSetEvent(window); + test.pass("onSelect catches exceptions."); + }); + + test.waitUntilDone(5000); +}; +*/ + +// If the module doesn't support the app we're being run in, require() will +// throw. In that case, remove all tests above from exports, and add one dummy +// test that passes. +try { + require("selection"); +} +catch (err) { + // This bug should be mentioned in the error message. + let bug = "https://bugzilla.mozilla.org/show_bug.cgi?id=560716"; + if (err.message.indexOf(bug) < 0) + throw err; + for (let [prop, val] in Iterator(exports)) { + if (/^test/.test(prop) && typeof(val) === "function") + delete exports[prop]; + } + exports.testAppNotSupported = function (test) { + test.pass("The selection module does not support this application."); + }; +} diff --git a/tools/addon-sdk-1.4/packages/addon-kit/tests/test-simple-prefs.js b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-simple-prefs.js new file mode 100644 index 0000000..581eca1 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-simple-prefs.js @@ -0,0 +1,180 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Erik Vold (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + + +const { Loader } = require("./helpers"); +const setTimeout = require("timers").setTimeout; +const notify = require("observer-service").notify; +const { jetpackID } = require("@packaging"); + +exports.testSetGetBool = function(test) { + test.waitUntilDone(); + + let loader = Loader(module); + let sp = loader.require("simple-prefs").prefs; + + test.assertEqual(sp.test, undefined, "Value should not exist"); + sp.test = true; + test.assert(sp.test, "Value read should be the value previously set"); + + loader.unload(); + test.done(); +}; + +exports.testSetGetInt = function(test) { + test.waitUntilDone(); + + // Load the module once, set a value. + let loader = Loader(module); + let sp = loader.require("simple-prefs").prefs; + + test.assertEqual(sp["test-int"], undefined, "Value should not exist"); + sp["test-int"] = 1; + test.assertEqual(sp["test-int"], 1, "Value read should be the value previously set"); + + loader.unload(); + test.done(); +}; + +exports.testSetComplex = function(test) { + test.waitUntilDone(); + + let loader = Loader(module); + let sp = loader.require("simple-prefs").prefs; + + try { + sp["test-complex"] = {test: true}; + test.fail("Complex values are not allowed"); + } + catch (e) { + test.pass("Complex values are not allowed"); + } + + loader.unload(); + test.done(); +}; + +exports.testSetGetString = function(test) { + test.waitUntilDone(); + + let loader = Loader(module); + let sp = loader.require("simple-prefs").prefs; + + test.assertEqual(sp["test-string"], undefined, "Value should not exist"); + sp["test-string"] = "test"; + test.assertEqual(sp["test-string"], "test", "Value read should be the value previously set"); + + loader.unload(); + test.done(); +}; + +exports.testHasAndRemove = function(test) { + test.waitUntilDone(); + + let loader = Loader(module); + let sp = loader.require("simple-prefs").prefs; + + sp.test = true; + test.assert(("test" in sp), "Value exists"); + delete sp.test; + test.assertEqual(sp.test, undefined, "Value should be undefined"); + + loader.unload(); + test.done(); + +}; + +exports.testPrefListener = function(test) { + test.waitUntilDone(); + + let loader = Loader(module); + let sp = loader.require("simple-prefs"); + + let listener = function(prefName) { + test.assertEqual(prefName, "test-listen", "The prefs listener heard the right event"); + test.done(); + }; + + sp.on("test-listen", listener); + + sp.prefs["test-listen"] = true; + loader.unload(); +}; + +exports.testBtnListener = function(test) { + test.waitUntilDone(); + + let loader = Loader(module); + let sp = loader.require("simple-prefs"); + + sp.on("test-btn-listen", function() { + test.pass("Button press event was heard"); + test.done(); + }); + notify((jetpackID + "-cmdPressed"), "", "test-btn-listen"); + + loader.unload(); +}; + +exports.testPrefRemoveListener = function(test) { + test.waitUntilDone(); + + let loader = Loader(module); + let sp = loader.require("simple-prefs"); + let counter = 0; + + let listener = function() { + test.pass("The prefs listener was not removed yet"); + + if (++counter > 1) + test.fail("The prefs listener was not removed"); + + sp.removeListener("test-listen2", listener); + + sp.prefs["test-listen2"] = false; + + setTimeout(function() { + test.pass("The prefs listener was removed"); + loader.unload(); + test.done(); + }, 250); + }; + + sp.on("test-listen2", listener); + + sp.prefs["test-listen2"] = true; +}; diff --git a/tools/addon-sdk-1.4/packages/addon-kit/tests/test-simple-storage.js b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-simple-storage.js new file mode 100644 index 0000000..95e4689 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-simple-storage.js @@ -0,0 +1,346 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim:set ts=2 sw=2 sts=2 et filetype=javascript + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Drew Willcoxon (Original Author) + * Irakli Gozalishvili + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const file = require("file"); +const prefs = require("preferences-service"); + +const QUOTA_PREF = "extensions.addon-sdk.simple-storage.quota"; + +let {Cc,Ci} = require("chrome"); + +const { Loader } = require("./helpers"); +const options = require("@packaging"); + +let storeFile = Cc["@mozilla.org/file/directory_service;1"]. + getService(Ci.nsIProperties). + get("ProfD", Ci.nsIFile); +storeFile.append("jetpack"); +storeFile.append(options.jetpackID); +storeFile.append("simple-storage"); +storeFile.append("store.json"); +let storeFilename = storeFile.path; + +exports.testSetGet = function (test) { + test.waitUntilDone(); + + // Load the module once, set a value. + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function (storage) { + test.assert(file.exists(storeFilename), "Store file should exist"); + + // Load the module again and make sure the value stuck. + loader = newLoader(test); + ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function (storage) { + file.remove(storeFilename); + test.done(); + }; + test.assertEqual(ss.storage.foo, val, "Value should persist"); + loader.unload(); + }; + let val = "foo"; + ss.storage.foo = val; + test.assertEqual(ss.storage.foo, val, "Value read should be value set"); + loader.unload(); +}; + +exports.testSetGetRootArray = function (test) { + setGetRoot(test, [1, 2, 3], function (arr1, arr2) { + if (arr1.length !== arr2.length) + return false; + for (let i = 0; i < arr1.length; i++) { + if (arr1[i] !== arr2[i]) + return false; + } + return true; + }); +}; + +exports.testSetGetRootBool = function (test) { + setGetRoot(test, true); +}; + +exports.testSetGetRootFunction = function (test) { + setGetRootError(test, function () {}, + "Setting storage to a function should fail"); +}; + +exports.testSetGetRootNull = function (test) { + setGetRoot(test, null); +}; + +exports.testSetGetRootNumber = function (test) { + setGetRoot(test, 3.14); +}; + +exports.testSetGetRootObject = function (test) { + setGetRoot(test, { foo: 1, bar: 2 }, function (obj1, obj2) { + for (let [prop, val] in Iterator(obj1)) { + if (!(prop in obj2) || obj2[prop] !== val) + return false; + } + for (let [prop, val] in Iterator(obj2)) { + if (!(prop in obj1) || obj1[prop] !== val) + return false; + } + return true; + }); +}; + +exports.testSetGetRootString = function (test) { + setGetRoot(test, "sho' 'nuff"); +}; + +exports.testSetGetRootUndefined = function (test) { + setGetRootError(test, undefined, "Setting storage to undefined should fail"); +}; + +exports.testEmpty = function (test) { + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + loader.unload(); + test.assert(!file.exists(storeFilename), "Store file should not exist"); +}; + +exports.testMalformed = function (test) { + let stream = file.open(storeFilename, "w"); + stream.write("i'm not json"); + stream.close(); + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + let empty = true; + for (let key in ss.storage) { + empty = false; + break; + } + test.assert(empty, "Malformed storage should cause root to be empty"); + loader.unload(); +}; + +// Go over quota and handle it by listener. +exports.testQuotaExceededHandle = function (test) { + test.waitUntilDone(); + prefs.set(QUOTA_PREF, 18); + + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + ss.on("OverQuota", function () { + test.pass("OverQuota was emitted as expected"); + test.assertEqual(this, ss, "`this` should be simple storage"); + ss.storage = { x: 4, y: 5 }; + + manager(loader).jsonStore.onWrite = function () { + loader = newLoader(test); + ss = loader.require("simple-storage"); + let numProps = 0; + for (let prop in ss.storage) + numProps++; + test.assert(numProps, 2, + "Store should contain 2 values: " + ss.storage.toSource()); + test.assertEqual(ss.storage.x, 4, "x value should be correct"); + test.assertEqual(ss.storage.y, 5, "y value should be correct"); + manager(loader).jsonStore.onWrite = function (storage) { + prefs.reset(QUOTA_PREF); + test.done(); + }; + loader.unload(); + }; + loader.unload(); + }); + // This will be JSON.stringify()ed to: {"a":1,"b":2,"c":3} (19 bytes) + ss.storage = { a: 1, b: 2, c: 3 }; + manager(loader).jsonStore.write(); +}; + +// Go over quota but don't handle it. The last good state should still persist. +exports.testQuotaExceededNoHandle = function (test) { + test.waitUntilDone(); + prefs.set(QUOTA_PREF, 5); + + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + + manager(loader).jsonStore.onWrite = function (storage) { + loader = newLoader(test); + ss = loader.require("simple-storage"); + test.assertEqual(ss.storage, val, + "Value should have persisted: " + ss.storage); + ss.storage = "some very long string that is very long"; + ss.on("OverQuota", function () { + test.pass("OverQuota emitted as expected"); + manager(loader).jsonStore.onWrite = function () { + test.fail("Over-quota value should not have been written"); + }; + loader.unload(); + + loader = newLoader(test); + ss = loader.require("simple-storage"); + test.assertEqual(ss.storage, val, + "Over-quota value should not have been written, " + + "old value should have persisted: " + ss.storage); + loader.unload(); + prefs.reset(QUOTA_PREF); + test.done(); + }); + manager(loader).jsonStore.write(); + }; + + let val = "foo"; + ss.storage = val; + loader.unload(); +}; + +exports.testQuotaUsage = function (test) { + test.waitUntilDone(); + + let quota = 21; + prefs.set(QUOTA_PREF, quota); + + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + + // {"a":1} (7 bytes) + ss.storage = { a: 1 }; + test.assertEqual(ss.quotaUsage, 7 / quota, "quotaUsage should be correct"); + + // {"a":1,"bb":2} (14 bytes) + ss.storage = { a: 1, bb: 2 }; + test.assertEqual(ss.quotaUsage, 14 / quota, "quotaUsage should be correct"); + + // {"a":1,"bb":2,"cc":3} (21 bytes) + ss.storage = { a: 1, bb: 2, cc: 3 }; + test.assertEqual(ss.quotaUsage, 21 / quota, "quotaUsage should be correct"); + + manager(loader).jsonStore.onWrite = function () { + prefs.reset(QUOTA_PREF); + test.done(); + }; + loader.unload(); +}; + +exports.testUninstall = function (test) { + test.waitUntilDone(); + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function () { + test.assert(file.exists(storeFilename), "Store file should exist"); + + loader = newLoader(test); + ss = loader.require("simple-storage"); + loader.unload("uninstall"); + test.assert(!file.exists(storeFilename), "Store file should be removed"); + test.done(); + }; + ss.storage.foo = "foo"; + loader.unload(); +}; + +exports.testSetNoSetRead = function (test) { + test.waitUntilDone(); + + // Load the module, set a value. + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function (storage) { + test.assert(file.exists(storeFilename), "Store file should exist"); + + // Load the module again but don't access ss.storage. + loader = newLoader(test); + ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function (storage) { + test.fail("Nothing should be written since `storage` was not accessed."); + }; + loader.unload(); + + // Load the module a third time and make sure the value stuck. + loader = newLoader(test); + ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function (storage) { + file.remove(storeFilename); + test.done(); + }; + test.assertEqual(ss.storage.foo, val, "Value should persist"); + loader.unload(); + }; + let val = "foo"; + ss.storage.foo = val; + test.assertEqual(ss.storage.foo, val, "Value read should be value set"); + loader.unload(); +}; + +function manager(loader) loader.sandbox("simple-storage").manager; + +function newLoader() Loader(module); + +function setGetRoot(test, val, compare) { + test.waitUntilDone(); + + compare = compare || function (a, b) a === b; + + // Load the module once, set a value. + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function () { + test.assert(file.exists(storeFilename), "Store file should exist"); + + // Load the module again and make sure the value stuck. + loader = newLoader(test); + ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function () { + file.remove(storeFilename); + test.done(); + }; + test.assert(compare(ss.storage, val), "Value should persist"); + loader.unload(); + }; + ss.storage = val; + test.assert(compare(ss.storage, val), "Value read should be value set"); + loader.unload(); +} + +function setGetRootError(test, val, msg) { + let pred = "storage must be one of the following types: " + + "array, boolean, null, number, object, string"; + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + test.assertRaises(function () ss.storage = val, pred, msg); + loader.unload(); +} diff --git a/tools/addon-sdk-1.4/packages/addon-kit/tests/test-tabs.js b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-tabs.js new file mode 100644 index 0000000..ae98312 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-tabs.js @@ -0,0 +1,905 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dietrich Ayala (Original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +"use strict"; + +var {Cc,Ci} = require("chrome"); +const { Loader } = require("./helpers"); + +// test tab.activeTab getter +exports.testActiveTab_getter = function(test) { + test.waitUntilDone(); + + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + + let url = "data:text/html,foo"; + require("tab-browser").addTab( + url, + { + onLoad: function(e) { + test.assert(tabs.activeTab); + test.assertEqual(tabs.activeTab.url, url); + test.assertEqual(tabs.activeTab.title, "foo"); + closeBrowserWindow(window, function() test.done()); + } + } + ); + }); +}; + +// test 'BrowserWindow' instance creation on tab 'activate' event +// See bug 648244: there was a infinite loop. +exports.testBrowserWindowCreationOnActivate = function(test) { + test.waitUntilDone(); + + let windows = require("windows").browserWindows; + let tabs = require("tabs"); + + let gotActivate = false; + + tabs.once('activate', function onActivate(eventTab) { + test.assert(windows.activeWindow, "Is able to fetch activeWindow"); + gotActivate = true; + }); + + openBrowserWindow(function(window, browser) { + test.assert(gotActivate, "Received activate event before openBrowserWindow's callback is called"); + closeBrowserWindow(window, function () test.done()); + }); +} + +// test tab.activeTab setter +exports.testActiveTab_setter = function(test) { + test.waitUntilDone(); + + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url = "data:text/html,foo"; + + tabs.on('ready', function onReady(tab) { + tabs.removeListener('ready', onReady); + test.assertEqual(tabs.activeTab.url, "about:blank", "activeTab url has not changed"); + test.assertEqual(tab.url, url, "url of new background tab matches"); + tabs.on('activate', function onActivate(eventTab) { + tabs.removeListener('activate', onActivate); + test.assertEqual(tabs.activeTab.url, url, "url after activeTab setter matches"); + test.assertEqual(eventTab, tab, "event argument is the activated tab"); + test.assertEqual(eventTab, tabs.activeTab, "the tab is the active one"); + closeBrowserWindow(window, function() test.done()); + }); + tab.activate(); + }) + + tabs.open({ + url: url, + inBackground: true + }); + }); +}; + +exports.testAutomaticDestroy = function(test) { + test.waitUntilDone(); + + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + + // Create a second tab instance that we will destroy + let called = false; + + let loader = Loader(module); + let tabs2 = loader.require("tabs"); + tabs2.on('open', function onOpen(tab) { + called = true; + }); + + loader.unload(); + + // Fire a tab event an ensure that this destroyed tab is inactive + tabs.once('open', function () { + require("timer").setTimeout(function () { + test.assert(!called, "Unloaded tab module is destroyed and inactive"); + closeBrowserWindow(window, function() test.done()); + }, 0); + }); + + tabs.open("data:text/html,foo"); + + }); +}; + +// test tab properties +exports.testTabProperties = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs= require("tabs"); + let url = "data:text/html,foofoo"; + tabs.open({ + url: url, + onReady: function(tab) { + test.assertEqual(tab.title, "foo", "title of the new tab matches"); + test.assertEqual(tab.url, url, "URL of the new tab matches"); + test.assert(tab.favicon, "favicon of the new tab is not empty"); + test.assertEqual(tab.style, null, "style of the new tab matches"); + test.assertEqual(tab.index, 1, "index of the new tab matches"); + test.assertNotEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); + closeBrowserWindow(window, function() test.done()); + } + }); + }); +}; + +// test tabs iterator and length property +exports.testTabsIteratorAndLength = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let startCount = 0; + for each (let t in tabs) startCount++; + test.assertEqual(startCount, tabs.length, "length property is correct"); + let url = "data:text/html,default"; + tabs.open(url); + tabs.open(url); + tabs.open({ + url: url, + onOpen: function(tab) { + let count = 0; + for each (let t in tabs) count++; + test.assertEqual(count, startCount + 3, "iterated tab count matches"); + test.assertEqual(startCount + 3, tabs.length, "iterated tab count matches length property"); + closeBrowserWindow(window, function() test.done()); + } + }); + }); +}; + +// test tab.url setter +exports.testTabLocation = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url1 = "data:text/html,foo"; + let url2 = "data:text/html,bar"; + + tabs.on('ready', function onReady(tab) { + if (tab.url != url2) + return; + tabs.removeListener('ready', onReady); + test.pass("tab.load() loaded the correct url"); + closeBrowserWindow(window, function() test.done()); + }); + + tabs.open({ + url: url1, + onOpen: function(tab) { + tab.url = url2 + } + }); + }); +}; + +// test tab.close() +exports.testTabClose = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url = "data:text/html,foo"; + + test.assertNotEqual(tabs.activeTab.url, url, "tab is now the active tab"); + tabs.on('ready', function onReady(tab) { + tabs.removeListener('ready', onReady); + test.assertEqual(tabs.activeTab.url, tab.url, "tab is now the active tab"); + tab.close(function() { + closeBrowserWindow(window, function() test.done()); + }); + test.assertNotEqual(tabs.activeTab.url, url, "tab is no longer the active tab"); + }); + + tabs.open(url); + }); +}; + +// test tab.reload() +exports.testTabReload = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url = "data:text/html,"; + + tabs.open({ url: url, onReady: function onReady(tab) { + tab.removeListener("ready", onReady); + + browser.addEventListener( + "load", + function onLoad() { + browser.removeEventListener("load", onLoad, true); + + browser.addEventListener( + "load", + function onReload() { + browser.removeEventListener("load", onReload, true); + test.pass("the tab was loaded again"); + test.assertEqual(tab.url, url, "the tab has the same URL"); + closeBrowserWindow(window, function() test.done()); + }, + true + ); + tab.reload(); + }, + true + ); + }}); + }); +}; + +// test tab.move() +exports.testTabMove = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url = "data:text/html,foo"; + + tabs.open({ + url: url, + onOpen: function(tab) { + test.assertEqual(tab.index, 1, "tab index before move matches"); + tab.index = 0; + test.assertEqual(tab.index, 0, "tab index after move matches"); + closeBrowserWindow(window, function() test.done()); + } + }); + }); +}; + +// open tab with default options +exports.testOpen = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url = "data:text/html,default"; + tabs.open({ + url: url, + onReady: function(tab) { + test.assertEqual(tab.url, url, "URL of the new tab matches"); + test.assertEqual(window.content.location, url, "URL of active tab in the current window matches"); + closeBrowserWindow(window, function() test.done()); + } + }); + }); +}; + +// open pinned tab +exports.testOpenPinned = function(test) { + const xulApp = require("xul-app"); + if (xulApp.versionInRange(xulApp.platformVersion, "2.0b2", "*")) { + // test tab pinning + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url = "data:text/html,default"; + tabs.open({ + url: url, + isPinned: true, + onOpen: function(tab) { + test.assertEqual(tab.isPinned, true, "The new tab is pinned"); + closeBrowserWindow(window, function() test.done()); + } + }); + }); + } + else { + test.pass("Pinned tabs are not supported in this application."); + } +}; + +// pin/unpin opened tab +exports.testPinUnpin = function(test) { + const xulApp = require("xul-app"); + if (xulApp.versionInRange(xulApp.platformVersion, "2.0b2", "*")) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url = "data:text/html,default"; + tabs.open({ + url: url, + onOpen: function(tab) { + tab.pin(); + test.assertEqual(tab.isPinned, true, "The tab was pinned correctly"); + tab.unpin(); + test.assertEqual(tab.isPinned, false, "The tab was unpinned correctly"); + closeBrowserWindow(window, function() test.done()); + } + }); + }); + } + else { + test.pass("Pinned tabs are not supported in this application."); + } +}; + +// open tab in background +exports.testInBackground = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let activeUrl = tabs.activeTab.url; + let url = "data:text/html,background"; + test.assertEqual(activeWindow, window, "activeWindow matches this window"); + tabs.on('ready', function onReady(tab) { + tabs.removeListener('ready', onReady); + test.assertEqual(tabs.activeTab.url, activeUrl, "URL of active tab has not changed"); + test.assertEqual(tab.url, url, "URL of the new background tab matches"); + test.assertEqual(activeWindow, window, "a new window was not opened"); + test.assertNotEqual(tabs.activeTab.url, url, "URL of active tab is not the new URL"); + closeBrowserWindow(window, function() test.done()); + }); + tabs.open({ + url: url, + inBackground: true + }); + }); +}; + +// open tab in new window +exports.testOpenInNewWindow = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + + let cache = []; + let windowUtils = require("window-utils"); + let wt = new windowUtils.WindowTracker({ + onTrack: function(win) { + cache.push(win); + }, + onUntrack: function(win) { + cache.splice(cache.indexOf(win), 1) + } + }); + let startWindowCount = cache.length; + + let url = "data:text/html,newwindow"; + tabs.open({ + url: url, + inNewWindow: true, + onReady: function(tab) { + let newWindow = cache[cache.length - 1]; + test.assertEqual(cache.length, startWindowCount + 1, "a new window was opened"); + test.assertEqual(activeWindow, newWindow, "new window is active"); + test.assertEqual(tab.url, url, "URL of the new tab matches"); + test.assertEqual(newWindow.content.location, url, "URL of new tab in new window matches"); + test.assertEqual(tabs.activeTab.url, url, "URL of activeTab matches"); + for (var i in cache) cache[i] = null; + wt.unload(); + closeBrowserWindow(newWindow, function() { + closeBrowserWindow(window, function() test.done()); + }); + } + }); + }); +}; + +// onOpen event handler +exports.testTabsEvent_onOpen = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + var tabs = require("tabs"); + let url = "data:text/html,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) { + test.assertEqual(++eventCount, 2, "both listeners notified"); + tabs.removeListener('open', listener1); + tabs.removeListener('open', listener2); + closeBrowserWindow(window, function() test.done()); + }); + + tabs.open(url); + }); +}; + +// onClose event handler +exports.testTabsEvent_onClose = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + var tabs = require("tabs"); + let url = "data:text/html,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) { + test.assertEqual(++eventCount, 2, "both listeners notified"); + tabs.removeListener('close', listener1); + tabs.removeListener('close', listener2); + closeBrowserWindow(window, function() test.done()); + }); + + tabs.on('ready', function onReady(tab) { + tabs.removeListener('ready', onReady); + tab.close(); + }); + + tabs.open(url); + }); +}; + +// onClose event handler when a window is closed +exports.testTabsEvent_onCloseWindow = function(test) { + test.waitUntilDone(); + + openBrowserWindow(function(window, browser) { + var tabs = require("tabs"); + + let closeCount = 0, individualCloseCount = 0; + function listener() { + closeCount++; + } + tabs.on('close', listener); + + // One tab is already open with the window + let openTabs = 1; + function testCasePossiblyLoaded() { + if (++openTabs == 4) { + beginCloseWindow(); + } + } + + tabs.open({ + url: "data:text/html,tab2", + onOpen: function() testCasePossiblyLoaded(), + onClose: function() individualCloseCount++ + }); + + tabs.open({ + url: "data:text/html,tab3", + onOpen: function() testCasePossiblyLoaded(), + onClose: function() individualCloseCount++ + }); + + tabs.open({ + url: "data:text/html,tab4", + onOpen: function() testCasePossiblyLoaded(), + onClose: function() individualCloseCount++ + }); + + function beginCloseWindow() { + closeBrowserWindow(window, function testFinished() { + tabs.removeListener("close", listener); + + test.assertEqual(closeCount, 4, "Correct number of close events received"); + test.assertEqual(individualCloseCount, 3, + "Each tab with an attached onClose listener received a close " + + "event when the window was closed"); + + test.done(); + }); + } + + }); +} + +// onReady event handler +exports.testTabsEvent_onReady = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + var tabs = require("tabs"); + let url = "data:text/html,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) { + test.assertEqual(++eventCount, 2, "both listeners notified"); + tabs.removeListener('ready', listener1); + tabs.removeListener('ready', listener2); + closeBrowserWindow(window, function() test.done()); + }); + + tabs.open(url); + }); +}; + +// onActivate event handler +exports.testTabsEvent_onActivate = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + var tabs = require("tabs"); + let url = "data:text/html,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) { + test.assertEqual(++eventCount, 2, "both listeners notified"); + tabs.removeListener('activate', listener1); + tabs.removeListener('activate', listener2); + closeBrowserWindow(window, function() test.done()); + }); + + tabs.open(url); + }); +}; + +// onDeactivate event handler +exports.testTabsEvent_onDeactivate = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + var tabs = require("tabs"); + let url = "data:text/html,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) { + test.assertEqual(++eventCount, 2, "both listeners notified"); + tabs.removeListener('deactivate', listener1); + tabs.removeListener('deactivate', listener2); + closeBrowserWindow(window, function() test.done()); + }); + + tabs.on('open', function onOpen(tab) { + tabs.removeListener('open', onOpen); + tabs.open("data:text/html,foo"); + }); + + tabs.open(url); + }); +}; + +// per-tab event handlers +exports.testPerTabEvents = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + var tabs = require("tabs"); + let eventCount = 0; + + tabs.open({ + url: "data:text/html,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() { + test.assertEqual(eventCount, 1, "both listeners notified"); + tab.removeListener('ready', listener1); + tab.removeListener('ready', listener2); + closeBrowserWindow(window, function() test.done()); + }); + } + }); + }); +}; + +exports.testAttachOnOpen = function (test) { + // Take care that attach has to be called on tab ready and not on tab open. + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + + tabs.open({ + url: "data:text/html,foobar", + onOpen: function (tab) { + let worker = tab.attach({ + contentScript: 'self.postMessage(document.location.href); ', + onMessage: function (msg) { + test.assertEqual(msg, "about:blank", + "Worker document url is about:blank on open"); + worker.destroy(); + closeBrowserWindow(window, function() test.done()); + } + }); + } + }); + + }); +} + +exports.testAttachOnMultipleDocuments = function (test) { + // Example of attach that process multiple tab documents + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let firstLocation = "data:text/html,foobar"; + let secondLocation = "data:text/html,bar"; + let thirdLocation = "data:text/html,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) { + test.assertEqual(msg, firstLocation, + "Worker url is equal to the 1st document"); + tab.url = secondLocation; + }, + onDetach: function () { + detachEventCount++; + test.pass("Got worker1 detach event"); + test.assertRaises(function () { + worker1.postMessage("ex-1"); + }, + /The page has been destroyed/, + "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) { + test.assertEqual(msg, secondLocation, + "Worker url is equal to the 2nd document"); + tab.url = thirdLocation; + }, + onDetach: function () { + detachEventCount++; + test.pass("Got worker2 detach event"); + test.assertRaises(function () { + worker2.postMessage("ex-2"); + }, + /The page has been destroyed/, + "postMessage throw because worker2 is destroyed"); + checkEnd(); + } + }); + worker2.postMessage("new-doc-2"); + } + else if (onReadyCount == 3) { + + tab.close(); + + } + + } + }); + + function checkEnd() { + if (detachEventCount != 2) + return; + + test.pass("Got all detach events"); + + closeBrowserWindow(window, function() test.done()); + } + + }); +} + + +exports.testAttachWrappers = function (test) { + // Check that content script has access to wrapped values by default + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let document = "data:text/html,"; + 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) { + test.assertEqual(msg, true, "Worker has wrapped objects ("+count+")"); + if (count++ == 1) + closeBrowserWindow(window, function() test.done()); + } + }); + } + }); + + }); +} + +/* +// We do not offer unwrapped access to DOM since bug 601295 landed +// See 660780 to track progress of unwrap feature +exports.testAttachUnwrapped = function (test) { + // Check that content script has access to unwrapped values through unsafeWindow + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let document = "data:text/html,"; + 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) { + test.assertEqual(msg, true, "Worker has access to javascript content globals ("+count+")"); + closeBrowserWindow(window, function() test.done()); + } + }); + } + }); + + }); +} +*/ + +exports['test window focus changes active tab'] = function(test) { + test.waitUntilDone(); + let win1 = openBrowserWindow(function() { + let win2 = openBrowserWindow(function() { + let tabs = require("tabs"); + tabs.on("activate", function onActivate() { + tabs.removeListener("activate", onActivate); + test.pass("activate was called on windows focus change."); + closeBrowserWindow(win1, function() { + closeBrowserWindow(win2, function() { test.done(); }); + }); + }); + win1.focus(); + }, "data:text/html,test window focus changes active tab

Window #2"); + }, "data:text/html,test window focus changes active tab

Window #1"); +}; + +exports['test ready event on new window tab'] = function(test) { + test.waitUntilDone(); + let uri = encodeURI("data:text/html,Waiting for ready event!"); + + require("tabs").on("ready", function onReady(tab) { + if (tab.url === uri) { + require("tabs").removeListener("ready", onReady); + test.pass("ready event was emitted"); + closeBrowserWindow(window, function() { + test.done(); + }); + } + }); + + let window = openBrowserWindow(function(){}, uri); +}; +/******************* 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 { + require("timer").setTimeout(function () { + callback(window, browsers[0]); + }, 10); + } catch (e) { console.exception(e); } + } + }, true); + } + + return window; +} + +// Helper for calling code at window close +function closeBrowserWindow(window, callback) { + window.addEventListener("unload", function unload() { + window.removeEventListener("unload", unload, false); + callback(); + }, false); + window.close(); +} + +// If the module doesn't support the app we're being run in, require() will +// throw. In that case, remove all tests above from exports, and add one dummy +// test that passes. +try { + require("tabs"); +} +catch (err) { + // This bug should be mentioned in the error message. + let bug = "https://bugzilla.mozilla.org/show_bug.cgi?id=560716"; + if (err.message.indexOf(bug) < 0) + throw err; + for (let [prop, val] in Iterator(exports)) { + if (/^test/.test(prop) && typeof(val) === "function") + delete exports[prop]; + } + exports.testAppNotSupported = function (test) { + test.pass("the tabs module does not support this application."); + }; +} diff --git a/tools/addon-sdk-1.4/packages/addon-kit/tests/test-timers.js b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-timers.js new file mode 100644 index 0000000..98ea613 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-timers.js @@ -0,0 +1,44 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Shane Tomlinson (Original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +const timers = require("timers"); + +exports.testTimeout = function (test) { + test.waitUntilDone(); + timers.setTimeout(function () { + test.pass("timers.setTimeout works"); + test.done(); + }, 0); +} diff --git a/tools/addon-sdk-1.4/packages/addon-kit/tests/test-widget.js b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-widget.js new file mode 100644 index 0000000..a5ab83c --- /dev/null +++ b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-widget.js @@ -0,0 +1,950 @@ +const {Cc,Ci} = require("chrome"); +const { Loader } = require('./helpers'); + +exports.testConstructor = function(test) { + + const tabBrowser = require("tab-browser"); + + test.waitUntilDone(30000); + + const widgets = require("widget"); + const url = require("url"); + const windowUtils = require("window-utils"); + + let browserWindow = windowUtils.activeBrowserWindow; + let doc = browserWindow.document; + let AddonsMgrListener = browserWindow.AddonsMgrListener; + + function container() doc.getElementById("addon-bar"); + function widgetCount() container() ? container().getElementsByTagName("toolbaritem").length : 0; + let widgetStartCount = widgetCount(); + function widgetNode(index) container() ? container().getElementsByTagName("toolbaritem")[index] : null; + + // Test basic construct/destroy + AddonsMgrListener.onInstalling(); + let w = widgets.Widget({ id: "fooID", label: "foo", content: "bar" }); + AddonsMgrListener.onInstalled(); + test.assertEqual(widgetCount(), widgetStartCount + 1, "panel has correct number of child elements after widget construction"); + + // test widget height + test.assertEqual(widgetNode(0).firstChild.boxObject.height, 16, "widget has correct default height"); + + AddonsMgrListener.onUninstalling(); + w.destroy(); + AddonsMgrListener.onUninstalled(); + w.destroy(); + test.pass("Multiple destroys do not cause an error"); + test.assertEqual(widgetCount(), widgetStartCount, "panel has correct number of child elements after destroy"); + + // Test automatic widget destroy on unload + let loader = Loader(module); + let widgetsFromLoader = loader.require("widget"); + let widgetStartCount = widgetCount(); + let w = widgetsFromLoader.Widget({ id: "fooID", label: "foo", content: "bar" }); + test.assertEqual(widgetCount(), widgetStartCount + 1, "widget has been correctly added"); + loader.unload(); + test.assertEqual(widgetCount(), widgetStartCount, "widget has been destroyed on module unload"); + + // Test nothing + test.assertRaises( + function() widgets.Widget({}), + "The widget must have a non-empty label property.", + "throws on no properties"); + + // Test no label + test.assertRaises( + function() widgets.Widget({content: "foo"}), + "The widget must have a non-empty label property.", + "throws on no label"); + + // Test empty label + test.assertRaises( + function() widgets.Widget({label: "", content: "foo"}), + "The widget must have a non-empty label property.", + "throws on empty label"); + + // Test no content or image + test.assertRaises( + function() widgets.Widget({id: "fooID", label: "foo"}), + "No content or contentURL property found. Widgets must have one or the other.", + "throws on no content"); + + // Test empty content, no image + test.assertRaises( + function() widgets.Widget({id:"fooID", label: "foo", content: ""}), + "No content or contentURL property found. Widgets must have one or the other.", + "throws on empty content"); + + // Test empty image, no content + test.assertRaises( + function() widgets.Widget({id:"fooID", label: "foo", image: ""}), + "No content or contentURL property found. Widgets must have one or the other.", + "throws on empty content"); + + // Test empty content, empty image + test.assertRaises( + function() widgets.Widget({id:"fooID", label: "foo", content: "", image: ""}), + "No content or contentURL property found. Widgets must have one or the other.", + "throws on empty content"); + + // Test duplicated ID + let duplicateID = widgets.Widget({id: "foo", label: "foo", content: "bar"}); + test.assertRaises( + function() widgets.Widget({id: "foo", label: "bar", content: "bar"}), + /This widget ID is already used:/, + "throws on duplicated id"); + duplicateID.destroy(); + + // Test duplicate label, different ID + let w1 = widgets.Widget({id: "id1", label: "foo", content: "bar"}); + let w2 = widgets.Widget({id: "id2", label: "foo", content: "bar"}); + w1.destroy(); + w2.destroy(); + + // Test position restore on create/destroy/create + // Create 3 ordered widgets + let w1 = widgets.Widget({id: "first", label:"first", content: "bar"}); + let w2 = widgets.Widget({id: "second", label:"second", content: "bar"}); + let w3 = widgets.Widget({id: "third", label:"third", content: "bar"}); + // Remove the middle widget + test.assertEqual(widgetNode(1).getAttribute("label"), "second", "second widget is the second widget inserted"); + w2.destroy(); + test.assertEqual(widgetNode(1).getAttribute("label"), "third", "second widget is removed, so second widget is now the third one"); + w2 = widgets.Widget({id: "second", label:"second", content: "bar"}); + test.assertEqual(widgetNode(1).getAttribute("label"), "second", "second widget is created again, at the same location"); + // Cleanup this testcase + AddonsMgrListener.onUninstalling(); + w1.destroy(); + w2.destroy(); + w3.destroy(); + AddonsMgrListener.onUninstalled(); + + // Test concurrent widget module instances on addon-bar hiding + let loader = Loader(module); + let anotherWidgetsInstance = loader.require("widget"); + test.assert(container().collapsed, "UI is hidden when no widgets"); + AddonsMgrListener.onInstalling(); + let w1 = widgets.Widget({id: "foo", label: "foo", content: "bar"}); + // Ideally we would let AddonsMgrListener display the addon bar + // But, for now, addon bar is immediatly displayed by sdk code + // https://bugzilla.mozilla.org/show_bug.cgi?id=627484 + test.assert(!container().collapsed, "UI is already visible when we just added the widget"); + AddonsMgrListener.onInstalled(); + test.assert(!container().collapsed, "UI become visible when we notify AddonsMgrListener about end of addon installation"); + let w2 = anotherWidgetsInstance.Widget({id: "bar", label: "bar", content: "foo"}); + test.assert(!container().collapsed, "UI still visible when we add a second widget"); + AddonsMgrListener.onUninstalling(); + w1.destroy(); + AddonsMgrListener.onUninstalled(); + test.assert(!container().collapsed, "UI still visible when we remove one of two widgets"); + AddonsMgrListener.onUninstalling(); + w2.destroy(); + test.assert(!container().collapsed, "UI is still visible when we have removed all widget but still not called onUninstalled"); + AddonsMgrListener.onUninstalled(); + test.assert(container().collapsed, "UI is hidden when we have removed all widget and called onUninstalled"); + + // Helper for testing a single widget. + // Confirms proper addition and content setup. + function testSingleWidget(widgetOptions) { + // We have to display which test is being run, because here we do not + // use the regular test framework but rather a custom one that iterates + // the `tests` array. + console.info("executing: " + widgetOptions.id); + + let startCount = widgetCount(); + let widget = widgets.Widget(widgetOptions); + let node = widgetNode(startCount); + test.assert(node, "widget node at index"); + test.assertEqual(node.tagName, "toolbaritem", "widget element is correct"); + test.assertEqual(widget.width + "px", node.style.minWidth, "widget width is correct"); + test.assertEqual(widgetCount(), startCount + 1, "container has correct number of child elements"); + let content = node.firstElementChild; + test.assert(content, "found content"); + test.assertMatches(content.tagName, /iframe|image/, "content is iframe or image"); + return widget; + } + + // Array of widgets to test + // and a function to test them. + let tests = []; + function nextTest() { + test.assertEqual(widgetCount(), 0, "widget in last test property cleaned itself up"); + if (!tests.length) + test.done(); + else + require("timer").setTimeout(tests.shift(), 0); + } + function doneTest() nextTest(); + + // text widget + tests.push(function testTextWidget() testSingleWidget({ + id: "text", + label: "text widget", + content: "oh yeah", + contentScript: "self.postMessage(document.body.innerHTML);", + contentScriptWhen: "end", + onMessage: function (message) { + test.assertEqual(this.content, message, "content matches"); + this.destroy(); + doneTest(); + } + })); + + // html widget + tests.push(function testHTMLWidget() testSingleWidget({ + id: "html", + label: "html widget", + content: "
oh yeah
", + contentScript: "self.postMessage(document.body.innerHTML);", + contentScriptWhen: "end", + onMessage: function (message) { + test.assertEqual(this.content, message, "content matches"); + this.destroy(); + doneTest(); + } + })); + + // image url widget + tests.push(function testImageURLWidget() testSingleWidget({ + id: "image", + label: "image url widget", + contentURL: require("self").data.url("test.html"), + contentScript: "self.postMessage({title: document.title, " + + "tag: document.body.firstElementChild.tagName, " + + "content: document.body.firstElementChild.innerHTML});", + contentScriptWhen: "end", + onMessage: function (message) { + test.assertEqual(message.title, "foo", "title matches"); + test.assertEqual(message.tag, "P", "element matches"); + test.assertEqual(message.content, "bar", "element content matches"); + this.destroy(); + doneTest(); + } + })); + + // web uri widget + tests.push(function testWebURIWidget() testSingleWidget({ + id: "web", + label: "web uri widget", + contentURL: require("self").data.url("test.html"), + contentScript: "self.postMessage({title: document.title, " + + "tag: document.body.firstElementChild.tagName, " + + "content: document.body.firstElementChild.innerHTML});", + contentScriptWhen: "end", + onMessage: function (message) { + test.assertEqual(message.title, "foo", "title matches"); + test.assertEqual(message.tag, "P", "element matches"); + test.assertEqual(message.content, "bar", "element content matches"); + this.destroy(); + doneTest(); + } + })); + + // event: onclick + content + tests.push(function testOnclickEventContent() testSingleWidget({ + id: "click", + label: "click test widget - content", + content: "
foo
", + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('click', true, true ); " + + "document.getElementById('me').dispatchEvent(evt);", + contentScriptWhen: "end", + onClick: function() { + test.pass("onClick called"); + this.destroy(); + doneTest(); + } + })); + + // event: onmouseover + content + tests.push(function testOnmouseoverEventContent() testSingleWidget({ + id: "mouseover", + label: "mouseover test widget - content", + content: "
foo
", + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('mouseover', true, true ); " + + "document.getElementById('me').dispatchEvent(evt);", + contentScriptWhen: "end", + onMouseover: function() { + test.pass("onMouseover called"); + this.destroy(); + doneTest(); + } + })); + + // event: onmouseout + content + tests.push(function testOnmouseoutEventContent() testSingleWidget({ + id: "mouseout", + label: "mouseout test widget - content", + content: "
foo
", + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('mouseout', true, true ); " + + "document.getElementById('me').dispatchEvent(evt);", + contentScriptWhen: "end", + onMouseout: function() { + test.pass("onMouseout called"); + this.destroy(); + doneTest(); + } + })); + + // event: onclick + image + tests.push(function testOnclickEventImage() testSingleWidget({ + id: "click", + label: "click test widget - image", + contentURL: require("self").data.url("moz_favicon.ico"), + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('click', true, true ); " + + "document.body.firstElementChild.dispatchEvent(evt);", + contentScriptWhen: "end", + onClick: function() { + test.pass("onClick called"); + this.destroy(); + doneTest(); + } + })); + + // event: onmouseover + image + tests.push(function testOnmouseoverEventImage() testSingleWidget({ + id: "mouseover", + label: "mouseover test widget - image", + contentURL: require("self").data.url("moz_favicon.ico"), + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('mouseover', true, true ); " + + "document.body.firstElementChild.dispatchEvent(evt);", + contentScriptWhen: "end", + onMouseover: function() { + test.pass("onMouseover called"); + this.destroy(); + doneTest(); + } + })); + + // event: onmouseout + image + tests.push(function testOnmouseoutEventImage() testSingleWidget({ + id: "mouseout", + label: "mouseout test widget - image", + contentURL: require("self").data.url("moz_favicon.ico"), + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('mouseout', true, true ); " + + "document.body.firstElementChild.dispatchEvent(evt);", + contentScriptWhen: "end", + onMouseout: function() { + test.pass("onMouseout called"); + this.destroy(); + doneTest(); + } + })); + + // test multiple widgets + tests.push(function testMultipleWidgets() { + let w1 = widgets.Widget({id: "first", label: "first widget", content: "first content"}); + let w2 = widgets.Widget({id: "second", label: "second widget", content: "second content"}); + + w1.destroy(); + w2.destroy(); + + doneTest(); + }); + + // test updating widget content + let loads = 0; + tests.push(function testUpdatingWidgetContent() testSingleWidget({ + id: "content", + label: "content update test widget", + content: "
foo
", + contentScript: "self.postMessage(1)", + contentScriptWhen: "ready", + onMessage: function(message) { + if (!this.flag) { + this.content = "
bar
"; + this.flag = 1; + } + else { + test.assertEqual(this.content, "
bar
"); + this.destroy(); + doneTest(); + } + } + })); + + // test updating widget contentURL + let url1 = "data:text/html,foodle"; + let url2 = "data:text/html,nistel"; + + tests.push(function testUpdatingContentURL() testSingleWidget({ + id: "content", + label: "content update test widget", + contentURL: url1, + contentScript: "self.postMessage(document.location.href);", + contentScriptWhen: "end", + onMessage: function(message) { + if (!this.flag) { + test.assertEqual(this.contentURL.toString(), url1); + test.assertEqual(message, url1); + this.contentURL = url2; + this.flag = 1; + } + else { + test.assertEqual(this.contentURL.toString(), url2); + test.assertEqual(message, url2); + this.destroy(); + doneTest(); + } + } + })); + + // test tooltip + tests.push(function testTooltip() testSingleWidget({ + id: "text", + label: "text widget", + content: "oh yeah", + tooltip: "foo", + contentScript: "self.postMessage(1)", + contentScriptWhen: "ready", + onMessage: function(message) { + test.assertEqual(this.tooltip, "foo", "tooltip matches"); + this.destroy(); + doneTest(); + } + })); + + // test tooltip fallback to label + tests.push(function testTooltipFallback() testSingleWidget({ + id: "fallback", + label: "fallback", + content: "oh yeah", + contentScript: "self.postMessage(1)", + contentScriptWhen: "ready", + onMessage: function(message) { + test.assertEqual(this.tooltip, this.label, "tooltip fallbacks to label"); + this.destroy(); + doneTest(); + } + })); + + // test updating widget tooltip + let updated = false; + tests.push(function testUpdatingTooltip() testSingleWidget({ + id: "tooltip", + label: "tooltip update test widget", + tooltip: "foo", + content: "
foo
", + contentScript: "self.postMessage(1)", + contentScriptWhen: "ready", + onMessage: function(message) { + this.tooltip = "bar"; + test.assertEqual(this.tooltip, "bar", "tooltip gets updated"); + this.destroy(); + doneTest(); + } + })); + + // test allow attribute + tests.push(function testDefaultAllow() testSingleWidget({ + id: "allow", + label: "allow.script attribute", + content: "", + contentScript: "self.postMessage(document.title)", + onMessage: function(message) { + test.assertEqual(message, "ok", "scripts are evaluated by default"); + this.destroy(); + doneTest(); + } + })); + + tests.push(function testExplicitAllow() testSingleWidget({ + id: "allow", + label: "allow.script attribute", + allow: {script: true}, + content: "", + contentScript: "self.postMessage(document.title)", + onMessage: function(message) { + test.assertEqual(message, "ok", "scripts are evaluated when we want to"); + this.destroy(); + doneTest(); + } + })); + + tests.push(function testExplicitDisallow() testSingleWidget({ + id: "allow", + label: "allow.script attribute", + content: "", + allow: {script: false}, + contentScript: "self.postMessage(document.title)", + onMessage: function(message) { + test.assertNotEqual(message, "ok", "scripts aren't evaluated when " + + "explicitly blocked it"); + this.destroy(); + doneTest(); + } + })); + + // test multiple windows + tests.push(function testMultipleWindows() { + tabBrowser.addTab("about:blank", { inNewWindow: true, onLoad: function(e) { + let browserWindow = e.target.defaultView; + let doc = browserWindow.document; + function container() doc.getElementById("addon-bar"); + function widgetCount2() container() ? container().childNodes.length : 0; + let widgetStartCount2 = widgetCount2(); + + let w1Opts = {id:"first", label: "first widget", content: "first content"}; + let w1 = testSingleWidget(w1Opts); + test.assertEqual(widgetCount2(), widgetStartCount2 + 1, "2nd window has correct number of child elements after first widget"); + + let w2Opts = {id:"second", label: "second widget", content: "second content"}; + let w2 = testSingleWidget(w2Opts); + test.assertEqual(widgetCount2(), widgetStartCount2 + 2, "2nd window has correct number of child elements after second widget"); + + w1.destroy(); + test.assertEqual(widgetCount2(), widgetStartCount2 + 1, "2nd window has correct number of child elements after first destroy"); + w2.destroy(); + test.assertEqual(widgetCount2(), widgetStartCount2, "2nd window has correct number of child elements after second destroy"); + + closeBrowserWindow(browserWindow, function() { + doneTest(); + }); + }}); + }); + + // test window closing + tests.push(function testWindowClosing() { + // 1/ Create a new widget + let w1Opts = { + id:"first", + label: "first widget", + content: "first content", + contentScript: "self.port.on('event', function () self.port.emit('event'))" + }; + let widget = testSingleWidget(w1Opts); + let windows = require("windows").browserWindows; + + // 2/ Retrieve a WidgetView for the initial browser window + let acceptDetach = false; + let mainView = widget.getView(windows.activeWindow); + test.assert(mainView, "Got first widget view"); + mainView.on("detach", function () { + // 8/ End of our test. Accept detach event only when it occurs after + // widget.destroy() + if (acceptDetach) + doneTest(); + else + test.fail("View on initial window should not be destroyed"); + }); + mainView.port.on("event", function () { + // 7/ Receive event sent during 6/ and cleanup our test + acceptDetach = true; + widget.destroy(); + }); + + // 3/ First: open a new browser window + windows.open({ + url: "about:blank", + onOpen: function(window) { + // 4/ Retrieve a WidgetView for this new window + let view = widget.getView(window); + test.assert(view, "Got second widget view"); + view.port.on("event", function () { + test.fail("We should not receive event on the detach view"); + }); + view.on("detach", function () { + // The related view is destroyed + // 6/ Send a custom event + test.assertRaises(function () { + view.port.emit("event"); + }, + /The widget has been destroyed and can no longer be used./, + "emit on a destroyed view should throw"); + widget.port.emit("event"); + }); + + // 5/ Destroy this window + window.close(); + } + }); + }); + + tests.push(function testAddonBarHide() { + // Hide the addon-bar + browserWindow.setToolbarVisibility(container(), false); + + // Then open a browser window and verify that the addon-bar remains hidden + tabBrowser.addTab("about:blank", { inNewWindow: true, onLoad: function(e) { + let browserWindow = e.target.defaultView; + let doc = browserWindow.document; + function container2() doc.getElementById("addon-bar"); + function widgetCount2() container2() ? container2().childNodes.length : 0; + let widgetStartCount2 = widgetCount2(); + + let w1Opts = {id:"first", label: "first widget", content: "first content"}; + let w1 = testSingleWidget(w1Opts); + test.assertEqual(widgetCount2(), widgetStartCount2 + 1, "2nd window has correct number of child elements after widget creation"); + + w1.destroy(); + test.assertEqual(widgetCount2(), widgetStartCount2, "2nd window has correct number of child elements after widget destroy"); + + test.assert(container().collapsed, "1st window has an hidden addon-bar"); + test.assert(container2().collapsed, "2nd window has an hidden addon-bar"); + + browserWindow.setToolbarVisibility(container(), true); + + closeBrowserWindow(browserWindow, function() { + doneTest(); + }); + }}); + }); + + // test widget.width + tests.push(function testWidgetWidth() testSingleWidget({ + id: "text", + label: "test widget.width", + content: "test width", + width: 200, + contentScript: "self.postMessage(1)", + contentScriptWhen: "ready", + onMessage: function(message) { + test.assertEqual(this.width, 200); + + let node = widgetNode(0); + test.assertEqual(this.width, node.style.minWidth.replace("px", "")); + test.assertEqual(this.width, node.firstElementChild.style.width.replace("px", "")); + this.width = 300; + test.assertEqual(this.width, node.style.minWidth.replace("px", "")); + test.assertEqual(this.width, node.firstElementChild.style.width.replace("px", "")); + + this.destroy(); + doneTest(); + } + })); + + // test click handler not respond to right-click + let clickCount = 0; + tests.push(function testNoRightClick() testSingleWidget({ + id: "click-content", + label: "click test widget - content", + content: "
foo
", + contentScript: "var evt = document.createEvent('MouseEvents'); " + + "evt.initMouseEvent('click', true, true, window, " + + " 0, 0, 0, 0, 0, false, false, false, false, 2, null); " + + "document.getElementById('me').dispatchEvent(evt); " + + "evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('click', true, true ); " + + "document.getElementById('me').dispatchEvent(evt); " + + "evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('mouseover', true, true ); " + + "document.getElementById('me').dispatchEvent(evt);", + contentScriptWhen: "end", + onClick: function() clickCount++, + onMouseover: function() { + test.assertEqual(clickCount, 1, "right click wasn't sent to click handler"); + this.destroy(); + doneTest(); + } + })); + + // kick off test execution + doneTest(); +}; + +exports.testPanelWidget1 = function testPanelWidget1(test) { + const widgets = require("widget"); + + let widget1 = widgets.Widget({ + id: "panel1", + label: "panel widget 1", + content: "
foo
", + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('click', true, true ); " + + "document.body.dispatchEvent(evt);", + contentScriptWhen: "end", + panel: require("panel").Panel({ + contentURL: "data:text/html,Look ma, a panel!", + onShow: function() { + widget1.destroy(); + test.pass("panel displayed on click"); + test.done(); + } + }) + }); + test.waitUntilDone(); +}; + +exports.testPanelWidget2 = function testPanelWidget2(test) { + const widgets = require("widget"); + test.assertRaises( + function() { + widgets.Widget({ + id: "panel2", + label: "panel widget 2", + panel: {} + }); + }, + "The option \"panel\" must be one of the following types: null, undefined, object", + "widget.panel must be a Panel object" + ); +}; + +exports.testPanelWidget3 = function testPanelWidget3(test) { + const widgets = require("widget"); + let onClickCalled = false; + let widget3 = widgets.Widget({ + id: "panel3", + label: "panel widget 3", + content: "
foo
", + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('click', true, true ); " + + "document.body.firstElementChild.dispatchEvent(evt);", + contentScriptWhen: "end", + onClick: function() { + onClickCalled = true; + this.panel.show(); + }, + panel: require("panel").Panel({ + contentURL: "data:text/html,Look ma, a panel!", + onShow: function() { + test.assert( + onClickCalled, + "onClick called on click for widget with both panel and onClick" + ); + widget3.destroy(); + test.done(); + } + }) + }); + test.waitUntilDone(); +}; + +exports.testWidgetMessaging = function testWidgetMessaging(test) { + test.waitUntilDone(); + let origMessage = "foo"; + const widgets = require("widget"); + let widget = widgets.Widget({ + id: "foo", + label: "foo", + content: "baz", + contentScriptWhen: "end", + contentScript: "self.on('message', function(data) { self.postMessage(data); }); self.postMessage('ready');", + onMessage: function(message) { + if (message == "ready") + widget.postMessage(origMessage); + else { + test.assertEqual(origMessage, message); + widget.destroy(); + test.done(); + } + } + }); +}; + +exports.testWidgetViews = function testWidgetViews(test) { + test.waitUntilDone(); + const widgets = require("widget"); + let widget = widgets.Widget({ + id: "foo", + label: "foo", + content: "baz", + contentScriptWhen: "ready", + contentScript: "self.on('message', function(data) self.postMessage(data)); self.postMessage('ready')", + onAttach: function(view) { + test.pass("WidgetView created"); + view.on("message", function () { + test.pass("Got message in WidgetView"); + widget.destroy(); + }); + view.on("detach", function () { + test.pass("WidgetView destroyed"); + test.done(); + }); + } + }); + +}; + +exports.testWidgetViewsUIEvents = function testWidgetViewsUIEvents(test) { + test.waitUntilDone(); + const widgets = require("widget"); + let view = null; + let widget = widgets.Widget({ + id: "foo", + label: "foo", + content: "
foo
", + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('click', true, true ); " + + "document.getElementById('me').dispatchEvent(evt);", + contentScriptWhen: "ready", + onAttach: function(attachView) { + view = attachView; + test.pass("Got attach event"); + }, + onClick: function (eventView) { + test.assertEqual(view, eventView, + "event first argument is equal to the WidgetView"); + let view2 = widget.getView(require("windows").browserWindows.activeWindow); + test.assertEqual(view, view2, + "widget.getView return the same WidgetView"); + widget.destroy(); + test.done(); + } + }); +}; + +exports.testWidgetViewsCustomEvents = function testWidgetViewsCustomEvents(test) { + test.waitUntilDone(); + const widgets = require("widget"); + let widget = widgets.Widget({ + id: "foo", + label: "foo", + content: "
foo
", + contentScript: "self.port.emit('event', 'ok');", + contentScriptWhen: "ready", + onAttach: function(view) { + view.port.on("event", function (data) { + test.assertEqual(data, "ok", + "event argument is valid on WidgetView"); + }); + }, + }); + widget.port.on("event", function (data) { + test.assertEqual(data, "ok", + "event argument is valid on Widget"); + widget.destroy(); + test.done(); + }); +}; + +exports.testWidgetViewsTooltip = function testWidgetViewsTooltip(test) { + test.waitUntilDone(); + const widgets = require("widget"); + + let widget = new widgets.Widget({ + id: "foo", + label: "foo", + content: "foo" + }); + let view = widget.getView(require("windows").browserWindows.activeWindow); + widget.tooltip = null; + test.assertEqual(view.tooltip, "foo", + "view tooltip defaults to base widget label"); + test.assertEqual(widget.tooltip, "foo", + "tooltip defaults to base widget label"); + widget.destroy(); + test.done(); +}; + +exports.testWidgetMove = function testWidgetMove(test) { + test.waitUntilDone(); + + let windowUtils = require("window-utils"); + let widgets = require("widget"); + + let browserWindow = windowUtils.activeBrowserWindow; + let doc = browserWindow.document; + + let label = "unique-widget-label"; + let origMessage = "message after node move"; + let gotFirstReady = false; + + let widget = widgets.Widget({ + id: "foo", + label: label, + content: "baz", + contentScriptWhen: "ready", + contentScript: "self.on('message', function(data) { self.postMessage(data); }); self.postMessage('ready');", + onMessage: function(message) { + if (message == "ready") { + if (!gotFirstReady) { + test.pass("Got first ready event"); + let widgetNode = doc.querySelector('toolbaritem[label="' + label + '"]'); + let parent = widgetNode.parentNode; + parent.insertBefore(widgetNode, parent.firstChild); + gotFirstReady = true; + } else { + test.pass("Got second ready event"); + widget.postMessage(origMessage); + } + } + else { + test.assertEqual(origMessage, message, "Got message after node move"); + widget.destroy(); + test.done(); + } + } + }); +}; + +/* +The bug is exhibited when a widget with HTML content has it's content +changed to new HTML content with a pound in it. Because the src of HTML +content is converted to a data URI, the underlying iframe doesn't +consider the content change a navigation change, so doesn't load +the new content. +*/ +exports.testWidgetWithPound = function testWidgetWithPound(test) { + test.waitUntilDone(); + + function getWidgetContent(widget) { + let windowUtils = require("window-utils"); + let browserWindow = windowUtils.activeBrowserWindow; + let doc = browserWindow.document; + let widgetNode = doc.querySelector('toolbaritem[label="' + widget.label + '"]'); + test.assert(widgetNode, 'found widget node in the front-end'); + return widgetNode.firstChild.contentDocument.body.innerHTML; + } + + let widgets = require("widget"); + let count = 0; + let widget = widgets.Widget({ + id: "1", + label: "foo", + content: "foo", + contentScript: "window.addEventListener('load', self.postMessage, false);", + onMessage: function() { + count++; + if (count == 1) { + widget.content = "foo#"; + } + else { + test.assertEqual(getWidgetContent(widget), "foo#", "content updated to pound?"); + widget.destroy(); + test.done(); + } + } + }); +}; + +/******************* helpers *********************/ + +// Helper for calling code at window close +function closeBrowserWindow(window, callback) { + require("timer").setTimeout(function() { + window.addEventListener("unload", function onUnload() { + window.removeEventListener("unload", onUnload, false); + callback(); + }, false); + window.close(); + }, 0); +} + +// ADD NO TESTS BELOW THIS LINE! /////////////////////////////////////////////// + +// If the module doesn't support the app we're being run in, require() will +// throw. In that case, remove all tests above from exports, and add one dummy +// test that passes. +try { + require("widget"); +} +catch (err) { + // This bug should be mentioned in the error message. + let bug = "https://bugzilla.mozilla.org/show_bug.cgi?id=560716"; + if (err.message.indexOf(bug) < 0) + throw err; + for (let [prop, val] in Iterator(exports)) { + if (/^test/.test(prop) && typeof(val) === "function") + delete exports[prop]; + } + exports.testAppNotSupported = function (test) { + test.pass("context-menu does not support this application."); + }; +} + diff --git a/tools/addon-sdk-1.4/packages/addon-kit/tests/test-windows.js b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-windows.js new file mode 100644 index 0000000..e2824f8 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/addon-kit/tests/test-windows.js @@ -0,0 +1,342 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Felipe Gomes (Original author) + * Irakli Gozalishvili + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const {Cc, Ci} = require("chrome"); +const { setTimeout } = require("timer"); +const { Loader } = require('./helpers'); +const wm = Cc["@mozilla.org/appshell/window-mediator;1"]. + getService(Ci.nsIWindowMediator); +let browserWindows; + +function getTestRunnerWindow() wm.getMostRecentWindow("test:runner") + +exports.testOpenAndCloseWindow = function(test) { + test.waitUntilDone(); + + test.assertEqual(browserWindows.length, 1, "Only one window open"); + + browserWindows.open({ + url: "data:text/html,windows API test", + onOpen: function(window) { + test.assertEqual(this, browserWindows, + "The 'this' object is the windows object."); + test.assertEqual(window.tabs.length, 1, "Only one tab open"); + test.assertEqual(browserWindows.length, 2, "Two windows open"); + window.tabs.activeTab.on('ready', function onReady(tab) { + tab.removeListener('ready', onReady); + test.assert(window.title.indexOf("windows API test") != -1, + "URL correctly loaded"); + window.close(); + }); + }, + onClose: function(window) { + test.assertEqual(window.tabs.length, 0, "Tabs were cleared"); + test.assertEqual(browserWindows.length, 1, "Only one window open"); + test.done(); + } + }); +}; + +exports.testAutomaticDestroy = function(test) { + + test.waitUntilDone(); + let windows = browserWindows; + + // Create a second windows instance that we will unload + let called = false; + let loader = Loader(module); + let windows2 = loader.require("windows").browserWindows; + windows2.on("open", function() { + called = true; + }); + + loader.unload(); + + // Fire a windows event and check that this unloaded instance is inactive + windows.open({ + url: "data:text/html,foo", + onOpen: function(window) { + setTimeout(function () { + test.assert(!called, + "Unloaded windows instance is destroyed and inactive"); + window.close(function () { + test.done(); + }); + }); + } + }); + +}; + +exports.testOnOpenOnCloseListeners = function(test) { + test.waitUntilDone(); + let windows = browserWindows; + + test.assertEqual(browserWindows.length, 1, "Only one window open"); + + let received = { + listener1: false, + listener2: false, + listener3: false, + listener4: false + } + + function listener1() { + test.assertEqual(this, windows, "The 'this' object is the windows object."); + if (received.listener1) + test.fail("Event received twice"); + received.listener1 = true; + } + + function listener2() { + if (received.listener2) + test.fail("Event received twice"); + received.listener2 = true; + } + + function listener3() { + test.assertEqual(this, windows, "The 'this' object is the windows object."); + if (received.listener3) + test.fail("Event received twice"); + received.listener3 = true; + } + + function listener4() { + if (received.listener4) + test.fail("Event received twice"); + received.listener4 = true; + } + + windows.on('open', listener1); + windows.on('open', listener2); + windows.on('close', listener3); + windows.on('close', listener4); + + function verify() { + test.assert(received.listener1, "onOpen handler called"); + test.assert(received.listener2, "onOpen handler called"); + test.assert(received.listener3, "onClose handler called"); + test.assert(received.listener4, "onClose handler called"); + + windows.removeListener('open', listener1); + windows.removeListener('open', listener2); + windows.removeListener('close', listener3); + windows.removeListener('close', listener4); + test.done(); + } + + + windows.open({ + url: "data:text/html,foo", + onOpen: function(window) { + window.close(verify); + } + }); +}; + +exports.testWindowTabsObject = function(test) { + test.waitUntilDone(); + + browserWindows.open({ + url: "data:text/html,tab 1", + onOpen: function onOpen(window) { + test.assertEqual(window.tabs.length, 1, "Only 1 tab open"); + + window.tabs.open({ + url: "data:text/html,tab 2", + inBackground: true, + onReady: function onReady(newTab) { + test.assertEqual(window.tabs.length, 2, "New tab open"); + test.assertEqual(newTab.title, "tab 2", "Correct new tab title"); + test.assertEqual(window.tabs.activeTab.title, "tab 1", "Correct active tab"); + + let i = 1; + for each (let tab in window.tabs) + test.assertEqual(tab.title, "tab " + i++, "Correct title"); + + window.close(); + } + }); + }, + onClose: function onClose(window) { + test.assertEqual(window.tabs.length, 0, "No more tabs on closed window"); + test.done(); + } + }); +}; + +exports.testActiveWindow = function(test) { + const xulApp = require("xul-app"); + if (xulApp.versionInRange(xulApp.platformVersion, "1.9.2", "1.9.2.*")) { + test.pass("This test is disabled on 3.6. For more information, see bug 598525"); + return; + } + + let windows = browserWindows; + + // API window objects + let window2, window3; + + // Raw window objects + let nonBrowserWindow = getTestRunnerWindow(), rawWindow2, rawWindow3; + + test.waitUntilDone(); + + let testSteps = [ + function() { + test.assertEqual(windows.length, 3, "Correct number of browser windows"); + let count = 0; + for (let window in windows) + count++; + test.assertEqual(count, 3, "Correct number of windows returned by iterator"); + + rawWindow2.focus(); + continueAfterFocus(rawWindow2); + }, + function() { + nonBrowserWindow.focus(); + continueAfterFocus(nonBrowserWindow); + }, + function() { + /** + * Bug 614079: This test fails intermittently on some specific linux + * environnements, without being able to reproduce it in same + * distribution with same window manager. + * Disable it until being able to reproduce it easily. + + // On linux, focus is not consistent, so we can't be sure + // what window will be on top. + // Here when we focus "non-browser" window, + // Any Browser window may be selected as "active". + test.assert(windows.activeWindow == window2 || windows.activeWindow == window3, + "Non-browser windows aren't handled by this module"); + */ + window2.activate(); + continueAfterFocus(rawWindow2); + }, + function() { + test.assertEqual(windows.activeWindow.title, window2.title, "Correct active window - 2"); + window3.activate(); + continueAfterFocus(rawWindow3); + }, + function() { + test.assertEqual(windows.activeWindow.title, window3.title, "Correct active window - 3"); + nonBrowserWindow.focus(); + finishTest(); + } + ]; + + windows.open({ + url: "data:text/html,window 2", + onOpen: function(window) { + window2 = window; + rawWindow2 = wm.getMostRecentWindow("navigator:browser"); + + windows.open({ + url: "data:text/html,window 3", + onOpen: function(window) { + window.tabs.activeTab.on('ready', function onReady() { + window3 = window; + rawWindow3 = wm.getMostRecentWindow("navigator:browser"); + nextStep() + }); + } + }); + } + }); + + function nextStep() { + if (testSteps.length > 0) + testSteps.shift()(); + } + + function continueAfterFocus(targetWindow) { + + // Based on SimpleTest.waitForFocus + var fm = Cc["@mozilla.org/focus-manager;1"]. + getService(Ci.nsIFocusManager); + + var childTargetWindow = {}; + fm.getFocusedElementForWindow(targetWindow, true, childTargetWindow); + childTargetWindow = childTargetWindow.value; + + var focusedChildWindow = {}; + if (fm.activeWindow) { + fm.getFocusedElementForWindow(fm.activeWindow, true, focusedChildWindow); + focusedChildWindow = focusedChildWindow.value; + } + + var focused = (focusedChildWindow == childTargetWindow); + if (focused) { + nextStep(); + } else { + childTargetWindow.addEventListener("focus", function focusListener() { + childTargetWindow.removeEventListener("focus", focusListener, true); + nextStep(); + }, true); + } + + } + + function finishTest() { + window3.close(function() { + window2.close(function() { + test.done(); + }); + }); + } +}; + +// If the module doesn't support the app we're being run in, require() will +// throw. In that case, remove all tests above from exports, and add one dummy +// test that passes. +try { + browserWindows = require("windows").browserWindows; +} +catch (err) { + // This bug should be mentioned in the error message. + let bug = "https://bugzilla.mozilla.org/show_bug.cgi?id=571449"; + if (err.message.indexOf(bug) < 0) + throw err; + for (let [prop, val] in Iterator(exports)) { + if (/^test/.test(prop) && typeof(val) === "function") + delete exports[prop]; + } + exports.testAppNotSupported = function (test) { + test.pass("the windows module does not support this application."); + }; +} diff --git a/tools/addon-sdk-1.4/packages/api-utils/README.md b/tools/addon-sdk-1.4/packages/api-utils/README.md new file mode 100644 index 0000000..e973e4c --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/README.md @@ -0,0 +1,31 @@ +API Utils provides a basic CommonJS infrastructure for +developing traditional XULRunner add-ons and applications. It is +the basis for the Add-on SDK. + +To address issues present in traditional add-on development, +API Utils provides mechanisms for: + +* writing and executing test cases, inspired by Python's [nose][] + package, +* tracking JS objects of interest to aid in memory profiling and leak + detection, +* registering callbacks that perform cleanup tasks when modules are + unloaded, +* easily reporting errors with full stack tracebacks. + +API Utils also has the following characteristics: + +* Beautiful, concise documentation. +* A rigorous test suite ensuring that the library doesn't break as the + Mozilla platform evolves. +* Solid developer ergonomics ensuring that developers can easily find + out why something they're doing isn't working. + +API Utils is intended to be very small and only contain the bare +minimum of functionality that all add-ons need. + +Note that the API Utils package has not fully stabilized yet, meaning that +we do still expect to make incompatible changes to its APIs in future releases +of the SDK. + + [nose]: http://code.google.com/p/python-nose/ diff --git a/tools/addon-sdk-1.4/packages/api-utils/data/content-proxy.js b/tools/addon-sdk-1.4/packages/api-utils/data/content-proxy.js new file mode 100644 index 0000000..6eadce9 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/data/content-proxy.js @@ -0,0 +1,834 @@ +"use strict"; + +let Ci = Components.interfaces; + +/** + * Access key that allows privileged code to unwrap proxy wrappers through + * valueOf: + * let xpcWrapper = proxyWrapper.valueOf(UNWRAP_ACCESS_KEY); + * This key should only be used by proxy unit test. + */ + const UNWRAP_ACCESS_KEY = {}; + + + /** + * Returns a closure that wraps arguments before calling the given function, + * which can be given to native functions that accept a function, such that when + * the closure is called, the given function is called with wrapped arguments. + * + * @param fun {Function} + * the function for which to create a closure wrapping its arguments + * @param obj {Object} + * target object from which `fun` comes from + * (optional, for debugging purpose) + * @param name {String} + * name of the attribute from which `fun` is binded on `obj` + * (optional, for debugging purpose) + * + * Example: + * function contentScriptListener(event) {} + * let wrapper = ContentScriptFunctionWrapper(contentScriptListener); + * xray.addEventListener("...", wrapper, false); + * -> Allow to `event` to be wrapped + */ +function ContentScriptFunctionWrapper(fun, obj, name) { + if ("___proxy" in fun && typeof fun.___proxy == "function") + return fun.___proxy; + + let wrappedFun = function () { + let args = []; + for (let i = 0, l = arguments.length; i < l; i++) + args.push(wrap(arguments[i])); + + //console.log("Called from native :"+obj+"."+name); + //console.log(">args "+arguments.length); + //console.log(fun); + + // Native code can execute this callback with `this` being the wrapped + // function. For example, window.mozRequestAnimationFrame. + if (this == wrappedFun) + return fun.apply(fun, args); + + return fun.apply(wrap(this), args); + }; + + Object.defineProperty(fun, "___proxy", {value : wrappedFun, + writable : false, + enumerable : false, + configurable : false}); + + return wrappedFun; +} + +/** + * Returns a closure that unwraps arguments before calling the `fun` function, + * which can be used to build a wrapper for a native function that accepts + * wrapped arguments, since native function only accept unwrapped arguments. + * + * @param fun {Function} + * the function to wrap + * @param originalObject {Object} + * target object from which `fun` comes from + * (optional, for debugging purpose) + * @param name {String} + * name of the attribute from which `fun` is binded on `originalObject` + * (optional, for debugging purpose) + * + * Example: + * wrapper.appendChild = NativeFunctionWrapper(xray.appendChild, xray); + * wrapper.appendChild(anotherWrapper); + * -> Allow to call xray.appendChild with unwrapped version of anotherWrapper + */ +function NativeFunctionWrapper(fun, originalObject, name) { + return function () { + let args = []; + let obj = this && typeof this.valueOf == "function" ? + this.valueOf(UNWRAP_ACCESS_KEY) : this; + + for (let i = 0, l = arguments.length; i < l; i++) + args.push( unwrap(arguments[i], obj, name) ); + + //if (name != "toString") + //console.log(">>calling native ["+(name?name:'#closure#')+"]: \n"+fun.apply+"\n"+obj+"\n("+args.join(', ')+")\nthis :"+obj+"from:"+originalObject+"\n"); + + // Need to use Function.prototype.apply.apply because XMLHttpRequest + // is a function (typeof return 'function') and fun.apply is null :/ + let unwrapResult = Function.prototype.apply.apply(fun, [obj, args]); + let result = wrap(unwrapResult, obj, name); + + //console.log("<< "+rr+" -> "+r); + + return result; + }; +} + +/* + * Unwrap a JS value that comes from the content script. + * Mainly converts proxy wrapper to XPCNativeWrapper. + */ +function unwrap(value, obj, name) { + //console.log("unwrap : "+value+" ("+name+")"); + if (!value) + return value; + let type = typeof value; + + // In case of proxy, unwrap them recursively + // (it should not be recursive, just in case of) + if (["object", "function"].indexOf(type) !== -1 && + "__isWrappedProxy" in value) { + while("__isWrappedProxy" in value) + value = value.valueOf(UNWRAP_ACCESS_KEY); + return value; + } + + // In case of functions we need to return a wrapper that converts native + // arguments applied to this function into proxies. + if (type == "function") + return ContentScriptFunctionWrapper(value, obj, name); + + // We must wrap objects coming from content script too, as they may have + // a function that will be called by a native method. + // For example: + // addEventListener(..., { handleEvent: function(event) {} }, ...); + if (type == "object") + return ContentScriptObjectWrapper(value); + + if (["string", "number", "boolean"].indexOf(type) !== -1) + return value; + //console.log("return non-wrapped to native : "+typeof value+" -- "+value); + return value; +} + +/** + * Returns an XrayWrapper proxy object that allow to wrap any of its function + * though `ContentScriptFunctionWrapper`. These proxies are given to + * XrayWrappers in order to automatically wrap values when they call a method + * of these proxies. So that they are only used internaly and content script, + * nor web page have ever access to them. As a conclusion, we can consider + * this code as being safe regarding web pages overload. + * + * + * @param obj {Object} + * object coming from content script context to wrap + * + * Example: + * let myListener = { handleEvent: function (event) {} }; + * node.addEventListener("click", myListener, false); + * `event` has to be wrapped, so handleEvent has to be wrapped using + * `ContentScriptFunctionWrapper` function. + * In order to do so, we build this new kind of proxies. + */ +function ContentScriptObjectWrapper(obj) { + if ("___proxy" in obj && typeof obj.___proxy == "object") + return obj.___proxy; + + function valueOf(key) { + if (key === UNWRAP_ACCESS_KEY) + return obj; + return this; + } + + let proxy = Proxy.create({ + // Fundamental traps + getPropertyDescriptor: function(name) { + return Object.getOwnPropertyDescriptor(obj, name); + }, + defineProperty: function(name, desc) { + return Object.defineProperty(obj, name, desc); + }, + getOwnPropertyNames: function () { + return Object.getOwnPropertyNames(obj); + }, + delete: function(name) { + return delete obj[name]; + }, + // derived traps + has: function(name) { + return name === "__isXrayWrapperProxy" || + name in obj; + }, + hasOwn: function(name) { + return Object.prototype.hasOwnProperty.call(obj, name); + }, + get: function(receiver, name) { + if (name == "valueOf") + return valueOf; + let value = obj[name]; + if (!value) + return value; + + return unwrap(value); + }, + set: function(receiver, name, val) { + obj[name] = val; + return true; + }, + enumerate: function() { + var result = []; + for each (let name in obj) { + result.push(name); + }; + return result; + }, + keys: function() { + return Object.keys(obj); + } + }); + + Object.defineProperty(obj, "___proxy", {value : proxy, + writable : false, + enumerable : false, + configurable : false}); + + return proxy; +} + +// List of all existing typed arrays. +// Can be found here: +// http://mxr.mozilla.org/mozilla-central/source/js/src/jsapi.cpp#1790 +const typedArraysCtor = [ + ArrayBuffer, + Int8Array, + Uint8Array, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, + Uint8ClampedArray +]; + +/* + * Wrap a JS value coming from the document by building a proxy wrapper. + */ +function wrap(value, obj, name, debug) { + if (!value) + return value; + let type = typeof value; + if (type == "object") { + // Bug 671016: Typed arrays don't need to be proxified. + // We avoid checking the whole constructor list on all objects + // by doing this check only on non-extensible objects: + if (!Object.isExtensible(value) && + typedArraysCtor.indexOf(value.constructor) !== -1) + return value; + + // We may have a XrayWrapper proxy. + // For example: + // let myListener = { handleEvent: function () {} }; + // node.addEventListener("click", myListener, false); + // When native code want to call handleEvent, + // we go though ContentScriptFunctionWrapper that calls `wrap(this)` + // `this` is the XrayWrapper proxy of myListener. + // We return this object without building a CS proxy as it is already + // a value coming from the CS. + if ("__isXrayWrapperProxy" in value) + return value.valueOf(UNWRAP_ACCESS_KEY); + + // Unwrap object before wrapping it. + // It should not happen with CS proxy objects. + while("__isWrappedProxy" in value) { + value = value.valueOf(UNWRAP_ACCESS_KEY); + } + + if (XPCNativeWrapper.unwrap(value) !== value) + return getProxyForObject(value); + // In case of Event, HTMLCollection or NodeList or ??? + // XPCNativeWrapper.unwrap(value) === value + // but it's still a XrayWrapper so let's build a proxy + return getProxyForObject(value); + } + if (type == "function") { + if (XPCNativeWrapper.unwrap(value) !== value + || (typeof value.toString === "function" && + value.toString().match(/\[native code\]/))) { + return getProxyForFunction(value, NativeFunctionWrapper(value, obj, name)); + } + return value; + } + if (type == "string") + return value; + if (type == "number") + return value; + if (type == "boolean") + return value; + //console.log("return non-wrapped to wrapped : "+value); + return value; +} + +/* + * Wrap an object from the document to a proxy wrapper + */ +function getProxyForObject(obj) { + if (typeof obj != "object") { + let msg = "tried to proxify something other than an object: " + typeof obj; + console.warn(msg); + throw msg; + } + if ("__isWrappedProxy" in obj) { + return obj; + } + // Check if there is a proxy cached on this wrapper, + // but take care of prototype ___proxy attribute inheritance! + if (obj && obj.___proxy && obj.___proxy.valueOf(UNWRAP_ACCESS_KEY) === obj) { + return obj.___proxy; + } + + let proxy = Proxy.create(handlerMaker(obj)); + + Object.defineProperty(obj, "___proxy", {value : proxy, + writable : false, + enumerable : false, + configurable : false}); + return proxy; +} + +/* + * Wrap a function from the document to a proxy wrapper + */ +function getProxyForFunction(fun, callTrap) { + if (typeof fun != "function") { + let msg = "tried to proxify something other than a function: " + typeof fun; + console.warn(msg); + throw msg; + } + if ("__isWrappedProxy" in fun) + return obj; + if ("___proxy" in fun) + return fun.___proxy; + + let proxy = Proxy.createFunction(handlerMaker(fun), callTrap); + + Object.defineProperty(fun, "___proxy", {value : proxy, + writable : false, + enumerable : false, + configurable : false}); + + return proxy; +} + +/* + * Check if a DOM attribute name is an event name. + */ +function isEventName(id) { + if (id.indexOf("on") != 0 || id.length == 2) + return false; + // Taken from: + // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsDOMClassInfo.cpp#7616 + switch (id[2]) { + case 'a' : + return (id == "onabort" || + id == "onafterscriptexecute" || + id == "onafterprint"); + case 'b' : + return (id == "onbeforeunload" || + id == "onbeforescriptexecute" || + id == "onblur" || + id == "onbeforeprint"); + case 'c' : + return (id == "onchange" || + id == "onclick" || + id == "oncontextmenu" || + id == "oncopy" || + id == "oncut" || + id == "oncanplay" || + id == "oncanplaythrough"); + case 'd' : + return (id == "ondblclick" || + id == "ondrag" || + id == "ondragend" || + id == "ondragenter" || + id == "ondragleave" || + id == "ondragover" || + id == "ondragstart" || + id == "ondrop" || + id == "ondurationchange"); + case 'e' : + return (id == "onerror" || + id == "onemptied" || + id == "onended"); + case 'f' : + return id == "onfocus"; + case 'h' : + return id == "onhashchange"; + case 'i' : + return (id == "oninput" || + id == "oninvalid"); + case 'k' : + return (id == "onkeydown" || + id == "onkeypress" || + id == "onkeyup"); + case 'l' : + return (id == "onload" || + id == "onloadeddata" || + id == "onloadedmetadata" || + id == "onloadstart"); + case 'm' : + return (id == "onmousemove" || + id == "onmouseout" || + id == "onmouseover" || + id == "onmouseup" || + id == "onmousedown" || + id == "onmessage"); + case 'p' : + return (id == "onpaint" || + id == "onpageshow" || + id == "onpagehide" || + id == "onpaste" || + id == "onpopstate" || + id == "onpause" || + id == "onplay" || + id == "onplaying" || + id == "onprogress"); + case 'r' : + return (id == "onreadystatechange" || + id == "onreset" || + id == "onresize" || + id == "onratechange"); + case 's' : + return (id == "onscroll" || + id == "onselect" || + id == "onsubmit" || + id == "onseeked" || + id == "onseeking" || + id == "onstalled" || + id == "onsuspend"); + case 't': + return id == "ontimeupdate" + /* + // TODO: Make it work for mobile version + || + (nsDOMTouchEvent::PrefEnabled() && + (id == "ontouchstart" || + id == "ontouchend" || + id == "ontouchmove" || + id == "ontouchenter" || + id == "ontouchleave" || + id == "ontouchcancel"))*/; + + case 'u' : + return id == "onunload"; + case 'v': + return id == "onvolumechange"; + case 'w': + return id == "onwaiting"; + } + + return false; +} + +// XrayWrappers miss some attributes. +// Here is a list of functions that return a value when it detects a miss: +const NODES_INDEXED_BY_NAME = ["IMG", "FORM", "APPLET", "EMBED", "OBJECT"]; +const xRayWrappersMissFixes = [ + + // Fix bug with XPCNativeWrapper on HTMLCollection + // We can only access array item once, then it's undefined :o + function (obj, name) { + let i = parseInt(name); + if (obj.toString().match(/HTMLCollection|NodeList/) && + i >= 0 && i < obj.length) { + return wrap(XPCNativeWrapper(obj.wrappedJSObject[name]), obj, name); + } + return null; + }, + + // Trap access to document["form name"] + // that may refer to an existing form node + // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsDOMClassInfo.cpp#9285 + function (obj, name) { + if ("nodeType" in obj && obj.nodeType == 9) { + let node = obj.wrappedJSObject[name]; + // List of supported tag: + // http://mxr.mozilla.org/mozilla-central/source/content/html/content/src/nsGenericHTMLElement.cpp#1267 + if (node && NODES_INDEXED_BY_NAME.indexOf(node.tagName) != -1) + return wrap(XPCNativeWrapper(node)); + } + return null; + }, + + // Trap access to window["frame name"] and window.frames[i] + // that refer to an (i)frame internal window object + // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsDOMClassInfo.cpp#6824 + function (obj, name) { + if (typeof obj == "object" && "document" in obj) { + // Ensure that we are on a window object + try { + obj.QueryInterface(Ci.nsIDOMWindow); + } + catch(e) { + return null; + } + + // Integer case: + let i = parseInt(name); + if (i >= 0 && i in obj) { + return wrap(XPCNativeWrapper(obj[i])); + } + + // String name case: + if (name in obj.wrappedJSObject) { + let win = obj.wrappedJSObject[name]; + let nodes = obj.document.getElementsByName(name); + for (let i = 0, l = nodes.length; i < l; i++) { + let node = nodes[i]; + if ("contentWindow" in node && node.contentWindow == win) + return wrap(node.contentWindow); + } + } + } + return null; + }, + + // Trap access to form["node name"] + // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsDOMClassInfo.cpp#9477 + function (obj, name) { + if (typeof obj == "object" && obj.tagName == "FORM") { + let match = obj.wrappedJSObject[name]; + let nodes = obj.ownerDocument.getElementsByName(name); + for (let i = 0, l = nodes.length; i < l; i++) { + let node = nodes[i]; + if (node == match) + return wrap(node); + } + } + return null; + }, + + // Fix XPathResult's constants being undefined on XrayWrappers + // these constants are defined here: + // http://mxr.mozilla.org/mozilla-central/source/dom/interfaces/xpath/nsIDOMXPathResult.idl + // and are only numbers. + // The platform bug 665279 was fixed in Gecko 10.0a1. + // FIXME: remove this workaround once the SDK no longer supports Firefox 9. + function (obj, name) { + if (typeof obj == "object" && name in Ci.nsIDOMXPathResult) { + let value = Ci.nsIDOMXPathResult[name]; + if (typeof value == "number" && value === obj.wrappedJSObject[name]) + return value; + } + return null; + } + +]; + +// XrayWrappers have some buggy methods. +// Here is the list of them with functions returning some replacement +// for a given object `obj`: +const xRayWrappersMethodsFixes = { + // postMessage method is checking the Javascript global + // and it expects it to be a window object. + // But in our case, the global object is our sandbox global object. + // See nsGlobalWindow::CallerInnerWindow(): + // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsGlobalWindow.cpp#5893 + // nsCOMPtr win = do_QueryWrappedNative(wrapper); + // win is null + postMessage: function (obj) { + // Ensure that we are on a window object + try { + obj.QueryInterface(Ci.nsIDOMWindow); + } + catch(e) { + return null; + } + // Create a wrapper that is going to call `postMessage` through `eval` + let f = function postMessage(message, targetOrigin) { + message = message.toString().replace(/['\\]/g,"\\$&"); + targetOrigin = targetOrigin.toString().replace(/['\\]/g,"\\$&"); + + let jscode = "this.postMessage('" + message + "', '" + + targetOrigin + "')"; + return this.wrappedJSObject.eval(jscode); + }; + return getProxyForFunction(f, NativeFunctionWrapper(f)); + }, + + // Fix mozMatchesSelector uses that is broken on XrayWrappers + // when we use document.documentElement.mozMatchesSelector.call(node, expr) + // It's only working if we call mozMatchesSelector on the node itself. + // SEE BUG 658909: mozMatchesSelector returns incorrect results with XrayWrappers + mozMatchesSelector: function (obj) { + // Ensure that we are on an object to expose this buggy method + try { + // Bug 707576 removed nsIDOMNSElement. + // Can be simplified as soon as Firefox 11 become the minversion + obj.QueryInterface("nsIDOMElement" in Ci ? Ci.nsIDOMElement : + Ci.nsIDOMNSElement); + } + catch(e) { + return null; + } + // We can't use `wrap` function as `f` is not a native function, + // so wrap it manually: + let f = function mozMatchesSelector(selectors) { + return this.mozMatchesSelector(selectors); + }; + + return getProxyForFunction(f, NativeFunctionWrapper(f)); + }, + + // Bug 679054: History API doesn't work with Proxy objects. We have to pass + // regular JS objects on `pushState` and `replaceState` methods. + // In addition, the first argument has to come from the same compartment. + pushState: function (obj) { + // Ensure that we are on an object that expose History API + try { + obj.QueryInterface(Ci.nsIDOMHistory); + } + catch(e) { + return null; + } + let f = function fix() { + // Call native method with JSON objects + // (need to convert `arguments` to an array via `slice`) + return this.pushState.apply(this, JSON.parse(JSON.stringify(Array.slice(arguments)))); + }; + + return getProxyForFunction(f, NativeFunctionWrapper(f)); + }, + replaceState: function (obj) { + // Ensure that we are on an object that expose History API + try { + obj.QueryInterface(Ci.nsIDOMHistory); + } + catch(e) { + return null; + } + let f = function fix() { + // Call native method with JSON objects + // (need to convert `arguments` to an array via `slice`) + return this.replaceState.apply(this, JSON.parse(JSON.stringify(Array.slice(arguments)))); + }; + + return getProxyForFunction(f, NativeFunctionWrapper(f)); + } +}; + +/* + * Generate handler for proxy wrapper + */ +function handlerMaker(obj) { + // Overloaded attributes dictionary + let overload = {}; + // Expando attributes dictionary (i.e. onclick, onfocus, on* ...) + let expando = {}; + // Cache of methods overloaded to fix XrayWrapper bug + let methodFixes = {}; + return { + // Fundamental traps + getPropertyDescriptor: function(name) { + return Object.getOwnPropertyDescriptor(obj, name); + }, + defineProperty: function(name, desc) { + return Object.defineProperty(obj, name, desc); + }, + getOwnPropertyNames: function () { + return Object.getOwnPropertyNames(obj); + }, + delete: function(name) { + delete expando[name]; + delete overload[name]; + return delete obj[name]; + }, + + // derived traps + has: function(name) { + if (name == "___proxy") return false; + if (isEventName(name)) { + // XrayWrappers throw exception when we try to access expando attributes + // even on "name in wrapper". So avoid doing it! + return name in expando; + } + return name in obj || name in overload || name == "__isWrappedProxy" || + undefined !== this.get(null, name); + }, + hasOwn: function(name) { + return Object.prototype.hasOwnProperty.call(obj, name); + }, + get: function(receiver, name) { + if (name == "___proxy") + return undefined; + + // Overload toString in order to avoid returning "[XrayWrapper [object HTMLElement]]" + // or "[object Function]" for function's Proxy + if (name == "toString") { + if ("wrappedJSObject" in obj) { + // Bug 714778: we should not pass obj.wrappedJSObject.toString + // in order to avoid sharing its proxy over contents scripts: + return wrap(function () { + return obj.wrappedJSObject.toString.call( + this.valueOf(UNWRAP_ACCESS_KEY), arguments); + }, obj, name); + } + else { + return wrap(obj.toString, obj, name); + } + } + + // Offer a way to retrieve XrayWrapper from a proxified node through `valueOf` + if (name == "valueOf") + return function (key) { + if (key === UNWRAP_ACCESS_KEY) + return obj; + return this; + }; + + // Return overloaded value if there is one. + // It allows to overload native methods like addEventListener that + // are not saved, even on the wrapper itself. + // (And avoid some methods like toSource from being returned here! [__proto__ test]) + if (name in overload && + overload[name] != Object.getPrototypeOf(overload)[name] && + name != "__proto__") { + return overload[name]; + } + + // Catch exceptions thrown by XrayWrappers when we try to access on* + // attributes like onclick, onfocus, ... + if (isEventName(name)) { + //console.log("expando:"+obj+" - "+obj.nodeType); + return name in expando ? expando[name].original : undefined; + } + + // Overload some XrayWrappers method in order to fix its bugs + if (name in methodFixes && + methodFixes[name] != Object.getPrototypeOf(methodFixes)[name] && + name != "__proto__") + return methodFixes[name]; + if (Object.keys(xRayWrappersMethodsFixes).indexOf(name) !== -1) { + let fix = xRayWrappersMethodsFixes[name](obj); + if (fix) + return methodFixes[name] = fix; + } + + let o = obj[name]; + + // XrayWrapper miss some attributes, try to catch these and return a value + if (!o) { + for each(let atttributeFixer in xRayWrappersMissFixes) { + let fix = atttributeFixer(obj, name); + if (fix) + return fix; + } + } + + // Generic case + return wrap(o, obj, name); + + }, + + set: function(receiver, name, val) { + + if (isEventName(name)) { + //console.log("SET on* attribute : " + name + " / " + val + "/" + obj); + let shortName = name.replace(/^on/,""); + + // Unregister previously set listener + if (expando[name]) { + obj.removeEventListener(shortName, expando[name], true); + delete expando[name]; + } + + // Only accept functions + if (typeof val != "function") + return false; + + // Register a new listener + let original = val; + val = ContentScriptFunctionWrapper(val); + expando[name] = val; + val.original = original; + obj.addEventListener(name.replace(/^on/, ""), val, true); + return true; + } + + obj[name] = val; + + // Handle native method not overloaded on XrayWrappers: + // obj.addEventListener = val; -> obj.addEventlistener = native method + // And, XPCNativeWrapper bug where nested values appear to be wrapped: + // obj.customNestedAttribute = val -> obj.customNestedAttribute !== val + // obj.customNestedAttribute = "waive wrapper something" + // SEE BUG 658560: Fix identity problem with CrossOriginWrappers + // TODO: check that DOM can't be updated by the document itself and so overloaded value becomes wrong + // but I think such behavior is limited to primitive type + if ((typeof val == "function" || typeof val == "object") && name) { + overload[name] = val; + } + + return true; + }, + + enumerate: function() { + var result = []; + for each (let name in Object.keys(obj)) { + result.push(name); + }; + return result; + }, + + keys: function() { + return Object.keys(obj); + } + }; +}; + + +/* + * Wrap an object from the document to a proxy wrapper. + */ +function create(object) { + if ("wrappedJSObject" in object) + object = object.wrappedJSObject; + let xpcWrapper = XPCNativeWrapper(object); + // If we can't build an XPCNativeWrapper, it doesn't make sense to build + // a proxy. All proxy code is based on having such wrapper that store + // different JS attributes set. + // (we can't build XPCNativeWrapper when object is from the same + // principal/domain) + if (object === xpcWrapper) { + return object; + } + return getProxyForObject(xpcWrapper); +} diff --git a/tools/addon-sdk-1.4/packages/api-utils/data/test-content-symbiont.js b/tools/addon-sdk-1.4/packages/api-utils/data/test-content-symbiont.js new file mode 100644 index 0000000..808af37 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/data/test-content-symbiont.js @@ -0,0 +1 @@ +// test-content-symbiont diff --git a/tools/addon-sdk-1.4/packages/api-utils/data/test-httpd.txt b/tools/addon-sdk-1.4/packages/api-utils/data/test-httpd.txt new file mode 100644 index 0000000..7956d3a --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/data/test-httpd.txt @@ -0,0 +1 @@ +This is the HTTPD test file. diff --git a/tools/addon-sdk-1.4/packages/api-utils/data/test-trusted-document.html b/tools/addon-sdk-1.4/packages/api-utils/data/test-trusted-document.html new file mode 100644 index 0000000..166009d --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/data/test-trusted-document.html @@ -0,0 +1,13 @@ + + + Worker test + + +

Lorem ipsum dolor sit amet.

+ + + diff --git a/tools/addon-sdk-1.4/packages/api-utils/docs/api-utils.md b/tools/addon-sdk-1.4/packages/api-utils/docs/api-utils.md new file mode 100644 index 0000000..d87acdb --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/docs/api-utils.md @@ -0,0 +1,153 @@ + + + +The `api-utils` module provides some helpers useful to the SDK's high-level API +implementations. + +Introduction +------------ + +The SDK high-level API design guidelines make a number of recommendations. +This module implements some of those patterns so that your own implementations +don't need to reinvent them. + +For example, public constructors should be callable both with and without the +`new` keyword. Your module can implement this recommendation using the +`publicConstructor` function. + +Options objects or "dictionaries" are also common throughout the high-level +APIs. The guidelines recommend that public constructors should generally define +a single `options` parameter rather than defining many parameters. Since one of +the SDK's principles is to be friendly to developers, ideally all properties on +options dictionaries should be checked for correct type, and informative error +messages should be generated when clients make mistakes. With the +`validateOptions` function, your module can easily do so. + +And objects are sometimes iterable over a custom set of key/value pairs. +Such objects should have custom iterators that let consumers iterate keys, +values, or [key, value] pairs. The `addIterator` function makes it easy to do +so in a way that is consistent with the behavior of default iterators during +`for...in`, `for each...in`, and `for...in Iterator()` loops. + + +@function +Returns a function *C* that creates an instance of `privateConstructor`. *C* +may be called with or without the `new` keyword. + +The prototype of each instance returned from *C* is *C*.`prototype`, and +*C*.`prototype` is an object whose prototype is +`privateConstructor.prototype`. Instances returned from *C* are therefore +instances of both *C* and `privateConstructor`. + +Additionally, the constructor of each instance returned from *C* is *C*. + +Instances returned from *C* are automatically memory tracked using +`memory.track` under the bin name `privateConstructor.name`. + +**Example** + + function MyObject() {} + exports.MyObject = apiUtils.publicConstructor(MyObject); + +@returns {function} +A function that makes new instances of `privateConstructor`. + +@param privateConstructor {constructor} + + + +@function +A function to validate an options dictionary according to the specified +constraints. + +`map`, `is`, and `ok` are used in that order. + +The return value is 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. + +**Examples** + +A typical use: + + var opts = { foo: 1337 }; + var requirements = { + foo: { + map: function (val) val.toString(), + is: ["string"], + ok: function (val) val.length > 0, + msg: "foo must be a non-empty string." + } + }; + var validatedOpts = apiUtils.validateOptions(opts, requirements); + // validatedOpts == { foo: "1337" } + +If the key `foo` is optional and doesn't need to be mapped: + + var opts = { foo: 1337 }; + var validatedOpts = apiUtils.validateOptions(opts, { foo: {} }); + // validatedOpts == { foo: 1337 } + + opts = {}; + validatedOpts = apiUtils.validateOptions(opts, { foo: {} }); + // validatedOpts == {} + +@returns {object} +A validated options dictionary given some requirements. If any of the +requirements are not met, an exception is thrown. + +@param options {object} +The options dictionary to validate. It's not modified. If it's null or +otherwise falsey, an empty object is assumed. + +@param requirements {object} +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. The keys of that object are the following, and each is optional: + +@prop [map] {function} +A function that's passed the value of the key in the `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 is caught and discarded, +and the key's value is its value in `options`. + +@prop [is] {array} +An array containing the number of `typeof` type names. If the key's value is +none of these types it fails validation. Arrays and nulls are identified by +the special type names "array" and "null"; "object" will not match either. +No type coercion is done. + +@prop [ok] {function} +A function that is passed the key's value. If it returns false, the value +fails validation. + +@prop [msg] {string} +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. + + + +@function +Adds an iterator to the specified object that iterates keys, values, +or [key, value] pairs depending on how it is invoked, i.e.: + + for (var key in obj) { ... } // iterate keys + for each (var val in obj) { ... } // iterate values + for (var [key, val] in Iterator(obj)) { ... } // iterate pairs + +If your object only iterates either keys or values, you don't need this +function. Simply assign a generator function that iterates the keys/values +to your object's `__iterator__` property instead, f.e.: + + obj.__iterator__ = function () { for each (var i in items) yield i; } + +@param obj {object} +the object to which to add the iterator + +@param keysValsGen {function} +a generator function that yields [key, value] pairs + diff --git a/tools/addon-sdk-1.4/packages/api-utils/docs/app-strings.md b/tools/addon-sdk-1.4/packages/api-utils/docs/app-strings.md new file mode 100644 index 0000000..3e4d358 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/docs/app-strings.md @@ -0,0 +1,61 @@ +The `app-strings` module gives you access to the host application's localized +string bundles (`.properties` files). + +The module exports the `StringBundle` constructor function. To access a string +bundle, construct an instance of `StringBundle`, passing it the bundle's URL: + + var StringBundle = require("app-strings").StringBundle; + var bundle = StringBundle("chrome://browser/locale/browser.properties"); + +To get the value of a string, call the object's `get` method, passing it +the name of the string: + + var accessKey = bundle.get("contextMenuSearchText.accesskey"); + // "S" in the en-US locale + +To get the formatted value of a string that accepts arguments, call the object's +`get` method, passing it the name of the string and an array of arguments +with which to format the string: + + var searchText = bundle.get("contextMenuSearchText", + ["universe", "signs of intelligent life"]); + // 'Search universe for "signs of intelligent life"' in the en-US locale + +To get all strings in the bundle, iterate the object, which returns arrays +of the form [name, value]: + + for (var [name, value] in Iterator(bundle)) + console.log(name + " = " + value); + +Iteration +--------- + +for (var name in bundle) { ... } + +Iterate the names of strings in the bundle. + +for each (var val in bundle) { ... } + +Iterate the values of strings in the bundle. + +for (var [name, value] in Iterator(bundle)) { ... } + +Iterate the names and values of strings in the bundle. + + + +@class +The `StringBundle` object represents a string bundle. + +@constructor +Creates a StringBundle object that gives you access to a string bundle. +@param url {string} the URL of the string bundle +@returns {StringBundle} the string bundle + + +@method Get the value of the string with the given name. +@param [name] {string} the name of the string to get +@param [args] {array} (optional) strings that replace placeholders in the string +@returns {string} the value of the string + + diff --git a/tools/addon-sdk-1.4/packages/api-utils/docs/byte-streams.md b/tools/addon-sdk-1.4/packages/api-utils/docs/byte-streams.md new file mode 100644 index 0000000..f7cffe8 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/docs/byte-streams.md @@ -0,0 +1,64 @@ + + + +The `byte-streams` module provides streams for reading and writing bytes. + + +@class + +@constructor + Creates a binary input stream that reads bytes from a backing stream. +@param inputStream {stream} + The backing stream, an nsIInputStream. + + +@property {boolean} + True if the stream is closed. + + + +@method + Closes both the stream and its backing stream. If the stream is already + closed, an exception is thrown. + + + +@method + Reads a string from the stream. If the stream is closed, an exception is + thrown. +@param [numBytes] {number} + The number of bytes to read. If not given, the remainder of the entire stream + is read. +@returns {string} + A string containing the bytes read. If the stream is at the end, returns the + empty string. + + + + +@class + +@constructor + Creates a binary output stream that writes bytes to a backing stream. +@param outputStream {stream} + The backing stream, an nsIOutputStream. + + +@property {boolean} + True if the stream is closed. + + +@method + Closes both the stream and its backing stream. If the stream is already + closed, an exception is thrown. + + +@method + Writes a string to the stream. If the stream is closed, an exception is + thrown. +@param str {string} + The string to write. + + diff --git a/tools/addon-sdk-1.4/packages/api-utils/docs/collection.md b/tools/addon-sdk-1.4/packages/api-utils/docs/collection.md new file mode 100644 index 0000000..796289a --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/docs/collection.md @@ -0,0 +1,73 @@ + + + +The `collection` module provides a simple list-like class and utilities for +using it. A collection is ordered, like an array, but its items are unique, +like a set. + + +@class +A collection object provides for...in-loop iteration. Items are yielded in the +order they were added. For example, the following code... + + var collection = require("collection"); + var c = new collection.Collection(); + c.add(1); + c.add(2); + c.add(3); + for (item in c) + console.log(item); + +... would print this to the console: + +
+  1
+  2
+  3
+
+ +Iteration proceeds over a copy of the collection made before iteration begins, +so it is safe to mutate the collection during iteration; doing so does not +affect the results of the iteration. + + +@constructor +Creates a new collection. The collection is backed by an array. +@param [array] {array} +If *array* is given, it will be used as the backing array. This way the caller +can fully control the collection. Otherwise a new empty array will be used, and +no one but the collection will have access to it. + + +@property {number} +The number of items in the collection array. + + +@method +Adds a single item or an array of items to the collection. Any items already +contained in the collection are ignored. +@param itemOrItems {object} An item or array of items. +@returns {Collection} The Collection. + + +@method +Removes a single item or an array of items from the collection. Any items not +contained in the collection are ignored. +@param itemOrItems {object} An item or array of items. +@returns {Collection} The Collection. + +
+ + +@function +Adds a collection property to the given object. Setting the property to a +scalar value empties the collection and adds the value. Setting it to an array +empties the collection and adds all the items in the array. +@param object {object} +The property will be defined on this object. +@param propName {string} +The name of the property. +@param [backingArray] {array} +If given, this will be used as the collection's backing array. + + diff --git a/tools/addon-sdk-1.4/packages/api-utils/docs/content.md b/tools/addon-sdk-1.4/packages/api-utils/docs/content.md new file mode 100644 index 0000000..b258d02 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/docs/content.md @@ -0,0 +1,11 @@ + + +The `content` module exports three different traits [Loader][], [Worker][] and +[Symbiont][]. None of this traits is intended to be used directly by programs. +Rather, they are intended to be used by other modules that provide high +level APIs to programs or libraries. + +[Loader]:packages/api-utils/docs/content/loader.html +[Worker]:packages/api-utils/docs/content/worker.html +[Symbiont]:packages/api-utils/docs/content/symbiont.html + diff --git a/tools/addon-sdk-1.4/packages/api-utils/docs/content/loader.md b/tools/addon-sdk-1.4/packages/api-utils/docs/content/loader.md new file mode 100644 index 0000000..83b6276 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/docs/content/loader.md @@ -0,0 +1,88 @@ + + +Loader is base trait and it provides set of core properties and associated +validations. Trait is useful for all the compositions providing high level +APIs for creating JavaScript contexts that can access web content. + +Loader is composed from the +[EventEmitter](packages/api-utils/docs/events.html) trait, therefore +instances of Loader and their descendants expose all the public properties +exposed by EventEmitter along with additional public properties: + +Value changes on all of the above mentioned properties emit `propertyChange` +events on an instances. + +**Example:** + +The following code creates a wrapper on hidden frame that reloads a web page +in frame every time `contentURL` property is changed: + + var hiddenFrames = require("hidden-frame"); + var { Loader } = require("content"); + var PageLoader = Loader.compose({ + constructor: function PageLoader(options) { + options = options || {}; + if (options.contentURL) + this.contentURL = options.contentURL; + this.on('propertyChange', this._onChange = this._onChange.bind(this)); + let self = this; + hiddenFrames.add(hiddenFrames.HiddenFrame({ + onReady: function onReady() { + let frame = self._frame = this.element; + self._emit('propertyChange', { contentURL: self.contentURL }); + } + })); + }, + _onChange: function _onChange(e) { + if ('contentURL' in e) + this._frame.setAttribute('src', this._contentURL); + } + }); + + +@class + +@property {array} +The local file URLs of content scripts to load. Content scripts specified by +this property are loaded *before* those specified by the `contentScript` +property. + + + +@property {array} +The texts of content scripts to load. Content scripts specified by this +property are loaded *after* those specified by the `contentScriptFile` property. + + + +@property {string} +When to load the content scripts. This may take one of the following +values: + +* "start": load content scripts immediately after the document +element for the page is inserted into the DOM, but before the DOM content +itself has been loaded +* "ready": load content scripts once DOM content has been loaded, +corresponding to the +[DOMContentLoaded](https://developer.mozilla.org/en/Gecko-Specific_DOM_Events) +event +* "end": load content scripts once all the content (DOM, JS, CSS, +images) for the page has been loaded, at the time the +[window.onload event](https://developer.mozilla.org/en/DOM/window.onload) +fires + + + + +@property {string} +The URL of the content loaded. + + + +@property {object} +Permissions for the content, with the following keys: +@prop script {boolean} + Whether or not to execute script in the content. Defaults to true. + + + diff --git a/tools/addon-sdk-1.4/packages/api-utils/docs/content/proxy.md b/tools/addon-sdk-1.4/packages/api-utils/docs/content/proxy.md new file mode 100644 index 0000000..012fbc7 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/docs/content/proxy.md @@ -0,0 +1,237 @@ + + +Content scripts need access to the DOM of the pages they are attached to. +However, those pages should be considered to be hostile environments: we +have no control over any other scripts loaded by the web page that may be +executing in the same context. If the content scripts and scripts loaded +by the web page were to access the same DOM objects, there are two possible +security problems: + +First, a malicious page might redefine functions and properties of DOM +objects so they don't do what the add-on expects. For example, if a +content script calls `document.getElementById()` to retrieve a DOM +element, then a malicious page could redefine its behavior to return +something unexpected: + +

+// If the web document contains the following script:
+document.getElementById = function (str) {
+  // Overload indexOf method of all string instances
+  str.__proto__.indexOf = function () {return -1;};
+  // Overload toString method of all object instances
+  str.__proto__.__proto__.toString = function () {return "evil";};
+};
+// After the following line, the content script will be compromised:
+var node = document.getElementById("element");
+// Then your content script is totally out of control.
+
+ +Second, changes the content script made to the DOM objects would be visible +to the page, leaking information to it. + +The general approach to fixing these problems is to wrap DOM objects in +[`XrayWrappers`](https://developer.mozilla.org/en/XPCNativeWrapper) +(also know as `XPCNativeWrapper`). This guarantees that: + +* when the content script accesses DOM properties and functions it gets the +original native version of them, ignoring any modifications made by the web +page +* changes to the DOM made by the content script are not visible to scripts +running in the page. + +However, `XrayWrapper` has some limitations and bugs, which break many +popular web frameworks. In particular, you can't: + +* define attributes like `onclick`: you have to use `addEventListener` syntax +* overload native methods on DOM objects, like this: +

+proxy.addEventListener = function () {};
+
+* access named elements using properties like `window[framename]` or +`document[formname]` +* use some other features that have bugs in the `XrayWrapper` +implementation, like `mozMatchesSelector` + +The `proxy` module uses `XrayWrapper` in combination with the +experimental +[Proxy API](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Proxy) +to address both the security vulnerabilities of content scripts and the +limitations of `XrayWrapper`. + +
+  /--------------------\                           /------------------------\
+  |    Web document    |                           | Content script sandbox |
+  | http://mozilla.org |                           |     data/worker.js     |
+  |                    | require('content-proxy'). |                        |
+  | window >-----------|-     create(window)      -|-> window               |
+  \--------------------/                           \------------------------/
+
+ + +## The Big Picture ## + +The implementation defines two different kinds of proxy: + + 1. Content script proxies that wrap DOM objects that are exposed to + content scripts as described above. + 2. XrayWrapper proxies that wrap objects from content scripts before handing + them over to XrayWrapper functions. These proxies are internal + and are not exposed to content scripts or document content. + +
+  /--------------------\                           /------------------------\
+  |    Web document    |                           | Content script sandbox |
+  | http://mozilla.org |                           |     data/worker.js     |
+  |                    |                   /-------|-> myObject = {}        |
+  |                    |  /----------------v--\    |                        |
+  |                    |  | XrayWrapper Proxy |    | - document             |
+  |                    |  \---------v---------/    \----^-------------------/
+  |                    |            v                   |
+  |                    |  /-------------\  /----------\ |
+  | - document >-------|->| XrayWrapper |<-| CS proxy |-/
+  \--------------------/  \-------------/  \----------/
+
+ +Everything begins with a single call to the `create` function exported by the +content-proxy module: + + // Retrieve the unwrapped reference to the current web page window object + var win = gBrowser.contentDocument.defaultView.wrappedJSObject; + // Or in addon sdk style + var win = require("tab-browser").activeTab.linkedBrowser.contentWindow.wrappedJSObject; + // Now create a content script proxy for the window object + var windowProxy = require("api-utils/content/content-proxy").create(win); + + // We finally use this window object as sandbox prototype, + // so that all web page globals are accessible in CS too: + var contentScriptSandbox = new Cu.Sandbox(win, { + sandboxPrototype: windowProxy + }); + +Then all other proxies are created from this one. Attempts to access DOM +attributes of this proxy are trapped, and the proxy constructs and returns +content script proxies for those attributes: + + // For example, if you simply do this: + var document = window.document; + // accessing the `document` attribute will be trapped by the `window` content script + // proxy, and that proxy will that create another content script proxy for `document` + +So the main responsibility of the content script proxy implementation is to +ensure that we always return content script proxies to the content script. + +## Internal Implementation ## + +Each content script proxy keeps a reference to the `XrayWrapper` that enables +it to be sure of calling native DOM methods. + +There are two internal functions to convert between content script proxy +values and `XrayWrapper` values. + +1. __`wrap`__ takes an XrayWrapper value and wraps it in a content script +proxy if needed. + This method is called when: + * a content script accesses an attribute of a content script proxy. + * XrayWrapper code calls a callback function defined in the content +script, so that arguments passed into the function by the XrayWrapper are +converted into content script proxies. For example, if a content script +calls `addEventListener`, then the listener function will expect any arguments +to be content script proxies. +

+2. __`unwrap`__ takes an object coming from the content script context and: + * if the object is a content script proxy, unwraps it back to an +XrayWrapper reference + * if the object is not a content script proxy, wraps it in an XrayWrapper +proxy. +

+This means we can call a XrayWrapper method either with: + + * a raw XrayWrapper object. + + // The following line doesn't work if child is a content script proxy, + // it has to be a raw XrayWrapper reference + xrayWrapper.appendChild(child) + + * an XrayWrapper proxy when you pass a custom object from the content +script context. + + var myListener = { + handleEvent: function(event) { + // `event` should be a content script proxy + } + }; + // `myListener` has to be another kind of Proxy: XrayWrapper proxy, + // that aims to catch the call to `handleEvent` in order to wrap its + // arguments in a content script proxy. + xrayWrapper.addEventListener("click", myListener, false); + + +## Stack Traces ## + +The following code: + + function listener(event) { + + } + csProxy.addEventListener("message", listener, false); + +generates the following internal calls: + + -> CS Proxy:: get("addEventListener") + -> wrap(xrayWrapper.addEventListener) + -> NativeFunctionWrapper(xrayWrapper.addEventListener) + // NativeFunctionWrapper generates: + function ("message", listener, false) { + return xraywrapper.addEventListener("message", unwrap(listener), false); + } + -> unwrap(listener) + -> ContentScriptFunctionWrapper(listener) + // ContentScriptFunctionWrapper generates: + function (event) { + return listener(wrap(event)); + } + +
+ + // First, create an object from content script context + var myListener = { + handleEvent: function (event) { + + } + }; + // Then, pass this object as an argument to a CS proxy method + window.addEventListener("message", myListener, false); + + // Generates the following internal calls: + -> CS Proxy:: get("addEventListener") + -> wrap(xrayWrapper.addEventListener) + -> NativeFunctionWrapper(xrayWrapper.addEventListener) + // Generate the following function: + function ("message", myListener, false) { + return xraywrapper.addEventListener("message", unwrap(myListener), false); + } + -> unwrap(myListener) + -> ContentScriptObjectWrapper(myListener) + // Generate an XrayWrapper proxy and give it to xrayWrapper method. + // Then when native code fires an event, the proxy will catch it: + -> XrayWrapper Proxy:: get("handleEvent") + -> unwrap(myListener.handleEvent) + -> ContentScriptFunctionWrapper(myListener.handleEvent) + // Generate following function: + function (event) { + return myListener.handleEvent(wrap(event)); + } + + + +@function + Create a content script proxy.
+ Doesn't create a proxy if we are not able to create a XrayWrapper for + this object: for example, if the object comes from system principal. + +@param object {Object} + The object to proxify. + +@returns {Object} + A content script proxy that wraps `object`. +
diff --git a/tools/addon-sdk-1.4/packages/api-utils/docs/content/symbiont.md b/tools/addon-sdk-1.4/packages/api-utils/docs/content/symbiont.md new file mode 100644 index 0000000..7519e52 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/docs/content/symbiont.md @@ -0,0 +1,136 @@ + + + + +This module is not intended to be used directly by programs. Rather, it is +intended to be used by other modules that provide APIs to programs. + + +This module exports `Symbiont` trait that can be used for creating JavaScript +contexts that can access web content in host application frames (i.e. XUL +` +

+ + diff --git a/tools/addon-sdk-1.5/packages/addon-kit/tests/test-context-menu.js b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-context-menu.js new file mode 100644 index 0000000..f4ae528 --- /dev/null +++ b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-context-menu.js @@ -0,0 +1,2070 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +let {Cc,Ci} = require("chrome"); +const { Loader } = require('./helpers'); + +// These should match the same constants in the module. +const ITEM_CLASS = "jetpack-context-menu-item"; +const SEPARATOR_ID = "jetpack-context-menu-separator"; +const OVERFLOW_THRESH_DEFAULT = 10; +const OVERFLOW_THRESH_PREF = + "extensions.addon-sdk.context-menu.overflowThreshold"; +const OVERFLOW_MENU_ID = "jetpack-content-menu-overflow-menu"; +const OVERFLOW_POPUP_ID = "jetpack-content-menu-overflow-popup"; + +const TEST_DOC_URL = module.uri.replace(/\.js$/, ".html"); + + +// Destroying items that were previously created should cause them to be absent +// from the menu. +exports.testConstructDestroy = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + // Create an item. + let item = new loader.cm.Item({ label: "item" }); + test.assertEqual(item.parentMenu, null, "item's parent menu should be null"); + + 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]); + test.done(); + }); + }); +}; + + +// Destroying an item twice should not cause an error. +exports.testDestroyTwice = function (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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], []); + 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 (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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, []); + test.done(); + }); + }); +}; + + +// Selection contexts should cause items to appear when a selection exists. +exports.testSelectionContextMatch = function (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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], []); + test.done(); + }); + }); +}; + + +// Selection contexts should not cause items to appear when a selection does +// not exist. +exports.testSelectionContextNoMatch = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let item = loader.cm.Item({ + label: "item", + context: loader.cm.SelectionContext() + }); + + test.showMenu(null, function (popup) { + test.checkMenu([], [item], []); + test.done(); + }); +}; + + +// URL contexts should cause items to appear on pages that match. +exports.testURLContextMatch = function (test) { + test = new TestHelper(test); + 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"]) + }) + ]; + + 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 (test) { + test = new TestHelper(test); + 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"]) + }) + ]; + + test.withTestDoc(function (window, doc) { + test.showMenu(null, function (popup) { + test.checkMenu([], 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. +exports.testURLContextRemove = function (test) { + test = new TestHelper(test); + 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");', + onMessage: function (msg) { + test.assert(shouldBeEvaled, + "content script should be evaluated when expected"); + shouldBeEvaled = false; + test.done(); + } + }); + + test.withTestDoc(function (window, doc) { + shouldBeEvaled = true; + item.context.remove(context); + }); +}; + + +// Adding a non-matching URL context after its item is created and the page is +// loaded should cause the item's worker to be destroyed. +exports.testURLContextAdd = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let item = loader.cm.Item({ label: "item" }); + + test.withTestDoc(function (window, doc) { + let privatePropsKey = loader.globalScope.PRIVATE_PROPS_KEY; + let workerReg = item.valueOf(privatePropsKey)._workerReg; + + let found = false; + for each (let winWorker in workerReg.winWorkers) { + if (winWorker.win === window) { + found = true; + break; + } + } + this.test.assert(found, "window should be present in worker registry"); + + item.context.add(loader.cm.URLContext("*.bogus.com")); + + for each (let winWorker in workerReg.winWorkers) + this.test.assertNotEqual(winWorker.win, window, + "window should not be present in worker registry"); + + test.done(); + }); +}; + + +// Content contexts that return true should cause their items to be present +// in the menu. +exports.testContentContextMatch = function (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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], []); + 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 (test) { + test = new TestHelper(test); + 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], [], []); + test.assertEqual(item.label, "second label", + "item's label should be updated"); + test.done(); + }); +}; + + +// Ensure that contentScripFile is working correctly +exports.testContentScriptFile = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + // Reject remote files + test.assertRaises(function() { + new loader.cm.Item({ + label: "item", + contentScriptFile: "http://mozilla.com/context-menu.js" + }); + }, + "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: require("self").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 (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + let callbacks = 0; + + let item = new loader.cm.Item({ + label: "item", + contentScript: 'self.on("context", function (node) {' + + ' let Ci = Components.interfaces;' + + ' self.postMessage(node instanceof Ci.nsIDOMHTMLElement);' + + ' return false;' + + '});', + onMessage: function (isElt) { + test.assert(isElt, "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 (test) { + test = new TestHelper(test); + 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], []); + test.done(); + }); + }); +}; + + +// Once a context is removed, it should no longer cause its item to appear. +exports.testRemoveContext = function (test) { + test = new TestHelper(test); + 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], []); + test.done(); + }); + }); + }); +}; + + +// Lots of items should overflow into the overflow submenu. +exports.testOverflow = function (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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]); + test.done(); + }); + }); +}; + + +// Using multiple module instances to add items without causing overflow should +// work OK. Assumes OVERFLOW_THRESH_DEFAULT <= 2. +exports.testMultipleModulesAdd = function (test) { + test = new TestHelper(test); + 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([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]); + test.done(); + }); + }); + }); +}; + + +// Using multiple module instances to add items causing overflow should work OK. +exports.testMultipleModulesAddOverflow = function (test) { + test = new TestHelper(test); + 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([item1], [], 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); + 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 (test) { + test = new TestHelper(test); + 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([item1], [item0], []); + popup.hidePopup(); + + // Unload module 0. + loader0.unload(); + test.showMenu(null, function (popup) { + + // item0 should be removed from the menu. + test.checkMenu([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]); + 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 (test) { + test = new TestHelper(test); + 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([item1], [item0], []); + popup.hidePopup(); + + // Unload module 0. + loader0.unload(); + test.showMenu(null, function (popup) { + + // item0 should be removed from the menu. + test.checkMenu([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]); + 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 (test) { + test = new TestHelper(test); + 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([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]); + popup.hidePopup(); + + // Unload module 0. + loader0.unload(); + test.showMenu(null, function (popup) { + + // Both items should be removed from the menu. + test.checkMenu([], [], [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 (test) { + test = new TestHelper(test); + 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([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]); + popup.hidePopup(); + + // Unload module 0. + loader0.unload(); + test.showMenu(null, function (popup) { + + // Both items should be removed from the menu. + test.checkMenu([], [], [item0, item1]); + test.done(); + }); + }); + }); +}; + + +// Test interactions between a loaded module, unloading another module, and the +// menu separator and overflow submenu. +exports.testMultipleModulesAddRemove = function (test) { + test = new TestHelper(test); + 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]); + 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]); + test.done(); + }); + }); + }); +}; + + +// An item's click listener should work. +exports.testItemClick = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "item", + data: "item data", + contentScript: 'self.on("click", function (node, data) {' + + ' let Ci = Components.interfaces;' + + ' self.postMessage({' + + ' isElt: node instanceof Ci.nsIDOMHTMLElement,' + + ' data: data' + + ' });' + + '});', + onMessage: function (data) { + test.assertEqual(this, item, "`this` inside onMessage should be item"); + test.assert(data.isElt, "node should be an HTML element"); + test.assertEqual(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 (test) { + // Create a top-level menu, submenu, and item, like this: + // topMenu -> submenu -> item + // Click the item and make sure the click bubbles. + test = new TestHelper(test); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "submenu item", + data: "submenu item data" + }); + + let submenu = new loader.cm.Menu({ + label: "submenu", + items: [item] + }); + + let topMenu = new loader.cm.Menu({ + label: "top menu", + contentScript: 'self.on("click", function (node, data) {' + + ' let Ci = Components.interfaces;' + + ' self.postMessage({' + + ' isAnchor: node instanceof Ci.nsIDOMHTMLAnchorElement,' + + ' data: data' + + ' });' + + '});', + onMessage: function (data) { + test.assertEqual(this, topMenu, "`this` inside top menu should be menu"); + test.assert(data.isAnchor, "Clicked node should be anchor"); + test.assertEqual(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 (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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(); + }); +}; + + +// Existing context menu modifications should apply to new windows. +exports.testNewWindow = function (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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([], [], []); + test.done(); + }); + }); + }); +}; + + +// Items in the context menu should be sorted according to locale. +exports.testSorting = function (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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 (test) { + test = new TestHelper(test); + 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); + test.done(); + }); + }); +}; + + +// The binary search of insertionPoint should work OK. +exports.testInsertionPoint = function (test) { + function mockElts(labels) { + return labels.map(function (label) { + return { label: label, getAttribute: function (l) label }; + }); + } + + test = new TestHelper(test); + let loader = test.newLoader(); + let insertionPoint = loader.globalScope.insertionPoint; + + let ip = insertionPoint("a", []); + test.assertStrictEqual(ip, null, "Insertion point should be null"); + + ip = insertionPoint("a", mockElts(["b"])); + test.assertEqual(ip.label, "b", "Insertion point should be 'b'"); + + ip = insertionPoint("c", mockElts(["b"])); + test.assertStrictEqual(ip, null, "Insertion point should be null"); + + ip = insertionPoint("b", mockElts(["a", "c"])); + test.assertEqual(ip.label, "c", "Insertion point should be 'c'"); + + ip = insertionPoint("c", mockElts(["a", "b", "d"])); + test.assertEqual(ip.label, "d", "Insertion point should be 'd'"); + + ip = insertionPoint("a", mockElts(["b", "c", "d"])); + test.assertEqual(ip.label, "b", "Insertion point should be 'b'"); + + ip = insertionPoint("d", mockElts(["a", "b", "c"])); + test.assertStrictEqual(ip, null, "Insertion point should be null"); + + 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 (test) { + test = new TestHelper(test); + 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) { + test.assertEqual(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 (test) { + test = new TestHelper(test); + 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(); + }); + }); +}; + + +// Setting an item's label before the menu is ever shown should correctly change +// its label and, if necessary, its order within the menu. +exports.testSetLabelBeforeShow = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let items = [ + new loader.cm.Item({ label: "a" }), + new loader.cm.Item({ label: "b" }) + ] + items[0].label = "z"; + test.assertEqual(items[0].label, "z"); + + test.showMenu(null, function (popup) { + test.checkMenu([items[1], items[0]], [], []); + test.done(); + }); +}; + + +// Setting an item's label after the menu is shown should correctly change its +// label and, if necessary, its order within the menu. +exports.testSetLabelAfterShow = function (test) { + test = new TestHelper(test); + 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"; + test.assertEqual(items[0].label, "z"); + test.showMenu(null, function (popup) { + test.checkMenu([items[1], items[0]], [], []); + test.done(); + }); + }); +}; + + +// Setting an item's label before the menu is ever shown should correctly change +// its label and, if necessary, its order within the menu if the item is in the +// overflow submenu. +exports.testSetLabelBeforeShowOverflow = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let prefs = loader.loader.require("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"; + test.assertEqual(items[0].label, "z"); + + test.showMenu(null, function (popup) { + test.checkMenu([items[1], items[0]], [], []); + prefs.set(OVERFLOW_THRESH_PREF, OVERFLOW_THRESH_DEFAULT); + test.done(); + }); +}; + + +// Setting an item's label after the menu is shown should correctly change its +// label and, if necessary, its order within the menu if the item is in the +// overflow submenu. +exports.testSetLabelAfterShowOverflow = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let prefs = loader.loader.require("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"; + test.assertEqual(items[0].label, "z"); + test.showMenu(null, function (popup) { + test.checkMenu([items[1], items[0]], [], []); + prefs.set(OVERFLOW_THRESH_PREF, OVERFLOW_THRESH_DEFAULT); + test.done(); + }); + }); +}; + + +// Setting the label of an item in a Menu should work. +exports.testSetLabelMenuItem = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let menu = loader.cm.Menu({ + label: "menu", + items: [loader.cm.Item({ label: "a" })] + }); + menu.items[0].label = "z"; + + test.assertEqual(menu.items[0].label, "z"); + + test.showMenu(null, function (popup) { + test.checkMenu([menu], [], []); + test.done(); + }); +}; + + +// Menu.addItem() should work. +exports.testMenuAddItem = function (test) { + test = new TestHelper(test); + 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" })); + + test.assertEqual(menu.items.length, 3, + "menu should have correct number of items"); + for (let i = 0; i < 3; i++) { + test.assertEqual(menu.items[i].label, "item " + i, + "item label should be correct"); + test.assertEqual(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 (test) { + test = new TestHelper(test); + 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); + + test.assertEqual(menu.items.length, 2, + "menu should have correct number of items"); + for (let i = 0; i < 2; i++) { + test.assertEqual(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 (test) { + test = new TestHelper(test); + 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); + + test.assertEqual(subitem.parentMenu, null, + "item's parent menu should be correct"); + + test.assertEqual(menu.items.length, 2, + "menu should have correct number of items"); + test.assertEqual(menu.items[0].label, "item 0", + "item label should be correct"); + test.assertEqual(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 (test) { + test = new TestHelper(test); + 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); + + test.assertEqual(menu0.items.length, 0, + "menu should have correct number of items"); + + test.assertEqual(menu1.items.length, 1, + "menu should have correct number of items"); + test.assertEqual(menu1.items[0].label, "item", + "item label should be correct"); + + test.assertEqual(subitem.parentMenu, menu1, + "item's parent menu should be correct"); + + test.showMenu(null, function (popup) { + test.checkMenu([menu0, menu1], [], []); + test.done(); + }); +}; + + +// Destroying an item should remove it from its parent menu. +exports.testMenuItemDestroy = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let subitem = loader.cm.Item({ label: "item" }); + let menu = loader.cm.Menu({ + label: "menu", + items: [subitem] + }); + subitem.destroy(); + + test.assertEqual(menu.items.length, 0, + "menu should have correct number of items"); + test.assertEqual(subitem.parentMenu, null, + "item's parent menu should be correct"); + + test.showMenu(null, function (popup) { + test.checkMenu([menu], [], []); + test.done(); + }); +}; + + +// Setting Menu.items should work. +exports.testMenuItemsSetter = function (test) { + test = new TestHelper(test); + 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" }) + ]; + + test.assertEqual(menu.items.length, 3, + "menu should have correct number of items"); + for (let i = 0; i < 3; i++) { + test.assertEqual(menu.items[i].label, "new item " + i, + "item label should be correct"); + test.assertEqual(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 (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let item = loader.cm.Item({ label: "old item 0", data: "old" }); + item.data = "new"; + + test.assertEqual(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 (test) { + test = new TestHelper(test); + 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(); + }); + }); +}; + + +// Test image support. +exports.testItemImage = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let imageURL = require("self").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: [] }); + + test.showMenu(null, function (popup) { + test.checkMenu([item, menu], [], []); + + let imageURL2 = require("self").data.url("dummy.ico"); + item.image = imageURL2; + menu.image = imageURL2; + test.checkMenu([item, menu], [], []); + + item.image = null; + menu.image = null; + test.checkMenu([item, menu], [], []); + + test.done(); + }); +}; + + +// Menu.destroy should destroy the item tree rooted at that menu. +exports.testMenuDestroy = function (test) { + test = new TestHelper(test); + 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++; + }); + test.assertEqual(numRegistryEntries, 0, "All items should be unregistered."); + + test.showMenu(null, function (popup) { + test.checkMenu([], [], [menu]); + test.done(); + }); +}; + + +// NO TESTS BELOW THIS LINE! /////////////////////////////////////////////////// + +// Run only a dummy test if context-menu doesn't support the host app. +if (!require("xul-app").is("Firefox")) { + for (let [prop, val] in Iterator(exports)) + if (/^test/.test(prop) && typeof(val) === "function") + delete exports[prop]; + exports.testAppNotSupported = function (test) { + test.pass("context-menu does not support this application."); + }; +} + + +// 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(test) { + // default waitUntilDone timeout is 10s, which is too short on the win7 + // buildslave + test.waitUntilDone(30*1000); + this.test = test; + this.loaders = []; + this.browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"]. + getService(Ci.nsIWindowMediator). + getMostRecentWindow("navigator:browser"); +} + +TestHelper.prototype = { + get contextMenuPopup() { + return this.browserWindow.document.getElementById("contentAreaContextMenu"); + }, + + get contextMenuSeparator() { + return this.browserWindow.document.getElementById(SEPARATOR_ID); + }, + + get overflowPopup() { + return this.browserWindow.document.getElementById(OVERFLOW_POPUP_ID); + }, + + get overflowSubmenu() { + return this.browserWindow.document.getElementById(OVERFLOW_MENU_ID); + }, + + get tabBrowser() { + return this.browserWindow.gBrowser; + }, + + // Methods on the wrapped test can be called on this object. + __noSuchMethod__: function (methodName, args) { + this.test[methodName].apply(this.test, args); + }, + + // Asserts that absentItems -- an array of items that should not match the + // current context -- aren't present in the menu. + checkAbsentItems: function (presentItems, absentItems) { + for (let i = 0; i < absentItems.length; i++) { + let item = absentItems[i]; + let elt = this.getItemElt(this.contextMenuPopup, item); + + // The implementation actually hides items rather than removing or not + // adding them in the first place, but that's an implementation detail. + this.test.assert(!elt || elt.hidden, + "Item should not be present in top-level menu"); + + if (this.shouldOverflow(presentItems)) { + elt = getItemElt(this.overflowPopup, item); + this.test.assert(!elt || elt.hidden, + "Item should not be present in overflow submenu"); + } + } + }, + + // Asserts that elt, a DOM element representing item, looks OK. + checkItemElt: function (elt, item) { + let itemType = this.getItemType(item); + + switch (itemType) { + case "Item": + this.test.assertEqual(elt.localName, "menuitem", + "Item DOM element should be a xul:menuitem"); + if (typeof(item.data) === "string") { + this.test.assertEqual(elt.getAttribute("value"), item.data, + "Item should have correct data"); + } + break + case "Menu": + this.test.assertEqual(elt.localName, "menu", + "Menu DOM element should be a xul:menu"); + let subPopup = elt.firstChild; + this.test.assert(subPopup, "xul:menu should have a child"); + this.test.assertEqual(subPopup.localName, "menupopup", + "xul:menu's first child should be a menupopup"); + break; + case "Separator": + this.test.assertEqual(elt.localName, "menuseparator", + "Separator DOM element should be a xul:menuseparator"); + break; + } + + if (itemType === "Item" || itemType === "Menu") { + this.test.assertEqual(elt.getAttribute("label"), item.label, + "Item should have correct title"); + if (typeof(item.image) === "string") + this.test.assertEqual(elt.getAttribute("image"), item.image, + "Item should have correct image"); + else + this.test.assert(!elt.hasAttribute("image"), + "Item should not have image"); + } + }, + + // Asserts that the context menu looks OK given the arguments. presentItems + // are items that should match the current context. absentItems are items + // that shouldn't. removedItems are items that have been removed from the + // menu. + checkMenu: function (presentItems, absentItems, removedItems) { + this.checkSeparator(presentItems); + this.checkOverflow(presentItems); + this.checkPresentItems(presentItems); + this.checkAbsentItems(presentItems, absentItems); + this.checkRemovedItems(removedItems); + this.checkSort(presentItems); + }, + + // Asserts that the overflow submenu is present or absent as appropriate for + // presentItems. + checkOverflow: function (presentItems) { + let submenu = this.overflowSubmenu; + if (this.shouldOverflow(presentItems)) { + this.test.assert(submenu && !submenu.hidden, + "Overflow submenu should be present"); + this.test.assert(submenu.localName, "menu", + "Overflow submenu should be a "); + let overflowPopup = this.overflowPopup; + this.test.assert(overflowPopup, + "Overflow submenu popup should be present"); + this.test.assert(overflowPopup.localName, "menupopup", + "Overflow submenu popup should be a "); + } + else { + this.test.assert(!submenu || submenu.hidden, + "Overflow submenu should be absent"); + } + }, + + // Asserts that the items that are present in the menu because they match the + // current context look OK. + checkPresentItems: function (presentItems) { + function recurse(popup, items, isTopLevel) { + items.forEach(function (item) { + let elt = this.getItemElt(popup, item); + + if (isTopLevel) { + if (this.shouldOverflow(items)) { + this.test.assert(!elt || elt.hidden, + "Item should not be present in top-level menu"); + + let overflowPopup = this.overflowPopup; + this.test.assert(overflowPopup, + "Overflow submenu should be present"); + + elt = this.getItemElt(overflowPopup, item); + this.test.assert(elt && !elt.hidden, + "Item should be present in overflow submenu"); + } + else { + this.test.assert(elt && !elt.hidden, + "Item should be present in top-level menu"); + } + } + else { + this.test.assert(elt && !elt.hidden, + "Item should be present in menu"); + } + + this.checkItemElt(elt, item); + if (this.getItemType(item) === "Menu") + recurse.call(this, elt.firstChild, item.items, false); + }, this); + } + + recurse.call(this, this.contextMenuPopup, presentItems, true); + }, + + // Asserts that items that have been removed from the menu are really removed. + checkRemovedItems: function (removedItems) { + for (let i = 0; i < removedItems.length; i++) { + let item = removedItems[i]; + + let elt = this.getItemElt(this.contextMenuPopup, item); + this.test.assert(!elt, "Item should be removed from top-level menu"); + + let overflowPopup = this.overflowPopup; + if (overflowPopup) { + elt = this.getItemElt(overflowPopup, item); + this.test.assert(!elt, "Item should be removed from overflow submenu"); + } + } + }, + + // Asserts that the menu separator separating standard items from our items + // looks OK. + checkSeparator: function (presentItems) { + let sep = this.contextMenuSeparator; + if (presentItems.length) { + this.test.assert(sep && !sep.hidden, "Menu separator should be present"); + this.test.assertEqual(sep.localName, "menuseparator", + "Menu separator should be a "); + } + else { + this.test.assert(!sep || sep.hidden, "Menu separator should be absent"); + } + }, + + // Asserts that our items are sorted. + checkSort: function (presentItems) { + // Get the first item in sorted order, get its elt, walk the nextSibling + // chain, making sure each is greater than the previous. + if (presentItems.length) { + let sorted = presentItems.slice(0). + sort(function (a, b) a.label.localeCompare(b.label)); + let elt = this.shouldOverflow(presentItems) ? + this.getItemElt(this.overflowPopup, sorted[0]) : + this.getItemElt(this.contextMenuPopup, sorted[0]); + let numElts = 1; + while (elt.nextSibling && + elt.nextSibling.className.split(/\s+/).indexOf(ITEM_CLASS) >= 0) { + let eltLabel = elt.getAttribute("label"); + let nextLabel = elt.nextSibling.getAttribute("label"); + this.test.assert(eltLabel.localeCompare(nextLabel) < 0, + "Item label should be < next item's label"); + elt = elt.nextSibling; + numElts++; + } + this.test.assertEqual(numElts, presentItems.length, + "The first item in sorted order should have the " + + "first element in sorted order"); + } + }, + + // 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. + delayedEventListener: function (node, event, callback, useCapture) { + const self = this; + node.addEventListener(event, function handler(evt) { + node.removeEventListener(event, handler, useCapture); + require("timer").setTimeout(function () { + try { + callback.call(self, evt); + } + catch (err) { + self.test.exception(err); + self.test.done(); + } + }, 20); + }, useCapture); + }, + + // Call to finish the test. + done: function () { + function commonDone() { + if (this.tab) { + this.tabBrowser.removeTab(this.tab); + this.tabBrowser.selectedTab = this.oldSelectedTab; + } + while (this.loaders.length) { + let browserManager = this.loaders[0].globalScope.browserManager; + let topLevelItems = browserManager.topLevelItems.slice(); + let privatePropsKey = this.loaders[0].globalScope.PRIVATE_PROPS_KEY; + let workerRegs = topLevelItems.map(function (item) { + return item.valueOf(privatePropsKey)._workerReg; + }); + + this.loaders[0].unload(); + + // Make sure the browser manager is cleaned up. + this.test.assertEqual(browserManager.browserWins.length, 0, + "browserManager should have no windows left"); + this.test.assertEqual(browserManager.topLevelItems.length, 0, + "browserManager should have no items left"); + this.test.assert(!("contentWins" in browserManager), + "browserManager should have no content windows left"); + + // Make sure the items' worker registries are cleaned up. + topLevelItems.forEach(function (item) { + this.test.assert(!("_workerReg" in item.valueOf(privatePropsKey)), + "item's worker registry should be removed"); + }, this); + workerRegs.forEach(function (workerReg) { + this.test.assertEqual(Object.keys(workerReg.winWorkers).length, 0, + "worker registry should be empty"); + this.test.assertEqual( + Object.keys(workerReg.winsWithoutWorkers).length, 0, + "worker registry list of windows without workers should be empty"); + }, this); + } + this.test.done(); + } + + 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(); + } + }, + + // 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("context-menu"), + globalScope: loader.sandbox("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 number of presentItems crosses the overflow threshold. + shouldOverflow: function (presentItems) { + return presentItems.length > + (this.loaders.length ? + this.loaders[0].loader.require("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 = 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); + }, + + // 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 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); + } +}; diff --git a/tools/addon-sdk-1.5/packages/addon-kit/tests/test-hotkeys.js b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-hotkeys.js new file mode 100644 index 0000000..0e0ecd6 --- /dev/null +++ b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-hotkeys.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 { Hotkey } = require("hotkeys"); +const { keyDown } = require("dom/events/keys"); +const { Loader } = require('./helpers'); + +exports["test hotkey: function key"] = function(assert, done) { + var element = require("window-utils").activeBrowserWindow.document.documentElement; + var showHotKey = Hotkey({ + combo: "f1", + onPress: function() { + assert.pass("first callback is called"); + 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 = require("window-utils").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 = require("window-utils").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 = require("window-utils").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 = require("window-utils").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("window-utils").activeBrowserWindow.document.documentElement; + var hotkey = loader.require("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"); + + require("timer").setTimeout(function () { + assert.ok(!called, "Hotkey is destroyed and not called."); + done(); + }, 0); +}; + +require("test").run(exports); diff --git a/tools/addon-sdk-1.5/packages/addon-kit/tests/test-l10n.js b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-l10n.js new file mode 100644 index 0000000..fe8eb1f --- /dev/null +++ b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-l10n.js @@ -0,0 +1,56 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 prefs = require("preferences-service"); +const { Loader } = require('./helpers'); + +exports.testExactMatching = function(test) { + let loader = Loader(module); + + prefs.set("general.useragent.locale", "fr-FR"); + let _ = loader.require("l10n").get; + test.assertEqual(_("Not translated"), "Not translated", + "Key not translated"); + test.assertEqual(_("Translated"), "Oui", + "Simple key translated"); + + // Placeholders + test.assertEqual(_("placeholderString", "works"), "Placeholder works", + "Value with placeholder"); + test.assertEqual(_("Placeholder %s", "works"), "Placeholder works", + "Key without value but with placeholder"); + test.assertEqual(_("Placeholders %2s %1s %s.", "working", "are", "correctly"), + "Placeholders are working correctly.", + "Multiple placeholders"); + + // Plurals + test.assertEqual(_("downloadsCount", 1), + "Un téléchargement", + "PluralForm as value, 1st form"); + test.assertEqual(_("downloadsCount", 2), + "2 téléchargements", + "PluralForm as value, 1nd form"); + + loader.unload(); +} + +exports.testEnUsLocaleName = function(test) { + let loader = Loader(module); + + prefs.set("general.useragent.locale", "en-US"); + let _ = loader.require("l10n").get; + test.assertEqual(_("Not translated"), "Not translated"); + test.assertEqual(_("Translated"), "Yes"); + loader.unload(); +} + +exports.testShortLocaleName = function(test) { + let loader = Loader(module); + + prefs.set("general.useragent.locale", "eo"); + let _ = loader.require("l10n").get; + test.assertEqual(_("Not translated"), "Not translated"); + test.assertEqual(_("Translated"), "jes"); + loader.unload(); +} diff --git a/tools/addon-sdk-1.5/packages/addon-kit/tests/test-module.js b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-module.js new file mode 100644 index 0000000..957d075 --- /dev/null +++ b/tools/addon-sdk-1.5/packages/addon-kit/tests/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("addon-kit/hotkeys")), + "module exports are frozen"); +}; + +exports["test redefine exported property"] = function(assert) { + let hotkeys = require("addon-kit/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("addon-kit/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("addon-kit/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/tools/addon-sdk-1.5/packages/addon-kit/tests/test-notifications.js b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-notifications.js new file mode 100644 index 0000000..b0e1f37 --- /dev/null +++ b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-notifications.js @@ -0,0 +1,46 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim:set ts=2 sw=2 sts=2 et filetype=javascript + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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('./helpers'); + +exports.testOnClick = function (test) { + let [loader, mockAlertServ] = makeLoader(module); + let notifs = loader.require("notifications"); + let data = "test data"; + let opts = { + onClick: function (clickedData) { + test.assertEqual(this, notifs, "|this| should be notifications module"); + test.assertEqual(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(); +}; + +// Returns [loader, mockAlertService]. +function makeLoader(test) { + 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("notifications"); + let scope = loader.sandbox("notifications"); + scope.notify = mockAlertServ.showAlertNotification.bind(mockAlertServ); + return [loader, mockAlertServ]; +}; diff --git a/tools/addon-sdk-1.5/packages/addon-kit/tests/test-page-mod.js b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-page-mod.js new file mode 100644 index 0000000..9c6c1de --- /dev/null +++ b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-page-mod.js @@ -0,0 +1,420 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 pageMod = require("page-mod"); +var testPageMod = require("pagemod-test-helpers").testPageMod; +const { Loader } = require('./helpers'); +const tabs = require("tabs"); + +/* XXX This can be used to delay closing the test Firefox instance for interactive + * testing or visual inspection. This test is registered first so that it runs + * the last. */ +exports.delay = function(test) { + if (false) { + test.waitUntilDone(60000); + require("timer").setTimeout(function() {test.done();}, 4000); + } else + test.pass(); +} + +/* Tests for the PageMod APIs */ + +exports.testPageMod1 = function(test) { + let mods = testPageMod(test, "about:", [{ + include: /about:/, + contentScriptWhen: 'end', + contentScript: 'new ' + function WorkerScope() { + window.document.body.setAttribute("JEP-107", "worked"); + }, + onAttach: function() { + test.assertEqual(this, mods[0], "The 'this' object is the page mod."); + } + }], + function(win, done) { + test.assertEqual( + win.document.body.getAttribute("JEP-107"), + "worked", + "PageMod.onReady test" + ); + done(); + } + ); +}; + +exports.testPageMod2 = function(test) { + testPageMod(test, "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) { + test.assertEqual(win.document.documentElement.getAttribute("first"), + "true", + "PageMod test #2: first script has run"); + test.assertEqual(win.document.documentElement.getAttribute("second"), + "true", + "PageMod test #2: second script has run"); + test.assertEqual("AUQLUE" in win, false, + "PageMod test #2: scripts get a wrapped window"); + done(); + }); +}; + +exports.testPageModIncludes = function(test) { + var asserts = []; + function createPageModTest(include, expectedMatch) { + // Create an 'onload' test function... + asserts.push(function(test, win) { + var matches = include in win.localStorage; + test.assert(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(test, "about:buildconfig", [ + createPageModTest("*", false), + createPageModTest("*.google.com", false), + createPageModTest("about:*", true), + createPageModTest("about:", false), + createPageModTest("about:buildconfig", true) + ], + function (win, done) { + test.waitUntil(function () win.localStorage["about:buildconfig"], + "about:buildconfig page-mod to be executed") + .then(function () { + asserts.forEach(function(fn) { + fn(test, win); + }); + done(); + }); + } + ); +}; + +exports.testPageModErrorHandling = function(test) { + test.assertRaises(function() { + new pageMod.PageMod(); + }, + 'pattern is undefined', + "PageMod() throws when 'include' option is not specified."); +}; + +/* Tests for internal functions. */ +exports.testCommunication1 = function(test) { + let workerDone = false, + callbackDone = null; + + testPageMod(test, "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) { + test.fail('Errors where reported'); + }); + worker.on('message', function(value) { + test.assertEqual( + "worked", + value, + "test comunication" + ); + workerDone = true; + if (callbackDone) + callbackDone(); + }); + worker.postMessage('do it!') + } + }], + function(win, done) { + (callbackDone = function() { + if (workerDone) { + test.assertEqual( + 'worked', + win.document.body.getAttribute('JEP-107'), + 'attribute should be modified' + ); + done(); + } + })(); + } + ); +}; + +exports.testCommunication2 = function(test) { + let callbackDone = null, + window; + + testPageMod(test, "about:", [{ + 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) { + test.fail('Errors where reported'); + }); + worker.on('message', function(msg) { + if ('onload' == msg) { + test.assertEqual( + '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 { + test.assertEqual( + 'changes in window', + msg, + 'PageMod test #2: second script has run' + ) + callbackDone(); + } + }); + } + }], + function(win, done) { + window = win; + callbackDone = done; + } + ); +}; + +exports.testEventEmitter = function(test) { + let workerDone = false, + callbackDone = null; + + testPageMod(test, "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) { + test.fail('Errors were reported : '+e); + }); + worker.port.on('content-to-addon', function(value) { + test.assertEqual( + "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(test) { + let doneCallback = null; + let messages = 0; + let modObject = { + include: "data:text/html,", + 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) { + test.fail("Page mod contexts are mixed."); + doneCallback(); + } + else if (++messages == 2) { + test.pass("Page mod contexts are different."); + doneCallback(); + } + }); + } + }; + testPageMod(test, "data:text/html,", [modObject, modObject], + function(win, done) { + doneCallback = done; + } + ); +}; + +exports.testHistory = function(test) { + // 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 = require("self").data.url("test-page-mod.html"); + let callbackDone = null; + testPageMod(test, 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) { + test.assertEqual(JSON.stringify(data), JSON.stringify({foo: "bar"}), + "History API works!"); + callbackDone(); + }); + } + }], + function(win, done) { + callbackDone = done; + } + ); +}; + +exports.testRelatedTab = function(test) { + test.waitUntilDone(); + + let tab; + let { PageMod } = require("page-mod"); + let pageMod = new PageMod({ + include: "about:*", + onAttach: function(worker) { + test.assertEqual(tab, worker.tab, "Worker.tab is valid"); + pageMod.destroy(); + tab.close(); + test.done(); + } + }); + + tabs.open({ + url: "about:", + onOpen: function onOpen(t) { + tab = t; + } + }); + +}; + +exports['test tab worker on message'] = function(test) { + test.waitUntilDone(); + + let { browserWindows } = require("windows"); + let tabs = require("tabs"); + let { PageMod } = require("page-mod"); + + let url1 = "data:text/html,tab1

worker1.tab

"; + let url2 = "data:text/html,tab2

worker2.tab

"; + let worker1 = null; + + let mod = PageMod({ + include: "data:text/html,*", + contentScriptWhen: "ready", + contentScript: "self.postMessage('#1');", + onAttach: function onAttach(worker) { + worker.on("message", function onMessage() { + this.tab.attach({ + contentScriptWhen: "ready", + contentScript: "self.postMessage({ url: window.location.href, title: document.title });", + onMessage: function onMessage(data) { + test.assertEqual(this.tab.url, data.url, "location is correct"); + test.assertEqual(this.tab.title, data.title, "title is correct"); + if (this.tab.url === url1) { + worker1 = this; + tabs.open({ url: url2, inBackground: true }); + } + else if (this.tab.url === url2) { + mod.destroy(); + worker1.tab.close(); + worker1.destroy(); + worker.tab.close(); + worker.destroy(); + test.done(); + } + } + }); + }); + } + }); + + tabs.open(url1); +}; + +exports.testAutomaticDestroy = function(test) { + test.waitUntilDone(); + let loader = Loader(module); + + let pageMod = loader.require("page-mod").PageMod({ + include: "about:*", + contentScriptWhen: "start", + onAttach: function(w) { + test.fail("Page-mod should have been detroyed during module unload"); + } + }); + + // Unload the page-mod module so that our page mod is destroyed + loader.unload(); + + // Then create a second tab to ensure that it is correctly destroyed + let tabs = require("tabs"); + tabs.open({ + url: "about:", + onReady: function onReady(tab) { + test.pass("check automatic destroy"); + tab.close(); + test.done(); + } + }); + +} diff --git a/tools/addon-sdk-1.5/packages/addon-kit/tests/test-page-worker.js b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-page-worker.js new file mode 100644 index 0000000..5fc3bec --- /dev/null +++ b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-page-worker.js @@ -0,0 +1,366 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +let tests = {}, Pages, Page; +const { Loader } = require('./helpers'); + +const ERR_DESTROYED = + "The page has been destroyed and can no longer be used."; + +tests.testSimplePageCreation = function(test) { + test.waitUntilDone(); + + let page = new Page({ + contentScript: "self.postMessage(window.location.href)", + contentScriptWhen: "end", + onMessage: function (message) { + test.assertEqual(message, "about:blank", + "Page Worker should start with a blank page by default"); + test.assertEqual(this, page, "The 'this' object is the page itself."); + test.done(); + } + }); +} + +/* + * Tests that we can't be tricked by document overloads as we have access + * to wrapped nodes + */ +tests.testWrappedDOM = function(test) { + test.waitUntilDone(); + + let page = Page({ + allow: { script: true }, + contentURL: "data:text/html,", + contentScript: "window.addEventListener('load', function () " + + "self.postMessage([typeof(document.getElementById), " + + "typeof(window.scrollTo)]), true)", + onMessage: function (message) { + test.assertEqual(message[0], + "function", + "getElementById from content script is the native one"); + + test.assertEqual(message[1], + "function", + "scrollTo from content script is the native one"); + + test.done(); + } + }); +} + +/* +// We do not offer unwrapped access to DOM since bug 601295 landed +// See 660780 to track progress of unwrap feature +tests.testUnwrappedDOM = function(test) { + test.waitUntilDone(); + + let page = Page({ + allow: { script: true }, + contentURL: "data:text/html,", + contentScript: "window.addEventListener('load', function () " + + "self.postMessage([typeof(unsafeWindow.document.getElementById), " + + "typeof(unsafeWindow.scrollTo)]), true)", + onMessage: function (message) { + test.assertEqual(message[0], + "number", + "document inside page is free to be changed"); + + test.assertEqual(message[1], + "number", + "window inside page is free to be changed"); + + test.done(); + } + }); +} +*/ + +tests.testPageProperties = function(test) { + let page = new Page(); + + for each (let prop in ['contentURL', 'allow', 'contentScriptFile', + 'contentScript', 'contentScriptWhen', 'on', + 'postMessage', 'removeListener']) { + test.assert(prop in page, prop + " property is defined on page."); + } + + test.assert(function () page.postMessage("foo") || true, + "postMessage doesn't throw exception on page."); +} + +tests.testConstructorAndDestructor = function(test) { + test.waitUntilDone(); + + let loader = Loader(module); + let Pages = loader.require("page-worker"); + let global = loader.sandbox("page-worker"); + + let pagesReady = 0; + + let page1 = Pages.Page({ + contentScript: "self.postMessage('')", + contentScriptWhen: "end", + onMessage: pageReady + }); + let page2 = Pages.Page({ + contentScript: "self.postMessage('')", + contentScriptWhen: "end", + onMessage: pageReady + }); + + test.assertNotEqual(page1, page2, + "Page 1 and page 2 should be different objects."); + + function pageReady() { + if (++pagesReady == 2) { + page1.destroy(); + page2.destroy(); + + test.assert(isDestroyed(page1), "page1 correctly unloaded."); + test.assert(isDestroyed(page2), "page2 correctly unloaded."); + + loader.unload(); + test.done(); + } + } +} + +tests.testAutoDestructor = function(test) { + test.waitUntilDone(); + + let loader = Loader(module); + let Pages = loader.require("page-worker"); + + let page = Pages.Page({ + contentScript: "self.postMessage('')", + contentScriptWhen: "end", + onMessage: function() { + loader.unload(); + test.assert(isDestroyed(page), "Page correctly unloaded."); + test.done(); + } + }); +} + +tests.testValidateOptions = function(test) { + test.assertRaises( + function () Page({ contentURL: 'home' }), + "The `contentURL` option must be a valid URL.", + "Validation correctly denied a non-URL contentURL" + ); + + test.assertRaises( + function () Page({ onMessage: "This is not a function."}), + "The event listener must be a function.", + "Validation correctly denied a non-function onMessage." + ); + + test.pass("Options validation is working."); +} + +tests.testContentAndAllowGettersAndSetters = function(test) { + test.waitUntilDone(); + let content = "data:text/html,"; + let page = Page({ + contentURL: content, + contentScript: "self.postMessage(window.localStorage.allowScript)", + contentScriptWhen: "end", + onMessage: step0 + }); + + function step0(message) { + test.assertEqual(message, "3", + "Correct value expected for allowScript - 3"); + test.assertEqual(page.contentURL, content, + "Correct content expected"); + page.removeListener('message', step0); + page.on('message', step1); + page.allow = { script: false }; + page.contentURL = content = + "data:text/html,"; + } + + function step1(message) { + test.assertEqual(message, "3", + "Correct value expected for allowScript - 3"); + test.assertEqual(page.contentURL, content, "Correct content expected"); + page.removeListener('message', step1); + page.on('message', step2); + page.allow = { script: true }; + page.contentURL = content = + "data:text/html,"; + } + + function step2(message) { + test.assertEqual(message, "g", + "Correct value expected for allowScript - g"); + test.assertEqual(page.contentURL, content, "Correct content expected"); + page.removeListener('message', step2); + page.on('message', step3); + page.allow.script = false; + page.contentURL = content = + "data:text/html,"; + } + + function step3(message) { + test.assertEqual(message, "g", + "Correct value expected for allowScript - g"); + test.assertEqual(page.contentURL, content, "Correct content expected"); + page.removeListener('message', step3); + page.on('message', step4); + page.allow.script = true; + page.contentURL = content = + "data:text/html,"; + } + + function step4(message) { + test.assertEqual(message, "4", + "Correct value expected for allowScript - 4"); + test.assertEqual(page.contentURL, content, "Correct content expected"); + test.done(); + } + +} + +tests.testOnMessageCallback = function(test) { + test.waitUntilDone(); + + Page({ + contentScript: "self.postMessage('')", + contentScriptWhen: "end", + onMessage: function() { + test.pass("onMessage callback called"); + test.done(); + } + }); +} + +tests.testMultipleOnMessageCallbacks = function(test) { + test.waitUntilDone(); + + let count = 0; + let page = Page({ + contentScript: "self.postMessage('')", + contentScriptWhen: "end", + onMessage: function() count += 1 + }); + page.on('message', function() count += 2); + page.on('message', function() count *= 3); + page.on('message', function() + test.assertEqual(count, 9, "All callbacks were called, in order.")); + page.on('message', function() test.done()); + +} + +tests.testLoadContentPage = function(test) { + + test.waitUntilDone(); + + let page = Page({ + onMessage: function(message) { + // The message is an array whose first item is the test method to call + // and the rest of whose items are arguments to pass it. + test[message.shift()].apply(test, message); + }, + contentURL: require("self").data.url("test-page-worker.html"), + contentScriptFile: require("self").data.url("test-page-worker.js"), + contentScriptWhen: "ready" + }); + +} + +tests.testAllowScriptDefault = function(test) { + + test.waitUntilDone(); + + let page = Page({ + onMessage: function(message) { + test.assert(message, "Script is allowed to run by default."); + test.done(); + }, + contentURL: "data:text/html,", + contentScript: "self.postMessage(document.documentElement.getAttribute('foo'))", + contentScriptWhen: "ready" + }); +} + +tests.testAllowScript = function(test) { + + test.waitUntilDone(); + + let page = Page({ + onMessage: function(message) { + test.assert(message, "Script runs when allowed to do so."); + test.done(); + }, + allow: { script: true }, + contentURL: "data:text/html,", + contentScript: "self.postMessage(document.documentElement.hasAttribute('foo') && " + + " document.documentElement.getAttribute('foo') == 3)", + contentScriptWhen: "ready" + }); +} + +tests.testPingPong = function(test) { + test.waitUntilDone(); + let page = Page({ + contentURL: 'data:text/html,ping-pong', + contentScript: 'self.on("message", function(message) self.postMessage("pong"));' + + 'self.postMessage("ready");', + onMessage: function(message) { + if ('ready' == message) { + page.postMessage('ping'); + } + else { + test.assert(message, 'pong', 'Callback from contentScript'); + test.done(); + } + } + }); +}; + +tests.testMultipleDestroys = function(test) { + let page = Page(); + page.destroy(); + page.destroy(); + test.pass("Multiple destroys should not cause an error"); +}; + + +function isDestroyed(page) { + try { + page.postMessage("foo"); + } + catch (err if err.message == ERR_DESTROYED) { + return true; + } + return false; +} + + +let pageWorkerSupported = true; + +try { + Pages = require("page-worker"); + Page = Pages.Page; +} +catch (ex if ex.message == [ + "The page-worker module currently supports only Firefox and Thunderbird. ", + "In the future, we would like it to support other applications, however. ", + "Please see https://bugzilla.mozilla.org/show_bug.cgi?id=546740 for more ", + "information." + ].join("")) { + pageWorkerSupported = false; +} + +if (pageWorkerSupported) { + for (let test in tests) { + exports[test] = tests[test]; + } +} else { + exports.testPageWorkerNotSupported = function(test) { + test.pass("The page-worker module is not supported on this app."); + } +} diff --git a/tools/addon-sdk-1.5/packages/addon-kit/tests/test-panel.js b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-panel.js new file mode 100644 index 0000000..d20639a --- /dev/null +++ b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-panel.js @@ -0,0 +1,441 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +let { Cc, Ci } = require("chrome"); +let panels = require('panel'); +let tests = {}, panels, Panel; +const { Loader } = require('./helpers'); + +tests.testPanel = function(test) { + test.waitUntilDone(); + let panel = Panel({ + contentURL: "about:buildconfig", + contentScript: "self.postMessage(1); self.on('message', function() self.postMessage(2));", + onMessage: function (message) { + test.assertEqual(this, panel, "The 'this' object is the panel."); + switch(message) { + case 1: + test.pass("The panel was loaded."); + panel.postMessage(''); + break; + case 2: + test.pass("The panel posted a message and received a response."); + panel.destroy(); + test.done(); + break; + } + } + }); +}; + +tests.testPanelEmit = function(test) { + test.waitUntilDone(); + let panel = Panel({ + contentURL: "about:buildconfig", + contentScript: "self.port.emit('loaded');" + + "self.port.on('addon-to-content', " + + " function() self.port.emit('received'));", + }); + panel.port.on("loaded", function () { + test.pass("The panel was loaded and sent a first event."); + panel.port.emit("addon-to-content"); + }); + panel.port.on("received", function () { + test.pass("The panel posted a message and received a response."); + panel.destroy(); + test.done(); + }); +}; + +tests.testPanelEmitEarly = function(test) { + test.waitUntilDone(); + let panel = Panel({ + contentURL: "about:buildconfig", + contentScript: "self.port.on('addon-to-content', " + + " function() self.port.emit('received'));", + }); + panel.port.on("received", function () { + test.pass("The panel posted a message early and received a response."); + panel.destroy(); + test.done(); + }); + panel.port.emit("addon-to-content"); +}; + +tests.testShowHidePanel = function(test) { + test.waitUntilDone(); + let panel = Panel({ + contentScript: "self.postMessage('')", + contentScriptWhen: "end", + onMessage: function (message) { + panel.show(); + }, + onShow: function () { + test.pass("The panel was shown."); + test.assertEqual(this, panel, "The 'this' object is the panel."); + test.assertEqual(this.isShowing, true, "panel.isShowing == true."); + panel.hide(); + }, + onHide: function () { + test.pass("The panel was hidden."); + test.assertEqual(this, panel, "The 'this' object is the panel."); + test.assertEqual(this.isShowing, false, "panel.isShowing == false."); + panel.destroy(); + test.done(); + } + }); +}; + +tests.testDocumentReload = function(test) { + test.waitUntilDone(); + let content = + ""; + let messageCount = 0; + let panel = Panel({ + contentURL: "data:text/html," + encodeURIComponent(content), + contentScript: "self.postMessage(window.location.href)", + onMessage: function (message) { + messageCount++; + if (messageCount == 1) { + test.assertMatches(message, /data:text\/html,/, "First document had a content script"); + } + else if (messageCount == 2) { + test.assertEqual(message, "about:blank", "Second document too"); + panel.destroy(); + test.done(); + } + } + }); +}; + +tests.testParentResizeHack = function(test) { + let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"]. + getService(Ci.nsIWindowMediator). + getMostRecentWindow("navigator:browser"); + let docShell = browserWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell); + if (!("allowWindowControl" in docShell)) { + // bug 635673 is not fixed in this firefox build + test.pass("allowWindowControl attribute that allow to fix browser window " + + "resize is not available on this build."); + return; + } + + test.waitUntilDone(30000); + + let previousWidth = browserWindow.outerWidth, previousHeight = browserWindow.outerHeight; + + let content = "" + + "Try to resize browser window"; + let panel = Panel({ + contentURL: "data:text/html," + encodeURIComponent(content), + contentScript: "self.on('message', function(message){" + + " if (message=='resize') " + + " unsafeWindow.contentResize();" + + "});", + contentScriptWhen: "ready", + onMessage: function (message) { + + }, + onShow: function () { + panel.postMessage('resize'); + require("timer").setTimeout(function () { + test.assertEqual(previousWidth,browserWindow.outerWidth,"Size doesn't change by calling resizeTo/By/..."); + test.assertEqual(previousHeight,browserWindow.outerHeight,"Size doesn't change by calling resizeTo/By/..."); + panel.destroy(); + test.done(); + },0); + } + }); + panel.show(); +} + +tests.testResizePanel = function(test) { + test.waitUntilDone(); + + // These tests fail on Linux if the browser window in which the panel + // is displayed is not active. And depending on what other tests have run + // before this one, it might not be (the untitled window in which the test + // runner executes is often active). So we make sure the browser window + // is focused by focusing it before running the tests. Then, to be the best + // possible test citizen, we refocus whatever window was focused before we + // started running these tests. + + let activeWindow = Cc["@mozilla.org/embedcomp/window-watcher;1"]. + getService(Ci.nsIWindowWatcher). + activeWindow; + let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"]. + getService(Ci.nsIWindowMediator). + getMostRecentWindow("navigator:browser"); + + + function onFocus() { + browserWindow.removeEventListener("focus", onFocus, true); + + let panel = Panel({ + contentScript: "self.postMessage('')", + contentScriptWhen: "end", + height: 10, + width: 10, + onMessage: function (message) { + panel.show(); + }, + onShow: function () { + panel.resize(100,100); + panel.hide(); + }, + onHide: function () { + test.assert((panel.width == 100) && (panel.height == 100), + "The panel was resized."); + if (activeWindow) + activeWindow.focus(); + test.done(); + } + }); + } + + if (browserWindow === activeWindow) { + onFocus(); + } + else { + browserWindow.addEventListener("focus", onFocus, true); + browserWindow.focus(); + } +}; + +tests.testHideBeforeShow = function(test) { + test.waitUntilDone(); + let showCalled = false; + let panel = Panel({ + onShow: function () { + showCalled = true; + }, + onHide: function () { + test.assert(!showCalled, 'must not emit show if was hidden before'); + test.done(); + } + }); + panel.show(); + panel.hide(); +}; + +tests.testSeveralShowHides = function(test) { + test.waitUntilDone(); + let hideCalled = 0; + let panel = panels.Panel({ + contentURL: "about:buildconfig", + onShow: function () { + panel.hide(); + }, + onHide: function () { + hideCalled++; + if (hideCalled < 3) + panel.show(); + else { + test.pass("onHide called three times as expected"); + test.done(); + } + } + }); + panel.on('error', function(e) { + test.fail('error was emitted:' + e.message + '\n' + e.stack); + }); + panel.show(); +}; + +tests.testAnchorAndArrow = function(test) { + test.waitUntilDone(20000); + let count = 0; + function newPanel(tab, anchor) { + let panel = panels.Panel({ + contentURL: "data:text/html,Anchor: " + + anchor.id + "", + width: 200, + height: 100, + onShow: function () { + count++; + panel.destroy(); + if (count==5) { + test.pass("All anchored panel test displayed"); + tab.close(function () { + test.done(); + }); + } + } + }); + panel.show(anchor); + } + + let tabs= require("tabs"); + let url = 'data:text/html,' + + 'foo' + + '' + + '
Top Left
' + + '
Top Right
' + + '
Bottom Left
' + + '
Bottom right
' + + ''; + + tabs.open({ + url: url, + onReady: function(tab) { + let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"]. + getService(Ci.nsIWindowMediator). + getMostRecentWindow("navigator:browser"); + let window = browserWindow.content; + newPanel(tab, window.document.getElementById('tl')); + newPanel(tab, window.document.getElementById('tr')); + newPanel(tab, window.document.getElementById('bl')); + newPanel(tab, window.document.getElementById('br')); + let anchor = browserWindow.document.getElementById("identity-box"); + newPanel(tab, anchor); + } + }); + + + +}; + +tests.testPanelTextColor = function(test) { + test.waitUntilDone(); + let html = "" + + "

Foo

"; + let panel = Panel({ + contentURL: "data:text/html," + encodeURI(html), + contentScript: "self.port.emit('color', " + + "window.getComputedStyle(document.body.firstChild, null). " + + " getPropertyValue('color'));" + }); + panel.port.on("color", function (color) { + test.assertEqual(color, "rgb(255, 255, 0)", + "The panel text color style is preserved when a style exists."); + panel.destroy(); + test.done(); + }); +}; + +function makeEventOrderTest(options) { + let expectedEvents = []; + + return function(test) { + let panel = panels.Panel({ contentURL: "about:buildconfig" }); + + function expect(event, cb) { + expectedEvents.push(event); + panel.on(event, function() { + test.assertEqual(event, expectedEvents.shift()); + if (cb) + require("timer").setTimeout(cb, 1); + }); + return {then: expect}; + } + + test.waitUntilDone(); + options.test(test, expect, panel); + } +} + +tests.testAutomaticDestroy = function(test) { + let loader = Loader(module); + let panel = loader.require("panel").Panel({ + contentURL: "about:buildconfig", + contentScript: + "self.port.on('event', function() self.port.emit('event-back'));" + }); + + loader.unload(); + + panel.port.on("event-back", function () { + test.fail("Panel should have been destroyed on module unload"); + }); + panel.port.emit("event"); + test.pass("check automatic destroy"); +}; + +tests.testWaitForInitThenShowThenDestroy = makeEventOrderTest({ + test: function(test, expect, panel) { + expect('inited', function() { panel.show(); }). + then('show', function() { panel.destroy(); }). + then('hide', function() { test.done(); }); + } +}); + +tests.testShowThenWaitForInitThenDestroy = makeEventOrderTest({ + test: function(test, expect, panel) { + panel.show(); + expect('inited'). + then('show', function() { panel.destroy(); }). + then('hide', function() { test.done(); }); + } +}); + +tests.testShowThenHideThenDestroy = makeEventOrderTest({ + test: function(test, expect, panel) { + panel.show(); + expect('show', function() { panel.hide(); }). + then('hide', function() { panel.destroy(); test.done(); }); + } +}); + +tests.testContentURLOption = function(test) { + const URL_STRING = "about:buildconfig"; + const HTML_CONTENT = "Test

This is a test.

"; + + let (panel = Panel({ contentURL: URL_STRING })) { + test.pass("contentURL accepts a string URL."); + test.assertEqual(panel.contentURL, URL_STRING, + "contentURL is the string to which it was set."); + } + + let dataURL = "data:text/html," + encodeURIComponent(HTML_CONTENT); + let (panel = Panel({ contentURL: dataURL })) { + test.pass("contentURL accepts a data: URL."); + } + + let (panel = Panel({})) { + test.assert(panel.contentURL == null, + "contentURL is undefined."); + } + + test.assertRaises(function () Panel({ contentURL: "foo" }), + "The `contentURL` option must be a valid URL.", + "Panel throws an exception if contentURL is not a URL."); +}; + +let panelSupported = true; + +try { + panels = require("panel"); + Panel = panels.Panel; +} +catch(ex if ex.message == [ + "The panel module currently supports only Firefox. In the future ", + "we would like it to support other applications, however. Please see ", + "https://bugzilla.mozilla.org/show_bug.cgi?id=jetpack-panel-apps ", + "for more information." + ].join("")) { + panelSupported = false; +} + +if (panelSupported) { + for (let test in tests) + exports[test] = tests[test]; +} +else { + exports.testPanelNotSupported = function(test) { + test.pass("The panel module is not supported on this app."); + } +} diff --git a/tools/addon-sdk-1.5/packages/addon-kit/tests/test-passwords.js b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-passwords.js new file mode 100644 index 0000000..bfb137a --- /dev/null +++ b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-passwords.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 { store, search, remove } = require("passwords"); + +exports["test store requires `password` field"] = function(assert, done) { + store({ + username: "foo", + realm: "bar", + onComplete: function onComplete() { + assert.fail("onComplete should not be called"); + }, + onError: function onError() { + assert.pass("'`password` is required"); + done(); + } + }); +}; + +exports["test store requires `username` field"] = function(assert, done) { + store({ + password: "foo", + realm: "bar", + onComplete: function onComplete() { + assert.fail("onComplete should not be called"); + }, + onError: function onError() { + assert.pass("'`username` is required"); + done(); + } + }); +}; + +exports["test onComplete is optional"] = function(assert, done) { + store({ + realm: "bla", + username: "bla", + password: "bla", + onError: function onError() { + assert.fail("onError was called"); + } + }); + assert.pass("exception is not thrown if `onComplete is missing") + done(); +}; + +exports["test exceptions in onComplete are reported"] = function(assert, done) { + store({ + realm: "throws", + username: "error", + password: "boom!", + onComplete: function onComplete(error) { + throw new Error("Boom!") + }, + onError: function onError(error) { + assert.equal(error.message, "Boom!", "Error thrown is reported"); + done(); + } + }); +}; + +exports["test store requires `realm` field"] = function(assert, done) { + store({ + username: "foo", + password: "bar", + onComplete: function onComplete() { + assert.fail("onComplete should not be called"); + }, + onError: function onError() { + assert.pass("'`realm` is required"); + done(); + } + }); +}; + +exports["test can't store same login twice"] = function(assert, done) { + store({ + username: "user", + password: "pass", + realm: "realm", + onComplete: function onComplete() { + assert.pass("credential saved"); + + store({ + username: "user", + password: "pass", + realm: "realm", + onComplete: function onComplete() { + assert.fail("onComplete should not be called"); + }, + onError: function onError() { + assert.pass("re-saving credential failed"); + + remove({ + username: "user", + password: "pass", + realm: "realm", + onComplete: function onComplete() { + assert.pass("credential was removed"); + done(); + }, + onError: function onError() { + assert.fail("remove should not fail"); + } + }); + } + }); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); +}; + +exports["test remove fails if no login found"] = function(assert, done) { + remove({ + username: "foo", + password: "bar", + realm: "baz", + onComplete: function onComplete() { + assert.fail("should not be able to remove unstored credentials"); + }, + onError: function onError() { + assert.pass("can't remove unstored credentials"); + done(); + } + }); +}; + +exports["test addon associated credentials"] = function(assert, done) { + store({ + username: "foo", + password: "bar", + realm: "baz", + onComplete: function onComplete() { + search({ + username: "foo", + password: "bar", + realm: "baz", + onComplete: function onComplete([credential]) { + assert.equal(credential.url.indexOf("addon:"), 0, + "`addon:` uri is used for add-on credentials"); + assert.equal(credential.username, "foo", + "username matches"); + assert.equal(credential.password, "bar", + "password matches"); + assert.equal(credential.realm, "baz", "realm matches"); + assert.equal(credential.formSubmitURL, null, + "`formSubmitURL` is `null` for add-on credentials"); + assert.equal(credential.usernameField, "", "usernameField is empty"); + assert.equal(credential.passwordField, "", "passwordField is empty"); + + remove({ + username: credential.username, + password: credential.password, + realm: credential.realm, + onComplete: function onComplete() { + assert.pass("credential is removed"); + done(); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); +}; + +exports["test web page associated credentials"] = function(assert, done) { + store({ + url: "http://bar.foo.com/authentication/?login", + formSubmitURL: "http://login.foo.com/authenticate.cgi", + username: "user", + password: "pass", + usernameField: "user-f", + passwordField: "pass-f", + onComplete: function onComplete() { + search({ + username: "user", + password: "pass", + url: "http://bar.foo.com", + formSubmitURL: "http://login.foo.com", + onComplete: function onComplete([credential]) { + assert.equal(credential.url, "http://bar.foo.com", "url matches"); + assert.equal(credential.username, "user", "username matches"); + assert.equal(credential.password, "pass", "password matches"); + assert.equal(credential.realm, null, "realm is null"); + assert.equal(credential.formSubmitURL, "http://login.foo.com", + "formSubmitURL matches"); + assert.equal(credential.usernameField, "user-f", + "usernameField is matches"); + assert.equal(credential.passwordField, "pass-f", + "passwordField matches"); + + remove({ + url: credential.url, + formSubmitURL: credential.formSubmitURL, + username: credential.username, + password: credential.password, + usernameField: credential.usernameField, + passwordField: credential.passwordField, + + onComplete: function onComplete() { + assert.pass("credential is removed"); + done(); + }, + onError: function onError(e) { + assert.fail("onError should not be called"); + } + }); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); +}; + +exports["test site authentication credentials"] = function(assert, done) { + store({ + url: "http://authentication.com", + username: "U", + password: "P", + realm: "R", + onComplete: function onComplete() { + search({ + url: "http://authentication.com", + username: "U", + password: "P", + realm: "R", + onComplete: function onComplete([credential]) { + assert.equal(credential.url,"http://authentication.com", + "url matches"); + assert.equal(credential.username, "U", "username matches"); + assert.equal(credential.password, "P", "password matches"); + assert.equal(credential.realm, "R", "realm matches"); + assert.equal(credential.formSubmitURL, null, "formSubmitURL is null"); + assert.equal(credential.usernameField, "", "usernameField is empty"); + assert.equal(credential.passwordField, "", "passwordField is empty"); + + remove({ + url: credential.url, + username: credential.username, + password: credential.password, + realm: credential.realm, + onComplete: function onComplete() { + assert.pass("credential is removed"); + done(); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); + }, + onError: function onError() { + assert.fail("onError should not be called"); + } + }); +}; + +require("test").run(exports); diff --git a/tools/addon-sdk-1.5/packages/addon-kit/tests/test-private-browsing.js b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-private-browsing.js new file mode 100644 index 0000000..1d60f6b --- /dev/null +++ b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-private-browsing.js @@ -0,0 +1,204 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +let pb = require("private-browsing"); +let {Cc,Ci} = require("chrome"); +const { Loader } = require('./helpers'); + +let pbService; +// Currently, only Firefox implements the private browsing service. +if (require("xul-app").is("Firefox")) { + pbService = Cc["@mozilla.org/privatebrowsing;1"]. + getService(Ci.nsIPrivateBrowsingService); +} + +if (pbService) { + + // tests that isActive has the same value as the private browsing service + // expects + exports.testGetIsActive = function (test) { + test.assertEqual(pb.isActive, false, + "private-browsing.isActive is correct without modifying PB service"); + + pbService.privateBrowsingEnabled = true; + test.assert(pb.isActive, + "private-browsing.isActive is correct after modifying PB service"); + + // Switch back to normal mode. + pbService.privateBrowsingEnabled = false; + }; + + // tests that activating does put the browser into private browsing mode + exports.testActivateDeactivate = function (test) { + test.waitUntilDone(); + pb.once("start", function onStart() { + test.assertEqual(pbService.privateBrowsingEnabled, true, + "private browsing mode was activated"); + pb.deactivate(); + }); + pb.once("stop", function onStop() { + test.assertEqual(pbService.privateBrowsingEnabled, false, + "private browsing mode was deactivate"); + test.done(); + }); + pb.activate(); + }; + + exports.testStart = function(test) { + test.waitUntilDone(); + pb.on("start", function onStart() { + test.assertEqual(this, pb, "`this` should be private-browsing module"); + test.assert(pbService.privateBrowsingEnabled, + 'private mode is active when "start" event is emitted'); + test.assert(pb.isActive, + '`isActive` is `true` when "start" event is emitted'); + pb.removeListener("start", onStart); + test.done(); + }); + pb.activate(); + }; + + exports.testStop = function(test) { + test.waitUntilDone(); + pb.on("stop", function onStop() { + test.assertEqual(this, pb, "`this` should be private-browsing module"); + test.assertEqual(pbService.privateBrowsingEnabled, false, + "private mode is disabled when stop event is emitted"); + test.assertEqual(pb.isActive, false, + "`isActive` is `false` when stop event is emitted"); + pb.removeListener("stop", onStop); + test.done(); + }); + pb.activate(); + pb.deactivate(); + }; + + exports.testAutomaticUnload = function(test) { + test.waitUntilDone(); + // Create another private browsing instance and unload it + let loader = Loader(module); + let pb2 = loader.require("private-browsing"); + let called = false; + pb2.on("start", function onStart() { + called = true; + test.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.activate(); + pb.once("start", function onStart() { + require("timer").setTimeout(function () { + test.assert(!called, + "First private browsing instance is destroyed and inactive"); + + // Must reset to normal mode, so that next test starts with it. + pb.deactivate(); + test.done(); + }, 0); + }); + }; + + exports.testBothListeners = function(test) { + test.waitUntilDone(); + let stop = false; + let start = false; + + function onStop() { + test.assertEqual(stop, false, + "stop callback must be called only once"); + test.assertEqual(pbService.privateBrowsingEnabled, false, + "private mode is disabled when stop event is emitted"); + test.assertEqual(pb.isActive, false, + "`isActive` 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() { + test.assertEqual(false, start, + "stop callback must be called only once"); + test.assert(pbService.privateBrowsingEnabled, + "private mode is active when start event is emitted"); + test.assert(pb.isActive, + "`isActive` is `true` when start event is emitted"); + + pb.on("stop", onStop); + pb.deactivate(); + start = true; + } + + function onStart2() { + test.assert(start, "start listener must be called already"); + test.assertEqual(false, stop, "stop callback must not be called yet"); + } + + function finish() { + test.assert(pbService.privateBrowsingEnabled, true, + "private mode is active when start event is emitted"); + test.assert(pb.isActive, + "`isActive` is `true` when start event is emitted"); + + pb.removeListener("start", finish); + pb.removeListener("stop", onStop); + + pb.deactivate(); + pb.once("stop", function () { + test.assertEqual(pbService.privateBrowsingEnabled, false); + test.assertEqual(pb.isActive, false); + + test.done(); + }); + } + + pb.on("start", onStart); + pb.on("start", onStart2); + pbService.privateBrowsingEnabled = true; + }; + + exports["test activate private mode via handler"] = function(test) { + const tabs = require("tabs"); + + test.waitUntilDone(); + 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() { + test.done(); + }); + } + } + + tabs.on("ready", onReady); + pb.once("start", function onStart() { + test.pass("private mode was activated"); + pb.deactivate(); + }); + pb.once("stop", function onStop() { + test.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:"); + }; +} +else { + // tests for the case where private browsing doesn't exist + exports.testNoImpl = function (test) { + test.assertEqual(pb.isActive, false, + "pb.isActive returns false when private browsing isn't supported"); + }; +} diff --git a/tools/addon-sdk-1.5/packages/addon-kit/tests/test-request.js b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-request.js new file mode 100644 index 0000000..770dbfb --- /dev/null +++ b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-request.js @@ -0,0 +1,340 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 { Request } = require("addon-kit/request"); +const { pathFor } = require("api-utils/system"); +const { startServerAsync } = require("api-utils/httpd"); +const file = require("api-utils/file"); + +const basePath = pathFor("TmpD") +const port = 8080; + + +exports.testOptionsValidator = function(test) { + // First, a simple test to make sure we didn't break normal functionality. + test.assertRaises(function () { + Request({ + url: null + }); + }, 'The option "url" must be one of the following types: string'); + + // Next we'll have a Request that doesn't throw from c'tor, but from a setter. + let req = Request({ + url: "http://playground.zpao.com/jetpack/request/text.php", + onComplete: function () {} + }); + test.assertRaises(function () { + req.url = null; + }, 'The option "url" must be one of the following types: string'); + // The url shouldn't have changed, so check that + test.assertEqual(req.url, "http://playground.zpao.com/jetpack/request/text.php"); +} + +exports.testContentValidator = function(test) { + test.waitUntilDone(); + Request({ + url: "data:text/html,response", + content: { 'key1' : null, 'key2' : 'some value' }, + onComplete: function(response) { + test.assertEqual(response.text, "response?key1=null&key2=some+value"); + test.done(); + } + }).get(); +}; + +// All tests below here require a network connection. They will be commented out +// when checked in. If you'd like to run them, simply uncomment them. +// +// When we have the means, these tests will be converted so that they don't +// require an external server nor a network connection. + +// This is a request to a file that exists. +exports.testStatus200 = function (test) { + let srv = startServerAsync(port, basePath); + let content = "Look ma, no hands!\n"; + let basename = "test-request.txt" + prepareFile(basename, content); + + test.waitUntilDone(); + var req = Request({ + url: "http://localhost:" + port + "/" + basename, + onComplete: function (response) { + test.assertEqual(this, req, "`this` should be request"); + test.assertEqual(response.status, 200); + test.assertEqual(response.statusText, "OK"); + test.assertEqual(response.headers["Content-Type"], "text/plain"); + test.assertEqual(response.text, content); + srv.stop(function() test.done()); + } + }).get(); +} + +// This tries to get a file that doesn't exist +exports.testStatus404 = function (test) { + var srv = startServerAsync(port, basePath); + + test.waitUntilDone(); + Request({ + // the following URL doesn't exist + url: "http://localhost:" + port + "/test-request-404.txt", + onComplete: function (response) { + test.assertEqual(response.status, 404); + test.assertEqual(response.statusText, "Not Found"); + srv.stop(function() test.done()); + } + }).get(); +} + +/* +// a simple file with a known header +exports.testKnownHeader = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/headers.php", + onComplete: function (response) { + test.assertEqual(response.headers["x-zpao-header"], "Jamba Juice"); + test.done(); + } + }).get(); +} + +// complex headers +exports.testKnownHeader = function (test) { + let headers = { + "x-zpao-header": "Jamba Juice is: delicious", + "x-zpao-header-2": "foo, bar", + "Set-Cookie": "foo=bar\nbaz=foo" + } + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/complex_headers.php", + onComplete: function (response) { + for (k in headers) { + test.assertEqual(response.headers[k], headers[k]); + } + test.done(); + } + }).get(); +} +*/ + +exports.testSimpleJSON = function (test) { + let srv = startServerAsync(port, basePath); + let json = { foo: "bar" }; + let basename = "test-request.json"; + prepareFile(basename, JSON.stringify(json)); + + test.waitUntilDone(); + Request({ + url: "http://localhost:" + port + "/" + basename, + onComplete: function (response) { + assertDeepEqual(test, response.json, json); + srv.stop(function() test.done()); + } + }).get(); +} + +exports.testInvalidJSON = function (test) { + let srv = startServerAsync(port, basePath); + let basename = "test-request-invalid.json"; + prepareFile(basename, '"this": "isn\'t JSON"'); + + test.waitUntilDone(); + Request({ + url: "http://localhost:" + port + "/" + basename, + onComplete: function (response) { + test.assertEqual(response.json, null); + srv.stop(function() test.done()); + } + }).get(); +} + +/* +exports.testGetWithParamsNotContent = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php?foo=bar", + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : { foo: "bar" } + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} + +exports.testGetWithContent = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php", + content: { foo: "bar" }, + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : { foo: "bar" } + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} + +exports.testGetWithParamsAndContent = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php?foo=bar", + content: { baz: "foo" }, + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : { foo: "bar", baz: "foo" } + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} + +exports.testSimplePost = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php", + content: { foo: "bar" }, + onComplete: function (response) { + let expected = { + "POST": { foo: "bar" }, + "GET" : [] + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).post(); +} + +exports.testEncodedContent = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php", + content: "foo=bar&baz=foo", + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : { foo: "bar", baz: "foo" } + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} + +exports.testEncodedContentWithSpaces = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php", + content: "foo=bar+hop!&baz=foo", + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : { foo: "bar hop!", baz: "foo" } + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} + +exports.testGetWithArray = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php", + content: { foo: [1, 2], baz: "foo" }, + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : { foo: [1, 2], baz: "foo" } + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} + +exports.testGetWithNestedArray = function (test) { + test.waitUntilDone(); + Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php", + content: { foo: [1, 2, [3, 4]], bar: "baz" }, + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : this.content + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} + +exports.testGetWithNestedArray = function (test) { + test.waitUntilDone(); + let request = Request({ + url: "http://playground.zpao.com/jetpack/request/getpost.php", + content: { + foo: [1, 2, { + omg: "bbq", + "all your base!": "are belong to us" + }], + bar: "baz" + }, + onComplete: function (response) { + let expected = { + "POST": [], + "GET" : request.content + }; + assertDeepEqual(test, response.json, expected); + test.done(); + } + }).get(); +} +*/ + +// This is not a proper testing for deep equal, but it's good enough for my uses +// here. It will do type coercion to check equality, but that's good here. Data +// coming from the server will be stringified and so "0" should be equal to 0. +function assertDeepEqual(test, obj1, obj2, msg) { + function equal(o1, o2) { + // cover our non-object cases well enough + if (o1 == o2) + return true; + if (typeof(o1) != typeof(o2)) + return false; + if (typeof(o1) != "object") + return o1 == o2; + + let e = true; + for (let [key, val] in Iterator(o1)) { + e = e && key in o2 && equal(o2[key], val); + if (!e) + break; + } + for (let [key, val] in Iterator(o2)) { + e = e && key in o1 && equal(o1[key], val); + if (!e) + break; + } + return e; + } + msg = msg || "objects not equal - " + JSON.stringify(obj1) + " != " + + JSON.stringify(obj2); + test.assert(equal(obj1, obj2), msg); +} + +function prepareFile(basename, content) { + let filePath = file.join(basePath, basename); + let fileStream = file.open(filePath, 'w'); + fileStream.write(content); + fileStream.close(); +} diff --git a/tools/addon-sdk-1.5/packages/addon-kit/tests/test-selection.js b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-selection.js new file mode 100644 index 0000000..06feb7e --- /dev/null +++ b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-selection.js @@ -0,0 +1,458 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +let timer = require("timer"); +let {Cc,Ci} = require("chrome"); + +// Arbitrary delay needed to avoid weird behavior. +// TODO: We need to find all uses of this and replace them +// with more deterministic solutions. +const ARB_DELAY = 100; + +// Select all divs elements in an HTML document +function selectAllDivs(window) { + let divs = window.document.getElementsByTagName("div"); + let s = window.getSelection(); + if (s.rangeCount > 0) + s.removeAllRanges(); + for (let i = 0; i < divs.length; i++) { + let range = window.document.createRange(); + range.selectNode(divs[i]); + s.addRange(range); + } +} + +function selectTextarea(window, from, to) { + let textarea = window.document.getElementsByTagName("textarea")[0]; + + from = from || 0; + to = to || textarea.value.length; + + textarea.setSelectionRange(from, to); + textarea.focus(); +} + +function primeTestCase(html, test, callback) { + let tabBrowser = require("tab-browser"); + let dataURL = "data:text/html," + encodeURI(html); + let tracker = tabBrowser.whenContentLoaded( + function(window) { + if (window.document.location.href != dataURL) + return; + callback(window, test); + timer.setTimeout(function() { + tracker.unload(); + test.done(); + window.close(); + }, + ARB_DELAY); + } + ); + tabBrowser.addTab(dataURL); +} + +const DIV1 = '
bar
'; +const DIV2 = '
noodles
'; +const HTML_MULTIPLE = '' + DIV1 + DIV2 + ''; +const HTML_SINGLE = '' + DIV1 + ''; + +// Tests of contiguous + +exports.testContiguousMultiple = function testContiguousMultiple(test) { + let selection = require("selection"); + primeTestCase(HTML_MULTIPLE, test, function(window, test) { + selectAllDivs(window); + test.assertEqual(selection.isContiguous, false, + "selection.isContiguous multiple works."); + }); + + test.waitUntilDone(5000); +}; + +exports.testContiguousSingle = function testContiguousSingle(test) { + let selection = require("selection"); + primeTestCase(HTML_SINGLE, test, function(window, test) { + selectAllDivs(window); + test.assertEqual(selection.isContiguous, true, + "selection.isContiguous single works."); + }); + + test.waitUntilDone(5000); +}; + +exports.testContiguousWithoutSelection = + function testContiguousWithoutSelection(test) { + let selection = require("selection"); + primeTestCase(HTML_SINGLE, test, function(window, test) { + test.assertEqual(selection.isContiguous, false, + "selection.isContiguous without selection works."); + }); + + test.waitUntilDone(5000); +}; + +/** + * Test that setting the contiguous property has no effect. + */ +/*exports.testSetContiguous = function testSetContiguous(test) { + let selection = require("selection"); + primeTestCase(HTML_MULTIPLE, test, function(window, test) { + selectAllDivs(window); + try { + selection.isContiguous = true; + test.assertEqual(selection.isContiguous, false, + "setting selection.isContiguous doesn't work (as expected)."); + } + catch (e) { + test.pass("setting selection.isContiguous doesn't work (as expected)."); + } + }); + + test.waitUntilDone(5000); +};*/ + + +// HTML tests + +exports.testGetHTMLSingleSelection = function testGetHTMLSingleSelection(test) { + let selection = require("selection"); + primeTestCase(HTML_SINGLE, test, function(window, test) { + selectAllDivs(window); + test.assertEqual(selection.html, DIV1, "get html selection works"); + }); + + test.waitUntilDone(5000); +}; + +/* Myk's comments: This is fine. However, it reminds me to figure out and + specify whether iteration is ordered. If so, we'll want to change this + test in the future to test that the discontiguous selections are returned in + the appropriate order. In the meantime, add a comment to that effect here */ +exports.testGetHTMLMultipleSelection = + function testGetHTMLMultipleSelection(test) { + let selection = require("selection"); + primeTestCase(HTML_MULTIPLE, test, function(window, test) { + selectAllDivs(window); + let assertions = false; + for each (let i in selection) { + test.assertEqual(true, [DIV1, DIV2].some(function(t) t == i.html), + "get multiple selection html works"); + assertions = true; + } + // Ensure we ran at least one assertEqual() + test.assert(assertions, "No assertions were called"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetHTMLNull = function testGetHTMLNull(test) { + let selection = require("selection"); + primeTestCase(HTML_SINGLE, test, function(window, test) { + test.assertEqual(selection.html, null, "get html null works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetHTMLWeird = function testGetHTMLWeird(test) { + let selection = require("selection"); + // If the getter is used when there are contiguous selections, the first + // selection should be returned + primeTestCase(HTML_MULTIPLE, test, function(window, test) { + selectAllDivs(window); + test.assertEqual(selection.html, DIV1, "get html weird works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetHTMLNullInTextareaSelection = + function testGetHTMLNullInTextareaSelection(test) { + let selection = require("selection"); + + primeTestCase(TEXT_FIELD, test, function(window, test) { + selectTextarea(window); + + test.assertEqual(selection.html, null, "get html null in textarea works") + }); + + test.waitUntilDone(5000); +}; + +const REPLACEMENT_HTML = "Lorem ipsum dolor sit amet"; + +exports.testSetHTMLSelection = function testSetHTMLSelection(test) { + let selection = require("selection"); + primeTestCase(HTML_SINGLE, test, function(window, test) { + selectAllDivs(window); + selection.html = REPLACEMENT_HTML; + test.assertEqual(selection.html, "" + REPLACEMENT_HTML + + "", "selection html works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testSetHTMLException = function testSetHTMLException(test) { + let selection = require("selection"); + primeTestCase(HTML_SINGLE, test, function(window, test) { + try { + selection.html = REPLACEMENT_HTML; + test.fail("set HTML throws when there's no selection."); + } + catch (e) { + test.pass("set HTML throws when there's no selection."); + } + }); + + test.waitUntilDone(5000); +}; + +const TEXT1 = "foo"; +const TEXT2 = "noodles"; +const TEXT_MULTIPLE = "
" + TEXT1 + "
" + TEXT2 + + "
"; +const TEXT_SINGLE = "
" + TEXT1 + "
"; +const TEXT_FIELD = ""; + +// Text tests + +exports.testSetHTMLinTextareaSelection = + function testSetHTMLinTextareaSelection(test) { + let selection = require("selection"); + + primeTestCase(TEXT_FIELD, test, function(window, test) { + selectTextarea(window); + + // HTML string is set as plain text in textareas, that's because + // `selection.html` and `selection.text` are basically aliases when a + // value is set. See bug 677269 + selection.html = REPLACEMENT_HTML; + + test.assertEqual(selection.text, REPLACEMENT_HTML, + "set selection html in textarea works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetTextSingleSelection = + function testGetTextSingleSelection(test) { + let selection = require("selection"); + primeTestCase(TEXT_SINGLE, test, function(window, test) { + selectAllDivs(window); + test.assertEqual(selection.text, TEXT1, "get text selection works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetTextMultipleSelection = + function testGetTextMultipleSelection(test) { + let selection = require("selection"); + primeTestCase(TEXT_MULTIPLE, test, function(window, test) { + selectAllDivs(window); + let assertions = false; + for each (let i in selection) { + test.assertEqual(true, [TEXT1, TEXT2].some(function(t) t == i.text), + "get multiple selection text works"); + assertions = true; + } + // Ensure we ran at least one assertEqual() + test.assert(assertions, "No assertions were called"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetTextNull = function testGetTextNull(test) { + let selection = require("selection"); + primeTestCase(TEXT_SINGLE, test, function(window, test) { + test.assertEqual(selection.text, null, "get text null works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetTextWeird = function testGetTextWeird(test) { + let selection = require("selection"); + // If the getter is used when there are contiguous selections, the first + // selection should be returned + primeTestCase(TEXT_MULTIPLE, test, function(window, test) { + selectAllDivs(window); + test.assertEqual(selection.text, TEXT1, "get text weird works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testGetTextNullInTextareaSelection = + function testGetTextInTextareaSelection(test) { + let selection = require("selection"); + + primeTestCase(TEXT_FIELD, test, function(window, test) { + test.assertEqual(selection.text, null, "get text null in textarea works") + }); + + test.waitUntilDone(5000); +}; + +exports.testGetTextInTextareaSelection = + function testGetTextInTextareaSelection(test) { + let selection = require("selection"); + + primeTestCase(TEXT_FIELD, test, function(window, test) { + selectTextarea(window); + + test.assertEqual(selection.text, TEXT1, "get text null in textarea works") + }); + + test.waitUntilDone(5000); +}; + +const REPLACEMENT_TEXT = "Lorem ipsum dolor sit amet"; + +exports.testSetTextSelection = function testSetTextSelection(test) { + let selection = require("selection"); + primeTestCase(TEXT_SINGLE, test, function(window, test) { + selectAllDivs(window); + selection.text = REPLACEMENT_TEXT; + test.assertEqual(selection.text, REPLACEMENT_TEXT, "selection text works"); + }); + + test.waitUntilDone(5000); +}; + +exports.testSetHTMLException = function testSetHTMLException(test) { + let selection = require("selection"); + primeTestCase(TEXT_SINGLE, test, function(window, test) { + try { + selection.text = REPLACEMENT_TEXT; + test.fail("set HTML throws when there's no selection."); + } + catch (e) { + test.pass("set HTML throws when there's no selection."); + } + }); + + test.waitUntilDone(5000); +}; + +exports.testSetTextInTextareaSelection = + function testSetTextInTextareaSelection(test) { + let selection = require("selection"); + + primeTestCase(TEXT_FIELD, test, function(window, test) { + selectTextarea(window); + + selection.text = REPLACEMENT_TEXT; + + test.assertEqual(selection.text, REPLACEMENT_TEXT, + "set selection text in textarea works"); + }); + + test.waitUntilDone(5000); +}; + +// Iterator tests + +exports.testIterator = function testIterator(test) { + let selection = require("selection"); + let selectionCount = 0; + primeTestCase(TEXT_MULTIPLE, test, function(window, test) { + selectAllDivs(window); + for each (let i in selection) + selectionCount++; + test.assertEqual(2, selectionCount, "iterator works."); + }); + + test.waitUntilDone(5000); +}; + +exports.testIteratorWithTextareaSelection = + function testIteratorWithTextareaSelection(test) { + let selection = require("selection"); + let selectionCount = 0; + + primeTestCase(TEXT_FIELD, test, function(window, test) { + selectTextarea(window); + + for each (let i in selection) + selectionCount++; + + test.assertEqual(1, selectionCount, "iterator works in textarea."); + }); + + test.waitUntilDone(5000); +}; + +/* onSelect tests */ + +/* +function sendSelectionSetEvent(window) { + const Ci = Components.interfaces; + let utils = window.QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIDOMWindowUtils); + if (!utils.sendSelectionSetEvent(0, 1, false)) + dump("**** sendSelectionSetEvent did not select anything\n"); + else + dump("**** sendSelectionSetEvent succeeded\n"); +} + +// testOnSelect() requires nsIDOMWindowUtils, which is only available in +// Firefox 3.7+. +exports.testOnSelect = function testOnSelect(test) { + let selection = require("selection"); + let callbackCount = 0; + primeTestCase(TEXT_SINGLE, test, function(window, test) { + selection.onSelect = function() {callbackCount++}; + // Now simulate the user selecting stuff + sendSelectionSetEvent(window); + selection.text = REPLACEMENT_TEXT; + test.assertEqual(1, callbackCount, "onSelect text listener works."); + //test.pass(); + //test.done(); + }); + + test.waitUntilDone(5000); +}; + +// testOnSelectExceptionNoBubble() requires nsIDOMWindowUtils, which is only +// available in Firefox 3.7+. +exports.testOnSelectExceptionNoBubble = + function testOnSelectTextSelection(test) { + let selection = require("selection"); + primeTestCase(HTML_SINGLE, test, function(window, test) { + selection.onSelect = function() { + throw new Error("Exception thrown in testOnSelectExceptionNoBubble"); + }; + // Now simulate the user selecting stuff + sendSelectionSetEvent(window); + test.pass("onSelect catches exceptions."); + }); + + test.waitUntilDone(5000); +}; +*/ + +// If the module doesn't support the app we're being run in, require() will +// throw. In that case, remove all tests above from exports, and add one dummy +// test that passes. +try { + require("selection"); +} +catch (err) { + // This bug should be mentioned in the error message. + let bug = "https://bugzilla.mozilla.org/show_bug.cgi?id=560716"; + if (err.message.indexOf(bug) < 0) + throw err; + for (let [prop, val] in Iterator(exports)) { + if (/^test/.test(prop) && typeof(val) === "function") + delete exports[prop]; + } + exports.testAppNotSupported = function (test) { + test.pass("The selection module does not support this application."); + }; +} diff --git a/tools/addon-sdk-1.5/packages/addon-kit/tests/test-simple-prefs.js b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-simple-prefs.js new file mode 100644 index 0000000..40740f7 --- /dev/null +++ b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-simple-prefs.js @@ -0,0 +1,147 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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("./helpers"); +const setTimeout = require("timers").setTimeout; +const notify = require("observer-service").notify; +const { jetpackID } = require("@packaging"); + +exports.testSetGetBool = function(test) { + test.waitUntilDone(); + + let loader = Loader(module); + let sp = loader.require("simple-prefs").prefs; + + test.assertEqual(sp.test, undefined, "Value should not exist"); + sp.test = true; + test.assert(sp.test, "Value read should be the value previously set"); + + loader.unload(); + test.done(); +}; + +exports.testSetGetInt = function(test) { + test.waitUntilDone(); + + // Load the module once, set a value. + let loader = Loader(module); + let sp = loader.require("simple-prefs").prefs; + + test.assertEqual(sp["test-int"], undefined, "Value should not exist"); + sp["test-int"] = 1; + test.assertEqual(sp["test-int"], 1, "Value read should be the value previously set"); + + loader.unload(); + test.done(); +}; + +exports.testSetComplex = function(test) { + test.waitUntilDone(); + + let loader = Loader(module); + let sp = loader.require("simple-prefs").prefs; + + try { + sp["test-complex"] = {test: true}; + test.fail("Complex values are not allowed"); + } + catch (e) { + test.pass("Complex values are not allowed"); + } + + loader.unload(); + test.done(); +}; + +exports.testSetGetString = function(test) { + test.waitUntilDone(); + + let loader = Loader(module); + let sp = loader.require("simple-prefs").prefs; + + test.assertEqual(sp["test-string"], undefined, "Value should not exist"); + sp["test-string"] = "test"; + test.assertEqual(sp["test-string"], "test", "Value read should be the value previously set"); + + loader.unload(); + test.done(); +}; + +exports.testHasAndRemove = function(test) { + test.waitUntilDone(); + + let loader = Loader(module); + let sp = loader.require("simple-prefs").prefs; + + sp.test = true; + test.assert(("test" in sp), "Value exists"); + delete sp.test; + test.assertEqual(sp.test, undefined, "Value should be undefined"); + + loader.unload(); + test.done(); + +}; + +exports.testPrefListener = function(test) { + test.waitUntilDone(); + + let loader = Loader(module); + let sp = loader.require("simple-prefs"); + + let listener = function(prefName) { + test.assertEqual(prefName, "test-listen", "The prefs listener heard the right event"); + test.done(); + }; + + sp.on("test-listen", listener); + + sp.prefs["test-listen"] = true; + loader.unload(); +}; + +exports.testBtnListener = function(test) { + test.waitUntilDone(); + + let loader = Loader(module); + let sp = loader.require("simple-prefs"); + + sp.on("test-btn-listen", function() { + test.pass("Button press event was heard"); + test.done(); + }); + notify((jetpackID + "-cmdPressed"), "", "test-btn-listen"); + + loader.unload(); +}; + +exports.testPrefRemoveListener = function(test) { + test.waitUntilDone(); + + let loader = Loader(module); + let sp = loader.require("simple-prefs"); + let counter = 0; + + let listener = function() { + test.pass("The prefs listener was not removed yet"); + + if (++counter > 1) + test.fail("The prefs listener was not removed"); + + sp.removeListener("test-listen2", listener); + + sp.prefs["test-listen2"] = false; + + setTimeout(function() { + test.pass("The prefs listener was removed"); + loader.unload(); + test.done(); + }, 250); + }; + + sp.on("test-listen2", listener); + + sp.prefs["test-listen2"] = true; +}; diff --git a/tools/addon-sdk-1.5/packages/addon-kit/tests/test-simple-storage.js b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-simple-storage.js new file mode 100644 index 0000000..5212006 --- /dev/null +++ b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-simple-storage.js @@ -0,0 +1,312 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim:set ts=2 sw=2 sts=2 et filetype=javascript + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 file = require("file"); +const prefs = require("preferences-service"); + +const QUOTA_PREF = "extensions.addon-sdk.simple-storage.quota"; + +let {Cc,Ci} = require("chrome"); + +const { Loader } = require("./helpers"); +const options = require("@packaging"); + +let storeFile = Cc["@mozilla.org/file/directory_service;1"]. + getService(Ci.nsIProperties). + get("ProfD", Ci.nsIFile); +storeFile.append("jetpack"); +storeFile.append(options.jetpackID); +storeFile.append("simple-storage"); +storeFile.append("store.json"); +let storeFilename = storeFile.path; + +exports.testSetGet = function (test) { + test.waitUntilDone(); + + // Load the module once, set a value. + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function (storage) { + test.assert(file.exists(storeFilename), "Store file should exist"); + + // Load the module again and make sure the value stuck. + loader = newLoader(test); + ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function (storage) { + file.remove(storeFilename); + test.done(); + }; + test.assertEqual(ss.storage.foo, val, "Value should persist"); + loader.unload(); + }; + let val = "foo"; + ss.storage.foo = val; + test.assertEqual(ss.storage.foo, val, "Value read should be value set"); + loader.unload(); +}; + +exports.testSetGetRootArray = function (test) { + setGetRoot(test, [1, 2, 3], function (arr1, arr2) { + if (arr1.length !== arr2.length) + return false; + for (let i = 0; i < arr1.length; i++) { + if (arr1[i] !== arr2[i]) + return false; + } + return true; + }); +}; + +exports.testSetGetRootBool = function (test) { + setGetRoot(test, true); +}; + +exports.testSetGetRootFunction = function (test) { + setGetRootError(test, function () {}, + "Setting storage to a function should fail"); +}; + +exports.testSetGetRootNull = function (test) { + setGetRoot(test, null); +}; + +exports.testSetGetRootNumber = function (test) { + setGetRoot(test, 3.14); +}; + +exports.testSetGetRootObject = function (test) { + setGetRoot(test, { foo: 1, bar: 2 }, function (obj1, obj2) { + for (let [prop, val] in Iterator(obj1)) { + if (!(prop in obj2) || obj2[prop] !== val) + return false; + } + for (let [prop, val] in Iterator(obj2)) { + if (!(prop in obj1) || obj1[prop] !== val) + return false; + } + return true; + }); +}; + +exports.testSetGetRootString = function (test) { + setGetRoot(test, "sho' 'nuff"); +}; + +exports.testSetGetRootUndefined = function (test) { + setGetRootError(test, undefined, "Setting storage to undefined should fail"); +}; + +exports.testEmpty = function (test) { + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + loader.unload(); + test.assert(!file.exists(storeFilename), "Store file should not exist"); +}; + +exports.testMalformed = function (test) { + let stream = file.open(storeFilename, "w"); + stream.write("i'm not json"); + stream.close(); + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + let empty = true; + for (let key in ss.storage) { + empty = false; + break; + } + test.assert(empty, "Malformed storage should cause root to be empty"); + loader.unload(); +}; + +// Go over quota and handle it by listener. +exports.testQuotaExceededHandle = function (test) { + test.waitUntilDone(); + prefs.set(QUOTA_PREF, 18); + + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + ss.on("OverQuota", function () { + test.pass("OverQuota was emitted as expected"); + test.assertEqual(this, ss, "`this` should be simple storage"); + ss.storage = { x: 4, y: 5 }; + + manager(loader).jsonStore.onWrite = function () { + loader = newLoader(test); + ss = loader.require("simple-storage"); + let numProps = 0; + for (let prop in ss.storage) + numProps++; + test.assert(numProps, 2, + "Store should contain 2 values: " + ss.storage.toSource()); + test.assertEqual(ss.storage.x, 4, "x value should be correct"); + test.assertEqual(ss.storage.y, 5, "y value should be correct"); + manager(loader).jsonStore.onWrite = function (storage) { + prefs.reset(QUOTA_PREF); + test.done(); + }; + loader.unload(); + }; + loader.unload(); + }); + // This will be JSON.stringify()ed to: {"a":1,"b":2,"c":3} (19 bytes) + ss.storage = { a: 1, b: 2, c: 3 }; + manager(loader).jsonStore.write(); +}; + +// Go over quota but don't handle it. The last good state should still persist. +exports.testQuotaExceededNoHandle = function (test) { + test.waitUntilDone(); + prefs.set(QUOTA_PREF, 5); + + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + + manager(loader).jsonStore.onWrite = function (storage) { + loader = newLoader(test); + ss = loader.require("simple-storage"); + test.assertEqual(ss.storage, val, + "Value should have persisted: " + ss.storage); + ss.storage = "some very long string that is very long"; + ss.on("OverQuota", function () { + test.pass("OverQuota emitted as expected"); + manager(loader).jsonStore.onWrite = function () { + test.fail("Over-quota value should not have been written"); + }; + loader.unload(); + + loader = newLoader(test); + ss = loader.require("simple-storage"); + test.assertEqual(ss.storage, val, + "Over-quota value should not have been written, " + + "old value should have persisted: " + ss.storage); + loader.unload(); + prefs.reset(QUOTA_PREF); + test.done(); + }); + manager(loader).jsonStore.write(); + }; + + let val = "foo"; + ss.storage = val; + loader.unload(); +}; + +exports.testQuotaUsage = function (test) { + test.waitUntilDone(); + + let quota = 21; + prefs.set(QUOTA_PREF, quota); + + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + + // {"a":1} (7 bytes) + ss.storage = { a: 1 }; + test.assertEqual(ss.quotaUsage, 7 / quota, "quotaUsage should be correct"); + + // {"a":1,"bb":2} (14 bytes) + ss.storage = { a: 1, bb: 2 }; + test.assertEqual(ss.quotaUsage, 14 / quota, "quotaUsage should be correct"); + + // {"a":1,"bb":2,"cc":3} (21 bytes) + ss.storage = { a: 1, bb: 2, cc: 3 }; + test.assertEqual(ss.quotaUsage, 21 / quota, "quotaUsage should be correct"); + + manager(loader).jsonStore.onWrite = function () { + prefs.reset(QUOTA_PREF); + test.done(); + }; + loader.unload(); +}; + +exports.testUninstall = function (test) { + test.waitUntilDone(); + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function () { + test.assert(file.exists(storeFilename), "Store file should exist"); + + loader = newLoader(test); + ss = loader.require("simple-storage"); + loader.unload("uninstall"); + test.assert(!file.exists(storeFilename), "Store file should be removed"); + test.done(); + }; + ss.storage.foo = "foo"; + loader.unload(); +}; + +exports.testSetNoSetRead = function (test) { + test.waitUntilDone(); + + // Load the module, set a value. + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function (storage) { + test.assert(file.exists(storeFilename), "Store file should exist"); + + // Load the module again but don't access ss.storage. + loader = newLoader(test); + ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function (storage) { + test.fail("Nothing should be written since `storage` was not accessed."); + }; + loader.unload(); + + // Load the module a third time and make sure the value stuck. + loader = newLoader(test); + ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function (storage) { + file.remove(storeFilename); + test.done(); + }; + test.assertEqual(ss.storage.foo, val, "Value should persist"); + loader.unload(); + }; + let val = "foo"; + ss.storage.foo = val; + test.assertEqual(ss.storage.foo, val, "Value read should be value set"); + loader.unload(); +}; + +function manager(loader) loader.sandbox("simple-storage").manager; + +function newLoader() Loader(module); + +function setGetRoot(test, val, compare) { + test.waitUntilDone(); + + compare = compare || function (a, b) a === b; + + // Load the module once, set a value. + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function () { + test.assert(file.exists(storeFilename), "Store file should exist"); + + // Load the module again and make sure the value stuck. + loader = newLoader(test); + ss = loader.require("simple-storage"); + manager(loader).jsonStore.onWrite = function () { + file.remove(storeFilename); + test.done(); + }; + test.assert(compare(ss.storage, val), "Value should persist"); + loader.unload(); + }; + ss.storage = val; + test.assert(compare(ss.storage, val), "Value read should be value set"); + loader.unload(); +} + +function setGetRootError(test, val, msg) { + let pred = "storage must be one of the following types: " + + "array, boolean, null, number, object, string"; + let loader = newLoader(test); + let ss = loader.require("simple-storage"); + test.assertRaises(function () ss.storage = val, pred, msg); + loader.unload(); +} diff --git a/tools/addon-sdk-1.5/packages/addon-kit/tests/test-tabs.js b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-tabs.js new file mode 100644 index 0000000..22f0e82 --- /dev/null +++ b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-tabs.js @@ -0,0 +1,873 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 {Cc,Ci} = require("chrome"); +const { Loader } = require("./helpers"); + +// test tab.activeTab getter +exports.testActiveTab_getter = function(test) { + test.waitUntilDone(); + + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + + let url = "data:text/html,foo"; + require("tab-browser").addTab( + url, + { + onLoad: function(e) { + test.assert(tabs.activeTab); + test.assertEqual(tabs.activeTab.url, url); + test.assertEqual(tabs.activeTab.title, "foo"); + closeBrowserWindow(window, function() test.done()); + } + } + ); + }); +}; + +// test 'BrowserWindow' instance creation on tab 'activate' event +// See bug 648244: there was a infinite loop. +exports.testBrowserWindowCreationOnActivate = function(test) { + test.waitUntilDone(); + + let windows = require("windows").browserWindows; + let tabs = require("tabs"); + + let gotActivate = false; + + tabs.once('activate', function onActivate(eventTab) { + test.assert(windows.activeWindow, "Is able to fetch activeWindow"); + gotActivate = true; + }); + + openBrowserWindow(function(window, browser) { + test.assert(gotActivate, "Received activate event before openBrowserWindow's callback is called"); + closeBrowserWindow(window, function () test.done()); + }); +} + +// test tab.activeTab setter +exports.testActiveTab_setter = function(test) { + test.waitUntilDone(); + + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url = "data:text/html,foo"; + + tabs.on('ready', function onReady(tab) { + tabs.removeListener('ready', onReady); + test.assertEqual(tabs.activeTab.url, "about:blank", "activeTab url has not changed"); + test.assertEqual(tab.url, url, "url of new background tab matches"); + tabs.on('activate', function onActivate(eventTab) { + tabs.removeListener('activate', onActivate); + test.assertEqual(tabs.activeTab.url, url, "url after activeTab setter matches"); + test.assertEqual(eventTab, tab, "event argument is the activated tab"); + test.assertEqual(eventTab, tabs.activeTab, "the tab is the active one"); + closeBrowserWindow(window, function() test.done()); + }); + tab.activate(); + }) + + tabs.open({ + url: url, + inBackground: true + }); + }); +}; + +exports.testAutomaticDestroy = function(test) { + test.waitUntilDone(); + + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + + // Create a second tab instance that we will destroy + let called = false; + + let loader = Loader(module); + let tabs2 = loader.require("tabs"); + tabs2.on('open', function onOpen(tab) { + called = true; + }); + + loader.unload(); + + // Fire a tab event an ensure that this destroyed tab is inactive + tabs.once('open', function () { + require("timer").setTimeout(function () { + test.assert(!called, "Unloaded tab module is destroyed and inactive"); + closeBrowserWindow(window, function() test.done()); + }, 0); + }); + + tabs.open("data:text/html,foo"); + + }); +}; + +// test tab properties +exports.testTabProperties = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs= require("tabs"); + let url = "data:text/html,foofoo"; + tabs.open({ + url: url, + onReady: function(tab) { + test.assertEqual(tab.title, "foo", "title of the new tab matches"); + test.assertEqual(tab.url, url, "URL of the new tab matches"); + test.assert(tab.favicon, "favicon of the new tab is not empty"); + test.assertEqual(tab.style, null, "style of the new tab matches"); + test.assertEqual(tab.index, 1, "index of the new tab matches"); + test.assertNotEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); + closeBrowserWindow(window, function() test.done()); + } + }); + }); +}; + +// test tabs iterator and length property +exports.testTabsIteratorAndLength = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let startCount = 0; + for each (let t in tabs) startCount++; + test.assertEqual(startCount, tabs.length, "length property is correct"); + let url = "data:text/html,default"; + tabs.open(url); + tabs.open(url); + tabs.open({ + url: url, + onOpen: function(tab) { + let count = 0; + for each (let t in tabs) count++; + test.assertEqual(count, startCount + 3, "iterated tab count matches"); + test.assertEqual(startCount + 3, tabs.length, "iterated tab count matches length property"); + closeBrowserWindow(window, function() test.done()); + } + }); + }); +}; + +// test tab.url setter +exports.testTabLocation = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url1 = "data:text/html,foo"; + let url2 = "data:text/html,bar"; + + tabs.on('ready', function onReady(tab) { + if (tab.url != url2) + return; + tabs.removeListener('ready', onReady); + test.pass("tab.load() loaded the correct url"); + closeBrowserWindow(window, function() test.done()); + }); + + tabs.open({ + url: url1, + onOpen: function(tab) { + tab.url = url2 + } + }); + }); +}; + +// test tab.close() +exports.testTabClose = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url = "data:text/html,foo"; + + test.assertNotEqual(tabs.activeTab.url, url, "tab is now the active tab"); + tabs.on('ready', function onReady(tab) { + tabs.removeListener('ready', onReady); + test.assertEqual(tabs.activeTab.url, tab.url, "tab is now the active tab"); + tab.close(function() { + closeBrowserWindow(window, function() test.done()); + }); + test.assertNotEqual(tabs.activeTab.url, url, "tab is no longer the active tab"); + }); + + tabs.open(url); + }); +}; + +// test tab.reload() +exports.testTabReload = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url = "data:text/html,"; + + tabs.open({ url: url, onReady: function onReady(tab) { + tab.removeListener("ready", onReady); + + browser.addEventListener( + "load", + function onLoad() { + browser.removeEventListener("load", onLoad, true); + + browser.addEventListener( + "load", + function onReload() { + browser.removeEventListener("load", onReload, true); + test.pass("the tab was loaded again"); + test.assertEqual(tab.url, url, "the tab has the same URL"); + closeBrowserWindow(window, function() test.done()); + }, + true + ); + tab.reload(); + }, + true + ); + }}); + }); +}; + +// test tab.move() +exports.testTabMove = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url = "data:text/html,foo"; + + tabs.open({ + url: url, + onOpen: function(tab) { + test.assertEqual(tab.index, 1, "tab index before move matches"); + tab.index = 0; + test.assertEqual(tab.index, 0, "tab index after move matches"); + closeBrowserWindow(window, function() test.done()); + } + }); + }); +}; + +// open tab with default options +exports.testOpen = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url = "data:text/html,default"; + tabs.open({ + url: url, + onReady: function(tab) { + test.assertEqual(tab.url, url, "URL of the new tab matches"); + test.assertEqual(window.content.location, url, "URL of active tab in the current window matches"); + closeBrowserWindow(window, function() test.done()); + } + }); + }); +}; + +// open pinned tab +exports.testOpenPinned = function(test) { + const xulApp = require("xul-app"); + if (xulApp.versionInRange(xulApp.platformVersion, "2.0b2", "*")) { + // test tab pinning + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url = "data:text/html,default"; + tabs.open({ + url: url, + isPinned: true, + onOpen: function(tab) { + test.assertEqual(tab.isPinned, true, "The new tab is pinned"); + closeBrowserWindow(window, function() test.done()); + } + }); + }); + } + else { + test.pass("Pinned tabs are not supported in this application."); + } +}; + +// pin/unpin opened tab +exports.testPinUnpin = function(test) { + const xulApp = require("xul-app"); + if (xulApp.versionInRange(xulApp.platformVersion, "2.0b2", "*")) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let url = "data:text/html,default"; + tabs.open({ + url: url, + onOpen: function(tab) { + tab.pin(); + test.assertEqual(tab.isPinned, true, "The tab was pinned correctly"); + tab.unpin(); + test.assertEqual(tab.isPinned, false, "The tab was unpinned correctly"); + closeBrowserWindow(window, function() test.done()); + } + }); + }); + } + else { + test.pass("Pinned tabs are not supported in this application."); + } +}; + +// open tab in background +exports.testInBackground = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let activeUrl = tabs.activeTab.url; + let url = "data:text/html,background"; + test.assertEqual(activeWindow, window, "activeWindow matches this window"); + tabs.on('ready', function onReady(tab) { + tabs.removeListener('ready', onReady); + test.assertEqual(tabs.activeTab.url, activeUrl, "URL of active tab has not changed"); + test.assertEqual(tab.url, url, "URL of the new background tab matches"); + test.assertEqual(activeWindow, window, "a new window was not opened"); + test.assertNotEqual(tabs.activeTab.url, url, "URL of active tab is not the new URL"); + closeBrowserWindow(window, function() test.done()); + }); + tabs.open({ + url: url, + inBackground: true + }); + }); +}; + +// open tab in new window +exports.testOpenInNewWindow = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + + let cache = []; + let windowUtils = require("window-utils"); + let wt = new windowUtils.WindowTracker({ + onTrack: function(win) { + cache.push(win); + }, + onUntrack: function(win) { + cache.splice(cache.indexOf(win), 1) + } + }); + let startWindowCount = cache.length; + + let url = "data:text/html,newwindow"; + tabs.open({ + url: url, + inNewWindow: true, + onReady: function(tab) { + let newWindow = cache[cache.length - 1]; + test.assertEqual(cache.length, startWindowCount + 1, "a new window was opened"); + test.assertEqual(activeWindow, newWindow, "new window is active"); + test.assertEqual(tab.url, url, "URL of the new tab matches"); + test.assertEqual(newWindow.content.location, url, "URL of new tab in new window matches"); + test.assertEqual(tabs.activeTab.url, url, "URL of activeTab matches"); + for (var i in cache) cache[i] = null; + wt.unload(); + closeBrowserWindow(newWindow, function() { + closeBrowserWindow(window, function() test.done()); + }); + } + }); + }); +}; + +// onOpen event handler +exports.testTabsEvent_onOpen = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + var tabs = require("tabs"); + let url = "data:text/html,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) { + test.assertEqual(++eventCount, 2, "both listeners notified"); + tabs.removeListener('open', listener1); + tabs.removeListener('open', listener2); + closeBrowserWindow(window, function() test.done()); + }); + + tabs.open(url); + }); +}; + +// onClose event handler +exports.testTabsEvent_onClose = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + var tabs = require("tabs"); + let url = "data:text/html,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) { + test.assertEqual(++eventCount, 2, "both listeners notified"); + tabs.removeListener('close', listener1); + tabs.removeListener('close', listener2); + closeBrowserWindow(window, function() test.done()); + }); + + tabs.on('ready', function onReady(tab) { + tabs.removeListener('ready', onReady); + tab.close(); + }); + + tabs.open(url); + }); +}; + +// onClose event handler when a window is closed +exports.testTabsEvent_onCloseWindow = function(test) { + test.waitUntilDone(); + + openBrowserWindow(function(window, browser) { + var tabs = require("tabs"); + + let closeCount = 0, individualCloseCount = 0; + function listener() { + closeCount++; + } + tabs.on('close', listener); + + // One tab is already open with the window + let openTabs = 1; + function testCasePossiblyLoaded() { + if (++openTabs == 4) { + beginCloseWindow(); + } + } + + tabs.open({ + url: "data:text/html,tab2", + onOpen: function() testCasePossiblyLoaded(), + onClose: function() individualCloseCount++ + }); + + tabs.open({ + url: "data:text/html,tab3", + onOpen: function() testCasePossiblyLoaded(), + onClose: function() individualCloseCount++ + }); + + tabs.open({ + url: "data:text/html,tab4", + onOpen: function() testCasePossiblyLoaded(), + onClose: function() individualCloseCount++ + }); + + function beginCloseWindow() { + closeBrowserWindow(window, function testFinished() { + tabs.removeListener("close", listener); + + test.assertEqual(closeCount, 4, "Correct number of close events received"); + test.assertEqual(individualCloseCount, 3, + "Each tab with an attached onClose listener received a close " + + "event when the window was closed"); + + test.done(); + }); + } + + }); +} + +// onReady event handler +exports.testTabsEvent_onReady = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + var tabs = require("tabs"); + let url = "data:text/html,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) { + test.assertEqual(++eventCount, 2, "both listeners notified"); + tabs.removeListener('ready', listener1); + tabs.removeListener('ready', listener2); + closeBrowserWindow(window, function() test.done()); + }); + + tabs.open(url); + }); +}; + +// onActivate event handler +exports.testTabsEvent_onActivate = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + var tabs = require("tabs"); + let url = "data:text/html,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) { + test.assertEqual(++eventCount, 2, "both listeners notified"); + tabs.removeListener('activate', listener1); + tabs.removeListener('activate', listener2); + closeBrowserWindow(window, function() test.done()); + }); + + tabs.open(url); + }); +}; + +// onDeactivate event handler +exports.testTabsEvent_onDeactivate = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + var tabs = require("tabs"); + let url = "data:text/html,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) { + test.assertEqual(++eventCount, 2, "both listeners notified"); + tabs.removeListener('deactivate', listener1); + tabs.removeListener('deactivate', listener2); + closeBrowserWindow(window, function() test.done()); + }); + + tabs.on('open', function onOpen(tab) { + tabs.removeListener('open', onOpen); + tabs.open("data:text/html,foo"); + }); + + tabs.open(url); + }); +}; + +// per-tab event handlers +exports.testPerTabEvents = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + var tabs = require("tabs"); + let eventCount = 0; + + tabs.open({ + url: "data:text/html,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() { + test.assertEqual(eventCount, 1, "both listeners notified"); + tab.removeListener('ready', listener1); + tab.removeListener('ready', listener2); + closeBrowserWindow(window, function() test.done()); + }); + } + }); + }); +}; + +exports.testAttachOnOpen = function (test) { + // Take care that attach has to be called on tab ready and not on tab open. + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + + tabs.open({ + url: "data:text/html,foobar", + onOpen: function (tab) { + let worker = tab.attach({ + contentScript: 'self.postMessage(document.location.href); ', + onMessage: function (msg) { + test.assertEqual(msg, "about:blank", + "Worker document url is about:blank on open"); + worker.destroy(); + closeBrowserWindow(window, function() test.done()); + } + }); + } + }); + + }); +} + +exports.testAttachOnMultipleDocuments = function (test) { + // Example of attach that process multiple tab documents + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let firstLocation = "data:text/html,foobar"; + let secondLocation = "data:text/html,bar"; + let thirdLocation = "data:text/html,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) { + test.assertEqual(msg, firstLocation, + "Worker url is equal to the 1st document"); + tab.url = secondLocation; + }, + onDetach: function () { + detachEventCount++; + test.pass("Got worker1 detach event"); + test.assertRaises(function () { + worker1.postMessage("ex-1"); + }, + /The page has been destroyed/, + "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) { + test.assertEqual(msg, secondLocation, + "Worker url is equal to the 2nd document"); + tab.url = thirdLocation; + }, + onDetach: function () { + detachEventCount++; + test.pass("Got worker2 detach event"); + test.assertRaises(function () { + worker2.postMessage("ex-2"); + }, + /The page has been destroyed/, + "postMessage throw because worker2 is destroyed"); + checkEnd(); + } + }); + worker2.postMessage("new-doc-2"); + } + else if (onReadyCount == 3) { + + tab.close(); + + } + + } + }); + + function checkEnd() { + if (detachEventCount != 2) + return; + + test.pass("Got all detach events"); + + closeBrowserWindow(window, function() test.done()); + } + + }); +} + + +exports.testAttachWrappers = function (test) { + // Check that content script has access to wrapped values by default + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let document = "data:text/html,"; + 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) { + test.assertEqual(msg, true, "Worker has wrapped objects ("+count+")"); + if (count++ == 1) + closeBrowserWindow(window, function() test.done()); + } + }); + } + }); + + }); +} + +/* +// We do not offer unwrapped access to DOM since bug 601295 landed +// See 660780 to track progress of unwrap feature +exports.testAttachUnwrapped = function (test) { + // Check that content script has access to unwrapped values through unsafeWindow + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + let tabs = require("tabs"); + let document = "data:text/html,"; + 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) { + test.assertEqual(msg, true, "Worker has access to javascript content globals ("+count+")"); + closeBrowserWindow(window, function() test.done()); + } + }); + } + }); + + }); +} +*/ + +exports['test window focus changes active tab'] = function(test) { + test.waitUntilDone(); + let win1 = openBrowserWindow(function() { + let win2 = openBrowserWindow(function() { + let tabs = require("tabs"); + tabs.on("activate", function onActivate() { + tabs.removeListener("activate", onActivate); + test.pass("activate was called on windows focus change."); + closeBrowserWindow(win1, function() { + closeBrowserWindow(win2, function() { test.done(); }); + }); + }); + win1.focus(); + }, "data:text/html,test window focus changes active tab

Window #2"); + }, "data:text/html,test window focus changes active tab

Window #1"); +}; + +exports['test ready event on new window tab'] = function(test) { + test.waitUntilDone(); + let uri = encodeURI("data:text/html,Waiting for ready event!"); + + require("tabs").on("ready", function onReady(tab) { + if (tab.url === uri) { + require("tabs").removeListener("ready", onReady); + test.pass("ready event was emitted"); + closeBrowserWindow(window, function() { + test.done(); + }); + } + }); + + let window = openBrowserWindow(function(){}, uri); +}; +/******************* 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 { + require("timer").setTimeout(function () { + callback(window, browsers[0]); + }, 10); + } catch (e) { console.exception(e); } + } + }, true); + } + + return window; +} + +// Helper for calling code at window close +function closeBrowserWindow(window, callback) { + window.addEventListener("unload", function unload() { + window.removeEventListener("unload", unload, false); + callback(); + }, false); + window.close(); +} + +// If the module doesn't support the app we're being run in, require() will +// throw. In that case, remove all tests above from exports, and add one dummy +// test that passes. +try { + require("tabs"); +} +catch (err) { + // This bug should be mentioned in the error message. + let bug = "https://bugzilla.mozilla.org/show_bug.cgi?id=560716"; + if (err.message.indexOf(bug) < 0) + throw err; + for (let [prop, val] in Iterator(exports)) { + if (/^test/.test(prop) && typeof(val) === "function") + delete exports[prop]; + } + exports.testAppNotSupported = function (test) { + test.pass("the tabs module does not support this application."); + }; +} diff --git a/tools/addon-sdk-1.5/packages/addon-kit/tests/test-timers.js b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-timers.js new file mode 100644 index 0000000..90b26bf --- /dev/null +++ b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-timers.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/. */ +const timers = require("timers"); + +exports.testTimeout = function (test) { + test.waitUntilDone(); + timers.setTimeout(function () { + test.pass("timers.setTimeout works"); + test.done(); + }, 0); +} diff --git a/tools/addon-sdk-1.5/packages/addon-kit/tests/test-widget.js b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-widget.js new file mode 100644 index 0000000..15df707 --- /dev/null +++ b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-widget.js @@ -0,0 +1,954 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 {Cc,Ci} = require("chrome"); +const { Loader } = require('./helpers'); + +exports.testConstructor = function(test) { + + const tabBrowser = require("tab-browser"); + + test.waitUntilDone(30000); + + const widgets = require("widget"); + const url = require("url"); + const windowUtils = require("window-utils"); + + let browserWindow = windowUtils.activeBrowserWindow; + let doc = browserWindow.document; + let AddonsMgrListener = browserWindow.AddonsMgrListener; + + function container() doc.getElementById("addon-bar"); + function widgetCount() container() ? container().getElementsByTagName("toolbaritem").length : 0; + let widgetStartCount = widgetCount(); + function widgetNode(index) container() ? container().getElementsByTagName("toolbaritem")[index] : null; + + // Test basic construct/destroy + AddonsMgrListener.onInstalling(); + let w = widgets.Widget({ id: "fooID", label: "foo", content: "bar" }); + AddonsMgrListener.onInstalled(); + test.assertEqual(widgetCount(), widgetStartCount + 1, "panel has correct number of child elements after widget construction"); + + // test widget height + test.assertEqual(widgetNode(0).firstChild.boxObject.height, 16, "widget has correct default height"); + + AddonsMgrListener.onUninstalling(); + w.destroy(); + AddonsMgrListener.onUninstalled(); + w.destroy(); + test.pass("Multiple destroys do not cause an error"); + test.assertEqual(widgetCount(), widgetStartCount, "panel has correct number of child elements after destroy"); + + // Test automatic widget destroy on unload + let loader = Loader(module); + let widgetsFromLoader = loader.require("widget"); + let widgetStartCount = widgetCount(); + let w = widgetsFromLoader.Widget({ id: "fooID", label: "foo", content: "bar" }); + test.assertEqual(widgetCount(), widgetStartCount + 1, "widget has been correctly added"); + loader.unload(); + test.assertEqual(widgetCount(), widgetStartCount, "widget has been destroyed on module unload"); + + // Test nothing + test.assertRaises( + function() widgets.Widget({}), + "The widget must have a non-empty label property.", + "throws on no properties"); + + // Test no label + test.assertRaises( + function() widgets.Widget({content: "foo"}), + "The widget must have a non-empty label property.", + "throws on no label"); + + // Test empty label + test.assertRaises( + function() widgets.Widget({label: "", content: "foo"}), + "The widget must have a non-empty label property.", + "throws on empty label"); + + // Test no content or image + test.assertRaises( + function() widgets.Widget({id: "fooID", label: "foo"}), + "No content or contentURL property found. Widgets must have one or the other.", + "throws on no content"); + + // Test empty content, no image + test.assertRaises( + function() widgets.Widget({id:"fooID", label: "foo", content: ""}), + "No content or contentURL property found. Widgets must have one or the other.", + "throws on empty content"); + + // Test empty image, no content + test.assertRaises( + function() widgets.Widget({id:"fooID", label: "foo", image: ""}), + "No content or contentURL property found. Widgets must have one or the other.", + "throws on empty content"); + + // Test empty content, empty image + test.assertRaises( + function() widgets.Widget({id:"fooID", label: "foo", content: "", image: ""}), + "No content or contentURL property found. Widgets must have one or the other.", + "throws on empty content"); + + // Test duplicated ID + let duplicateID = widgets.Widget({id: "foo", label: "foo", content: "bar"}); + test.assertRaises( + function() widgets.Widget({id: "foo", label: "bar", content: "bar"}), + /This widget ID is already used:/, + "throws on duplicated id"); + duplicateID.destroy(); + + // Test duplicate label, different ID + let w1 = widgets.Widget({id: "id1", label: "foo", content: "bar"}); + let w2 = widgets.Widget({id: "id2", label: "foo", content: "bar"}); + w1.destroy(); + w2.destroy(); + + // Test position restore on create/destroy/create + // Create 3 ordered widgets + let w1 = widgets.Widget({id: "first", label:"first", content: "bar"}); + let w2 = widgets.Widget({id: "second", label:"second", content: "bar"}); + let w3 = widgets.Widget({id: "third", label:"third", content: "bar"}); + // Remove the middle widget + test.assertEqual(widgetNode(1).getAttribute("label"), "second", "second widget is the second widget inserted"); + w2.destroy(); + test.assertEqual(widgetNode(1).getAttribute("label"), "third", "second widget is removed, so second widget is now the third one"); + w2 = widgets.Widget({id: "second", label:"second", content: "bar"}); + test.assertEqual(widgetNode(1).getAttribute("label"), "second", "second widget is created again, at the same location"); + // Cleanup this testcase + AddonsMgrListener.onUninstalling(); + w1.destroy(); + w2.destroy(); + w3.destroy(); + AddonsMgrListener.onUninstalled(); + + // Test concurrent widget module instances on addon-bar hiding + let loader = Loader(module); + let anotherWidgetsInstance = loader.require("widget"); + test.assert(container().collapsed, "UI is hidden when no widgets"); + AddonsMgrListener.onInstalling(); + let w1 = widgets.Widget({id: "foo", label: "foo", content: "bar"}); + // Ideally we would let AddonsMgrListener display the addon bar + // But, for now, addon bar is immediatly displayed by sdk code + // https://bugzilla.mozilla.org/show_bug.cgi?id=627484 + test.assert(!container().collapsed, "UI is already visible when we just added the widget"); + AddonsMgrListener.onInstalled(); + test.assert(!container().collapsed, "UI become visible when we notify AddonsMgrListener about end of addon installation"); + let w2 = anotherWidgetsInstance.Widget({id: "bar", label: "bar", content: "foo"}); + test.assert(!container().collapsed, "UI still visible when we add a second widget"); + AddonsMgrListener.onUninstalling(); + w1.destroy(); + AddonsMgrListener.onUninstalled(); + test.assert(!container().collapsed, "UI still visible when we remove one of two widgets"); + AddonsMgrListener.onUninstalling(); + w2.destroy(); + test.assert(!container().collapsed, "UI is still visible when we have removed all widget but still not called onUninstalled"); + AddonsMgrListener.onUninstalled(); + test.assert(container().collapsed, "UI is hidden when we have removed all widget and called onUninstalled"); + + // Helper for testing a single widget. + // Confirms proper addition and content setup. + function testSingleWidget(widgetOptions) { + // We have to display which test is being run, because here we do not + // use the regular test framework but rather a custom one that iterates + // the `tests` array. + console.info("executing: " + widgetOptions.id); + + let startCount = widgetCount(); + let widget = widgets.Widget(widgetOptions); + let node = widgetNode(startCount); + test.assert(node, "widget node at index"); + test.assertEqual(node.tagName, "toolbaritem", "widget element is correct"); + test.assertEqual(widget.width + "px", node.style.minWidth, "widget width is correct"); + test.assertEqual(widgetCount(), startCount + 1, "container has correct number of child elements"); + let content = node.firstElementChild; + test.assert(content, "found content"); + test.assertMatches(content.tagName, /iframe|image/, "content is iframe or image"); + return widget; + } + + // Array of widgets to test + // and a function to test them. + let tests = []; + function nextTest() { + test.assertEqual(widgetCount(), 0, "widget in last test property cleaned itself up"); + if (!tests.length) + test.done(); + else + require("timer").setTimeout(tests.shift(), 0); + } + function doneTest() nextTest(); + + // text widget + tests.push(function testTextWidget() testSingleWidget({ + id: "text", + label: "text widget", + content: "oh yeah", + contentScript: "self.postMessage(document.body.innerHTML);", + contentScriptWhen: "end", + onMessage: function (message) { + test.assertEqual(this.content, message, "content matches"); + this.destroy(); + doneTest(); + } + })); + + // html widget + tests.push(function testHTMLWidget() testSingleWidget({ + id: "html", + label: "html widget", + content: "
oh yeah
", + contentScript: "self.postMessage(document.body.innerHTML);", + contentScriptWhen: "end", + onMessage: function (message) { + test.assertEqual(this.content, message, "content matches"); + this.destroy(); + doneTest(); + } + })); + + // image url widget + tests.push(function testImageURLWidget() testSingleWidget({ + id: "image", + label: "image url widget", + contentURL: require("self").data.url("test.html"), + contentScript: "self.postMessage({title: document.title, " + + "tag: document.body.firstElementChild.tagName, " + + "content: document.body.firstElementChild.innerHTML});", + contentScriptWhen: "end", + onMessage: function (message) { + test.assertEqual(message.title, "foo", "title matches"); + test.assertEqual(message.tag, "P", "element matches"); + test.assertEqual(message.content, "bar", "element content matches"); + this.destroy(); + doneTest(); + } + })); + + // web uri widget + tests.push(function testWebURIWidget() testSingleWidget({ + id: "web", + label: "web uri widget", + contentURL: require("self").data.url("test.html"), + contentScript: "self.postMessage({title: document.title, " + + "tag: document.body.firstElementChild.tagName, " + + "content: document.body.firstElementChild.innerHTML});", + contentScriptWhen: "end", + onMessage: function (message) { + test.assertEqual(message.title, "foo", "title matches"); + test.assertEqual(message.tag, "P", "element matches"); + test.assertEqual(message.content, "bar", "element content matches"); + this.destroy(); + doneTest(); + } + })); + + // event: onclick + content + tests.push(function testOnclickEventContent() testSingleWidget({ + id: "click", + label: "click test widget - content", + content: "
foo
", + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('click', true, true ); " + + "document.getElementById('me').dispatchEvent(evt);", + contentScriptWhen: "end", + onClick: function() { + test.pass("onClick called"); + this.destroy(); + doneTest(); + } + })); + + // event: onmouseover + content + tests.push(function testOnmouseoverEventContent() testSingleWidget({ + id: "mouseover", + label: "mouseover test widget - content", + content: "
foo
", + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('mouseover', true, true ); " + + "document.getElementById('me').dispatchEvent(evt);", + contentScriptWhen: "end", + onMouseover: function() { + test.pass("onMouseover called"); + this.destroy(); + doneTest(); + } + })); + + // event: onmouseout + content + tests.push(function testOnmouseoutEventContent() testSingleWidget({ + id: "mouseout", + label: "mouseout test widget - content", + content: "
foo
", + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('mouseout', true, true ); " + + "document.getElementById('me').dispatchEvent(evt);", + contentScriptWhen: "end", + onMouseout: function() { + test.pass("onMouseout called"); + this.destroy(); + doneTest(); + } + })); + + // event: onclick + image + tests.push(function testOnclickEventImage() testSingleWidget({ + id: "click", + label: "click test widget - image", + contentURL: require("self").data.url("moz_favicon.ico"), + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('click', true, true ); " + + "document.body.firstElementChild.dispatchEvent(evt);", + contentScriptWhen: "end", + onClick: function() { + test.pass("onClick called"); + this.destroy(); + doneTest(); + } + })); + + // event: onmouseover + image + tests.push(function testOnmouseoverEventImage() testSingleWidget({ + id: "mouseover", + label: "mouseover test widget - image", + contentURL: require("self").data.url("moz_favicon.ico"), + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('mouseover', true, true ); " + + "document.body.firstElementChild.dispatchEvent(evt);", + contentScriptWhen: "end", + onMouseover: function() { + test.pass("onMouseover called"); + this.destroy(); + doneTest(); + } + })); + + // event: onmouseout + image + tests.push(function testOnmouseoutEventImage() testSingleWidget({ + id: "mouseout", + label: "mouseout test widget - image", + contentURL: require("self").data.url("moz_favicon.ico"), + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('mouseout', true, true ); " + + "document.body.firstElementChild.dispatchEvent(evt);", + contentScriptWhen: "end", + onMouseout: function() { + test.pass("onMouseout called"); + this.destroy(); + doneTest(); + } + })); + + // test multiple widgets + tests.push(function testMultipleWidgets() { + let w1 = widgets.Widget({id: "first", label: "first widget", content: "first content"}); + let w2 = widgets.Widget({id: "second", label: "second widget", content: "second content"}); + + w1.destroy(); + w2.destroy(); + + doneTest(); + }); + + // test updating widget content + let loads = 0; + tests.push(function testUpdatingWidgetContent() testSingleWidget({ + id: "content", + label: "content update test widget", + content: "
foo
", + contentScript: "self.postMessage(1)", + contentScriptWhen: "ready", + onMessage: function(message) { + if (!this.flag) { + this.content = "
bar
"; + this.flag = 1; + } + else { + test.assertEqual(this.content, "
bar
"); + this.destroy(); + doneTest(); + } + } + })); + + // test updating widget contentURL + let url1 = "data:text/html,foodle"; + let url2 = "data:text/html,nistel"; + + tests.push(function testUpdatingContentURL() testSingleWidget({ + id: "content", + label: "content update test widget", + contentURL: url1, + contentScript: "self.postMessage(document.location.href);", + contentScriptWhen: "end", + onMessage: function(message) { + if (!this.flag) { + test.assertEqual(this.contentURL.toString(), url1); + test.assertEqual(message, url1); + this.contentURL = url2; + this.flag = 1; + } + else { + test.assertEqual(this.contentURL.toString(), url2); + test.assertEqual(message, url2); + this.destroy(); + doneTest(); + } + } + })); + + // test tooltip + tests.push(function testTooltip() testSingleWidget({ + id: "text", + label: "text widget", + content: "oh yeah", + tooltip: "foo", + contentScript: "self.postMessage(1)", + contentScriptWhen: "ready", + onMessage: function(message) { + test.assertEqual(this.tooltip, "foo", "tooltip matches"); + this.destroy(); + doneTest(); + } + })); + + // test tooltip fallback to label + tests.push(function testTooltipFallback() testSingleWidget({ + id: "fallback", + label: "fallback", + content: "oh yeah", + contentScript: "self.postMessage(1)", + contentScriptWhen: "ready", + onMessage: function(message) { + test.assertEqual(this.tooltip, this.label, "tooltip fallbacks to label"); + this.destroy(); + doneTest(); + } + })); + + // test updating widget tooltip + let updated = false; + tests.push(function testUpdatingTooltip() testSingleWidget({ + id: "tooltip", + label: "tooltip update test widget", + tooltip: "foo", + content: "
foo
", + contentScript: "self.postMessage(1)", + contentScriptWhen: "ready", + onMessage: function(message) { + this.tooltip = "bar"; + test.assertEqual(this.tooltip, "bar", "tooltip gets updated"); + this.destroy(); + doneTest(); + } + })); + + // test allow attribute + tests.push(function testDefaultAllow() testSingleWidget({ + id: "allow", + label: "allow.script attribute", + content: "", + contentScript: "self.postMessage(document.title)", + onMessage: function(message) { + test.assertEqual(message, "ok", "scripts are evaluated by default"); + this.destroy(); + doneTest(); + } + })); + + tests.push(function testExplicitAllow() testSingleWidget({ + id: "allow", + label: "allow.script attribute", + allow: {script: true}, + content: "", + contentScript: "self.postMessage(document.title)", + onMessage: function(message) { + test.assertEqual(message, "ok", "scripts are evaluated when we want to"); + this.destroy(); + doneTest(); + } + })); + + tests.push(function testExplicitDisallow() testSingleWidget({ + id: "allow", + label: "allow.script attribute", + content: "", + allow: {script: false}, + contentScript: "self.postMessage(document.title)", + onMessage: function(message) { + test.assertNotEqual(message, "ok", "scripts aren't evaluated when " + + "explicitly blocked it"); + this.destroy(); + doneTest(); + } + })); + + // test multiple windows + tests.push(function testMultipleWindows() { + tabBrowser.addTab("about:blank", { inNewWindow: true, onLoad: function(e) { + let browserWindow = e.target.defaultView; + let doc = browserWindow.document; + function container() doc.getElementById("addon-bar"); + function widgetCount2() container() ? container().childNodes.length : 0; + let widgetStartCount2 = widgetCount2(); + + let w1Opts = {id:"first", label: "first widget", content: "first content"}; + let w1 = testSingleWidget(w1Opts); + test.assertEqual(widgetCount2(), widgetStartCount2 + 1, "2nd window has correct number of child elements after first widget"); + + let w2Opts = {id:"second", label: "second widget", content: "second content"}; + let w2 = testSingleWidget(w2Opts); + test.assertEqual(widgetCount2(), widgetStartCount2 + 2, "2nd window has correct number of child elements after second widget"); + + w1.destroy(); + test.assertEqual(widgetCount2(), widgetStartCount2 + 1, "2nd window has correct number of child elements after first destroy"); + w2.destroy(); + test.assertEqual(widgetCount2(), widgetStartCount2, "2nd window has correct number of child elements after second destroy"); + + closeBrowserWindow(browserWindow, function() { + doneTest(); + }); + }}); + }); + + // test window closing + tests.push(function testWindowClosing() { + // 1/ Create a new widget + let w1Opts = { + id:"first", + label: "first widget", + content: "first content", + contentScript: "self.port.on('event', function () self.port.emit('event'))" + }; + let widget = testSingleWidget(w1Opts); + let windows = require("windows").browserWindows; + + // 2/ Retrieve a WidgetView for the initial browser window + let acceptDetach = false; + let mainView = widget.getView(windows.activeWindow); + test.assert(mainView, "Got first widget view"); + mainView.on("detach", function () { + // 8/ End of our test. Accept detach event only when it occurs after + // widget.destroy() + if (acceptDetach) + doneTest(); + else + test.fail("View on initial window should not be destroyed"); + }); + mainView.port.on("event", function () { + // 7/ Receive event sent during 6/ and cleanup our test + acceptDetach = true; + widget.destroy(); + }); + + // 3/ First: open a new browser window + windows.open({ + url: "about:blank", + onOpen: function(window) { + // 4/ Retrieve a WidgetView for this new window + let view = widget.getView(window); + test.assert(view, "Got second widget view"); + view.port.on("event", function () { + test.fail("We should not receive event on the detach view"); + }); + view.on("detach", function () { + // The related view is destroyed + // 6/ Send a custom event + test.assertRaises(function () { + view.port.emit("event"); + }, + /The widget has been destroyed and can no longer be used./, + "emit on a destroyed view should throw"); + widget.port.emit("event"); + }); + + // 5/ Destroy this window + window.close(); + } + }); + }); + + tests.push(function testAddonBarHide() { + // Hide the addon-bar + browserWindow.setToolbarVisibility(container(), false); + + // Then open a browser window and verify that the addon-bar remains hidden + tabBrowser.addTab("about:blank", { inNewWindow: true, onLoad: function(e) { + let browserWindow = e.target.defaultView; + let doc = browserWindow.document; + function container2() doc.getElementById("addon-bar"); + function widgetCount2() container2() ? container2().childNodes.length : 0; + let widgetStartCount2 = widgetCount2(); + + let w1Opts = {id:"first", label: "first widget", content: "first content"}; + let w1 = testSingleWidget(w1Opts); + test.assertEqual(widgetCount2(), widgetStartCount2 + 1, "2nd window has correct number of child elements after widget creation"); + + w1.destroy(); + test.assertEqual(widgetCount2(), widgetStartCount2, "2nd window has correct number of child elements after widget destroy"); + + test.assert(container().collapsed, "1st window has an hidden addon-bar"); + test.assert(container2().collapsed, "2nd window has an hidden addon-bar"); + + browserWindow.setToolbarVisibility(container(), true); + + closeBrowserWindow(browserWindow, function() { + doneTest(); + }); + }}); + }); + + // test widget.width + tests.push(function testWidgetWidth() testSingleWidget({ + id: "text", + label: "test widget.width", + content: "test width", + width: 200, + contentScript: "self.postMessage(1)", + contentScriptWhen: "ready", + onMessage: function(message) { + test.assertEqual(this.width, 200); + + let node = widgetNode(0); + test.assertEqual(this.width, node.style.minWidth.replace("px", "")); + test.assertEqual(this.width, node.firstElementChild.style.width.replace("px", "")); + this.width = 300; + test.assertEqual(this.width, node.style.minWidth.replace("px", "")); + test.assertEqual(this.width, node.firstElementChild.style.width.replace("px", "")); + + this.destroy(); + doneTest(); + } + })); + + // test click handler not respond to right-click + let clickCount = 0; + tests.push(function testNoRightClick() testSingleWidget({ + id: "click-content", + label: "click test widget - content", + content: "
foo
", + contentScript: "var evt = document.createEvent('MouseEvents'); " + + "evt.initMouseEvent('click', true, true, window, " + + " 0, 0, 0, 0, 0, false, false, false, false, 2, null); " + + "document.getElementById('me').dispatchEvent(evt); " + + "evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('click', true, true ); " + + "document.getElementById('me').dispatchEvent(evt); " + + "evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('mouseover', true, true ); " + + "document.getElementById('me').dispatchEvent(evt);", + contentScriptWhen: "end", + onClick: function() clickCount++, + onMouseover: function() { + test.assertEqual(clickCount, 1, "right click wasn't sent to click handler"); + this.destroy(); + doneTest(); + } + })); + + // kick off test execution + doneTest(); +}; + +exports.testPanelWidget1 = function testPanelWidget1(test) { + const widgets = require("widget"); + + let widget1 = widgets.Widget({ + id: "panel1", + label: "panel widget 1", + content: "
foo
", + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('click', true, true ); " + + "document.body.dispatchEvent(evt);", + contentScriptWhen: "end", + panel: require("panel").Panel({ + contentURL: "data:text/html,Look ma, a panel!", + onShow: function() { + widget1.destroy(); + test.pass("panel displayed on click"); + test.done(); + } + }) + }); + test.waitUntilDone(); +}; + +exports.testPanelWidget2 = function testPanelWidget2(test) { + const widgets = require("widget"); + test.assertRaises( + function() { + widgets.Widget({ + id: "panel2", + label: "panel widget 2", + panel: {} + }); + }, + "The option \"panel\" must be one of the following types: null, undefined, object", + "widget.panel must be a Panel object" + ); +}; + +exports.testPanelWidget3 = function testPanelWidget3(test) { + const widgets = require("widget"); + let onClickCalled = false; + let widget3 = widgets.Widget({ + id: "panel3", + label: "panel widget 3", + content: "
foo
", + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('click', true, true ); " + + "document.body.firstElementChild.dispatchEvent(evt);", + contentScriptWhen: "end", + onClick: function() { + onClickCalled = true; + this.panel.show(); + }, + panel: require("panel").Panel({ + contentURL: "data:text/html,Look ma, a panel!", + onShow: function() { + test.assert( + onClickCalled, + "onClick called on click for widget with both panel and onClick" + ); + widget3.destroy(); + test.done(); + } + }) + }); + test.waitUntilDone(); +}; + +exports.testWidgetMessaging = function testWidgetMessaging(test) { + test.waitUntilDone(); + let origMessage = "foo"; + const widgets = require("widget"); + let widget = widgets.Widget({ + id: "foo", + label: "foo", + content: "baz", + contentScriptWhen: "end", + contentScript: "self.on('message', function(data) { self.postMessage(data); }); self.postMessage('ready');", + onMessage: function(message) { + if (message == "ready") + widget.postMessage(origMessage); + else { + test.assertEqual(origMessage, message); + widget.destroy(); + test.done(); + } + } + }); +}; + +exports.testWidgetViews = function testWidgetViews(test) { + test.waitUntilDone(); + const widgets = require("widget"); + let widget = widgets.Widget({ + id: "foo", + label: "foo", + content: "baz", + contentScriptWhen: "ready", + contentScript: "self.on('message', function(data) self.postMessage(data)); self.postMessage('ready')", + onAttach: function(view) { + test.pass("WidgetView created"); + view.on("message", function () { + test.pass("Got message in WidgetView"); + widget.destroy(); + }); + view.on("detach", function () { + test.pass("WidgetView destroyed"); + test.done(); + }); + } + }); + +}; + +exports.testWidgetViewsUIEvents = function testWidgetViewsUIEvents(test) { + test.waitUntilDone(); + const widgets = require("widget"); + let view = null; + let widget = widgets.Widget({ + id: "foo", + label: "foo", + content: "
foo
", + contentScript: "var evt = document.createEvent('HTMLEvents'); " + + "evt.initEvent('click', true, true ); " + + "document.getElementById('me').dispatchEvent(evt);", + contentScriptWhen: "ready", + onAttach: function(attachView) { + view = attachView; + test.pass("Got attach event"); + }, + onClick: function (eventView) { + test.assertEqual(view, eventView, + "event first argument is equal to the WidgetView"); + let view2 = widget.getView(require("windows").browserWindows.activeWindow); + test.assertEqual(view, view2, + "widget.getView return the same WidgetView"); + widget.destroy(); + test.done(); + } + }); +}; + +exports.testWidgetViewsCustomEvents = function testWidgetViewsCustomEvents(test) { + test.waitUntilDone(); + const widgets = require("widget"); + let widget = widgets.Widget({ + id: "foo", + label: "foo", + content: "
foo
", + contentScript: "self.port.emit('event', 'ok');", + contentScriptWhen: "ready", + onAttach: function(view) { + view.port.on("event", function (data) { + test.assertEqual(data, "ok", + "event argument is valid on WidgetView"); + }); + }, + }); + widget.port.on("event", function (data) { + test.assertEqual(data, "ok", + "event argument is valid on Widget"); + widget.destroy(); + test.done(); + }); +}; + +exports.testWidgetViewsTooltip = function testWidgetViewsTooltip(test) { + test.waitUntilDone(); + const widgets = require("widget"); + + let widget = new widgets.Widget({ + id: "foo", + label: "foo", + content: "foo" + }); + let view = widget.getView(require("windows").browserWindows.activeWindow); + widget.tooltip = null; + test.assertEqual(view.tooltip, "foo", + "view tooltip defaults to base widget label"); + test.assertEqual(widget.tooltip, "foo", + "tooltip defaults to base widget label"); + widget.destroy(); + test.done(); +}; + +exports.testWidgetMove = function testWidgetMove(test) { + test.waitUntilDone(); + + let windowUtils = require("window-utils"); + let widgets = require("widget"); + + let browserWindow = windowUtils.activeBrowserWindow; + let doc = browserWindow.document; + + let label = "unique-widget-label"; + let origMessage = "message after node move"; + let gotFirstReady = false; + + let widget = widgets.Widget({ + id: "foo", + label: label, + content: "baz", + contentScriptWhen: "ready", + contentScript: "self.on('message', function(data) { self.postMessage(data); }); self.postMessage('ready');", + onMessage: function(message) { + if (message == "ready") { + if (!gotFirstReady) { + test.pass("Got first ready event"); + let widgetNode = doc.querySelector('toolbaritem[label="' + label + '"]'); + let parent = widgetNode.parentNode; + parent.insertBefore(widgetNode, parent.firstChild); + gotFirstReady = true; + } else { + test.pass("Got second ready event"); + widget.postMessage(origMessage); + } + } + else { + test.assertEqual(origMessage, message, "Got message after node move"); + widget.destroy(); + test.done(); + } + } + }); +}; + +/* +The bug is exhibited when a widget with HTML content has it's content +changed to new HTML content with a pound in it. Because the src of HTML +content is converted to a data URI, the underlying iframe doesn't +consider the content change a navigation change, so doesn't load +the new content. +*/ +exports.testWidgetWithPound = function testWidgetWithPound(test) { + test.waitUntilDone(); + + function getWidgetContent(widget) { + let windowUtils = require("window-utils"); + let browserWindow = windowUtils.activeBrowserWindow; + let doc = browserWindow.document; + let widgetNode = doc.querySelector('toolbaritem[label="' + widget.label + '"]'); + test.assert(widgetNode, 'found widget node in the front-end'); + return widgetNode.firstChild.contentDocument.body.innerHTML; + } + + let widgets = require("widget"); + let count = 0; + let widget = widgets.Widget({ + id: "1", + label: "foo", + content: "foo", + contentScript: "window.addEventListener('load', self.postMessage, false);", + onMessage: function() { + count++; + if (count == 1) { + widget.content = "foo#"; + } + else { + test.assertEqual(getWidgetContent(widget), "foo#", "content updated to pound?"); + widget.destroy(); + test.done(); + } + } + }); +}; + +/******************* helpers *********************/ + +// Helper for calling code at window close +function closeBrowserWindow(window, callback) { + require("timer").setTimeout(function() { + window.addEventListener("unload", function onUnload() { + window.removeEventListener("unload", onUnload, false); + callback(); + }, false); + window.close(); + }, 0); +} + +// ADD NO TESTS BELOW THIS LINE! /////////////////////////////////////////////// + +// If the module doesn't support the app we're being run in, require() will +// throw. In that case, remove all tests above from exports, and add one dummy +// test that passes. +try { + require("widget"); +} +catch (err) { + // This bug should be mentioned in the error message. + let bug = "https://bugzilla.mozilla.org/show_bug.cgi?id=560716"; + if (err.message.indexOf(bug) < 0) + throw err; + for (let [prop, val] in Iterator(exports)) { + if (/^test/.test(prop) && typeof(val) === "function") + delete exports[prop]; + } + exports.testAppNotSupported = function (test) { + test.pass("context-menu does not support this application."); + }; +} + diff --git a/tools/addon-sdk-1.5/packages/addon-kit/tests/test-windows.js b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-windows.js new file mode 100644 index 0000000..b85fec7 --- /dev/null +++ b/tools/addon-sdk-1.5/packages/addon-kit/tests/test-windows.js @@ -0,0 +1,309 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 {Cc, Ci} = require("chrome"); +const { setTimeout } = require("timer"); +const { Loader } = require('./helpers'); +const wm = Cc["@mozilla.org/appshell/window-mediator;1"]. + getService(Ci.nsIWindowMediator); +let browserWindows; + +function getTestRunnerWindow() wm.getMostRecentWindow("test:runner") + +exports.testOpenAndCloseWindow = function(test) { + test.waitUntilDone(); + + test.assertEqual(browserWindows.length, 1, "Only one window open"); + + browserWindows.open({ + url: "data:text/html,windows API test", + onOpen: function(window) { + test.assertEqual(this, browserWindows, + "The 'this' object is the windows object."); + test.assertEqual(window.tabs.length, 1, "Only one tab open"); + test.assertEqual(browserWindows.length, 2, "Two windows open"); + window.tabs.activeTab.on('ready', function onReady(tab) { + tab.removeListener('ready', onReady); + test.assert(window.title.indexOf("windows API test") != -1, + "URL correctly loaded"); + window.close(); + }); + }, + onClose: function(window) { + test.assertEqual(window.tabs.length, 0, "Tabs were cleared"); + test.assertEqual(browserWindows.length, 1, "Only one window open"); + test.done(); + } + }); +}; + +exports.testAutomaticDestroy = function(test) { + + test.waitUntilDone(); + let windows = browserWindows; + + // Create a second windows instance that we will unload + let called = false; + let loader = Loader(module); + let windows2 = loader.require("windows").browserWindows; + windows2.on("open", function() { + called = true; + }); + + loader.unload(); + + // Fire a windows event and check that this unloaded instance is inactive + windows.open({ + url: "data:text/html,foo", + onOpen: function(window) { + setTimeout(function () { + test.assert(!called, + "Unloaded windows instance is destroyed and inactive"); + window.close(function () { + test.done(); + }); + }); + } + }); + +}; + +exports.testOnOpenOnCloseListeners = function(test) { + test.waitUntilDone(); + let windows = browserWindows; + + test.assertEqual(browserWindows.length, 1, "Only one window open"); + + let received = { + listener1: false, + listener2: false, + listener3: false, + listener4: false + } + + function listener1() { + test.assertEqual(this, windows, "The 'this' object is the windows object."); + if (received.listener1) + test.fail("Event received twice"); + received.listener1 = true; + } + + function listener2() { + if (received.listener2) + test.fail("Event received twice"); + received.listener2 = true; + } + + function listener3() { + test.assertEqual(this, windows, "The 'this' object is the windows object."); + if (received.listener3) + test.fail("Event received twice"); + received.listener3 = true; + } + + function listener4() { + if (received.listener4) + test.fail("Event received twice"); + received.listener4 = true; + } + + windows.on('open', listener1); + windows.on('open', listener2); + windows.on('close', listener3); + windows.on('close', listener4); + + function verify() { + test.assert(received.listener1, "onOpen handler called"); + test.assert(received.listener2, "onOpen handler called"); + test.assert(received.listener3, "onClose handler called"); + test.assert(received.listener4, "onClose handler called"); + + windows.removeListener('open', listener1); + windows.removeListener('open', listener2); + windows.removeListener('close', listener3); + windows.removeListener('close', listener4); + test.done(); + } + + + windows.open({ + url: "data:text/html,foo", + onOpen: function(window) { + window.close(verify); + } + }); +}; + +exports.testWindowTabsObject = function(test) { + test.waitUntilDone(); + + browserWindows.open({ + url: "data:text/html,tab 1", + onOpen: function onOpen(window) { + test.assertEqual(window.tabs.length, 1, "Only 1 tab open"); + + window.tabs.open({ + url: "data:text/html,tab 2", + inBackground: true, + onReady: function onReady(newTab) { + test.assertEqual(window.tabs.length, 2, "New tab open"); + test.assertEqual(newTab.title, "tab 2", "Correct new tab title"); + test.assertEqual(window.tabs.activeTab.title, "tab 1", "Correct active tab"); + + let i = 1; + for each (let tab in window.tabs) + test.assertEqual(tab.title, "tab " + i++, "Correct title"); + + window.close(); + } + }); + }, + onClose: function onClose(window) { + test.assertEqual(window.tabs.length, 0, "No more tabs on closed window"); + test.done(); + } + }); +}; + +exports.testActiveWindow = function(test) { + const xulApp = require("xul-app"); + if (xulApp.versionInRange(xulApp.platformVersion, "1.9.2", "1.9.2.*")) { + test.pass("This test is disabled on 3.6. For more information, see bug 598525"); + return; + } + + let windows = browserWindows; + + // API window objects + let window2, window3; + + // Raw window objects + let nonBrowserWindow = getTestRunnerWindow(), rawWindow2, rawWindow3; + + test.waitUntilDone(); + + let testSteps = [ + function() { + test.assertEqual(windows.length, 3, "Correct number of browser windows"); + let count = 0; + for (let window in windows) + count++; + test.assertEqual(count, 3, "Correct number of windows returned by iterator"); + + rawWindow2.focus(); + continueAfterFocus(rawWindow2); + }, + function() { + nonBrowserWindow.focus(); + continueAfterFocus(nonBrowserWindow); + }, + function() { + /** + * Bug 614079: This test fails intermittently on some specific linux + * environnements, without being able to reproduce it in same + * distribution with same window manager. + * Disable it until being able to reproduce it easily. + + // On linux, focus is not consistent, so we can't be sure + // what window will be on top. + // Here when we focus "non-browser" window, + // Any Browser window may be selected as "active". + test.assert(windows.activeWindow == window2 || windows.activeWindow == window3, + "Non-browser windows aren't handled by this module"); + */ + window2.activate(); + continueAfterFocus(rawWindow2); + }, + function() { + test.assertEqual(windows.activeWindow.title, window2.title, "Correct active window - 2"); + window3.activate(); + continueAfterFocus(rawWindow3); + }, + function() { + test.assertEqual(windows.activeWindow.title, window3.title, "Correct active window - 3"); + nonBrowserWindow.focus(); + finishTest(); + } + ]; + + windows.open({ + url: "data:text/html,window 2", + onOpen: function(window) { + window2 = window; + rawWindow2 = wm.getMostRecentWindow("navigator:browser"); + + windows.open({ + url: "data:text/html,window 3", + onOpen: function(window) { + window.tabs.activeTab.on('ready', function onReady() { + window3 = window; + rawWindow3 = wm.getMostRecentWindow("navigator:browser"); + nextStep() + }); + } + }); + } + }); + + function nextStep() { + if (testSteps.length > 0) + testSteps.shift()(); + } + + function continueAfterFocus(targetWindow) { + + // Based on SimpleTest.waitForFocus + var fm = Cc["@mozilla.org/focus-manager;1"]. + getService(Ci.nsIFocusManager); + + var childTargetWindow = {}; + fm.getFocusedElementForWindow(targetWindow, true, childTargetWindow); + childTargetWindow = childTargetWindow.value; + + var focusedChildWindow = {}; + if (fm.activeWindow) { + fm.getFocusedElementForWindow(fm.activeWindow, true, focusedChildWindow); + focusedChildWindow = focusedChildWindow.value; + } + + var focused = (focusedChildWindow == childTargetWindow); + if (focused) { + nextStep(); + } else { + childTargetWindow.addEventListener("focus", function focusListener() { + childTargetWindow.removeEventListener("focus", focusListener, true); + nextStep(); + }, true); + } + + } + + function finishTest() { + window3.close(function() { + window2.close(function() { + test.done(); + }); + }); + } +}; + +// If the module doesn't support the app we're being run in, require() will +// throw. In that case, remove all tests above from exports, and add one dummy +// test that passes. +try { + browserWindows = require("windows").browserWindows; +} +catch (err) { + // This bug should be mentioned in the error message. + let bug = "https://bugzilla.mozilla.org/show_bug.cgi?id=571449"; + if (err.message.indexOf(bug) < 0) + throw err; + for (let [prop, val] in Iterator(exports)) { + if (/^test/.test(prop) && typeof(val) === "function") + delete exports[prop]; + } + exports.testAppNotSupported = function (test) { + test.pass("the windows module does not support this application."); + }; +} diff --git a/tools/addon-sdk-1.5/packages/api-utils/README.md b/tools/addon-sdk-1.5/packages/api-utils/README.md new file mode 100644 index 0000000..55adb57 --- /dev/null +++ b/tools/addon-sdk-1.5/packages/api-utils/README.md @@ -0,0 +1,35 @@ + + +API Utils provides a basic CommonJS infrastructure for +developing traditional XULRunner add-ons and applications. It is +the basis for the Add-on SDK. + +To address issues present in traditional add-on development, +API Utils provides mechanisms for: + +* writing and executing test cases, inspired by Python's [nose][] + package, +* tracking JS objects of interest to aid in memory profiling and leak + detection, +* registering callbacks that perform cleanup tasks when modules are + unloaded, +* easily reporting errors with full stack tracebacks. + +API Utils also has the following characteristics: + +* Beautiful, concise documentation. +* A rigorous test suite ensuring that the library doesn't break as the + Mozilla platform evolves. +* Solid developer ergonomics ensuring that developers can easily find + out why something they're doing isn't working. + +API Utils is intended to be very small and only contain the bare +minimum of functionality that all add-ons need. + +Note that the API Utils package has not fully stabilized yet, meaning that +we do still expect to make incompatible changes to its APIs in future releases +of the SDK. + + [nose]: http://code.google.com/p/python-nose/ diff --git a/tools/addon-sdk-1.5/packages/api-utils/data/content-proxy.js b/tools/addon-sdk-1.5/packages/api-utils/data/content-proxy.js new file mode 100644 index 0000000..e950eaf --- /dev/null +++ b/tools/addon-sdk-1.5/packages/api-utils/data/content-proxy.js @@ -0,0 +1,838 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 Ci = Components.interfaces; + +/** + * Access key that allows privileged code to unwrap proxy wrappers through + * valueOf: + * let xpcWrapper = proxyWrapper.valueOf(UNWRAP_ACCESS_KEY); + * This key should only be used by proxy unit test. + */ + const UNWRAP_ACCESS_KEY = {}; + + + /** + * Returns a closure that wraps arguments before calling the given function, + * which can be given to native functions that accept a function, such that when + * the closure is called, the given function is called with wrapped arguments. + * + * @param fun {Function} + * the function for which to create a closure wrapping its arguments + * @param obj {Object} + * target object from which `fun` comes from + * (optional, for debugging purpose) + * @param name {String} + * name of the attribute from which `fun` is binded on `obj` + * (optional, for debugging purpose) + * + * Example: + * function contentScriptListener(event) {} + * let wrapper = ContentScriptFunctionWrapper(contentScriptListener); + * xray.addEventListener("...", wrapper, false); + * -> Allow to `event` to be wrapped + */ +function ContentScriptFunctionWrapper(fun, obj, name) { + if ("___proxy" in fun && typeof fun.___proxy == "function") + return fun.___proxy; + + let wrappedFun = function () { + let args = []; + for (let i = 0, l = arguments.length; i < l; i++) + args.push(wrap(arguments[i])); + + //console.log("Called from native :"+obj+"."+name); + //console.log(">args "+arguments.length); + //console.log(fun); + + // Native code can execute this callback with `this` being the wrapped + // function. For example, window.mozRequestAnimationFrame. + if (this == wrappedFun) + return fun.apply(fun, args); + + return fun.apply(wrap(this), args); + }; + + Object.defineProperty(fun, "___proxy", {value : wrappedFun, + writable : false, + enumerable : false, + configurable : false}); + + return wrappedFun; +} + +/** + * Returns a closure that unwraps arguments before calling the `fun` function, + * which can be used to build a wrapper for a native function that accepts + * wrapped arguments, since native function only accept unwrapped arguments. + * + * @param fun {Function} + * the function to wrap + * @param originalObject {Object} + * target object from which `fun` comes from + * (optional, for debugging purpose) + * @param name {String} + * name of the attribute from which `fun` is binded on `originalObject` + * (optional, for debugging purpose) + * + * Example: + * wrapper.appendChild = NativeFunctionWrapper(xray.appendChild, xray); + * wrapper.appendChild(anotherWrapper); + * -> Allow to call xray.appendChild with unwrapped version of anotherWrapper + */ +function NativeFunctionWrapper(fun, originalObject, name) { + return function () { + let args = []; + let obj = this && typeof this.valueOf == "function" ? + this.valueOf(UNWRAP_ACCESS_KEY) : this; + + for (let i = 0, l = arguments.length; i < l; i++) + args.push( unwrap(arguments[i], obj, name) ); + + //if (name != "toString") + //console.log(">>calling native ["+(name?name:'#closure#')+"]: \n"+fun.apply+"\n"+obj+"\n("+args.join(', ')+")\nthis :"+obj+"from:"+originalObject+"\n"); + + // Need to use Function.prototype.apply.apply because XMLHttpRequest + // is a function (typeof return 'function') and fun.apply is null :/ + let unwrapResult = Function.prototype.apply.apply(fun, [obj, args]); + let result = wrap(unwrapResult, obj, name); + + //console.log("<< "+rr+" -> "+r); + + return result; + }; +} + +/* + * Unwrap a JS value that comes from the content script. + * Mainly converts proxy wrapper to XPCNativeWrapper. + */ +function unwrap(value, obj, name) { + //console.log("unwrap : "+value+" ("+name+")"); + if (!value) + return value; + let type = typeof value; + + // In case of proxy, unwrap them recursively + // (it should not be recursive, just in case of) + if (["object", "function"].indexOf(type) !== -1 && + "__isWrappedProxy" in value) { + while("__isWrappedProxy" in value) + value = value.valueOf(UNWRAP_ACCESS_KEY); + return value; + } + + // In case of functions we need to return a wrapper that converts native + // arguments applied to this function into proxies. + if (type == "function") + return ContentScriptFunctionWrapper(value, obj, name); + + // We must wrap objects coming from content script too, as they may have + // a function that will be called by a native method. + // For example: + // addEventListener(..., { handleEvent: function(event) {} }, ...); + if (type == "object") + return ContentScriptObjectWrapper(value); + + if (["string", "number", "boolean"].indexOf(type) !== -1) + return value; + //console.log("return non-wrapped to native : "+typeof value+" -- "+value); + return value; +} + +/** + * Returns an XrayWrapper proxy object that allow to wrap any of its function + * though `ContentScriptFunctionWrapper`. These proxies are given to + * XrayWrappers in order to automatically wrap values when they call a method + * of these proxies. So that they are only used internaly and content script, + * nor web page have ever access to them. As a conclusion, we can consider + * this code as being safe regarding web pages overload. + * + * + * @param obj {Object} + * object coming from content script context to wrap + * + * Example: + * let myListener = { handleEvent: function (event) {} }; + * node.addEventListener("click", myListener, false); + * `event` has to be wrapped, so handleEvent has to be wrapped using + * `ContentScriptFunctionWrapper` function. + * In order to do so, we build this new kind of proxies. + */ +function ContentScriptObjectWrapper(obj) { + if ("___proxy" in obj && typeof obj.___proxy == "object") + return obj.___proxy; + + function valueOf(key) { + if (key === UNWRAP_ACCESS_KEY) + return obj; + return this; + } + + let proxy = Proxy.create({ + // Fundamental traps + getPropertyDescriptor: function(name) { + return Object.getOwnPropertyDescriptor(obj, name); + }, + defineProperty: function(name, desc) { + return Object.defineProperty(obj, name, desc); + }, + getOwnPropertyNames: function () { + return Object.getOwnPropertyNames(obj); + }, + delete: function(name) { + return delete obj[name]; + }, + // derived traps + has: function(name) { + return name === "__isXrayWrapperProxy" || + name in obj; + }, + hasOwn: function(name) { + return Object.prototype.hasOwnProperty.call(obj, name); + }, + get: function(receiver, name) { + if (name == "valueOf") + return valueOf; + let value = obj[name]; + if (!value) + return value; + + return unwrap(value); + }, + set: function(receiver, name, val) { + obj[name] = val; + return true; + }, + enumerate: function() { + var result = []; + for each (let name in obj) { + result.push(name); + }; + return result; + }, + keys: function() { + return Object.keys(obj); + } + }); + + Object.defineProperty(obj, "___proxy", {value : proxy, + writable : false, + enumerable : false, + configurable : false}); + + return proxy; +} + +// List of all existing typed arrays. +// Can be found here: +// http://mxr.mozilla.org/mozilla-central/source/js/src/jsapi.cpp#1790 +const typedArraysCtor = [ + ArrayBuffer, + Int8Array, + Uint8Array, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, + Uint8ClampedArray +]; + +/* + * Wrap a JS value coming from the document by building a proxy wrapper. + */ +function wrap(value, obj, name, debug) { + if (!value) + return value; + let type = typeof value; + if (type == "object") { + // Bug 671016: Typed arrays don't need to be proxified. + // We avoid checking the whole constructor list on all objects + // by doing this check only on non-extensible objects: + if (!Object.isExtensible(value) && + typedArraysCtor.indexOf(value.constructor) !== -1) + return value; + + // We may have a XrayWrapper proxy. + // For example: + // let myListener = { handleEvent: function () {} }; + // node.addEventListener("click", myListener, false); + // When native code want to call handleEvent, + // we go though ContentScriptFunctionWrapper that calls `wrap(this)` + // `this` is the XrayWrapper proxy of myListener. + // We return this object without building a CS proxy as it is already + // a value coming from the CS. + if ("__isXrayWrapperProxy" in value) + return value.valueOf(UNWRAP_ACCESS_KEY); + + // Unwrap object before wrapping it. + // It should not happen with CS proxy objects. + while("__isWrappedProxy" in value) { + value = value.valueOf(UNWRAP_ACCESS_KEY); + } + + if (XPCNativeWrapper.unwrap(value) !== value) + return getProxyForObject(value); + // In case of Event, HTMLCollection or NodeList or ??? + // XPCNativeWrapper.unwrap(value) === value + // but it's still a XrayWrapper so let's build a proxy + return getProxyForObject(value); + } + if (type == "function") { + if (XPCNativeWrapper.unwrap(value) !== value + || (typeof value.toString === "function" && + value.toString().match(/\[native code\]/))) { + return getProxyForFunction(value, NativeFunctionWrapper(value, obj, name)); + } + return value; + } + if (type == "string") + return value; + if (type == "number") + return value; + if (type == "boolean") + return value; + //console.log("return non-wrapped to wrapped : "+value); + return value; +} + +/* + * Wrap an object from the document to a proxy wrapper + */ +function getProxyForObject(obj) { + if (typeof obj != "object") { + let msg = "tried to proxify something other than an object: " + typeof obj; + console.warn(msg); + throw msg; + } + if ("__isWrappedProxy" in obj) { + return obj; + } + // Check if there is a proxy cached on this wrapper, + // but take care of prototype ___proxy attribute inheritance! + if (obj && obj.___proxy && obj.___proxy.valueOf(UNWRAP_ACCESS_KEY) === obj) { + return obj.___proxy; + } + + let proxy = Proxy.create(handlerMaker(obj)); + + Object.defineProperty(obj, "___proxy", {value : proxy, + writable : false, + enumerable : false, + configurable : false}); + return proxy; +} + +/* + * Wrap a function from the document to a proxy wrapper + */ +function getProxyForFunction(fun, callTrap) { + if (typeof fun != "function") { + let msg = "tried to proxify something other than a function: " + typeof fun; + console.warn(msg); + throw msg; + } + if ("__isWrappedProxy" in fun) + return obj; + if ("___proxy" in fun) + return fun.___proxy; + + let proxy = Proxy.createFunction(handlerMaker(fun), callTrap); + + Object.defineProperty(fun, "___proxy", {value : proxy, + writable : false, + enumerable : false, + configurable : false}); + + return proxy; +} + +/* + * Check if a DOM attribute name is an event name. + */ +function isEventName(id) { + if (id.indexOf("on") != 0 || id.length == 2) + return false; + // Taken from: + // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsDOMClassInfo.cpp#7616 + switch (id[2]) { + case 'a' : + return (id == "onabort" || + id == "onafterscriptexecute" || + id == "onafterprint"); + case 'b' : + return (id == "onbeforeunload" || + id == "onbeforescriptexecute" || + id == "onblur" || + id == "onbeforeprint"); + case 'c' : + return (id == "onchange" || + id == "onclick" || + id == "oncontextmenu" || + id == "oncopy" || + id == "oncut" || + id == "oncanplay" || + id == "oncanplaythrough"); + case 'd' : + return (id == "ondblclick" || + id == "ondrag" || + id == "ondragend" || + id == "ondragenter" || + id == "ondragleave" || + id == "ondragover" || + id == "ondragstart" || + id == "ondrop" || + id == "ondurationchange"); + case 'e' : + return (id == "onerror" || + id == "onemptied" || + id == "onended"); + case 'f' : + return id == "onfocus"; + case 'h' : + return id == "onhashchange"; + case 'i' : + return (id == "oninput" || + id == "oninvalid"); + case 'k' : + return (id == "onkeydown" || + id == "onkeypress" || + id == "onkeyup"); + case 'l' : + return (id == "onload" || + id == "onloadeddata" || + id == "onloadedmetadata" || + id == "onloadstart"); + case 'm' : + return (id == "onmousemove" || + id == "onmouseout" || + id == "onmouseover" || + id == "onmouseup" || + id == "onmousedown" || + id == "onmessage"); + case 'p' : + return (id == "onpaint" || + id == "onpageshow" || + id == "onpagehide" || + id == "onpaste" || + id == "onpopstate" || + id == "onpause" || + id == "onplay" || + id == "onplaying" || + id == "onprogress"); + case 'r' : + return (id == "onreadystatechange" || + id == "onreset" || + id == "onresize" || + id == "onratechange"); + case 's' : + return (id == "onscroll" || + id == "onselect" || + id == "onsubmit" || + id == "onseeked" || + id == "onseeking" || + id == "onstalled" || + id == "onsuspend"); + case 't': + return id == "ontimeupdate" + /* + // TODO: Make it work for mobile version + || + (nsDOMTouchEvent::PrefEnabled() && + (id == "ontouchstart" || + id == "ontouchend" || + id == "ontouchmove" || + id == "ontouchenter" || + id == "ontouchleave" || + id == "ontouchcancel"))*/; + + case 'u' : + return id == "onunload"; + case 'v': + return id == "onvolumechange"; + case 'w': + return id == "onwaiting"; + } + + return false; +} + +// XrayWrappers miss some attributes. +// Here is a list of functions that return a value when it detects a miss: +const NODES_INDEXED_BY_NAME = ["IMG", "FORM", "APPLET", "EMBED", "OBJECT"]; +const xRayWrappersMissFixes = [ + + // Fix bug with XPCNativeWrapper on HTMLCollection + // We can only access array item once, then it's undefined :o + function (obj, name) { + let i = parseInt(name); + if (obj.toString().match(/HTMLCollection|NodeList/) && + i >= 0 && i < obj.length) { + return wrap(XPCNativeWrapper(obj.wrappedJSObject[name]), obj, name); + } + return null; + }, + + // Trap access to document["form name"] + // that may refer to an existing form node + // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsDOMClassInfo.cpp#9285 + function (obj, name) { + if ("nodeType" in obj && obj.nodeType == 9) { + let node = obj.wrappedJSObject[name]; + // List of supported tag: + // http://mxr.mozilla.org/mozilla-central/source/content/html/content/src/nsGenericHTMLElement.cpp#1267 + if (node && NODES_INDEXED_BY_NAME.indexOf(node.tagName) != -1) + return wrap(XPCNativeWrapper(node)); + } + return null; + }, + + // Trap access to window["frame name"] and window.frames[i] + // that refer to an (i)frame internal window object + // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsDOMClassInfo.cpp#6824 + function (obj, name) { + if (typeof obj == "object" && "document" in obj) { + // Ensure that we are on a window object + try { + obj.QueryInterface(Ci.nsIDOMWindow); + } + catch(e) { + return null; + } + + // Integer case: + let i = parseInt(name); + if (i >= 0 && i in obj) { + return wrap(XPCNativeWrapper(obj[i])); + } + + // String name case: + if (name in obj.wrappedJSObject) { + let win = obj.wrappedJSObject[name]; + let nodes = obj.document.getElementsByName(name); + for (let i = 0, l = nodes.length; i < l; i++) { + let node = nodes[i]; + if ("contentWindow" in node && node.contentWindow == win) + return wrap(node.contentWindow); + } + } + } + return null; + }, + + // Trap access to form["node name"] + // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsDOMClassInfo.cpp#9477 + function (obj, name) { + if (typeof obj == "object" && obj.tagName == "FORM") { + let match = obj.wrappedJSObject[name]; + let nodes = obj.ownerDocument.getElementsByName(name); + for (let i = 0, l = nodes.length; i < l; i++) { + let node = nodes[i]; + if (node == match) + return wrap(node); + } + } + return null; + }, + + // Fix XPathResult's constants being undefined on XrayWrappers + // these constants are defined here: + // http://mxr.mozilla.org/mozilla-central/source/dom/interfaces/xpath/nsIDOMXPathResult.idl + // and are only numbers. + // The platform bug 665279 was fixed in Gecko 10.0a1. + // FIXME: remove this workaround once the SDK no longer supports Firefox 9. + function (obj, name) { + if (typeof obj == "object" && name in Ci.nsIDOMXPathResult) { + let value = Ci.nsIDOMXPathResult[name]; + if (typeof value == "number" && value === obj.wrappedJSObject[name]) + return value; + } + return null; + } + +]; + +// XrayWrappers have some buggy methods. +// Here is the list of them with functions returning some replacement +// for a given object `obj`: +const xRayWrappersMethodsFixes = { + // postMessage method is checking the Javascript global + // and it expects it to be a window object. + // But in our case, the global object is our sandbox global object. + // See nsGlobalWindow::CallerInnerWindow(): + // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsGlobalWindow.cpp#5893 + // nsCOMPtr win = do_QueryWrappedNative(wrapper); + // win is null + postMessage: function (obj) { + // Ensure that we are on a window object + try { + obj.QueryInterface(Ci.nsIDOMWindow); + } + catch(e) { + return null; + } + // Create a wrapper that is going to call `postMessage` through `eval` + let f = function postMessage(message, targetOrigin) { + message = message.toString().replace(/['\\]/g,"\\$&"); + targetOrigin = targetOrigin.toString().replace(/['\\]/g,"\\$&"); + + let jscode = "this.postMessage('" + message + "', '" + + targetOrigin + "')"; + return this.wrappedJSObject.eval(jscode); + }; + return getProxyForFunction(f, NativeFunctionWrapper(f)); + }, + + // Fix mozMatchesSelector uses that is broken on XrayWrappers + // when we use document.documentElement.mozMatchesSelector.call(node, expr) + // It's only working if we call mozMatchesSelector on the node itself. + // SEE BUG 658909: mozMatchesSelector returns incorrect results with XrayWrappers + mozMatchesSelector: function (obj) { + // Ensure that we are on an object to expose this buggy method + try { + // Bug 707576 removed nsIDOMNSElement. + // Can be simplified as soon as Firefox 11 become the minversion + obj.QueryInterface("nsIDOMElement" in Ci ? Ci.nsIDOMElement : + Ci.nsIDOMNSElement); + } + catch(e) { + return null; + } + // We can't use `wrap` function as `f` is not a native function, + // so wrap it manually: + let f = function mozMatchesSelector(selectors) { + return this.mozMatchesSelector(selectors); + }; + + return getProxyForFunction(f, NativeFunctionWrapper(f)); + }, + + // Bug 679054: History API doesn't work with Proxy objects. We have to pass + // regular JS objects on `pushState` and `replaceState` methods. + // In addition, the first argument has to come from the same compartment. + pushState: function (obj) { + // Ensure that we are on an object that expose History API + try { + obj.QueryInterface(Ci.nsIDOMHistory); + } + catch(e) { + return null; + } + let f = function fix() { + // Call native method with JSON objects + // (need to convert `arguments` to an array via `slice`) + return this.pushState.apply(this, JSON.parse(JSON.stringify(Array.slice(arguments)))); + }; + + return getProxyForFunction(f, NativeFunctionWrapper(f)); + }, + replaceState: function (obj) { + // Ensure that we are on an object that expose History API + try { + obj.QueryInterface(Ci.nsIDOMHistory); + } + catch(e) { + return null; + } + let f = function fix() { + // Call native method with JSON objects + // (need to convert `arguments` to an array via `slice`) + return this.replaceState.apply(this, JSON.parse(JSON.stringify(Array.slice(arguments)))); + }; + + return getProxyForFunction(f, NativeFunctionWrapper(f)); + } +}; + +/* + * Generate handler for proxy wrapper + */ +function handlerMaker(obj) { + // Overloaded attributes dictionary + let overload = {}; + // Expando attributes dictionary (i.e. onclick, onfocus, on* ...) + let expando = {}; + // Cache of methods overloaded to fix XrayWrapper bug + let methodFixes = {}; + return { + // Fundamental traps + getPropertyDescriptor: function(name) { + return Object.getOwnPropertyDescriptor(obj, name); + }, + defineProperty: function(name, desc) { + return Object.defineProperty(obj, name, desc); + }, + getOwnPropertyNames: function () { + return Object.getOwnPropertyNames(obj); + }, + delete: function(name) { + delete expando[name]; + delete overload[name]; + return delete obj[name]; + }, + + // derived traps + has: function(name) { + if (name == "___proxy") return false; + if (isEventName(name)) { + // XrayWrappers throw exception when we try to access expando attributes + // even on "name in wrapper". So avoid doing it! + return name in expando; + } + return name in obj || name in overload || name == "__isWrappedProxy" || + undefined !== this.get(null, name); + }, + hasOwn: function(name) { + return Object.prototype.hasOwnProperty.call(obj, name); + }, + get: function(receiver, name) { + if (name == "___proxy") + return undefined; + + // Overload toString in order to avoid returning "[XrayWrapper [object HTMLElement]]" + // or "[object Function]" for function's Proxy + if (name == "toString") { + if ("wrappedJSObject" in obj) { + // Bug 714778: we should not pass obj.wrappedJSObject.toString + // in order to avoid sharing its proxy over contents scripts: + return wrap(function () { + return obj.wrappedJSObject.toString.call( + this.valueOf(UNWRAP_ACCESS_KEY), arguments); + }, obj, name); + } + else { + return wrap(obj.toString, obj, name); + } + } + + // Offer a way to retrieve XrayWrapper from a proxified node through `valueOf` + if (name == "valueOf") + return function (key) { + if (key === UNWRAP_ACCESS_KEY) + return obj; + return this; + }; + + // Return overloaded value if there is one. + // It allows to overload native methods like addEventListener that + // are not saved, even on the wrapper itself. + // (And avoid some methods like toSource from being returned here! [__proto__ test]) + if (name in overload && + overload[name] != Object.getPrototypeOf(overload)[name] && + name != "__proto__") { + return overload[name]; + } + + // Catch exceptions thrown by XrayWrappers when we try to access on* + // attributes like onclick, onfocus, ... + if (isEventName(name)) { + //console.log("expando:"+obj+" - "+obj.nodeType); + return name in expando ? expando[name].original : undefined; + } + + // Overload some XrayWrappers method in order to fix its bugs + if (name in methodFixes && + methodFixes[name] != Object.getPrototypeOf(methodFixes)[name] && + name != "__proto__") + return methodFixes[name]; + if (Object.keys(xRayWrappersMethodsFixes).indexOf(name) !== -1) { + let fix = xRayWrappersMethodsFixes[name](obj); + if (fix) + return methodFixes[name] = fix; + } + + let o = obj[name]; + + // XrayWrapper miss some attributes, try to catch these and return a value + if (!o) { + for each(let atttributeFixer in xRayWrappersMissFixes) { + let fix = atttributeFixer(obj, name); + if (fix) + return fix; + } + } + + // Generic case + return wrap(o, obj, name); + + }, + + set: function(receiver, name, val) { + + if (isEventName(name)) { + //console.log("SET on* attribute : " + name + " / " + val + "/" + obj); + let shortName = name.replace(/^on/,""); + + // Unregister previously set listener + if (expando[name]) { + obj.removeEventListener(shortName, expando[name], true); + delete expando[name]; + } + + // Only accept functions + if (typeof val != "function") + return false; + + // Register a new listener + let original = val; + val = ContentScriptFunctionWrapper(val); + expando[name] = val; + val.original = original; + obj.addEventListener(name.replace(/^on/, ""), val, true); + return true; + } + + obj[name] = val; + + // Handle native method not overloaded on XrayWrappers: + // obj.addEventListener = val; -> obj.addEventlistener = native method + // And, XPCNativeWrapper bug where nested values appear to be wrapped: + // obj.customNestedAttribute = val -> obj.customNestedAttribute !== val + // obj.customNestedAttribute = "waive wrapper something" + // SEE BUG 658560: Fix identity problem with CrossOriginWrappers + // TODO: check that DOM can't be updated by the document itself and so overloaded value becomes wrong + // but I think such behavior is limited to primitive type + if ((typeof val == "function" || typeof val == "object") && name) { + overload[name] = val; + } + + return true; + }, + + enumerate: function() { + var result = []; + for each (let name in Object.keys(obj)) { + result.push(name); + }; + return result; + }, + + keys: function() { + return Object.keys(obj); + } + }; +}; + + +/* + * Wrap an object from the document to a proxy wrapper. + */ +function create(object) { + if ("wrappedJSObject" in object) + object = object.wrappedJSObject; + let xpcWrapper = XPCNativeWrapper(object); + // If we can't build an XPCNativeWrapper, it doesn't make sense to build + // a proxy. All proxy code is based on having such wrapper that store + // different JS attributes set. + // (we can't build XPCNativeWrapper when object is from the same + // principal/domain) + if (object === xpcWrapper) { + return object; + } + return getProxyForObject(xpcWrapper); +} diff --git a/tools/addon-sdk-1.5/packages/api-utils/data/test-content-symbiont.js b/tools/addon-sdk-1.5/packages/api-utils/data/test-content-symbiont.js new file mode 100644 index 0000000..65a2a21 --- /dev/null +++ b/tools/addon-sdk-1.5/packages/api-utils/data/test-content-symbiont.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/. */ + +// test-content-symbiont diff --git a/tools/addon-sdk-1.5/packages/api-utils/data/test-message-manager.js b/tools/addon-sdk-1.5/packages/api-utils/data/test-message-manager.js new file mode 100644 index 0000000..d647bd8 --- /dev/null +++ b/tools/addon-sdk-1.5/packages/api-utils/data/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/tools/addon-sdk-1.5/packages/api-utils/data/test-trusted-document.html b/tools/addon-sdk-1.5/packages/api-utils/data/test-trusted-document.html new file mode 100644 index 0000000..5845cf5 --- /dev/null +++ b/tools/addon-sdk-1.5/packages/api-utils/data/test-trusted-document.html @@ -0,0 +1,17 @@ + + + + + Worker test + + +

Lorem ipsum dolor sit amet.

+ + + diff --git a/tools/addon-sdk-1.5/packages/api-utils/docs/api-utils.md b/tools/addon-sdk-1.5/packages/api-utils/docs/api-utils.md new file mode 100644 index 0000000..aee88da --- /dev/null +++ b/tools/addon-sdk-1.5/packages/api-utils/docs/api-utils.md @@ -0,0 +1,157 @@ + + + + + +The `api-utils` module provides some helpers useful to the SDK's high-level API +implementations. + +Introduction +------------ + +The SDK high-level API design guidelines make a number of recommendations. +This module implements some of those patterns so that your own implementations +don't need to reinvent them. + +For example, public constructors should be callable both with and without the +`new` keyword. Your module can implement this recommendation using the +`publicConstructor` function. + +Options objects or "dictionaries" are also common throughout the high-level +APIs. The guidelines recommend that public constructors should generally define +a single `options` parameter rather than defining many parameters. Since one of +the SDK's principles is to be friendly to developers, ideally all properties on +options dictionaries should be checked for correct type, and informative error +messages should be generated when clients make mistakes. With the +`validateOptions` function, your module can easily do so. + +And objects are sometimes iterable over a custom set of key/value pairs. +Such objects should have custom iterators that let consumers iterate keys, +values, or [key, value] pairs. The `addIterator` function makes it easy to do +so in a way that is consistent with the behavior of default iterators during +`for...in`, `for each...in`, and `for...in Iterator()` loops. + + +@function +Returns a function *C* that creates an instance of `privateConstructor`. *C* +may be called with or without the `new` keyword. + +The prototype of each instance returned from *C* is *C*.`prototype`, and +*C*.`prototype` is an object whose prototype is +`privateConstructor.prototype`. Instances returned from *C* are therefore +instances of both *C* and `privateConstructor`. + +Additionally, the constructor of each instance returned from *C* is *C*. + +Instances returned from *C* are automatically memory tracked using +`memory.track` under the bin name `privateConstructor.name`. + +**Example** + + function MyObject() {} + exports.MyObject = apiUtils.publicConstructor(MyObject); + +@returns {function} +A function that makes new instances of `privateConstructor`. + +@param privateConstructor {constructor} + + + +@function +A function to validate an options dictionary according to the specified +constraints. + +`map`, `is`, and `ok` are used in that order. + +The return value is 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. + +**Examples** + +A typical use: + + var opts = { foo: 1337 }; + var requirements = { + foo: { + map: function (val) val.toString(), + is: ["string"], + ok: function (val) val.length > 0, + msg: "foo must be a non-empty string." + } + }; + var validatedOpts = apiUtils.validateOptions(opts, requirements); + // validatedOpts == { foo: "1337" } + +If the key `foo` is optional and doesn't need to be mapped: + + var opts = { foo: 1337 }; + var validatedOpts = apiUtils.validateOptions(opts, { foo: {} }); + // validatedOpts == { foo: 1337 } + + opts = {}; + validatedOpts = apiUtils.validateOptions(opts, { foo: {} }); + // validatedOpts == {} + +@returns {object} +A validated options dictionary given some requirements. If any of the +requirements are not met, an exception is thrown. + +@param options {object} +The options dictionary to validate. It's not modified. If it's null or +otherwise falsey, an empty object is assumed. + +@param requirements {object} +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. The keys of that object are the following, and each is optional: + +@prop [map] {function} +A function that's passed the value of the key in the `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 is caught and discarded, +and the key's value is its value in `options`. + +@prop [is] {array} +An array containing the number of `typeof` type names. If the key's value is +none of these types it fails validation. Arrays and nulls are identified by +the special type names "array" and "null"; "object" will not match either. +No type coercion is done. + +@prop [ok] {function} +A function that is passed the key's value. If it returns false, the value +fails validation. + +@prop [msg] {string} +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. + + + +@function +Adds an iterator to the specified object that iterates keys, values, +or [key, value] pairs depending on how it is invoked, i.e.: + + for (var key in obj) { ... } // iterate keys + for each (var val in obj) { ... } // iterate values + for (var [key, val] in Iterator(obj)) { ... } // iterate pairs + +If your object only iterates either keys or values, you don't need this +function. Simply assign a generator function that iterates the keys/values +to your object's `__iterator__` property instead, f.e.: + + obj.__iterator__ = function () { for each (var i in items) yield i; } + +@param obj {object} +the object to which to add the iterator + +@param keysValsGen {function} +a generator function that yields [key, value] pairs + diff --git a/tools/addon-sdk-1.5/packages/api-utils/docs/app-strings.md b/tools/addon-sdk-1.5/packages/api-utils/docs/app-strings.md new file mode 100644 index 0000000..c40ddaf --- /dev/null +++ b/tools/addon-sdk-1.5/packages/api-utils/docs/app-strings.md @@ -0,0 +1,65 @@ + + +The `app-strings` module gives you access to the host application's localized +string bundles (`.properties` files). + +The module exports the `StringBundle` constructor function. To access a string +bundle, construct an instance of `StringBundle`, passing it the bundle's URL: + + var StringBundle = require("app-strings").StringBundle; + var bundle = StringBundle("chrome://browser/locale/browser.properties"); + +To get the value of a string, call the object's `get` method, passing it +the name of the string: + + var accessKey = bundle.get("contextMenuSearchText.accesskey"); + // "S" in the en-US locale + +To get the formatted value of a string that accepts arguments, call the object's +`get` method, passing it the name of the string and an array of arguments +with which to format the string: + + var searchText = bundle.get("contextMenuSearchText", + ["universe", "signs of intelligent life"]); + // 'Search universe for "signs of intelligent life"' in the en-US locale + +To get all strings in the bundle, iterate the object, which returns arrays +of the form [name, value]: + + for (var [name, value] in Iterator(bundle)) + console.log(name + " = " + value); + +Iteration +--------- + +for (var name in bundle) { ... } + +Iterate the names of strings in the bundle. + +for each (var val in bundle) { ... } + +Iterate the values of strings in the bundle. + +for (var [name, value] in Iterator(bundle)) { ... } + +Iterate the names and values of strings in the bundle. + + + +@class +The `StringBundle` object represents a string bundle. + +@constructor +Creates a StringBundle object that gives you access to a string bundle. +@param url {string} the URL of the string bundle +@returns {StringBundle} the string bundle + + +@method Get the value of the string with the given name. +@param [name] {string} the name of the string to get +@param [args] {array} (optional) strings that replace placeholders in the string +@returns {string} the value of the string + + diff --git a/tools/addon-sdk-1.5/packages/api-utils/docs/base.md b/tools/addon-sdk-1.5/packages/api-utils/docs/base.md new file mode 100644 index 0000000..7a694f2 --- /dev/null +++ b/tools/addon-sdk-1.5/packages/api-utils/docs/base.md @@ -0,0 +1,207 @@ + + +### Inheritance ### + +Doing [inheritance in JavaScript](https://developer.mozilla.org/en/Introduction_to_Object-Oriented_JavaScript) +is both verbose and painful. Reading or writing such code requires requires +sharp eye and lot's of discipline, mainly due to code fragmentation and lots of +machinery exposed: + + // Defining a simple Class + function Dog(name) { + // Classes are for creating instances, calling them without `new` changes + // behavior, which in majority cases you need to handle, so you end up + // with additional boilerplate. + if (!(this instanceof Dog)) return new Dog(name); + + this.name = name; + }; + // To define methods you need to make a dance with a special 'prototype' + // property of the constructor function. This is too much machinery exposed. + Dog.prototype.type = 'dog'; + Dog.prototype.bark = function bark() { + return 'Ruff! Ruff!' + }; + + // Subclassing a `Dog` + function Pet(name, breed) { + // Once again we do our little dance + if (!(this instanceof Pet)) return new Pet(name, breed); + + Dog.call(this, name); + this.breed = breed; + } + // To subclass, you need to make another special dance with special + // 'prototype' properties. + Pet.prototype = Object.create(Dog.prototype); + // If you want correct instanceof behavior you need to make a dance with + // another special `constructor` property of the `prototype` object. + Object.defineProperty(Pet.prototype, 'contsructor', { value: Pet }); + // Finally you can define some properties. + Pet.prototype.call = function(name) { + return this.name === name ? this.bark() : ''; + }; + +An "exemplar" is a factory for instances. Usually exemplars are defined as +(constructor) functions as in examples above. But that does not necessary has +to be the case. Prototype (object) can form far more simpler exemplars. After +all what could be more object oriented than objects that inherit from objects. + + var Dog = { + new: function(name) { + var instance = Object.create(this); + this.initialize.apply(instance, arguments); + return instance; + }, + initialize: function initialize(name) { + this.name = name; + }, + type: 'dog', + bark: function bark() { + return 'Ruff! Ruff!' + } + }; + var fluffy = Dog.new('fluffy'); + + + var Pet = Object.create(Dog); + Pet.initialize = function initialize(name, breed) { + Dog.initialize.call(this, name); + this.breed = breed; + }; + Pet.call = function call(name) { + return this.name === name ? this.bark() : ''; + }; + +While this small trick solves some readability issues, there are still more. To +address them this module exports `Base` exemplar with few methods predefined: + + var Dog = Base.extend({ + initialize: function initialize(name) { + this.name = name; + }, + type: 'dog', + bark: function bark() { + return 'Ruff! Ruff!' + } + }); + + var Pet = Dog.extend({ + initialize: function initialize(name, breed) { + Dog.initialize.call(this, name); + this.breed = breed; + }, + function call(name) { + return this.name === name ? this.bark() : ''; + } + }); + + var fluffy = Dog.new('fluffy'); + dog.bark(); // 'Ruff! Ruff!' + Dog.isPrototypeOf(fluffy); // true + Pet.isPrototypeOf(fluffy); // true + +### Composition ### + +Even though (single) inheritance is very powerful it's not always enough. +Sometimes it's more useful suitable to define reusable pieces of functionality +and then compose bigger pieces out of them: + + var HEX = Base.extend({ + hex: function hex() { + return '#' + this.color + } + }) + + var RGB = Base.extend({ + 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 = Base.extend(RGB, { + 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) + } + }) + + // Composing `Color` prototype out of reusable components: + var Color = Base.extend(HEX, RGB, CMYK, { + initialize: function initialize(color) { + this.color = color + } + }) + + var pink = Color.new('FFC0CB') + // RGB + pink.red() // 255 + pink.green() // 192 + pink.blue() // 203 + + // CMYK + pink.magenta() // 0.2471 + pink.yellow() // 0.2039 + pink.cyan() // 0.0000 + +### Combining composition & inheritance ### + +Also it's easy to mix composition with inheritance: + + var Pixel = Color.extend({ + initialize: function initialize(x, y, color) { + Color.initialize.call(this, color) + this.x = x + this.y = y + }, + toString: function toString() { + return this.x + ':' + this.y + '@' + this.hex() + } + }); + + var pixel = Pixel.new(11, 23, 'CC3399'); + pixel.toString() // 11:23@#CC3399 + Pixel.isPrototypeOf(pixel) // true + + // Pixel instances inhertis from `Color` + Color.isPrototypeOf(pixel); // true + + // In fact `Pixel` itself inherits from `Color`, remember just simple and + // pure prototypal inheritance where object inherit from objects. + Color.isPrototypeOf(Pixel); // true + +### Classes ### + +Module exports `Class` function. `Class` takes argument of exemplar object +extending `Base` and returns `constructor` function that can be used for +simulating classes defined by given exemplar. + + var CPixel = Class(Pixel); + var pixel = CPixel(11, 12, '000000'); + pixel instanceof CPixel // true + Pixel.prototypeOf(pixel); // true + + // Use of `new` is optional, but possible. + var p2 = CPixel(17, 2, 'cccccc'); + p2 instanceof CPixel // true + p2.prototypeOf(pixel); // true diff --git a/tools/addon-sdk-1.5/packages/api-utils/docs/byte-streams.md b/tools/addon-sdk-1.5/packages/api-utils/docs/byte-streams.md new file mode 100644 index 0000000..1238cd8 --- /dev/null +++ b/tools/addon-sdk-1.5/packages/api-utils/docs/byte-streams.md @@ -0,0 +1,68 @@ + + + + + +The `byte-streams` module provides streams for reading and writing bytes. + + +@class + +@constructor + Creates a binary input stream that reads bytes from a backing stream. +@param inputStream {stream} + The backing stream, an nsIInputStream. + + +@property {boolean} + True if the stream is closed. + + + +@method + Closes both the stream and its backing stream. If the stream is already + closed, an exception is thrown. + + + +@method + Reads a string from the stream. If the stream is closed, an exception is + thrown. +@param [numBytes] {number} + The number of bytes to read. If not given, the remainder of the entire stream + is read. +@returns {string} + A string containing the bytes read. If the stream is at the end, returns the + empty string. + + + + +@class + +@constructor + Creates a binary output stream that writes bytes to a backing stream. +@param outputStream {stream} + The backing stream, an nsIOutputStream. + + +@property {boolean} + True if the stream is closed. + + +@method + Closes both the stream and its backing stream. If the stream is already + closed, an exception is thrown. + + +@method + Writes a string to the stream. If the stream is closed, an exception is + thrown. +@param str {string} + The string to write. + + diff --git a/tools/addon-sdk-1.5/packages/api-utils/docs/collection.md b/tools/addon-sdk-1.5/packages/api-utils/docs/collection.md new file mode 100644 index 0000000..1e557c6 --- /dev/null +++ b/tools/addon-sdk-1.5/packages/api-utils/docs/collection.md @@ -0,0 +1,77 @@ + + + + + +The `collection` module provides a simple list-like class and utilities for +using it. A collection is ordered, like an array, but its items are unique, +like a set. + + +@class +A collection object provides for...in-loop iteration. Items are yielded in the +order they were added. For example, the following code... + + var collection = require("collection"); + var c = new collection.Collection(); + c.add(1); + c.add(2); + c.add(3); + for (item in c) + console.log(item); + +... would print this to the console: + +
+  1
+  2
+  3
+
+ +Iteration proceeds over a copy of the collection made before iteration begins, +so it is safe to mutate the collection during iteration; doing so does not +affect the results of the iteration. + + +@constructor +Creates a new collection. The collection is backed by an array. +@param [array] {array} +If *array* is given, it will be used as the backing array. This way the caller +can fully control the collection. Otherwise a new empty array will be used, and +no one but the collection will have access to it. + + +@property {number} +The number of items in the collection array. + + +@method +Adds a single item or an array of items to the collection. Any items already +contained in the collection are ignored. +@param itemOrItems {object} An item or array of items. +@returns {Collection} The Collection. + + +@method +Removes a single item or an array of items from the collection. Any items not +contained in the collection are ignored. +@param itemOrItems {object} An item or array of items. +@returns {Collection} The Collection. + +
+ + +@function +Adds a collection property to the given object. Setting the property to a +scalar value empties the collection and adds the value. Setting it to an array +empties the collection and adds all the items in the array. +@param object {object} +The property will be defined on this object. +@param propName {string} +The name of the property. +@param [backingArray] {array} +If given, this will be used as the collection's backing array. + + diff --git a/tools/addon-sdk-1.5/packages/api-utils/docs/content.md b/tools/addon-sdk-1.5/packages/api-utils/docs/content.md new file mode 100644 index 0000000..30a1cf3 --- /dev/null +++ b/tools/addon-sdk-1.5/packages/api-utils/docs/content.md @@ -0,0 +1,15 @@ + + + + +The `content` module exports three different traits [Loader][], [Worker][] and +[Symbiont][]. None of this traits is intended to be used directly by programs. +Rather, they are intended to be used by other modules that provide high +level APIs to programs or libraries. + +[Loader]:packages/api-utils/docs/content/loader.html +[Worker]:packages/api-utils/docs/content/worker.html +[Symbiont]:packages/api-utils/docs/content/symbiont.html + diff --git a/tools/addon-sdk-1.5/packages/api-utils/docs/content/loader.md b/tools/addon-sdk-1.5/packages/api-utils/docs/content/loader.md new file mode 100644 index 0000000..095e8c8 --- /dev/null +++ b/tools/addon-sdk-1.5/packages/api-utils/docs/content/loader.md @@ -0,0 +1,92 @@ + + + + +Loader is base trait and it provides set of core properties and associated +validations. Trait is useful for all the compositions providing high level +APIs for creating JavaScript contexts that can access web content. + +Loader is composed from the +[EventEmitter](packages/api-utils/docs/events.html) trait, therefore +instances of Loader and their descendants expose all the public properties +exposed by EventEmitter along with additional public properties: + +Value changes on all of the above mentioned properties emit `propertyChange` +events on an instances. + +**Example:** + +The following code creates a wrapper on hidden frame that reloads a web page +in frame every time `contentURL` property is changed: + + var hiddenFrames = require("hidden-frame"); + var { Loader } = require("content"); + var PageLoader = Loader.compose({ + constructor: function PageLoader(options) { + options = options || {}; + if (options.contentURL) + this.contentURL = options.contentURL; + this.on('propertyChange', this._onChange = this._onChange.bind(this)); + let self = this; + hiddenFrames.add(hiddenFrames.HiddenFrame({ + onReady: function onReady() { + let frame = self._frame = this.element; + self._emit('propertyChange', { contentURL: self.contentURL }); + } + })); + }, + _onChange: function _onChange(e) { + if ('contentURL' in e) + this._frame.setAttribute('src', this._contentURL); + } + }); + + +@class + +@property {array} +The local file URLs of content scripts to load. Content scripts specified by +this property are loaded *before* those specified by the `contentScript` +property. + + + +@property {array} +The texts of content scripts to load. Content scripts specified by this +property are loaded *after* those specified by the `contentScriptFile` property. + + + +@property {string} +When to load the content scripts. This may take one of the following +values: + +* "start": load content scripts immediately after the document +element for the page is inserted into the DOM, but before the DOM content +itself has been loaded +* "ready": load content scripts once DOM content has been loaded, +corresponding to the +[DOMContentLoaded](https://developer.mozilla.org/en/Gecko-Specific_DOM_Events) +event +* "end": load content scripts once all the content (DOM, JS, CSS, +images) for the page has been loaded, at the time the +[window.onload event](https://developer.mozilla.org/en/DOM/window.onload) +fires + + + + +@property {string} +The URL of the content loaded. + + + +@property {object} +Permissions for the content, with the following keys: +@prop script {boolean} + Whether or not to execute script in the content. Defaults to true. + + + diff --git a/tools/addon-sdk-1.5/packages/api-utils/docs/content/proxy.md b/tools/addon-sdk-1.5/packages/api-utils/docs/content/proxy.md new file mode 100644 index 0000000..095e8ff --- /dev/null +++ b/tools/addon-sdk-1.5/packages/api-utils/docs/content/proxy.md @@ -0,0 +1,241 @@ + + + + +Content scripts need access to the DOM of the pages they are attached to. +However, those pages should be considered to be hostile environments: we +have no control over any other scripts loaded by the web page that may be +executing in the same context. If the content scripts and scripts loaded +by the web page were to access the same DOM objects, there are two possible +security problems: + +First, a malicious page might redefine functions and properties of DOM +objects so they don't do what the add-on expects. For example, if a +content script calls `document.getElementById()` to retrieve a DOM +element, then a malicious page could redefine its behavior to return +something unexpected: + +

+// If the web document contains the following script:
+document.getElementById = function (str) {
+  // Overload indexOf method of all string instances
+  str.__proto__.indexOf = function () {return -1;};
+  // Overload toString method of all object instances
+  str.__proto__.__proto__.toString = function () {return "evil";};
+};
+// After the following line, the content script will be compromised:
+var node = document.getElementById("element");
+// Then your content script is totally out of control.
+
+ +Second, changes the content script made to the DOM objects would be visible +to the page, leaking information to it. + +The general approach to fixing these problems is to wrap DOM objects in +[`XrayWrappers`](https://developer.mozilla.org/en/XPCNativeWrapper) +(also know as `XPCNativeWrapper`). This guarantees that: + +* when the content script accesses DOM properties and functions it gets the +original native version of them, ignoring any modifications made by the web +page +* changes to the DOM made by the content script are not visible to scripts +running in the page. + +However, `XrayWrapper` has some limitations and bugs, which break many +popular web frameworks. In particular, you can't: + +* define attributes like `onclick`: you have to use `addEventListener` syntax +* overload native methods on DOM objects, like this: +

+proxy.addEventListener = function () {};
+
+* access named elements using properties like `window[framename]` or +`document[formname]` +* use some other features that have bugs in the `XrayWrapper` +implementation, like `mozMatchesSelector` + +The `proxy` module uses `XrayWrapper` in combination with the +experimental +[Proxy API](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Proxy) +to address both the security vulnerabilities of content scripts and the +limitations of `XrayWrapper`. + +
+  /--------------------\                           /------------------------\
+  |    Web document    |                           | Content script sandbox |
+  | http://mozilla.org |                           |     data/worker.js     |
+  |                    | require('content-proxy'). |                        |
+  | window >-----------|-     create(window)      -|-> window               |
+  \--------------------/                           \------------------------/
+
+ + +## The Big Picture ## + +The implementation defines two different kinds of proxy: + + 1. Content script proxies that wrap DOM objects that are exposed to + content scripts as described above. + 2. XrayWrapper proxies that wrap objects from content scripts before handing + them over to XrayWrapper functions. These proxies are internal + and are not exposed to content scripts or document content. + +
+  /--------------------\                           /------------------------\
+  |    Web document    |                           | Content script sandbox |
+  | http://mozilla.org |                           |     data/worker.js     |
+  |                    |                   /-------|-> myObject = {}        |
+  |                    |  /----------------v--\    |                        |
+  |                    |  | XrayWrapper Proxy |    | - document             |
+  |                    |  \---------v---------/    \----^-------------------/
+  |                    |            v                   |
+  |                    |  /-------------\  /----------\ |
+  | - document >-------|->| XrayWrapper |<-| CS proxy |-/
+  \--------------------/  \-------------/  \----------/
+
+ +Everything begins with a single call to the `create` function exported by the +content-proxy module: + + // Retrieve the unwrapped reference to the current web page window object + var win = gBrowser.contentDocument.defaultView.wrappedJSObject; + // Or in addon sdk style + var win = require("tab-browser").activeTab.linkedBrowser.contentWindow.wrappedJSObject; + // Now create a content script proxy for the window object + var windowProxy = require("api-utils/content/content-proxy").create(win); + + // We finally use this window object as sandbox prototype, + // so that all web page globals are accessible in CS too: + var contentScriptSandbox = new Cu.Sandbox(win, { + sandboxPrototype: windowProxy + }); + +Then all other proxies are created from this one. Attempts to access DOM +attributes of this proxy are trapped, and the proxy constructs and returns +content script proxies for those attributes: + + // For example, if you simply do this: + var document = window.document; + // accessing the `document` attribute will be trapped by the `window` content script + // proxy, and that proxy will that create another content script proxy for `document` + +So the main responsibility of the content script proxy implementation is to +ensure that we always return content script proxies to the content script. + +## Internal Implementation ## + +Each content script proxy keeps a reference to the `XrayWrapper` that enables +it to be sure of calling native DOM methods. + +There are two internal functions to convert between content script proxy +values and `XrayWrapper` values. + +1. __`wrap`__ takes an XrayWrapper value and wraps it in a content script +proxy if needed. + This method is called when: + * a content script accesses an attribute of a content script proxy. + * XrayWrapper code calls a callback function defined in the content +script, so that arguments passed into the function by the XrayWrapper are +converted into content script proxies. For example, if a content script +calls `addEventListener`, then the listener function will expect any arguments +to be content script proxies. +

+2. __`unwrap`__ takes an object coming from the content script context and: + * if the object is a content script proxy, unwraps it back to an +XrayWrapper reference + * if the object is not a content script proxy, wraps it in an XrayWrapper +proxy. +

+This means we can call a XrayWrapper method either with: + + * a raw XrayWrapper object. + + // The following line doesn't work if child is a content script proxy, + // it has to be a raw XrayWrapper reference + xrayWrapper.appendChild(child) + + * an XrayWrapper proxy when you pass a custom object from the content +script context. + + var myListener = { + handleEvent: function(event) { + // `event` should be a content script proxy + } + }; + // `myListener` has to be another kind of Proxy: XrayWrapper proxy, + // that aims to catch the call to `handleEvent` in order to wrap its + // arguments in a content script proxy. + xrayWrapper.addEventListener("click", myListener, false); + + +## Stack Traces ## + +The following code: + + function listener(event) { + + } + csProxy.addEventListener("message", listener, false); + +generates the following internal calls: + + -> CS Proxy:: get("addEventListener") + -> wrap(xrayWrapper.addEventListener) + -> NativeFunctionWrapper(xrayWrapper.addEventListener) + // NativeFunctionWrapper generates: + function ("message", listener, false) { + return xraywrapper.addEventListener("message", unwrap(listener), false); + } + -> unwrap(listener) + -> ContentScriptFunctionWrapper(listener) + // ContentScriptFunctionWrapper generates: + function (event) { + return listener(wrap(event)); + } + +
+ + // First, create an object from content script context + var myListener = { + handleEvent: function (event) { + + } + }; + // Then, pass this object as an argument to a CS proxy method + window.addEventListener("message", myListener, false); + + // Generates the following internal calls: + -> CS Proxy:: get("addEventListener") + -> wrap(xrayWrapper.addEventListener) + -> NativeFunctionWrapper(xrayWrapper.addEventListener) + // Generate the following function: + function ("message", myListener, false) { + return xraywrapper.addEventListener("message", unwrap(myListener), false); + } + -> unwrap(myListener) + -> ContentScriptObjectWrapper(myListener) + // Generate an XrayWrapper proxy and give it to xrayWrapper method. + // Then when native code fires an event, the proxy will catch it: + -> XrayWrapper Proxy:: get("handleEvent") + -> unwrap(myListener.handleEvent) + -> ContentScriptFunctionWrapper(myListener.handleEvent) + // Generate following function: + function (event) { + return myListener.handleEvent(wrap(event)); + } + + + +@function + Create a content script proxy.
+ Doesn't create a proxy if we are not able to create a XrayWrapper for + this object: for example, if the object comes from system principal. + +@param object {Object} + The object to proxify. + +@returns {Object} + A content script proxy that wraps `object`. +
diff --git a/tools/addon-sdk-1.5/packages/api-utils/docs/content/symbiont.md b/tools/addon-sdk-1.5/packages/api-utils/docs/content/symbiont.md new file mode 100644 index 0000000..3036c6d --- /dev/null +++ b/tools/addon-sdk-1.5/packages/api-utils/docs/content/symbiont.md @@ -0,0 +1,140 @@ + + + + + + +This module is not intended to be used directly by programs. Rather, it is +intended to be used by other modules that provide APIs to programs. + + +This module exports `Symbiont` trait that can be used for creating JavaScript +contexts that can access web content in host application frames (i.e. XUL +`