aboutsummaryrefslogtreecommitdiff
path: root/tools/addon-sdk-1.7/packages/api-utils
diff options
context:
space:
mode:
Diffstat (limited to 'tools/addon-sdk-1.7/packages/api-utils')
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/README.md35
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/data/content-proxy.js847
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/data/test-content-symbiont.js5
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/data/test-message-manager.js6
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/data/test-trusted-document.html17
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/data/worker.js247
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/api-utils.md157
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/app-strings.md65
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/base.md207
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/byte-streams.md68
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/collection.md77
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/content.md15
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/content/loader.md92
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/content/proxy.md241
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/content/symbiont.md140
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/content/worker.md130
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/cortex.md160
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/cuddlefish.md7
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/environment.md43
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/errors.md42
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/event/core.md51
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/event/target.md95
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/events.md78
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/file.md151
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/frame/utils.md53
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/globals.md100
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/hidden-frame.md83
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/httpd.md31
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/light-traits.md295
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/list.md98
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/match-pattern.md246
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/memory.md7
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/message-manager.md13
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/namespace.md71
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/observer-service.md73
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/plain-text-console.md7
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/preferences-service.md116
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/promise.md394
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/querystring.md37
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/runtime.md75
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/sandbox.md51
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/tab-browser.md140
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/text-streams.md102
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/traceback.md66
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/traits.md244
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/unit-test.md393
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/unload.md61
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/url.md85
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/uuid.md27
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/window-utils.md88
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/window/utils.md90
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/xhr.md95
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/xpcom.md216
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/docs/xul-app.md76
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/api-utils.js153
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/app-strings.js63
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/array.js69
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/base.js177
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/byte-streams.js102
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/channel.js46
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/collection.js108
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/content.js11
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/content/loader.js179
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/content/symbiont.js183
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/content/worker.js491
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/cortex.js109
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/cuddlefish.js302
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/dom/events.js136
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/dom/events/keys.js60
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/env!.js20
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/environment.js54
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/errors.js60
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/event/core.js147
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/event/target.js76
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/events.js178
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/events/assembler.js53
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/file.js192
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/find-tests.js5
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/frame/utils.js59
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/functional.js159
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/globals!.js93
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/hidden-frame.js166
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/httpd.js5166
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/keyboard/hotkeys.js107
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/keyboard/observer.js53
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/keyboard/utils.js186
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/l10n/locale.js122
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/l10n/plural-rules.js387
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/light-traits.js596
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/list.js114
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/match-pattern.js103
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/memory.js114
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/message-manager.js203
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/namespace.js43
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/observer-service.js175
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/passwords/utils.js101
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/plain-text-console.js82
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/preferences-service.js124
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/process.js76
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/promise.js219
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/querystring.js117
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/runtime.js15
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/sandbox.js46
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/self!.js48
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/system.js120
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/tab-browser.js727
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/tabs/events.js26
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/tabs/observer.js93
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/tabs/tab.js264
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/tabs/utils.js59
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/test.js108
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/test/assert.js331
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/text-streams.js240
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/timer.js78
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/traceback.js123
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/traits.js183
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/traits/core.js317
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/type.js340
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/unit-test-finder.js76
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/unit-test.js440
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/unload.js63
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/url.js113
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/utils/data.js71
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/utils/object.js46
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/utils/registry.js57
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/utils/thumbnail.js43
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/uuid.js13
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/window-utils.js278
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/window/utils.js110
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/windows/dom.js27
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/windows/loader.js120
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/windows/observer.js53
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/windows/tabs.js178
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/xhr.js150
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/xpcom.js207
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/lib/xul-app.js63
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/package.json14
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/commonjs-test-adapter/asserts.js54
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/fixtures/es5.js8
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/fixtures/sandbox-complex-character.js5
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/fixtures/sandbox-normal.js7
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/helpers.js24
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/loader/fixture.js6
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/add.js9
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/async1.js14
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/async2.js8
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/badExportAndReturn.js10
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/badFirst.js9
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/badSecond.js8
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/blue.js9
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/castor.js10
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/cheetah.js9
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/color.js7
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/dupe.js15
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/dupeNested.js15
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/dupeSetExports.js8
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/exportsEquals.js5
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/green.js10
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/lion.js7
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/orange.js10
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/pollux.js10
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/red.js16
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/setExports.js5
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/subtract.js9
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/tiger.js8
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/traditional1.js12
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/traditional2.js6
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/modules/types/cat.js5
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-api-utils.js241
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-app-strings.js62
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-array.js40
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-base.js240
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-byte-streams.js169
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-collection.js127
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-commonjs-test-adapter.js11
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-content-loader.js226
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-content-proxy.js807
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-content-symbiont.js190
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-content-worker.js465
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-cortex.js122
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-dom.js88
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-environment.js50
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-errors.js70
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-event-core.js217
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-event-target.js167
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-events.js267
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-file.js273
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-frame-utils.js67
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-functional.js170
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-globals.js22
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-hidden-frame.js51
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-httpd.js72
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-keyboard-observer.js36
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-keyboard-utils.js62
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-l10n-locale.js141
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-l10n-plural-rules.js82
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-light-traits.js11
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-list.js207
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-loader.js35
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-match-pattern.js127
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-memory.js19
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-message-manager.js600
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-modules.js148
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-namespace.js95
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-observer-service.js77
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-passwords-utils.js142
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-plain-text-console.js68
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-preferences-service.js112
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-process.js36
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-promise.js311
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-querystring.js206
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-registry.js80
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-require.js29
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-sandbox.js113
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-self.js54
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-set-exports.js35
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-tab-browser.js493
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-tab-observer.js39
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-tab.js110
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-text-streams.js156
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-timer.js131
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-traceback.js118
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-traits-core.js838
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-traits.js398
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-type.js92
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-unit-test.js251
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-unload.js161
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-url.js204
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-uuid.js27
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-window-loader.js120
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-window-observer.js48
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-window-utils.js321
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-window-utils2.js67
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-xhr.js75
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-xpcom.js217
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/test-xul-app.js45
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/traits/assert.js98
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/traits/descriptor-tests.js335
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/traits/inheritance-tests.js104
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/traits/object-tests.js321
-rw-r--r--tools/addon-sdk-1.7/packages/api-utils/tests/traits/utils.js56
241 files changed, 35391 insertions, 0 deletions
diff --git a/tools/addon-sdk-1.7/packages/api-utils/README.md b/tools/addon-sdk-1.7/packages/api-utils/README.md
new file mode 100644
index 0000000..55adb57
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/README.md
@@ -0,0 +1,35 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+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.7/packages/api-utils/data/content-proxy.js b/tools/addon-sdk-1.7/packages/api-utils/data/content-proxy.js
new file mode 100644
index 0000000..f0a39d3
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/data/content-proxy.js
@@ -0,0 +1,847 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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;
+
+ // Bug 715755: do not proxify COW wrappers
+ // These wrappers throw an exception when trying to access
+ // any attribute that is not in a white list
+ try {
+ ("nonExistantAttribute" in value);
+ }
+ catch(e) {
+ if (e.message.indexOf("Permission denied to access property") !== -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<nsPIDOMWindow> 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") {
+ // Bug 714778: we should not pass obj.wrappedJSObject.toString
+ // in order to avoid sharing its proxy between two contents scripts.
+ // (not that `unwrappedObj` can be equal to `obj` when `obj` isn't
+ // an xraywrapper)
+ let unwrappedObj = XPCNativeWrapper.unwrap(obj);
+ return wrap(function () {
+ return unwrappedObj.toString.call(
+ this.valueOf(UNWRAP_ACCESS_KEY), arguments);
+ }, 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.7/packages/api-utils/data/test-content-symbiont.js b/tools/addon-sdk-1.7/packages/api-utils/data/test-content-symbiont.js
new file mode 100644
index 0000000..65a2a21
--- /dev/null
+++ b/tools/addon-sdk-1.7/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.7/packages/api-utils/data/test-message-manager.js b/tools/addon-sdk-1.7/packages/api-utils/data/test-message-manager.js
new file mode 100644
index 0000000..d647bd8
--- /dev/null
+++ b/tools/addon-sdk-1.7/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.7/packages/api-utils/data/test-trusted-document.html b/tools/addon-sdk-1.7/packages/api-utils/data/test-trusted-document.html
new file mode 100644
index 0000000..5845cf5
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/data/test-trusted-document.html
@@ -0,0 +1,17 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<html>
+<head>
+ <title>Worker test</title>
+</head>
+<body>
+ <p id="paragraph">Lorem ipsum dolor sit amet.</p>
+ <script>
+ addon.port.on('addon-to-document', function (arg) {
+ addon.port.emit('document-to-addon', arg);
+ });
+ </script>
+</body>
+</html>
diff --git a/tools/addon-sdk-1.7/packages/api-utils/data/worker.js b/tools/addon-sdk-1.7/packages/api-utils/data/worker.js
new file mode 100644
index 0000000..aba594d
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/data/worker.js
@@ -0,0 +1,247 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const ContentWorker = Object.freeze({
+ // TODO: Bug 727854 Use same implementation than common JS modules,
+ // i.e. EventEmitter module
+
+ /**
+ * Create an EventEmitter instance.
+ */
+ createEventEmitter: function createEventEmitter(emit) {
+ let listeners = Object.create(null);
+ let eventEmitter = Object.freeze({
+ emit: emit,
+ on: function on(name, callback) {
+ if (typeof callback !== "function")
+ return this;
+ if (!(name in listeners))
+ listeners[name] = [];
+ listeners[name].push(callback);
+ return this;
+ },
+ once: function once(name, callback) {
+ eventEmitter.on(name, function onceCallback() {
+ eventEmitter.removeListener(name, onceCallback);
+ callback.apply(callback, arguments);
+ });
+ },
+ removeListener: function removeListener(name, callback) {
+ if (!(name in listeners))
+ return;
+ let index = listeners[name].indexOf(name);
+ if (index == -1)
+ return;
+ listeners[name].splice(index, 1);
+ }
+ });
+ function onEvent(name) {
+ if (!(name in listeners))
+ return [];
+ let args = Array.slice(arguments, 1);
+ let results = [];
+ for each (let callback in listeners[name]) {
+ results.push(callback.apply(null, args));
+ }
+ return results;
+ }
+ function hasListenerFor(name) {
+ if (!(name in listeners))
+ return false;
+ return listeners[name].length > 0;
+ }
+ return {
+ eventEmitter: eventEmitter,
+ emit: onEvent,
+ hasListenerFor: hasListenerFor
+ };
+ },
+
+ /**
+ * Create an EventEmitter instance to communicate with chrome module
+ * by passing only strings between compartments.
+ * This function expects `emitToChrome` function, that allows to send
+ * events to the chrome module. It returns the EventEmitter as `pipe`
+ * attribute, and, `onChromeEvent` a function that allows chrome module
+ * to send event into the EventEmitter.
+ *
+ * pipe.emit --> emitToChrome
+ * onChromeEvent --> callback registered through pipe.on
+ */
+ createPipe: function createPipe(emitToChrome) {
+ function onEvent() {
+ // Convert to real array
+ let args = Array.slice(arguments);
+ // JSON.stringify is buggy with cross-sandbox values,
+ // it may return "{}" on functions. Use a replacer to match them correctly.
+ function replacer(k, v) {
+ return typeof v === "function" ? undefined : v;
+ }
+ let str = JSON.stringify(args, replacer);
+ emitToChrome(str);
+ }
+
+ let { eventEmitter, emit, hasListenerFor } =
+ ContentWorker.createEventEmitter(onEvent);
+
+ return {
+ pipe: eventEmitter,
+ onChromeEvent: function onChromeEvent(array) {
+ // We either receive a stringified array, or a real array.
+ // We still allow to pass an array of objects, in WorkerSandbox.emitSync
+ // in order to allow sending DOM node reference between content script
+ // and modules (only used for context-menu API)
+ let args = typeof array == "string" ? JSON.parse(array) : array;
+ return emit.apply(null, args);
+ },
+ hasListenerFor: hasListenerFor
+ };
+ },
+
+ injectConsole: function injectConsole(exports, pipe) {
+ exports.console = Object.freeze({
+ log: pipe.emit.bind(null, "console", "log"),
+ info: pipe.emit.bind(null, "console", "info"),
+ warn: pipe.emit.bind(null, "console", "warn"),
+ error: pipe.emit.bind(null, "console", "error"),
+ debug: pipe.emit.bind(null, "console", "debug"),
+ exception: pipe.emit.bind(null, "console", "exception"),
+ trace: pipe.emit.bind(null, "console", "trace")
+ });
+ },
+
+ injectTimers: function injectTimers(exports, chromeAPI, pipe, console) {
+ // wrapped functions from `'timer'` module.
+ // Wrapper adds `try catch` blocks to the callbacks in order to
+ // emit `error` event on a symbiont if exception is thrown in
+ // the Worker global scope.
+ // @see http://www.w3.org/TR/workers/#workerutils
+
+ // List of all living timeouts/intervals
+ let _timers = Object.create(null);
+
+ // Keep a reference to original timeout functions
+ let {
+ setTimeout: chromeSetTimeout,
+ setInterval: chromeSetInterval,
+ clearTimeout: chromeClearTimeout,
+ clearInterval: chromeClearInterval
+ } = chromeAPI.timers;
+
+ exports.setTimeout = function ContentScriptSetTimeout(callback, delay) {
+ let params = Array.slice(arguments, 2);
+ let id = chromeSetTimeout(function() {
+ try {
+ delete _timers[id];
+ callback.apply(null, params);
+ } catch(e) {
+ console.exception(e);
+ }
+ }, delay);
+ _timers[id] = "timeout";
+ return id;
+ };
+ exports.clearTimeout = function ContentScriptClearTimeout(id) {
+ delete _timers[id];
+ return chromeClearTimeout(id);
+ };
+
+ exports.setInterval = function ContentScriptSetInterval(callback, delay) {
+ let params = Array.slice(arguments, 2);
+ let id = chromeSetInterval(function() {
+ try {
+ callback.apply(null, params);
+ } catch(e) {
+ console.exception(e);
+ }
+ }, delay);
+ _timers[id] = "interval";
+ return id;
+ };
+ exports.clearInterval = function ContentScriptClearInterval(id) {
+ delete _timers[id];
+ return chromeClearInterval(id);
+ };
+ pipe.on("destroy", function clearTimeouts() {
+ // Unregister all setTimeout/setInterval on page unload
+ for (let id in _timers) {
+ let kind = _timers[id];
+ if (kind == "timeout")
+ chromeClearTimeout(id);
+ else
+ chromeClearInterval(id);
+ }
+ });
+ },
+
+ injectMessageAPI: function injectMessageAPI(exports, pipe) {
+
+ let { eventEmitter: port, emit : portEmit } =
+ ContentWorker.createEventEmitter(pipe.emit.bind(null, "event"));
+ pipe.on("event", portEmit);
+
+ let self = Object.freeze({
+ port: port,
+ postMessage: pipe.emit.bind(null, "message"),
+ on: pipe.on.bind(null),
+ once: pipe.once.bind(null),
+ removeListener: pipe.removeListener.bind(null),
+ });
+ Object.defineProperty(exports, "self", {
+ value: self
+ });
+
+ // Deprecated use of on/postMessage from globals
+ exports.postMessage = function deprecatedPostMessage() {
+ console.warn("The global `postMessage()` function in content " +
+ "scripts is deprecated in favor of the " +
+ "`self.postMessage()` function, which works the same. " +
+ "Replace calls to `postMessage()` with calls to " +
+ "`self.postMessage()`." +
+ "For more info on `self.on`, see " +
+ "<https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/addon-development/web-content.html>.");
+ return self.postMessage.apply(null, arguments);
+ };
+ exports.on = function deprecatedOn() {
+ console.warn("The global `on()` function in content scripts is " +
+ "deprecated in favor of the `self.on()` function, " +
+ "which works the same. Replace calls to `on()` with " +
+ "calls to `self.on()`" +
+ "For more info on `self.on`, see " +
+ "<https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/addon-development/web-content.html>.");
+ return self.on.apply(null, arguments);
+ };
+
+ // Deprecated use of `onMessage` from globals
+ let onMessage = null;
+ Object.defineProperty(exports, "onMessage", {
+ get: function () onMessage,
+ set: function (v) {
+ if (onMessage)
+ self.removeListener("message", onMessage);
+ console.warn("The global `onMessage` function in content scripts " +
+ "is deprecated in favor of the `self.on()` function. " +
+ "Replace `onMessage = function (data){}` definitions " +
+ "with calls to `self.on('message', function (data){})`. " +
+ "For more info on `self.on`, see " +
+ "<https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/addon-development/web-content.html>.");
+ onMessage = v;
+ if (typeof onMessage == "function")
+ self.on("message", onMessage);
+ }
+ });
+ },
+
+ inject: function (exports, chromeAPI, emitToChrome) {
+ let { pipe, onChromeEvent, hasListenerFor } =
+ ContentWorker.createPipe(emitToChrome);
+ ContentWorker.injectConsole(exports, pipe);
+ ContentWorker.injectTimers(exports, chromeAPI, pipe, exports.console);
+ ContentWorker.injectMessageAPI(exports, pipe);
+ return {
+ emitToContent: onChromeEvent,
+ hasListenerFor: hasListenerFor
+ };
+ }
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/api-utils.md b/tools/addon-sdk-1.7/packages/api-utils/docs/api-utils.md
new file mode 100644
index 0000000..aee88da
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/api-utils.md
@@ -0,0 +1,157 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Drew Willcoxon [adw@mozilla.com] -->
+<!-- edited by Noelle Murata [fiveinchpixie@gmail.com] -->
+
+The `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.
+
+<api name="publicConstructor">
+@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}
+</api>
+
+<api name="validateOptions">
+@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.
+</api>
+
+<api name="addIterator">
+@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
+</api>
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/app-strings.md b/tools/addon-sdk-1.7/packages/api-utils/docs/app-strings.md
new file mode 100644
index 0000000..c40ddaf
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/app-strings.md
@@ -0,0 +1,65 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+The `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
+---------
+
+<code>for (var name in bundle) { ... }</code>
+
+Iterate the names of strings in the bundle.
+
+<code>for each (var val in bundle) { ... }</code>
+
+Iterate the values of strings in the bundle.
+
+<code>for (var [name, value] in Iterator(bundle)) { ... }</code>
+
+Iterate the names and values of strings in the bundle.
+
+
+<api name="StringBundle">
+@class
+The `StringBundle` object represents a string bundle.
+<api name="StringBundle">
+@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
+</api>
+<api name="get">
+@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
+</api>
+</api>
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/base.md b/tools/addon-sdk-1.7/packages/api-utils/docs/base.md
new file mode 100644
index 0000000..7a694f2
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/base.md
@@ -0,0 +1,207 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+### 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.7/packages/api-utils/docs/byte-streams.md b/tools/addon-sdk-1.7/packages/api-utils/docs/byte-streams.md
new file mode 100644
index 0000000..1238cd8
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/byte-streams.md
@@ -0,0 +1,68 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Drew Willcoxon [adw@mozilla.com] -->
+<!-- edited by Noelle Murata [fiveinchpixie@gmail.com] -->
+
+The `byte-streams` module provides streams for reading and writing bytes.
+
+<api name="ByteReader">
+@class
+<api name="ByteReader">
+@constructor
+ Creates a binary input stream that reads bytes from a backing stream.
+@param inputStream {stream}
+ The backing stream, an <a href="http://mxr.mozilla.org/mozilla-central/
+source/xpcom/io/nsIInputStream.idl"><code>nsIInputStream</code></a>.
+</api>
+<api name="closed">
+@property {boolean}
+ True if the stream is closed.
+</api>
+
+<api name="close">
+@method
+ Closes both the stream and its backing stream. If the stream is already
+ closed, an exception is thrown.
+</api>
+
+<api name="read">
+@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.
+</api>
+</api>
+
+<api name="ByteWriter">
+@class
+<api name="ByteWriter">
+@constructor
+ Creates a binary output stream that writes bytes to a backing stream.
+@param outputStream {stream}
+ The backing stream, an <a href="http://mxr.mozilla.org/mozilla-central/
+source/xpcom/io/nsIOutputStream.idl"><code>nsIOutputStream</code></a>.
+</api>
+<api name="closed">
+@property {boolean}
+ True if the stream is closed.
+</api>
+<api name="close">
+@method
+ Closes both the stream and its backing stream. If the stream is already
+ closed, an exception is thrown.
+</api>
+<api name="write">
+@method
+ Writes a string to the stream. If the stream is closed, an exception is
+ thrown.
+@param str {string}
+ The string to write.
+</api>
+</api>
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/collection.md b/tools/addon-sdk-1.7/packages/api-utils/docs/collection.md
new file mode 100644
index 0000000..1e557c6
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/collection.md
@@ -0,0 +1,77 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Drew Willcoxon [adw@mozilla.com] -->
+<!-- edited by Noelle Murata [fiveinchpixie@gmail.com] -->
+
+The `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.
+
+<api name="Collection">
+@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:
+
+<pre>
+ 1
+ 2
+ 3
+</pre>
+
+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.
+
+<api name="Collection">
+@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.
+</api>
+<api name="length">
+@property {number}
+The number of items in the collection array.
+</api>
+<api name="add">
+@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.
+</api>
+<api name="remove">
+@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.
+</api>
+</api>
+
+<api name="addCollectionProperty">
+@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.
+</api>
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/content.md b/tools/addon-sdk-1.7/packages/api-utils/docs/content.md
new file mode 100644
index 0000000..1c25eb4
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/content.md
@@ -0,0 +1,15 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Irakli Gozalishvili [gozala@mozilla.com] -->
+
+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/content/loader.html
+[Worker]:packages/api-utils/content/worker.html
+[Symbiont]:packages/api-utils/content/symbiont.html
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/content/loader.md b/tools/addon-sdk-1.7/packages/api-utils/docs/content/loader.md
new file mode 100644
index 0000000..a460476
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/content/loader.md
@@ -0,0 +1,92 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Irakli Gozalishvili [gozala@mozilla.com] -->
+
+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/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);
+ }
+ });
+
+<api name="Loader">
+@class
+<api name="contentScriptFile">
+@property {array}
+The local file URLs of content scripts to load. Content scripts specified by
+this property are loaded *before* those specified by the `contentScript`
+property.
+</api>
+
+<api name="contentScript">
+@property {array}
+The texts of content scripts to load. Content scripts specified by this
+property are loaded *after* those specified by the `contentScriptFile` property.
+</api>
+
+<api name="contentScriptWhen">
+@property {string}
+When to load the content scripts. This may take one of the following
+values:
+
+* "start": load content scripts immediately after the document
+element for the page is inserted into the DOM, but before the DOM content
+itself has been loaded
+* "ready": load content scripts once DOM content has been loaded,
+corresponding to the
+[DOMContentLoaded](https://developer.mozilla.org/en/Gecko-Specific_DOM_Events)
+event
+* "end": load content scripts once all the content (DOM, JS, CSS,
+images) for the page has been loaded, at the time the
+[window.onload event](https://developer.mozilla.org/en/DOM/window.onload)
+fires
+
+</api>
+
+<api name="contentURL">
+@property {string}
+The URL of the content loaded.
+</api>
+
+<api name="allow">
+@property {object}
+Permissions for the content, with the following keys:
+@prop script {boolean}
+ Whether or not to execute script in the content. Defaults to true.
+</api>
+</api>
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/content/proxy.md b/tools/addon-sdk-1.7/packages/api-utils/docs/content/proxy.md
new file mode 100644
index 0000000..095e8ff
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/content/proxy.md
@@ -0,0 +1,241 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Alexandre Poirot [apoirot@mozilla.com] -->
+
+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:
+
+<pre><code>
+// 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.
+</code></pre>
+
+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:
+<pre><code>
+proxy.addEventListener = function () {};
+</code></pre>
+* 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`.
+
+<pre>
+ /--------------------\ /------------------------\
+ | Web document | | Content script sandbox |
+ | http://mozilla.org | | data/worker.js |
+ | | require('content-proxy'). | |
+ | window >-----------|- create(window) -|-> window |
+ \--------------------/ \------------------------/
+</pre>
+
+
+## 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.
+
+<pre>
+ /--------------------\ /------------------------\
+ | Web document | | Content script sandbox |
+ | http://mozilla.org | | data/worker.js |
+ | | /-------|-> myObject = {} |
+ | | /----------------v--\ | |
+ | | | XrayWrapper Proxy | | - document |
+ | | \---------v---------/ \----^-------------------/
+ | | v |
+ | | /-------------\ /----------\ |
+ | - document >-------|->| XrayWrapper |<-| CS proxy |-/
+ \--------------------/ \-------------/ \----------/
+</pre>
+
+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.
+<br/><br/>
+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.
+<br/><br/>
+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));
+ }
+
+<br>
+
+ // 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));
+ }
+
+
+<api name="create">
+@function
+ Create a content script proxy. <br/>
+ 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`.
+</api>
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/content/symbiont.md b/tools/addon-sdk-1.7/packages/api-utils/docs/content/symbiont.md
new file mode 100644
index 0000000..1ff7dd1
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/content/symbiont.md
@@ -0,0 +1,140 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Myk Melez [myk@mozilla.org] -->
+<!-- contributed by Irakli Gozalishvili [gozala@mozilla.com] -->
+
+
+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
+`<iframe>` and `<browser>` elements) and communicate with programs via
+asynchronous JSON pipes. It is useful in the construction of APIs that
+are compatible with the execution model codenamed "electrolysis" in which
+programs run in separate processes from web content.
+
+Introduction
+------------
+
+`Symbiont` constructs a content symbiont for a given frame, it loads the
+specified contentURL and scripts into it, and plumbs an asynchronous
+JSON pipe between the content symbiont object and the content symbiont
+context. If frame is not provided hidden frame will be created.
+
+Examples
+--------
+
+ var { Symbiont } = require('content');
+ var Thing = Symbiont.resolve({ constructor: '_init' }).compose({
+ constructor: function Thing(options) {
+ // `getMyFrame` returns the host application frame in which
+ // the page is loaded.
+ this._frame = getMyFrame();
+ this._init(options)
+ }
+ });
+
+See the [panel][] module for a real-world example of usage of this module.
+
+[panel]:packages/addon-kit/panel.html
+
+Reference
+---------
+
+<api name="Symbiont">
+@class
+Symbiont is composed from the [Worker][] trait, therefore instances
+of Symbiont and their descendants expose all the public properties
+exposed by [Worker][] along with additional public properties that
+are listed below:
+
+[Worker]:packages/api-utils/content/worker.html
+
+<api name="Symbiont">
+@constructor
+Creates a content symbiont.
+@param options {object}
+ Options for the constructor. Includes all the keys that
+the [Worker](packages/api-utils/content/worker.html)
+constructor accepts and a few more:
+
+ @prop [frame] {object}
+ The host application frame in which the page is loaded.
+ If frame is not provided hidden one will be created.
+ @prop [contentScriptWhen="end"] {string}
+ When to load the content scripts. This may take one of the following
+ values:
+
+ * "start": load content scripts immediately after the document
+ element for the page is inserted into the DOM, but before the DOM content
+ itself has been loaded
+ * "ready": load content scripts once DOM content has been loaded,
+ corresponding to the
+ [DOMContentLoaded](https://developer.mozilla.org/en/Gecko-Specific_DOM_Events)
+ event
+ * "end": load content scripts once all the content (DOM, JS, CSS,
+ images) for the page has been loaded, at the time the
+ [window.onload event](https://developer.mozilla.org/en/DOM/window.onload)
+ fires
+
+ This property is optional and defaults to "end".
+
+ @prop [allow] {object}
+ Permissions for the content, with the following keys:
+ @prop [script] {boolean}
+ Whether or not to execute script in the content. Defaults to true.
+ Optional.
+ Optional.
+</api>
+
+<api name="contentScriptFile">
+@property {array}
+The local file URLs of content scripts to load. Content scripts specified by
+this property are loaded *before* those specified by the `contentScript`
+property.
+</api>
+
+<api name="contentScript">
+@property {array}
+The texts of content scripts to load. Content scripts specified by this
+property are loaded *after* those specified by the `contentScriptFile` property.
+</api>
+
+<api name="contentScriptWhen">
+@property {string}
+When to load the content scripts. This may have one of the following
+values:
+
+* "start": load content scripts immediately after the document
+element for the page is inserted into the DOM, but before the DOM content
+itself has been loaded
+* "ready": load content scripts once DOM content has been loaded,
+corresponding to the
+[DOMContentLoaded](https://developer.mozilla.org/en/Gecko-Specific_DOM_Events)
+event
+* "end": load content scripts once all the content (DOM, JS, CSS,
+images) for the page has been loaded, at the time the
+[window.onload event](https://developer.mozilla.org/en/DOM/window.onload)
+fires
+
+</api>
+
+<api name="contentURL">
+@property {string}
+The URL of the content loaded.
+</api>
+
+<api name="allow">
+@property {object}
+Permissions for the content, with a single boolean key called `script` which
+defaults to true and indicates whether or not to execute scripts in the
+content.
+</api>
+
+</api>
+
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/content/worker.md b/tools/addon-sdk-1.7/packages/api-utils/docs/content/worker.md
new file mode 100644
index 0000000..b3e17ea
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/content/worker.md
@@ -0,0 +1,130 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Irakli Gozalishvili [gozala@mozilla.com] -->
+
+This module exports the `Worker` trait, which may be used to construct objects
+implementing the [Worker][] interface defined by the W3C, with minor
+differences.
+
+Content workers are message-passing facilities for communication between
+[content scripts](dev-guide/guides/content-scripts/index.html) and the main
+add-on code.
+
+It is important to note that unlike "web workers," these workers run in the
+same process as web content and browser chrome, so code within workers can
+block the UI.
+
+[Worker]:http://www.w3.org/TR/workers/#worker
+
+<api name="Worker">
+@class
+Worker is composed from the [EventEmitter][] trait, therefore instances
+of Worker and their descendants expose all the public properties
+exposed by [EventEmitter][] along with additional public properties that
+are listed below.
+
+**Example**
+
+ var workers = require("content/worker");
+ let worker = workers.Worker({
+ window: require("window-utils").activeWindow,
+ contentScript:
+ "self.port.on('hello', function(name) { " +
+ " self.port.emit('response', window.location); " +
+ "});"
+ });
+ worker.port.emit("hello", { name: "worker"});
+ worker.port.on("response", function (location) {
+ console.log(location);
+ });
+
+[EventEmitter]:packages/api-utils/events.html
+
+<api name="Worker">
+@constructor
+Creates a content worker.
+@param options {object}
+Options for the constructor, with the following keys:
+ @prop window {object}
+ The content window to create JavaScript sandbox for communication with.
+ @prop [contentScriptFile] {string,array}
+ The local file URLs of content scripts to load. Content scripts specified
+ by this option are loaded *before* those specified by the `contentScript`
+ option. Optional.
+ @prop [contentScript] {string,array}
+ The texts of content scripts to load. Content scripts specified by this
+ option are loaded *after* those specified by the `contentScriptFile` option.
+ Optional.
+ @prop [onMessage] {function}
+ Functions that will registered as a listener to a 'message' events.
+ @prop [onError] {function}
+ Functions that will registered as a listener to an 'error' events.
+</api>
+
+<api name="port">
+@property {EventEmitter}
+[EventEmitter](packages/api-utils/events.html) object that allows you to:
+
+* send customized messages to the worker using the `port.emit` function
+* receive events from the worker using the `port.on` function
+
+</api>
+
+<api name="postMessage">
+@method
+Asynchronously emits `"message"` events in the enclosed worker, where content
+script was loaded.
+@param data {number,string,JSON}
+The data to send. Must be stringifiable to JSON.
+</api>
+
+<api name="destroy">
+@method
+Destroy the worker by removing the content script from the page and removing
+all registered listeners. A `detach` event is fired just before removal.
+</api>
+
+<api name="url">
+@property {string}
+The URL of the content.
+</api>
+
+<api name="tab">
+@property {object}
+If this worker is attached to a content document, returns the related
+[tab](packages/addon-kit/tabs.html).
+</api>
+
+<api name="message">
+@event
+This event allows the content worker to receive messages from its associated
+content scripts. Calling the `self.postMessage()` function from a content
+script will asynchronously emit the `message` event on the corresponding
+worker.
+
+@argument {value}
+The event listener is passed the message, which must be a
+<a href = "dev-guide/guides/content-scripts/using-port.html#json_serializable">JSON-serializable value</a>.
+</api>
+
+<api name="error">
+@event
+This event allows the content worker to react to an uncaught runtime script
+error that occurs in one of the content scripts.
+
+@argument {Error}
+The event listener is passed a single argument which is an
+[Error](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error)
+object.
+</api>
+
+<api name="detach">
+@event
+This event is emitted when the document associated with this worker is unloaded
+or the worker's `destroy()` method is called.
+</api>
+
+</api>
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/cortex.md b/tools/addon-sdk-1.7/packages/api-utils/docs/cortex.md
new file mode 100644
index 0000000..afae8cb
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/cortex.md
@@ -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/. -->
+
+
+## Property Encapsulation ##
+
+In JavaScript it is not possible to create properties that have limited or
+controlled accessibility. It is possible to create non-enumerable and
+non-writable properties, but still they can be discovered and accessed.
+Usually so called "closure capturing" is used to encapsulate such properties
+in lexical scope:
+
+ function Foo() {
+ var _secret = 'secret';
+ this.hello = function hello() {
+ return 'Hello ' + _secret;
+ }
+ }
+
+This provides desired result, but has side effect of degrading code readability,
+especially with object-oriented programs. Another disadvantage with this pattern
+is that there is no immediate solution for inheriting access to the privates
+(illustrated by the following example):
+
+ function Derived() {
+ this.hello = function hello() {
+ return _secret;
+ }
+ this.bye = function bye() {
+ return _secret;
+ }
+ }
+ Derived.prototype = Object.create(Foo.prototype);
+
+## Facade Objects ##
+
+Alternatively constructor can returned facade objects - proxies to the
+instance's public properties:
+
+ function Foo() {
+ var foo = Object.create(Foo.prototype);
+ return {
+ bar: foo.hello.bind(foo);
+ }
+ }
+ Foo.prototype._secret = 'secret';
+ Foo.prototype.hello = function hello() {
+ return 'Hello ' + this._secret;
+ }
+
+ function Derived() {
+ var derived = Object.create(Derived.prototype);
+ return {
+ bar: derived.hello.bind(derived);
+ bye: derived.bye.bind(derived);
+ }
+ }
+ Derived.prototype = Object.create(Foo.prototype);
+ Derived.prototype.bye = function bye() {
+ return 'Bye ' + this._secret;
+ };
+
+While this solution solves given issue and provides proper encapsulation for
+both own and inherited private properties, it does not addresses following:
+
+ - Privates defined on the `prototype` can be compromised, since they are
+ accessible through the constructor (`Foo.prototype._secret`).
+ - Behavior of `instanceof` is broken, since `new Derived() instanceof Derived`
+ is going to evaluate to `false`.
+
+## Tamper Proofing with Property Descriptor Maps ##
+
+In ES5 new property descriptor maps were introduced, which can be used as a
+building blocks for defining reusable peace of functionality. To some degree
+they are similar to a `prototype` objects, and can be used so to define pieces
+of functionality that is considered to be private (In contrast to `prototype`
+they are not exposed by default).
+
+ function Foo() {
+ var foo = Object.create(Foo.prototype, FooDescriptor);
+ var facade = Object.create(Foo.prototype);
+ facade.hello = foo.hello.bind(foo);
+ return facade;
+ }
+ Foo.prototype.hello = function hello() {
+ return 'Hello ' + this._secret;
+ }
+ var FooDescriptor = {
+ _secret: { value: 'secret' };
+ }
+
+ function Derived() {
+ var derived = Object.create(Derived.prototype, DerivedDescriptor);
+ var facade = Object.create(Derived.prototype);
+ facade.hello = derived.hello.bind(derived);
+ facade.bye = derived.bye.bind(derived);
+ return facade;
+ }
+ Derived.prototype = Object.create(Foo.prototype);
+ Derived.prototype.bye = function bye() {
+ return 'Bye ' + this._secret;
+ };
+ DerivedDescriptor = {};
+
+ Object.keys(FooDescriptor).forEach(function(key) {
+ DerivedDescriptor[key] = FooDescriptor[key];
+ });
+
+## Cortex Objects ##
+
+Last approach solves all of the concerns, but adds complexity, verbosity
+and decreases code readability. Combination of `Cortex`'s and `Trait`'s
+will gracefully solve all these issues and keep code clean:
+
+ var Cortex = require('cortex').Cortex;
+ var Trait = require('light-traits').Trait;
+
+ var FooTrait = Trait({
+ _secret: 'secret',
+ hello: function hello() {
+ return 'Hello ' + this._secret;
+ }
+ });
+ function Foo() {
+ return Cortex(FooTrait.create(Foo.prototype));
+ }
+
+ var DerivedTrait = Trait.compose(FooTrait, Trait({
+ bye: function bye() {
+ return 'Bye ' + this._secret;
+ }
+ }));
+ function Derived() {
+ var derived = DerivedTrait.create(Derived.prototype);
+ return Cortex(derived);
+ }
+
+Function `Cortex` takes any object and returns a proxy for its public
+properties. By default properties are considered to be public if they don't
+start with `"_"`, but default behavior can be overridden if needed, by passing
+array of public property names as a second argument.
+
+## Gotchas ##
+
+`Cortex` is just a utility function to create a proxy object, and it does not
+solve the `prototype`-related issues highlighted earlier, but since traits make
+use of property descriptor maps instead of `prototype`s, there aren't any
+issues with using `Cortex` to wrap objects created from traits.
+
+If you want to use `Cortex` with an object that uses a `prototype` chain,
+however, you should either make sure you don't have any private properties
+in the prototype chain or pass the optional third `prototype` argument.
+
+In the latter case, the returned proxy will inherit from the given prototype,
+and the `prototype` chain of the wrapped object will be inaccessible.
+However, note that the behavior of the `instanceof` operator will vary,
+as `proxy instanceof Constructor` will return false even if the Constructor
+function's prototype is in the wrapped object's prototype chain.
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/cuddlefish.md b/tools/addon-sdk-1.7/packages/api-utils/docs/cuddlefish.md
new file mode 100644
index 0000000..27daf30
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/cuddlefish.md
@@ -0,0 +1,7 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+`cuddlefish` is the name of the SDK's module loader.
+
+This module still needs to be documented.
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/environment.md b/tools/addon-sdk-1.7/packages/api-utils/docs/environment.md
new file mode 100644
index 0000000..b5057a2
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/environment.md
@@ -0,0 +1,43 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+Module provides API to access, set and unset environment variables via exported
+`env` object.
+
+ var { env } = require('api-utils/environment');
+
+You can get the value of an environment variable, by accessing property that
+has name of desired variable:
+
+ var PATH = env.PATH;
+
+You can check existence of an environment variable by checking if property with
+such variable name exists:
+
+ console.log('PATH' in env); // true
+ console.log('FOO' in env); // false
+
+You can set value of an environment variable by setting a property:
+
+ env.FOO = 'foo';
+ env.PATH += ':/my/path/'
+
+You can unset environment variable by deleting a property:
+
+ delete env.FOO;
+
+## Limitations ##
+
+There is no way to enumerate existing environment variables, also `env`
+won't have any enumerable properties:
+
+ console.log(Object.keys(env)); // []
+
+Environment variable will be unset, show up as non-existing if it's set
+to `null`, `undefined` or `''`.
+
+ env.FOO = null;
+ console.log('FOO' in env); // false
+ env.BAR = '';
+ console.log(env.BAR); // undefined
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/errors.md b/tools/addon-sdk-1.7/packages/api-utils/docs/errors.md
new file mode 100644
index 0000000..98e94ac
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/errors.md
@@ -0,0 +1,42 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Drew Willcoxon [adw@mozilla.com] -->
+
+The `errors` module provides helpers for safely invoking user callbacks.
+
+<api name="catchAndLog">
+@function
+ Wraps a callback in a function that when invoked will catch and log any
+ exception thrown by the callback.
+@param callback {function}
+ The callback to wrap.
+@param [defaultResponse] {value}
+ This value will be returned by the wrapper if `callback` throws an exception.
+ If not given, `undefined` is used.
+@param [logException] {function}
+ When `callback` throws an exception, it will be passed to this function. If
+ not given, the exception is logged using `console.exception()`.
+@returns {function}
+ A function that will invoke `callback` when called. The return value of this
+ function is the return value of `callback` unless `callback` throws an
+ exception. In that case, `defaultResponse` is returned or `undefined` if
+ `defaultResponse` is not given.
+</api>
+
+<api name="catchAndLogProps">
+@function
+ Replaces methods of an object with wrapped versions of those methods returned
+ by `catchAndLog()`.
+@param object {object}
+ The object whose methods to replace.
+@param props {string,array}
+ The names of the methods of `object` to replace, either a string for a single
+ method or an array of strings for multiple methods.
+@param [defaultResponse] {value}
+ This value will be returned by any wrapper whose wrapped method throws an
+ exception. If not given, `undefined` is used.
+@param [logException] {function}
+ See `catchAndLog()`.
+</api>
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/event/core.md b/tools/addon-sdk-1.7/packages/api-utils/docs/event/core.md
new file mode 100644
index 0000000..6ab3317
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/event/core.md
@@ -0,0 +1,51 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+Module provides core (low level) API for working with events in the SDK. This
+API is mainly for implementing higher level event APIs.
+
+Event `listener` may be registered on any (event `target`) object using
+provided `on` function:
+
+ var { on, once, off, emit } = require('api-utils/event/core');
+ var target = { name: 'target' };
+ on(target, 'message', function listener(event) {
+ console.log('hello ' + event);
+ });
+ on(target, 'data', console.log);
+
+Event of specific `type` may be emitted on any event `target` object using
+`emit` function. This will call all registered `listener`s for the given `type`
+on the given event `target` in the same order they were registered.
+
+ emit(target, 'message', 'event');
+ // info: 'hello event'
+ emit(target, 'data', { type: 'data' }, 'second arg');
+ // info: [Object object] 'second arg'
+
+Registered event listeners may be removed using `off` function:
+
+ off(target, 'message');
+ emit(target, 'message', 'bye');
+ // info: 'hello bye'
+
+Sometimes listener only cares about first event of specific `type`. To avoid
+hassles of removing such listeners there is convenient `once` function:
+
+ once(target, 'load', function() {
+ console.log('ready');
+ });
+ emit(target, 'load')
+ // info: 'ready'
+ emit(target, 'load')
+
+There are also convenient ways to remove registered listeners. All listeners of
+the specific type can be easily removed (only two argument must be passed):
+
+ off(target, 'message');
+
+Also, removing all registered listeners is possible (only one argument must be
+passed):
+
+ off(target);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/event/target.md b/tools/addon-sdk-1.7/packages/api-utils/docs/event/target.md
new file mode 100644
index 0000000..96feb79
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/event/target.md
@@ -0,0 +1,95 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+Provides an exemplar `EventTarget` object, that implements interface for
+adding removing event listeners of a specific type. `EventTarget` is a
+base of all objects in SDK on which events are emitted.
+
+### Instantiation
+
+It's easy to create event target objects, no special arguments are required.
+
+ const { EventTarget } = require('api-utils/event/target');
+ let target = EventTarget.new();
+
+For a convenience though optional `options` arguments may be used, in which
+case all the function properties with keys like: `onMessage`, `onMyEvent`...
+will be auto registered for associated `'message'`, `'myEvent'` events on
+the created instance. _All other properties of `options` will be ignored_.
+
+### Adding listeners
+
+`EventTarget` interface defines `on` method, that can be used to register
+event listeners on them for the given event type:
+
+ target.on('message', function onMessage(message) {
+ // Note: `this` pseudo variable is an event `target` unless
+ // intentionally overridden via `.bind()`.
+ console.log(message);
+ });
+
+Sometimes event listener may care only about very first event of specific
+`type`. `EventTarget` interface defines convenience method for adding one
+shot event listeners via method `once`. Such listeners are called only once
+next time event of the specified type is emitted:
+
+ target.once('ready', function onReady() {
+ // Do the thing once ready!
+ });
+
+### Removing listeners
+
+`EventTarget` interface defines API for unregistering event listeners, via
+`removeListener` method:
+
+ target.removeListener('message', onMessage);
+
+### Emitting events
+
+`EventTarget` interface intentionally does not defines an API for emitting
+events. In majority of cases party emitting events is different from party
+registering listeners. In order to emit events one needs to use `event/core`
+module instead:
+
+ let { emit } = require('api-utils/event/core');
+
+ target.on('hi', function(person) { console.log(person + 'tells hi'); });
+ emit(target, 'hi', 'Mark');
+ // info: 'Mark tells hi'
+
+For more details see **event/core** documentation.
+
+### More details
+
+Listeners registered during the event propagation (by one of the listeners)
+won't be triggered until next emit of the matching type:
+
+ let { emit } = require('api-utils/event/core');
+
+ target.on('message', function onMessage(message) {
+ console.log('listener trigerred');
+ target.on('message', function() {
+ console.log('nested listener triggered');
+ });
+ });
+
+ emit(target, 'message');
+ // info: 'listener trigerred'
+ emit(target, 'message');
+ // info: 'listener trigerred'
+ // info: 'nested listener trigerred'
+
+Exceptions in the listeners can be handled via `'error'` event listeners:
+
+ target.on('boom', function() {
+ throw Error('Boom!');
+ });
+ target.once('error', function(error) {
+ console.log('caught an error: ' + error.message);
+ });
+ emit(target, 'boom');
+ // info: caught an error: Boom!
+
+If there is no listener registered for `error` event or if it also throws
+exception then such exceptions are logged into a console.
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/events.md b/tools/addon-sdk-1.7/packages/api-utils/docs/events.md
new file mode 100644
index 0000000..76f9efd
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/events.md
@@ -0,0 +1,78 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+The `events` module provides base API for emitting events.
+
+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.
+
+<api name="EventEmitter">
+@class
+The EventEmitter is the base building block for all compositions that
+would need to broadcast data to multiple consumers.
+
+Please note that `EventEmitter` does not expose either a method for emitting
+events or a list of available event listeners as its public API. Obviously
+both are accessible but from the instance itself through the private API.
+<api name="EventEmitter">
+@constructor
+Creates an EventEmitter object.
+</api>
+
+<api name="on">
+@method
+Registers an event `listener` that will be called when events of
+specified `type` are emitted.
+
+If the `listener` is already registered for this `type`, a call to this
+method has no effect.
+
+If the event listener is being registered while an event is being processed,
+the event listener is not called during the current emit.
+
+**Example:**
+
+ // worker is instance of EventEmitter
+ worker.on('message', function (data) {
+ console.log('data received: ' + data)
+ });
+
+@param type {String}
+ The type of the event.
+@param listener {Function}
+ The listener function that processes the event.
+</api>
+
+<api name="once">
+@method
+Registers an event `listener` that will only be called once, the next time
+an event of the specified `type` is emitted.
+
+If the event listener is registered while an event of the specified `type`
+is being emitted, the event listener will not be called during the current
+emit.
+
+@param type {String}
+ The type of the event.
+@param listener {Function}
+ The listener function that processes the event.
+</api>
+
+<api name="removeListener">
+@method
+Unregisters an event `listener` for the specified event `type`.
+
+If the `listener` is not registered for this `type`, a call to this
+method has no effect.
+
+If an event listener is removed while an event is being processed, it is
+still triggered by the current emit. After it is removed, the event listener
+is never invoked again (unless registered again for future processing).
+
+@param type {String}
+ The type of the event.
+@param listener {Function}
+ The listener function that processes the event.
+</api>
+</api>
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/file.md b/tools/addon-sdk-1.7/packages/api-utils/docs/file.md
new file mode 100644
index 0000000..9587353
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/file.md
@@ -0,0 +1,151 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Drew Willcoxon [adw@mozilla.com] -->
+<!-- contributed by Atul Varma [atul@mozilla.com] -->
+<!-- edited by Noelle Murata [fiveinchpixie@gmail.com] -->
+
+The `file` module provides access to the local filesystem.
+
+
+Paths
+-----
+
+Path specifications in this API are platform-specific. This means that on
+Windows paths are specified using the backslash path separator (`\`), and on
+Unix-like systems like Linux and OS X paths are specified using the forward
+slash path separator (`/`).
+
+If your add-on uses literal Windows-style path specifications with this API,
+your add-on likely won't work when users run it on Unix-like systems. Likewise,
+if your add-on uses literal Unix-style path specifications, it won't work for
+users on Windows.
+
+To ensure your add-on works for everyone, generate paths using the
+[`join`](packages/api-utils/file.html#join(...)) function. Unfortunately
+this API does not currently provide a way to obtain an absolute base path which
+you could then use with `join`. For now, you need to
+[`require("chrome")`](dev-guide/tutorials/chrome.html) and use the
+XPCOM directory service as described at
+[MDN](https://developer.mozilla.org/en/Code_snippets/File_I%2F%2FO#Getting_special_files).
+
+Note that if you do decide to hardcode Windows-style paths, be sure to escape
+backslashes in strings. For example, to specify the file at `C:\Users\Myk`, you
+need to use the string `"C:\\Users\\Myk"`, not `"C:\Users\Myk"`. You can read
+more about escaping characters in strings at
+[MDN](https://developer.mozilla.org/en/JavaScript/Guide/Values,_Variables,_and_Literals#Escaping_Characters).
+
+
+<api name="basename">
+@function
+ Returns the last component of the given path. For example,
+ `basename("/foo/bar/baz")` returns `"baz"`. If the path has no components,
+ the empty string is returned.
+@param path {string}
+ The path of a file.
+@returns {string}
+ The last component of the given path.
+</api>
+
+<api name="dirname">
+@function
+ Returns the path of the directory containing the given file. If the file is
+ at the top of the volume, the empty string is returned.
+@param path {string}
+ The path of a file.
+@returns {string}
+ The path of the directory containing the file.
+</api>
+
+<api name="exists">
+@function
+ Returns true if a file exists at the given path and false otherwise.
+@param path {string}
+ The path of a file.
+@returns {boolean}
+ True if the file exists and false otherwise.
+</api>
+
+<api name="join">
+@function
+ Takes a variable number of strings, joins them on the file system's path
+ separator, and returns the result.
+@param ... {strings}
+ A variable number of strings to join. The first string must be an absolute
+ path.
+@returns {string}
+ A single string formed by joining the strings on the file system's path
+ separator.
+</api>
+
+<api name="list">
+@function
+ Returns an array of file names in the given directory.
+@param path {string}
+ The path of the directory.
+@returns {array}
+ An array of file names. Each is a basename, not a full path.
+</api>
+
+<api name="mkpath">
+@function
+ Makes a new directory named by the given path. Any subdirectories that do not
+ exist are also created. `mkpath` can be called multiple times on the same
+ path.
+@param path {string}
+ The path to create.
+</api>
+
+<api name="open">
+@function
+ Returns a stream providing access to the contents of a file.
+@param path {string}
+ The path of the file to open.
+@param [mode] {string}
+ An optional string, each character of which describes a characteristic of the
+ returned stream. If the string contains `"r"`, the file is opened in
+ read-only mode. `"w"` opens the file in write-only mode. `"b"` opens the
+ file in binary mode. If `"b"` is not present, the file is opened in text
+ mode, and its contents are assumed to be UTF-8. If *`mode`* is not given,
+ `"r"` is assumed, and the file is opened in read-only text mode.
+@returns {stream}
+ If the file is opened in text read-only `mode`, a `TextReader` is returned,
+ and if text write-only mode, a `TextWriter` is returned. See
+ [`text-streams`](packages/api-utils/text-streams.html) for information on
+ these text stream objects. If the file is opened in binary read-only `mode`,
+ a `ByteReader` is returned, and if binary write-only mode, a `ByteWriter` is
+ returned. See
+ [`byte-streams`](packages/api-utils/byte-streams.html) for more
+ information on these byte stream objects. Opened files should always be
+ closed after use by calling `close` on the returned stream.
+</api>
+
+<api name="read">
+@function
+ Opens a file and returns a string containing its entire contents.
+@param path {string}
+ The path of the file to read.
+@param [mode] {string}
+ An optional string, each character of which describes a characteristic of the
+ returned stream. If the string contains `"b"`, the contents will be returned
+ in binary mode. If `"b"` is not present or `mode` is not given, the file
+ contents will be returned in text mode.
+@returns {string}
+ A string containing the file's entire contents.
+</api>
+
+<api name="remove">
+@function
+ Removes a file from the file system. To remove directories, use `rmdir`.
+@param path {string}
+ The path of the file to remove.
+</api>
+
+<api name="rmdir">
+@function
+ Removes a directory from the file system. If the directory is not empty, an
+ exception is thrown.
+@param path {string}
+ The path of the directory to remove.
+</api>
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/frame/utils.md b/tools/addon-sdk-1.7/packages/api-utils/docs/frame/utils.md
new file mode 100644
index 0000000..c88d198
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/frame/utils.md
@@ -0,0 +1,53 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+The `frame/utils` module provides helper functions for working with platform
+internals like [frames](https://developer.mozilla.org/en/XUL/iframe) and
+[browsers](https://developer.mozilla.org/en/XUL/browser).
+
+### create
+
+Module exports `create` function that takes `nsIDOMDocument` of the
+[privileged document](https://developer.mozilla.org/en/Working_with_windows_in_chrome_code)
+and creates a `browser` element in it's `documentElement`:
+
+ let { open } = require('api-utils/window/utils');
+ let { create } = require('api-utils/frame/utils');
+ let window = open('data:text/html,Foo');
+ let frame = create(window.document);
+
+Optionally `create` can be passed set of `options` to configure created frame
+even further. Following options are supported:
+
+- `type`
+String that defines access type of the document loaded into it. Defaults to
+`'content'`. For more details and other possible values see
+[documentation on MDN](https://developer.mozilla.org/en/XUL/Attribute/browser.type)
+
+- `uri`
+URI of the document to be loaded into created frame. Defaults to `about:blank`.
+
+- `remote`
+If `true` separate process will be used for this frame, also in such case all
+the following options are ignored.
+
+- `allowAuth`
+Whether to allow auth dialogs. Defaults to `false`.
+
+- `allowJavascript`
+Whether to allow Javascript execution. Defaults to `false`.
+
+- `allowPlugins`
+Whether to allow plugin execution. Defaults to `false`.
+
+Execution of scripts may easily be enabled:
+
+ let { open } = require('api-utils/window/utils');
+ let { create } = require('api-utils/frame/utils');
+ let window = open('data:text/html,top');
+ let frame = create(window.document, {
+ uri: 'data:text/html,<script>alert("Hello")</script>',
+ allowJavascript: true
+ });
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/globals.md b/tools/addon-sdk-1.7/packages/api-utils/docs/globals.md
new file mode 100644
index 0000000..e6df8a8
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/globals.md
@@ -0,0 +1,100 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+Globals in this section are subject to change in the future and/or are likely
+to be of interest to SDK module developers, rather than add-on developers.
+
+## Components ##
+
+To access the infamous and powerful `Components` object, see the
+[Chrome Authority](dev-guide/tutorials/chrome.html) documentation.
+
+## \_\_url\_\_ ##
+
+The `__url__` global is a string identifying the URL from which the code has
+been retrieved. If the code has no identifiable URL, this value may be `null`.
+
+## packaging ##
+
+<span class="aside">
+For more information on packaging, see the [Package Specification][] appendix.
+</span>
+
+The `packaging` global contains methods and metadata related to
+the packages available in the current environment.
+
+<code>packaging.**getURLForData**(*path*)</code>
+
+Given a unix-style path relative to the calling package's `data`
+directory, returns an absolute URL to the file or directory.
+
+By "calling package", we mean the package in which the caller's source
+code resides.
+
+Thus, for example, if a package contains a resource at
+`data/mydata.dat` and a module at `lib/foo.js`, the module at
+`lib/foo.js` may make the following call to retrieve an absolute URL
+to `data/mydata.dat`:
+
+ var myDataURL = packaging.getURLForData("/mydata.dat");
+
+If the calling package has no `data` directory, an exception is
+thrown.
+
+## memory ##
+
+`memory` is an object that exposes functionality to track
+objects of interest and help diagnose and prevent memory leaks.
+
+<code>memory.**track**(*object*, [*bin*])</code>
+
+Marks *object* for being tracked, and categorizes it with the given
+bin name. If *bin* isn't specified, the memory tracker attempts to
+infer a bin name by first checking the object's
+`constructor.name`; if that fails or results in the generic
+`Object`, the stack is inspected and the name of the current
+function being executed&mdash;which is assumed to be a constructor
+function&mdash;is used. If that fails, then the object is placed in a
+bin named `generic`.
+
+<code>memory.**getObjects**([*bin*])</code>
+
+Returns an `Array` containing information about tracked objects
+that have been categorized with the given bin name. If *bin* isn't
+provided, information about all live tracked objects are returned.
+
+Each element of the array is an object with the following keys:
+
+<table>
+ <tr>
+ <td><code>weakref</code></td>
+ <td>A weak reference to the object being tracked. Call
+ <code>get()</code> on this object to retrieve its strong reference; if
+ a strong reference to the object no longer exists, <code>get()</code>
+ will return <code>null</code>.</td>
+ </tr>
+ <tr>
+ <td><code>created</code></td>
+ <td>A <code>Date</code> representing the date and time that
+ <code>memory.track()</code> was called on the object being
+ tracked.</td>
+ </tr>
+ <tr>
+ <td><code>filename</code></td>
+ <td>The name of the file that called <code>memory.track()</code> on
+ the object being tracked.</td>
+ </tr>
+ <tr>
+ <td><code>lineNo</code></td>
+ <td>The line number of the file that called
+ <code>memory.track()</code> on the object being tracked.</td>
+ </tr>
+</table>
+
+<code>memory.**getBins**()</code>
+
+Returns an `Array` containing the names of all bins that aren't
+currently empty.
+
+ [Package Specification]: dev-guide/package-spec.html \ No newline at end of file
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/hidden-frame.md b/tools/addon-sdk-1.7/packages/api-utils/docs/hidden-frame.md
new file mode 100644
index 0000000..9d16dfa
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/hidden-frame.md
@@ -0,0 +1,83 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Myk Melez [myk@mozilla.org] -->
+
+The `hidden-frame` module creates host application frames (i.e. XUL `<iframe>`
+elements) that are not displayed to the user. It is useful in the construction
+of APIs that load web content not intended to be directly seen or accessed
+by users, like `page-worker`. It is also useful in the construction of APIs
+that load web content for intermittent display, such as `panel`.
+
+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.
+
+Introduction
+------------
+
+The module exports a constructor function, `HiddenFrame`, and two other
+functions, `add` and `remove`.
+
+`HiddenFrame` constructs a new hidden frame. `add` registers a hidden frame,
+preparing it to load content. `remove` unregisters a frame, unloading any
+content that was loaded in it.
+
+Examples
+--------
+
+The following code creates a hidden frame, loads a web page into it, and then
+logs its title:
+
+ var hiddenFrames = require("hidden-frame");
+ let hiddenFrame = hiddenFrames.add(hiddenFrames.HiddenFrame({
+ onReady: function() {
+ this.element.contentWindow.location = "http://www.mozilla.org/";
+ let self = this;
+ this.element.addEventListener("DOMContentLoaded", function() {
+ console.log(self.element.contentDocument.title);
+ }, true, true);
+ }
+ }));
+
+See the `panel` module for a real-world example of usage of this module.
+
+Reference
+---------
+<api name="HiddenFrame">
+@class
+`HiddenFrame` objects represent hidden frames.
+<api name="HiddenFrame">
+@constructor
+Creates a hidden frame.
+@param options {object}
+ Options for the frame, with the following keys:
+ @prop onReady {function,array}
+ Functions to call when the frame is ready to load content. You must specify
+ an `onReady` callback and refrain from using the hidden frame until
+ the callback gets called, because hidden frames are not always ready to load
+ content the moment they are added.
+</api>
+
+<api name="add">
+@function
+Register a hidden frame, preparing it to load content.
+@param hiddenFrame {HiddenFrame} the frame to add
+</api>
+
+<api name="remove">
+@function
+Unregister a hidden frame, unloading any content that was loaded in it.
+@param hiddenFrame {HiddenFrame} the frame to remove
+</api>
+
+<api name="element">
+@property {DOMElement}
+The host application frame in which the page is loaded.
+</api>
+
+<api name="onReady">
+@property {array}
+Functions to call when the frame is ready to load content.
+</api>
+</api>
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/httpd.md b/tools/addon-sdk-1.7/packages/api-utils/docs/httpd.md
new file mode 100644
index 0000000..3f131f9
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/httpd.md
@@ -0,0 +1,31 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+Provides an HTTP server written in JavaScript for the Mozilla platform, which
+can be used in unit tests.
+
+The most basic usage is:
+
+ var {startServerAsync} = require("httpd");
+ var srv = startServerAsync(port, basePath);
+ require("unload").when(function cleanup() {
+ srv.stop(function() { // you should continue execution from this point.
+ })
+ });
+
+This starts a server in background (assuming you're running this code in an
+application that has an event loop, such as Firefox). The server listens at
+http://localhost:port/ and serves files from the specified directory. You
+can serve static content or use SJS scripts, as described in documentation
+on developer.mozilla.org.
+
+You can also use `nsHttpServer` to start the server manually:
+
+ var {nsHttpServer} = require("httpd");
+ var srv = new nsHttpServer();
+ // further documentation on developer.mozilla.org
+
+See
+[HTTP server for unit tests](https://developer.mozilla.org/En/HTTP_server_for_unit_tests)
+for general information.
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/light-traits.md b/tools/addon-sdk-1.7/packages/api-utils/docs/light-traits.md
new file mode 100644
index 0000000..367a6a9
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/light-traits.md
@@ -0,0 +1,295 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+[Traits](http://en.wikipedia.org/wiki/Trait_%28computer_science%29) are a simple
+mechanism for structuring object-oriented programs. They represent reusable and
+composable building blocks of functionality that factor out the common
+attributes and behavior of objects.
+
+They are a more robust alternative to
+[mixins](http://en.wikipedia.org/wiki/Mixins) and
+[multiple inheritance](http://en.wikipedia.org/wiki/Multiple_inheritance),
+because name clashes must be explicitly resolved and composition is commutative
+and associative (i.e. the order of traits in a composition is irrelevant).
+
+Use traits to share functionality between similar objects without duplicating
+code or creating complex inheritance chains.
+
+## Trait Creation ##
+
+To create a trait, call the `Trait` constructor function exported by this
+module, passing it a JavaScript object that specifies the properties of the
+trait.
+
+ let Trait = require('light-traits').Trait;
+ let t = Trait({
+ foo: "foo",
+ bar: function bar() {
+ return "Hi!"
+ },
+ baz: Trait.required
+ });
+
+Traits can both provide and require properties. A *provided* property is a
+property for which the trait itself provides a value. A *required* property is a
+property that the trait needs in order to function correctly but for which
+it doesn't provide a value.
+
+Required properties must be provided by another trait or by an object with a
+trait. Creation of an object with a trait will fail if required properties are
+not provided. Specify a required property by setting the value of the property
+to `Trait.required`.
+
+## Object Creation ##
+
+Create objects with a single trait by calling the trait's `create` method. The
+method takes a single argument, the object to serve as the new object's
+prototype. If no prototype is specified, the new object's prototype will be
+`Object.prototype`.
+
+ let t = Trait({
+ foo: 'foo',
+ bar: 2
+ });
+ let foo1 = t.create();
+ let foo2 = t.create({ name: 'Super' });
+
+## Trait Composition ##
+
+Traits are designed to be composed with other traits to create objects with the
+properties of multiple traits. To compose an object with multiple traits, you
+first create a composite trait and then use it to create the object. A composite
+trait is a trait that contains all of the properties of the traits from which it
+is composed. In the following example, MagnitudeTrait is a composite trait.
+
+ let EqualityTrait = Trait({
+ equal: Trait.required,
+ notEqual: function notEqual(x) {
+ return !this.equal(x)
+ }
+ });
+
+ let ComparisonTrait = Trait({
+ less: Trait.required,
+ notEqual: Trait.required,
+ greater: function greater(x) {
+ return !this.less(x) && this.notEqual(x)
+ }
+ });
+
+ let MagnitudeTrait = Trait.compose(EqualityTrait, ComparisonTrait);
+
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="-11 121 490 190" width="490px" height="190px">
+ <defs>
+ <marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="SharpArrow_Marker" viewBox="-4 -4 10 8" markerWidth="10" markerHeight="8" color="black">
+ <g>
+ <path d="M 5 0 L -3 -3 L 0 0 L 0 0 L -3 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1px"/>
+ </g>
+ </marker>
+ </defs>
+ <g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1">
+ <g>
+ <rect x="9" y="165.33334" width="141" height="14"/>
+ <rect x="9" y="165.33334" width="141" height="14" stroke="black" stroke-width="1px"/>
+ <text transform="translate(14 165.33334)" fill="black">
+ <tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="47.373047">notEqual</tspan>
+ </text>
+ <rect x="9" y="151.33334" width="141" height="14"/>
+ <rect x="9" y="151.33334" width="141" height="14" stroke="black" stroke-width="1px"/>
+ <text transform="translate(14 151.33334)" fill="red">
+ <tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="0" y="11" textLength="29.361328">equal</tspan>
+ </text>
+ <rect x="9" y="137.33334" width="141" height="14"/>
+ <rect x="9" y="137.33334" width="141" height="14" stroke="black" stroke-width="1px"/>
+ <text transform="translate(14 137.33334)" fill="black">
+ <tspan font-family="Helvetica" font-size="12" font-weight="bold" x="38.49707" y="11" textLength="54.00586">EqualityTrait</tspan>
+ </text>
+ <rect x="9" y="273" width="141" height="14"/>
+ <rect x="9" y="273" width="141" height="14" stroke="black" stroke-width="1px"/>
+ <text transform="translate(14 273)" fill="black">
+ <tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="38.021484">greater</tspan>
+ </text>
+ <rect x="9" y="259" width="141" height="14"/>
+ <rect x="9" y="259" width="141" height="14" stroke="black" stroke-width="1px"/>
+ <text transform="translate(14 259)" fill="red">
+ <tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="0" y="11" textLength="47.373047">notEqual</tspan>
+ </text>
+ <rect x="9" y="245" width="141" height="14"/>
+ <rect x="9" y="245" width="141" height="14" stroke="black" stroke-width="1px"/>
+ <text transform="translate(14 245)" fill="red">
+ <tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="0" y="11" textLength="21.339844">less</tspan>
+ </text>
+ <rect x="9" y="231" width="141" height="14"/>
+ <rect x="9" y="231" width="141" height="14" stroke="black" stroke-width="1px"/>
+ <text transform="translate(14 231)" fill="black">
+ <tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".15332031" y="11" textLength="112.67578">ComparisonTrait</tspan>
+ </text>
+ <rect x="317.75" y="235.5" width="141" height="14"/>
+ <rect x="317.75" y="235.5" width="141" height="14" stroke="black" stroke-width="1px"/>
+ <text transform="translate(322.75 235.5)" fill="black">
+ <tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="38.021484">greater</tspan>
+ </text>
+ <rect x="317.75" y="221.5" width="141" height="14"/>
+ <rect x="317.75" y="221.5" width="141" height="14" stroke="black" stroke-width="1px"/>
+ <text transform="translate(322.75 221.5)" fill="red">
+ <tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="0" y="11" textLength="21.339844">less</tspan>
+ </text>
+ <rect x="317.75" y="207.5" width="141" height="14"/>
+ <rect x="317.75" y="207.5" width="141" height="14" stroke="black" stroke-width="1px"/>
+ <text transform="translate(322.75 207.5)" fill="black">
+ <tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="47.373047">notEqual</tspan>
+ </text>
+ <rect x="317.75" y="193.5" width="141" height="14"/>
+ <rect x="317.75" y="193.5" width="141" height="14" stroke="black" stroke-width="1px"/>
+ <text transform="translate(322.75 193.5)" fill="red">
+ <tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="0" y="11" textLength="29.361328">equal</tspan>
+ </text>
+ <rect x="317.75" y="179.5" width="141" height="14"/>
+ <rect x="317.75" y="179.5" width="141" height="14" stroke="black" stroke-width="1px"/>
+ <text transform="translate(322.75 179.5)" fill="black">
+ <tspan font-family="Helvetica" font-size="12" font-weight="bold" x="31.83789" y="11" textLength="67.32422">MagnitudeTrait</tspan>
+ </text>
+ <path d="M 150 248.83887 L 158.89999 248.83887 L 235.9 248.83887 L 235.9 224.66113 L 308.85 224.66113 L 310.85 224.66113" marker-end="url(#SharpArrow_Marker)" stroke="black" stroke-linecap="butt" stroke-linejoin="miter" stroke-width="1px"/>
+ <path d="M 150 171.15845 L 158.89999 171.15845 L 233.9 171.15845 L 233.9 201.6749 L 308.85 201.6749 L 310.85 201.6749" marker-end="url(#SharpArrow_Marker)" stroke="black" stroke-linecap="butt" stroke-linejoin="miter" stroke-width="1px"/>
+ </g>
+ </g>
+</svg>
+
+## Trait Resolution ##
+
+Composite traits have conflicts when two of the traits in the composition
+provide properties with the same name but different values (when compared using
+the `===` strict equality operator). In the following example, `TC` has a
+conflict because `T1` and `T2` both define a `foo` property:
+
+ let T1 = Trait({
+ foo: function () {
+ // do something
+ },
+ bar: 'bar',
+ t1: 1
+ });
+
+ let T2 = Trait({
+ foo: function() {
+ // do something else
+ },
+ bar: 'bar',
+ t2: 2
+ });
+
+ let TC = Trait.compose(T1, T2);
+
+Attempting to create an object from a composite trait with conflicts throws a
+`remaining conflicting property` exception. To create objects from such traits,
+you must resolve the conflict.
+
+You do so by excluding or renaming the conflicting property of one of the
+traits. Excluding a property removes it from the composition, so the composition
+only acquires the property from the other trait. Renaming a property gives it a
+new, non-conflicting name at which it can be accessed.
+
+In both cases, you call the `resolve` method on the trait whose property you
+want to exclude or rename, passing it an object. Each key in the object is the
+name of a conflicting property; each value is either `null` to exclude the
+property or a string representing the new name of the property.
+
+For example, the conflict in the previous example could be resolved by excluding
+the `foo` property of the second trait.
+
+ let TC = Trait(T1, T2.resolve({ foo: null }));
+
+It could also be resolved by renaming the `foo` property of the first trait to
+`foo2`:
+
+ let TC = Trait(T1.resolve({ foo: "foo2" }), T2);
+
+When you resolve a conflict, the same-named property of the other trait (the one
+that wasn't excluded or renamed) remains available in the composition under its
+original name.
+
+## Constructor Functions ##
+
+When your code is going to create more than one object with traits, you may want
+to define a constructor function to create them. To do so, create a composite
+trait representing the traits the created objects should have, then define a
+constructor function that creates objects with that trait and whose prototype is
+the prototype of the constructor:
+
+ let PointTrait = Trait.compose(T1, T2, T3);
+ function Point(options) {
+ let point = PointTrait.create(Point.prototype);
+ return point;
+ }
+
+## Property Descriptor Maps ##
+
+Traits are designed to work with the new object manipulation APIs defined in
+[ECMAScript-262, Edition
+5](http://www.ecma-international.org/publications/standards/Ecma-262.htm) (ES5).
+Traits are also property descriptor maps that inherit from `Trait.prototype` to
+expose methods for creating objects and resolving conflicts.
+
+The following trait definition:
+
+ let FooTrait = Trait({
+ foo: "foo",
+ bar: function bar() {
+ return "Hi!"
+ },
+ baz: Trait.required
+ });
+
+Creates the following property descriptor map:
+
+ {
+ foo: {
+ value: 'foo',
+ enumerable: true,
+ configurable: true,
+ writable: true
+ },
+
+ bar: {
+ value: function b() {
+ return 'bar'
+ },
+ enumerable: true,
+ configurable: true,
+ writable: true
+ },
+
+ baz: {
+ get baz() { throw new Error('Missing required property: `baz`') }
+ set baz() { throw new Error('Missing required property: `baz`') }
+ },
+
+ __proto__: Trait.prototype
+ }
+
+Since Traits are also property descriptor maps, they can be used with built-in
+`Object.*` methods that accept such maps:
+
+ Object.create(proto, FooTrait);
+ Object.defineProperties(myObject, FooTrait);
+
+Note that conflicting and required properties won't cause exceptions to be
+thrown when traits are used with the `Object.*` methods, since those methods are
+not aware of those constraints. However, such exceptions will be thrown when the
+property with the conflict or the required but missing property is accessed.
+
+Property descriptor maps can also be used in compositions. This may be useful
+for defining non-enumerable properties, for example:
+
+ let TC = Trait.compose(
+ Trait({ foo: 'foo' }),
+ { bar: { value: 'bar', enumerable: false } }
+ );
+
+_When using property descriptor maps in this way, make sure the map is not the
+only argument to `Trait.compose`, since in that case it will be interpreted as
+an object literal with properties to be defined._
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/list.md b/tools/addon-sdk-1.7/packages/api-utils/docs/list.md
new file mode 100644
index 0000000..fd70698
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/list.md
@@ -0,0 +1,98 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Irakli Gozalishvili [gozala@mozilla.com] -->
+
+The `"list"` module provides base building blocks for composing lists.
+
+<api name="Iterable">
+@class
+Base trait that can be used to compose traits with non-standard
+enumeration behaviors.
+
+This trait is supposed to be used as part of a composition, since it only
+provides custom enumeration behavior to a composed object.
+It defines one required `_keyValueMap` property, that is used as a hash of
+"key-values" to iterate on during enumeration.
+
+<api name="Iterable">
+@constructor
+Constructs an `Iterable` object.
+</api>
+
+<api name="_keyValueMap">
+@property {Object}
+Hash map of key-values to iterate over. _Required_ property: that is, the
+property must be supplied by objects that compose this trait.
+_Note: That this property can be a getter if you need dynamic behavior._
+</api>
+
+</api>
+
+<api name="List">
+@class
+An ordered collection (also known as a sequence) disallowing duplicate
+elements. List is composed out of `Iterable`, therefore it provides custom
+enumeration behavior that is similar to array (enumerates only on the
+elements of the list).
+
+List is a base trait and is meant to be part of a
+composition, since all of its API is private except for the `length` property.
+
+**Examples:**
+
+ var MyList = List.compose({
+ add: function add(item1, item2, /*item3...*/) {
+ Array.slice(arguments).forEach(this._add.bind(this));
+ },
+ remove: function remove(item1, item2, /*item3...*/) {
+ Array.slice(arguments).forEach(this._remove.bind(this));
+ }
+ });
+ MyList('foo', 'bar', 'baz').length == 3; // true
+ new MyList('new', 'keyword').length == 2; // true
+ MyList.apply(null, [1, 2, 3]).length == 3; // true
+ let list = MyList();
+ list.length == 0; // true
+ list.add(1, 2, 3) == 3; // true
+
+<api name="List">
+@constructor
+Constructor can takes any number of elements and creates an instance of
+`List` populated with the specified elements.
+@param [element1] {Object|String|Number}
+@param [element2] {Object|String|Number}
+@param [...] {Object|String|Number}
+</api>
+
+<api name="length">
+@property {Number}
+Number of elements in this list.
+</api>
+
+<api name="_has">
+@method
+@param element {Object|Number|String}
+Returns `true` if this list contains the specified `element`.
+</api>
+<api name="_add">
+@method
+@param element {Object|Number|String}
+Appends the specified `element` to the end of this list, if it doesn't
+contain it.
+
+_Ignores the call if `element` is already contained._
+</api>
+<api name="_remove">
+@method
+@param element {Object|Number|String}
+Removes specified `element` from this list, if it contains it.
+
+_Ignores the call if `element` is not contained._
+</api>
+<api name="_clear">
+@method
+Removes all of the elements from this list.
+</api>
+</api>
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/match-pattern.md b/tools/addon-sdk-1.7/packages/api-utils/docs/match-pattern.md
new file mode 100644
index 0000000..0222e65
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/match-pattern.md
@@ -0,0 +1,246 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+The `match-pattern` module can be used to test strings containing URLs
+against simple patterns.
+
+## Specifying Patterns ##
+
+There are three ways you can specify patterns:
+
+* as an exact match string
+* using a wildcard in a string
+* using a regular expression
+
+### Exact Matches ###
+
+**A URL** matches only that URL. The URL must start with a scheme, end with a
+slash, and contain no wildcards.
+
+<table>
+
+ <colgroup>
+ <col width="30%">
+ <col width="35%">
+ <col width="35%">
+ </colgroup>
+
+ <tr>
+ <th>Example pattern</th>
+ <th>Example matching URLs</th>
+ <th>Example non-matching URLs</th>
+ </tr>
+
+ <tr>
+ <td><code>"http://example.com/"</code></td>
+ <td><code>http://example.com/</code></td>
+ <td><code>http://example.com</code><br>
+ <code>http://example.com/foo</code><br>
+ <code>https://example.com/</code><br>
+ <code>http://foo.example.com/</code></td>
+ </tr>
+
+</table>
+
+### Wildcards ###
+
+**A single asterisk** matches any URL with an `http`, `https`, or `ftp`
+scheme. For other schemes like `file`, use a scheme followed by an
+asterisk, as below.
+
+<table>
+
+ <colgroup>
+ <col width="30%">
+ <col width="35%">
+ <col width="35%">
+ </colgroup>
+
+ <tr>
+ <th>Example pattern</th>
+ <th>Example matching URLs</th>
+ <th>Example non-matching URLs</th>
+ </tr>
+
+ <tr>
+ <td><code>"*"</code></td>
+ <td><code>http://example.com/</code><br>
+ <code>https://example.com/</code><br>
+ <code>ftp://example.com/</code><br>
+ <code>http://bar.com/foo.js</code><br>
+ <code>http://foo.com/</code></td>
+ <td><code>file://example.js</code></td>
+ </tr>
+
+</table>
+
+**A domain name prefixed with an asterisk and dot** matches any URL of that
+domain or a subdomain, using any of `http`, `https`, `ftp`.
+
+<table>
+
+ <colgroup>
+ <col width="30%">
+ <col width="35%">
+ <col width="35%">
+ </colgroup>
+
+ <tr>
+ <th>Example pattern</th>
+ <th>Example matching URLs</th>
+ <th>Example non-matching URLs</th>
+ </tr>
+
+ <tr>
+ <td><code>"*.example.com"</code></td>
+ <td><code>http://example.com/</code><br>
+ <code>http://foo.example.com/</code><br>
+ <code>https://example.com/</code><br>
+ <code>http://example.com/foo</code><br>
+ <code>ftp://foo.example.com/</code></td>
+ <td><code>ldap://example.com</code><br>
+ <code>http://example.foo.com/</code></td>
+ </tr>
+
+</table>
+
+**A URL followed by an asterisk** matches that URL and any URL prefixed with
+the pattern.
+
+<table>
+
+ <colgroup>
+ <col width="30%">
+ <col width="35%">
+ <col width="35%">
+ </colgroup>
+
+ <tr>
+ <th>Example pattern</th>
+ <th>Example matching URLs</th>
+ <th>Example non-matching URLs</th>
+ </tr>
+
+ <tr>
+ <td><code>"https://foo.com/*"</code></td>
+ <td><code>https://foo.com/</code><br>
+ <code>https://foo.com/bar</code></td>
+ <td><code>http://foo.com/</code><br>
+ <code>https://foo.com</code><br>
+ <code>https://bar.foo.com/</code></td>
+ </tr>
+
+</table>
+
+**A scheme followed by an asterisk** matches all URLs with that scheme. To
+match local files, use `file://*`.
+
+<table>
+
+ <colgroup>
+ <col width="30%">
+ <col width="70%">
+ </colgroup>
+
+ <tr>
+ <th>Example pattern</th>
+ <th>Example matching URLs</th>
+ </tr>
+
+ <tr>
+ <td><code>"file://*"</code></td>
+ <td><code>file://C:/file.html</code><br>
+ <code>file:///home/file.png</code></td>
+ </tr>
+
+</table>
+
+### Regular Expressions ###
+
+You can specify patterns using a
+[regular expression](https://developer.mozilla.org/en/JavaScript/Guide/Regular_Expressions):
+
+ var { MatchPattern } = require("match-pattern");
+ var pattern = new MatchPattern(/.*example.*/);
+
+The regular expression is subject to restrictions based on those applied to the
+[HTML5 pattern attribute](http://dev.w3.org/html5/spec/common-input-element-attributes.html#attr-input-pattern). In particular:
+
+* The pattern must match the entire value, not just any subset. For example, the
+pattern `/moz.*/` will not match the URL `http://mozilla.org`.
+
+* The expression is compiled with the `global`, `ignoreCase`, and `multiline` flags
+ disabled. The `MatchPattern` constructor will throw an exception
+ if you try to set any of these flags.
+
+<table>
+
+ <colgroup>
+ <col width="30%">
+ <col width="35%">
+ <col width="35%">
+ </colgroup>
+
+ <tr>
+ <th>Example pattern</th>
+ <th>Example matching URLs</th>
+ <th>Example non-matching URLs</th>
+ </tr>
+
+ <tr>
+ <td><code>/.*moz.*/</code></td>
+ <td><code>http://foo.mozilla.org/</code><br>
+ <code>http://mozilla.org</code><br>
+ <code>https://mozilla.org</code><br>
+ <code>http://foo.com/mozilla</code><br>
+ <code>http://hemozoon.org</code><br>
+ <code>mozscheme://foo.org</code><br></td>
+ <td><code>http://foo.org</code><br>
+ </tr>
+
+ <tr>
+ <td><code>/http:\/\/moz.*/</code></td>
+ <td><code>http://mozilla.org</code><br>
+ <code>http://mozzarella.com</code></td>
+ <td><code>https://mozilla.org</code><br>
+ <code>http://foo.mozilla.org/</code><br>
+ <code>http://foo.com/moz</code></td>
+ </tr>
+
+ <tr>
+ <td><code>/http.*moz.*/</code><br></td>
+ <td><code>http://foo.mozilla.org/</code><br>
+ <code>http://mozilla.org</code><br>
+ <code>http://hemozoon.org/</code></td>
+ <td><code>ftp://http/mozilla.org</code></td>
+ </tr>
+
+</table>
+
+## Examples ##
+
+ var { MatchPattern } = require("match-pattern");
+ var pattern = new MatchPattern("http://example.com/*");
+ console.log(pattern.test("http://example.com/")); // true
+ console.log(pattern.test("http://example.com/foo")); // true
+ console.log(pattern.test("http://foo.com/")); // false!
+
+<api name="MatchPattern">
+@class
+<api name="MatchPattern">
+@constructor
+ This constructor creates match pattern objects that can be used to test URLs.
+@param pattern {string}
+ The pattern to use. See Patterns above.
+</api>
+
+<api name="test">
+@method
+ Tests a URL against the match pattern.
+@param url {string}
+ The URL to test.
+@returns {boolean}
+ True if the URL matches the pattern and false otherwise.
+</api>
+</api>
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/memory.md b/tools/addon-sdk-1.7/packages/api-utils/docs/memory.md
new file mode 100644
index 0000000..04de606
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/memory.md
@@ -0,0 +1,7 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+The `memory` module provides a concrete default implementation for the SDK's
+`memory` global. For documentation on the `memory` global, see the
+[Globals](packages/api-utils/globals.html) reference.
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/message-manager.md b/tools/addon-sdk-1.7/packages/api-utils/docs/message-manager.md
new file mode 100644
index 0000000..522c613
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/message-manager.md
@@ -0,0 +1,13 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Matteo Ferretti [zer0@mozilla.com] -->
+
+Overview
+--------
+The `message-manager` module provides a minimalist implementation
+of the [Message Manager](https://developer.mozilla.org/en/The_message_manager)
+APIs, in a single process environment.
+
+It's mainly used internally for Fennec Birch support. \ No newline at end of file
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/namespace.md b/tools/addon-sdk-1.7/packages/api-utils/docs/namespace.md
new file mode 100644
index 0000000..07ec96e
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/namespace.md
@@ -0,0 +1,71 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+Provides an API for creating namespaces for any given objects, which
+effectively may be used for creating fields that are not part of objects
+public API.
+
+ let { ns } = require('api-utils/namespace');
+ let aNamespace = ns();
+
+ aNamespace(publicAPI).secret = secret;
+
+One namespace may be used with multiple objects:
+
+ let { ns } = require('api-utils/namespace');
+ let dom = ns();
+
+ function View(element) {
+ let view = Object.create(View.prototype);
+ dom(view).element = element;
+ // ....
+ }
+ View.prototype.destroy = function destroy() {
+ let { element } = dom(this);
+ element.parentNode.removeChild(element);
+ // ...
+ };
+ // ...
+ exports.View = View;
+ // ...
+
+Also, multiple namespaces can be used with one object:
+
+ // ./widget.js
+
+ let { Cu } = require('chrome');
+ let { ns } = require('api-utils/namespace');
+ let { View } = require('./view');
+
+ // Note this is completely independent from View's internal Namespace object.
+ let sandboxes = ns();
+
+ function Widget(options) {
+ let { element, contentScript } = options;
+ let widget = Object.create(Widget.prototype);
+ View.call(widget, options.element);
+ sandboxes(widget).sandbox = Cu.Sandbox(element.ownerDocument.defaultView);
+ // ...
+ }
+ Widget.prototype = Object.create(View.prototype);
+ Widget.prototype.postMessage = function postMessage(message) {
+ let { sandbox } = sandboxes(this);
+ sandbox.postMessage(JSON.stringify(JSON.parse(message)));
+ ...
+ };
+ Widget.prototype.destroy = function destroy() {
+ View.prototype.destroy.call(this);
+ // ...
+ delete sandboxes(this).sandbox;
+ };
+ exports.Widget = Widget;
+
+In addition access to the namespace can be shared with other code by just
+handing them a namespace accessor function.
+
+ let { dom } = require('./view');
+ Widget.prototype.setInnerHTML = function setInnerHTML(html) {
+ dom(this).element.innerHTML = String(html);
+ };
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/observer-service.md b/tools/addon-sdk-1.7/packages/api-utils/docs/observer-service.md
new file mode 100644
index 0000000..4d7cb58
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/observer-service.md
@@ -0,0 +1,73 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Atul Varma [atul@mozilla.com] -->
+<!-- edited by Noelle Murata [fiveinchpixie@gmail.com] -->
+
+The `observer-service` module provides access to the
+application-wide observer service singleton.
+
+For a list of common observer topics across a variety of Mozilla-based
+applications, see the MDC page on
+[Observer Notifications](https://developer.mozilla.org/en/Observer_Notifications).
+
+## Observer Callbacks ##
+
+Observer callbacks are functions of the following form:
+
+ function callback(subject, data) {
+ /* Respond to the event notification here... */
+ }
+
+In the above example, `subject` is any JavaScript object, as is
+`data`. The particulars of what the two contain are specific
+to the notification topic.
+
+<api name="add">
+@function
+ Adds an observer callback to be triggered whenever a notification matching the
+ topic is broadcast throughout the application.
+
+@param topic {string}
+ The topic to observe.
+
+@param callback {function,object}
+ Either a function or an object that implements [`nsIObserver`](http://mxr.mozilla.org/mozilla-central/source/xpcom/ds/nsIObserver.idl).
+ If a function, then it is called when the notification occurs. If an object,
+ then its `observe()` method is called when the notification occurs.
+
+@param [thisObject] {object}
+ An optional object to use as `this` when a function callback is called.
+</api>
+
+<api name="remove">
+@function
+ Unsubscribes a callback from being triggered whenever a notification
+ matching the topic is broadcast throughout the application.
+
+@param topic {string}
+ The topic being observed by the previous call to `add()`.
+
+@param callback {function,object}
+ The callback subscribed in the previous call to `add()`, either a function or
+ object.
+
+@param [thisObject] {object}
+ If `thisObject` was passed to the previous call to `add()`, it should be
+ passed to `remove()` as well.
+</api>
+
+<api name="notify">
+@function
+ Broadcasts a notification event for a topic, passing a subject and data to all
+ applicable observers in the application.
+
+@param topic {string}
+ The topic about which to broadcast a notification.
+
+@param [subject] {value}
+ Optional information about the topic. This can be any JS object or primitive.
+ If you have multiple values to pass to observers, wrap them in an object,
+ e.g., `{ foo: 1, bar: "some string", baz: myObject }`.
+</api>
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/plain-text-console.md b/tools/addon-sdk-1.7/packages/api-utils/docs/plain-text-console.md
new file mode 100644
index 0000000..d139587
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/plain-text-console.md
@@ -0,0 +1,7 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+The `plain-text-console` module provides a minimalist implementation
+of the [console](dev-guide/console.html) global,
+which simply logs all messages to standard output.
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/preferences-service.md b/tools/addon-sdk-1.7/packages/api-utils/docs/preferences-service.md
new file mode 100644
index 0000000..03b4725
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/preferences-service.md
@@ -0,0 +1,116 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Myk Melez [myk@mozilla.org] -->
+<!-- contributed by Daniel Aquino [mr.danielaquino@gmail.com] -->
+<!-- contributed by Atul Varma [atul@mozilla.com] -->
+<!-- edited by Noelle Murata [fiveinchpixie@gmail.com] -->
+
+The `preferences-service` module provides access to the
+application-wide preferences service singleton.
+
+
+<api name="set">
+@function
+Sets the application preference `name` to `value`.
+@param name {string} Preference name.
+@param value {string,number,bool} Preference value.
+
+**Example:**
+
+ var name = "extensions.checkCompatibility.nightly";
+ require("preferences-service").set(name, false);
+</api>
+
+
+<api name="get">
+@function
+Gets the application preference `name`.
+@param name {string}
+@param defaultValue {string,number,bool} Preference value.
+@returns {string,number,bool} Preference value, returns a default value if no
+preference is set.
+
+**Example:**
+
+ var name = "extensions.checkCompatibility.nightly";
+ var nightlyCompatChk = require("preferences-service").get(name);
+</api>
+
+
+<api name="has">
+@function
+@param name {string} Preference name.
+@returns {bool} Returns whether or not the application preference `name` exists.
+
+**Example:**
+
+ var name = "extensions.checkCompatibility.nightly";
+ if (require("preferences-service").has(name)) {
+ // ...
+ }
+</api>
+
+
+<api name="isSet">
+@function
+@param name {string} Preference name.
+@returns {bool}
+Returns whether or not the application preference `name` both exists
+and has been set to a non-default value by the user (or a program
+acting on the user's behalf).
+
+**Example:**
+
+ var name = "extensions.checkCompatibility.nightly";
+ if (require("preferences-service").isSet(name)) {
+ // ...
+ }
+</api>
+
+
+<api name="reset">
+@function
+Clears a non-default, user-set value from the application preference
+`name`. If no user-set value is defined on `name`, the function
+does nothing. If no default value exists the preference will cease to exist.
+@param name {string} Preference name.
+
+**Example:**
+
+ var name = "extensions.checkCompatibility.nightly";
+ require("preferences-service").reset(name);
+</api>
+
+<api name="getLocalized">
+@function
+Gets the localized value for an application preference `name`.
+@param name {string}
+@param defaultValue {string} Preference value.
+@returns {string} Localized preference value, returns a default value if no
+preference is set. Some preferences refer to a properties file.
+So that `prefs.get` returns the properties file URL whereas
+`prefs.getLocalized` returns the value defined in the properties file.
+
+**Example:**
+
+ var prefs = require("preferences-service");
+ var name = "general.useragent.locale";
+ prefs.get(name); // is equal to "chrome://global/locale/intl.properties"
+ prefs.getLocalized(name) // is equal to "en-US"
+
+</api>
+
+<api name="setLocalized">
+@function
+Sets the localized application preference `name` to `value`.
+@param name {string} Preference name.
+@param value {string} Preference value, a URL to a properties file
+
+**Example:**
+
+ require("preferences-service").set("general.useragent.locale",
+ "chrome://global/locale/intl.properties");
+
+</api>
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/promise.md b/tools/addon-sdk-1.7/packages/api-utils/docs/promise.md
new file mode 100644
index 0000000..d67b820
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/promise.md
@@ -0,0 +1,394 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+## Rationale
+
+Most of the JS APIs are asynchronous complementing it's non-blocking nature.
+While this has a good reason and many advantages, it comes with a price.
+Instead of structuring our programs into logical black boxes:
+
+ function blackbox(a, b) {
+ var c = assemble(a);
+ return combine(b, c);
+ }
+
+
+We're forced into continuation passing style, involving lot's of machinery:
+
+ function sphagetti(a, b, callback) {
+ assemble(a, function continueWith(error, c) {
+ if (error) callback(error);
+ else combine(b, c, callback);
+ });
+ }
+
+This style also makes doing things in sequence hard:
+
+ widget.on('click', function onClick() {
+ promptUserForTwitterHandle(function continueWith(error, handle) {
+ if (error) return ui.displayError(error);
+ twitter.getTweetsFor(handle, funtion continueWith(error, tweets) {
+ if (error) return ui.displayError(error);
+ ui.showTweets(tweets);
+ });
+ });
+ });
+
+Doing things in parallel is even harder:
+
+ var tweets, answers, checkins;
+ twitter.getTweetsFor(user, function continueWith(result) {
+ tweets = result;
+ somethingFinished();
+ });
+
+ stackOverflow.getAnswersFor(question, function continueWith(result) {
+ answers = result;
+ somethingFinished();
+ });
+
+ fourSquare.getCheckinsBy(user, function continueWith(result) {
+ checkins=result;
+ somethingFinished();
+ });
+
+ var finished = 0;
+ functions somethingFinished() {
+ if (++finished === 3)
+ ui.show(tweets, answers, checkins);
+ }
+
+This also makes error handling quite of an adventure.
+
+## Promises
+
+Consider another approach, where instead of continuation passing via `callback`,
+function returns an object, that represents eventual result, either successful
+or failed. This object is a promise, both figuratively and by name, to
+eventually resolve. We can call a function on the promise to observe
+either its fulfillment or rejection. If the promise is rejected and the
+rejection is not explicitly observed, any derived promises will be implicitly
+rejected for the same reason.
+
+In the Add-on SDK we follow
+[CommonJS Promises/A](http://wiki.commonjs.org/wiki/Promises/A) specification
+and model a promise as an object with a `then` method, which can be used to get
+the eventual return (fulfillment) value or thrown exception (rejection):
+
+ foo().then(function success(value) {
+ // ...
+ }, function failure(reason) {
+ // ...
+ });
+
+If `foo` returns a promise that gets fulfilled with the `value`, `success`
+callback (the value handler) will be called with that `value`. However,
+if the returned promise gets rejected, the `failure` callback (the error
+handler) will be called with the `reason` of an error.
+
+## Propagation
+
+The `then` method of a promise returns a new promise that is resolved with the
+return value of either handler. Since function can either return value or throw
+an exception, only one handler will be ever called.
+
+
+ var bar = foo().then(function success(value) {
+ // compute something from a value...
+ }, function failure(reason) {
+ // handle an error...
+ });
+
+In this example `bar` is a promise and it's fulfilled by one of two handlers
+that are responsible for:
+
+ - If handler returns a value, `bar` will be resolved with it.
+ - If handler throws an exception, `bar` will be rejected with it.
+ - If handler returns a **promise**, `bar` will "become" that promise. To be
+ more precise it will be resolved with a resolution value of the returned
+ promise, which will appear and feel as if it was that returned promise.
+
+If the `foo()` promise gets rejected and you omit the error handler, the
+**error** will propagate to `bar` (`bar` will be rejected with that error):
+
+ var bar = foo().then(function success(value) {
+ // compute something out of the value...
+ });
+
+If the `foo()` promise gets fulfilled and you omit the value handler, the
+**value** will propagate to `bar` (`bar` will be fulfilled with that value):
+
+ var bar = foo().then(null, function failure(error) {
+ // handle error...
+ });
+
+
+## Chaining
+
+There are two ways to chain promise operations. You can chain them using either
+inside or outside handlers.
+
+### Flat chaining
+
+You can use `then` for chaining intermediate operations on promises
+(`var data = readAsync().then(parse).then(extract)`). You can chain multiple
+`then` functions, because `then` returns a promise resolved to a return value
+of an operation and errors propagate through the promise chains. In general
+good rule of thumb is to prefer `then` based flat chaining. It makes code
+easier to read and make changes later:
+
+ var data = readAsync(url). // read content of url asynchronously
+ then(parse). // parse content from the url
+ then(extractQuery). // extract SQL query
+ then(readDBAsync); // exectue extracted query against DB
+
+### Nested chaining
+
+Flat chaining is not always an option though, as in some cases you may want to
+capture an intermediate values of the chain:
+
+ var result = readAsync(url).then(function(source) {
+ var json = parse(source)
+ return readDBAsync(extractQuery(json)).then(function(data) {
+ return writeAsync(json.url, data);
+ });
+ });
+
+In general, nesting is useful for computing values from more then one promise:
+
+ function eventualAdd(a, b) {
+ return a.then(function (a) {
+ return b.then(function (b) {
+ return a + b;
+ });
+ });
+ }
+
+ var c = eventualAdd(aAsync(), bAsync());
+
+## Error handling
+
+One sometimes-unintuitive aspect of promises is that if you throw an exception
+in the value handler, it will not be be caught by the error handler.
+
+ readAsync(url).then(function (value) {
+ throw new Error("Can't bar.");
+ }, function (error) {
+ // We only get here if `readAsync` fails.
+ });
+
+To see why this is, consider the parallel between promises and `try`/`catch`.
+We are `try`-ing to execute `readAsync()`: the error handler represents a
+`catch` for `readAsync()`, while the value handler represents code that happens
+*after* the `try`/`catch` block. That code then needs its own `try`/`catch`
+block to handle errors there.
+
+In terms of promises, this means chaining your error handler:
+
+ readAsync(url).
+ then(parse).
+ then(null, function handleParseError(error) {
+ // handle here both `readAsync` and `parse` errors.
+ });
+
+
+# Consuming promises
+
+In general, whole purpose of promises is to avoid a callback spaghetti in the
+code. As a matter of fact it would be great if we could convert any synchronous
+functions to asynchronous by making it aware of promises. Module exports
+`promised` function to do exactly that:
+
+ const { promised } = require('api-utils/promise');
+ function sum(x, y) { return x + y }
+ var sumAsync = promised(sum);
+
+ var c = sum(a, b);
+ var cAsync = asyncSum(aAsync(), bAsinc());
+
+`promised` takes normal function and composes new promise aware version of it
+that may take both normal values and promises as arguments and returns promise
+that will resolve to value that would have being returned by an original
+function if it was called with fulfillment values of given arguments.
+
+This technique is so powerful that it can replace most of the promise utility
+functions provided by other promise libraries. For example grouping promises
+to observe single resolution of all of them is as simple as this:
+
+ var group = promised(Array);
+ var abc = group(aAsync, bAsync, cAsync).then(function(items) {
+ return items[0] + items[1] + items[2];
+ });
+
+# Making promises
+
+Everything above assumes you get a promise from somewhere else. This
+is the common case, but every once in a while, you will need to create a
+promise from scratch. Add-on SDK's `promise` module provides API for doing
+that.
+
+## defer
+
+Module exports `defer` function, which is where all promises ultimately
+come from. Lets see implementation of `readAsync` that we used in lot's
+of examples above:
+
+ const { defer } = require('api-utils/promise');
+ function readAsync(url) {
+ var deferred = defer();
+
+ let xhr = new XMLHttpRequest();
+ xhr.open("GET", url, true);
+ xhr.onload = function() {
+ deferred.resolve(xhr.responseText);
+ }
+ xhr.onerror = function(event) {
+ deferred.reject(event);
+ }
+ xhr.send();
+
+ return deferred.promise;
+ }
+
+So `defer` returns an object that contains `promise` and two `resolve`, `reject`
+functions that can be used to resolve / reject that `promise`. **Note:** that
+promise can be rejected or resolved and only once. All subsequent attempts will be
+ignored.
+
+Another simple example may be `delay` function that returns promise which
+is fulfilled with a given `value` in a given `ms`, kind of promise based
+alternative to `setTimeout`:
+
+ function delay(ms, value) {
+ let { promise, resolve } = defer();
+ setTimeout(resolve, ms, value);
+ return promise;
+ }
+
+ delay(10, 'Hello world').then(console.log);
+ // After 10ms => 'Helo world'
+
+# Advanced usage
+
+If general `defer` and `promised` should be enough to doing almost anything
+you may think of with promises, but once you start using promises extensively
+you may discover some missing pieces and this section of documentation may
+help you to discover them.
+
+## Doing things concurrently
+
+So far we have being playing with promises that do things sequentially, but
+there are bunch of cases where one would need to do things concurrently. In the
+following example we implement functions that takes multiple promises and
+returns one that resolves to first on being fulfilled:
+
+ function race() {
+ let { promise, resolve } = defer();
+ Array.slice(arguments).forEach(function(promise) {
+ promise.then(resolve);
+ });
+ return promise;
+ }
+
+ var asyncAorB = race(readAsync(urlA), readAsync(urlB));
+
+*Note: that this implementation forgives failures and would fail if all
+promises fail to resolve.*
+
+There are cases when promise may or may not be fulfilled in a reasonable time.
+In such cases it's useful to put a timer on such tasks:
+
+ function timeout(promise, ms) {
+ let deferred = defer();
+ promise.then(deferred.resolve, deferred.reject);
+ delay(ms, 'timeout').then(deferred.reject);
+ return deferred.promise;
+ }
+
+ var tweets = readAsync(url);
+ timeout(tweets, 20).then(function(data) {
+ ui.display(data);
+ }, function() {
+ alert('Network is being too slow, try again later');
+ });
+
+## Alternative promise APIs
+
+There may be a cases where you will want to provide more than just `then`
+method on your promises. In fact some other promise frameworks do that.
+Such use cases are also supported. Earlier described `defer` may be passed
+optional `prototype` argument, in order to make returned promise and all
+the subsequent promises decedents of that `prototype`:
+
+ let { promise, resolve } = defer({
+ get: function get(name) {
+ return this.then(function(value) {
+ return value[name];
+ })
+ }
+ });
+
+ promise.get('foo').get('bar').then(console.log);
+ resolve({ foo: { bar: 'taram !!' } });
+
+ // => 'taram !!'
+
+Also `promised` function maybe passed second optional `prototype` argument to
+achieve same effect.
+
+## Treat all values as promises
+
+Module provides a simple function for wrapping values into promises:
+
+ const { resolve } = require('api-utils/promise');
+
+ var a = resolve(5).then(function(value) {
+ return value + 2
+ });
+ a.then(console.log); // => 7
+
+Also `resolve` not only takes values, but also promises. If you pass it
+a promise it will return new identical one:
+
+ const { resolve } = require('api-utils/promise');
+
+ resolve(resolve(resolve(3))).then(console.log); // => 3
+
+If this construct may look strange at first, but it becomes quite handy
+when writing functions that deal with both promises and values. In such
+cases it's usually easier to wrap value into promise than branch on value
+type:
+
+ function or(a, b) {
+ var second = resolve(b).then(function(bValue) { return !!bValue });
+ return resolve(a).then(function(aValue) {
+ return !!aValue || second;
+ }, function() {
+ return second;
+ })
+ }
+
+*Note: We could not use `promised` function here, as they reject returned
+promise if any of the given arguments is rejected.*
+
+If you need to customize your promises even further you may pass `resolve` a
+second optional `prototype` argument that will have same effect as with `defer`.
+
+## Treat errors as promises
+
+Now that we can create all kinds of eventual values, it's useful to have a
+way to create eventual errors. Module exports `reject` exactly for that.
+It takes anything as an argument and returns a promise that is rejected with
+it.
+
+ const { reject } = require('api-utils/promise');
+
+ var boom = reject(Error('boom!'));
+
+ future(function() {
+ return Math.random() < 0.5 ? boom : value
+ })
+
+As with rest of the APIs error may be given second optional `prototype`
+argument to customize resulting promise to your needs.
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/querystring.md b/tools/addon-sdk-1.7/packages/api-utils/docs/querystring.md
new file mode 100644
index 0000000..30f4117
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/querystring.md
@@ -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/. -->
+
+Module exports utility functions for working with query strings.
+
+### stringify
+
+Object may be serialize to a query string via exported `stringify` function:
+
+ querystring.stringify({ foo: 'bar', baz: 4 }); // => 'foo=bar&baz=4'
+
+Optionally `separator` and `assignment` arguments may be passed to
+override default `'&'` and`'='` characters:
+
+ querystring.stringify({ foo: 'bar', baz: 4 }, ';', ':'); // => 'foo:bar;baz:4'
+
+### parse
+
+Query string may be deserialized to an object via exported `parse`
+function:
+
+ querystring.parse('foo=bar&baz=bla') // => { foo: 'bar', baz: 'bla' }
+
+Optionally `separator` and `assignment` arguments may be passed to
+override default `'&'` and `'='` characters:
+
+ querystring.parse('foo:bar|baz:bla', '|', ':') // => { foo: 'bar', baz: 'bla' }
+
+### escape
+
+The escape function used by `stringify` to encodes a string safely
+matching RFC 3986 for `application/x-www-form-urlencoded`.
+
+### unescape
+
+The unescape function used by `parse` to decode a string safely.
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/runtime.md b/tools/addon-sdk-1.7/packages/api-utils/docs/runtime.md
new file mode 100644
index 0000000..c8d8ed3
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/runtime.md
@@ -0,0 +1,75 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Wes Kocher [kwierso@gmail.com] -->
+
+The `runtime` module provides access to information about Firefox's
+runtime environment. All properties exposed are read-only.
+
+For more information, see [nsIXULRuntime][nsIXULRuntime].
+[nsIXULRuntime]: https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIXULRuntime
+
+<api name="inSafeMode">
+@property {boolean}
+ This value is `true` if Firefox was started in safe mode,
+ otherwise `false`.
+</api>
+
+<api name="OS">
+@property {string}
+ A string identifying the current operating system. For example, .
+ `"WINNT"`, `"Darwin"`, or `"Linux"`. See [OS_TARGET][OS_TARGET]
+ for a more complete list of possible values.
+
+[OS_TARGET]: https://developer.mozilla.org/en/OS_TARGET
+</api>
+
+<api name="processType">
+@property {long}
+ The type of the caller's process, which will be one of these constants\:
+<table>
+ <tr>
+ <th>Constant</th>
+ <th>Value</th>
+ <th>Description</th>
+ </tr>
+
+ <tr>
+ <td>PROCESS_TYPE_DEFAULT</td>
+ <td>0</td>
+ <td>The default (chrome) process.</td>
+ </tr>
+
+ <tr>
+ <td>PROCESS_TYPE_PLUGIN</td>
+ <td>1</td>
+ <td>A plugin subprocess.</td>
+ </tr>
+
+ <tr>
+ <td>PROCESS_TYPE_CONTENT</td>
+ <td>2</td>
+ <td>A content subprocess.</td>
+ </tr>
+
+ <tr>
+ <td>PROCESS_TYPE_IPDLUNITTEST</td>
+ <td>3</td>
+ <td>An IPDL unit testing subprocess.</td>
+ </tr>
+</table>
+</api>
+
+<api name="widgetToolkit">
+@property {string}
+ A string identifying the target widget toolkit in use.
+</api>
+
+<api name="XPCOMABI">
+@property {string}
+ A string identifying the [ABI][ABI] of the current processor and compiler vtable.
+ This string takes the form \<`processor`\>-\<`compilerABI`\>,
+ for example\: "`x86-msvc`" or "`ppc-gcc3`".
+[ABI]: https://developer.mozilla.org/en/XPCOM_ABI
+</api>
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/sandbox.md b/tools/addon-sdk-1.7/packages/api-utils/docs/sandbox.md
new file mode 100644
index 0000000..026b569
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/sandbox.md
@@ -0,0 +1,51 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+Provides an API for creating javascript sandboxes and for executing scripts
+in them.
+
+### Create a sandbox ###
+
+For the starting point you need to create a sandbox:
+
+ const { sandbox, evaluate, load } = require("api-utils/sandbox");
+ let scope = sandbox('http://example.com');
+
+Argument passed to the sandbox defines it's privileges. Argument may be an URL
+string, in which case sandbox will get exact same privileges as a scripts
+loaded from that URL. Argument also could be a DOM window object, to inherit
+privileges from the window being passed. Finally if argument is omitted or is
+`null` sandbox will have a chrome privileges giving it access to all the XPCOM
+components. Optionally `sandbox` function can be passed a second optional
+argument (See [sandbox documentation on MDN](https://developer.mozilla.org/en/Components.utils.Sandbox#Optional_parameter)
+for details).
+
+### Evaluate code ###
+
+Module provides `evaluate` function that allows executing code in the given
+sandbox:
+
+ evaluate(scope, 'var a = 5;');
+ evaluate(scope, 'a + 2;'); //=> 7
+
+More details about evaluated script may be passed via optional arguments that
+may improve an exception reporting:
+
+ // Evaluate code as if it was loaded from 'http://foo.com/bar.js' and
+ // start from 2nd line.
+ evaluate(scope, 'a ++', 'http://foo.com/bar.js', 2);
+
+Version of JavaScript can be also specified via optional argument:
+
+ evaluate(scope, 'let b = 2;', 'bar.js', 1, '1.5');
+ // throws cause `let` is not defined in JS 1.5.
+
+### Loading scripts ###
+
+API provides limited API for loading scripts right form the local URLs,
+but data: URLs are supported.
+
+ load(scope, 'resource://path/to/my/script.js');
+ load(scope, 'file:///path/to/script.js');
+ load(scope, 'data:,var a = 5;'); \ No newline at end of file
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/tab-browser.md b/tools/addon-sdk-1.7/packages/api-utils/docs/tab-browser.md
new file mode 100644
index 0000000..295fcc7
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/tab-browser.md
@@ -0,0 +1,140 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Dietrich Ayala [dietrich@mozilla.com] -->
+
+The `tab-browser` module is a low-level API that provides privileged
+access to browser tab events and actions.
+
+Introduction
+------------
+
+The `tab-browser` module contains helpers for tracking tabbrowser elements
+and tabs, as well as a few utilities for actions such as opening a new
+tab, and catching all tab content loads.
+
+This is a low-level API that has full privileges, and is intended to be used
+by SDK internal modules. If you just need easy access to tab events for your
+add-on, use the Tabs module (JEP 110).
+
+<api name="activeTab">
+@property {element}
+The XUL tab element of the currently active tab.
+</api>
+
+<api name="addTab">
+@function
+Adds a new tab.
+
+**Example**
+
+ var tabBrowser = require("tab-browser");
+ tabBrowser.addTab("http://google.com");
+
+ var tabBrowser = require("tab-browser");
+ tabBrowser.addTab("http://google.com", {
+ inBackground: true
+ });
+
+ var tabBrowser = require("tab-browser");
+ tabBrowser.addTab("http://google.com", {
+ inNewWindow: true,
+ onLoad: function(tab) {
+ console.log("tab is open.");
+ }
+ });
+
+@returns {element}
+The XUL tab element of the newly created tab.
+
+@param URL {string}
+The URL to be opened in the new tab.
+
+@param options {object}
+Options for how and where to open the new tab.
+
+@prop [inNewWindow] {boolean}
+An optional parameter whose key can be set in `options`.
+If true, the tab is opened in a new window. Default is false.
+
+@prop [inBackground] {boolean}
+An optional parameter whose key can be set in `options`.
+If true, the tab is opened adjacent to the active tab, but not
+switched to. Default is false.
+
+@prop [onLoad] {function}
+An optional parameter whose key can be set in `options`.
+A callback function that is called once the tab has loaded.
+The XUL element for the tab is passed as a parameter to
+this function.
+</api>
+
+<api name="Tracker">
+@function
+Register a delegate object to be notified when tabbrowsers are created
+and destroyed.
+
+The onTrack method will be called once per pre-existing tabbrowser, upon
+tracker registration.
+
+**Example**
+
+ var tabBrowser = require("tab-browser");
+ let tracker = {
+ onTrack: function(tabbrowser) {
+ console.log("A new tabbrowser is being tracked.");
+ },
+ onUntrack: function(tabbrowser) {
+ console.log("A tabbrowser is no longer being tracked.");
+ }
+ };
+ tabBrowser.Tracker(tracker);
+
+@param delegate {object}
+Delegate object to be notified each time a tabbrowser is created or destroyed.
+The object should contain the following methods:
+
+@prop [onTrack] {function}
+Method of delegate that is called when a new tabbrowser starts to be tracked.
+The tabbrowser element is passed as a parameter to this method.
+
+@prop [onUntrack] {function}
+Method of delegate that is called when a tabbrowser stops being tracked.
+The tabbrowser element is passed as a parameter to this method.
+</api>
+
+<api name="TabTracker">
+@function
+Register a delegate object to be notified when tabs are opened and closed.
+
+
+The onTrack method will be called once per pre-existing tab, upon
+tracker registration.
+
+**Example**
+
+ var tabBrowser = require("tab-browser");
+ let tracker = {
+ onTrack: function(tab) {
+ console.log("A new tab is being tracked.");
+ },
+ onUntrack: function(tab) {
+ console.log("A tab is no longer being tracked.");
+ }
+ };
+ tabBrowser.TabTracker(tracker);
+
+@param delegate {object}
+Delegate object to be notified each time a tab is opened or closed.
+The object should contain the following methods:
+
+@prop [onTrack] {function}
+Method of delegate that is called when a new tab starts to be tracked.
+The tab element is passed as a parameter to this method.
+
+@prop [onUntrack] {function}
+Method of delegate that is called when a tab stops being tracked.
+The tab element is passed as a parameter to this method.
+</api>
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/text-streams.md b/tools/addon-sdk-1.7/packages/api-utils/docs/text-streams.md
new file mode 100644
index 0000000..55f177c
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/text-streams.md
@@ -0,0 +1,102 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Drew Willcoxon [adw@mozilla.com] -->
+<!-- edited by Noelle Murata [fiveinchpixie@gmail.com] -->
+
+The `text-streams` module provides streams for reading and writing text using
+particular character encodings.
+
+<api name="TextReader">
+@class
+<api name="TextReader">
+@constructor
+ Creates a buffered input stream that reads text from a backing stream using a
+ given text encoding.
+@param inputStream {stream}
+ The backing stream, an [`nsIInputStream`](http://mxr.mozilla.org/mozilla-central/source/xpcom/io/nsIInputStream.idl).
+ It must already be opened.
+@param [charset] {string}
+ `inputStream` is expected to be in the character encoding named by this value.
+ If not specified, "UTF-8" is assumed. See [`nsICharsetConverterManager.idl`](http://mxr.mozilla.org/mozilla-central/source/intl/uconv/idl/nsICharsetConverterManager.idl)
+ for documentation on how to determine other valid values for this.
+</api>
+
+<api name="closed">
+@property {boolean}
+ True if the stream is closed.
+</api>
+
+<api name="close">
+@method
+ Closes both the stream and its backing stream.
+</api>
+
+<api name="read">
+@method
+ Reads and returns a string from the stream. If the stream is closed, an
+ exception is thrown.
+@param [numChars] {number}
+ The number of characters to read. If not given, the remainder of the stream
+ is read.
+@returns {string}
+ The string read. If the stream is at the end, the empty string is returned.
+</api>
+
+</api>
+
+
+<api name="TextWriter">
+@class
+<api name="TextWriter">
+@constructor
+ Creates a buffered output stream that writes text to a backing stream using a
+ given text encoding.
+@param outputStream {stream}
+ The backing stream, an [`nsIOutputStream`](http://mxr.mozilla.org/mozilla-central/source/xpcom/io/nsIOutputStream.idl).
+ It must already be opened.
+@param [charset] {string}
+ Text will be written to `outputStream` using the character encoding named by
+ this value. If not specified, "UTF-8" is assumed. See [`nsICharsetConverterManager.idl`](http://mxr.mozilla.org/mozilla-central/source/intl/uconv/idl/nsICharsetConverterManager.idl)
+ for documentation on how to determine other valid values for this.
+</api>
+
+<api name="closed">
+@property {boolean}
+ True if the stream is closed.
+</api>
+
+<api name="close">
+@method
+ Flushes the backing stream's buffer and closes both the stream and the backing
+ stream. If the stream is already closed, an exception is thrown.
+</api>
+
+<api name="flush">
+@method
+ Flushes the backing stream's buffer.
+</api>
+
+<api name="write">
+@method
+ Writes a string to the stream. If the stream is closed, an exception is
+ thrown.
+@param str {string}
+ The string to write.
+</api>
+
+<api name="writeAsync">
+@method
+ Writes a string on a background thread. After the write completes, the
+ backing stream's buffer is flushed, and both the stream and the backing stream
+ are closed, also on the background thread. If the stream is already closed,
+ an exception is thrown immediately.
+@param str {string}
+ The string to write.
+@param [callback] {callback}
+ *`callback`*, if given, must be a function. It's called as `callback(error)`
+ when the write completes. `error` is an `Error` object or undefined if there
+ was no error. Inside *`callback`*, `this` is the `TextWriter` object.
+</api>
+</api>
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/traceback.md b/tools/addon-sdk-1.7/packages/api-utils/docs/traceback.md
new file mode 100644
index 0000000..b52c183
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/traceback.md
@@ -0,0 +1,66 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Atul Varma [atul@mozilla.com] -->
+<!-- edited by Noelle Murata [fiveinchpixie@gmail.com] -->
+
+
+The `traceback` module contains functionality similar to
+Python's [traceback](http://docs.python.org/library/traceback.html) module.
+
+## JSON Traceback Objects ##
+
+Tracebacks are stored in JSON format. The stack is represented as an
+array in which the most recent stack frame is the last element; each
+element thus represents a stack frame and has the following keys:
+
+<table>
+ <tr>
+ <td><code>filename</code></td>
+ <td>The name of the file that the stack frame takes place in.</td>
+ </tr>
+ <tr>
+ <td><code>lineNo</code></td>
+ <td>The line number is being executed at the stack frame.</td>
+ </tr>
+ <tr>
+ <td><code>funcName</code></td>
+ <td>The name of the function being executed at the stack frame, or
+ <code>null</code> if the function is anonymous or the stack frame is
+ being executed in a top-level script or module.</td>
+ </tr>
+</table>
+
+<api name="fromException">
+@function
+ Attempts to extract the traceback from *`exception`*.
+
+@returns {traceback}
+ JSON representation of the traceback or `null` if not found.
+
+@param exception {exception}
+ exception where exception is an `nsIException`.
+</api>
+
+See [nsIException](https://developer.mozilla.org/en/NsIException) for more
+information.
+
+<api name="get">
+@function
+
+@returns {JSON}
+ Returns the JSON representation of the stack at the point that this
+ function is called.
+</api>
+
+<api name="format">
+@function
+Given a JSON representation of the stack or an exception instance,
+returns a formatted plain text representation of it, similar to
+Python's formatted stack tracebacks. If no argument is provided, the
+stack at the point this function is called is used.
+
+@param [tbOrException] {object}
+</api>
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/traits.md b/tools/addon-sdk-1.7/packages/api-utils/docs/traits.md
new file mode 100644
index 0000000..910cd9a
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/traits.md
@@ -0,0 +1,244 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Irakli Gozalishvil [gozala@mozilla.com] -->
+
+The `traits` module provides base building blocks for secure object
+composition. It exports base trait / constructor function that
+constructs an instance of `Trait`.
+
+[Traits](http://en.wikipedia.org/wiki/Trait_%28computer_science%29) are a
+simple composition mechanism for structuring object-oriented programs. Traits
+are similar to
+[interfaces](http://en.wikipedia.org/wiki/Interface_%28object-oriented_programming%29),
+except that they often define only a part of an object's data and behavior and
+are intended to be used in conjunction with other traits to completely define
+the object.
+
+Traits are also considered to be a more robust alternative to
+[mixins](http://en.wikipedia.org/wiki/Mixins) because, name conflicts have to
+be resolved explicitly by composer & because trait composition is
+order-independent (hence more declarative).
+
+
+There are some other implementations of traits in JavaScript & some ideas /
+APIs are borrowed from them:
+
+- [traitsjs](http://www.traitsjs.org/)
+- [joose](http://code.google.com/p/joose-js/)
+
+Object-capability security model
+--------------------------------
+
+Implementation uses an
+[object-capability security model](http://en.wikipedia.org/wiki/Object-capability_model)
+to allow protection of private APIs. At the same private APIs can be shared
+between among trait composition parties. To put it simply: All the properties
+whose names start with `"_"` are considered to be **private**, and are
+unaccessible from anywhere except other **public** methods / accessors of the
+instance that had been defined during composition.
+
+<api name="Trait">
+@class
+<api name="Trait">
+@constructor
+Creates an instance of Trait and returns it if it has no `constructor` method
+defined. If instance has `constructor` method, then it is called with all the
+arguments passed to this function and returned value is returned instead,
+unless it's `undefined`. In that case instance is returned.
+
+`Trait` function represents a base trait. As with any other trait it represents
+a constructor function for creating instances of its own & a placeholder
+for a trait compositions functions.
+</api>
+
+<api name="compose">
+@method
+Composes new trait out of itself and traits / property maps passed as an
+arguments. If two or more traits / property maps have properties with the same
+name, the new trait will contain a "conflict" property for that name (see
+examples in Examples section to find out more about "conflict" properties).
+This is a commutative and associative operation, and the order of its
+arguments is not significant.
+
+**Examples:**
+
+Let's say we want to define a reusable piece of code for a lists of elements.
+
+ var { Trait } = require('traits');
+ var List = Trait.compose({
+ // private API:
+ _list: null,
+ // public API
+ constructor: function List() {
+ this._list = [];
+ },
+ get length() this._list.length,
+ add: function add(item) this._list.push(item),
+ remove: function remove(item) {
+ let list = this._list;
+ let index = list.indexOf(item);
+ if (0 <= index) list.splice(index, 1);
+ }
+ });
+
+Instances of `List` can be created by calling `List` function with or without
+`new` keyword.
+
+ let l1 = List();
+ l1 instanceof List; // true
+ let l2 = new List();
+ l2 instanceof List; // true
+
+As you can see `add` and `remove` functions are capable of accessing private
+`_list` property, but thats about it, there's nothing else that will be able
+to access this property:
+
+ '_list' in l1; // false
+ '_list' in l2; // false
+ '_list' in List.protoype; // false
+ l1.has = function(name) name in this
+ l1.has('_list'); // false
+ l1.length; // 0
+ l1.add('test')
+ l1.length // 1
+
+@param trait1 {Object|Function}
+ Trait or property map to compose new trait from.
+@param trait2 {Object|Function}
+ Trait or property map to compose new trait from.
+@param ... {Object|Function}
+ Traits or property maps to compose new trait from.
+
+@returns {Function}
+ New trait containing the combined properties of all the traits.
+</api>
+
+<api name="required">
+@property {Object}
+Singleton, used during trait composition to define "required" properties.
+
+**Example:**
+
+ var Enumerable = Trait.compose({
+ list: Trait.required,
+ forEach: function forEach(consumer) {
+ return this.list.forEach(consumer);
+ }
+ });
+
+ let c1 = Enumerable(); // Error: Missing required property: list
+
+ var EnumerableList = List.compose({
+ get list() this._list.slice(0)
+ }, Enumerable);
+
+ let c2 = EnumerableList();
+ c2.add('test')
+ c2.length // 1
+ c2.list[0] // 'test'
+ c2.forEach(console.log) // > info: 'test 0 test'
+
+</api>
+
+
+<api name="resolve">
+@method
+Composes a new trait that has all the same properties
+as the trait on which it is called, except that each property listed
+in the `resolutions` argument will be renamed from the name
+of the property in the `resolutions` argument to its value.
+And if its value is `null`, the property will become required.
+
+**Example:**
+
+ var Range = List.resolve({
+ constructor: null,
+ add: '_add',
+ }).compose({
+ min: null,
+ max: null,
+ get list() this._list.slice(0),
+ constructor: function Range(min, max) {
+ this.min = min;
+ this.max = max;
+ this._list = [];
+ },
+ add: function(item) {
+ if (item <= this.max && item >= this.min)
+ this._add(item)
+ }
+ });
+
+
+ let r = Range(0, 10);
+ r.min; // 0
+ r.max; // 10
+ r.length; // 0;
+ r.add(5);
+ r.length; // 1
+ r.add(12);
+ r.length; // 1 (12 was not in a range)
+
+@param resolutions {Object}
+@returns {Function}
+ New resolved trait.
+</api>
+
+<api name="override">
+@method
+Composes a new trait with all of the combined properties of `this` and the
+argument traits. In contrast to `compose`, `override` immediately resolves
+all conflicts resulting from this composition by overriding the properties of
+later traits. Trait priority is from left to right. I.e. the properties of
+the leftmost trait are never overridden.
+
+**Example:**
+
+ // will compose trait with conflict property 'constructor'
+ var ConstructableList = List.compose({
+ constructor: function List() this._list = Array.slice(arguments)
+ });
+ // throws error with message 'Remaining conflicting property: constructor'
+ ConstructableList(1, 2, 3);
+
+ var ConstructableList = List.override({
+ constructor: function List() this._list = Array.slice(arguments)
+ });
+ ConstructableList(1, 2, 3).length // 3
+
+@param trait1 {Object|Function}
+ Trait or property map to compose new trait from.
+@param trait2 {Object|Function}
+ Trait or property map to compose new trait from.
+@param ... {Object|Function}
+ Traits or property maps to compose new trait from.
+
+@returns {Function}
+ New trait containing the combined properties of all the traits.
+</api>
+
+<api name="_public">
+@property {Object}
+Internal property of instance representing public API that is exposed to the
+consumers of an instance.
+</api>
+
+<api name='toString'>
+@method
+Textual representation of an object. All the traits will return:
+`'[object Trait]'` string, unless they have `constructor` property, in that
+case string `'Trait'` is replaced with the name of `constructor` property.
+
+**Example:**
+
+ var MyTrait = Trait.compose({
+ constructor: function MyTrait() {
+ // do your initialization here
+ }
+ });
+ MyTrait().toString(); // [object MyTrait]
+
+</api>
+</api>
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/unit-test.md b/tools/addon-sdk-1.7/packages/api-utils/docs/unit-test.md
new file mode 100644
index 0000000..6962c41
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/unit-test.md
@@ -0,0 +1,393 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Atul Varma [atul@mozilla.com] -->
+<!-- edited by Noelle Murata [fiveinchpixie@gmail.com] -->
+<!-- edited by Shane Tomlinson[stomlinson@mozilla.com] -->
+
+The `unit-test` module makes it easy to find and run unit tests.
+
+<api name="test">
+@class
+Each function which represents a test case is passed a single argument
+`test`, which represents the test runner.
+
+<api name="pass">
+@method
+ Marks a test as passing, with the given optional message.
+
+@param [message] {string}
+ Optional passing message.
+</api>
+
+
+<api name="fail">
+@method
+ Marks a test as failing, with the given optional message.
+
+@param [message] {string}
+ Optional failure message.
+</api>
+
+<api name="expectFail">
+@method
+ *experimental* Expect the test enclosed within `func` to fail.
+
+@param func {function}
+ A function that should contain a test that is expected to fail.
+</api>
+
+<api name="exception">
+@method
+ Marks a test as failing due to the given exception having been thrown.
+ This can be put in a `catch` clause.
+
+@param e {exception}
+ An exception.
+</api>
+
+<api name="assert">
+@method
+ Ensures that `a` has a truthy value.
+
+@param a {value}
+ Value to verify.
+@param [message] {string}
+ The test is marked as passing or failing depending on the result, logging
+ *message* with it.
+</api>
+
+
+<api name="assertEqual">
+@method
+ Ensures that `a == b` without recursing into `a` or `b`.
+
+@param a {value}
+ A value.
+
+@param b {value}
+ Another value.
+
+@param [message] {string}
+ The test is marked as passing or failing depending on the result, logging
+ *message* with it.
+</api>
+
+<api name="assertNotEqual">
+@method
+ Ensures that `a != b` without recursing into `a` or `b`.
+
+@param a {value}
+ A value.
+
+@param b {value}
+ Another value.
+
+@param [message] {string}
+ The test is marked as passing or failing depending on the result, logging
+ *message* with it.
+</api>
+
+<api name="assertStrictEqual">
+@method
+ Ensures that `a === b` without recursing into `a` or `b`.
+
+@param a {value}
+ A value.
+
+@param b {value}
+ Another value.
+
+@param [message] {string}
+ The test is marked as passing or failing depending on the result, logging
+ *message* with it.
+</api>
+
+
+<api name="assertNotStrictEqual">
+@method
+ Ensures that `a !== b` without recursing into `a` or `b`.
+
+@param a {value}
+ A value.
+
+@param b {value}
+ Another value.
+
+@param [message] {string}
+ The test is marked as passing or failing depending on the result, logging
+ *message* with it.
+</api>
+
+<api name="assertMatches">
+@method
+ Ensures that the given string matches the given regular expression.
+ If it does, marks a test as passing, otherwise marks a test as
+ failing.
+
+@param string {string}
+ The string to test.
+
+@param regexp {regexp}
+ The string should match this regular expression.
+
+@param [message] {string}
+ The test is marked as passing or failing depending on the result, logging
+ *message* with it.
+</api>
+
+
+<api name="assertRaises">
+@method
+ Calls the function `func` with no arguments, expecting an exception
+ to be raised. If nothing is raised, marks the test as failing. If an
+ exception is raised, the exception's `message` property is
+ compared with `predicate`: if `predicate` is a string, then a
+ simple equality comparison is done with `message`. Otherwise,
+ if `predicate` is a regular expression, `message` is tested
+ against it.
+
+@param func {function}
+ A function that should raise an exception when called.
+
+@param predicate {string,regexp}
+ A string or regular expression to compare to the exception's message.
+
+@param [message] {string}
+ Depending on the outcome, a test is marked as passing or failing, and
+ *message* is logged.
+</api>
+
+
+<api name="assertFunction">
+@method
+ Ensures that `a` is a function.
+
+@param a {value}
+ A value.
+
+@param [message] {string}
+ The test is marked as passing or failing depending on the result, logging
+ *message* with it.
+
+</api>
+
+
+<api name="assertUndefined">
+@method
+ Ensures that `a` is `undefined`. `null`, `0`, and `false` will all fail.
+
+@param a {value}
+ A value.
+
+@param [message] {string}
+ The test is marked as passing or failing depending on the result, logging
+ *message* with it.
+
+</api>
+
+
+<api name="assertNotUndefined">
+@method
+ Ensures that `a` is not `undefined`. `null`, `0`, and `false` will all pass.
+
+@param a {value}
+ A value.
+
+@param [message] {string}
+ The test is marked as passing or failing depending on the result, logging
+ *message* with it.
+
+</api>
+
+
+<api name="assertNull">
+@method
+ Ensures that `a` is `null`. `undefined`, `0`, and `false` will all fail.
+
+@param a {value}
+ A value.
+
+@param [message] {string}
+ The test is marked as passing or failing depending on the result, logging
+ *message* with it.
+
+</api>
+
+
+<api name="assertNotNull">
+@method
+ Ensures that `a` is not `null`. `undefined`, `0`, and `false` will all pass.
+
+@param a {value}
+ A value.
+
+@param [message] {string}
+ The test is marked as passing or failing depending on the result, logging
+ *message* with it.
+
+</api>
+
+
+<api name="assertObject">
+@method
+ Ensures that `a` is an object. A function, string, or number will fail.
+
+@param a {value}
+ A value.
+
+@param [message] {string}
+ The test is marked as passing or failing depending on the result, logging
+ *message* with it.
+
+</api>
+
+
+<api name="assertString">
+@method
+ Ensures that `a` is a string.
+
+@param a {value}
+ A value.
+
+@param [message] {string}
+ The test is marked as passing or failing depending on the result, logging
+ *message* with it.
+
+</api>
+
+
+<api name="assertArray">
+@method
+ Ensures that `a` is an array.
+
+@param a {value}
+ A value.
+
+@param [message] {string}
+ The test is marked as passing or failing depending on the result, logging
+ *message* with it.
+
+</api>
+
+
+<api name="assertNumber">
+@method
+ Ensures that `a` is a number.
+
+@param a {value}
+ A value.
+
+@param [message] {string}
+ The test is marked as passing or failing depending on the result, logging
+ *message* with it.
+
+</api>
+
+
+<api name="waitUntilDone">
+@method
+ Puts the test runner into asynchronous testing mode, waiting up to
+ *timeout* milliseconds for `test.done()` to be called. This
+ is intended for use in situations where a test suite schedules a
+ callback, calls `test.waitUntilDone()`, and then calls
+ `test.done()` in the callback.
+
+@param [timeout] {integer}
+ If this number of milliseconds elapses and `test.done()` has not yet been
+ called, the test is marked as failing.
+</api>
+
+
+<api name="done">
+@method
+ Marks a test as being complete. Assumes a previous call to
+ `test.waitUntilDone()`.
+</api>
+
+</api>
+
+
+<api name="waitUntil">
+@method
+ Ensures that `a` returns a truthy value within a reasonable amount of time.
+
+@param a {function}
+ Function that returns the value to verify.
+@param [message] {string}
+ The test is marked as passing or failing depending on the result, logging
+ *message* with it.
+</api>
+
+
+<api name="waitUntilEqual">
+@method
+ Ensures that `a == b` returned values or values without without recursing
+ into `a` or `b`.
+
+@param a {Function}
+ A value, or a function that returns a value.
+
+@param b {value}
+ Another value, or a function that returns value.
+
+@param [message] {string}
+ The test is marked as passing or failing depending on the result, logging
+ *message* with it.
+</api>
+
+<api name="waitUntilNotEqual">
+@method
+ Ensures that `a != b` without recursing into `a` or `b`.
+
+@param a {Function}
+ A value, or a function that returns a value.
+
+@param b {value}
+ Another value, or a function that returns another value.
+
+@param [message] {string}
+ The test is marked as passing or failing depending on the result, logging
+ *message* with it.
+</api>
+
+
+<api name="waitUntilMatches">
+@method
+ Ensures that the given string matches the given regular expression.
+ If it does, marks the test as passing, otherwise marks the test as
+ failing.
+
+@param string {Function}
+ A function that returns the string to test.
+
+@param regexp {regexp}
+ The string should match this regular expression.
+
+@param [message] {string}
+ The test is marked as passing or failing depending on the result, logging
+ *message* with it.
+</api>
+
+
+
+<api name="findAndRunTests">
+@function
+ The list of directories is searched for SecurableModules that start
+ with the prefix `test-`. Each module matching this criteria is
+ expected to export functions that are test cases or a suite of test
+ cases; each is called with a single argument, which is a Test Runner
+ Object.
+
+@param options {object}
+ An object with the following properties:
+ @prop dirs {string}
+ A list of absolute paths representing directories to search
+ for tests in. It's assumed that all of these directories are also
+ in the module search path, i.e. any JS files found in them are
+ SecurableModules that can be loaded via a call to
+ `require()`.
+ @prop onDone {function}
+ A function to call when testing is complete.
+</api>
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/unload.md b/tools/addon-sdk-1.7/packages/api-utils/docs/unload.md
new file mode 100644
index 0000000..8a76b3f
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/unload.md
@@ -0,0 +1,61 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Atul Varma [atul@mozilla.com] -->
+<!-- contributed by Drew Willcoxon [adw@mozilla.com] -->
+<!-- edited by Noelle Murata [fiveinchpixie@gmail.com] -->
+
+The `unload` module allows modules to register callbacks that are called
+when they are unloaded. It is similar to the CommonJS module of the same
+name in the [Narwhal][] platform.
+
+[Narwhal]: http://narwhaljs.org/
+
+<api name="ensure">
+@function
+ Calling `ensure()` on an object does two things:
+
+ 1. It replaces a destructor method with a wrapper method that will never call
+ the destructor more than once.
+ 2. It ensures that this wrapper method is called when `send()` is
+ called.
+
+ Therefore, when you register an object with `ensure()`, you can call its
+ destructor method yourself, you can let it happen for you, or you can do both.
+
+ The destructor will be called with a single argument describing the reason
+ for the unload; see `when()`. If `object` does not have the expected
+ destructor method, then an exception is thrown when `ensure()` is called.
+
+@param object {object}
+ An object that defines a destructor method.
+@param [name] {string}
+ Optional name of the destructor method. Default is `unload`.
+</api>
+
+<api name="when">
+@function
+ Registers a function to be called when `send()` is called.
+
+@param callback {function}
+ A function that will be called when `send()` is called. It is called with a
+ single argument, one of the following strings describing the reason for
+ unload: `"uninstall"`, `"disable"`, `"shutdown"`, `"upgrade"`, or
+ `"downgrade"`. (On Gecko 1.9.2-based applications such as Firefox 3.6,
+ `"upgrade"` and `"downgrade"` are not available, and `"shutdown"` will be sent
+ in their place.) If a reason could not be determined, `undefined` will be
+ passed instead. Note that if an add-on is unloaded with reason `"disable"`,
+ it will not be notified about `"uninstall"` while it is disabled. A solution
+ to this issue is being investigated; see bug 571049.
+</api>
+
+<api name="send">
+@function
+ Sends an "unload signal", thereby triggering all callbacks registered via
+ `when()`. In general, this function need not be manually called; it is
+ automatically triggered by the embedder.
+
+@param [reason] {string}
+ An optional string describing the reason for unload; see `unload.when()`.
+</api>
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/url.md b/tools/addon-sdk-1.7/packages/api-utils/docs/url.md
new file mode 100644
index 0000000..2974797
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/url.md
@@ -0,0 +1,85 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Atul Varma [atul@mozilla.com] -->
+<!-- edited by Noelle Murata [fiveinchpixie@gmail.com] -->
+
+
+The `url` module provides functionality for the parsing and retrieving of URLs.
+
+<api name="URL">
+@class
+<api name="URL">
+@constructor
+ The URL constructor creates an object that represents a URL, verifying that
+ the provided string is a valid URL in the process. Any API in the SDK which
+ has a URL parameter will accept `URL` objects, not raw strings, unless
+ otherwise noted.
+
+@param source {string}
+ A string to be converted into a URL. If `source` is not a valid URI, this
+ constructor will throw an exception.
+
+@param [base] {string}
+ An optional string used to resolve relative `source` URLs into absolute ones.
+</api>
+
+<api name="scheme">
+@property {string}
+ The name of the protocol in the URL.
+</api>
+
+<api name="userPass">
+@property {string}
+ The username:password part of the URL, `null` if not present.
+</api>
+
+<api name="host">
+@property {string}
+ The host of the URL, `null` if not present.
+</api>
+
+<api name="port">
+@property {integer}
+ The port number of the URL, `null` if none was specified.
+</api>
+
+<api name="path">
+@property {string}
+ The path component of the URL.
+</api>
+
+<api name="toString">
+@method
+ Returns a string representation of the URL.
+@returns {string}
+ The URL as a string.
+</api>
+</api>
+
+<api name="toFilename">
+@function
+ Attempts to convert the given URL to a native file path. This function will
+ automatically attempt to resolve non-file protocols, such as the `resource:`
+ protocol, to their place on the file system. An exception is raised if the URL
+ can't be converted; otherwise, the native file path is returned as a string.
+
+@param url {string}
+ The URL, as a string, to be converted.
+
+@returns {string}
+ The converted native file path as a string.
+</api>
+
+<api name="fromFilename">
+@function
+ Converts the given native file path to a `file:` URL.
+
+@param path {string}
+ The native file path, as a string, to be converted.
+
+@returns {string}
+ The converted URL as a string.
+</api>
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/uuid.md b/tools/addon-sdk-1.7/packages/api-utils/docs/uuid.md
new file mode 100644
index 0000000..429e3a0
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/uuid.md
@@ -0,0 +1,27 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+Module `uuid` provides low level API for generating / parsing UUID, that may
+be necessary when hacking on internals of the platform.
+
+
+## Generate UUID
+
+Module exports `uuid` function. When called without arguments it will uses
+platform-specific methods to obtain a `nsID` that can be considered to be
+globally unique.
+
+ let uuid = require('api-utils/uuid').uuid()
+
+## Parsing UUID
+
+Sometimes one might need to create `nsID` from an existing UUID string. Same
+`uuid` function may be used to parse such UUID strings into an `nsID`:
+
+ let { uuid } = require('api-utils/uuid');
+ let firefoxUUID = uuid('{ec8030f7-c20a-464f-9b0e-13a3a9e97384}');
+
+For more details about UUID representations and what they are used for by the
+platform see MDN documentation for
+[JSID](https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIJSID)
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/window-utils.md b/tools/addon-sdk-1.7/packages/api-utils/docs/window-utils.md
new file mode 100644
index 0000000..1435fd2
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/window-utils.md
@@ -0,0 +1,88 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Drew Willcoxon [adw@mozilla.com] -->
+<!-- edited by Erik Vold [erikvvold@gmail.com] -->
+
+The `window-utils` module provides helpers for accessing and tracking
+application windows. These windows implement the [`nsIDOMWindow`][nsIDOMWindow]
+interface.
+
+[nsIDOMWindow]: http://mxr.mozilla.org/mozilla-central/source/dom/interfaces/base/nsIDOMWindow.idl
+
+<api name="WindowTracker">
+@class
+`WindowTracker` objects make it easy to "monkeypatch" windows when a program is
+loaded and "un-monkeypatch" those windows when the program is unloaded. For
+example, if a Firefox add-on needs to add a status bar icon to all browser
+windows, it can use a single `WindowTracker` object to gain access to windows
+when they are opened and closed and also when the add-on is loaded and unloaded.
+
+When a window is opened or closed, a `WindowTracker` notifies its delegate
+object, which is passed to the constructor. The delegate is also notified of
+all windows that are open at the time that the `WindowTracker` is created and
+all windows that are open at the time that the `WindowTracker` is unloaded. The
+caller can therefore use the same code to act on all windows, regardless of
+whether they are currently open or are opened in the future, or whether they are
+closed while the parent program is loaded or remain open when the program is
+unloaded.
+
+When a window is opened or when a window is open at the time that the
+`WindowTracker` is created, the delegate's `onTrack()` method is called and
+passed the window.
+
+When a window is closed or when a window is open at the time that the
+`WindowTracker` is unloaded, the delegate's `onUntrack()` method is called and
+passed the window. (The `WindowTracker` is unloaded when its its `unload()`
+method is called, or when its parent program is unloaded, disabled, or
+uninstalled, whichever comes first.)
+
+**Example**
+
+ var delegate = {
+ onTrack: function (window) {
+ console.log("Tracking a window: " + window.location);
+ // Modify the window!
+ },
+ onUntrack: function (window) {
+ console.log("Untracking a window: " + window.location);
+ // Undo your modifications!
+ }
+ };
+ var winUtils = require("window-utils");
+ var tracker = new winUtils.WindowTracker(delegate);
+
+<api name="WindowTracker">
+@constructor
+ A `WindowTracker` object listens for openings and closings of application
+ windows.
+@param delegate {object}
+ An object that implements `onTrack()` and `onUntrack()` methods.
+@prop onTrack {function}
+ A function to be called when a window is open or loads, with the window as the
+ first and only argument.
+@prop [onUntrack] {function}
+ A function to be called when a window unloads, with the window as the first
+ and only argument.
+</api>
+</api>
+
+<api name="windowIterator">
+@function
+ An iterator for windows currently open in the application.
+
+**Example**
+
+ var winUtils = require("window-utils");
+ for (window in winUtils.windowIterator())
+ console.log("An open window! " + window.location);
+
+</api>
+
+<api name="closeOnUnload">
+@function
+ Marks an application window to be closed when the program is unloaded.
+@param window {window}
+ The window to close.
+</api>
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/window/utils.md b/tools/addon-sdk-1.7/packages/api-utils/docs/window/utils.md
new file mode 100644
index 0000000..276a17a
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/window/utils.md
@@ -0,0 +1,90 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+The `window/utils` module provides helper functions for working with
+application windows.
+
+### getInnerId
+
+Returns the ID of the given window's current inner window.
+
+### getOuterId
+
+Returns the ID of the given window's outer window.
+
+### getXULWindow
+
+Module provides `getXULWindow` function that can be used get access
+[nsIXULWindow](https://developer.mozilla.org/en/nsIDOMWindow) for the given
+[nsIDOMWindow](https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIXULWindow):
+
+ let { Ci } = require('chrome');
+ let utils = require('api-utils/window-utils');
+ let active = utils.activeBrowserWindow;
+ active instanceof Ci.nsIXULWindow // => false
+ utils.getXULWindow(active) instanceof Ci.nsIXULWindow // => true
+
+### getBaseWindow
+
+Module provides `getBaseWindow` function that can be used get access
+[nsIBaseWindow](http://mxr.mozilla.org/mozilla-central/source/widget/nsIBaseWindow.idl)
+for the given [nsIDOMWindow](https://developer.mozilla.org/en/nsIDOMWindow):
+
+ let { Ci } = require('chrome');
+ let utils = require('api-utils/window-utils');
+ let active = utils.activeBrowserWindow;
+ active instanceof Ci.nsIBaseWindow // => false
+ utils.getBaseWindow(active) instanceof Ci.nsIBaseWindow // => true
+
+### open
+
+Module exports `open` function that may be used to open top level
+(application) windows. Function takes `uri` of the window document as a first
+argument and optional hash of `options` as second argument.
+
+ let { open } = require('api-utils/window-utils');
+ let window = open('data:text/html,Hello Window');
+
+Following options may be provided to configure created window behavior:
+
+- `parent`
+If provided must be `nsIDOMWindow` and will be used as parent for the created
+window.
+
+- `name`
+Optional name that will be assigned to the window.
+
+- `features`
+Hash of options that will be serialized to features string. See
+[features documentation](https://developer.mozilla.org/en/DOM/window.open#Position_and_size_features)
+for more details.
+
+ let { open } = require('api-utils/window/utils');
+ let window = open('data:text/html,Hello Window', {
+ name: 'jetpack window',
+ features: {
+ width: 200,
+ height: 50,
+ popup: true
+ }
+ });
+
+### backgroundify
+
+Module exports `backgroundify` function that takes `nsIDOMWindow` and
+removes it from the application's window registry, so that they won't appear
+in the OS specific window lists for the application.
+
+ let { backgroundify, open } = require('api-utils/window/utils');
+ let bgwin = backgroundify(open('data:text/html,Hello backgroundy'));
+
+Optionally more configuration options via second `options` argument. If
+`options.close` is `false` unregistered window won't automatically
+be closed on application quit, preventing application from quitting. While this
+is possible you should make sure to close all such windows manually:
+
+ let { backgroundify, open } = require('api-utils/window-utils');
+ let bgwin = backgroundify(open('data:text/html,Foo'), {
+ close: false
+ });
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/xhr.md b/tools/addon-sdk-1.7/packages/api-utils/docs/xhr.md
new file mode 100644
index 0000000..d0381c0
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/xhr.md
@@ -0,0 +1,95 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Atul Varma [atul@mozilla.com] -->
+<!-- edited by Noelle Murata [fiveinchpixie@gmail.com] -->
+
+The `xhr` module provides access to `XMLHttpRequest` functionality, also known
+as AJAX.
+
+## Limitations ##
+
+The `XMLHttpRequest` object is currently fairly limited, and does not
+yet implement the `addEventListener()` or `removeEventListener()`
+methods. It also doesn't yet implement the `upload` property.
+
+Furthermore, the `XMLHttpRequest` object does not currently support
+the `mozBackgroundRequest` property. All security UI, such as
+username/password prompts, are automatically suppressed, so if
+required authentication information isn't passed to the `open()`
+method, the request will fail.
+
+## Resource Use ##
+
+Whenever this module is unloaded, all in-progress requests are immediately
+aborted.
+
+## Security Concerns ##
+
+By default, the `XMLHttpRequest` object grants full access to any
+protocol scheme, which means that it can be used to read from (but not
+write to) the host system's entire filesystem. It also has unfettered
+access to any local area networks, VPNs, and the internet.
+
+### Threat Model ###
+
+The `XMLHttpRequest` object can be used by an add-on to "phone
+home" and transmit potentially sensitive user data to third
+parties.
+
+If access to the filesystem isn't prevented, it could easily be used
+to access sensitive user data, though this may be inconsequential if
+the client can't access the network.
+
+If access to local area networks isn't prevented, malicious code could access
+sensitive data.
+
+If transmission of cookies isn't prevented, malicious code could access
+sensitive data.
+
+Attenuating access based on a regular expression may be ineffective if
+it's easy to write a regular expression that *looks* safe but contains
+a special character or two that makes it far less secure than it seems
+at first glance.
+
+### Possible Attenuations ###
+
+<span class="aside">
+We may also want to consider attenuating further based on domain name
+and possibly even restricting the protocol to `https:` only, to reduce
+risk.
+</span>
+
+Before being exposed to unprivileged code, this object needs
+to be attenuated in such a way that, at the very least, it can't
+access the user's filesystem. This can probably be done most securely
+by white-listing the protocols that can be used in the URL passed to
+the `open()` method, and limiting them to `http:`, `https:`, and
+possibly a special scheme that can be used to access the add-on's
+packaged, read-only resources.
+
+Finally, we need to also consider attenuating http/https requests such
+that they're "sandboxed" and don't communicate potentially sensitive
+cookie information.
+
+<api name="XMLHttpRequest">
+@class
+
+<api name="XMLHttpRequest">
+@constructor
+ Creates an `XMLHttpRequest`. This is a constructor, so its use should always
+ be preceded by the `new` operator. For more information about
+ `XMLHttpRequest` objects, see the MDC page on
+ [Using XMLHttpRequest](https://developer.mozilla.org/En/Using_XMLHttpRequest)
+ and the Limitations section in this page.
+</api>
+</api>
+
+<api name="getRequestCount">
+@function
+ Returns the number of `XMLHttpRequest` objects that are alive (i.e., currently
+ active or about to be).
+@returns {integer}
+ The number of live `XMLHttpRequest` objects.
+</api>
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/xpcom.md b/tools/addon-sdk-1.7/packages/api-utils/docs/xpcom.md
new file mode 100644
index 0000000..6d96650
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/xpcom.md
@@ -0,0 +1,216 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+Module `xpcom` provides low level API for implementing and registering /
+unregistering various XCOM interfaces.
+
+## Implementing XPCOM interfaces
+
+Module exports `Unknow` exemplar object, that may be extended to implement
+specific XCOM interface(s). For example [nsIObserver]
+(https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIObserver) may be
+implemented as follows:
+
+ const { Unknown } = require('api-utils/xpcom');
+ const { Cc, Ci } = require('chrome')
+ const observerService = Cc["@mozilla.org/observer-service;1"].
+ getService(Ci.nsIObserverService);
+
+ // Create my observer exemplar
+ const SleepObserver = Unknown.extend({
+ interfaces: [ 'nsIObserver' ], // Interfaces component implements
+ topic: 'sleep_notification',
+ initialize: function(fn) { this.fn = fn },
+ register: function register() {
+ observerService.addObserver(this, this.topic, false);
+ },
+ unregister: function() {
+ addObserver.removeObserver(this, this.topic, false);
+ },
+ observe: function observe(subject, topic, data) {
+ this.fn({
+ subject: subject,
+ type: topic,
+ data: data
+ });
+ }
+ });
+
+ // create instances of observers
+ let observer = SleepObserver.new(function(event) {
+ console.log('Going to sleep!')
+ });
+ // register observer
+ observer.register();
+
+## Implementing XCOM factories
+
+Module exports `Factory` exemplar, object that may be used to create objects
+implementing
+[nsIFactory](https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIFactory)
+interface:
+
+ const { Factory } = require('api-utils/xpcom');
+ let Request = Unknown.extend({
+ interfaces: [ 'nsIRequest' ],
+ initialize: function initialize() {
+ this.pending = false;
+ // Do some initialization
+ },
+ isPending: function() { return this.pending },
+ resume: function() { /* Implementation */ },
+ suspend: function() { /* Implementation */ },
+ cancel: function() { /* Implementation */ },
+ initiate: function() {
+ this.pending = true;
+ /* Implementation */
+ }
+ });
+
+ let requestFactory = Factory.new({ component: Request });
+
+Factories registered into a runtime may be accessed from the rest of the
+application via standard XPCOM API using factory's auto generated `id`
+(optionally you could specify specific `id` by passing it as an option):
+
+ let request = Components.classesByID[requestFactory.id].
+ createInstance(Ci.nsIRequest);
+ request.isPending() // => false
+
+Be aware that traditional XPCOM API will always return a wrapped JS objects
+exposing only properties defined by a given interface (`nsIRequest`) in our
+case:
+
+ request.initiate() // TypeError: request.initiate is not a function
+
+You still can expose unwrapped JS object, by a special `wrappedJSObject`
+property of the component:
+
+ let Request = Unknown.extend({
+ get wrappedJSObject() this,
+ interfaces: [ 'nsIRequest' ],
+ initialize: function initialize() {
+ this.pending = false;
+ // Do some initialization
+ },
+ isPending: function() { return this.pending },
+ resume: function() { /* Implementation */ },
+ suspend: function() { /* Implementation */ },
+ cancel: function() { /* Implementation */ },
+ initiate: function() {
+ this.pending = true;
+ /* Implementation */
+ }
+ });
+
+ let requestFactory = Factory.new({ component: Request });
+ let request = Components.classesByID[requestFactory.id].
+ createInstance(Ci.nsIRequest);
+ request.isPending(); // => false
+ request.wrappedJSObject.initiate();
+ request.isPending(); // => true
+
+Optionally `Factory.new` may be passed globally unique string in a format of:
+`'@domain.com/unique/identifier;1'` as a `contract` option in order to
+associate it with it:
+
+ let namedFactory = Factory.new({
+ contract: '@examples.com/request/factory;1',
+ component: Request
+ });
+
+Such factories when registered can be accessed form rest of the application by
+human readable `contract` strings:
+
+ let request = Components.classes['@examples.com/request/factory;1'].
+ createInstance(Components.interfaces.nsIRequest);
+
+In addition factories associated with a given `contract` may be replaced at
+runtime:
+
+ let renewedFactory = Factory.new({
+ contract: '@examples.com/request/factory;1',
+ component: Unknown.extend({ /* Implementation */ })
+ })
+
+Unfortunately commonly used `Components.classes` won't get updated at runtime
+but there is an alternative, more verbose way to access last registered factory
+for a given `contract`:
+
+ let id = Components.manager.QueryInterface(Ci.nsIComponentRegistrar).
+ contractIDToCID('@examples.com/request/factory;1');
+ Components.classesByID[requestFactory.id].
+ createInstance(Ci.nsISupports);
+
+Module also exports `factoryByContract` function to simplify this:
+
+ factoryByContract('@examples.com/request/factory;1').
+ createInstance(Ci.nsISupports);
+
+It's also recommended to construct factories with an optional `description`
+property, providing human readable description of it:
+
+ let factory = Factory.new({
+ contract: '@examples.com/request/factory;1',
+ description: 'Super duper request factory',
+ component: Request
+ });
+
+## Registering / Unregistering factories
+
+All factories created using `Factory.new` get automatically registered into
+runtime unless explicitly specified otherwise by setting `register` option to
+`false`:
+
+ var factoryToRegister = Factory.new({
+ register: false,
+ component: Unknown.extend({ /* Implementation */ })
+ });
+
+Such factories still may be registered manually using exported `register`
+function:
+
+ const { register } = require('api-utils/xpcom');
+ register(factoryToRegister);
+
+All factories created using `Factory.new` also get unregistered automatically
+when add-on is unloaded. This also can be disabled by setting `unregister`
+option to `false`.
+
+ var factoryToUnregister = Service.new({
+ unregister: false,
+ component: Unknown.extend({ /* Implementation */ })
+ });
+
+All registered services may be unregistered at any time using exported
+`unregister` function:
+
+ unregister(factoryToUnregister);
+
+## Implementing XCOM services
+
+Module exports `Service` exemplar object, that has exact same API as `Factory`
+and can be used to register services:
+
+ const { Service } = require('api-utils/xpcom');
+ let service = Service.new({
+ contract: '@examples/demo/service;1',
+ description: 'My demo service',
+ component: Unknown.extend({
+ // Implementation
+ get wrappedJSObject() this
+ })
+ });
+
+Registered services can be accessed through the rest of the application via
+standard XPCOM API:
+
+ let s = Components.classes['@examples/demo/service;1'].
+ getService(Components.interfaces.nsISupports);
+
+In contrast to factories, services do not create instances of enclosed
+components, they expose component itself. Also please note that idiomatic way
+to work with a service is via `getService` method:
+
+ s.wrappedJSObject === service.component // => true
diff --git a/tools/addon-sdk-1.7/packages/api-utils/docs/xul-app.md b/tools/addon-sdk-1.7/packages/api-utils/docs/xul-app.md
new file mode 100644
index 0000000..597af56
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/docs/xul-app.md
@@ -0,0 +1,76 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- contributed by Drew Willcoxon [adw@mozilla.com] -->
+
+The `xul-app` module provides facilities for introspecting the application on
+which your program is running.
+
+With the exception of `ids`, each of these properties exposes the attribute of
+the same name on the [`nsIXULAppInfo`][nsIXULAppInfo] interface. For more
+information, see the [MDC documentation][].
+
+[nsIXULAppInfo]: http://mxr.mozilla.org/mozilla-central/source/xpcom/system/nsIXULAppInfo.idl
+[MDC documentation]: https://developer.mozilla.org/en/nsIXULAppInfo
+
+<api name="ID">
+@property {string}
+ The GUID of the host application. For example, for Firefox this value is
+ `"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"`.
+</api>
+
+<api name="name">
+@property {string}
+ The host application name. For example, `"Firefox"`.
+</api>
+
+<api name="version">
+@property {string}
+ The host application version.
+</api>
+
+<api name="platformVersion">
+@property {string}
+ The Gecko/XULRunner platform version.
+</api>
+
+<api name="ids">
+@property {object}
+ A mapping of application names to their IDs. For example,
+ `ids["Firefox"] == "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"`.
+</api>
+
+<api name="is">
+@function
+ Checks whether the host application is the given application.
+@param name {string}
+ A host application name.
+@returns {boolean}
+ True if the host application is `name` and false otherwise.
+</api>
+
+<api name="isOneOf">
+@function
+ Checks whether the host application is one of the given applications.
+@param names {array}
+ An array of host application names.
+@returns {boolean}
+ True if the host application is one of the `names` and false otherwise.
+</api>
+
+<api name="versionInRange">
+@function
+ Compares a given version to a version range. See the [MDC documentation](https://developer.mozilla.org/en/Toolkit_version_format#Comparing_versions)
+ for details on version comparisons.
+@param version {string}
+ The version to compare.
+@param lowInclusive {string}
+ The lower bound of the version range to compare. The range includes this
+ bound.
+@param highExclusive {string}
+ The upper bound of the version range to compare. The range does not include
+ this bound.
+@returns {boolean}
+ True if `version` falls in the given range and false otherwise.
+</api>
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/api-utils.js b/tools/addon-sdk-1.7/packages/api-utils/lib/api-utils.js
new file mode 100644
index 0000000..94d13b7
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/api-utils.js
@@ -0,0 +1,153 @@
+/* -*- 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/. */
+
+"use strict";
+
+const memory = require("api-utils/memory");
+// The possible return values of getTypeOf.
+const VALID_TYPES = [
+ "array",
+ "boolean",
+ "function",
+ "null",
+ "number",
+ "object",
+ "string",
+ "undefined",
+];
+
+/**
+ * Returns a function C that creates instances of privateCtor. 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
+ * privateCtor.prototype. Instances returned from C will therefore be instances
+ * of both C and privateCtor. Additionally, the constructor of each instance
+ * returned from C is C.
+ *
+ * @param privateCtor
+ * A constructor.
+ * @return A function that makes new instances of privateCtor.
+ */
+exports.publicConstructor = function publicConstructor(privateCtor) {
+ function PublicCtor() {
+ let obj = { constructor: PublicCtor, __proto__: PublicCtor.prototype };
+ memory.track(obj, privateCtor.name);
+ privateCtor.apply(obj, arguments);
+ return obj;
+ }
+ PublicCtor.prototype = { __proto__: privateCtor.prototype };
+ return PublicCtor;
+};
+
+/**
+ * Returns a validated options dictionary given some requirements. If any of
+ * the requirements are not met, an exception is thrown.
+ *
+ * @param options
+ * An object, the options dictionary to validate. It's not modified.
+ * If it's null or otherwise falsey, an empty object is assumed.
+ * @param requirements
+ * An object whose keys are the expected keys in options. Any key in
+ * options that is not present in requirements is ignored. Each value
+ * in requirements is itself an object describing the requirements of
+ * its key. There are four optional keys in this object:
+ * map: A function that's passed the value of the key in options.
+ * map's return value is taken as the key's value in the final
+ * validated options, is, and ok. If map throws an exception
+ * it's caught and discarded, and the key's value is its value in
+ * options.
+ * is: An array containing any number of the typeof type names. If
+ * the key's value is none of these types, it fails validation.
+ * Arrays and null are identified by the special type names
+ * "array" and "null"; "object" will not match either. No type
+ * coercion is done.
+ * ok: A function that's passed the key's value. If it returns
+ * false, the value fails validation.
+ * msg: If the key's value fails validation, an exception is thrown.
+ * This string will be used as its message. If undefined, a
+ * generic message is used, unless is is defined, in which case
+ * the message will state that the value needs to be one of the
+ * given types.
+ * @return An object whose keys are those keys in requirements that are also in
+ * options and whose values are the corresponding return values of map
+ * or the corresponding values in options. Note that any keys not
+ * shared by both requirements and options are not in the returned
+ * object.
+ */
+exports.validateOptions = function validateOptions(options, requirements) {
+ options = options || {};
+ let validatedOptions = {};
+ let mapThrew = false;
+
+ for (let [key, req] in Iterator(requirements)) {
+ let [optsVal, keyInOpts] = (key in options) ?
+ [options[key], true] :
+ [undefined, false];
+ if (req.map) {
+ try {
+ optsVal = req.map(optsVal);
+ }
+ catch (err) {
+ mapThrew = true;
+ }
+ }
+ if (req.is) {
+ // Sanity check the caller's type names.
+ req.is.forEach(function (typ) {
+ if (VALID_TYPES.indexOf(typ) < 0) {
+ let msg = 'Internal error: invalid requirement type "' + typ + '".';
+ throw new Error(msg);
+ }
+ });
+ if (req.is.indexOf(getTypeOf(optsVal)) < 0)
+ throw requirementError(key, req);
+ }
+ if (req.ok && !req.ok(optsVal))
+ throw requirementError(key, req);
+
+ if (keyInOpts || (req.map && !mapThrew))
+ validatedOptions[key] = optsVal;
+ }
+
+ return validatedOptions;
+};
+
+exports.addIterator = function addIterator(obj, keysValsGenerator) {
+ obj.__iterator__ = function(keysOnly, keysVals) {
+ let keysValsIterator = keysValsGenerator.call(this);
+
+ // "for (.. in ..)" gets only keys, "for each (.. in ..)" gets values,
+ // and "for (.. in Iterator(..))" gets [key, value] pairs.
+ let index = keysOnly ? 0 : 1;
+ while (true)
+ yield keysVals ? keysValsIterator.next() : keysValsIterator.next()[index];
+ };
+};
+
+// Similar to typeof, except arrays and null are identified by "array" and
+// "null", not "object".
+let getTypeOf = exports.getTypeOf = function getTypeOf(val) {
+ let typ = typeof(val);
+ if (typ === "object") {
+ if (!val)
+ return "null";
+ if (Array.isArray(val))
+ return "array";
+ }
+ return typ;
+}
+
+// Returns a new Error with a nice message.
+function requirementError(key, requirement) {
+ let msg = requirement.msg;
+ if (!msg) {
+ msg = 'The option "' + key + '" ';
+ msg += requirement.is ?
+ "must be one of the following types: " + requirement.is.join(", ") :
+ "is invalid.";
+ }
+ return new Error(msg);
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/app-strings.js b/tools/addon-sdk-1.7/packages/api-utils/lib/app-strings.js
new file mode 100644
index 0000000..c03ae7c
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/app-strings.js
@@ -0,0 +1,63 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {Cc,Ci} = require("chrome");
+const apiUtils = require("./api-utils");
+
+/**
+ * A bundle of strings.
+ *
+ * @param url {String}
+ * the URL of the string bundle
+ */
+exports.StringBundle = apiUtils.publicConstructor(function StringBundle(url) {
+
+ let stringBundle = Cc["@mozilla.org/intl/stringbundle;1"].
+ getService(Ci.nsIStringBundleService).
+ createBundle(url);
+
+ this.__defineGetter__("url", function () url);
+
+ /**
+ * Get a string from the bundle.
+ *
+ * @param name {String}
+ * the name of the string to get
+ * @param args {array} [optional]
+ * an array of arguments that replace occurrences of %S in the string
+ *
+ * @returns {String} the value of the string
+ */
+ this.get = function strings_get(name, args) {
+ try {
+ if (args)
+ return stringBundle.formatStringFromName(name, args, args.length);
+ else
+ return stringBundle.GetStringFromName(name);
+ }
+ catch(ex) {
+ // f.e. "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE)
+ // [nsIStringBundle.GetStringFromName]"
+ throw new Error("String '" + name + "' could not be retrieved from the " +
+ "bundle due to an unknown error (it doesn't exist?).");
+ }
+ },
+
+ /**
+ * Iterate the strings in the bundle.
+ *
+ */
+ apiUtils.addIterator(
+ this,
+ function keysValsGen() {
+ let enumerator = stringBundle.getSimpleEnumeration();
+ while (enumerator.hasMoreElements()) {
+ let elem = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement);
+ yield [elem.key, elem.value];
+ }
+ }
+ );
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/array.js b/tools/addon-sdk-1.7/packages/api-utils/lib/array.js
new file mode 100644
index 0000000..dadcfa6
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/array.js
@@ -0,0 +1,69 @@
+/* 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/. */
+
+"use strict";
+
+/**
+ * Returns `true` if given `array` contain given `element` or `false`
+ * otherwise.
+ * @param {Array} array
+ * Target array.
+ * @param {Object|String|Number|Boolean} element
+ * Element being looked up.
+ * @returns {Boolean}
+ */
+var has = exports.has = function has(array, element) {
+ // shorter and faster equivalent of `array.indexOf(element) >= 0`
+ return !!~array.indexOf(element);
+};
+
+/**
+ * Adds given `element` to the given `array` if it does not contain it yet.
+ * `true` is returned if element was added otherwise `false` is returned.
+ * @param {Array} array
+ * Target array.
+ * @param {Object|String|Number|Boolean} element
+ * Element to be added.
+ * @returns {Boolean}
+ */
+var add = exports.add = function add(array, element) {
+ var result;
+ if ((result = !has(array, element)))
+ array.push(element);
+
+ return result;
+};
+
+/**
+ * Removes first occurrence of the given `element` from the given `array`. If
+ * `array` does not contain given `element` `false` is returned otherwise
+ * `true` is returned.
+ * @param {Array} array
+ * Target array.
+ * @param {Object|String|Number|Boolean} element
+ * Element to be removed.
+ * @returns {Boolean}
+ */
+exports.remove = function remove(array, element) {
+ var result;
+ if ((result = has(array, element)))
+ array.splice(array.indexOf(element), 1);
+
+ return result;
+};
+
+/**
+ * Produces a duplicate-free version of the given `array`.
+ * @param {Array} array
+ * Source array.
+ * @returns {Array}
+ */
+exports.unique = function unique(array) {
+ var value = [];
+ return array.forEach(function(element) {
+ add(value, element);
+ });
+ return value;
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/base.js b/tools/addon-sdk-1.7/packages/api-utils/lib/base.js
new file mode 100644
index 0000000..63acabc
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/base.js
@@ -0,0 +1,177 @@
+/* vim:set ts=2 sw=2 sts=2 expandtab */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// Instead of inheriting from `Object.prototype` we copy all interesting
+// properties from it and then freeze. This way we can guarantee integrity
+// of components build on top.
+exports.Base = Object.freeze(Object.create(null, {
+ toString: { value: Object.prototype.toString },
+ toLocaleString: { value: Object.prototype.toLocaleString },
+ toSource: { value: Object.prototype.toSource },
+ valueOf: { value: Object.prototype.valueOf },
+ isPrototypeOf: { value: Object.prototype.isPrototypeOf },
+ /**
+ * Creates an object that inherits from `this` object (Analog of
+ * `new Object()`).
+ * @examples
+ *
+ * var Dog = Base.extend({
+ * bark: function bark() {
+ * return 'Ruff! Ruff!'
+ * }
+ * });
+ * var dog = Dog.new();
+ */
+ new: { value: function create() {
+ var object = Object.create(this);
+ object.initialize.apply(object, arguments);
+ return object;
+ }},
+ /**
+ * When new instance of the this prototype is created it's `initialize`
+ * method is called with all the arguments passed to the `new`. You can
+ * override `initialize` to set up an instance.
+ */
+ initialize: { value: function initialize() {
+ }},
+ /**
+ * Merges all the properties of the passed objects into `this` instance (This
+ * method can be used on instances only as prototype objects are frozen).
+ *
+ * If two or more argument objects have own properties with the same name,
+ * the property is overridden, with precedence from right to left, implying,
+ * that properties of the object on the left are overridden by a same named
+ * property of the object on the right.
+ *
+ * @examples
+ *
+ * var Pet = Dog.extend({
+ * initialize: function initialize(options) {
+ * // this.name = options.name -> would have thrown (frozen prototype)
+ * this.merge(options) // will override all properties.
+ * },
+ * call: function(name) {
+ * return this.name === name ? this.bark() : ''
+ * },
+ * name: null
+ * })
+ * var pet = Pet.new({ name: 'Benzy', breed: 'Labrador' })
+ * pet.call('Benzy') // 'Ruff! Ruff!'
+ */
+ merge: { value: function merge() {
+ var descriptor = {};
+ Array.prototype.forEach.call(arguments, function (properties) {
+ Object.getOwnPropertyNames(properties).forEach(function(name) {
+ descriptor[name] = Object.getOwnPropertyDescriptor(properties, name);
+ });
+ });
+ Object.defineProperties(this, descriptor);
+ return this;
+ }},
+ /**
+ * Takes any number of argument objects and returns frozen, composite object
+ * that inherits from `this` object and combines all of the own properties of
+ * the argument objects. (Objects returned by this function are frozen as
+ * they are intended to be used as types).
+ *
+ * If two or more argument objects have own properties with the same name,
+ * the property is overridden, with precedence from right to left, implying,
+ * that properties of the object on the left are overridden by a same named
+ * property of the object on the right.
+ * @examples
+ *
+ * // ## Object composition ##
+ *
+ * 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);
+ * },
+ * cyan: function cyan() {
+ * var K = this.black();
+ * return (((1 - this.red() / 255).toFixed(4) - K) / (1 - K)).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);
+ * }
+ * })
+ *
+ * var Color = Base.extend(HEX, RGB, CMYK, {
+ * initialize: function Color(color) {
+ * this.color = color;
+ * }
+ * });
+ *
+ * // ## Prototypal inheritance ##
+ *
+ * var Pixel = Color.extend({
+ * initialize: function Pixel(x, y, hex) {
+ * Color.initialize.call(this, hex);
+ * 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.red(); // 204
+ * pixel.green(); // 51
+ * pixel.blue(); // 153
+ *
+ * pixel.cyan(); // 0.0000
+ * pixel.magenta(); // 0.7500
+ * pixel.yellow(); // 0.2500
+ *
+ */
+ extend: { value: function extend() {
+ return Object.freeze(this.merge.apply(Object.create(this), arguments));
+ }}
+}));
+
+/**
+ * Function takes prototype object that implements `initialize` method, and
+ * returns `constructor` function (with correct prototype property), that can
+ * be used for simulating classes for given prototypes.
+ */
+exports.Class = Object.freeze(function Class(prototype) {
+ function constructor() {
+ var instance = Object.create(prototype);
+ prototype.initialize.apply(instance, arguments);
+ return instance;
+ }
+ return Object.freeze(Object.defineProperties(constructor, {
+ prototype: { value: prototype },
+ new: { value: constructor }
+ }));
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/byte-streams.js b/tools/addon-sdk-1.7/packages/api-utils/lib/byte-streams.js
new file mode 100644
index 0000000..edc8407
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/byte-streams.js
@@ -0,0 +1,102 @@
+/* -*- 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/. */
+
+"use strict";
+
+exports.ByteReader = ByteReader;
+exports.ByteWriter = ByteWriter;
+
+const {Cc, Ci} = require("chrome");
+
+// This just controls the maximum number of bytes we read in at one time.
+const BUFFER_BYTE_LEN = 0x8000;
+
+function ByteReader(inputStream) {
+ const self = this;
+
+ let stream = Cc["@mozilla.org/binaryinputstream;1"].
+ createInstance(Ci.nsIBinaryInputStream);
+ stream.setInputStream(inputStream);
+
+ let manager = new StreamManager(this, stream);
+
+ this.read = function ByteReader_read(numBytes) {
+ manager.ensureOpened();
+ if (typeof(numBytes) !== "number")
+ numBytes = Infinity;
+
+ let data = "";
+ let read = 0;
+ try {
+ while (true) {
+ let avail = stream.available();
+ let toRead = Math.min(numBytes - read, avail, BUFFER_BYTE_LEN);
+ if (toRead <= 0)
+ break;
+ data += stream.readBytes(toRead);
+ read += toRead;
+ }
+ }
+ catch (err) {
+ throw new Error("Error reading from stream: " + err);
+ }
+
+ return data;
+ };
+}
+
+function ByteWriter(outputStream) {
+ const self = this;
+
+ let stream = Cc["@mozilla.org/binaryoutputstream;1"].
+ createInstance(Ci.nsIBinaryOutputStream);
+ stream.setOutputStream(outputStream);
+
+ let manager = new StreamManager(this, stream);
+
+ this.write = function ByteWriter_write(str) {
+ manager.ensureOpened();
+ try {
+ stream.writeBytes(str, str.length);
+ }
+ catch (err) {
+ throw new Error("Error writing to stream: " + err);
+ }
+ };
+}
+
+
+// This manages the lifetime of stream, a ByteReader or ByteWriter. It defines
+// closed and close() on stream and registers an unload listener that closes
+// rawStream if it's still opened. It also provides ensureOpened(), which
+// throws an exception if the stream is closed.
+function StreamManager(stream, rawStream) {
+ const self = this;
+ this.rawStream = rawStream;
+ this.opened = true;
+
+ stream.__defineGetter__("closed", function stream_closed() {
+ return !self.opened;
+ });
+
+ stream.close = function stream_close() {
+ self.ensureOpened();
+ self.unload();
+ };
+
+ require("./unload").ensure(this);
+}
+
+StreamManager.prototype = {
+ ensureOpened: function StreamManager_ensureOpened() {
+ if (!this.opened)
+ throw new Error("The stream is closed and cannot be used.");
+ },
+ unload: function StreamManager_unload() {
+ this.rawStream.close();
+ this.opened = false;
+ }
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/channel.js b/tools/addon-sdk-1.7/packages/api-utils/lib/channel.js
new file mode 100644
index 0000000..a888af8
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/channel.js
@@ -0,0 +1,46 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { jetpackID } = require('@packaging');
+const { when } = require('./unload');
+
+// TODO: Create a bug report and remove this workaround once it's fixed.
+// Only function needs defined in the context of the message manager window
+// can be registered via `addMessageListener`.
+function listener(callee) {
+ return function listener() { return callee.apply(this, arguments); };
+}
+function messageListener(scope, callee) {
+ return scope ? scope.eval('(' + listener + ')')(callee) : callee
+}
+
+exports.channel = function channel(scope, messageManager, address, raw) {
+ address = jetpackID + ':' + address
+ return {
+ input: function input(next, stop) {
+ let listener = messageListener(scope, function onMessage(message) {
+ if (false === next(raw ? message : message.json) && listener) {
+ messageManager.removeMessageListener(address, listener);
+ listener = null;
+ }
+ });
+ messageManager.addMessageListener(address, listener);
+
+ // Bug 724433: do not leak `listener` on addon disabling
+ when(function () {
+ if (listener) {
+ messageManager.removeMessageListener(address, listener);
+ listener = null;
+ }
+ });
+ },
+ output: function output(data) {
+ messageManager.sendAsyncMessage(address, data);
+ },
+ sync: !messageManager.sendSyncMessage ? null : function sync(data) {
+ messageManager.sendSyncMessage(address, data);
+ }
+ };
+};
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/collection.js b/tools/addon-sdk-1.7/packages/api-utils/lib/collection.js
new file mode 100644
index 0000000..b9dd352
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/collection.js
@@ -0,0 +1,108 @@
+/* -*- 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/. */
+
+"use strict";
+
+exports.Collection = Collection;
+
+/**
+ * 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 obj
+ * The property will be defined on this object.
+ * @param propName
+ * The name of the property.
+ * @param array
+ * If given, this will be used as the collection's backing array.
+ */
+exports.addCollectionProperty = function addCollProperty(obj, propName, array) {
+ array = array || [];
+ let publicIface = new Collection(array);
+
+ obj.__defineSetter__(propName, function (itemOrItems) {
+ array.splice(0, array.length);
+ publicIface.add(itemOrItems);
+ });
+
+ obj.__defineGetter__(propName, function () {
+ return publicIface;
+ });
+};
+
+/**
+ * A collection is ordered, like an array, but its items are unique, like a set.
+ *
+ * @param array
+ * The collection is backed by an array. If this 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.
+ */
+function Collection(array) {
+ array = array || [];
+
+ /**
+ * Provides iteration over the collection. Items are yielded in the order
+ * they were added.
+ */
+ this.__iterator__ = function Collection___iterator__() {
+ let items = array.slice();
+ for (let i = 0; i < items.length; i++)
+ yield items[i];
+ };
+
+ /**
+ * The number of items in the collection.
+ */
+ this.__defineGetter__("length", function Collection_get_length() {
+ return array.length;
+ });
+
+ /**
+ * Adds a single item or an array of items to the collection. Any items
+ * already contained in the collection are ignored.
+ *
+ * @param itemOrItems
+ * An item or array of items.
+ * @return The collection.
+ */
+ this.add = function Collection_add(itemOrItems) {
+ let items = toArray(itemOrItems);
+ for (let i = 0; i < items.length; i++) {
+ let item = items[i];
+ if (array.indexOf(item) < 0)
+ array.push(item);
+ }
+ return this;
+ };
+
+ /**
+ * Removes a single item or an array of items from the collection. Any items
+ * not contained in the collection are ignored.
+ *
+ * @param itemOrItems
+ * An item or array of items.
+ * @return The collection.
+ */
+ this.remove = function Collection_remove(itemOrItems) {
+ let items = toArray(itemOrItems);
+ for (let i = 0; i < items.length; i++) {
+ let idx = array.indexOf(items[i]);
+ if (idx >= 0)
+ array.splice(idx, 1);
+ }
+ return this;
+ };
+};
+
+function toArray(itemOrItems) {
+ let isArr = itemOrItems &&
+ itemOrItems.constructor &&
+ itemOrItems.constructor.name === "Array";
+ return isArr ? itemOrItems : [itemOrItems];
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/content.js b/tools/addon-sdk-1.7/packages/api-utils/lib/content.js
new file mode 100644
index 0000000..a4acb94
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/content.js
@@ -0,0 +1,11 @@
+/* -*- 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/. */
+"use strict";
+
+exports.Loader = require('./content/loader').Loader;
+exports.Symbiont = require('./content/symbiont').Symbiont;
+exports.Worker = require('./content/worker').Worker;
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/content/loader.js b/tools/addon-sdk-1.7/packages/api-utils/lib/content/loader.js
new file mode 100644
index 0000000..62c3ad0
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/content/loader.js
@@ -0,0 +1,179 @@
+/* -*- 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/. */
+
+"use strict";
+
+const { EventEmitter } = require('../events');
+const { validateOptions } = require('../api-utils');
+const { URL } = require('../url');
+const file = require('../file');
+
+const LOCAL_URI_SCHEMES = ['resource', 'data'];
+
+// Returns `null` if `value` is `null` or `undefined`, otherwise `value`.
+function ensureNull(value) {
+ return value == null ? null : value;
+}
+
+// map of property validations
+const valid = {
+ contentURL: {
+ ok: function (value) {
+ try {
+ URL(value);
+ }
+ catch(e) {
+ return false;
+ }
+ return true;
+ },
+ msg: 'The `contentURL` option must be a valid URL.'
+ },
+ contentScriptFile: {
+ is: ['undefined', 'null', 'string', 'array'],
+ map: ensureNull,
+ ok: function(value) {
+ if (value === null)
+ return true;
+
+ value = [].concat(value);
+
+ // Make sure every item is a local file URL.
+ return value.every(function (item) {
+ try {
+ return ~LOCAL_URI_SCHEMES.indexOf(URL(item).scheme);
+ }
+ catch(e) {
+ return false;
+ }
+ });
+
+ },
+ msg: 'The `contentScriptFile` option must be a local URL or an array of URLs.'
+ },
+ contentScript: {
+ is: ['undefined', 'null', 'string', 'array'],
+ map: ensureNull,
+ ok: function(value) {
+ return !Array.isArray(value) || value.every(
+ function(item) { return typeof item === 'string' }
+ );
+ },
+ msg: 'The `contentScript` option must be a string or an array of strings.'
+ },
+ contentScriptWhen: {
+ is: ['string'],
+ ok: function(value) { return ~['start', 'ready', 'end'].indexOf(value) },
+ map: function(value) {
+ return value || 'end';
+ },
+ msg: 'The `contentScriptWhen` option must be either "start", "ready" or "end".'
+ }
+};
+exports.validationAttributes = valid;
+
+/**
+ * Shortcut function to validate property with validation.
+ * @param {Object|Number|String} suspect
+ * value to validate
+ * @param {Object} validation
+ * validation rule passed to `api-utils`
+ */
+function validate(suspect, validation) validateOptions(
+ { $: suspect },
+ { $: validation }
+).$
+
+function Allow(script) ({
+ get script() script,
+ set script(value) script = !!value
+})
+
+/**
+ * Trait is intended to be used in some composition. It provides set of core
+ * properties and bounded validations to them. Trait is useful for all the
+ * compositions providing high level APIs for interaction with content.
+ * Property changes emit `"propertyChange"` events on instances.
+ */
+const Loader = EventEmitter.compose({
+ /**
+ * Permissions for the content, with the following keys:
+ * @property {Object} [allow = { script: true }]
+ * @property {Boolean} [allow.script = true]
+ * Whether or not to execute script in the content. Defaults to true.
+ */
+ get allow() this._allow || (this._allow = Allow(true)),
+ set allow(value) this.allow.script = value && value.script,
+ _allow: null,
+ /**
+ * The content to load. Either a string of HTML or a URL.
+ * @type {String}
+ */
+ get contentURL() this._contentURL,
+ set contentURL(value) {
+ value = validate(value, valid.contentURL);
+ if (this._contentURL != value) {
+ this._emit('propertyChange', {
+ contentURL: this._contentURL = value
+ });
+ }
+ },
+ _contentURL: null,
+ /**
+ * When to load the content scripts.
+ * Possible values are "end" (default), which loads them once all page
+ * contents have been loaded, "ready", which loads them once DOM nodes are
+ * ready (ie like DOMContentLoaded event), and "start", which loads them once
+ * the `window` object for the page has been created, but before any scripts
+ * specified by the page have been loaded.
+ * Property change emits `propertyChange` event on instance with this key
+ * and new value.
+ * @type {'start'|'ready'|'end'}
+ */
+ get contentScriptWhen() this._contentScriptWhen,
+ set contentScriptWhen(value) {
+ value = validate(value, valid.contentScriptWhen);
+ if (value !== this._contentScriptWhen) {
+ this._emit('propertyChange', {
+ contentScriptWhen: this._contentScriptWhen = value
+ });
+ }
+ },
+ _contentScriptWhen: 'end',
+ /**
+ * The URLs of content scripts.
+ * Property change emits `propertyChange` event on instance with this key
+ * and new value.
+ * @type {String[]}
+ */
+ get contentScriptFile() this._contentScriptFile,
+ set contentScriptFile(value) {
+ value = validate(value, valid.contentScriptFile);
+ if (value != this._contentScriptFile) {
+ this._emit('propertyChange', {
+ contentScriptFile: this._contentScriptFile = value
+ });
+ }
+ },
+ _contentScriptFile: null,
+ /**
+ * The texts of content script.
+ * Property change emits `propertyChange` event on instance with this key
+ * and new value.
+ * @type {String|undefined}
+ */
+ get contentScript() this._contentScript,
+ set contentScript(value) {
+ value = validate(value, valid.contentScript);
+ if (value != this._contentScript) {
+ this._emit('propertyChange', {
+ contentScript: this._contentScript = value
+ });
+ }
+ },
+ _contentScript: null
+});
+exports.Loader = Loader;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/content/symbiont.js b/tools/addon-sdk-1.7/packages/api-utils/lib/content/symbiont.js
new file mode 100644
index 0000000..e7c959c
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/content/symbiont.js
@@ -0,0 +1,183 @@
+/* -*- 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/. */
+"use strict";
+
+const { Worker } = require('./worker');
+const { Loader } = require('./loader');
+const hiddenFrames = require('../hidden-frame');
+const observers = require('../observer-service');
+const unload = require('../unload');
+
+/**
+ * This trait is layered on top of `Worker` and in contrast to symbiont
+ * Worker constructor requires `content` option that represents content
+ * that will be loaded in the provided frame, if frame is not provided
+ * Worker will create hidden one.
+ */
+const Symbiont = Worker.resolve({
+ constructor: '_initWorker',
+ destroy: '_workerDestroy'
+ }).compose(Loader, {
+
+ /**
+ * The constructor requires all the options that are required by
+ * `require('content').Worker` with the difference that the `frame` option
+ * is optional. If `frame` is not provided, `contentURL` is expected.
+ * @param {Object} options
+ * @param {String} options.contentURL
+ * URL of a content to load into `this._frame` and create worker for.
+ * @param {Element} [options.frame]
+ * iframe element that is used to load `options.contentURL` into.
+ * if frame is not provided hidden iframe will be created.
+ */
+ constructor: function Symbiont(options) {
+ options = options || {};
+
+ if ('contentURL' in options)
+ this.contentURL = options.contentURL;
+ if ('contentScriptWhen' in options)
+ this.contentScriptWhen = options.contentScriptWhen;
+ if ('contentScriptFile' in options)
+ this.contentScriptFile = options.contentScriptFile;
+ if ('contentScript' in options)
+ this.contentScript = options.contentScript;
+ if ('allow' in options)
+ this.allow = options.allow;
+ if ('onError' in options)
+ this.on('error', options.onError);
+ if ('onMessage' in options)
+ this.on('message', options.onMessage);
+ if ('frame' in options) {
+ this._initFrame(options.frame);
+ }
+ else {
+ let self = this;
+ this._hiddenFrame = hiddenFrames.HiddenFrame({
+ onReady: function onFrame() {
+ self._initFrame(this.element);
+ }
+ });
+ hiddenFrames.add(this._hiddenFrame);
+ }
+
+ unload.ensure(this._public, "destroy");
+ },
+
+ destroy: function destroy() {
+ this._workerDestroy();
+ this._unregisterListener();
+ this._frame = null;
+ if (this._hiddenFrame) {
+ hiddenFrames.remove(this._hiddenFrame);
+ this._hiddenFrame = null;
+ }
+ },
+
+ /**
+ * XUL iframe or browser elements with attribute `type` being `content`.
+ * Used to create `ContentSymbiont` from.
+ * @type {nsIFrame|nsIBrowser}
+ */
+ _frame: null,
+
+ /**
+ * Listener to the `'frameReady"` event (emitted when `iframe` is ready).
+ * Removes listener, sets right permissions to the frame and loads content.
+ */
+ _initFrame: function _initFrame(frame) {
+ if (this._loadListener)
+ this._unregisterListener();
+
+ this._frame = frame;
+ frame.docShell.allowJavascript = this.allow.script;
+ frame.setAttribute("src", this._contentURL);
+
+ // Inject `addon` object in document if we load a document from
+ // one of our addon folder and if no content script are defined. bug 612726
+ let isDataResource =
+ typeof this._contentURL == "string" &&
+ this._contentURL.indexOf(require("@packaging").uriPrefix) == 0;
+ let hasContentScript =
+ (Array.isArray(this.contentScript) ? this.contentScript.length > 0
+ : !!this.contentScript) ||
+ (Array.isArray(this.contentScriptFile) ? this.contentScriptFile.length > 0
+ : !!this.contentScriptFile);
+ // If we have to inject `addon` we have to do it before document
+ // script execution, so during `start`:
+ this._injectInDocument = isDataResource && !hasContentScript;
+ if (this._injectInDocument)
+ this.contentScriptWhen = "start";
+
+ if ((frame.contentDocument.readyState == "complete" ||
+ (frame.contentDocument.readyState == "interactive" &&
+ this.contentScriptWhen != 'end' )) &&
+ frame.contentDocument.location == this._contentURL) {
+ // In some cases src doesn't change and document is already ready
+ // (for ex: when the user moves a widget while customizing toolbars.)
+ this._onInit();
+ return;
+ }
+
+ let self = this;
+
+ if ('start' == this.contentScriptWhen) {
+ this._loadEvent = 'start';
+ observers.add('document-element-inserted',
+ this._loadListener = function onStart(doc) {
+
+ let window = doc.defaultView;
+ if (window && window == frame.contentWindow) {
+ self._unregisterListener();
+ self._onInit();
+ }
+
+ });
+ return;
+ }
+
+ let eventName = 'end' == this.contentScriptWhen ? 'load' : 'DOMContentLoaded';
+ let self = this;
+ this._loadEvent = eventName;
+ frame.addEventListener(eventName,
+ this._loadListener = function _onReady(event) {
+
+ if (event.target != frame.contentDocument)
+ return;
+ self._unregisterListener();
+
+ self._onInit();
+
+ }, true);
+
+ },
+
+ /**
+ * Unregister listener that watchs for document being ready to be injected.
+ * This listener is registered in `Symbiont._initFrame`.
+ */
+ _unregisterListener: function _unregisterListener() {
+ if (!this._loadListener)
+ return;
+ if (this._loadEvent == "start") {
+ observers.remove('document-element-inserted', this._loadListener);
+ }
+ else {
+ this._frame.removeEventListener(this._loadEvent, this._loadListener,
+ true);
+ }
+ this._loadListener = null;
+ },
+
+ /**
+ * Called by Symbiont itself when the frame is ready to load
+ * content scripts according to contentScriptWhen. Overloaded by Panel.
+ */
+ _onInit: function () {
+ this._initWorker({ window: this._frame.contentWindow });
+ }
+
+});
+exports.Symbiont = Symbiont;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/content/worker.js b/tools/addon-sdk-1.7/packages/api-utils/lib/content/worker.js
new file mode 100644
index 0000000..c6f35d9
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/content/worker.js
@@ -0,0 +1,491 @@
+/* -*- 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/. */
+"use strict";
+
+const { Trait } = require('../traits');
+const { EventEmitter, EventEmitterTrait } = require('../events');
+const { Ci, Cu, Cc } = require('chrome');
+const timer = require('../timer');
+const { URL } = require('../url');
+const unload = require('../unload');
+const observers = require('../observer-service');
+const { Cortex } = require('../cortex');
+const self = require("self");
+const { sandbox, evaluate, load } = require("../sandbox");
+const { merge } = require('../utils/object');
+
+const CONTENT_PROXY_URL = self.data.url("content-proxy.js");
+const CONTENT_WORKER_URL = self.data.url("worker.js");
+
+const JS_VERSION = '1.8';
+
+const ERR_DESTROYED =
+ "The page has been destroyed and can no longer be used.";
+
+/**
+ * This key is not exported and should only be used for proxy tests.
+ * The following `PRIVATE_KEY` is used in addon module scope in order to tell
+ * Worker API to expose `UNWRAP_ACCESS_KEY` in content script.
+ * This key allows test-content-proxy.js to unwrap proxy with valueOf:
+ * let xpcWrapper = proxyWrapper.valueOf(UNWRAP_ACCESS_KEY);
+ */
+const PRIVATE_KEY = {};
+
+
+const WorkerSandbox = EventEmitter.compose({
+
+ /**
+ * Emit a message to the worker content sandbox
+ */
+ emit: function emit() {
+ // First ensure having a regular array
+ // (otherwise, `arguments` would be mapped to an object by `stringify`)
+ let array = Array.slice(arguments);
+ // JSON.stringify is buggy with cross-sandbox values,
+ // it may return "{}" on functions. Use a replacer to match them correctly.
+ function replacer(k, v) {
+ return typeof v === "function" ? undefined : v;
+ }
+ // Ensure having an asynchronous behavior
+ let self = this;
+ timer.setTimeout(function () {
+ self._emitToContent(JSON.stringify(array, replacer));
+ }, 0);
+ },
+
+ /**
+ * Synchronous version of `emit`.
+ * /!\ Should only be used when it is strictly mandatory /!\
+ * Doesn't ensure passing only JSON values.
+ * Mainly used by context-menu in order to avoid breaking it.
+ */
+ emitSync: function emitSync() {
+ return this._emitToContent(Array.slice(arguments));
+ },
+
+ /**
+ * Tells if content script has at least one listener registered for one event,
+ * through `self.on('xxx', ...)`.
+ * /!\ Shouldn't be used. Implemented to avoid breaking context-menu API.
+ */
+ hasListenerFor: function hasListenerFor(name) {
+ return this._hasListenerFor(name);
+ },
+
+ /**
+ * Method called by the worker sandbox when it needs to send a message
+ */
+ _onContentEvent: function onContentEvent(args) {
+ // As `emit`, we ensure having an asynchronous behavior
+ let self = this;
+ timer.setTimeout(function () {
+ // We emit event to chrome/addon listeners
+ self._emit.apply(self, JSON.parse(args));
+ }, 0);
+ },
+
+ /**
+ * Configures sandbox and loads content scripts into it.
+ * @param {Worker} worker
+ * content worker
+ */
+ constructor: function WorkerSandbox(worker) {
+ this._addonWorker = worker;
+
+ // Ensure that `emit` has always the right `this`
+ this.emit = this.emit.bind(this);
+ this.emitSync = this.emitSync.bind(this);
+
+ // We receive a wrapped window, that may be an xraywrapper if it's content
+ let window = worker._window;
+ let proto = window;
+
+ // Instantiate trusted code in another Sandbox in order to prevent content
+ // script from messing with standard classes used by proxy and API code.
+ let apiSanbox = sandbox(window, { wantXrays: true });
+
+ // Build content proxies only if the document has a non-system principal
+ if (window.wrappedJSObject) {
+ apiSanbox.console = console;
+ // Execute the proxy code
+ load(apiSanbox, CONTENT_PROXY_URL);
+ // Get a reference of the window's proxy
+ proto = apiSanbox.create(window);
+ }
+
+ // Create the sandbox and bind it to window in order for content scripts to
+ // have access to all standard globals (window, document, ...)
+ let content = this._sandbox = sandbox(window, {
+ sandboxPrototype: proto,
+ wantXrays: true
+ });
+ merge(content, {
+ // We need "this === window === top" to be true in toplevel scope:
+ get window() content,
+ get top() content,
+ // Use the Greasemonkey naming convention to provide access to the
+ // unwrapped window object so the content script can access document
+ // JavaScript values.
+ // NOTE: this functionality is experimental and may change or go away
+ // at any time!
+ get unsafeWindow() window.wrappedJSObject
+ });
+
+ // Load trusted code that will inject content script API.
+ // We need to expose JS objects defined in same principal in order to
+ // avoid having any kind of wrapper.
+ load(apiSanbox, CONTENT_WORKER_URL);
+
+ // Then call `inject` method and communicate with this script
+ // by trading two methods that allow to send events to the other side:
+ // - `onEvent` called by content script
+ // - `result.emitToContent` called by addon script
+ let chromeAPI = {
+ timers: {
+ setTimeout: timer.setTimeout,
+ setInterval: timer.setInterval,
+ clearTimeout: timer.clearTimeout,
+ clearInterval: timer.clearInterval
+ }
+ };
+ let onEvent = this._onContentEvent.bind(this);
+ // `ContentWorker` is defined in CONTENT_WORKER_URL file
+ let result = apiSanbox.ContentWorker.inject(content, chromeAPI, onEvent);
+ this._emitToContent = result.emitToContent;
+ this._hasListenerFor = result.hasListenerFor;
+
+ // Handle messages send by this script:
+ let self = this;
+ // console.xxx calls
+ this.on("console", function consoleListener(kind) {
+ console[kind].apply(console, Array.slice(arguments, 1));
+ });
+
+ // self.postMessage calls
+ this.on("message", function postMessage(data) {
+ self._addonWorker._emit('message', data);
+ });
+
+ // self.port.emit calls
+ this.on("event", function portEmit(name, args) {
+ self._addonWorker._onContentScriptEvent.apply(self._addonWorker, arguments);
+ });
+
+ // Internal feature that is only used by SDK tests:
+ // Expose unlock key to content script context.
+ // See `PRIVATE_KEY` definition for more information.
+ if (apiSanbox && worker._expose_key)
+ content.UNWRAP_ACCESS_KEY = apiSanbox.UNWRAP_ACCESS_KEY;
+
+ // Inject `addon` global into target document if document is trusted,
+ // `addon` in document is equivalent to `self` in content script.
+ if (worker._injectInDocument) {
+ let win = window.wrappedJSObject ? window.wrappedJSObject : window;
+ Object.defineProperty(win, "addon", {
+ value: content.self
+ }
+ );
+ }
+
+ // The order of `contentScriptFile` and `contentScript` evaluation is
+ // intentional, so programs can load libraries like jQuery from script URLs
+ // and use them in scripts.
+ let contentScriptFile = ('contentScriptFile' in worker) ? worker.contentScriptFile
+ : null,
+ contentScript = ('contentScript' in worker) ? worker.contentScript : null;
+
+ if (contentScriptFile) {
+ if (Array.isArray(contentScriptFile))
+ this._importScripts.apply(this, contentScriptFile);
+ else
+ this._importScripts(contentScriptFile);
+ }
+ if (contentScript) {
+ this._evaluate(
+ Array.isArray(contentScript) ? contentScript.join(';\n') : contentScript
+ );
+ }
+ },
+ destroy: function destroy() {
+ this.emitSync("destroy");
+ this._sandbox = null;
+ this._addonWorker = null;
+ },
+
+ /**
+ * JavaScript sandbox where all the content scripts are evaluated.
+ * {Sandbox}
+ */
+ _sandbox: null,
+
+ /**
+ * Reference to the addon side of the worker.
+ * @type {Worker}
+ */
+ _addonWorker: null,
+
+ /**
+ * Evaluates code in the sandbox.
+ * @param {String} code
+ * JavaScript source to evaluate.
+ * @param {String} [filename='javascript:' + code]
+ * Name of the file
+ */
+ _evaluate: function(code, filename) {
+ try {
+ evaluate(this._sandbox, code, filename || 'javascript:' + code);
+ }
+ catch(e) {
+ this._addonWorker._emit('error', e);
+ }
+ },
+ /**
+ * Imports scripts to the sandbox by reading files under urls and
+ * evaluating its source. If exception occurs during evaluation
+ * `"error"` event is emitted on the worker.
+ * This is actually an analog to the `importScript` method in web
+ * workers but in our case it's not exposed even though content
+ * scripts may be able to do it synchronously since IO operation
+ * takes place in the UI process.
+ */
+ _importScripts: function _importScripts(url) {
+ let urls = Array.slice(arguments, 0);
+ for each (let contentScriptFile in urls) {
+ try {
+ let uri = URL(contentScriptFile);
+ if (uri.scheme === 'resource')
+ load(this._sandbox, String(uri));
+ else
+ throw Error("Unsupported `contentScriptFile` url: " + String(uri));
+ }
+ catch(e) {
+ this._addonWorker._emit('error', e);
+ }
+ }
+ }
+});
+
+/**
+ * Message-passing facility for communication between code running
+ * in the content and add-on process.
+ * @see https://jetpack.mozillalabs.com/sdk/latest/docs/#module/api-utils/content/worker
+ */
+const Worker = EventEmitter.compose({
+ on: Trait.required,
+ _removeAllListeners: Trait.required,
+
+ /**
+ * Sends a message to the worker's global scope. Method takes single
+ * argument, which represents data to be sent to the worker. The data may
+ * be any primitive type value or `JSON`. Call of this method asynchronously
+ * emits `message` event with data value in the global scope of this
+ * symbiont.
+ *
+ * `message` event listeners can be set either by calling
+ * `self.on` with a first argument string `"message"` or by
+ * implementing `onMessage` function in the global scope of this worker.
+ * @param {Number|String|JSON} data
+ */
+ postMessage: function postMessage(data) {
+ if (!this._contentWorker)
+ throw new Error(ERR_DESTROYED);
+ this._contentWorker.emit("message", data);
+ },
+
+ /**
+ * EventEmitter, that behaves (calls listeners) asynchronously.
+ * A way to send customized messages to / from the worker.
+ * Events from in the worker can be observed / emitted via
+ * worker.on / worker.emit.
+ */
+ get port() {
+ // We generate dynamically this attribute as it needs to be accessible
+ // before Worker.constructor gets called. (For ex: Panel)
+
+ // create an event emitter that receive and send events from/to the worker
+ let self = this;
+ this._port = EventEmitterTrait.create({
+ emit: function () self._emitEventToContent(Array.slice(arguments))
+ });
+
+ // expose wrapped port, that exposes only public properties:
+ // We need to destroy this getter in order to be able to set the
+ // final value. We need to update only public port attribute as we never
+ // try to access port attribute from private API.
+ delete this._public.port;
+ this._public.port = Cortex(this._port);
+ // Replicate public port to the private object
+ delete this.port;
+ this.port = this._public.port;
+
+ return this._port;
+ },
+
+ /**
+ * Same object than this.port but private API.
+ * Allow access to _emit, in order to send event to port.
+ */
+ _port: null,
+
+ /**
+ * Emit a custom event to the content script,
+ * i.e. emit this event on `self.port`
+ */
+ _emitEventToContent: function _emitEventToContent(args) {
+ // We need to save events that are emitted before the worker is
+ // initialized
+ if (!this._inited) {
+ this._earlyEvents.push(args);
+ return;
+ }
+
+ // We throw exception when the worker has been destroyed
+ if (!this._contentWorker) {
+ throw new Error(ERR_DESTROYED);
+ }
+
+ // Forward the event to the WorkerSandbox object
+ this._contentWorker.emit.apply(null, ["event"].concat(args));
+ },
+
+ // Is worker connected to the content worker sandbox ?
+ _inited: false,
+
+ // List of custom events fired before worker is initialized
+ get _earlyEvents() {
+ delete this._earlyEvents;
+ this._earlyEvents = [];
+ return this._earlyEvents;
+ },
+
+ constructor: function Worker(options) {
+ options = options || {};
+
+ if ('window' in options)
+ this._window = options.window;
+ if ('contentScriptFile' in options)
+ this.contentScriptFile = options.contentScriptFile;
+ if ('contentScript' in options)
+ this.contentScript = options.contentScript;
+ if ('onError' in options)
+ this.on('error', options.onError);
+ if ('onMessage' in options)
+ this.on('message', options.onMessage);
+ if ('onDetach' in options)
+ this.on('detach', options.onDetach);
+
+ // Internal feature that is only used by SDK unit tests.
+ // See `PRIVATE_KEY` definition for more information.
+ if ('exposeUnlockKey' in options && options.exposeUnlockKey === PRIVATE_KEY)
+ this._expose_key = true;
+
+ // Track document unload to destroy this worker.
+ // We can't watch for unload event on page's window object as it
+ // prevents bfcache from working:
+ // https://developer.mozilla.org/En/Working_with_BFCache
+ this._windowID = this._window.
+ QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIDOMWindowUtils).
+ currentInnerWindowID;
+ observers.add("inner-window-destroyed",
+ this._documentUnload = this._documentUnload.bind(this));
+
+ unload.ensure(this._public, "destroy");
+
+ // Ensure that worker._port is initialized for contentWorker to be able
+ // to send use event during WorkerSandbox(this)
+ this.port;
+
+ // will set this._contentWorker pointing to the private API:
+ this._contentWorker = WorkerSandbox(this);
+
+ // Mainly enable worker.port.emit to send event to the content worker
+ this._inited = true;
+
+ // Flush all events that have been fired before the worker is initialized.
+ this._earlyEvents.forEach((function (args) this._emitEventToContent(args)).
+ bind(this));
+ },
+
+ _documentUnload: function _documentUnload(subject, topic, data) {
+ let innerWinID = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
+ if (innerWinID != this._windowID) return false;
+ this._workerCleanup();
+ return true;
+ },
+
+ get url() {
+ // this._window will be null after detach
+ return this._window ? this._window.document.location.href : null;
+ },
+
+ get tab() {
+ if (this._window) {
+ let tab = require("../tabs/tab");
+ // this._window will be null after detach
+ return tab.getTabForWindow(this._window);
+ }
+ return null;
+ },
+
+ /**
+ * Tells content worker to unload itself and
+ * removes all the references from itself.
+ */
+ destroy: function destroy() {
+ this._workerCleanup();
+ this._removeAllListeners();
+ },
+
+ /**
+ * Remove all internal references to the attached document
+ * Tells _port to unload itself and removes all the references from itself.
+ */
+ _workerCleanup: function _workerCleanup() {
+ // maybe unloaded before content side is created
+ // As Symbiont call worker.constructor on document load
+ if (this._contentWorker)
+ this._contentWorker.destroy();
+ this._contentWorker = null;
+ this._window = null;
+ // This method may be called multiple times,
+ // avoid dispatching `detach` event more than once
+ if (this._windowID) {
+ this._windowID = null;
+ observers.remove("inner-window-destroyed", this._documentUnload);
+ this._earlyEvents.slice(0, this._earlyEvents.length);
+ this._emit("detach");
+ }
+ },
+
+ /**
+ * Receive an event from the content script that need to be sent to
+ * worker.port. Provide a way for composed object to catch all events.
+ */
+ _onContentScriptEvent: function _onContentScriptEvent() {
+ this._port._emit.apply(this._port, arguments);
+ },
+
+ /**
+ * Reference to the content side of the worker.
+ * @type {WorkerGlobalScope}
+ */
+ _contentWorker: null,
+
+ /**
+ * Reference to the window that is accessible from
+ * the content scripts.
+ * @type {Object}
+ */
+ _window: null,
+
+ /**
+ * Flag to enable `addon` object injection in document. (bug 612726)
+ * @type {Boolean}
+ */
+ _injectInDocument: false
+});
+exports.Worker = Worker;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/cortex.js b/tools/addon-sdk-1.7/packages/api-utils/lib/cortex.js
new file mode 100644
index 0000000..1c3a1b3
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/cortex.js
@@ -0,0 +1,109 @@
+/* vim:set ts=2 sw=2 sts=2
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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` is being used in the module in order to make it reusable in
+// environments in which `let` and `const` is not yet supported.
+
+// Returns `object`'s property value, where `name` is a name of the property.
+function get(object, name) {
+ return object[name];
+}
+
+// Assigns `value` to the `object`'s property, where `name` is the name of the
+// property.
+function set(object, name, value) {
+ return object[name] = value;
+}
+
+/**
+ * Given an `object` containing a property with the given `name`, create
+ * a property descriptor that can be used to define alias/proxy properties
+ * on other objects. A change in the value of an alias will propagate
+ * to the aliased property and vice versa.
+ */
+function createAliasProperty(object, name) {
+ // Getting own property descriptor of an `object` for the given `name` as
+ // we are going to create proxy analog.
+ var property = Object.getOwnPropertyDescriptor(object, name);
+ var descriptor = {
+ configurable: property.configurable,
+ enumerable: property.enumerable,
+ alias: true
+ };
+
+ // If the original property has a getter and/or setter, bind a
+ // corresponding getter/setter in the alias descriptor to the original
+ // object, so the `this` object in the getter/setter is the original object
+ // rather than the alias.
+ if ("get" in property && property.get)
+ descriptor.get = property.get.bind(object);
+ if ("set" in property && property.set)
+ descriptor.set = property.set.bind(object);
+
+ // If original property was a value property.
+ if ("value" in property) {
+ // If original property is a method using it's `object` bounded copy.
+ if (typeof property.value === "function") {
+ descriptor.value = property.value.bind(object);
+ // Also preserving writability of the original property.
+ descriptor.writable = property.writable;
+ }
+
+ // If the original property was just a data property, we create proxy
+ // accessors using our custom get/set functions to propagate changes to the
+ // original `object` and vice versa.
+ else {
+ descriptor.get = get.bind(null, object, name);
+ descriptor.set = set.bind(null, object, name);
+ }
+ }
+ return descriptor;
+}
+
+// Defines property on `object` object with a name `alias` if given if not
+// defaults to `name` that represents an alias of `source[name]`. If aliased
+// property was an assessor or a method `this` pseudo-variable will be `source`
+// when invoked. If aliased property was a data property changes on any of the
+// aliases will propagate to the `source[name]` and also other way round.
+function defineAlias(source, target, name, alias) {
+ return Object.defineProperty(target, alias || name,
+ createAliasProperty(source, name));
+}
+
+/**
+ * Function takes any `object` and returns a proxy for its own public
+ * properties. By default properties are considered to be public if they don't
+ * start with `"_"`, but default behavior can be overridden if needed, by
+ * passing array of public property `names` as a second argument. By default
+ * returned object will be direct decedent of the given `object`'s prototype,
+ * but this can be overridden by passing third optional argument, that will be
+ * used as `prototype` instead.
+ * @param {Object} object
+ * Object to create cortex for.
+ * @param {String[]} [names]
+ * Optional array of public property names.
+ * @param {Object} [prototype]
+ * Optional argument that will be used as `prototype` of the returned object,
+ * if not provided `Object.getPrototypeOf(object)` is used instead.
+ */
+exports.Cortex = function Cortex(object, names, prototype) {
+ // Creating a cortex object from the given `prototype`, if one was not
+ // provided then `prototype` of a given `object` is used. This allows
+ // consumer to define expected behavior `instanceof`. In common case
+ // `prototype` argument can be omitted to preserve same behavior of
+ // `instanceof` as on original `object`.
+ var cortex = Object.create(prototype || Object.getPrototypeOf(object));
+ // Creating alias properties on the `cortex` object for all the own
+ // properties of the original `object` that are contained in `names` array.
+ // If `names` array is not provided then all the properties that don't
+ // start with `"_"` are aliased.
+ Object.getOwnPropertyNames(object).forEach(function (name) {
+ if ((!names && "_" !== name.charAt(0)) || (names && ~names.indexOf(name)))
+ defineAlias(object, cortex, name);
+ });
+ return cortex;
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/cuddlefish.js b/tools/addon-sdk-1.7/packages/api-utils/lib/cuddlefish.js
new file mode 100644
index 0000000..ac01aee
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/cuddlefish.js
@@ -0,0 +1,302 @@
+/* vim:set ts=2 sw=2 sts=2 expandtab */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+var EXPORTED_SYMBOLS = [ 'Loader' ];
+
+!function(exports) {
+
+"use strict";
+
+const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu,
+ results: Cr, manager: Cm } = Components;
+const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')();
+const scriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1'].
+ getService(Ci.mozIJSSubScriptLoader);
+
+const Sandbox = {
+ new: function ({ principal, prototype, name, existingSandbox }) {
+ let options = { sandboxPrototype: prototype || Sandbox.prototype,
+ wantXrays: Sandbox.wantXrays };
+ if (name)
+ options.sandboxName = name;
+ if (existingSandbox)
+ options.sameGroupAs = existingSandbox.sandbox;
+ let sandbox = Object.create(Sandbox, {
+ sandbox: { value: Cu.Sandbox(principal || Sandbox.principal, options) }
+ });
+ // There are few properties (dump, Iterator) that by default appear in
+ // sandboxes shadowing properties provided by a prototype. To workaround
+ // this we override all such properties by copying them directly to the
+ // sandbox.
+ Object.keys(prototype).forEach(function onEach(key) {
+ if (sandbox.sandbox[key] !== prototype[key])
+ sandbox.sandbox[key] = prototype[key];
+ });
+ return sandbox;
+ },
+ evaluate: function evaluate(source, uri, lineNumber) {
+ return Cu.evalInSandbox(
+ source,
+ this.sandbox,
+ this.version,
+ uri,
+ lineNumber || this.lineNumber
+ );
+ },
+ load: function load(uri) {
+ scriptLoader.loadSubScript(uri, this.sandbox, 'UTF-8');
+ },
+ merge: function merge(properties) {
+ Object.getOwnPropertyNames(properties).forEach(function(name) {
+ Object.defineProperty(this.sandbox, name,
+ Object.getOwnPropertyDescriptor(properties, name));
+ }, this);
+ },
+ principal: systemPrincipal,
+ version: '1.8',
+ lineNumber: 1,
+ wantXrays: false,
+ prototype: {}
+};
+
+// the Module object made available to CommonJS modules when they are
+// evaluated, along with 'exports' and 'uri'
+const Module = {
+ new: function(id, path, uri) {
+ let module = Object.create(this);
+
+ module.id = id;
+ module.path = path;
+ module.uri = uri;
+ module.exports = {};
+
+ return module;
+ },
+ // TODO: I'd like to remove this, it's not used adds complexity and does
+ // not has much adoption in commonjs either.
+ setExports: function setExports(exports) {
+ this.exports = exports;
+ }
+};
+
+const Loader = {
+ new: function (options) {
+ let loader = Object.create(Loader, {
+ // Manifest generated by a linker, containing map of module url's mapped
+ // to it's requirements, comes from harness-options.json
+ manifest: { value: options.manifest || {} },
+
+ // Following property may be passed in (usually for mocking purposes) in
+ // order to override default modules cache.
+ modules: { value: options.modules || Object.create(Loader.modules) },
+ globals: { value: options.globals || {} },
+
+ uriPrefix: { value: options.uriPrefix },
+
+ sandboxes: { value: {} }
+ });
+ loader.require = this.require.bind(loader, options.loader);
+
+ // some 'magic' modules, that have no corresponding .js file
+ loader.modules['@packaging'] = Object.freeze({
+ id: '@packaging',
+ exports: JSON.parse(JSON.stringify(options))
+ });
+ loader.modules['@loader'] = Object.freeze({
+ exports: Object.freeze({ Loader: Loader }),
+ id: '@loader'
+ });
+
+ // This special module defines globals which will be added to every
+ // module this loader creates
+ let globals = loader.require('api-utils/globals!');
+ Object.getOwnPropertyNames(globals).forEach(function(name) {
+ Object.defineProperty(loader.globals, name,
+ Object.getOwnPropertyDescriptor(globals, name));
+ });
+ // Freeze globals so that modules won't have a chance to mutate scope of
+ // other modules.
+ Object.freeze(globals);
+
+ // Override global `dump` so that it behaves same as in any other module (
+ // currently we override dump to write to a file instead of `stdout` so that
+ // python can read it on windows).
+ dump = globals.dump;
+
+ return Object.freeze(loader);
+ },
+ modules: {
+ 'chrome': Object.freeze({
+ exports: Object.freeze({
+ Cc: Cc,
+ CC: CC,
+ Ci: Ci,
+ Cu: Cu,
+ Cr: Cr,
+ Cm: Cm,
+ components: Components,
+ messageManager: 'addMessageListener' in exports ? exports : null
+ }),
+ id: 'chrome'
+ }),
+ 'self': function self(loader, requirer) {
+ return loader.require('api-utils/self!').create(requirer.path);
+ },
+ },
+
+ // populate a Module by evaluating the CommonJS module code in the sandbox
+ load: function load(module) {
+ let require = Loader.require.bind(this, module.path);
+ require.main = this.main;
+
+ // Get an existing module sandbox, if any, so we can reuse its compartment
+ // when creating the new one to reduce memory consumption.
+ let existingSandbox = [this.sandboxes[p] for (p in this.sandboxes)][0];
+
+ // XXX Always set "principal" to work around bug 705795, which generates
+ // 'reference to undefined property "principal"' warnings when the argument
+ // is deconstructed in the "new" function's parameter list.
+ // FIXME: stop setting "principal" once bug 705795 is fixed.
+ let sandbox = this.sandboxes[module.path] =
+ Sandbox.new({ principal: null,
+ prototype: this.globals,
+ name: module.uri,
+ existingSandbox: existingSandbox });
+ sandbox.merge({
+ require: require,
+ module: module,
+ exports: module.exports
+ });
+
+ sandbox.load(module.uri);
+
+ // Workaround for bug 674195. Freezing objects from other sandboxes fail,
+ // so we create descendant and freeze it instead.
+ if (typeof(module.exports) === 'object') {
+ module.exports = Object.prototype.isPrototypeOf(module.exports) ?
+ Object.freeze(module.exports) :
+ Object.freeze(Object.create(module.exports));
+ }
+ },
+
+ // this require() is the main entry point for regular CommonJS modules. The
+ // bind() in load (above) causes those modules to get a very restricted
+ // form of this require(): one which can only ever reference this one
+ // loader, and which always uses their URI as a "base" (so they're limited
+ // to their own manifest entries, and can't import anything off the
+ // manifest).
+ require: function require(base, id) {
+ let module, manifest = this.manifest[base], requirer = this.modules[base];
+
+ if (!id)
+ throw Error("you must provide a module name when calling require() from "
+ + (requirer && requirer.id), base, id);
+
+ // If we have a manifest for requirer, then all it's requirements have been
+ // registered by linker and we should have a `path` to the required module.
+ // Even pseudo-modules like 'chrome', 'self', '@packaging', and '@loader'
+ // have pseudo-paths: exactly those same names.
+ // details see: Bug-697422.
+ let requirement = manifest && manifest.requirements[id];
+ if (!requirement)
+ throw Error("Module: " + (requirer && requirer.id) + ' located at ' +
+ base + " has no authority to load: " + id);
+ let path = requirement.path;
+
+ if (path in this.modules) {
+ module = this.modules[path];
+ }
+ else {
+ let uri = this.uriPrefix + path;
+ module = this.modules[path] = Module.new(id, path, uri);
+ this.load(module);
+ Object.freeze(module);
+ }
+
+ // "magic" modules which have contents that depend upon who imports them
+ // (like "self") are expressed in the Loader's pre-populated 'modules'
+ // table as callable functions, which are given the reference to this
+ // Loader and a copy of the importer's URI
+ //
+ // TODO: Find a better way to implement `self`.
+ // Maybe something like require('self!path/to/data')
+ if (typeof(module) === 'function')
+ module = module(this, requirer);
+
+ return module.exports;
+ },
+
+ // process.process() will eventually cause a call to main() to be evaluated
+ // in the addon's context. This function loads and executes the addon's
+ // entry point module.
+ main: function main(id, path) {
+ try {
+ let uri = this.uriPrefix + path;
+ let module = this.modules[path] = Module.new(id, path, uri);
+ this.load(module); // this is where the addon's main.js finally runs
+ let program = Object.freeze(module).exports;
+
+ if (typeof(program.onUnload) === 'function')
+ this.require('api-utils/unload').when(program.onUnload);
+
+ if (program.main) {
+ let { exit, staticArgs } = this.require('api-utils/system');
+ let { loadReason } = this.require('@packaging');
+ program.main({ loadReason: loadReason, staticArgs: staticArgs },
+ { print: function($) dump($ + '\n'), quit: exit });
+ }
+ } catch (error) {
+ Cu.reportError(error);
+ if (this.globals.console) this.globals.console.exception(error);
+ throw error;
+ }
+ },
+
+ // This is the main entry-point: bootstrap.js calls this when the add-on is
+ // installed. The order of calls is a bit confusing, but here's what
+ // happens (in temporal order):
+ // * process.spawn creates a new XUL 'browser' element which will house the
+ // main addon code. When e10s is active, this uses a real separate OS
+ // process. When e10s is disabled, this element lives in the one original
+ // process. Either way, its API is the same.
+ // * Grab the channel named "require!" and attach a handler which will load
+ // modules (in the chrome process) when requested to by the addon
+ // process. This handler uses Loader.require to import the module, then
+ // calls the module's .initialize() function to connect a new channel.
+ // The remote caller winds up with a channel reference, which they can
+ // use to send messages to the newly loaded module. This is for e10s.
+ // * After the channel handler is attached, process.process() (invoked by
+ // process.spawn()) will use loadScript() to evaluate code in the
+ // 'browser' element (which is where the main addon code starts running),
+ // to do the following:
+ // * create a Loader, initialized with the same manifest and
+ // harness-options.json that we've got
+ // * invoke it's main() method, with the name and path of the addon's
+ // entry module (which comes from linker via harness-options.js, and is
+ // usually main.js). That executes main(), above.
+ // * main() loads the addon's main.js, which executes all top-level
+ // forms. If the module defines an "exports.main=" function, we invoke
+ // that too. This is where the addon finally gets to run.
+ spawn: function spawn(id, path) {
+ let loader = this;
+ let process = this.require('api-utils/process');
+ process.spawn(id, path)(function(addon) {
+ // Listen to `require!` channel's input messages from the add-on process
+ // and load modules being required.
+ addon.channel('require!').input(function({ requirer: { path }, id }) {
+ try {
+ Loader.require.call(loader, path, id).initialize(addon.channel(id));
+ } catch (error) {
+ this.globals.console.exception(error);
+ }
+ });
+ });
+ },
+ unload: function unload(reason, callback) {
+ this.require('api-utils/unload').send(reason, callback);
+ }
+};
+exports.Loader = Loader;
+
+}(this);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/dom/events.js b/tools/addon-sdk-1.7/packages/api-utils/lib/dom/events.js
new file mode 100644
index 0000000..2168f42
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/dom/events.js
@@ -0,0 +1,136 @@
+/* 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/. */
+
+"use strict";
+
+// Utility function that returns copy of the given `text` with last character
+// removed if it is `"s"`.
+function singularify(text) {
+ return text[text.length - 1] === "s" ? text.substr(0, text.length - 1) : text;
+}
+
+// Utility function that takes event type, argument is passed to
+// `document.createEvent` and returns name of the initializer method of the
+// given event. Please note that there are some event types whose initializer
+// methods can't be guessed by this function. For more details see following
+// link: https://developer.mozilla.org/En/DOM/Document.createEvent
+function getInitializerName(category) {
+ return "init" + singularify(category);
+}
+
+/**
+ * Registers an event `listener` on a given `element`, that will be called
+ * when events of specified `type` is dispatched on the `element`.
+ * @param {Element} element
+ * Dom element to register listener on.
+ * @param {String} type
+ * A string representing the
+ * [event type](https://developer.mozilla.org/en/DOM/event.type) to
+ * listen for.
+ * @param {Function} listener
+ * Function that is called whenever an event of the specified `type`
+ * occurs.
+ * @param {Boolean} capture
+ * If true, indicates that the user wishes to initiate capture. After
+ * initiating capture, all events of the specified type will be dispatched
+ * to the registered listener before being dispatched to any `EventTarget`s
+ * beneath it in the DOM tree. Events which are bubbling upward through
+ * the tree will not trigger a listener designated to use capture.
+ * See [DOM Level 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/#event-flow)
+ * for a detailed explanation.
+ */
+function on(element, type, listener, capture) {
+ // `capture` defaults to `false`.
+ capture = capture || false;
+ element.addEventListener(type, listener, capture);
+}
+exports.on = on;
+
+/**
+ * Registers an event `listener` on a given `element`, that will be called
+ * only once, next time event of specified `type` is dispatched on the
+ * `element`.
+ * @param {Element} element
+ * Dom element to register listener on.
+ * @param {String} type
+ * A string representing the
+ * [event type](https://developer.mozilla.org/en/DOM/event.type) to
+ * listen for.
+ * @param {Function} listener
+ * Function that is called whenever an event of the specified `type`
+ * occurs.
+ * @param {Boolean} capture
+ * If true, indicates that the user wishes to initiate capture. After
+ * initiating capture, all events of the specified type will be dispatched
+ * to the registered listener before being dispatched to any `EventTarget`s
+ * beneath it in the DOM tree. Events which are bubbling upward through
+ * the tree will not trigger a listener designated to use capture.
+ * See [DOM Level 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/#event-flow)
+ * for a detailed explanation.
+ */
+function once(element, type, listener, capture) {
+ on(element, type, function selfRemovableListener(event) {
+ removeListener(element, type, selfRemovableListener, capture);
+ listener.apply(this, arguments);
+ }, capture);
+}
+exports.once = once;
+
+/**
+ * Unregisters an event `listener` on a given `element` for the events of the
+ * specified `type`.
+ *
+ * @param {Element} element
+ * Dom element to unregister listener from.
+ * @param {String} type
+ * A string representing the
+ * [event type](https://developer.mozilla.org/en/DOM/event.type) to
+ * listen for.
+ * @param {Function} listener
+ * Function that is called whenever an event of the specified `type`
+ * occurs.
+ * @param {Boolean} capture
+ * If true, indicates that the user wishes to initiate capture. After
+ * initiating capture, all events of the specified type will be dispatched
+ * to the registered listener before being dispatched to any `EventTarget`s
+ * beneath it in the DOM tree. Events which are bubbling upward through
+ * the tree will not trigger a listener designated to use capture.
+ * See [DOM Level 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/#event-flow)
+ * for a detailed explanation.
+ */
+function removeListener(element, type, listener, capture) {
+ element.removeEventListener(type, listener, capture);
+}
+exports.removeListener = removeListener;
+
+/**
+ * Emits event of the specified `type` and `category` on the given `element`.
+ * Specified `settings` are used to initialize event before dispatching it.
+ * @param {Element} element
+ * Dom element to dispatch event on.
+ * @param {String} type
+ * A string representing the
+ * [event type](https://developer.mozilla.org/en/DOM/event.type).
+ * @param {Object} options
+ * Options object containing following properties:
+ * - `category`: String passed to the `document.createEvent`. Option is
+ * optional and defaults to "UIEvents".
+ * - `initializer`: If passed it will be used as name of the method used
+ * to initialize event. If omitted name will be generated from the
+ * `category` field by prefixing it with `"init"` and removing last
+ * character if it matches `"s"`.
+ * - `settings`: Array of settings that are forwarded to the event
+ * initializer after firs `type` argument.
+ * @see https://developer.mozilla.org/En/DOM/Document.createEvent
+ */
+function emit(element, type, { category, initializer, settings }) {
+ category = category || "UIEvents";
+ initializer = initializer || getInitializerName(category);
+ let document = element.ownerDocument;
+ let event = document.createEvent(category);
+ event[initializer].apply(event, [type].concat(settings));
+ element.dispatchEvent(event);
+};
+exports.emit = emit;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/dom/events/keys.js b/tools/addon-sdk-1.7/packages/api-utils/lib/dom/events/keys.js
new file mode 100644
index 0000000..53107ae
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/dom/events/keys.js
@@ -0,0 +1,60 @@
+/* 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/. */
+
+"use strict";
+
+const { emit } = require("../events");
+const { getCodeForKey, toJSON } = require("../../keyboard/utils");
+const { has } = require("../../array");
+const { isString } = require("../../type");
+
+const INITIALIZER = "initKeyEvent";
+const CATEGORY = "KeyboardEvent";
+
+function Options(options) {
+ if (!isString(options))
+ return options;
+
+ var { key, modifiers } = toJSON(options);
+ return {
+ key: key,
+ control: has(modifiers, "control"),
+ alt: has(modifiers, "alt"),
+ shift: has(modifiers, "shift"),
+ meta: has(modifiers, "meta")
+ };
+}
+
+var keyEvent = exports.keyEvent = function keyEvent(element, type, options) {
+
+ emit(element, type, {
+ initializer: INITIALIZER,
+ category: CATEGORY,
+ settings: [
+ !("bubbles" in options) || options.bubbles !== false,
+ !("cancelable" in options) || options.cancelable !== false,
+ "window" in options && options.window ? options.window : null,
+ "control" in options && !!options.control,
+ "alt" in options && !!options.alt,
+ "shift" in options && !!options.shift,
+ "meta" in options && !!options.meta,
+ getCodeForKey(options.key) || 0,
+ options.key.length === 1 ? options.key.charCodeAt(0) : 0
+ ]
+ });
+}
+
+exports.keyDown = function keyDown(element, options) {
+ keyEvent(element, "keydown", Options(options));
+};
+
+exports.keyUp = function keyUp(element, options) {
+ keyEvent(element, "keyup", Options(options));
+};
+
+exports.keyPress = function keyPress(element, options) {
+ keyEvent(element, "keypress", Options(options));
+};
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/env!.js b/tools/addon-sdk-1.7/packages/api-utils/lib/env!.js
new file mode 100644
index 0000000..f7144ad
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/env!.js
@@ -0,0 +1,20 @@
+/* vim:set ts=2 sw=2 sts=2 expandtab */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { messageManager } = require("chrome");
+const { channel } = require("./channel");
+
+module.exports = function load(module) {
+ return {
+ require: function require(id) {
+ // Load required module on the chrome process.
+ channel(messageManager, messageManager, 'require!').sync({
+ requirer: module,
+ id: id
+ });
+ return channel(messageManager, messageManager, id);
+ }
+ };
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/environment.js b/tools/addon-sdk-1.7/packages/api-utils/lib/environment.js
new file mode 100644
index 0000000..fd95c67
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/environment.js
@@ -0,0 +1,54 @@
+/* vim:set ts=2 sw=2 sts=2 expandtab */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+const { Cc, Ci } = require('chrome');
+const { get, set, exists } = Cc['@mozilla.org/process/environment;1'].
+ getService(Ci.nsIEnvironment);
+
+exports.env = Proxy.create({
+ // XPCOM does not provides a way to enumerate environment variables, so we
+ // just don't support enumeration.
+ getPropertyNames: function() [],
+ getOwnPropertyNames: function() [],
+ enumerate: function() [],
+ keys: function() [],
+ // We do not support freezing, cause it would make it impossible to set new
+ // environment variables.
+ fix: function() undefined,
+ // We present all environment variables as own properties of this object,
+ // so we just delegate this call to `getOwnPropertyDescriptor`.
+ getPropertyDescriptor: function(name) this.getOwnPropertyDescriptor(name),
+ // If environment variable with this name is defined, we generate proprety
+ // descriptor for it, otherwise fall back to `undefined` so that for consumer
+ // this property does not exists.
+ getOwnPropertyDescriptor: function(name) {
+ return !exists(name) ? undefined : {
+ value: get(name),
+ enumerable: false, // Non-enumerable as we don't support enumeration.
+ configurable: true, // Configurable as it may be deleted.
+ writable: true // Writable as we do support set.
+ }
+ },
+
+ // New environment variables can be defined just by defining properties
+ // on this object.
+ defineProperty: function(name, { value }) set(name, value),
+ delete: function(name) set(name, null),
+
+ // We present all properties as own, there for we just delegate to `hasOwn`.
+ has: function(name) this.hasOwn(name),
+ // We do support checks for existence of an environment variable, via `in`
+ // operator on this object.
+ hasOwn: function(name) exists(name),
+
+ // On property get / set we do read / write appropriate environment variables,
+ // please note though, that variables with names of standard object properties
+ // intentionally (so that this behaves as normal object) can not be
+ // read / set.
+ get: function(proxy, name) Object.prototype[name] || get(name) || undefined,
+ set: function(proxy, name, value) Object.prototype[name] || set(name, value)
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/errors.js b/tools/addon-sdk-1.7/packages/api-utils/lib/errors.js
new file mode 100644
index 0000000..7029158
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/errors.js
@@ -0,0 +1,60 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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";
+
+function logToConsole(e) {
+ console.exception(e);
+}
+
+var catchAndLog = exports.catchAndLog = function(callback,
+ defaultResponse,
+ logException) {
+ if (!logException)
+ logException = logToConsole;
+
+ return function() {
+ try {
+ return callback.apply(this, arguments);
+ } catch (e) {
+ logException(e);
+ return defaultResponse;
+ }
+ };
+};
+
+exports.catchAndLogProps = function catchAndLogProps(object,
+ props,
+ defaultResponse,
+ logException) {
+ if (typeof(props) == "string")
+ props = [props];
+ props.forEach(
+ function(property) {
+ object[property] = catchAndLog(object[property],
+ defaultResponse,
+ logException);
+ });
+};
+
+/**
+ * Catch and return an exception while calling the callback. If the callback
+ * doesn't throw, return the return value of the callback in a way that makes it
+ * possible to distinguish between a return value and an exception.
+ *
+ * This function is useful when you need to pass the result of a call across
+ * a process boundary (across which exceptions don't propagate). It probably
+ * doesn't need to be factored out into this module, since it is only used by
+ * a single caller, but putting it here works around bug 625560.
+ */
+exports.catchAndReturn = function(callback) {
+ return function() {
+ try {
+ return { returnValue: callback.apply(this, arguments) };
+ }
+ catch (exception) {
+ return { exception: exception };
+ }
+ };
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/event/core.js b/tools/addon-sdk-1.7/packages/api-utils/lib/event/core.js
new file mode 100644
index 0000000..ed2a0d4
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/event/core.js
@@ -0,0 +1,147 @@
+/* -*- 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/. */
+
+"use strict";
+
+const UNCAUGHT_ERROR = 'An error event was emitted for which there was no listener.';
+const BAD_LISTENER = 'The event listener must be a function.';
+
+const { ns } = require('../namespace');
+
+const event = ns();
+
+// Utility function to access given event `target` object's event listeners for
+// the specific event `type`. If listeners for this type does not exists they
+// will be created.
+const observers = function observers(target, type) {
+ let listeners = event(target);
+ return type in listeners ? listeners[type] : listeners[type] = [];
+};
+
+/**
+ * Registers an event `listener` that is called every time events of
+ * specified `type` is emitted on the given event `target`.
+ * @param {Object} target
+ * Event target object.
+ * @param {String} type
+ * The type of event.
+ * @param {Function} listener
+ * The listener function that processes the event.
+ */
+function on(target, type, listener) {
+ if (typeof(listener) !== 'function')
+ throw new Error(BAD_LISTENER);
+
+ let listeners = observers(target, type);
+ if (!~listeners.indexOf(listener))
+ listeners.push(listener);
+}
+exports.on = on;
+
+/**
+ * Registers an event `listener` that is called only the next time an event
+ * of the specified `type` is emitted on the given event `target`.
+ * @param {Object} target
+ * Event target object.
+ * @param {String} type
+ * The type of the event.
+ * @param {Function} listener
+ * The listener function that processes the event.
+ */
+function once(target, type, listener) {
+ on(target, type, function observer() {
+ off(target, type, observer);
+ listener.apply(target, arguments);
+ });
+}
+exports.once = once;
+
+/**
+ * Execute each of the listeners in order with the supplied arguments.
+ * All the exceptions that are thrown by listeners during the emit
+ * are caught and can be handled by listeners of 'error' event. Thrown
+ * exceptions are passed as an argument to an 'error' event listener.
+ * If no 'error' listener is registered exception will be logged into an
+ * error console.
+ * @param {Object} target
+ * Event target object.
+ * @param {String} type
+ * The type of event.
+ * @params {Object|Number|String|Boolean} message
+ * First argument that will be passed to listeners.
+ * @params {Object|Number|String|Boolean} ...
+ * More arguments that will be passed to listeners.
+ */
+function emit(target, type, message /*, ...*/) {
+ for each (let item in emit.lazy.apply(emit.lazy, arguments))
+ item;
+}
+
+/**
+ * This is very experimental feature that you should not use unless absolutely
+ * need it. Also it may be removed at any point without any further notice.
+ *
+ * Creates lazy iterator of return values of listeners. You can think of it
+ * as lazy array of return values of listeners for the `emit` with the given
+ * arguments.
+ */
+emit.lazy = function lazy(target, type, message /*, ...*/) {
+ let args = Array.slice(arguments, 2)
+ let listeners = observers(target, type).slice()
+ while (listeners.length) {
+ try {
+ yield listeners.shift().apply(target, args);
+ }
+ catch (error) {
+ // If exception is not thrown by a error listener and error listener is
+ // registered emit `error` event. Otherwise dump exception to the console.
+ if (type !== 'error' && observers(target, 'error').length)
+ emit(target, 'error', error);
+ else
+ console.exception(error);
+ }
+ }
+}
+exports.emit = emit;
+
+/**
+ * Removes an event `listener` for the given event `type` on the given event
+ * `target`. If no `listener` is passed removes all listeners of the given
+ * `type`. If `type` is not passed removes all the listeners of the given
+ * event `target`.
+ * @param {Object} target
+ * The event target object.
+ * @param {String} type
+ * The type of event.
+ * @param {Function} listener
+ * The listener function that processes the event.
+ */
+function off(target, type, listener) {
+ let length = arguments.length;
+ if (length === 3) {
+ let listeners = observers(target, type);
+ let index = listeners.indexOf(listener);
+ if (~index)
+ listeners.splice(index, 1);
+ }
+ else if (length === 2) {
+ observers(target, type).splice(0);
+ }
+ else if (length === 1) {
+ let listeners = event(target);
+ Object.keys(listeners).forEach(function(type) delete listeners[type]);
+ }
+}
+exports.off = off;
+
+/**
+ * Returns a number of event listeners registered for the given event `type`
+ * on the given event `target`.
+ */
+function count(target, type) {
+ return observers(target, type).length;
+}
+exports.count = count;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/event/target.js b/tools/addon-sdk-1.7/packages/api-utils/lib/event/target.js
new file mode 100644
index 0000000..60b19d2
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/event/target.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: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+const { on, once, off } = require('./core');
+const { method } = require('../functional');
+const { Base } = require('../base');
+
+const EVENT_TYPE_PATTERN = /^on([A-Z]\w+$)/;
+
+/**
+ * `EventTarget` is an exemplar for creating an objects that can be used to
+ * add / remove event listeners on them. Events on these objects may be emitted
+ * via `emit` function exported by 'event/core' module.
+ */
+const EventTarget = Base.extend({
+ /**
+ * Method initializes `this` event source. It goes through properties of a
+ * given `options` and registers listeners for the ones that look like an
+ * event listeners.
+ */
+ initialize: function initialize(options) {
+ options = options || {};
+ // Go through each property and registers event listeners for those
+ // that have a name matching following pattern (`onEventType`).
+ Object.keys(options).forEach(function onEach(key) {
+ let match = EVENT_TYPE_PATTERN.exec(key);
+ let type = match && match[1].toLowerCase();
+ let listener = options[key];
+
+ if (type && typeof(listener) === 'function')
+ this.on(type, listener);
+ }, this);
+ },
+ /**
+ * Registers an event `listener` that is called every time events of
+ * specified `type` are emitted.
+ * @param {String} type
+ * The type of event.
+ * @param {Function} listener
+ * The listener function that processes the event.
+ * @example
+ * worker.on('message', function (data) {
+ * console.log('data received: ' + data)
+ * })
+ */
+ on: method(on),
+ /**
+ * Registers an event `listener` that is called once the next time an event
+ * of the specified `type` is emitted.
+ * @param {String} type
+ * The type of the event.
+ * @param {Function} listener
+ * The listener function that processes the event.
+ */
+ once: method(once),
+ /**
+ * Removes an event `listener` for the given event `type`.
+ * @param {String} type
+ * The type of event.
+ * @param {Function} listener
+ * The listener function that processes the event.
+ */
+ removeListener: function removeListener(type, listener) {
+ // Note: We can't just wrap `off` in `method` as we do it for other methods
+ // cause skipping a second or third argument will behave very differently
+ // than intended. This way we make sure all arguments are passed and only
+ // one listener is removed at most.
+ off(this, type, listener);
+ }
+});
+exports.EventTarget = EventTarget;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/events.js b/tools/addon-sdk-1.7/packages/api-utils/lib/events.js
new file mode 100644
index 0000000..531a7a3
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/events.js
@@ -0,0 +1,178 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 ERROR_TYPE = 'error',
+ UNCAUGHT_ERROR = 'An error event was dispatched for which there was'
+ + ' no listener.',
+ BAD_LISTENER = 'The event listener must be a function.';
+/**
+ * This object is used to create an `EventEmitter` that, useful for composing
+ * objects that emit events. It implements an interface like `EventTarget` from
+ * DOM Level 2, which is implemented by Node objects in implementations that
+ * support the DOM Event Model.
+ * @see http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget
+ * @see http://nodejs.org/api.html#EventEmitter
+ * @see http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/events/EventDispatcher.html
+ */
+const eventEmitter = {
+ /**
+ * Registers an event `listener` that is called every time events of
+ * specified `type` are emitted.
+ * @param {String} type
+ * The type of event.
+ * @param {Function} listener
+ * The listener function that processes the event.
+ * @example
+ * worker.on('message', function (data) {
+ * console.log('data received: ' + data)
+ * })
+ */
+ on: function on(type, listener) {
+ if ('function' !== typeof listener)
+ throw new Error(BAD_LISTENER);
+ let listeners = this._listeners(type);
+ if (0 > listeners.indexOf(listener))
+ listeners.push(listener);
+ // Use of `_public` is required by the legacy traits code that will go away
+ // once bug-637633 is fixed.
+ return this._public || this;
+ },
+
+ /**
+ * Registers an event `listener` that is called once the next time an event
+ * of the specified `type` is emitted.
+ * @param {String} type
+ * The type of the event.
+ * @param {Function} listener
+ * The listener function that processes the event.
+ */
+ once: function once(type, listener) {
+ this.on(type, function selfRemovableListener() {
+ this.removeListener(type, selfRemovableListener);
+ listener.apply(this, arguments);
+ });
+ },
+
+ /**
+ * Unregister `listener` for the specified event type.
+ * @param {String} type
+ * The type of event.
+ * @param {Function} listener
+ * The listener function that processes the event.
+ */
+ removeListener: function removeListener(type, listener) {
+ if ('function' !== typeof listener)
+ throw new Error(BAD_LISTENER);
+ let listeners = this._listeners(type),
+ index = listeners.indexOf(listener);
+ if (0 <= index)
+ listeners.splice(index, 1);
+ // Use of `_public` is required by the legacy traits code, that will go away
+ // once bug-637633 is fixed.
+ return this._public || this;
+ },
+
+ /**
+ * Hash of listeners on this EventEmitter.
+ */
+ _events: null,
+
+ /**
+ * Returns an array of listeners for the specified event `type`. This array
+ * can be manipulated, e.g. to remove listeners.
+ * @param {String} type
+ * The type of event.
+ */
+ _listeners: function listeners(type) {
+ let events = this._events || (this._events = {});
+ return events[type] || (events[type] = []);
+ },
+
+ /**
+ * Execute each of the listeners in order with the supplied arguments.
+ * Returns `true` if listener for this event was called, `false` if there are
+ * no listeners for this event `type`.
+ *
+ * All the exceptions that are thrown by listeners during the emit
+ * are caught and can be handled by listeners of 'error' event. Thrown
+ * exceptions are passed as an argument to an 'error' event listener.
+ * If no 'error' listener is registered exception will propagate to a
+ * caller of this method.
+ *
+ * **It's recommended to have a default 'error' listener in all the complete
+ * composition that in worst case may dump errors to the console.**
+ *
+ * @param {String} type
+ * The type of event.
+ * @params {Object|Number|String|Boolean}
+ * Arguments that will be passed to listeners.
+ * @returns {Boolean}
+ */
+ _emit: function _emit(type, event) {
+ let args = Array.slice(arguments);
+ // Use of `_public` is required by the legacy traits code that will go away
+ // once bug-637633 is fixed.
+ args.unshift(this._public || this);
+ return this._emitOnObject.apply(this, args);
+ },
+
+ /**
+ * A version of _emit that lets you specify the object on which listeners are
+ * called. This is a hack that is sometimes necessary when such an object
+ * (exports, for example) cannot be an EventEmitter for some reason, but other
+ * object(s) managing events for the object are EventEmitters. Once bug
+ * 577782 is fixed, this method shouldn't be necessary.
+ *
+ * @param {object} targetObj
+ * The object on which listeners will be called.
+ * @param {string} type
+ * The event name.
+ * @param {value} event
+ * The first argument to pass to listeners.
+ * @param {value} ...
+ * More arguments to pass to listeners.
+ * @returns {boolean}
+ */
+ _emitOnObject: function _emitOnObject(targetObj, type, event /* , ... */) {
+ let listeners = this._listeners(type).slice(0);
+ // If there is no 'error' event listener then throw.
+ if (type === ERROR_TYPE && !listeners.length)
+ console.exception(event);
+ if (!listeners.length)
+ return false;
+ let params = Array.slice(arguments, 2);
+ for each (let listener in listeners) {
+ try {
+ listener.apply(targetObj, params);
+ } catch(e) {
+ // Bug 726967: Ignore exceptions being throws while notifying the error
+ // in order to avoid infinite loops.
+ if (type !== ERROR_TYPE)
+ this._emit(ERROR_TYPE, e);
+ else
+ console.exception("Exception in error event listener " + e);
+ }
+ }
+ return true;
+ },
+
+ /**
+ * Removes all the event listeners for the specified event `type`.
+ * @param {String} type
+ * The type of event.
+ */
+ _removeAllListeners: function _removeAllListeners(type) {
+ if (typeof type == "undefined") {
+ this._events = null;
+ return this;
+ }
+
+ this._listeners(type).splice(0);
+ return this;
+ }
+};
+exports.EventEmitter = require("./traits").Trait.compose(eventEmitter);
+exports.EventEmitterTrait = require('./light-traits').Trait(eventEmitter);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/events/assembler.js b/tools/addon-sdk-1.7/packages/api-utils/lib/events/assembler.js
new file mode 100644
index 0000000..5d11be2
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/events/assembler.js
@@ -0,0 +1,53 @@
+/* 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/. */
+
+"use strict";
+
+const { Trait } = require("../light-traits");
+const { removeListener, on } = require("../dom/events");
+
+/**
+ * Trait may be used for building objects / composing traits that wish to handle
+ * multiple dom events from multiple event targets in one place. Event targets
+ * can be added / removed by calling `observe / ignore` methods. Composer should
+ * provide array of event types it wishes to handle as property
+ * `supportedEventsTypes` and function for handling all those events as
+ * `handleEvent` property.
+ */
+exports.DOMEventAssembler = Trait({
+ /**
+ * Function that is supposed to handle all the supported events (that are
+ * present in the `supportedEventsTypes`) from all the observed
+ * `eventTargets`.
+ * @param {Event} event
+ * Event being dispatched.
+ */
+ handleEvent: Trait.required,
+ /**
+ * Array of supported event names.
+ * @type {String[]}
+ */
+ supportedEventsTypes: Trait.required,
+ /**
+ * Adds `eventTarget` to the list of observed `eventTarget`s. Listeners for
+ * supported events will be registered on the given `eventTarget`.
+ * @param {EventTarget} eventTarget
+ */
+ observe: function observe(eventTarget) {
+ this.supportedEventsTypes.forEach(function(eventType) {
+ on(eventTarget, eventType, this);
+ }, this);
+ },
+ /**
+ * Removes `eventTarget` from the list of observed `eventTarget`s. Listeners
+ * for all supported events will be unregistered from the given `eventTarget`.
+ * @param {EventTarget} eventTarget
+ */
+ ignore: function ignore(eventTarget) {
+ this.supportedEventsTypes.forEach(function(eventType) {
+ removeListener(eventTarget, eventType, this);
+ }, this);
+ }
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/file.js b/tools/addon-sdk-1.7/packages/api-utils/lib/file.js
new file mode 100644
index 0000000..0b4eac9
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/file.js
@@ -0,0 +1,192 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {Cc,Ci,Cr} = require("chrome");
+const byteStreams = require("./byte-streams");
+const textStreams = require("./text-streams");
+
+// Flags passed when opening a file. See nsprpub/pr/include/prio.h.
+const OPEN_FLAGS = {
+ RDONLY: parseInt("0x01"),
+ WRONLY: parseInt("0x02"),
+ CREATE_FILE: parseInt("0x08"),
+ APPEND: parseInt("0x10"),
+ TRUNCATE: parseInt("0x20"),
+ EXCL: parseInt("0x80")
+};
+
+var dirsvc = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties);
+
+function MozFile(path) {
+ var file = Cc['@mozilla.org/file/local;1']
+ .createInstance(Ci.nsILocalFile);
+ file.initWithPath(path);
+ return file;
+}
+
+function ensureReadable(file) {
+ if (!file.isReadable())
+ throw new Error("path is not readable: " + file.path);
+}
+
+function ensureDir(file) {
+ ensureExists(file);
+ if (!file.isDirectory())
+ throw new Error("path is not a directory: " + file.path);
+}
+
+function ensureFile(file) {
+ ensureExists(file);
+ if (!file.isFile())
+ throw new Error("path is not a file: " + file.path);
+}
+
+function ensureExists(file) {
+ if (!file.exists())
+ throw friendlyError(Cr.NS_ERROR_FILE_NOT_FOUND, file.path);
+}
+
+function friendlyError(errOrResult, filename) {
+ var isResult = typeof(errOrResult) === "number";
+ var result = isResult ? errOrResult : errOrResult.result;
+ switch (result) {
+ case Cr.NS_ERROR_FILE_NOT_FOUND:
+ return new Error("path does not exist: " + filename);
+ }
+ return isResult ? new Error("XPCOM error code: " + errOrResult) : errOrResult;
+}
+
+exports.exists = function exists(filename) {
+ return MozFile(filename).exists();
+};
+
+exports.isFile = function isFile(filename) {
+ return MozFile(filename).isFile();
+};
+
+exports.read = function read(filename, mode) {
+ if (typeof(mode) !== "string")
+ mode = "";
+
+ // Ensure mode is read-only.
+ mode = /b/.test(mode) ? "b" : "";
+
+ var stream = exports.open(filename, mode);
+ try {
+ var str = stream.read();
+ }
+ finally {
+ stream.close();
+ }
+
+ return str;
+};
+
+exports.join = function join(base) {
+ if (arguments.length < 2)
+ throw new Error("need at least 2 args");
+ base = MozFile(base);
+ for (var i = 1; i < arguments.length; i++)
+ base.append(arguments[i]);
+ return base.path;
+};
+
+exports.dirname = function dirname(path) {
+ var parent = MozFile(path).parent;
+ return parent ? parent.path : "";
+};
+
+exports.basename = function basename(path) {
+ var leafName = MozFile(path).leafName;
+
+ // On Windows, leafName when the path is a volume letter and colon ("c:") is
+ // the path itself. But such a path has no basename, so we want the empty
+ // string.
+ return leafName == path ? "" : leafName;
+};
+
+exports.list = function list(path) {
+ var file = MozFile(path);
+ ensureDir(file);
+ ensureReadable(file);
+
+ var entries = file.directoryEntries;
+ var entryNames = [];
+ while(entries.hasMoreElements()) {
+ var entry = entries.getNext();
+ entry.QueryInterface(Ci.nsIFile);
+ entryNames.push(entry.leafName);
+ }
+ return entryNames;
+};
+
+exports.open = function open(filename, mode) {
+ var file = MozFile(filename);
+ if (typeof(mode) !== "string")
+ mode = "";
+
+ // File opened for write only.
+ if (/w/.test(mode)) {
+ if (file.exists())
+ ensureFile(file);
+ var stream = Cc['@mozilla.org/network/file-output-stream;1'].
+ createInstance(Ci.nsIFileOutputStream);
+ var openFlags = OPEN_FLAGS.WRONLY |
+ OPEN_FLAGS.CREATE_FILE |
+ OPEN_FLAGS.TRUNCATE;
+ var permFlags = parseInt("0644"); // u+rw go+r
+ try {
+ stream.init(file, openFlags, permFlags, 0);
+ }
+ catch (err) {
+ throw friendlyError(err, filename);
+ }
+ return /b/.test(mode) ?
+ new byteStreams.ByteWriter(stream) :
+ new textStreams.TextWriter(stream);
+ }
+
+ // File opened for read only, the default.
+ ensureFile(file);
+ stream = Cc['@mozilla.org/network/file-input-stream;1'].
+ createInstance(Ci.nsIFileInputStream);
+ try {
+ stream.init(file, OPEN_FLAGS.RDONLY, 0, 0);
+ }
+ catch (err) {
+ throw friendlyError(err, filename);
+ }
+ return /b/.test(mode) ?
+ new byteStreams.ByteReader(stream) :
+ new textStreams.TextReader(stream);
+};
+
+exports.remove = function remove(path) {
+ var file = MozFile(path);
+ ensureFile(file);
+ file.remove(false);
+};
+
+exports.mkpath = function mkpath(path) {
+ var file = MozFile(path);
+ if (!file.exists())
+ file.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755")); // u+rwx go+rx
+ else if (!file.isDirectory())
+ throw new Error("The path already exists and is not a directory: " + path);
+};
+
+exports.rmdir = function rmdir(path) {
+ var file = MozFile(path);
+ ensureDir(file);
+ try {
+ file.remove(false);
+ }
+ catch (err) {
+ // Bug 566950 explains why we're not catching a specific exception here.
+ throw new Error("The directory is not empty: " + path);
+ }
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/find-tests.js b/tools/addon-sdk-1.7/packages/api-utils/lib/find-tests.js
new file mode 100644
index 0000000..60550bf
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/find-tests.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/. */
+
+// this file left intentionally blank
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/frame/utils.js b/tools/addon-sdk-1.7/packages/api-utils/lib/frame/utils.js
new file mode 100644
index 0000000..5c9da38
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/frame/utils.js
@@ -0,0 +1,59 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+const XUL = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
+
+/**
+ * Creates a XUL `browser` element in a privileged document.
+ * @params {nsIDOMDocument} document
+ * @params {String} options.type
+ * By default is 'content' for possible values see:
+ * https://developer.mozilla.org/en/XUL/iframe#a-browser.type
+ * @params {String} options.uri
+ * URI of the document to be loaded into created frame.
+ * @params {Boolean} options.remote
+ * If `true` separate process will be used for this frame, also in such
+ * case all the following options are ignored.
+ * @params {Boolean} options.allowAuth
+ * Whether to allow auth dialogs. Defaults to `false`.
+ * @params {Boolean} options.allowJavascript
+ * Whether to allow Javascript execution. Defaults to `false`.
+ * @params {Boolean} options.allowPlugins
+ * Whether to allow plugin execution. Defaults to `false`.
+ */
+function create(document, options) {
+ options = options || {};
+ let remote = 'remote' in options && options.remote === true;
+
+ let frame = document.createElementNS(XUL, 'browser');
+ // Type="content" is mandatory to enable stuff here:
+ // http://mxr.mozilla.org/mozilla-central/source/content/base/src/nsFrameLoader.cpp#1776
+ frame.setAttribute('type', options.type || 'content');
+ frame.setAttribute('src', options.uri || 'about:blank');
+
+ // Load in separate process if `options.remote` is `true`.
+ // http://mxr.mozilla.org/mozilla-central/source/content/base/src/nsFrameLoader.cpp#1347
+ if (remote) {
+ // We remove XBL binding to avoid execution of code that is not going to
+ // work because browser has no docShell attribute in remote mode
+ // (for example)
+ frame.setAttribute('style', '-moz-binding: none;');
+ frame.setAttribute('remote', 'true');
+ }
+
+ document.documentElement.appendChild(frame);
+
+ // If browser is remote it won't have a `docShell`.
+ if (!remote) {
+ let docShell = frame.docShell;
+ docShell.allowAuth = options.allowAuth || false;
+ docShell.allowJavascript = options.allowJavascript || false;
+ docShell.allowPlugins = options.allowPlugins || false;
+ }
+
+ return frame;
+}
+exports.create = create;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/functional.js b/tools/addon-sdk-1.7/packages/api-utils/lib/functional.js
new file mode 100644
index 0000000..61738d9
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/functional.js
@@ -0,0 +1,159 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Disclaimer: Most of the functions in this module implement APIs from
+// Jeremy Ashkenas's http://underscorejs.org/ library and all credits for
+// those goes to him.
+
+"use strict";
+
+const { setTimeout } = require("./timer");
+
+/**
+ * Takes `lambda` function and returns a method. When returned method is
+ * invoked it calls wrapped `lambda` and passes `this` as a first argument
+ * and given argument as rest.
+ */
+function method(lambda) {
+ return function method() {
+ return lambda.apply(null, [this].concat(Array.slice(arguments)));
+ }
+}
+exports.method = method;
+
+/**
+ * Takes a function and returns a wrapped one instead, calling which will call
+ * original function in the next turn of event loop. This is basically utility
+ * to do `setTimeout(function() { ... }, 0)`, with a difference that returned
+ * function is reused, instead of creating a new one each time. This also allows
+ * to use this functions as event listeners.
+ */
+function defer(f) {
+ return function deferred()
+ setTimeout(invoke, 0, f, arguments, this);
+}
+exports.defer = defer;
+// Exporting `remit` alias as `defer` may conflict with promises.
+exports.remit = defer;
+
+/**
+ * Invokes `callee` by passing `params` as an arguments and `self` as `this`
+ * pseudo-variable. Returns value that is returned by a callee.
+ * @param {Function} callee
+ * Function to invoke.
+ * @param {Array} params
+ * Arguments to invoke function with.
+ * @param {Object} self
+ * Object to be passed as a `this` pseudo variable.
+ */
+function invoke(callee, params, self) callee.apply(self, params);
+exports.invoke = invoke;
+
+/**
+ * Curries a function with the arguments given.
+ *
+ * @param {Function} fn
+ * The function to curry
+ *
+ * @returns The function curried
+ */
+function curry(fn) {
+ if (typeof fn !== "function")
+ throw new TypeError(String(fn) + " is not a function");
+
+ let args = Array.slice(arguments, 1);
+
+ return function() fn.apply(this, args.concat(Array.slice(arguments)));
+}
+exports.curry = curry;
+
+/**
+ * Returns the composition of a list of functions, where each function consumes
+ * the return value of the function that follows. In math terms, composing the
+ * functions `f()`, `g()`, and `h()` produces `f(g(h()))`.
+ * @example
+ *
+ * var greet = function(name) { return "hi: " + name; };
+ * var exclaim = function(statement) { return statement + "!"; };
+ * var welcome = compose(exclaim, greet);
+ *
+ * welcome('moe'); // => 'hi: moe!'
+ */
+function compose() {
+ let lambdas = Array.slice(arguments);
+ return function composed() {
+ let args = Array.slice(arguments), index = lambdas.length;
+ while (0 <= --index)
+ args = [ lambdas[index].apply(this, args) ];
+ return args[0];
+ };
+}
+exports.compose = compose;
+
+/*
+ * Returns the first function passed as an argument to the second,
+ * allowing you to adjust arguments, run code before and after, and
+ * conditionally execute the original function.
+ * @example
+ *
+ * var hello = function(name) { return "hello: " + name; };
+ * hello = wrap(hello, function(f) {
+ * return "before, " + f("moe") + ", after";
+ * });
+ *
+ * hello(); // => 'before, hello: moe, after'
+ */
+function wrap(f, wrapper) {
+ return function wrapped()
+ wrapper.apply(this, [ f ].concat(Array.slice(arguments)))
+};
+exports.wrap = wrap;
+
+/**
+ * Returns the same value that is used as the argument. In math: f(x) = x
+ */
+function identity(value) value
+exports.identity = identity;
+
+/**
+ * Memoizes a given function by caching the computed result. Useful for
+ * speeding up slow-running computations. If passed an optional hashFunction,
+ * it will be used to compute the hash key for storing the result, based on
+ * the arguments to the original function. The default hashFunction just uses
+ * the first argument to the memoized function as the key.
+ */
+function memoize(f, hasher) {
+ let memo = Object.create(null);
+ hasher = hasher || identity;
+ return function memoizer() {
+ let key = hasher.apply(this, arguments);
+ return key in memo ? memo[key] : (memo[key] = f.apply(this, arguments));
+ };
+}
+exports.memoize = memoize;
+
+/**
+ * Much like setTimeout, invokes function after wait milliseconds. If you pass
+ * the optional arguments, they will be forwarded on to the function when it is
+ * invoked.
+ */
+function delay(f, ms) {
+ let args = Array.slice(arguments, 2);
+ setTimeout(function(context) { return f.apply(context, args); }, ms, this);
+};
+exports.delay = delay;
+
+/**
+ * Creates a version of the function that can only be called one time. Repeated
+ * calls to the modified function will have no effect, returning the value from
+ * the original call. Useful for initialization functions, instead of having to
+ * set a boolean flag and then check it later.
+ */
+function once(f) {
+ let ran = false, cache;
+ return function() ran ? cache : (ran = true, cache = f.apply(this, arguments))
+};
+exports.once = once;
+// export cache as once will may be conflicting with event once a lot.
+exports.cache = once;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/globals!.js b/tools/addon-sdk-1.7/packages/api-utils/lib/globals!.js
new file mode 100644
index 0000000..01e85b7
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/globals!.js
@@ -0,0 +1,93 @@
+/* vim:set ts=2 sw=2 sts=2 expandtab */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+let { Cc, Ci } = require('chrome');
+let { PlainTextConsole } = require('./plain-text-console');
+let options = require('@packaging');
+let consoleService = Cc['@mozilla.org/consoleservice;1'].getService().
+ QueryInterface(Ci.nsIConsoleService);
+
+// On windows dump does not writes into stdout so cfx can't read thous dumps.
+// To workaround this issue we write to a special file from which cfx will
+// read and print to the console.
+// For more details see: bug-673383
+exports.dump = (function define(global) {
+ const PR_WRONLY = 0x02;
+ const PR_CREATE_FILE = 0x08;
+ const PR_APPEND = 0x10;
+ let print = Object.getPrototypeOf(global).dump
+ if (print) return print;
+ if ('logFile' in options) {
+ let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+ file.initWithPath(options.logFile);
+ let stream = Cc["@mozilla.org/network/file-output-stream;1"].
+ createInstance(Ci.nsIFileOutputStream);
+ stream.init(file, PR_WRONLY|PR_CREATE_FILE|PR_APPEND, -1, 0);
+
+ return function print(message) {
+ message = String(message);
+ stream.write(message, message.length);
+ stream.flush();
+ };
+ }
+ return dump;
+})(this);
+
+// Override the default Iterator function with one that passes
+// a second argument to custom iterator methods that identifies
+// the call as originating from an Iterator function so the custom
+// iterator method can return [key, value] pairs just like default
+// iterators called via the default Iterator function.
+exports.Iterator = (function(DefaultIterator) {
+ return function Iterator(obj, keysOnly) {
+ if ("__iterator__" in obj && !keysOnly)
+ return obj.__iterator__.call(obj, false, true);
+ return DefaultIterator(obj, keysOnly);
+ };
+})(Iterator);
+
+// Bug 718230: We need to send console messages to stdout and JS Console
+function forsakenConsoleDump(msg, level) {
+ exports.dump(msg);
+
+ if (level === "error") {
+ let err = Cc["@mozilla.org/scripterror;1"].
+ createInstance(Ci.nsIScriptError);
+ msg = msg.replace(/^error: /, "");
+ err.init(msg, null, null, 0, 0, 0, "Add-on SDK");
+ consoleService.logMessage(err);
+ }
+ else
+ consoleService.logStringMessage(msg);
+};
+exports.console = new PlainTextConsole(forsakenConsoleDump);
+
+// Provide CommonJS `define` to allow authoring modules in a format that can be
+// loaded both into jetpack and into browser via AMD loaders.
+Object.defineProperty(exports, 'define', {
+ // `define` is provided as a lazy getter that binds below defined `define`
+ // function to the module scope, so that require, exports and module
+ // variables remain accessible.
+ configurable: true,
+ get: (function() {
+ function define(factory) {
+ factory = Array.slice(arguments).pop();
+ factory.call(this, this.require, this.exports, this.module);
+ }
+
+ return function getter() {
+ // Redefine `define` as a static property to make sure that module
+ // gets access to the same function so that `define === define` is
+ // `true`.
+ Object.defineProperty(this, 'define', {
+ configurable: false,
+ value: define.bind(this)
+ });
+ return this.define;
+ }
+ })()
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/hidden-frame.js b/tools/addon-sdk-1.7/packages/api-utils/lib/hidden-frame.js
new file mode 100644
index 0000000..6f0016b
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/hidden-frame.js
@@ -0,0 +1,166 @@
+/* -*- 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/. */
+
+"use strict";
+
+const {Cc, Ci} = require("chrome");
+const errors = require("./errors");
+const apiUtils = require("./api-utils");
+const timer = require("./timer");
+
+const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+let hostFrame, hostDocument, hiddenWindow, isHostFrameReady = false;
+
+if (!require("./xul-app").isOneOf(["Firefox", "Fennec", "Thunderbird"])) {
+ throw new Error([
+ "The hidden-frame 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(""));
+}
+
+let appShellService = Cc["@mozilla.org/appshell/appShellService;1"].
+ getService(Ci.nsIAppShellService);
+hiddenWindow = appShellService.hiddenDOMWindow;
+
+if (!hiddenWindow) {
+ throw new Error([
+ "The hidden-frame module needs an app that supports a hidden window. ",
+ "We would like it to support other applications, however. Please see ",
+ "https://bugzilla.mozilla.org/show_bug.cgi?id=546740 for more information."
+ ].join(""));
+}
+
+// Check if we can use the hidden window itself to host our iframes.
+// If it's not a suitable host, the hostFrame will be lazily created
+// by the first HiddenFrame instance.
+if (hiddenWindow.location.protocol == "chrome:" &&
+ (hiddenWindow.document.contentType == "application/vnd.mozilla.xul+xml" ||
+ hiddenWindow.document.contentType == "application/xhtml+xml")) {
+ hostFrame = hiddenWindow;
+ hostDocument = hiddenWindow.document;
+ isHostFrameReady = true;
+}
+
+function setHostFrameReady() {
+ hostDocument = hostFrame.contentDocument;
+ hostFrame.removeEventListener("DOMContentLoaded", setHostFrameReady, false);
+ isHostFrameReady = true;
+}
+
+// This cache is used to access friend properties between functions
+// without exposing them on the public API.
+let cache = [];
+
+exports.HiddenFrame = apiUtils.publicConstructor(HiddenFrame);
+
+function HiddenFrame(options) {
+ options = options || {};
+ let self = this;
+
+ for each (let [key, val] in Iterator(apiUtils.validateOptions(options, {
+ onReady: {
+ is: ["undefined", "function", "array"],
+ ok: function(v) {
+ if (apiUtils.getTypeOf(v) === "array") {
+ // make sure every item is a function
+ return v.every(function (item) typeof(item) === "function")
+ }
+ return true;
+ }
+ }
+ }))) {
+ if (typeof(val) != "undefined")
+ options[key] = val;
+ }
+
+ require("./collection").addCollectionProperty(this, "onReady");
+ if (options.onReady)
+ this.onReady.add(options.onReady);
+
+ if (!hostFrame) {
+ hostFrame = hiddenWindow.document.createElement("iframe");
+
+ // ugly ugly hack. This is the most lightweight chrome:// file I could find on the tree
+ // This hack should be removed by proper platform support on bug 565388
+ hostFrame.setAttribute("src", "chrome://global/content/mozilla.xhtml");
+ hostFrame.addEventListener("DOMContentLoaded", setHostFrameReady, false);
+
+ hiddenWindow.document.body.appendChild(hostFrame);
+ }
+
+ this.toString = function toString() "[object Frame]";
+}
+
+exports.add = function JP_SDK_Frame_add(frame) {
+ if (!(frame instanceof HiddenFrame))
+ throw new Error("The object to be added must be a HiddenFrame.");
+
+ // This instance was already added.
+ if (cache.filter(function (v) v.frame === frame)[0])
+ return frame;
+
+ function createElement() {
+ hostFrame.removeEventListener("DOMContentLoaded", createElement, false);
+
+ let element = hostDocument.createElementNS(XUL_NS, "iframe");
+
+ element.setAttribute("type", "content");
+ hostDocument.documentElement.appendChild(element);
+
+ /* Public API: hiddenFrame.element */
+ frame.__defineGetter__("element", function () element);
+
+ // Notify consumers that the frame is ready.
+ function onReadyListener(event) {
+ element.removeEventListener("DOMContentLoaded", onReadyListener, false);
+ if (event.target == element.contentDocument) {
+ for (let handler in frame.onReady)
+ errors.catchAndLog(function () handler.call(frame))();
+ }
+ }
+ element.addEventListener("DOMContentLoaded", onReadyListener, false);
+
+ cache.push({
+ frame: frame,
+ element: element,
+ unload: function unload() {
+ hostDocument.documentElement.removeChild(element);
+ }
+ });
+ }
+
+ /* Begin element construction or schedule it for later */
+ if (isHostFrameReady) {
+ createElement();
+ } else {
+ hostFrame.addEventListener("DOMContentLoaded", createElement, false);
+ }
+
+ return frame;
+}
+
+exports.remove = function remove(frame) {
+ if (!(frame instanceof HiddenFrame))
+ throw new Error("The object to be removed must be a HiddenFrame.");
+
+ let entry = cache.filter(function (v) v.frame === frame)[0];
+ if (!entry)
+ return;
+
+ entry.unload();
+ cache.splice(cache.indexOf(entry), 1);
+}
+
+require("./unload").when(function () {
+ for each (let entry in cache.slice())
+ exports.remove(entry.frame);
+
+ if (hostFrame && hostFrame !== hiddenWindow)
+ hiddenWindow.document.body.removeChild(hostFrame);
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/httpd.js b/tools/addon-sdk-1.7/packages/api-utils/lib/httpd.js
new file mode 100644
index 0000000..7f6a363
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/httpd.js
@@ -0,0 +1,5166 @@
+/* -*- Mode: JavaScript; 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/. */
+
+/*
+* An implementation of an HTTP server both as a loadable script and as an XPCOM
+* component. See the accompanying README file for user documentation on
+* httpd.js.
+*/
+
+
+var {components,Cc,Ci,Cr,Cu} = require("chrome");
+components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const CC = components.Constructor;
+
+const PR_UINT32_MAX = Math.pow(2, 32) - 1;
+
+/** True if debugging output is enabled, false otherwise. */
+var DEBUG = false; // non-const *only* so tweakable in server tests
+
+/** True if debugging output should be timestamped. */
+var DEBUG_TIMESTAMP = false; // non-const so tweakable in server tests
+
+var gGlobalObject = Cc["@mozilla.org/systemprincipal;1"].createInstance();
+
+/**
+* Asserts that the given condition holds. If it doesn't, the given message is
+* dumped, a stack trace is printed, and an exception is thrown to attempt to
+* stop execution (which unfortunately must rely upon the exception not being
+* accidentally swallowed by the code that uses it).
+*/
+function NS_ASSERT(cond, msg)
+{
+ if (DEBUG && !cond)
+ {
+ dumpn("###!!!");
+ dumpn("###!!! ASSERTION" + (msg ? ": " + msg : "!"));
+ dumpn("###!!! Stack follows:");
+
+ var stack = new Error().stack.split(/\n/);
+ dumpn(stack.map(function(val) { return "###!!! " + val; }).join("\n"));
+
+ throw Cr.NS_ERROR_ABORT;
+ }
+}
+
+/** Constructs an HTTP error object. */
+function HttpError(code, description)
+{
+ this.code = code;
+ this.description = description;
+}
+HttpError.prototype =
+{
+ toString: function()
+ {
+ return this.code + " " + this.description;
+ }
+};
+
+/**
+* Errors thrown to trigger specific HTTP server responses.
+*/
+const HTTP_400 = new HttpError(400, "Bad Request");
+const HTTP_401 = new HttpError(401, "Unauthorized");
+const HTTP_402 = new HttpError(402, "Payment Required");
+const HTTP_403 = new HttpError(403, "Forbidden");
+const HTTP_404 = new HttpError(404, "Not Found");
+const HTTP_405 = new HttpError(405, "Method Not Allowed");
+const HTTP_406 = new HttpError(406, "Not Acceptable");
+const HTTP_407 = new HttpError(407, "Proxy Authentication Required");
+const HTTP_408 = new HttpError(408, "Request Timeout");
+const HTTP_409 = new HttpError(409, "Conflict");
+const HTTP_410 = new HttpError(410, "Gone");
+const HTTP_411 = new HttpError(411, "Length Required");
+const HTTP_412 = new HttpError(412, "Precondition Failed");
+const HTTP_413 = new HttpError(413, "Request Entity Too Large");
+const HTTP_414 = new HttpError(414, "Request-URI Too Long");
+const HTTP_415 = new HttpError(415, "Unsupported Media Type");
+const HTTP_417 = new HttpError(417, "Expectation Failed");
+
+const HTTP_500 = new HttpError(500, "Internal Server Error");
+const HTTP_501 = new HttpError(501, "Not Implemented");
+const HTTP_502 = new HttpError(502, "Bad Gateway");
+const HTTP_503 = new HttpError(503, "Service Unavailable");
+const HTTP_504 = new HttpError(504, "Gateway Timeout");
+const HTTP_505 = new HttpError(505, "HTTP Version Not Supported");
+
+/** Creates a hash with fields corresponding to the values in arr. */
+function array2obj(arr)
+{
+ var obj = {};
+ for (var i = 0; i < arr.length; i++)
+ obj[arr[i]] = arr[i];
+ return obj;
+}
+
+/** Returns an array of the integers x through y, inclusive. */
+function range(x, y)
+{
+ var arr = [];
+ for (var i = x; i <= y; i++)
+ arr.push(i);
+ return arr;
+}
+
+/** An object (hash) whose fields are the numbers of all HTTP error codes. */
+const HTTP_ERROR_CODES = array2obj(range(400, 417).concat(range(500, 505)));
+
+
+/**
+* The character used to distinguish hidden files from non-hidden files, a la
+* the leading dot in Apache. Since that mechanism also hides files from
+* easy display in LXR, ls output, etc. however, we choose instead to use a
+* suffix character. If a requested file ends with it, we append another
+* when getting the file on the server. If it doesn't, we just look up that
+* file. Therefore, any file whose name ends with exactly one of the character
+* is "hidden" and available for use by the server.
+*/
+const HIDDEN_CHAR = "^";
+
+/**
+* The file name suffix indicating the file containing overridden headers for
+* a requested file.
+*/
+const HEADERS_SUFFIX = HIDDEN_CHAR + "headers" + HIDDEN_CHAR;
+
+/** Type used to denote SJS scripts for CGI-like functionality. */
+const SJS_TYPE = "sjs";
+
+/** Base for relative timestamps produced by dumpn(). */
+var firstStamp = 0;
+
+/** dump(str) with a trailing "\n" -- only outputs if DEBUG. */
+function dumpn(str)
+{
+ if (DEBUG)
+ {
+ var prefix = "HTTPD-INFO | ";
+ if (DEBUG_TIMESTAMP)
+ {
+ if (firstStamp === 0)
+ firstStamp = Date.now();
+
+ var elapsed = Date.now() - firstStamp; // milliseconds
+ var min = Math.floor(elapsed / 60000);
+ var sec = (elapsed % 60000) / 1000;
+
+ if (sec < 10)
+ prefix += min + ":0" + sec.toFixed(3) + " | ";
+ else
+ prefix += min + ":" + sec.toFixed(3) + " | ";
+ }
+
+ dump(prefix + str + "\n");
+ }
+}
+
+/** Dumps the current JS stack if DEBUG. */
+function dumpStack()
+{
+ // peel off the frames for dumpStack() and Error()
+ var stack = new Error().stack.split(/\n/).slice(2);
+ stack.forEach(dumpn);
+}
+
+
+/** The XPCOM thread manager. */
+var gThreadManager = null;
+
+/** The XPCOM prefs service. */
+var gRootPrefBranch = null;
+function getRootPrefBranch()
+{
+ if (!gRootPrefBranch)
+ {
+ gRootPrefBranch = Cc["@mozilla.org/preferences-service;1"]
+ .getService(Ci.nsIPrefBranch);
+ }
+ return gRootPrefBranch;
+}
+
+/**
+* JavaScript constructors for commonly-used classes; precreating these is a
+* speedup over doing the same from base principles. See the docs at
+* http://developer.mozilla.org/en/docs/components.Constructor for details.
+*/
+const ServerSocket = CC("@mozilla.org/network/server-socket;1",
+ "nsIServerSocket",
+ "init");
+const ScriptableInputStream = CC("@mozilla.org/scriptableinputstream;1",
+ "nsIScriptableInputStream",
+ "init");
+const Pipe = CC("@mozilla.org/pipe;1",
+ "nsIPipe",
+ "init");
+const FileInputStream = CC("@mozilla.org/network/file-input-stream;1",
+ "nsIFileInputStream",
+ "init");
+const ConverterInputStream = CC("@mozilla.org/intl/converter-input-stream;1",
+ "nsIConverterInputStream",
+ "init");
+const WritablePropertyBag = CC("@mozilla.org/hash-property-bag;1",
+ "nsIWritablePropertyBag2");
+const SupportsString = CC("@mozilla.org/supports-string;1",
+ "nsISupportsString");
+
+/* These two are non-const only so a test can overwrite them. */
+var BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream");
+var BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1",
+ "nsIBinaryOutputStream",
+ "setOutputStream");
+
+/**
+* Returns the RFC 822/1123 representation of a date.
+*
+* @param date : Number
+* the date, in milliseconds from midnight (00:00:00), January 1, 1970 GMT
+* @returns string
+* the representation of the given date
+*/
+function toDateString(date)
+{
+ //
+ // rfc1123-date = wkday "," SP date1 SP time SP "GMT"
+ // date1 = 2DIGIT SP month SP 4DIGIT
+ // ; day month year (e.g., 02 Jun 1982)
+ // time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
+ // ; 00:00:00 - 23:59:59
+ // wkday = "Mon" | "Tue" | "Wed"
+ // | "Thu" | "Fri" | "Sat" | "Sun"
+ // month = "Jan" | "Feb" | "Mar" | "Apr"
+ // | "May" | "Jun" | "Jul" | "Aug"
+ // | "Sep" | "Oct" | "Nov" | "Dec"
+ //
+
+ const wkdayStrings = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
+ const monthStrings = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
+
+ /**
+* Processes a date and returns the encoded UTC time as a string according to
+* the format specified in RFC 2616.
+*
+* @param date : Date
+* the date to process
+* @returns string
+* a string of the form "HH:MM:SS", ranging from "00:00:00" to "23:59:59"
+*/
+ function toTime(date)
+ {
+ var hrs = date.getUTCHours();
+ var rv = (hrs < 10) ? "0" + hrs : hrs;
+
+ var mins = date.getUTCMinutes();
+ rv += ":";
+ rv += (mins < 10) ? "0" + mins : mins;
+
+ var secs = date.getUTCSeconds();
+ rv += ":";
+ rv += (secs < 10) ? "0" + secs : secs;
+
+ return rv;
+ }
+
+ /**
+* Processes a date and returns the encoded UTC date as a string according to
+* the date1 format specified in RFC 2616.
+*
+* @param date : Date
+* the date to process
+* @returns string
+* a string of the form "HH:MM:SS", ranging from "00:00:00" to "23:59:59"
+*/
+ function toDate1(date)
+ {
+ var day = date.getUTCDate();
+ var month = date.getUTCMonth();
+ var year = date.getUTCFullYear();
+
+ var rv = (day < 10) ? "0" + day : day;
+ rv += " " + monthStrings[month];
+ rv += " " + year;
+
+ return rv;
+ }
+
+ date = new Date(date);
+
+ const fmtString = "%wkday%, %date1% %time% GMT";
+ var rv = fmtString.replace("%wkday%", wkdayStrings[date.getUTCDay()]);
+ rv = rv.replace("%time%", toTime(date));
+ return rv.replace("%date1%", toDate1(date));
+}
+
+/**
+* Prints out a human-readable representation of the object o and its fields,
+* omitting those whose names begin with "_" if showMembers != true (to ignore
+* "private" properties exposed via getters/setters).
+*/
+function printObj(o, showMembers)
+{
+ var s = "******************************\n";
+ s += "o = {\n";
+ for (var i in o)
+ {
+ if (typeof(i) != "string" ||
+ (showMembers || (i.length > 0 && i[0] != "_")))
+ s+= " " + i + ": " + o[i] + ",\n";
+ }
+ s += " };\n";
+ s += "******************************";
+ dumpn(s);
+}
+
+/**
+* Instantiates a new HTTP server.
+*/
+function nsHttpServer()
+{
+ if (!gThreadManager)
+ gThreadManager = Cc["@mozilla.org/thread-manager;1"].getService();
+
+ /** The port on which this server listens. */
+ this._port = undefined;
+
+ /** The socket associated with this. */
+ this._socket = null;
+
+ /** The handler used to process requests to this server. */
+ this._handler = new ServerHandler(this);
+
+ /** Naming information for this server. */
+ this._identity = new ServerIdentity();
+
+ /**
+* Indicates when the server is to be shut down at the end of the request.
+*/
+ this._doQuit = false;
+
+ /**
+* True if the socket in this is closed (and closure notifications have been
+* sent and processed if the socket was ever opened), false otherwise.
+*/
+ this._socketClosed = true;
+
+ /**
+* Used for tracking existing connections and ensuring that all connections
+* are properly cleaned up before server shutdown; increases by 1 for every
+* new incoming connection.
+*/
+ this._connectionGen = 0;
+
+ /**
+* Hash of all open connections, indexed by connection number at time of
+* creation.
+*/
+ this._connections = {};
+}
+nsHttpServer.prototype =
+{
+ classID: components.ID("{54ef6f81-30af-4b1d-ac55-8ba811293e41}"),
+
+ // NSISERVERSOCKETLISTENER
+
+ /**
+* Processes an incoming request coming in on the given socket and contained
+* in the given transport.
+*
+* @param socket : nsIServerSocket
+* the socket through which the request was served
+* @param trans : nsISocketTransport
+* the transport for the request/response
+* @see nsIServerSocketListener.onSocketAccepted
+*/
+ onSocketAccepted: function(socket, trans)
+ {
+ dumpn("*** onSocketAccepted(socket=" + socket + ", trans=" + trans + ")");
+
+ dumpn(">>> new connection on " + trans.host + ":" + trans.port);
+
+ const SEGMENT_SIZE = 8192;
+ const SEGMENT_COUNT = 1024;
+ try
+ {
+ var input = trans.openInputStream(0, SEGMENT_SIZE, SEGMENT_COUNT)
+ .QueryInterface(Ci.nsIAsyncInputStream);
+ var output = trans.openOutputStream(0, 0, 0);
+ }
+ catch (e)
+ {
+ dumpn("*** error opening transport streams: " + e);
+ trans.close(Cr.NS_BINDING_ABORTED);
+ return;
+ }
+
+ var connectionNumber = ++this._connectionGen;
+
+ try
+ {
+ var conn = new Connection(input, output, this, socket.port, trans.port,
+ connectionNumber);
+ var reader = new RequestReader(conn);
+
+ // XXX add request timeout functionality here!
+
+ // Note: must use main thread here, or we might get a GC that will cause
+ // threadsafety assertions. We really need to fix XPConnect so that
+ // you can actually do things in multi-threaded JS. :-(
+ input.asyncWait(reader, 0, 0, gThreadManager.mainThread);
+ }
+ catch (e)
+ {
+ // Assume this connection can't be salvaged and bail on it completely;
+ // don't attempt to close it so that we can assert that any connection
+ // being closed is in this._connections.
+ dumpn("*** error in initial request-processing stages: " + e);
+ trans.close(Cr.NS_BINDING_ABORTED);
+ return;
+ }
+
+ this._connections[connectionNumber] = conn;
+ dumpn("*** starting connection " + connectionNumber);
+ },
+
+ /**
+* Called when the socket associated with this is closed.
+*
+* @param socket : nsIServerSocket
+* the socket being closed
+* @param status : nsresult
+* the reason the socket stopped listening (NS_BINDING_ABORTED if the server
+* was stopped using nsIHttpServer.stop)
+* @see nsIServerSocketListener.onStopListening
+*/
+ onStopListening: function(socket, status)
+ {
+ dumpn(">>> shutting down server on port " + socket.port);
+ this._socketClosed = true;
+ if (!this._hasOpenConnections())
+ {
+ dumpn("*** no open connections, notifying async from onStopListening");
+
+ // Notify asynchronously so that any pending teardown in stop() has a
+ // chance to run first.
+ var self = this;
+ var stopEvent =
+ {
+ run: function()
+ {
+ dumpn("*** _notifyStopped async callback");
+ self._notifyStopped();
+ }
+ };
+ gThreadManager.currentThread
+ .dispatch(stopEvent, Ci.nsIThread.DISPATCH_NORMAL);
+ }
+ },
+
+ // NSIHTTPSERVER
+
+ //
+ // see nsIHttpServer.start
+ //
+ start: function(port)
+ {
+ this._start(port, "localhost")
+ },
+
+ _start: function(port, host)
+ {
+ if (this._socket)
+ throw Cr.NS_ERROR_ALREADY_INITIALIZED;
+
+ this._port = port;
+ this._doQuit = this._socketClosed = false;
+
+ this._host = host;
+
+ // The listen queue needs to be long enough to handle
+ // network.http.max-connections-per-server concurrent connections,
+ // plus a safety margin in case some other process is talking to
+ // the server as well.
+ var prefs = getRootPrefBranch();
+ var maxConnections =
+ prefs.getIntPref("network.http.max-connections-per-server") + 5;
+
+ try
+ {
+ var loopback = true;
+ if (this._host != "127.0.0.1" && this._host != "localhost") {
+ var loopback = false;
+ }
+
+ var socket = new ServerSocket(this._port,
+ loopback, // true = localhost, false = everybody
+ maxConnections);
+ dumpn(">>> listening on port " + socket.port + ", " + maxConnections +
+ " pending connections");
+ socket.asyncListen(this);
+ this._identity._initialize(port, host, true);
+ this._socket = socket;
+ }
+ catch (e)
+ {
+ dumpn("!!! could not start server on port " + port + ": " + e);
+ throw Cr.NS_ERROR_NOT_AVAILABLE;
+ }
+ },
+
+ //
+ // see nsIHttpServer.stop
+ //
+ stop: function(callback)
+ {
+ if (!callback)
+ throw Cr.NS_ERROR_NULL_POINTER;
+ if (!this._socket)
+ throw Cr.NS_ERROR_UNEXPECTED;
+
+ this._stopCallback = typeof callback === "function"
+ ? callback
+ : function() { callback.onStopped(); };
+
+ dumpn(">>> stopping listening on port " + this._socket.port);
+ this._socket.close();
+ this._socket = null;
+
+ // We can't have this identity any more, and the port on which we're running
+ // this server now could be meaningless the next time around.
+ this._identity._teardown();
+
+ this._doQuit = false;
+
+ // socket-close notification and pending request completion happen async
+ },
+
+ //
+ // see nsIHttpServer.registerFile
+ //
+ registerFile: function(path, file)
+ {
+ if (file && (!file.exists() || file.isDirectory()))
+ throw Cr.NS_ERROR_INVALID_ARG;
+
+ this._handler.registerFile(path, file);
+ },
+
+ //
+ // see nsIHttpServer.registerDirectory
+ //
+ registerDirectory: function(path, directory)
+ {
+ // XXX true path validation!
+ if (path.charAt(0) != "/" ||
+ path.charAt(path.length - 1) != "/" ||
+ (directory &&
+ (!directory.exists() || !directory.isDirectory())))
+ throw Cr.NS_ERROR_INVALID_ARG;
+
+ // XXX determine behavior of nonexistent /foo/bar when a /foo/bar/ mapping
+ // exists!
+
+ this._handler.registerDirectory(path, directory);
+ },
+
+ //
+ // see nsIHttpServer.registerPathHandler
+ //
+ registerPathHandler: function(path, handler)
+ {
+ this._handler.registerPathHandler(path, handler);
+ },
+
+ //
+ // see nsIHttpServer.registerErrorHandler
+ //
+ registerErrorHandler: function(code, handler)
+ {
+ this._handler.registerErrorHandler(code, handler);
+ },
+
+ //
+ // see nsIHttpServer.setIndexHandler
+ //
+ setIndexHandler: function(handler)
+ {
+ this._handler.setIndexHandler(handler);
+ },
+
+ //
+ // see nsIHttpServer.registerContentType
+ //
+ registerContentType: function(ext, type)
+ {
+ this._handler.registerContentType(ext, type);
+ },
+
+ //
+ // see nsIHttpServer.serverIdentity
+ //
+ get identity()
+ {
+ return this._identity;
+ },
+
+ //
+ // see nsIHttpServer.getState
+ //
+ getState: function(path, k)
+ {
+ return this._handler._getState(path, k);
+ },
+
+ //
+ // see nsIHttpServer.setState
+ //
+ setState: function(path, k, v)
+ {
+ return this._handler._setState(path, k, v);
+ },
+
+ //
+ // see nsIHttpServer.getSharedState
+ //
+ getSharedState: function(k)
+ {
+ return this._handler._getSharedState(k);
+ },
+
+ //
+ // see nsIHttpServer.setSharedState
+ //
+ setSharedState: function(k, v)
+ {
+ return this._handler._setSharedState(k, v);
+ },
+
+ //
+ // see nsIHttpServer.getObjectState
+ //
+ getObjectState: function(k)
+ {
+ return this._handler._getObjectState(k);
+ },
+
+ //
+ // see nsIHttpServer.setObjectState
+ //
+ setObjectState: function(k, v)
+ {
+ return this._handler._setObjectState(k, v);
+ },
+
+
+ // NSISUPPORTS
+
+ //
+ // see nsISupports.QueryInterface
+ //
+ QueryInterface: function(iid)
+ {
+ if (iid.equals(Ci.nsIServerSocketListener) || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+
+ // NON-XPCOM PUBLIC API
+
+ /**
+* Returns true iff this server is not running (and is not in the process of
+* serving any requests still to be processed when the server was last
+* stopped after being run).
+*/
+ isStopped: function()
+ {
+ return this._socketClosed && !this._hasOpenConnections();
+ },
+
+ // PRIVATE IMPLEMENTATION
+
+ /** True if this server has any open connections to it, false otherwise. */
+ _hasOpenConnections: function()
+ {
+ //
+ // If we have any open connections, they're tracked as numeric properties on
+ // |this._connections|. The non-standard __count__ property could be used
+ // to check whether there are any properties, but standard-wise, even
+ // looking forward to ES5, there's no less ugly yet still O(1) way to do
+ // this.
+ //
+ for (var n in this._connections)
+ return true;
+ return false;
+ },
+
+ /** Calls the server-stopped callback provided when stop() was called. */
+ _notifyStopped: function()
+ {
+ NS_ASSERT(this._stopCallback !== null, "double-notifying?");
+ NS_ASSERT(!this._hasOpenConnections(), "should be done serving by now");
+
+ //
+ // NB: We have to grab this now, null out the member, *then* call the
+ // callback here, or otherwise the callback could (indirectly) futz with
+ // this._stopCallback by starting and immediately stopping this, at
+ // which point we'd be nulling out a field we no longer have a right to
+ // modify.
+ //
+ var callback = this._stopCallback;
+ this._stopCallback = null;
+ try
+ {
+ callback();
+ }
+ catch (e)
+ {
+ // not throwing because this is specified as being usually (but not
+ // always) asynchronous
+ dump("!!! error running onStopped callback: " + e + "\n");
+ }
+ },
+
+ /**
+* Notifies this server that the given connection has been closed.
+*
+* @param connection : Connection
+* the connection that was closed
+*/
+ _connectionClosed: function(connection)
+ {
+ NS_ASSERT(connection.number in this._connections,
+ "closing a connection " + this + " that we never added to the " +
+ "set of open connections?");
+ NS_ASSERT(this._connections[connection.number] === connection,
+ "connection number mismatch? " +
+ this._connections[connection.number]);
+ delete this._connections[connection.number];
+
+ // Fire a pending server-stopped notification if it's our responsibility.
+ if (!this._hasOpenConnections() && this._socketClosed)
+ this._notifyStopped();
+ },
+
+ /**
+* Requests that the server be shut down when possible.
+*/
+ _requestQuit: function()
+ {
+ dumpn(">>> requesting a quit");
+ dumpStack();
+ this._doQuit = true;
+ }
+};
+
+
+//
+// RFC 2396 section 3.2.2:
+//
+// host = hostname | IPv4address
+// hostname = *( domainlabel "." ) toplabel [ "." ]
+// domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
+// toplabel = alpha | alpha *( alphanum | "-" ) alphanum
+// IPv4address = 1*digit "." 1*digit "." 1*digit "." 1*digit
+//
+
+const HOST_REGEX =
+ new RegExp("^(?:" +
+ // *( domainlabel "." )
+ "(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)*" +
+ // toplabel
+ "[a-z](?:[a-z0-9-]*[a-z0-9])?" +
+ "|" +
+ // IPv4 address
+ "\\d+\\.\\d+\\.\\d+\\.\\d+" +
+ ")$",
+ "i");
+
+
+/**
+* Represents the identity of a server. An identity consists of a set of
+* (scheme, host, port) tuples denoted as locations (allowing a single server to
+* serve multiple sites or to be used behind both HTTP and HTTPS proxies for any
+* host/port). Any incoming request must be to one of these locations, or it
+* will be rejected with an HTTP 400 error. One location, denoted as the
+* primary location, is the location assigned in contexts where a location
+* cannot otherwise be endogenously derived, such as for HTTP/1.0 requests.
+*
+* A single identity may contain at most one location per unique host/port pair;
+* other than that, no restrictions are placed upon what locations may
+* constitute an identity.
+*/
+function ServerIdentity()
+{
+ /** The scheme of the primary location. */
+ this._primaryScheme = "http";
+
+ /** The hostname of the primary location. */
+ this._primaryHost = "127.0.0.1"
+
+ /** The port number of the primary location. */
+ this._primaryPort = -1;
+
+ /**
+* The current port number for the corresponding server, stored so that a new
+* primary location can always be set if the current one is removed.
+*/
+ this._defaultPort = -1;
+
+ /**
+* Maps hosts to maps of ports to schemes, e.g. the following would represent
+* https://example.com:789/ and http://example.org/:
+*
+* {
+* "xexample.com": { 789: "https" },
+* "xexample.org": { 80: "http" }
+* }
+*
+* Note the "x" prefix on hostnames, which prevents collisions with special
+* JS names like "prototype".
+*/
+ this._locations = { "xlocalhost": {} };
+}
+ServerIdentity.prototype =
+{
+ // NSIHTTPSERVERIDENTITY
+
+ //
+ // see nsIHttpServerIdentity.primaryScheme
+ //
+ get primaryScheme()
+ {
+ if (this._primaryPort === -1)
+ throw Cr.NS_ERROR_NOT_INITIALIZED;
+ return this._primaryScheme;
+ },
+
+ //
+ // see nsIHttpServerIdentity.primaryHost
+ //
+ get primaryHost()
+ {
+ if (this._primaryPort === -1)
+ throw Cr.NS_ERROR_NOT_INITIALIZED;
+ return this._primaryHost;
+ },
+
+ //
+ // see nsIHttpServerIdentity.primaryPort
+ //
+ get primaryPort()
+ {
+ if (this._primaryPort === -1)
+ throw Cr.NS_ERROR_NOT_INITIALIZED;
+ return this._primaryPort;
+ },
+
+ //
+ // see nsIHttpServerIdentity.add
+ //
+ add: function(scheme, host, port)
+ {
+ this._validate(scheme, host, port);
+
+ var entry = this._locations["x" + host];
+ if (!entry)
+ this._locations["x" + host] = entry = {};
+
+ entry[port] = scheme;
+ },
+
+ //
+ // see nsIHttpServerIdentity.remove
+ //
+ remove: function(scheme, host, port)
+ {
+ this._validate(scheme, host, port);
+
+ var entry = this._locations["x" + host];
+ if (!entry)
+ return false;
+
+ var present = port in entry;
+ delete entry[port];
+
+ if (this._primaryScheme == scheme &&
+ this._primaryHost == host &&
+ this._primaryPort == port &&
+ this._defaultPort !== -1)
+ {
+ // Always keep at least one identity in existence at any time, unless
+ // we're in the process of shutting down (the last condition above).
+ this._primaryPort = -1;
+ this._initialize(this._defaultPort, host, false);
+ }
+
+ return present;
+ },
+
+ //
+ // see nsIHttpServerIdentity.has
+ //
+ has: function(scheme, host, port)
+ {
+ this._validate(scheme, host, port);
+
+ return "x" + host in this._locations &&
+ scheme === this._locations["x" + host][port];
+ },
+
+ //
+ // see nsIHttpServerIdentity.has
+ //
+ getScheme: function(host, port)
+ {
+ this._validate("http", host, port);
+
+ var entry = this._locations["x" + host];
+ if (!entry)
+ return "";
+
+ return entry[port] || "";
+ },
+
+ //
+ // see nsIHttpServerIdentity.setPrimary
+ //
+ setPrimary: function(scheme, host, port)
+ {
+ this._validate(scheme, host, port);
+
+ this.add(scheme, host, port);
+
+ this._primaryScheme = scheme;
+ this._primaryHost = host;
+ this._primaryPort = port;
+ },
+
+
+ // NSISUPPORTS
+
+ //
+ // see nsISupports.QueryInterface
+ //
+ QueryInterface: function(iid)
+ {
+ if (iid.equals(Ci.nsIHttpServerIdentity) || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+
+ // PRIVATE IMPLEMENTATION
+
+ /**
+* Initializes the primary name for the corresponding server, based on the
+* provided port number.
+*/
+ _initialize: function(port, host, addSecondaryDefault)
+ {
+ this._host = host;
+ if (this._primaryPort !== -1)
+ this.add("http", host, port);
+ else
+ this.setPrimary("http", "localhost", port);
+ this._defaultPort = port;
+
+ // Only add this if we're being called at server startup
+ if (addSecondaryDefault && host != "127.0.0.1")
+ this.add("http", "127.0.0.1", port);
+ },
+
+ /**
+* Called at server shutdown time, unsets the primary location only if it was
+* the default-assigned location and removes the default location from the
+* set of locations used.
+*/
+ _teardown: function()
+ {
+ if (this._host != "127.0.0.1") {
+ // Not the default primary location, nothing special to do here
+ this.remove("http", "127.0.0.1", this._defaultPort);
+ }
+
+ // This is a *very* tricky bit of reasoning here; make absolutely sure the
+ // tests for this code pass before you commit changes to it.
+ if (this._primaryScheme == "http" &&
+ this._primaryHost == this._host &&
+ this._primaryPort == this._defaultPort)
+ {
+ // Make sure we don't trigger the readding logic in .remove(), then remove
+ // the default location.
+ var port = this._defaultPort;
+ this._defaultPort = -1;
+ this.remove("http", this._host, port);
+
+ // Ensure a server start triggers the setPrimary() path in ._initialize()
+ this._primaryPort = -1;
+ }
+ else
+ {
+ // No reason not to remove directly as it's not our primary location
+ this.remove("http", this._host, this._defaultPort);
+ }
+ },
+
+ /**
+* Ensures scheme, host, and port are all valid with respect to RFC 2396.
+*
+* @throws NS_ERROR_ILLEGAL_VALUE
+* if any argument doesn't match the corresponding production
+*/
+ _validate: function(scheme, host, port)
+ {
+ if (scheme !== "http" && scheme !== "https")
+ {
+ dumpn("*** server only supports http/https schemes: '" + scheme + "'");
+ dumpStack();
+ throw Cr.NS_ERROR_ILLEGAL_VALUE;
+ }
+ if (!HOST_REGEX.test(host))
+ {
+ dumpn("*** unexpected host: '" + host + "'");
+ throw Cr.NS_ERROR_ILLEGAL_VALUE;
+ }
+ if (port < 0 || port > 65535)
+ {
+ dumpn("*** unexpected port: '" + port + "'");
+ throw Cr.NS_ERROR_ILLEGAL_VALUE;
+ }
+ }
+};
+
+
+/**
+* Represents a connection to the server (and possibly in the future the thread
+* on which the connection is processed).
+*
+* @param input : nsIInputStream
+* stream from which incoming data on the connection is read
+* @param output : nsIOutputStream
+* stream to write data out the connection
+* @param server : nsHttpServer
+* the server handling the connection
+* @param port : int
+* the port on which the server is running
+* @param outgoingPort : int
+* the outgoing port used by this connection
+* @param number : uint
+* a serial number used to uniquely identify this connection
+*/
+function Connection(input, output, server, port, outgoingPort, number)
+{
+ dumpn("*** opening new connection " + number + " on port " + outgoingPort);
+
+ /** Stream of incoming data. */
+ this.input = input;
+
+ /** Stream for outgoing data. */
+ this.output = output;
+
+ /** The server associated with this request. */
+ this.server = server;
+
+ /** The port on which the server is running. */
+ this.port = port;
+
+ /** The outgoing poort used by this connection. */
+ this._outgoingPort = outgoingPort;
+
+ /** The serial number of this connection. */
+ this.number = number;
+
+ /**
+* The request for which a response is being generated, null if the
+* incoming request has not been fully received or if it had errors.
+*/
+ this.request = null;
+
+ /** State variables for debugging. */
+ this._closed = this._processed = false;
+}
+Connection.prototype =
+{
+ /** Closes this connection's input/output streams. */
+ close: function()
+ {
+ dumpn("*** closing connection " + this.number +
+ " on port " + this._outgoingPort);
+
+ this.input.close();
+ this.output.close();
+ this._closed = true;
+
+ var server = this.server;
+ server._connectionClosed(this);
+
+ // If an error triggered a server shutdown, act on it now
+ if (server._doQuit)
+ server.stop(function() { /* not like we can do anything better */ });
+ },
+
+ /**
+* Initiates processing of this connection, using the data in the given
+* request.
+*
+* @param request : Request
+* the request which should be processed
+*/
+ process: function(request)
+ {
+ NS_ASSERT(!this._closed && !this._processed);
+
+ this._processed = true;
+
+ this.request = request;
+ this.server._handler.handleResponse(this);
+ },
+
+ /**
+* Initiates processing of this connection, generating a response with the
+* given HTTP error code.
+*
+* @param code : uint
+* an HTTP code, so in the range [0, 1000)
+* @param request : Request
+* incomplete data about the incoming request (since there were errors
+* during its processing
+*/
+ processError: function(code, request)
+ {
+ NS_ASSERT(!this._closed && !this._processed);
+
+ this._processed = true;
+ this.request = request;
+ this.server._handler.handleError(code, this);
+ },
+
+ /** Converts this to a string for debugging purposes. */
+ toString: function()
+ {
+ return "<Connection(" + this.number +
+ (this.request ? ", " + this.request.path : "") +"): " +
+ (this._closed ? "closed" : "open") + ">";
+ }
+};
+
+
+
+/** Returns an array of count bytes from the given input stream. */
+function readBytes(inputStream, count)
+{
+ return new BinaryInputStream(inputStream).readByteArray(count);
+}
+
+
+
+/** Request reader processing states; see RequestReader for details. */
+const READER_IN_REQUEST_LINE = 0;
+const READER_IN_HEADERS = 1;
+const READER_IN_BODY = 2;
+const READER_FINISHED = 3;
+
+
+/**
+* Reads incoming request data asynchronously, does any necessary preprocessing,
+* and forwards it to the request handler. Processing occurs in three states:
+*
+* READER_IN_REQUEST_LINE Reading the request's status line
+* READER_IN_HEADERS Reading headers in the request
+* READER_IN_BODY Reading the body of the request
+* READER_FINISHED Entire request has been read and processed
+*
+* During the first two stages, initial metadata about the request is gathered
+* into a Request object. Once the status line and headers have been processed,
+* we start processing the body of the request into the Request. Finally, when
+* the entire body has been read, we create a Response and hand it off to the
+* ServerHandler to be given to the appropriate request handler.
+*
+* @param connection : Connection
+* the connection for the request being read
+*/
+function RequestReader(connection)
+{
+ /** Connection metadata for this request. */
+ this._connection = connection;
+
+ /**
+* A container providing line-by-line access to the raw bytes that make up the
+* data which has been read from the connection but has not yet been acted
+* upon (by passing it to the request handler or by extracting request
+* metadata from it).
+*/
+ this._data = new LineData();
+
+ /**
+* The amount of data remaining to be read from the body of this request.
+* After all headers in the request have been read this is the value in the
+* Content-Length header, but as the body is read its value decreases to zero.
+*/
+ this._contentLength = 0;
+
+ /** The current state of parsing the incoming request. */
+ this._state = READER_IN_REQUEST_LINE;
+
+ /** Metadata constructed from the incoming request for the request handler. */
+ this._metadata = new Request(connection.port);
+
+ /**
+* Used to preserve state if we run out of line data midway through a
+* multi-line header. _lastHeaderName stores the name of the header, while
+* _lastHeaderValue stores the value we've seen so far for the header.
+*
+* These fields are always either both undefined or both strings.
+*/
+ this._lastHeaderName = this._lastHeaderValue = undefined;
+}
+RequestReader.prototype =
+{
+ // NSIINPUTSTREAMCALLBACK
+
+ /**
+* Called when more data from the incoming request is available. This method
+* then reads the available data from input and deals with that data as
+* necessary, depending upon the syntax of already-downloaded data.
+*
+* @param input : nsIAsyncInputStream
+* the stream of incoming data from the connection
+*/
+ onInputStreamReady: function(input)
+ {
+ dumpn("*** onInputStreamReady(input=" + input + ") on thread " +
+ gThreadManager.currentThread + " (main is " +
+ gThreadManager.mainThread + ")");
+ dumpn("*** this._state == " + this._state);
+
+ // Handle cases where we get more data after a request error has been
+ // discovered but *before* we can close the connection.
+ var data = this._data;
+ if (!data)
+ return;
+
+ try
+ {
+ data.appendBytes(readBytes(input, input.available()));
+ }
+ catch (e)
+ {
+ if (streamClosed(e))
+ {
+ dumpn("*** WARNING: unexpected error when reading from socket; will " +
+ "be treated as if the input stream had been closed");
+ dumpn("*** WARNING: actual error was: " + e);
+ }
+
+ // We've lost a race -- input has been closed, but we're still expecting
+ // to read more data. available() will throw in this case, and since
+ // we're dead in the water now, destroy the connection.
+ dumpn("*** onInputStreamReady called on a closed input, destroying " +
+ "connection");
+ this._connection.close();
+ return;
+ }
+
+ switch (this._state)
+ {
+ default:
+ NS_ASSERT(false, "invalid state: " + this._state);
+ break;
+
+ case READER_IN_REQUEST_LINE:
+ if (!this._processRequestLine())
+ break;
+ /* fall through */
+
+ case READER_IN_HEADERS:
+ if (!this._processHeaders())
+ break;
+ /* fall through */
+
+ case READER_IN_BODY:
+ this._processBody();
+ }
+
+ if (this._state != READER_FINISHED)
+ input.asyncWait(this, 0, 0, gThreadManager.currentThread);
+ },
+
+ //
+ // see nsISupports.QueryInterface
+ //
+ QueryInterface: function(aIID)
+ {
+ if (aIID.equals(Ci.nsIInputStreamCallback) ||
+ aIID.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+
+ // PRIVATE API
+
+ /**
+* Processes unprocessed, downloaded data as a request line.
+*
+* @returns boolean
+* true iff the request line has been fully processed
+*/
+ _processRequestLine: function()
+ {
+ NS_ASSERT(this._state == READER_IN_REQUEST_LINE);
+
+ // Servers SHOULD ignore any empty line(s) received where a Request-Line
+ // is expected (section 4.1).
+ var data = this._data;
+ var line = {};
+ var readSuccess;
+ while ((readSuccess = data.readLine(line)) && line.value == "")
+ dumpn("*** ignoring beginning blank line...");
+
+ // if we don't have a full line, wait until we do
+ if (!readSuccess)
+ return false;
+
+ // we have the first non-blank line
+ try
+ {
+ this._parseRequestLine(line.value);
+ this._state = READER_IN_HEADERS;
+ return true;
+ }
+ catch (e)
+ {
+ this._handleError(e);
+ return false;
+ }
+ },
+
+ /**
+* Processes stored data, assuming it is either at the beginning or in
+* the middle of processing request headers.
+*
+* @returns boolean
+* true iff header data in the request has been fully processed
+*/
+ _processHeaders: function()
+ {
+ NS_ASSERT(this._state == READER_IN_HEADERS);
+
+ // XXX things to fix here:
+ //
+ // - need to support RFC 2047-encoded non-US-ASCII characters
+
+ try
+ {
+ var done = this._parseHeaders();
+ if (done)
+ {
+ var request = this._metadata;
+
+ // XXX this is wrong for requests with transfer-encodings applied to
+ // them, particularly chunked (which by its nature can have no
+ // meaningful Content-Length header)!
+ this._contentLength = request.hasHeader("Content-Length")
+ ? parseInt(request.getHeader("Content-Length"), 10)
+ : 0;
+ dumpn("_processHeaders, Content-length=" + this._contentLength);
+
+ this._state = READER_IN_BODY;
+ }
+ return done;
+ }
+ catch (e)
+ {
+ this._handleError(e);
+ return false;
+ }
+ },
+
+ /**
+* Processes stored data, assuming it is either at the beginning or in
+* the middle of processing the request body.
+*
+* @returns boolean
+* true iff the request body has been fully processed
+*/
+ _processBody: function()
+ {
+ NS_ASSERT(this._state == READER_IN_BODY);
+
+ // XXX handle chunked transfer-coding request bodies!
+
+ try
+ {
+ if (this._contentLength > 0)
+ {
+ var data = this._data.purge();
+ var count = Math.min(data.length, this._contentLength);
+ dumpn("*** loading data=" + data + " len=" + data.length +
+ " excess=" + (data.length - count));
+
+ var bos = new BinaryOutputStream(this._metadata._bodyOutputStream);
+ bos.writeByteArray(data, count);
+ this._contentLength -= count;
+ }
+
+ dumpn("*** remaining body data len=" + this._contentLength);
+ if (this._contentLength == 0)
+ {
+ this._validateRequest();
+ this._state = READER_FINISHED;
+ this._handleResponse();
+ return true;
+ }
+
+ return false;
+ }
+ catch (e)
+ {
+ this._handleError(e);
+ return false;
+ }
+ },
+
+ /**
+* Does various post-header checks on the data in this request.
+*
+* @throws : HttpError
+* if the request was malformed in some way
+*/
+ _validateRequest: function()
+ {
+ NS_ASSERT(this._state == READER_IN_BODY);
+
+ dumpn("*** _validateRequest");
+
+ var metadata = this._metadata;
+ var headers = metadata._headers;
+
+ // 19.6.1.1 -- servers MUST report 400 to HTTP/1.1 requests w/o Host header
+ var identity = this._connection.server.identity;
+ if (metadata._httpVersion.atLeast(nsHttpVersion.HTTP_1_1))
+ {
+ if (!headers.hasHeader("Host"))
+ {
+ dumpn("*** malformed HTTP/1.1 or greater request with no Host header!");
+ throw HTTP_400;
+ }
+
+ // If the Request-URI wasn't absolute, then we need to determine our host.
+ // We have to determine what scheme was used to access us based on the
+ // server identity data at this point, because the request just doesn't
+ // contain enough data on its own to do this, sadly.
+ if (!metadata._host)
+ {
+ var host, port;
+ var hostPort = headers.getHeader("Host");
+ var colon = hostPort.indexOf(":");
+ if (colon < 0)
+ {
+ host = hostPort;
+ port = "";
+ }
+ else
+ {
+ host = hostPort.substring(0, colon);
+ port = hostPort.substring(colon + 1);
+ }
+
+ // NB: We allow an empty port here because, oddly, a colon may be
+ // present even without a port number, e.g. "example.com:"; in this
+ // case the default port applies.
+ if (!HOST_REGEX.test(host) || !/^\d*$/.test(port))
+ {
+ dumpn("*** malformed hostname (" + hostPort + ") in Host " +
+ "header, 400 time");
+ throw HTTP_400;
+ }
+
+ // If we're not given a port, we're stuck, because we don't know what
+ // scheme to use to look up the correct port here, in general. Since
+ // the HTTPS case requires a tunnel/proxy and thus requires that the
+ // requested URI be absolute (and thus contain the necessary
+ // information), let's assume HTTP will prevail and use that.
+ port = +port || 80;
+
+ var scheme = identity.getScheme(host, port);
+ if (!scheme)
+ {
+ dumpn("*** unrecognized hostname (" + hostPort + ") in Host " +
+ "header, 400 time");
+ throw HTTP_400;
+ }
+
+ metadata._scheme = scheme;
+ metadata._host = host;
+ metadata._port = port;
+ }
+ }
+ else
+ {
+ NS_ASSERT(metadata._host === undefined,
+ "HTTP/1.0 doesn't allow absolute paths in the request line!");
+
+ metadata._scheme = identity.primaryScheme;
+ metadata._host = identity.primaryHost;
+ metadata._port = identity.primaryPort;
+ }
+
+ NS_ASSERT(identity.has(metadata._scheme, metadata._host, metadata._port),
+ "must have a location we recognize by now!");
+ },
+
+ /**
+* Handles responses in case of error, either in the server or in the request.
+*
+* @param e
+* the specific error encountered, which is an HttpError in the case where
+* the request is in some way invalid or cannot be fulfilled; if this isn't
+* an HttpError we're going to be paranoid and shut down, because that
+* shouldn't happen, ever
+*/
+ _handleError: function(e)
+ {
+ // Don't fall back into normal processing!
+ this._state = READER_FINISHED;
+
+ var server = this._connection.server;
+ if (e instanceof HttpError)
+ {
+ var code = e.code;
+ }
+ else
+ {
+ dumpn("!!! UNEXPECTED ERROR: " + e +
+ (e.lineNumber ? ", line " + e.lineNumber : ""));
+
+ // no idea what happened -- be paranoid and shut down
+ code = 500;
+ server._requestQuit();
+ }
+
+ // make attempted reuse of data an error
+ this._data = null;
+
+ this._connection.processError(code, this._metadata);
+ },
+
+ /**
+* Now that we've read the request line and headers, we can actually hand off
+* the request to be handled.
+*
+* This method is called once per request, after the request line and all
+* headers and the body, if any, have been received.
+*/
+ _handleResponse: function()
+ {
+ NS_ASSERT(this._state == READER_FINISHED);
+
+ // We don't need the line-based data any more, so make attempted reuse an
+ // error.
+ this._data = null;
+
+ this._connection.process(this._metadata);
+ },
+
+
+ // PARSING
+
+ /**
+* Parses the request line for the HTTP request associated with this.
+*
+* @param line : string
+* the request line
+*/
+ _parseRequestLine: function(line)
+ {
+ NS_ASSERT(this._state == READER_IN_REQUEST_LINE);
+
+ dumpn("*** _parseRequestLine('" + line + "')");
+
+ var metadata = this._metadata;
+
+ // clients and servers SHOULD accept any amount of SP or HT characters
+ // between fields, even though only a single SP is required (section 19.3)
+ var request = line.split(/[ \t]+/);
+ if (!request || request.length != 3)
+ throw HTTP_400;
+
+ metadata._method = request[0];
+
+ // get the HTTP version
+ var ver = request[2];
+ var match = ver.match(/^HTTP\/(\d+\.\d+)$/);
+ if (!match)
+ throw HTTP_400;
+
+ // determine HTTP version
+ try
+ {
+ metadata._httpVersion = new nsHttpVersion(match[1]);
+ if (!metadata._httpVersion.atLeast(nsHttpVersion.HTTP_1_0))
+ throw "unsupported HTTP version";
+ }
+ catch (e)
+ {
+ // we support HTTP/1.0 and HTTP/1.1 only
+ throw HTTP_501;
+ }
+
+
+ var fullPath = request[1];
+ var serverIdentity = this._connection.server.identity;
+
+ var scheme, host, port;
+
+ if (fullPath.charAt(0) != "/")
+ {
+ // No absolute paths in the request line in HTTP prior to 1.1
+ if (!metadata._httpVersion.atLeast(nsHttpVersion.HTTP_1_1))
+ throw HTTP_400;
+
+ try
+ {
+ var uri = Cc["@mozilla.org/network/io-service;1"]
+ .getService(Ci.nsIIOService)
+ .newURI(fullPath, null, null);
+ fullPath = uri.path;
+ scheme = uri.scheme;
+ host = metadata._host = uri.asciiHost;
+ port = uri.port;
+ if (port === -1)
+ {
+ if (scheme === "http")
+ port = 80;
+ else if (scheme === "https")
+ port = 443;
+ else
+ throw HTTP_400;
+ }
+ }
+ catch (e)
+ {
+ // If the host is not a valid host on the server, the response MUST be a
+ // 400 (Bad Request) error message (section 5.2). Alternately, the URI
+ // is malformed.
+ throw HTTP_400;
+ }
+
+ if (!serverIdentity.has(scheme, host, port) || fullPath.charAt(0) != "/")
+ throw HTTP_400;
+ }
+
+ var splitter = fullPath.indexOf("?");
+ if (splitter < 0)
+ {
+ // _queryString already set in ctor
+ metadata._path = fullPath;
+ }
+ else
+ {
+ metadata._path = fullPath.substring(0, splitter);
+ metadata._queryString = fullPath.substring(splitter + 1);
+ }
+
+ metadata._scheme = scheme;
+ metadata._host = host;
+ metadata._port = port;
+ },
+
+ /**
+* Parses all available HTTP headers in this until the header-ending CRLFCRLF,
+* adding them to the store of headers in the request.
+*
+* @throws
+* HTTP_400 if the headers are malformed
+* @returns boolean
+* true if all headers have now been processed, false otherwise
+*/
+ _parseHeaders: function()
+ {
+ NS_ASSERT(this._state == READER_IN_HEADERS);
+
+ dumpn("*** _parseHeaders");
+
+ var data = this._data;
+
+ var headers = this._metadata._headers;
+ var lastName = this._lastHeaderName;
+ var lastVal = this._lastHeaderValue;
+
+ var line = {};
+ while (true)
+ {
+ NS_ASSERT(!((lastVal === undefined) ^ (lastName === undefined)),
+ lastName === undefined ?
+ "lastVal without lastName? lastVal: '" + lastVal + "'" :
+ "lastName without lastVal? lastName: '" + lastName + "'");
+
+ if (!data.readLine(line))
+ {
+ // save any data we have from the header we might still be processing
+ this._lastHeaderName = lastName;
+ this._lastHeaderValue = lastVal;
+ return false;
+ }
+
+ var lineText = line.value;
+ var firstChar = lineText.charAt(0);
+
+ // blank line means end of headers
+ if (lineText == "")
+ {
+ // we're finished with the previous header
+ if (lastName)
+ {
+ try
+ {
+ headers.setHeader(lastName, lastVal, true);
+ }
+ catch (e)
+ {
+ dumpn("*** e == " + e);
+ throw HTTP_400;
+ }
+ }
+ else
+ {
+ // no headers in request -- valid for HTTP/1.0 requests
+ }
+
+ // either way, we're done processing headers
+ this._state = READER_IN_BODY;
+ return true;
+ }
+ else if (firstChar == " " || firstChar == "\t")
+ {
+ // multi-line header if we've already seen a header line
+ if (!lastName)
+ {
+ // we don't have a header to continue!
+ throw HTTP_400;
+ }
+
+ // append this line's text to the value; starts with SP/HT, so no need
+ // for separating whitespace
+ lastVal += lineText;
+ }
+ else
+ {
+ // we have a new header, so set the old one (if one existed)
+ if (lastName)
+ {
+ try
+ {
+ headers.setHeader(lastName, lastVal, true);
+ }
+ catch (e)
+ {
+ dumpn("*** e == " + e);
+ throw HTTP_400;
+ }
+ }
+
+ var colon = lineText.indexOf(":"); // first colon must be splitter
+ if (colon < 1)
+ {
+ // no colon or missing header field-name
+ throw HTTP_400;
+ }
+
+ // set header name, value (to be set in the next loop, usually)
+ lastName = lineText.substring(0, colon);
+ lastVal = lineText.substring(colon + 1);
+ } // empty, continuation, start of header
+ } // while (true)
+ }
+};
+
+
+/** The character codes for CR and LF. */
+const CR = 0x0D, LF = 0x0A;
+
+/**
+* Calculates the number of characters before the first CRLF pair in array, or
+* -1 if the array contains no CRLF pair.
+*
+* @param array : Array
+* an array of numbers in the range [0, 256), each representing a single
+* character; the first CRLF is the lowest index i where
+* |array[i] == "\r".charCodeAt(0)| and |array[i+1] == "\n".charCodeAt(0)|,
+* if such an |i| exists, and -1 otherwise
+* @returns int
+* the index of the first CRLF if any were present, -1 otherwise
+*/
+function findCRLF(array)
+{
+ for (var i = array.indexOf(CR); i >= 0; i = array.indexOf(CR, i + 1))
+ {
+ if (array[i + 1] == LF)
+ return i;
+ }
+ return -1;
+}
+
+
+/**
+* A container which provides line-by-line access to the arrays of bytes with
+* which it is seeded.
+*/
+function LineData()
+{
+ /** An array of queued bytes from which to get line-based characters. */
+ this._data = [];
+}
+LineData.prototype =
+{
+ /**
+* Appends the bytes in the given array to the internal data cache maintained
+* by this.
+*/
+ appendBytes: function(bytes)
+ {
+ Array.prototype.push.apply(this._data, bytes);
+ },
+
+ /**
+* Removes and returns a line of data, delimited by CRLF, from this.
+*
+* @param out
+* an object whose "value" property will be set to the first line of text
+* present in this, sans CRLF, if this contains a full CRLF-delimited line
+* of text; if this doesn't contain enough data, the value of the property
+* is undefined
+* @returns boolean
+* true if a full line of data could be read from the data in this, false
+* otherwise
+*/
+ readLine: function(out)
+ {
+ var data = this._data;
+ var length = findCRLF(data);
+ if (length < 0)
+ return false;
+
+ //
+ // We have the index of the CR, so remove all the characters, including
+ // CRLF, from the array with splice, and convert the removed array into the
+ // corresponding string, from which we then strip the trailing CRLF.
+ //
+ // Getting the line in this matter acknowledges that substring is an O(1)
+ // operation in SpiderMonkey because strings are immutable, whereas two
+ // splices, both from the beginning of the data, are less likely to be as
+ // cheap as a single splice plus two extra character conversions.
+ //
+ var line = String.fromCharCode.apply(null, data.splice(0, length + 2));
+ out.value = line.substring(0, length);
+
+ return true;
+ },
+
+ /**
+* Removes the bytes currently within this and returns them in an array.
+*
+* @returns Array
+* the bytes within this when this method is called
+*/
+ purge: function()
+ {
+ var data = this._data;
+ this._data = [];
+ return data;
+ }
+};
+
+
+
+/**
+* Creates a request-handling function for an nsIHttpRequestHandler object.
+*/
+function createHandlerFunc(handler)
+{
+ return function(metadata, response) { handler.handle(metadata, response); };
+}
+
+
+/**
+* The default handler for directories; writes an HTML response containing a
+* slightly-formatted directory listing.
+*/
+function defaultIndexHandler(metadata, response)
+{
+ response.setHeader("Content-Type", "text/html", false);
+
+ var path = htmlEscape(decodeURI(metadata.path));
+
+ //
+ // Just do a very basic bit of directory listings -- no need for too much
+ // fanciness, especially since we don't have a style sheet in which we can
+ // stick rules (don't want to pollute the default path-space).
+ //
+
+ var body = '<html>\
+<head>\
+<title>' + path + '</title>\
+</head>\
+<body>\
+<h1>' + path + '</h1>\
+<ol style="list-style-type: none">';
+
+ var directory = metadata.getProperty("directory").QueryInterface(Ci.nsILocalFile);
+ NS_ASSERT(directory && directory.isDirectory());
+
+ var fileList = [];
+ var files = directory.directoryEntries;
+ while (files.hasMoreElements())
+ {
+ var f = files.getNext().QueryInterface(Ci.nsIFile);
+ var name = f.leafName;
+ if (!f.isHidden() &&
+ (name.charAt(name.length - 1) != HIDDEN_CHAR ||
+ name.charAt(name.length - 2) == HIDDEN_CHAR))
+ fileList.push(f);
+ }
+
+ fileList.sort(fileSort);
+
+ for (var i = 0; i < fileList.length; i++)
+ {
+ var file = fileList[i];
+ try
+ {
+ var name = file.leafName;
+ if (name.charAt(name.length - 1) == HIDDEN_CHAR)
+ name = name.substring(0, name.length - 1);
+ var sep = file.isDirectory() ? "/" : "";
+
+ // Note: using " to delimit the attribute here because encodeURIComponent
+ // passes through '.
+ var item = '<li><a href="' + encodeURIComponent(name) + sep + '">' +
+ htmlEscape(name) + sep +
+ '</a></li>';
+
+ body += item;
+ }
+ catch (e) { /* some file system error, ignore the file */ }
+ }
+
+ body += ' </ol>\
+</body>\
+</html>';
+
+ response.bodyOutputStream.write(body, body.length);
+}
+
+/**
+* Sorts a and b (nsIFile objects) into an aesthetically pleasing order.
+*/
+function fileSort(a, b)
+{
+ var dira = a.isDirectory(), dirb = b.isDirectory();
+
+ if (dira && !dirb)
+ return -1;
+ if (dirb && !dira)
+ return 1;
+
+ var namea = a.leafName.toLowerCase(), nameb = b.leafName.toLowerCase();
+ return nameb > namea ? -1 : 1;
+}
+
+
+/**
+* Converts an externally-provided path into an internal path for use in
+* determining file mappings.
+*
+* @param path
+* the path to convert
+* @param encoded
+* true if the given path should be passed through decodeURI prior to
+* conversion
+* @throws URIError
+* if path is incorrectly encoded
+*/
+function toInternalPath(path, encoded)
+{
+ if (encoded)
+ path = decodeURI(path);
+
+ var comps = path.split("/");
+ for (var i = 0, sz = comps.length; i < sz; i++)
+ {
+ var comp = comps[i];
+ if (comp.charAt(comp.length - 1) == HIDDEN_CHAR)
+ comps[i] = comp + HIDDEN_CHAR;
+ }
+ return comps.join("/");
+}
+
+
+/**
+* Adds custom-specified headers for the given file to the given response, if
+* any such headers are specified.
+*
+* @param file
+* the file on the disk which is to be written
+* @param metadata
+* metadata about the incoming request
+* @param response
+* the Response to which any specified headers/data should be written
+* @throws HTTP_500
+* if an error occurred while processing custom-specified headers
+*/
+function maybeAddHeaders(file, metadata, response)
+{
+ var name = file.leafName;
+ if (name.charAt(name.length - 1) == HIDDEN_CHAR)
+ name = name.substring(0, name.length - 1);
+
+ var headerFile = file.parent;
+ headerFile.append(name + HEADERS_SUFFIX);
+
+ if (!headerFile.exists())
+ return;
+
+ const PR_RDONLY = 0x01;
+ var fis = new FileInputStream(headerFile, PR_RDONLY, parseInt("444", 8),
+ Ci.nsIFileInputStream.CLOSE_ON_EOF);
+
+ try
+ {
+ var lis = new ConverterInputStream(fis, "UTF-8", 1024, 0x0);
+ lis.QueryInterface(Ci.nsIUnicharLineInputStream);
+
+ var line = {value: ""};
+ var more = lis.readLine(line);
+
+ if (!more && line.value == "")
+ return;
+
+
+ // request line
+
+ var status = line.value;
+ if (status.indexOf("HTTP ") == 0)
+ {
+ status = status.substring(5);
+ var space = status.indexOf(" ");
+ var code, description;
+ if (space < 0)
+ {
+ code = status;
+ description = "";
+ }
+ else
+ {
+ code = status.substring(0, space);
+ description = status.substring(space + 1, status.length);
+ }
+
+ response.setStatusLine(metadata.httpVersion, parseInt(code, 10), description);
+
+ line.value = "";
+ more = lis.readLine(line);
+ }
+
+ // headers
+ while (more || line.value != "")
+ {
+ var header = line.value;
+ var colon = header.indexOf(":");
+
+ response.setHeader(header.substring(0, colon),
+ header.substring(colon + 1, header.length),
+ false); // allow overriding server-set headers
+
+ line.value = "";
+ more = lis.readLine(line);
+ }
+ }
+ catch (e)
+ {
+ dumpn("WARNING: error in headers for " + metadata.path + ": " + e);
+ throw HTTP_500;
+ }
+ finally
+ {
+ fis.close();
+ }
+}
+
+
+/**
+* An object which handles requests for a server, executing default and
+* overridden behaviors as instructed by the code which uses and manipulates it.
+* Default behavior includes the paths / and /trace (diagnostics), with some
+* support for HTTP error pages for various codes and fallback to HTTP 500 if
+* those codes fail for any reason.
+*
+* @param server : nsHttpServer
+* the server in which this handler is being used
+*/
+function ServerHandler(server)
+{
+ // FIELDS
+
+ /**
+* The nsHttpServer instance associated with this handler.
+*/
+ this._server = server;
+
+ /**
+* A FileMap object containing the set of path->nsILocalFile mappings for
+* all directory mappings set in the server (e.g., "/" for /var/www/html/,
+* "/foo/bar/" for /local/path/, and "/foo/bar/baz/" for /local/path2).
+*
+* Note carefully: the leading and trailing "/" in each path (not file) are
+* removed before insertion to simplify the code which uses this. You have
+* been warned!
+*/
+ this._pathDirectoryMap = new FileMap();
+
+ /**
+* Custom request handlers for the server in which this resides. Path-handler
+* pairs are stored as property-value pairs in this property.
+*
+* @see ServerHandler.prototype._defaultPaths
+*/
+ this._overridePaths = {};
+
+ /**
+* Custom request handlers for the error handlers in the server in which this
+* resides. Path-handler pairs are stored as property-value pairs in this
+* property.
+*
+* @see ServerHandler.prototype._defaultErrors
+*/
+ this._overrideErrors = {};
+
+ /**
+* Maps file extensions to their MIME types in the server, overriding any
+* mapping that might or might not exist in the MIME service.
+*/
+ this._mimeMappings = {};
+
+ /**
+* The default handler for requests for directories, used to serve directories
+* when no index file is present.
+*/
+ this._indexHandler = defaultIndexHandler;
+
+ /** Per-path state storage for the server. */
+ this._state = {};
+
+ /** Entire-server state storage. */
+ this._sharedState = {};
+
+ /** Entire-server state storage for nsISupports values. */
+ this._objectState = {};
+}
+ServerHandler.prototype =
+{
+ // PUBLIC API
+
+ /**
+* Handles a request to this server, responding to the request appropriately
+* and initiating server shutdown if necessary.
+*
+* This method never throws an exception.
+*
+* @param connection : Connection
+* the connection for this request
+*/
+ handleResponse: function(connection)
+ {
+ var request = connection.request;
+ var response = new Response(connection);
+
+ var path = request.path;
+ dumpn("*** path == " + path);
+
+ try
+ {
+ try
+ {
+ if (path in this._overridePaths)
+ {
+ // explicit paths first, then files based on existing directory mappings,
+ // then (if the file doesn't exist) built-in server default paths
+ dumpn("calling override for " + path);
+ this._overridePaths[path](request, response);
+ }
+ else
+ {
+ this._handleDefault(request, response);
+ }
+ }
+ catch (e)
+ {
+ if (response.partiallySent())
+ {
+ response.abort(e);
+ return;
+ }
+
+ if (!(e instanceof HttpError))
+ {
+ dumpn("*** unexpected error: e == " + e);
+ throw HTTP_500;
+ }
+ if (e.code !== 404)
+ throw e;
+
+ dumpn("*** default: " + (path in this._defaultPaths));
+
+ response = new Response(connection);
+ if (path in this._defaultPaths)
+ this._defaultPaths[path](request, response);
+ else
+ throw HTTP_404;
+ }
+ }
+ catch (e)
+ {
+ if (response.partiallySent())
+ {
+ response.abort(e);
+ return;
+ }
+
+ var errorCode = "internal";
+
+ try
+ {
+ if (!(e instanceof HttpError))
+ throw e;
+
+ errorCode = e.code;
+ dumpn("*** errorCode == " + errorCode);
+
+ response = new Response(connection);
+ if (e.customErrorHandling)
+ e.customErrorHandling(response);
+ this._handleError(errorCode, request, response);
+ return;
+ }
+ catch (e2)
+ {
+ dumpn("*** error handling " + errorCode + " error: " +
+ "e2 == " + e2 + ", shutting down server");
+
+ connection.server._requestQuit();
+ response.abort(e2);
+ return;
+ }
+ }
+
+ response.complete();
+ },
+
+ //
+ // see nsIHttpServer.registerFile
+ //
+ registerFile: function(path, file)
+ {
+ if (!file)
+ {
+ dumpn("*** unregistering '" + path + "' mapping");
+ delete this._overridePaths[path];
+ return;
+ }
+
+ dumpn("*** registering '" + path + "' as mapping to " + file.path);
+ file = file.clone();
+
+ var self = this;
+ this._overridePaths[path] =
+ function(request, response)
+ {
+ if (!file.exists())
+ throw HTTP_404;
+
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ self._writeFileResponse(request, file, response, 0, file.fileSize);
+ };
+ },
+
+ //
+ // see nsIHttpServer.registerPathHandler
+ //
+ registerPathHandler: function(path, handler)
+ {
+ // XXX true path validation!
+ if (path.charAt(0) != "/")
+ throw Cr.NS_ERROR_INVALID_ARG;
+
+ this._handlerToField(handler, this._overridePaths, path);
+ },
+
+ //
+ // see nsIHttpServer.registerDirectory
+ //
+ registerDirectory: function(path, directory)
+ {
+ // strip off leading and trailing '/' so that we can use lastIndexOf when
+ // determining exactly how a path maps onto a mapped directory --
+ // conditional is required here to deal with "/".substring(1, 0) being
+ // converted to "/".substring(0, 1) per the JS specification
+ var key = path.length == 1 ? "" : path.substring(1, path.length - 1);
+
+ // the path-to-directory mapping code requires that the first character not
+ // be "/", or it will go into an infinite loop
+ if (key.charAt(0) == "/")
+ throw Cr.NS_ERROR_INVALID_ARG;
+
+ key = toInternalPath(key, false);
+
+ if (directory)
+ {
+ dumpn("*** mapping '" + path + "' to the location " + directory.path);
+ this._pathDirectoryMap.put(key, directory);
+ }
+ else
+ {
+ dumpn("*** removing mapping for '" + path + "'");
+ this._pathDirectoryMap.put(key, null);
+ }
+ },
+
+ //
+ // see nsIHttpServer.registerErrorHandler
+ //
+ registerErrorHandler: function(err, handler)
+ {
+ if (!(err in HTTP_ERROR_CODES))
+ dumpn("*** WARNING: registering non-HTTP/1.1 error code " +
+ "(" + err + ") handler -- was this intentional?");
+
+ this._handlerToField(handler, this._overrideErrors, err);
+ },
+
+ //
+ // see nsIHttpServer.setIndexHandler
+ //
+ setIndexHandler: function(handler)
+ {
+ if (!handler)
+ handler = defaultIndexHandler;
+ else if (typeof(handler) != "function")
+ handler = createHandlerFunc(handler);
+
+ this._indexHandler = handler;
+ },
+
+ //
+ // see nsIHttpServer.registerContentType
+ //
+ registerContentType: function(ext, type)
+ {
+ if (!type)
+ delete this._mimeMappings[ext];
+ else
+ this._mimeMappings[ext] = headerUtils.normalizeFieldValue(type);
+ },
+
+ // PRIVATE API
+
+ /**
+* Sets or remove (if handler is null) a handler in an object with a key.
+*
+* @param handler
+* a handler, either function or an nsIHttpRequestHandler
+* @param dict
+* The object to attach the handler to.
+* @param key
+* The field name of the handler.
+*/
+ _handlerToField: function(handler, dict, key)
+ {
+ // for convenience, handler can be a function if this is run from xpcshell
+ if (typeof(handler) == "function")
+ dict[key] = handler;
+ else if (handler)
+ dict[key] = createHandlerFunc(handler);
+ else
+ delete dict[key];
+ },
+
+ /**
+* Handles a request which maps to a file in the local filesystem (if a base
+* path has already been set; otherwise the 404 error is thrown).
+*
+* @param metadata : Request
+* metadata for the incoming request
+* @param response : Response
+* an uninitialized Response to the given request, to be initialized by a
+* request handler
+* @throws HTTP_###
+* if an HTTP error occurred (usually HTTP_404); note that in this case the
+* calling code must handle post-processing of the response
+*/
+ _handleDefault: function(metadata, response)
+ {
+ dumpn("*** _handleDefault()");
+
+ response.setStatusLine(metadata.httpVersion, 200, "OK");
+
+ var path = metadata.path;
+ NS_ASSERT(path.charAt(0) == "/", "invalid path: <" + path + ">");
+
+ // determine the actual on-disk file; this requires finding the deepest
+ // path-to-directory mapping in the requested URL
+ var file = this._getFileForPath(path);
+
+ // the "file" might be a directory, in which case we either serve the
+ // contained index.html or make the index handler write the response
+ if (file.exists() && file.isDirectory())
+ {
+ file.append("index.html"); // make configurable?
+ if (!file.exists() || file.isDirectory())
+ {
+ metadata._ensurePropertyBag();
+ metadata._bag.setPropertyAsInterface("directory", file.parent);
+ this._indexHandler(metadata, response);
+ return;
+ }
+ }
+
+ // alternately, the file might not exist
+ if (!file.exists())
+ throw HTTP_404;
+
+ var start, end;
+ if (metadata._httpVersion.atLeast(nsHttpVersion.HTTP_1_1) &&
+ metadata.hasHeader("Range") &&
+ this._getTypeFromFile(file) !== SJS_TYPE)
+ {
+ var rangeMatch = metadata.getHeader("Range").match(/^bytes=(\d+)?-(\d+)?$/);
+ if (!rangeMatch)
+ throw HTTP_400;
+
+ if (rangeMatch[1] !== undefined)
+ start = parseInt(rangeMatch[1], 10);
+
+ if (rangeMatch[2] !== undefined)
+ end = parseInt(rangeMatch[2], 10);
+
+ if (start === undefined && end === undefined)
+ throw HTTP_400;
+
+ // No start given, so the end is really the count of bytes from the
+ // end of the file.
+ if (start === undefined)
+ {
+ start = Math.max(0, file.fileSize - end);
+ end = file.fileSize - 1;
+ }
+
+ // start and end are inclusive
+ if (end === undefined || end >= file.fileSize)
+ end = file.fileSize - 1;
+
+ if (start !== undefined && start >= file.fileSize) {
+ var HTTP_416 = new HttpError(416, "Requested Range Not Satisfiable");
+ HTTP_416.customErrorHandling = function(errorResponse)
+ {
+ maybeAddHeaders(file, metadata, errorResponse);
+ };
+ throw HTTP_416;
+ }
+
+ if (end < start)
+ {
+ response.setStatusLine(metadata.httpVersion, 200, "OK");
+ start = 0;
+ end = file.fileSize - 1;
+ }
+ else
+ {
+ response.setStatusLine(metadata.httpVersion, 206, "Partial Content");
+ var contentRange = "bytes " + start + "-" + end + "/" + file.fileSize;
+ response.setHeader("Content-Range", contentRange);
+ }
+ }
+ else
+ {
+ start = 0;
+ end = file.fileSize - 1;
+ }
+
+ // finally...
+ dumpn("*** handling '" + path + "' as mapping to " + file.path + " from " +
+ start + " to " + end + " inclusive");
+ this._writeFileResponse(metadata, file, response, start, end - start + 1);
+ },
+
+ /**
+* Writes an HTTP response for the given file, including setting headers for
+* file metadata.
+*
+* @param metadata : Request
+* the Request for which a response is being generated
+* @param file : nsILocalFile
+* the file which is to be sent in the response
+* @param response : Response
+* the response to which the file should be written
+* @param offset: uint
+* the byte offset to skip to when writing
+* @param count: uint
+* the number of bytes to write
+*/
+ _writeFileResponse: function(metadata, file, response, offset, count)
+ {
+ const PR_RDONLY = 0x01;
+
+ var type = this._getTypeFromFile(file);
+ if (type === SJS_TYPE)
+ {
+ var fis = new FileInputStream(file, PR_RDONLY, parseInt("444", 8),
+ Ci.nsIFileInputStream.CLOSE_ON_EOF);
+
+ try
+ {
+ var sis = new ScriptableInputStream(fis);
+ var s = Cu.Sandbox(gGlobalObject);
+ s.importFunction(dump, "dump");
+
+ // Define a basic key-value state-preservation API across requests, with
+ // keys initially corresponding to the empty string.
+ var self = this;
+ var path = metadata.path;
+ s.importFunction(function getState(k)
+ {
+ return self._getState(path, k);
+ });
+ s.importFunction(function setState(k, v)
+ {
+ self._setState(path, k, v);
+ });
+ s.importFunction(function getSharedState(k)
+ {
+ return self._getSharedState(k);
+ });
+ s.importFunction(function setSharedState(k, v)
+ {
+ self._setSharedState(k, v);
+ });
+ s.importFunction(function getObjectState(k, callback)
+ {
+ callback(self._getObjectState(k));
+ });
+ s.importFunction(function setObjectState(k, v)
+ {
+ self._setObjectState(k, v);
+ });
+ s.importFunction(function registerPathHandler(p, h)
+ {
+ self.registerPathHandler(p, h);
+ });
+
+ // Make it possible for sjs files to access their location
+ this._setState(path, "__LOCATION__", file.path);
+
+ try
+ {
+ // Alas, the line number in errors dumped to console when calling the
+ // request handler is simply an offset from where we load the SJS file.
+ // Work around this in a reasonably non-fragile way by dynamically
+ // getting the line number where we evaluate the SJS file. Don't
+ // separate these two lines!
+ var line = new Error().lineNumber;
+ Cu.evalInSandbox(sis.read(file.fileSize), s);
+ }
+ catch (e)
+ {
+ dumpn("*** syntax error in SJS at " + file.path + ": " + e);
+ throw HTTP_500;
+ }
+
+ try
+ {
+ s.handleRequest(metadata, response);
+ }
+ catch (e)
+ {
+ dump("*** error running SJS at " + file.path + ": " +
+ e + " on line " +
+ (e instanceof Error
+ ? e.lineNumber + " in httpd.js"
+ : (e.lineNumber - line)) + "\n");
+ throw HTTP_500;
+ }
+ }
+ finally
+ {
+ fis.close();
+ }
+ }
+ else
+ {
+ try
+ {
+ response.setHeader("Last-Modified",
+ toDateString(file.lastModifiedTime),
+ false);
+ }
+ catch (e) { /* lastModifiedTime threw, ignore */ }
+
+ response.setHeader("Content-Type", type, false);
+ maybeAddHeaders(file, metadata, response);
+ response.setHeader("Content-Length", "" + count, false);
+
+ var fis = new FileInputStream(file, PR_RDONLY, parseInt("444", 8),
+ Ci.nsIFileInputStream.CLOSE_ON_EOF);
+
+ offset = offset || 0;
+ count = count || file.fileSize;
+ NS_ASSERT(offset === 0 || offset < file.fileSize, "bad offset");
+ NS_ASSERT(count >= 0, "bad count");
+ NS_ASSERT(offset + count <= file.fileSize, "bad total data size");
+
+ try
+ {
+ if (offset !== 0)
+ {
+ // Seek (or read, if seeking isn't supported) to the correct offset so
+ // the data sent to the client matches the requested range.
+ if (fis instanceof Ci.nsISeekableStream)
+ fis.seek(Ci.nsISeekableStream.NS_SEEK_SET, offset);
+ else
+ new ScriptableInputStream(fis).read(offset);
+ }
+ }
+ catch (e)
+ {
+ fis.close();
+ throw e;
+ }
+
+ function writeMore()
+ {
+ gThreadManager.currentThread
+ .dispatch(writeData, Ci.nsIThread.DISPATCH_NORMAL);
+ }
+
+ var input = new BinaryInputStream(fis);
+ var output = new BinaryOutputStream(response.bodyOutputStream);
+ var writeData =
+ {
+ run: function()
+ {
+ var chunkSize = Math.min(65536, count);
+ count -= chunkSize;
+ NS_ASSERT(count >= 0, "underflow");
+
+ try
+ {
+ var data = input.readByteArray(chunkSize);
+ NS_ASSERT(data.length === chunkSize,
+ "incorrect data returned? got " + data.length +
+ ", expected " + chunkSize);
+ output.writeByteArray(data, data.length);
+ if (count === 0)
+ {
+ fis.close();
+ response.finish();
+ }
+ else
+ {
+ writeMore();
+ }
+ }
+ catch (e)
+ {
+ try
+ {
+ fis.close();
+ }
+ finally
+ {
+ response.finish();
+ }
+ throw e;
+ }
+ }
+ };
+
+ writeMore();
+
+ // Now that we know copying will start, flag the response as async.
+ response.processAsync();
+ }
+ },
+
+ /**
+* Get the value corresponding to a given key for the given path for SJS state
+* preservation across requests.
+*
+* @param path : string
+* the path from which the given state is to be retrieved
+* @param k : string
+* the key whose corresponding value is to be returned
+* @returns string
+* the corresponding value, which is initially the empty string
+*/
+ _getState: function(path, k)
+ {
+ var state = this._state;
+ if (path in state && k in state[path])
+ return state[path][k];
+ return "";
+ },
+
+ /**
+* Set the value corresponding to a given key for the given path for SJS state
+* preservation across requests.
+*
+* @param path : string
+* the path from which the given state is to be retrieved
+* @param k : string
+* the key whose corresponding value is to be set
+* @param v : string
+* the value to be set
+*/
+ _setState: function(path, k, v)
+ {
+ if (typeof v !== "string")
+ throw new Error("non-string value passed");
+ var state = this._state;
+ if (!(path in state))
+ state[path] = {};
+ state[path][k] = v;
+ },
+
+ /**
+* Get the value corresponding to a given key for SJS state preservation
+* across requests.
+*
+* @param k : string
+* the key whose corresponding value is to be returned
+* @returns string
+* the corresponding value, which is initially the empty string
+*/
+ _getSharedState: function(k)
+ {
+ var state = this._sharedState;
+ if (k in state)
+ return state[k];
+ return "";
+ },
+
+ /**
+* Set the value corresponding to a given key for SJS state preservation
+* across requests.
+*
+* @param k : string
+* the key whose corresponding value is to be set
+* @param v : string
+* the value to be set
+*/
+ _setSharedState: function(k, v)
+ {
+ if (typeof v !== "string")
+ throw new Error("non-string value passed");
+ this._sharedState[k] = v;
+ },
+
+ /**
+* Returns the object associated with the given key in the server for SJS
+* state preservation across requests.
+*
+* @param k : string
+* the key whose corresponding object is to be returned
+* @returns nsISupports
+* the corresponding object, or null if none was present
+*/
+ _getObjectState: function(k)
+ {
+ if (typeof k !== "string")
+ throw new Error("non-string key passed");
+ return this._objectState[k] || null;
+ },
+
+ /**
+* Sets the object associated with the given key in the server for SJS
+* state preservation across requests.
+*
+* @param k : string
+* the key whose corresponding object is to be set
+* @param v : nsISupports
+* the object to be associated with the given key; may be null
+*/
+ _setObjectState: function(k, v)
+ {
+ if (typeof k !== "string")
+ throw new Error("non-string key passed");
+ if (typeof v !== "object")
+ throw new Error("non-object value passed");
+ if (v && !("QueryInterface" in v))
+ {
+ throw new Error("must pass an nsISupports; use wrappedJSObject to ease " +
+ "pain when using the server from JS");
+ }
+
+ this._objectState[k] = v;
+ },
+
+ /**
+* Gets a content-type for the given file, first by checking for any custom
+* MIME-types registered with this handler for the file's extension, second by
+* asking the global MIME service for a content-type, and finally by failing
+* over to application/octet-stream.
+*
+* @param file : nsIFile
+* the nsIFile for which to get a file type
+* @returns string
+* the best content-type which can be determined for the file
+*/
+ _getTypeFromFile: function(file)
+ {
+ try
+ {
+ var name = file.leafName;
+ var dot = name.lastIndexOf(".");
+ if (dot > 0)
+ {
+ var ext = name.slice(dot + 1);
+ if (ext in this._mimeMappings)
+ return this._mimeMappings[ext];
+ }
+ return Cc["@mozilla.org/uriloader/external-helper-app-service;1"]
+ .getService(Ci.nsIMIMEService)
+ .getTypeFromFile(file);
+ }
+ catch (e)
+ {
+ return "application/octet-stream";
+ }
+ },
+
+ /**
+* Returns the nsILocalFile which corresponds to the path, as determined using
+* all registered path->directory mappings and any paths which are explicitly
+* overridden.
+*
+* @param path : string
+* the server path for which a file should be retrieved, e.g. "/foo/bar"
+* @throws HttpError
+* when the correct action is the corresponding HTTP error (i.e., because no
+* mapping was found for a directory in path, the referenced file doesn't
+* exist, etc.)
+* @returns nsILocalFile
+* the file to be sent as the response to a request for the path
+*/
+ _getFileForPath: function(path)
+ {
+ // decode and add underscores as necessary
+ try
+ {
+ path = toInternalPath(path, true);
+ }
+ catch (e)
+ {
+ throw HTTP_400; // malformed path
+ }
+
+ // next, get the directory which contains this path
+ var pathMap = this._pathDirectoryMap;
+
+ // An example progression of tmp for a path "/foo/bar/baz/" might be:
+ // "foo/bar/baz/", "foo/bar/baz", "foo/bar", "foo", ""
+ var tmp = path.substring(1);
+ while (true)
+ {
+ // do we have a match for current head of the path?
+ var file = pathMap.get(tmp);
+ if (file)
+ {
+ // XXX hack; basically disable showing mapping for /foo/bar/ when the
+ // requested path was /foo/bar, because relative links on the page
+ // will all be incorrect -- we really need the ability to easily
+ // redirect here instead
+ if (tmp == path.substring(1) &&
+ tmp.length != 0 &&
+ tmp.charAt(tmp.length - 1) != "/")
+ file = null;
+ else
+ break;
+ }
+
+ // if we've finished trying all prefixes, exit
+ if (tmp == "")
+ break;
+
+ tmp = tmp.substring(0, tmp.lastIndexOf("/"));
+ }
+
+ // no mapping applies, so 404
+ if (!file)
+ throw HTTP_404;
+
+
+ // last, get the file for the path within the determined directory
+ var parentFolder = file.parent;
+ var dirIsRoot = (parentFolder == null);
+
+ // Strategy here is to append components individually, making sure we
+ // never move above the given directory; this allows paths such as
+ // "<file>/foo/../bar" but prevents paths such as "<file>/../base-sibling";
+ // this component-wise approach also means the code works even on platforms
+ // which don't use "/" as the directory separator, such as Windows
+ var leafPath = path.substring(tmp.length + 1);
+ var comps = leafPath.split("/");
+ for (var i = 0, sz = comps.length; i < sz; i++)
+ {
+ var comp = comps[i];
+
+ if (comp == "..")
+ file = file.parent;
+ else if (comp == "." || comp == "")
+ continue;
+ else
+ file.append(comp);
+
+ if (!dirIsRoot && file.equals(parentFolder))
+ throw HTTP_403;
+ }
+
+ return file;
+ },
+
+ /**
+* Writes the error page for the given HTTP error code over the given
+* connection.
+*
+* @param errorCode : uint
+* the HTTP error code to be used
+* @param connection : Connection
+* the connection on which the error occurred
+*/
+ handleError: function(errorCode, connection)
+ {
+ var response = new Response(connection);
+
+ dumpn("*** error in request: " + errorCode);
+
+ this._handleError(errorCode, new Request(connection.port), response);
+ },
+
+ /**
+* Handles a request which generates the given error code, using the
+* user-defined error handler if one has been set, gracefully falling back to
+* the x00 status code if the code has no handler, and failing to status code
+* 500 if all else fails.
+*
+* @param errorCode : uint
+* the HTTP error which is to be returned
+* @param metadata : Request
+* metadata for the request, which will often be incomplete since this is an
+* error
+* @param response : Response
+* an uninitialized Response should be initialized when this method
+* completes with information which represents the desired error code in the
+* ideal case or a fallback code in abnormal circumstances (i.e., 500 is a
+* fallback for 505, per HTTP specs)
+*/
+ _handleError: function(errorCode, metadata, response)
+ {
+ if (!metadata)
+ throw Cr.NS_ERROR_NULL_POINTER;
+
+ var errorX00 = errorCode - (errorCode % 100);
+
+ try
+ {
+ if (!(errorCode in HTTP_ERROR_CODES))
+ dumpn("*** WARNING: requested invalid error: " + errorCode);
+
+ // RFC 2616 says that we should try to handle an error by its class if we
+ // can't otherwise handle it -- if that fails, we revert to handling it as
+ // a 500 internal server error, and if that fails we throw and shut down
+ // the server
+
+ // actually handle the error
+ try
+ {
+ if (errorCode in this._overrideErrors)
+ this._overrideErrors[errorCode](metadata, response);
+ else
+ this._defaultErrors[errorCode](metadata, response);
+ }
+ catch (e)
+ {
+ if (response.partiallySent())
+ {
+ response.abort(e);
+ return;
+ }
+
+ // don't retry the handler that threw
+ if (errorX00 == errorCode)
+ throw HTTP_500;
+
+ dumpn("*** error in handling for error code " + errorCode + ", " +
+ "falling back to " + errorX00 + "...");
+ response = new Response(response._connection);
+ if (errorX00 in this._overrideErrors)
+ this._overrideErrors[errorX00](metadata, response);
+ else if (errorX00 in this._defaultErrors)
+ this._defaultErrors[errorX00](metadata, response);
+ else
+ throw HTTP_500;
+ }
+ }
+ catch (e)
+ {
+ if (response.partiallySent())
+ {
+ response.abort();
+ return;
+ }
+
+ // we've tried everything possible for a meaningful error -- now try 500
+ dumpn("*** error in handling for error code " + errorX00 + ", falling " +
+ "back to 500...");
+
+ try
+ {
+ response = new Response(response._connection);
+ if (500 in this._overrideErrors)
+ this._overrideErrors[500](metadata, response);
+ else
+ this._defaultErrors[500](metadata, response);
+ }
+ catch (e2)
+ {
+ dumpn("*** multiple errors in default error handlers!");
+ dumpn("*** e == " + e + ", e2 == " + e2);
+ response.abort(e2);
+ return;
+ }
+ }
+
+ response.complete();
+ },
+
+ // FIELDS
+
+ /**
+* This object contains the default handlers for the various HTTP error codes.
+*/
+ _defaultErrors:
+ {
+ 400: function(metadata, response)
+ {
+ // none of the data in metadata is reliable, so hard-code everything here
+ response.setStatusLine("1.1", 400, "Bad Request");
+ response.setHeader("Content-Type", "text/plain", false);
+
+ var body = "Bad request\n";
+ response.bodyOutputStream.write(body, body.length);
+ },
+ 403: function(metadata, response)
+ {
+ response.setStatusLine(metadata.httpVersion, 403, "Forbidden");
+ response.setHeader("Content-Type", "text/html", false);
+
+ var body = "<html>\
+<head><title>403 Forbidden</title></head>\
+<body>\
+<h1>403 Forbidden</h1>\
+</body>\
+</html>";
+ response.bodyOutputStream.write(body, body.length);
+ },
+ 404: function(metadata, response)
+ {
+ response.setStatusLine(metadata.httpVersion, 404, "Not Found");
+ response.setHeader("Content-Type", "text/html", false);
+
+ var body = "<html>\
+<head><title>404 Not Found</title></head>\
+<body>\
+<h1>404 Not Found</h1>\
+<p>\
+<span style='font-family: monospace;'>" +
+ htmlEscape(metadata.path) +
+ "</span> was not found.\
+</p>\
+</body>\
+</html>";
+ response.bodyOutputStream.write(body, body.length);
+ },
+ 416: function(metadata, response)
+ {
+ response.setStatusLine(metadata.httpVersion,
+ 416,
+ "Requested Range Not Satisfiable");
+ response.setHeader("Content-Type", "text/html", false);
+
+ var body = "<html>\
+<head>\
+<title>416 Requested Range Not Satisfiable</title></head>\
+<body>\
+<h1>416 Requested Range Not Satisfiable</h1>\
+<p>The byte range was not valid for the\
+requested resource.\
+</p>\
+</body>\
+</html>";
+ response.bodyOutputStream.write(body, body.length);
+ },
+ 500: function(metadata, response)
+ {
+ response.setStatusLine(metadata.httpVersion,
+ 500,
+ "Internal Server Error");
+ response.setHeader("Content-Type", "text/html", false);
+
+ var body = "<html>\
+<head><title>500 Internal Server Error</title></head>\
+<body>\
+<h1>500 Internal Server Error</h1>\
+<p>Something's broken in this server and\
+needs to be fixed.</p>\
+</body>\
+</html>";
+ response.bodyOutputStream.write(body, body.length);
+ },
+ 501: function(metadata, response)
+ {
+ response.setStatusLine(metadata.httpVersion, 501, "Not Implemented");
+ response.setHeader("Content-Type", "text/html", false);
+
+ var body = "<html>\
+<head><title>501 Not Implemented</title></head>\
+<body>\
+<h1>501 Not Implemented</h1>\
+<p>This server is not (yet) Apache.</p>\
+</body>\
+</html>";
+ response.bodyOutputStream.write(body, body.length);
+ },
+ 505: function(metadata, response)
+ {
+ response.setStatusLine("1.1", 505, "HTTP Version Not Supported");
+ response.setHeader("Content-Type", "text/html", false);
+
+ var body = "<html>\
+<head><title>505 HTTP Version Not Supported</title></head>\
+<body>\
+<h1>505 HTTP Version Not Supported</h1>\
+<p>This server only supports HTTP/1.0 and HTTP/1.1\
+connections.</p>\
+</body>\
+</html>";
+ response.bodyOutputStream.write(body, body.length);
+ }
+ },
+
+ /**
+* Contains handlers for the default set of URIs contained in this server.
+*/
+ _defaultPaths:
+ {
+ "/": function(metadata, response)
+ {
+ response.setStatusLine(metadata.httpVersion, 200, "OK");
+ response.setHeader("Content-Type", "text/html", false);
+
+ var body = "<html>\
+<head><title>httpd.js</title></head>\
+<body>\
+<h1>httpd.js</h1>\
+<p>If you're seeing this page, httpd.js is up and\
+serving requests! Now set a base path and serve some\
+files!</p>\
+</body>\
+</html>";
+
+ response.bodyOutputStream.write(body, body.length);
+ },
+
+ "/trace": function(metadata, response)
+ {
+ response.setStatusLine(metadata.httpVersion, 200, "OK");
+ response.setHeader("Content-Type", "text/plain", false);
+
+ var body = "Request-URI: " +
+ metadata.scheme + "://" + metadata.host + ":" + metadata.port +
+ metadata.path + "\n\n";
+ body += "Request (semantically equivalent, slightly reformatted):\n\n";
+ body += metadata.method + " " + metadata.path;
+
+ if (metadata.queryString)
+ body += "?" + metadata.queryString;
+
+ body += " HTTP/" + metadata.httpVersion + "\r\n";
+
+ var headEnum = metadata.headers;
+ while (headEnum.hasMoreElements())
+ {
+ var fieldName = headEnum.getNext()
+ .QueryInterface(Ci.nsISupportsString)
+ .data;
+ body += fieldName + ": " + metadata.getHeader(fieldName) + "\r\n";
+ }
+
+ response.bodyOutputStream.write(body, body.length);
+ }
+ }
+};
+
+
+/**
+* Maps absolute paths to files on the local file system (as nsILocalFiles).
+*/
+function FileMap()
+{
+ /** Hash which will map paths to nsILocalFiles. */
+ this._map = {};
+}
+FileMap.prototype =
+{
+ // PUBLIC API
+
+ /**
+* Maps key to a clone of the nsILocalFile value if value is non-null;
+* otherwise, removes any extant mapping for key.
+*
+* @param key : string
+* string to which a clone of value is mapped
+* @param value : nsILocalFile
+* the file to map to key, or null to remove a mapping
+*/
+ put: function(key, value)
+ {
+ if (value)
+ this._map[key] = value.clone();
+ else
+ delete this._map[key];
+ },
+
+ /**
+* Returns a clone of the nsILocalFile mapped to key, or null if no such
+* mapping exists.
+*
+* @param key : string
+* key to which the returned file maps
+* @returns nsILocalFile
+* a clone of the mapped file, or null if no mapping exists
+*/
+ get: function(key)
+ {
+ var val = this._map[key];
+ return val ? val.clone() : null;
+ }
+};
+
+
+// Response CONSTANTS
+
+// token = *<any CHAR except CTLs or separators>
+// CHAR = <any US-ASCII character (0-127)>
+// CTL = <any US-ASCII control character (0-31) and DEL (127)>
+// separators = "(" | ")" | "<" | ">" | "@"
+// | "," | ";" | ":" | "\" | <">
+// | "/" | "[" | "]" | "?" | "="
+// | "{" | "}" | SP | HT
+const IS_TOKEN_ARRAY =
+ [0, 0, 0, 0, 0, 0, 0, 0, // 0
+ 0, 0, 0, 0, 0, 0, 0, 0, // 8
+ 0, 0, 0, 0, 0, 0, 0, 0, // 16
+ 0, 0, 0, 0, 0, 0, 0, 0, // 24
+
+ 0, 1, 0, 1, 1, 1, 1, 1, // 32
+ 0, 0, 1, 1, 0, 1, 1, 0, // 40
+ 1, 1, 1, 1, 1, 1, 1, 1, // 48
+ 1, 1, 0, 0, 0, 0, 0, 0, // 56
+
+ 0, 1, 1, 1, 1, 1, 1, 1, // 64
+ 1, 1, 1, 1, 1, 1, 1, 1, // 72
+ 1, 1, 1, 1, 1, 1, 1, 1, // 80
+ 1, 1, 1, 0, 0, 0, 1, 1, // 88
+
+ 1, 1, 1, 1, 1, 1, 1, 1, // 96
+ 1, 1, 1, 1, 1, 1, 1, 1, // 104
+ 1, 1, 1, 1, 1, 1, 1, 1, // 112
+ 1, 1, 1, 0, 1, 0, 1]; // 120
+
+
+/**
+* Determines whether the given character code is a CTL.
+*
+* @param code : uint
+* the character code
+* @returns boolean
+* true if code is a CTL, false otherwise
+*/
+function isCTL(code)
+{
+ return (code >= 0 && code <= 31) || (code == 127);
+}
+
+/**
+* Represents a response to an HTTP request, encapsulating all details of that
+* response. This includes all headers, the HTTP version, status code and
+* explanation, and the entity itself.
+*
+* @param connection : Connection
+* the connection over which this response is to be written
+*/
+function Response(connection)
+{
+ /** The connection over which this response will be written. */
+ this._connection = connection;
+
+ /**
+* The HTTP version of this response; defaults to 1.1 if not set by the
+* handler.
+*/
+ this._httpVersion = nsHttpVersion.HTTP_1_1;
+
+ /**
+* The HTTP code of this response; defaults to 200.
+*/
+ this._httpCode = 200;
+
+ /**
+* The description of the HTTP code in this response; defaults to "OK".
+*/
+ this._httpDescription = "OK";
+
+ /**
+* An nsIHttpHeaders object in which the headers in this response should be
+* stored. This property is null after the status line and headers have been
+* written to the network, and it may be modified up until it is cleared,
+* except if this._finished is set first (in which case headers are written
+* asynchronously in response to a finish() call not preceded by
+* flushHeaders()).
+*/
+ this._headers = new nsHttpHeaders();
+
+ /**
+* Set to true when this response is ended (completely constructed if possible
+* and the connection closed); further actions on this will then fail.
+*/
+ this._ended = false;
+
+ /**
+* A stream used to hold data written to the body of this response.
+*/
+ this._bodyOutputStream = null;
+
+ /**
+* A stream containing all data that has been written to the body of this
+* response so far. (Async handlers make the data contained in this
+* unreliable as a way of determining content length in general, but auxiliary
+* saved information can sometimes be used to guarantee reliability.)
+*/
+ this._bodyInputStream = null;
+
+ /**
+* A stream copier which copies data to the network. It is initially null
+* until replaced with a copier for response headers; when headers have been
+* fully sent it is replaced with a copier for the response body, remaining
+* so for the duration of response processing.
+*/
+ this._asyncCopier = null;
+
+ /**
+* True if this response has been designated as being processed
+* asynchronously rather than for the duration of a single call to
+* nsIHttpRequestHandler.handle.
+*/
+ this._processAsync = false;
+
+ /**
+* True iff finish() has been called on this, signaling that no more changes
+* to this may be made.
+*/
+ this._finished = false;
+
+ /**
+* True iff powerSeized() has been called on this, signaling that this
+* response is to be handled manually by the response handler (which may then
+* send arbitrary data in response, even non-HTTP responses).
+*/
+ this._powerSeized = false;
+}
+Response.prototype =
+{
+ // PUBLIC CONSTRUCTION API
+
+ //
+ // see nsIHttpResponse.bodyOutputStream
+ //
+ get bodyOutputStream()
+ {
+ if (this._finished)
+ throw Cr.NS_ERROR_NOT_AVAILABLE;
+
+ if (!this._bodyOutputStream)
+ {
+ var pipe = new Pipe(true, false, Response.SEGMENT_SIZE, PR_UINT32_MAX,
+ null);
+ this._bodyOutputStream = pipe.outputStream;
+ this._bodyInputStream = pipe.inputStream;
+ if (this._processAsync || this._powerSeized)
+ this._startAsyncProcessor();
+ }
+
+ return this._bodyOutputStream;
+ },
+
+ //
+ // see nsIHttpResponse.write
+ //
+ write: function(data)
+ {
+ if (this._finished)
+ throw Cr.NS_ERROR_NOT_AVAILABLE;
+
+ var dataAsString = String(data);
+ this.bodyOutputStream.write(dataAsString, dataAsString.length);
+ },
+
+ //
+ // see nsIHttpResponse.setStatusLine
+ //
+ setStatusLine: function(httpVersion, code, description)
+ {
+ if (!this._headers || this._finished || this._powerSeized)
+ throw Cr.NS_ERROR_NOT_AVAILABLE;
+ this._ensureAlive();
+
+ if (!(code >= 0 && code < 1000))
+ throw Cr.NS_ERROR_INVALID_ARG;
+
+ try
+ {
+ var httpVer;
+ // avoid version construction for the most common cases
+ if (!httpVersion || httpVersion == "1.1")
+ httpVer = nsHttpVersion.HTTP_1_1;
+ else if (httpVersion == "1.0")
+ httpVer = nsHttpVersion.HTTP_1_0;
+ else
+ httpVer = new nsHttpVersion(httpVersion);
+ }
+ catch (e)
+ {
+ throw Cr.NS_ERROR_INVALID_ARG;
+ }
+
+ // Reason-Phrase = *<TEXT, excluding CR, LF>
+ // TEXT = <any OCTET except CTLs, but including LWS>
+ //
+ // XXX this ends up disallowing octets which aren't Unicode, I think -- not
+ // much to do if description is IDL'd as string
+ if (!description)
+ description = "";
+ for (var i = 0; i < description.length; i++)
+ if (isCTL(description.charCodeAt(i)) && description.charAt(i) != "\t")
+ throw Cr.NS_ERROR_INVALID_ARG;
+
+ // set the values only after validation to preserve atomicity
+ this._httpDescription = description;
+ this._httpCode = code;
+ this._httpVersion = httpVer;
+ },
+
+ //
+ // see nsIHttpResponse.setHeader
+ //
+ setHeader: function(name, value, merge)
+ {
+ if (!this._headers || this._finished || this._powerSeized)
+ throw Cr.NS_ERROR_NOT_AVAILABLE;
+ this._ensureAlive();
+
+ this._headers.setHeader(name, value, merge);
+ },
+
+ //
+ // see nsIHttpResponse.processAsync
+ //
+ processAsync: function()
+ {
+ if (this._finished)
+ throw Cr.NS_ERROR_UNEXPECTED;
+ if (this._powerSeized)
+ throw Cr.NS_ERROR_NOT_AVAILABLE;
+ if (this._processAsync)
+ return;
+ this._ensureAlive();
+
+ dumpn("*** processing connection " + this._connection.number + " async");
+ this._processAsync = true;
+
+ /*
+* Either the bodyOutputStream getter or this method is responsible for
+* starting the asynchronous processor and catching writes of data to the
+* response body of async responses as they happen, for the purpose of
+* forwarding those writes to the actual connection's output stream.
+* If bodyOutputStream is accessed first, calling this method will create
+* the processor (when it first is clear that body data is to be written
+* immediately, not buffered). If this method is called first, accessing
+* bodyOutputStream will create the processor. If only this method is
+* called, we'll write nothing, neither headers nor the nonexistent body,
+* until finish() is called. Since that delay is easily avoided by simply
+* getting bodyOutputStream or calling write(""), we don't worry about it.
+*/
+ if (this._bodyOutputStream && !this._asyncCopier)
+ this._startAsyncProcessor();
+ },
+
+ //
+ // see nsIHttpResponse.seizePower
+ //
+ seizePower: function()
+ {
+ if (this._processAsync)
+ throw Cr.NS_ERROR_NOT_AVAILABLE;
+ if (this._finished)
+ throw Cr.NS_ERROR_UNEXPECTED;
+ if (this._powerSeized)
+ return;
+ this._ensureAlive();
+
+ dumpn("*** forcefully seizing power over connection " +
+ this._connection.number + "...");
+
+ // Purge any already-written data without sending it. We could as easily
+ // swap out the streams entirely, but that makes it possible to acquire and
+ // unknowingly use a stale reference, so we require there only be one of
+ // each stream ever for any response to avoid this complication.
+ if (this._asyncCopier)
+ this._asyncCopier.cancel(Cr.NS_BINDING_ABORTED);
+ this._asyncCopier = null;
+ if (this._bodyOutputStream)
+ {
+ var input = new BinaryInputStream(this._bodyInputStream);
+ var avail;
+ while ((avail = input.available()) > 0)
+ input.readByteArray(avail);
+ }
+
+ this._powerSeized = true;
+ if (this._bodyOutputStream)
+ this._startAsyncProcessor();
+ },
+
+ //
+ // see nsIHttpResponse.finish
+ //
+ finish: function()
+ {
+ if (!this._processAsync && !this._powerSeized)
+ throw Cr.NS_ERROR_UNEXPECTED;
+ if (this._finished)
+ return;
+
+ dumpn("*** finishing connection " + this._connection.number);
+ this._startAsyncProcessor(); // in case bodyOutputStream was never accessed
+ if (this._bodyOutputStream)
+ this._bodyOutputStream.close();
+ this._finished = true;
+ },
+
+
+ // NSISUPPORTS
+
+ //
+ // see nsISupports.QueryInterface
+ //
+ QueryInterface: function(iid)
+ {
+ if (iid.equals(Ci.nsIHttpResponse) || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+
+ // POST-CONSTRUCTION API (not exposed externally)
+
+ /**
+* The HTTP version number of this, as a string (e.g. "1.1").
+*/
+ get httpVersion()
+ {
+ this._ensureAlive();
+ return this._httpVersion.toString();
+ },
+
+ /**
+* The HTTP status code of this response, as a string of three characters per
+* RFC 2616.
+*/
+ get httpCode()
+ {
+ this._ensureAlive();
+
+ var codeString = (this._httpCode < 10 ? "0" : "") +
+ (this._httpCode < 100 ? "0" : "") +
+ this._httpCode;
+ return codeString;
+ },
+
+ /**
+* The description of the HTTP status code of this response, or "" if none is
+* set.
+*/
+ get httpDescription()
+ {
+ this._ensureAlive();
+
+ return this._httpDescription;
+ },
+
+ /**
+* The headers in this response, as an nsHttpHeaders object.
+*/
+ get headers()
+ {
+ this._ensureAlive();
+
+ return this._headers;
+ },
+
+ //
+ // see nsHttpHeaders.getHeader
+ //
+ getHeader: function(name)
+ {
+ this._ensureAlive();
+
+ return this._headers.getHeader(name);
+ },
+
+ /**
+* Determines whether this response may be abandoned in favor of a newly
+* constructed response. A response may be abandoned only if it is not being
+* sent asynchronously and if raw control over it has not been taken from the
+* server.
+*
+* @returns boolean
+* true iff no data has been written to the network
+*/
+ partiallySent: function()
+ {
+ dumpn("*** partiallySent()");
+ return this._processAsync || this._powerSeized;
+ },
+
+ /**
+* If necessary, kicks off the remaining request processing needed to be done
+* after a request handler performs its initial work upon this response.
+*/
+ complete: function()
+ {
+ dumpn("*** complete()");
+ if (this._processAsync || this._powerSeized)
+ {
+ NS_ASSERT(this._processAsync ^ this._powerSeized,
+ "can't both send async and relinquish power");
+ return;
+ }
+
+ NS_ASSERT(!this.partiallySent(), "completing a partially-sent response?");
+
+ this._startAsyncProcessor();
+
+ // Now make sure we finish processing this request!
+ if (this._bodyOutputStream)
+ this._bodyOutputStream.close();
+ },
+
+ /**
+* Abruptly ends processing of this response, usually due to an error in an
+* incoming request but potentially due to a bad error handler. Since we
+* cannot handle the error in the usual way (giving an HTTP error page in
+* response) because data may already have been sent (or because the response
+* might be expected to have been generated asynchronously or completely from
+* scratch by the handler), we stop processing this response and abruptly
+* close the connection.
+*
+* @param e : Error
+* the exception which precipitated this abort, or null if no such exception
+* was generated
+*/
+ abort: function(e)
+ {
+ dumpn("*** abort(<" + e + ">)");
+
+ // This response will be ended by the processor if one was created.
+ var copier = this._asyncCopier;
+ if (copier)
+ {
+ // We dispatch asynchronously here so that any pending writes of data to
+ // the connection will be deterministically written. This makes it easier
+ // to specify exact behavior, and it makes observable behavior more
+ // predictable for clients. Note that the correctness of this depends on
+ // callbacks in response to _waitToReadData in WriteThroughCopier
+ // happening asynchronously with respect to the actual writing of data to
+ // bodyOutputStream, as they currently do; if they happened synchronously,
+ // an event which ran before this one could write more data to the
+ // response body before we get around to canceling the copier. We have
+ // tests for this in test_seizepower.js, however, and I can't think of a
+ // way to handle both cases without removing bodyOutputStream access and
+ // moving its effective write(data, length) method onto Response, which
+ // would be slower and require more code than this anyway.
+ gThreadManager.currentThread.dispatch({
+ run: function()
+ {
+ dumpn("*** canceling copy asynchronously...");
+ copier.cancel(Cr.NS_ERROR_UNEXPECTED);
+ }
+ }, Ci.nsIThread.DISPATCH_NORMAL);
+ }
+ else
+ {
+ this.end();
+ }
+ },
+
+ /**
+* Closes this response's network connection, marks the response as finished,
+* and notifies the server handler that the request is done being processed.
+*/
+ end: function()
+ {
+ NS_ASSERT(!this._ended, "ending this response twice?!?!");
+
+ this._connection.close();
+ if (this._bodyOutputStream)
+ this._bodyOutputStream.close();
+
+ this._finished = true;
+ this._ended = true;
+ },
+
+ // PRIVATE IMPLEMENTATION
+
+ /**
+* Sends the status line and headers of this response if they haven't been
+* sent and initiates the process of copying data written to this response's
+* body to the network.
+*/
+ _startAsyncProcessor: function()
+ {
+ dumpn("*** _startAsyncProcessor()");
+
+ // Handle cases where we're being called a second time. The former case
+ // happens when this is triggered both by complete() and by processAsync(),
+ // while the latter happens when processAsync() in conjunction with sent
+ // data causes abort() to be called.
+ if (this._asyncCopier || this._ended)
+ {
+ dumpn("*** ignoring second call to _startAsyncProcessor");
+ return;
+ }
+
+ // Send headers if they haven't been sent already and should be sent, then
+ // asynchronously continue to send the body.
+ if (this._headers && !this._powerSeized)
+ {
+ this._sendHeaders();
+ return;
+ }
+
+ this._headers = null;
+ this._sendBody();
+ },
+
+ /**
+* Signals that all modifications to the response status line and headers are
+* complete and then sends that data over the network to the client. Once
+* this method completes, a different response to the request that resulted
+* in this response cannot be sent -- the only possible action in case of
+* error is to abort the response and close the connection.
+*/
+ _sendHeaders: function()
+ {
+ dumpn("*** _sendHeaders()");
+
+ NS_ASSERT(this._headers);
+ NS_ASSERT(!this._powerSeized);
+
+ // request-line
+ var statusLine = "HTTP/" + this.httpVersion + " " +
+ this.httpCode + " " +
+ this.httpDescription + "\r\n";
+
+ // header post-processing
+
+ var headers = this._headers;
+ headers.setHeader("Connection", "close", false);
+ headers.setHeader("Server", "httpd.js", false);
+ if (!headers.hasHeader("Date"))
+ headers.setHeader("Date", toDateString(Date.now()), false);
+
+ // Any response not being processed asynchronously must have an associated
+ // Content-Length header for reasons of backwards compatibility with the
+ // initial server, which fully buffered every response before sending it.
+ // Beyond that, however, it's good to do this anyway because otherwise it's
+ // impossible to test behaviors that depend on the presence or absence of a
+ // Content-Length header.
+ if (!this._processAsync)
+ {
+ dumpn("*** non-async response, set Content-Length");
+
+ var bodyStream = this._bodyInputStream;
+ var avail = bodyStream ? bodyStream.available() : 0;
+
+ // XXX assumes stream will always report the full amount of data available
+ headers.setHeader("Content-Length", "" + avail, false);
+ }
+
+
+ // construct and send response
+ dumpn("*** header post-processing completed, sending response head...");
+
+ // request-line
+ var preambleData = [statusLine];
+
+ // headers
+ var headEnum = headers.enumerator;
+ while (headEnum.hasMoreElements())
+ {
+ var fieldName = headEnum.getNext()
+ .QueryInterface(Ci.nsISupportsString)
+ .data;
+ var values = headers.getHeaderValues(fieldName);
+ for (var i = 0, sz = values.length; i < sz; i++)
+ preambleData.push(fieldName + ": " + values[i] + "\r\n");
+ }
+
+ // end request-line/headers
+ preambleData.push("\r\n");
+
+ var preamble = preambleData.join("");
+
+ var responseHeadPipe = new Pipe(true, false, 0, PR_UINT32_MAX, null);
+ responseHeadPipe.outputStream.write(preamble, preamble.length);
+
+ var response = this;
+ var copyObserver =
+ {
+ onStartRequest: function(request, cx)
+ {
+ dumpn("*** preamble copying started");
+ },
+
+ onStopRequest: function(request, cx, statusCode)
+ {
+ dumpn("*** preamble copying complete " +
+ "[status=0x" + statusCode.toString(16) + "]");
+
+ if (!components.isSuccessCode(statusCode))
+ {
+ dumpn("!!! header copying problems: non-success statusCode, " +
+ "ending response");
+
+ response.end();
+ }
+ else
+ {
+ response._sendBody();
+ }
+ },
+
+ QueryInterface: function(aIID)
+ {
+ if (aIID.equals(Ci.nsIRequestObserver) || aIID.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+ };
+
+ var headerCopier = this._asyncCopier =
+ new WriteThroughCopier(responseHeadPipe.inputStream,
+ this._connection.output,
+ copyObserver, null);
+
+ responseHeadPipe.outputStream.close();
+
+ // Forbid setting any more headers or modifying the request line.
+ this._headers = null;
+ },
+
+ /**
+* Asynchronously writes the body of the response (or the entire response, if
+* seizePower() has been called) to the network.
+*/
+ _sendBody: function()
+ {
+ dumpn("*** _sendBody");
+
+ NS_ASSERT(!this._headers, "still have headers around but sending body?");
+
+ // If no body data was written, we're done
+ if (!this._bodyInputStream)
+ {
+ dumpn("*** empty body, response finished");
+ this.end();
+ return;
+ }
+
+ var response = this;
+ var copyObserver =
+ {
+ onStartRequest: function(request, context)
+ {
+ dumpn("*** onStartRequest");
+ },
+
+ onStopRequest: function(request, cx, statusCode)
+ {
+ dumpn("*** onStopRequest [status=0x" + statusCode.toString(16) + "]");
+
+ if (statusCode === Cr.NS_BINDING_ABORTED)
+ {
+ dumpn("*** terminating copy observer without ending the response");
+ }
+ else
+ {
+ if (!components.isSuccessCode(statusCode))
+ dumpn("*** WARNING: non-success statusCode in onStopRequest");
+
+ response.end();
+ }
+ },
+
+ QueryInterface: function(aIID)
+ {
+ if (aIID.equals(Ci.nsIRequestObserver) || aIID.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+ };
+
+ dumpn("*** starting async copier of body data...");
+ this._asyncCopier =
+ new WriteThroughCopier(this._bodyInputStream, this._connection.output,
+ copyObserver, null);
+ },
+
+ /** Ensures that this hasn't been ended. */
+ _ensureAlive: function()
+ {
+ NS_ASSERT(!this._ended, "not handling response lifetime correctly");
+ }
+};
+
+/**
+* Size of the segments in the buffer used in storing response data and writing
+* it to the socket.
+*/
+Response.SEGMENT_SIZE = 8192;
+
+/** Serves double duty in WriteThroughCopier implementation. */
+function notImplemented()
+{
+ throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/** Returns true iff the given exception represents stream closure. */
+function streamClosed(e)
+{
+ return e === Cr.NS_BASE_STREAM_CLOSED ||
+ (typeof e === "object" && e.result === Cr.NS_BASE_STREAM_CLOSED);
+}
+
+/** Returns true iff the given exception represents a blocked stream. */
+function wouldBlock(e)
+{
+ return e === Cr.NS_BASE_STREAM_WOULD_BLOCK ||
+ (typeof e === "object" && e.result === Cr.NS_BASE_STREAM_WOULD_BLOCK);
+}
+
+/**
+* Copies data from source to sink as it becomes available, when that data can
+* be written to sink without blocking.
+*
+* @param source : nsIAsyncInputStream
+* the stream from which data is to be read
+* @param sink : nsIAsyncOutputStream
+* the stream to which data is to be copied
+* @param observer : nsIRequestObserver
+* an observer which will be notified when the copy starts and finishes
+* @param context : nsISupports
+* context passed to observer when notified of start/stop
+* @throws NS_ERROR_NULL_POINTER
+* if source, sink, or observer are null
+*/
+function WriteThroughCopier(source, sink, observer, context)
+{
+ if (!source || !sink || !observer)
+ throw Cr.NS_ERROR_NULL_POINTER;
+
+ /** Stream from which data is being read. */
+ this._source = source;
+
+ /** Stream to which data is being written. */
+ this._sink = sink;
+
+ /** Observer watching this copy. */
+ this._observer = observer;
+
+ /** Context for the observer watching this. */
+ this._context = context;
+
+ /**
+* True iff this is currently being canceled (cancel has been called, the
+* callback may not yet have been made).
+*/
+ this._canceled = false;
+
+ /**
+* False until all data has been read from input and written to output, at
+* which point this copy is completed and cancel() is asynchronously called.
+*/
+ this._completed = false;
+
+ /** Required by nsIRequest, meaningless. */
+ this.loadFlags = 0;
+ /** Required by nsIRequest, meaningless. */
+ this.loadGroup = null;
+ /** Required by nsIRequest, meaningless. */
+ this.name = "response-body-copy";
+
+ /** Status of this request. */
+ this.status = Cr.NS_OK;
+
+ /** Arrays of byte strings waiting to be written to output. */
+ this._pendingData = [];
+
+ // start copying
+ try
+ {
+ observer.onStartRequest(this, context);
+ this._waitToReadData();
+ this._waitForSinkClosure();
+ }
+ catch (e)
+ {
+ dumpn("!!! error starting copy: " + e +
+ ("lineNumber" in e ? ", line " + e.lineNumber : ""));
+ dumpn(e.stack);
+ this.cancel(Cr.NS_ERROR_UNEXPECTED);
+ }
+}
+WriteThroughCopier.prototype =
+{
+ /* nsISupports implementation */
+
+ QueryInterface: function(iid)
+ {
+ if (iid.equals(Ci.nsIInputStreamCallback) ||
+ iid.equals(Ci.nsIOutputStreamCallback) ||
+ iid.equals(Ci.nsIRequest) ||
+ iid.equals(Ci.nsISupports))
+ {
+ return this;
+ }
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+
+ // NSIINPUTSTREAMCALLBACK
+
+ /**
+* Receives a more-data-in-input notification and writes the corresponding
+* data to the output.
+*
+* @param input : nsIAsyncInputStream
+* the input stream on whose data we have been waiting
+*/
+ onInputStreamReady: function(input)
+ {
+ if (this._source === null)
+ return;
+
+ dumpn("*** onInputStreamReady");
+
+ //
+ // Ordinarily we'll read a non-zero amount of data from input, queue it up
+ // to be written and then wait for further callbacks. The complications in
+ // this method are the cases where we deviate from that behavior when errors
+ // occur or when copying is drawing to a finish.
+ //
+ // The edge cases when reading data are:
+ //
+ // Zero data is read
+ // If zero data was read, we're at the end of available data, so we can
+ // should stop reading and move on to writing out what we have (or, if
+ // we've already done that, onto notifying of completion).
+ // A stream-closed exception is thrown
+ // This is effectively a less kind version of zero data being read; the
+ // only difference is that we notify of completion with that result
+ // rather than with NS_OK.
+ // Some other exception is thrown
+ // This is the least kind result. We don't know what happened, so we
+ // act as though the stream closed except that we notify of completion
+ // with the result NS_ERROR_UNEXPECTED.
+ //
+
+ var bytesWanted = 0, bytesConsumed = -1;
+ try
+ {
+ input = new BinaryInputStream(input);
+
+ bytesWanted = Math.min(input.available(), Response.SEGMENT_SIZE);
+ dumpn("*** input wanted: " + bytesWanted);
+
+ if (bytesWanted > 0)
+ {
+ var data = input.readByteArray(bytesWanted);
+ bytesConsumed = data.length;
+ this._pendingData.push(String.fromCharCode.apply(String, data));
+ }
+
+ dumpn("*** " + bytesConsumed + " bytes read");
+
+ // Handle the zero-data edge case in the same place as all other edge
+ // cases are handled.
+ if (bytesWanted === 0)
+ throw Cr.NS_BASE_STREAM_CLOSED;
+ }
+ catch (e)
+ {
+ if (streamClosed(e))
+ {
+ dumpn("*** input stream closed");
+ e = bytesWanted === 0 ? Cr.NS_OK : Cr.NS_ERROR_UNEXPECTED;
+ }
+ else
+ {
+ dumpn("!!! unexpected error reading from input, canceling: " + e);
+ e = Cr.NS_ERROR_UNEXPECTED;
+ }
+
+ this._doneReadingSource(e);
+ return;
+ }
+
+ var pendingData = this._pendingData;
+
+ NS_ASSERT(bytesConsumed > 0);
+ NS_ASSERT(pendingData.length > 0, "no pending data somehow?");
+ NS_ASSERT(pendingData[pendingData.length - 1].length > 0,
+ "buffered zero bytes of data?");
+
+ NS_ASSERT(this._source !== null);
+
+ // Reading has gone great, and we've gotten data to write now. What if we
+ // don't have a place to write that data, because output went away just
+ // before this read? Drop everything on the floor, including new data, and
+ // cancel at this point.
+ if (this._sink === null)
+ {
+ pendingData.length = 0;
+ this._doneReadingSource(Cr.NS_ERROR_UNEXPECTED);
+ return;
+ }
+
+ // Okay, we've read the data, and we know we have a place to write it. We
+ // need to queue up the data to be written, but *only* if none is queued
+ // already -- if data's already queued, the code that actually writes the
+ // data will make sure to wait on unconsumed pending data.
+ try
+ {
+ if (pendingData.length === 1)
+ this._waitToWriteData();
+ }
+ catch (e)
+ {
+ dumpn("!!! error waiting to write data just read, swallowing and " +
+ "writing only what we already have: " + e);
+ this._doneWritingToSink(Cr.NS_ERROR_UNEXPECTED);
+ return;
+ }
+
+ // Whee! We successfully read some data, and it's successfully queued up to
+ // be written. All that remains now is to wait for more data to read.
+ try
+ {
+ this._waitToReadData();
+ }
+ catch (e)
+ {
+ dumpn("!!! error waiting to read more data: " + e);
+ this._doneReadingSource(Cr.NS_ERROR_UNEXPECTED);
+ }
+ },
+
+
+ // NSIOUTPUTSTREAMCALLBACK
+
+ /**
+* Callback when data may be written to the output stream without blocking, or
+* when the output stream has been closed.
+*
+* @param output : nsIAsyncOutputStream
+* the output stream on whose writability we've been waiting, also known as
+* this._sink
+*/
+ onOutputStreamReady: function(output)
+ {
+ if (this._sink === null)
+ return;
+
+ dumpn("*** onOutputStreamReady");
+
+ var pendingData = this._pendingData;
+ if (pendingData.length === 0)
+ {
+ // There's no pending data to write. The only way this can happen is if
+ // we're waiting on the output stream's closure, so we can respond to a
+ // copying failure as quickly as possible (rather than waiting for data to
+ // be available to read and then fail to be copied). Therefore, we must
+ // be done now -- don't bother to attempt to write anything and wrap
+ // things up.
+ dumpn("!!! output stream closed prematurely, ending copy");
+
+ this._doneWritingToSink(Cr.NS_ERROR_UNEXPECTED);
+ return;
+ }
+
+
+ NS_ASSERT(pendingData[0].length > 0, "queued up an empty quantum?");
+
+ //
+ // Write out the first pending quantum of data. The possible errors here
+ // are:
+ //
+ // The write might fail because we can't write that much data
+ // Okay, we've written what we can now, so re-queue what's left and
+ // finish writing it out later.
+ // The write failed because the stream was closed
+ // Discard pending data that we can no longer write, stop reading, and
+ // signal that copying finished.
+ // Some other error occurred.
+ // Same as if the stream were closed, but notify with the status
+ // NS_ERROR_UNEXPECTED so the observer knows something was wonky.
+ //
+
+ try
+ {
+ var quantum = pendingData[0];
+
+ // XXX |quantum| isn't guaranteed to be ASCII, so we're relying on
+ // undefined behavior! We're only using this because writeByteArray
+ // is unusably broken for asynchronous output streams; see bug 532834
+ // for details.
+ var bytesWritten = output.write(quantum, quantum.length);
+ if (bytesWritten === quantum.length)
+ pendingData.shift();
+ else
+ pendingData[0] = quantum.substring(bytesWritten);
+
+ dumpn("*** wrote " + bytesWritten + " bytes of data");
+ }
+ catch (e)
+ {
+ if (wouldBlock(e))
+ {
+ NS_ASSERT(pendingData.length > 0,
+ "stream-blocking exception with no data to write?");
+ NS_ASSERT(pendingData[0].length > 0,
+ "stream-blocking exception with empty quantum?");
+ this._waitToWriteData();
+ return;
+ }
+
+ if (streamClosed(e))
+ dumpn("!!! output stream prematurely closed, signaling error...");
+ else
+ dumpn("!!! unknown error: " + e + ", quantum=" + quantum);
+
+ this._doneWritingToSink(Cr.NS_ERROR_UNEXPECTED);
+ return;
+ }
+
+ // The day is ours! Quantum written, now let's see if we have more data
+ // still to write.
+ try
+ {
+ if (pendingData.length > 0)
+ {
+ this._waitToWriteData();
+ return;
+ }
+ }
+ catch (e)
+ {
+ dumpn("!!! unexpected error waiting to write pending data: " + e);
+ this._doneWritingToSink(Cr.NS_ERROR_UNEXPECTED);
+ return;
+ }
+
+ // Okay, we have no more pending data to write -- but might we get more in
+ // the future?
+ if (this._source !== null)
+ {
+ /*
+* If we might, then wait for the output stream to be closed. (We wait
+* only for closure because we have no data to write -- and if we waited
+* for a specific amount of data, we would get repeatedly notified for no
+* reason if over time the output stream permitted more and more data to
+* be written to it without blocking.)
+*/
+ this._waitForSinkClosure();
+ }
+ else
+ {
+ /*
+* On the other hand, if we can't have more data because the input
+* stream's gone away, then it's time to notify of copy completion.
+* Victory!
+*/
+ this._sink = null;
+ this._cancelOrDispatchCancelCallback(Cr.NS_OK);
+ }
+ },
+
+
+ // NSIREQUEST
+
+ /** Returns true if the cancel observer hasn't been notified yet. */
+ isPending: function()
+ {
+ return !this._completed;
+ },
+
+ /** Not implemented, don't use! */
+ suspend: notImplemented,
+ /** Not implemented, don't use! */
+ resume: notImplemented,
+
+ /**
+* Cancels data reading from input, asynchronously writes out any pending
+* data, and causes the observer to be notified with the given error code when
+* all writing has finished.
+*
+* @param status : nsresult
+* the status to pass to the observer when data copying has been canceled
+*/
+ cancel: function(status)
+ {
+ dumpn("*** cancel(" + status.toString(16) + ")");
+
+ if (this._canceled)
+ {
+ dumpn("*** suppressing a late cancel");
+ return;
+ }
+
+ this._canceled = true;
+ this.status = status;
+
+ // We could be in the middle of absolutely anything at this point. Both
+ // input and output might still be around, we might have pending data to
+ // write, and in general we know nothing about the state of the world. We
+ // therefore must assume everything's in progress and take everything to its
+ // final steady state (or so far as it can go before we need to finish
+ // writing out remaining data).
+
+ this._doneReadingSource(status);
+ },
+
+
+ // PRIVATE IMPLEMENTATION
+
+ /**
+* Stop reading input if we haven't already done so, passing e as the status
+* when closing the stream, and kick off a copy-completion notice if no more
+* data remains to be written.
+*
+* @param e : nsresult
+* the status to be used when closing the input stream
+*/
+ _doneReadingSource: function(e)
+ {
+ dumpn("*** _doneReadingSource(0x" + e.toString(16) + ")");
+
+ this._finishSource(e);
+ if (this._pendingData.length === 0)
+ this._sink = null;
+ else
+ NS_ASSERT(this._sink !== null, "null output?");
+
+ // If we've written out all data read up to this point, then it's time to
+ // signal completion.
+ if (this._sink === null)
+ {
+ NS_ASSERT(this._pendingData.length === 0, "pending data still?");
+ this._cancelOrDispatchCancelCallback(e);
+ }
+ },
+
+ /**
+* Stop writing output if we haven't already done so, discard any data that
+* remained to be sent, close off input if it wasn't already closed, and kick
+* off a copy-completion notice.
+*
+* @param e : nsresult
+* the status to be used when closing input if it wasn't already closed
+*/
+ _doneWritingToSink: function(e)
+ {
+ dumpn("*** _doneWritingToSink(0x" + e.toString(16) + ")");
+
+ this._pendingData.length = 0;
+ this._sink = null;
+ this._doneReadingSource(e);
+ },
+
+ /**
+* Completes processing of this copy: either by canceling the copy if it
+* hasn't already been canceled using the provided status, or by dispatching
+* the cancel callback event (with the originally provided status, of course)
+* if it already has been canceled.
+*
+* @param status : nsresult
+* the status code to use to cancel this, if this hasn't already been
+* canceled
+*/
+ _cancelOrDispatchCancelCallback: function(status)
+ {
+ dumpn("*** _cancelOrDispatchCancelCallback(" + status + ")");
+
+ NS_ASSERT(this._source === null, "should have finished input");
+ NS_ASSERT(this._sink === null, "should have finished output");
+ NS_ASSERT(this._pendingData.length === 0, "should have no pending data");
+
+ if (!this._canceled)
+ {
+ this.cancel(status);
+ return;
+ }
+
+ var self = this;
+ var event =
+ {
+ run: function()
+ {
+ dumpn("*** onStopRequest async callback");
+
+ self._completed = true;
+ try
+ {
+ self._observer.onStopRequest(self, self._context, self.status);
+ }
+ catch (e)
+ {
+ NS_ASSERT(false,
+ "how are we throwing an exception here? we control " +
+ "all the callers! " + e);
+ }
+ }
+ };
+
+ gThreadManager.currentThread.dispatch(event, Ci.nsIThread.DISPATCH_NORMAL);
+ },
+
+ /**
+* Kicks off another wait for more data to be available from the input stream.
+*/
+ _waitToReadData: function()
+ {
+ dumpn("*** _waitToReadData");
+ this._source.asyncWait(this, 0, Response.SEGMENT_SIZE,
+ gThreadManager.mainThread);
+ },
+
+ /**
+* Kicks off another wait until data can be written to the output stream.
+*/
+ _waitToWriteData: function()
+ {
+ dumpn("*** _waitToWriteData");
+
+ var pendingData = this._pendingData;
+ NS_ASSERT(pendingData.length > 0, "no pending data to write?");
+ NS_ASSERT(pendingData[0].length > 0, "buffered an empty write?");
+
+ this._sink.asyncWait(this, 0, pendingData[0].length,
+ gThreadManager.mainThread);
+ },
+
+ /**
+* Kicks off a wait for the sink to which data is being copied to be closed.
+* We wait for stream closure when we don't have any data to be copied, rather
+* than waiting to write a specific amount of data. We can't wait to write
+* data because the sink might be infinitely writable, and if no data appears
+* in the source for a long time we might have to spin quite a bit waiting to
+* write, waiting to write again, &c. Waiting on stream closure instead means
+* we'll get just one notification if the sink dies. Note that when data
+* starts arriving from the sink we'll resume waiting for data to be written,
+* dropping this closure-only callback entirely.
+*/
+ _waitForSinkClosure: function()
+ {
+ dumpn("*** _waitForSinkClosure");
+
+ this._sink.asyncWait(this, Ci.nsIAsyncOutputStream.WAIT_CLOSURE_ONLY, 0,
+ gThreadManager.mainThread);
+ },
+
+ /**
+* Closes input with the given status, if it hasn't already been closed;
+* otherwise a no-op.
+*
+* @param status : nsresult
+* status code use to close the source stream if necessary
+*/
+ _finishSource: function(status)
+ {
+ dumpn("*** _finishSource(" + status.toString(16) + ")");
+
+ if (this._source !== null)
+ {
+ this._source.closeWithStatus(status);
+ this._source = null;
+ }
+ }
+};
+
+
+/**
+* A container for utility functions used with HTTP headers.
+*/
+const headerUtils =
+{
+ /**
+* Normalizes fieldName (by converting it to lowercase) and ensures it is a
+* valid header field name (although not necessarily one specified in RFC
+* 2616).
+*
+* @throws NS_ERROR_INVALID_ARG
+* if fieldName does not match the field-name production in RFC 2616
+* @returns string
+* fieldName converted to lowercase if it is a valid header, for characters
+* where case conversion is possible
+*/
+ normalizeFieldName: function(fieldName)
+ {
+ if (fieldName == "")
+ throw Cr.NS_ERROR_INVALID_ARG;
+
+ for (var i = 0, sz = fieldName.length; i < sz; i++)
+ {
+ if (!IS_TOKEN_ARRAY[fieldName.charCodeAt(i)])
+ {
+ dumpn(fieldName + " is not a valid header field name!");
+ throw Cr.NS_ERROR_INVALID_ARG;
+ }
+ }
+
+ return fieldName.toLowerCase();
+ },
+
+ /**
+* Ensures that fieldValue is a valid header field value (although not
+* necessarily as specified in RFC 2616 if the corresponding field name is
+* part of the HTTP protocol), normalizes the value if it is, and
+* returns the normalized value.
+*
+* @param fieldValue : string
+* a value to be normalized as an HTTP header field value
+* @throws NS_ERROR_INVALID_ARG
+* if fieldValue does not match the field-value production in RFC 2616
+* @returns string
+* fieldValue as a normalized HTTP header field value
+*/
+ normalizeFieldValue: function(fieldValue)
+ {
+ // field-value = *( field-content | LWS )
+ // field-content = <the OCTETs making up the field-value
+ // and consisting of either *TEXT or combinations
+ // of token, separators, and quoted-string>
+ // TEXT = <any OCTET except CTLs,
+ // but including LWS>
+ // LWS = [CRLF] 1*( SP | HT )
+ //
+ // quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
+ // qdtext = <any TEXT except <">>
+ // quoted-pair = "\" CHAR
+ // CHAR = <any US-ASCII character (octets 0 - 127)>
+
+ // Any LWS that occurs between field-content MAY be replaced with a single
+ // SP before interpreting the field value or forwarding the message
+ // downstream (section 4.2); we replace 1*LWS with a single SP
+ var val = fieldValue.replace(/(?:(?:\r\n)?[ \t]+)+/g, " ");
+
+ // remove leading/trailing LWS (which has been converted to SP)
+ val = val.replace(/^ +/, "").replace(/ +$/, "");
+
+ // that should have taken care of all CTLs, so val should contain no CTLs
+ for (var i = 0, len = val.length; i < len; i++)
+ if (isCTL(val.charCodeAt(i)))
+ throw Cr.NS_ERROR_INVALID_ARG;
+
+ // XXX disallows quoted-pair where CHAR is a CTL -- will not invalidly
+ // normalize, however, so this can be construed as a tightening of the
+ // spec and not entirely as a bug
+ return val;
+ }
+};
+
+
+
+/**
+* Converts the given string into a string which is safe for use in an HTML
+* context.
+*
+* @param str : string
+* the string to make HTML-safe
+* @returns string
+* an HTML-safe version of str
+*/
+function htmlEscape(str)
+{
+ // this is naive, but it'll work
+ var s = "";
+ for (var i = 0; i < str.length; i++)
+ s += "&#" + str.charCodeAt(i) + ";";
+ return s;
+}
+
+
+/**
+* Constructs an object representing an HTTP version (see section 3.1).
+*
+* @param versionString
+* a string of the form "#.#", where # is an non-negative decimal integer with
+* or without leading zeros
+* @throws
+* if versionString does not specify a valid HTTP version number
+*/
+function nsHttpVersion(versionString)
+{
+ var matches = /^(\d+)\.(\d+)$/.exec(versionString);
+ if (!matches)
+ throw "Not a valid HTTP version!";
+
+ /** The major version number of this, as a number. */
+ this.major = parseInt(matches[1], 10);
+
+ /** The minor version number of this, as a number. */
+ this.minor = parseInt(matches[2], 10);
+
+ if (isNaN(this.major) || isNaN(this.minor) ||
+ this.major < 0 || this.minor < 0)
+ throw "Not a valid HTTP version!";
+}
+nsHttpVersion.prototype =
+{
+ /**
+* Returns the standard string representation of the HTTP version represented
+* by this (e.g., "1.1").
+*/
+ toString: function ()
+ {
+ return this.major + "." + this.minor;
+ },
+
+ /**
+* Returns true if this represents the same HTTP version as otherVersion,
+* false otherwise.
+*
+* @param otherVersion : nsHttpVersion
+* the version to compare against this
+*/
+ equals: function (otherVersion)
+ {
+ return this.major == otherVersion.major &&
+ this.minor == otherVersion.minor;
+ },
+
+ /** True if this >= otherVersion, false otherwise. */
+ atLeast: function(otherVersion)
+ {
+ return this.major > otherVersion.major ||
+ (this.major == otherVersion.major &&
+ this.minor >= otherVersion.minor);
+ }
+};
+
+nsHttpVersion.HTTP_1_0 = new nsHttpVersion("1.0");
+nsHttpVersion.HTTP_1_1 = new nsHttpVersion("1.1");
+
+
+/**
+* An object which stores HTTP headers for a request or response.
+*
+* Note that since headers are case-insensitive, this object converts headers to
+* lowercase before storing them. This allows the getHeader and hasHeader
+* methods to work correctly for any case of a header, but it means that the
+* values returned by .enumerator may not be equal case-sensitively to the
+* values passed to setHeader when adding headers to this.
+*/
+function nsHttpHeaders()
+{
+ /**
+* A hash of headers, with header field names as the keys and header field
+* values as the values. Header field names are case-insensitive, but upon
+* insertion here they are converted to lowercase. Header field values are
+* normalized upon insertion to contain no leading or trailing whitespace.
+*
+* Note also that per RFC 2616, section 4.2, two headers with the same name in
+* a message may be treated as one header with the same field name and a field
+* value consisting of the separate field values joined together with a "," in
+* their original order. This hash stores multiple headers with the same name
+* in this manner.
+*/
+ this._headers = {};
+}
+nsHttpHeaders.prototype =
+{
+ /**
+* Sets the header represented by name and value in this.
+*
+* @param name : string
+* the header name
+* @param value : string
+* the header value
+* @throws NS_ERROR_INVALID_ARG
+* if name or value is not a valid header component
+*/
+ setHeader: function(fieldName, fieldValue, merge)
+ {
+ var name = headerUtils.normalizeFieldName(fieldName);
+ var value = headerUtils.normalizeFieldValue(fieldValue);
+
+ // The following three headers are stored as arrays because their real-world
+ // syntax prevents joining individual headers into a single header using
+ // ",". See also <http://hg.mozilla.org/mozilla-central/diff/9b2a99adc05e/netwerk/protocol/http/src/nsHttpHeaderArray.cpp#l77>
+ if (merge && name in this._headers)
+ {
+ if (name === "www-authenticate" ||
+ name === "proxy-authenticate" ||
+ name === "set-cookie")
+ {
+ this._headers[name].push(value);
+ }
+ else
+ {
+ this._headers[name][0] += "," + value;
+ NS_ASSERT(this._headers[name].length === 1,
+ "how'd a non-special header have multiple values?")
+ }
+ }
+ else
+ {
+ this._headers[name] = [value];
+ }
+ },
+
+ /**
+* Returns the value for the header specified by this.
+*
+* @throws NS_ERROR_INVALID_ARG
+* if fieldName does not constitute a valid header field name
+* @throws NS_ERROR_NOT_AVAILABLE
+* if the given header does not exist in this
+* @returns string
+* the field value for the given header, possibly with non-semantic changes
+* (i.e., leading/trailing whitespace stripped, whitespace runs replaced
+* with spaces, etc.) at the option of the implementation; multiple
+* instances of the header will be combined with a comma, except for
+* the three headers noted in the description of getHeaderValues
+*/
+ getHeader: function(fieldName)
+ {
+ return this.getHeaderValues(fieldName).join("\n");
+ },
+
+ /**
+* Returns the value for the header specified by fieldName as an array.
+*
+* @throws NS_ERROR_INVALID_ARG
+* if fieldName does not constitute a valid header field name
+* @throws NS_ERROR_NOT_AVAILABLE
+* if the given header does not exist in this
+* @returns [string]
+* an array of all the header values in this for the given
+* header name. Header values will generally be collapsed
+* into a single header by joining all header values together
+* with commas, but certain headers (Proxy-Authenticate,
+* WWW-Authenticate, and Set-Cookie) violate the HTTP spec
+* and cannot be collapsed in this manner. For these headers
+* only, the returned array may contain multiple elements if
+* that header has been added more than once.
+*/
+ getHeaderValues: function(fieldName)
+ {
+ var name = headerUtils.normalizeFieldName(fieldName);
+
+ if (name in this._headers)
+ return this._headers[name];
+ else
+ throw Cr.NS_ERROR_NOT_AVAILABLE;
+ },
+
+ /**
+* Returns true if a header with the given field name exists in this, false
+* otherwise.
+*
+* @param fieldName : string
+* the field name whose existence is to be determined in this
+* @throws NS_ERROR_INVALID_ARG
+* if fieldName does not constitute a valid header field name
+* @returns boolean
+* true if the header's present, false otherwise
+*/
+ hasHeader: function(fieldName)
+ {
+ var name = headerUtils.normalizeFieldName(fieldName);
+ return (name in this._headers);
+ },
+
+ /**
+* Returns a new enumerator over the field names of the headers in this, as
+* nsISupportsStrings. The names returned will be in lowercase, regardless of
+* how they were input using setHeader (header names are case-insensitive per
+* RFC 2616).
+*/
+ get enumerator()
+ {
+ var headers = [];
+ for (var i in this._headers)
+ {
+ var supports = new SupportsString();
+ supports.data = i;
+ headers.push(supports);
+ }
+
+ return new nsSimpleEnumerator(headers);
+ }
+};
+
+
+/**
+* Constructs an nsISimpleEnumerator for the given array of items.
+*
+* @param items : Array
+* the items, which must all implement nsISupports
+*/
+function nsSimpleEnumerator(items)
+{
+ this._items = items;
+ this._nextIndex = 0;
+}
+nsSimpleEnumerator.prototype =
+{
+ hasMoreElements: function()
+ {
+ return this._nextIndex < this._items.length;
+ },
+ getNext: function()
+ {
+ if (!this.hasMoreElements())
+ throw Cr.NS_ERROR_NOT_AVAILABLE;
+
+ return this._items[this._nextIndex++];
+ },
+ QueryInterface: function(aIID)
+ {
+ if (Ci.nsISimpleEnumerator.equals(aIID) ||
+ Ci.nsISupports.equals(aIID))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+};
+
+
+/**
+* A representation of the data in an HTTP request.
+*
+* @param port : uint
+* the port on which the server receiving this request runs
+*/
+function Request(port)
+{
+ /** Method of this request, e.g. GET or POST. */
+ this._method = "";
+
+ /** Path of the requested resource; empty paths are converted to '/'. */
+ this._path = "";
+
+ /** Query string, if any, associated with this request (not including '?'). */
+ this._queryString = "";
+
+ /** Scheme of requested resource, usually http, always lowercase. */
+ this._scheme = "http";
+
+ /** Hostname on which the requested resource resides. */
+ this._host = undefined;
+
+ /** Port number over which the request was received. */
+ this._port = port;
+
+ var bodyPipe = new Pipe(false, false, 0, PR_UINT32_MAX, null);
+
+ /** Stream from which data in this request's body may be read. */
+ this._bodyInputStream = bodyPipe.inputStream;
+
+ /** Stream to which data in this request's body is written. */
+ this._bodyOutputStream = bodyPipe.outputStream;
+
+ /**
+* The headers in this request.
+*/
+ this._headers = new nsHttpHeaders();
+
+ /**
+* For the addition of ad-hoc properties and new functionality without having
+* to change nsIHttpRequest every time; currently lazily created, as its only
+* use is in directory listings.
+*/
+ this._bag = null;
+}
+Request.prototype =
+{
+ // SERVER METADATA
+
+ //
+ // see nsIHttpRequest.scheme
+ //
+ get scheme()
+ {
+ return this._scheme;
+ },
+
+ //
+ // see nsIHttpRequest.host
+ //
+ get host()
+ {
+ return this._host;
+ },
+
+ //
+ // see nsIHttpRequest.port
+ //
+ get port()
+ {
+ return this._port;
+ },
+
+ // REQUEST LINE
+
+ //
+ // see nsIHttpRequest.method
+ //
+ get method()
+ {
+ return this._method;
+ },
+
+ //
+ // see nsIHttpRequest.httpVersion
+ //
+ get httpVersion()
+ {
+ return this._httpVersion.toString();
+ },
+
+ //
+ // see nsIHttpRequest.path
+ //
+ get path()
+ {
+ return this._path;
+ },
+
+ //
+ // see nsIHttpRequest.queryString
+ //
+ get queryString()
+ {
+ return this._queryString;
+ },
+
+ // HEADERS
+
+ //
+ // see nsIHttpRequest.getHeader
+ //
+ getHeader: function(name)
+ {
+ return this._headers.getHeader(name);
+ },
+
+ //
+ // see nsIHttpRequest.hasHeader
+ //
+ hasHeader: function(name)
+ {
+ return this._headers.hasHeader(name);
+ },
+
+ //
+ // see nsIHttpRequest.headers
+ //
+ get headers()
+ {
+ return this._headers.enumerator;
+ },
+
+ //
+ // see nsIPropertyBag.enumerator
+ //
+ get enumerator()
+ {
+ this._ensurePropertyBag();
+ return this._bag.enumerator;
+ },
+
+ //
+ // see nsIHttpRequest.headers
+ //
+ get bodyInputStream()
+ {
+ return this._bodyInputStream;
+ },
+
+ //
+ // see nsIPropertyBag.getProperty
+ //
+ getProperty: function(name)
+ {
+ this._ensurePropertyBag();
+ return this._bag.getProperty(name);
+ },
+
+
+ // NSISUPPORTS
+
+ //
+ // see nsISupports.QueryInterface
+ //
+ QueryInterface: function(iid)
+ {
+ if (iid.equals(Ci.nsIHttpRequest) || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+
+ // PRIVATE IMPLEMENTATION
+
+ /** Ensures a property bag has been created for ad-hoc behaviors. */
+ _ensurePropertyBag: function()
+ {
+ if (!this._bag)
+ this._bag = new WritablePropertyBag();
+ }
+};
+
+
+// XPCOM trappings
+if ("XPCOMUtils" in this && // Firefox 3.6 doesn't load XPCOMUtils in this scope for some reason...
+ "generateNSGetFactory" in XPCOMUtils) {
+ var NSGetFactory = XPCOMUtils.generateNSGetFactory([nsHttpServer]);
+}
+
+
+
+/**
+* Creates a new HTTP server listening for loopback traffic on the given port,
+* starts it, and runs the server until the server processes a shutdown request,
+* spinning an event loop so that events posted by the server's socket are
+* processed.
+*
+* This method is primarily intended for use in running this script from within
+* xpcshell and running a functional HTTP server without having to deal with
+* non-essential details.
+*
+* Note that running multiple servers using variants of this method probably
+* doesn't work, simply due to how the internal event loop is spun and stopped.
+*
+* @note
+* This method only works with Mozilla 1.9 (i.e., Firefox 3 or trunk code);
+* you should use this server as a component in Mozilla 1.8.
+* @param port
+* the port on which the server will run, or -1 if there exists no preference
+* for a specific port; note that attempting to use some values for this
+* parameter (particularly those below 1024) may cause this method to throw or
+* may result in the server being prematurely shut down
+* @param basePath
+* a local directory from which requests will be served (i.e., if this is
+* "/home/jwalden/" then a request to /index.html will load
+* /home/jwalden/index.html); if this is omitted, only the default URLs in
+* this server implementation will be functional
+*/
+function server(port, basePath)
+{
+ if (basePath)
+ {
+ var lp = Cc["@mozilla.org/file/local;1"]
+ .createInstance(Ci.nsILocalFile);
+ lp.initWithPath(basePath);
+ }
+
+ // if you're running this, you probably want to see debugging info
+ DEBUG = true;
+
+ var srv = new nsHttpServer();
+ if (lp)
+ srv.registerDirectory("/", lp);
+ srv.registerContentType("sjs", SJS_TYPE);
+ srv.identity.setPrimary("http", "localhost", port);
+ srv.start(port);
+
+ var thread = gThreadManager.currentThread;
+ while (!srv.isStopped())
+ thread.processNextEvent(true);
+
+ // get rid of any pending requests
+ while (thread.hasPendingEvents())
+ thread.processNextEvent(true);
+
+ DEBUG = false;
+}
+
+function startServerAsync(port, basePath)
+{
+ if (basePath)
+ {
+ var lp = Cc["@mozilla.org/file/local;1"]
+ .createInstance(Ci.nsILocalFile);
+ lp.initWithPath(basePath);
+ }
+
+ var srv = new nsHttpServer();
+ if (lp)
+ srv.registerDirectory("/", lp);
+ srv.registerContentType("sjs", "sjs");
+ srv.identity.setPrimary("http", "localhost", port);
+ srv.start(port);
+ return srv;
+}
+
+exports.nsHttpServer = nsHttpServer;
+exports.ScriptableInputStream = ScriptableInputStream;
+exports.server = server;
+exports.startServerAsync = startServerAsync;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/keyboard/hotkeys.js b/tools/addon-sdk-1.7/packages/api-utils/lib/keyboard/hotkeys.js
new file mode 100644
index 0000000..bb296ed
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/keyboard/hotkeys.js
@@ -0,0 +1,107 @@
+/* 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/. */
+
+"use strict";
+
+const { observer: keyboardObserver } = require("./observer");
+const { getKeyForCode, normalize, isFunctionKey,
+ MODIFIERS } = require("./utils");
+
+/**
+ * Register a global `hotkey` that executes `listener` when the key combination
+ * in `hotkey` is pressed. If more then one `listener` is registered on the same
+ * key combination only last one will be executed.
+ *
+ * @param {string} hotkey
+ * Key combination in the format of 'modifier key'.
+ *
+ * Examples:
+ *
+ * "accel s"
+ * "meta shift i"
+ * "control alt d"
+ *
+ * Modifier keynames:
+ *
+ * - **shift**: The Shift key.
+ * - **alt**: The Alt key. On the Macintosh, this is the Option key. On
+ * Macintosh this can only be used in conjunction with another modifier,
+ * since `Alt+Letter` combinations are reserved for entering special
+ * characters in text.
+ * - **meta**: The Meta key. On the Macintosh, this is the Command key.
+ * - **control**: The Control key.
+ * - **accel**: The key used for keyboard shortcuts on the user's platform,
+ * which is Control on Windows and Linux, and Command on Mac. Usually, this
+ * would be the value you would use.
+ *
+ * @param {function} listener
+ * Function to execute when the `hotkey` is executed.
+ */
+exports.register = function register(hotkey, listener) {
+ hotkey = normalize(hotkey);
+ hotkeys[hotkey] = listener;
+};
+
+/**
+ * Unregister a global `hotkey`. If passed `listener` is not the one registered
+ * for the given `hotkey`, the call to this function will be ignored.
+ *
+ * @param {string} hotkey
+ * Key combination in the format of 'modifier key'.
+ * @param {function} listener
+ * Function that will be invoked when the `hotkey` is pressed.
+ */
+exports.unregister = function unregister(hotkey, listener) {
+ hotkey = normalize(hotkey);
+ if (hotkeys[hotkey] === listener)
+ delete hotkeys[hotkey];
+};
+
+/**
+ * Map of hotkeys and associated functions.
+ */
+const hotkeys = exports.hotkeys = {};
+
+keyboardObserver.on("keydown", function onKeypress(event, window) {
+ let key, modifiers = [];
+ let isChar = "isChar" in event && event.isChar;
+ let which = "which" in event ? event.which : null;
+ let keyCode = "keyCode" in event ? event.keyCode : null;
+
+ if ("shiftKey" in event && event.shiftKey)
+ modifiers.push("shift");
+ if ("altKey" in event && event.altKey)
+ modifiers.push("alt");
+ if ("ctrlKey" in event && event.ctrlKey)
+ modifiers.push("control");
+ if ("metaKey" in event && event.metaKey)
+ modifiers.push("meta");
+
+ // If it's not a printable character then we fall back to a human readable
+ // equivalent of one of the following constants.
+ // http://mxr.mozilla.org/mozilla-central/source/dom/interfaces/events/nsIDOMKeyEvent.idl
+ key = getKeyForCode(keyCode);
+
+ // If only non-function (f1 - f24) key or only modifiers are pressed we don't
+ // have a valid combination so we return immediately (Also, sometimes
+ // `keyCode` may be one for the modifier which means we do not have a
+ // modifier).
+ if (!key || (!isFunctionKey(key) && !modifiers.length) || key in MODIFIERS)
+ return;
+
+ let combination = normalize({ key: key, modifiers: modifiers });
+ let hotkey = hotkeys[combination];
+
+ if (hotkey) {
+ try {
+ hotkey();
+ } catch (exception) {
+ console.exception(exception);
+ } finally {
+ // Work around bug 582052 by preventing the (nonexistent) default action.
+ event.preventDefault();
+ }
+ }
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/keyboard/observer.js b/tools/addon-sdk-1.7/packages/api-utils/lib/keyboard/observer.js
new file mode 100644
index 0000000..dd24865
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/keyboard/observer.js
@@ -0,0 +1,53 @@
+/* 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/. */
+
+"use strict";
+
+const { Trait } = require("../light-traits");
+const { EventEmitterTrait: EventEmitter } = require("../events");
+const { DOMEventAssembler } = require("../events/assembler");
+const { browserWindowIterator, isBrowser } = require('../window-utils');
+const { observer: windowObserver } = require("../windows/observer");
+
+// Event emitter objects used to register listeners and emit events on them
+// when they occur.
+const observer = Trait.compose(DOMEventAssembler, EventEmitter).create({
+ /**
+ * Method is implemented by `EventEmitter` and is used just for emitting
+ * events on registered listeners.
+ */
+ _emit: Trait.required,
+ /**
+ * Events that are supported and emitted by the module.
+ */
+ supportedEventsTypes: [ "keydown", "keyup", "keypress" ],
+ /**
+ * Function handles all the supported events on all the windows that are
+ * observed. Method is used to proxy events to the listeners registered on
+ * this event emitter.
+ * @param {Event} event
+ * Keyboard event being emitted.
+ */
+ handleEvent: function handleEvent(event) {
+ this._emit(event.type, event, event.target.ownerDocument.defaultView);
+ }
+});
+
+// Adding each opened window to a list of observed windows.
+windowObserver.on("open", function onOpen(window) {
+ if (isBrowser(window))
+ observer.observe(window);
+});
+// Removing each closed window form the list of observed windows.
+windowObserver.on("close", function onClose(window) {
+ if (isBrowser(window))
+ observer.ignore(window);
+});
+
+// Making observer aware of already opened windows.
+for each (let window in browserWindowIterator())
+ observer.observe(window);
+
+exports.observer = observer;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/keyboard/utils.js b/tools/addon-sdk-1.7/packages/api-utils/lib/keyboard/utils.js
new file mode 100644
index 0000000..4693a12
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/keyboard/utils.js
@@ -0,0 +1,186 @@
+/* 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/. */
+
+"use strict";
+
+const { Cc, Ci } = require("chrome");
+const runtime = require("../runtime");
+const { isString } = require("../type");
+const array = require("../array");
+
+
+const SWP = "{{SEPARATOR}}";
+const SEPARATOR = "-"
+const INVALID_COMBINATION = "Hotkey key combination must contain one or more " +
+ "modifiers and only one key";
+
+// Map of modifier key mappings.
+const MODIFIERS = exports.MODIFIERS = {
+ 'accel': runtime.OS === "Darwin" ? 'meta' : 'control',
+ 'meta': 'meta',
+ 'control': 'control',
+ 'ctrl': 'control',
+ 'option': 'alt',
+ 'command': 'meta',
+ 'alt': 'alt',
+ 'shift': 'shift'
+};
+
+// Hash of key:code pairs for all the chars supported by `nsIDOMKeyEvent`.
+// This is just a copy of the `nsIDOMKeyEvent` hash with normalized names.
+// @See: http://mxr.mozilla.org/mozilla-central/source/dom/interfaces/events/nsIDOMKeyEvent.idl
+const CODES = exports.CODES = new function Codes() {
+ let nsIDOMKeyEvent = Ci.nsIDOMKeyEvent;
+ // Names that will be substituted with a shorter analogs.
+ let aliases = {
+ 'subtract': '-',
+ 'add': '+',
+ 'equals': '=',
+ 'slash': '/',
+ 'backslash': '\\',
+ 'openbracket': '[',
+ 'closebracket': ']',
+ 'quote': '\'',
+ 'backquote': '`',
+ 'period': '.',
+ 'semicolon': ';',
+ 'comma': ','
+ };
+
+ // Normalizing keys and copying values to `this` object.
+ Object.keys(nsIDOMKeyEvent).filter(function(key) {
+ // Filter out only key codes.
+ return key.indexOf('DOM_VK') === 0;
+ }).map(function(key) {
+ // Map to key:values
+ return [ key, nsIDOMKeyEvent[key] ];
+ }).map(function([key, value]) {
+ return [ key.replace('DOM_VK_', '').replace('_', '').toLowerCase(), value ];
+ }).forEach(function ([ key, value ]) {
+ this[aliases[key] || key] = value;
+ }, this);
+};
+
+// Inverted `CODES` hash of `code:key`.
+const KEYS = exports.KEYS = new function Keys() {
+ Object.keys(CODES).forEach(function(key) {
+ this[CODES[key]] = key;
+ }, this)
+}
+
+exports.getKeyForCode = function getKeyForCode(code) {
+ return (code in KEYS) && KEYS[code];
+};
+exports.getCodeForKey = function getCodeForKey(key) {
+ return (key in CODES) && CODES[key];
+};
+
+/**
+ * Utility function that takes string or JSON that defines a `hotkey` and
+ * returns normalized string version of it.
+ * @param {JSON|String} hotkey
+ * @param {String} [separator=" "]
+ * Optional string that represents separator used to concatenate keys in the
+ * given `hotkey`.
+ * @returns {String}
+ * @examples
+ *
+ * require("keyboard/hotkeys").normalize("b Shift accel");
+ * // 'control shift b' -> on windows & linux
+ * // 'meta shift b' -> on mac
+ * require("keyboard/hotkeys").normalize("alt-d-shift", "-");
+ * // 'alt shift d'
+ */
+var normalize = exports.normalize = function normalize(hotkey, separator) {
+ if (!isString(hotkey))
+ hotkey = toString(hotkey, separator);
+ return toString(toJSON(hotkey, separator), separator);
+};
+
+/*
+ * Utility function that splits a string of characters that defines a `hotkey`
+ * into modifier keys and the defining key.
+ * @param {String} hotkey
+ * @param {String} [separator=" "]
+ * Optional string that represents separator used to concatenate keys in the
+ * given `hotkey`.
+ * @returns {JSON}
+ * @examples
+ *
+ * require("keyboard/hotkeys").toJSON("accel shift b");
+ * // { key: 'b', modifiers: [ 'control', 'shift' ] } -> on windows & linux
+ * // { key: 'b', modifiers: [ 'meta', 'shift' ] } -> on mac
+ *
+ * require("keyboard/hotkeys").normalize("alt-d-shift", "-");
+ * // { key: 'd', modifiers: [ 'alt', 'shift' ] }
+ */
+var toJSON = exports.toJSON = function toJSON(hotkey, separator) {
+ separator = separator || SEPARATOR;
+ // Since default separator is `-`, combination may take form of `alt--`. To
+ // avoid misbehavior we replace `--` with `-{{SEPARATOR}}` where
+ // `{{SEPARATOR}}` can be swapped later.
+ hotkey = hotkey.toLowerCase().replace(separator + separator, separator + SWP);
+
+ let value = {};
+ let modifiers = [];
+ let keys = hotkey.split(separator);
+ keys.forEach(function(name) {
+ // If name is `SEPARATOR` than we swap it back.
+ if (name === SWP)
+ name = separator;
+ if (name in MODIFIERS) {
+ array.add(modifiers, MODIFIERS[name]);
+ } else {
+ if (!value.key)
+ value.key = name;
+ else
+ throw new TypeError(INVALID_COMBINATION);
+ }
+ });
+
+ if (!value.key)
+ throw new TypeError(INVALID_COMBINATION);
+
+ value.modifiers = modifiers.sort();
+ return value;
+};
+
+/**
+ * Utility function that takes object that defines a `hotkey` and returns
+ * string representation of it.
+ *
+ * _Please note that this function does not validates data neither it normalizes
+ * it, if you are unsure that data is well formed use `normalize` function
+ * instead.
+ *
+ * @param {JSON} hotkey
+ * @param {String} [separator=" "]
+ * Optional string that represents separator used to concatenate keys in the
+ * given `hotkey`.
+ * @returns {String}
+ * @examples
+ *
+ * require("keyboard/hotkeys").toString({
+ * key: 'b',
+ * modifiers: [ 'control', 'shift' ]
+ * }, '+');
+ * // 'control+shift+b
+ *
+ */
+var toString = exports.toString = function toString(hotkey, separator) {
+ let keys = hotkey.modifiers.slice();
+ keys.push(hotkey.key);
+ return keys.join(separator || SEPARATOR);
+};
+
+/**
+ * Utility function takes `key` name and returns `true` if it's function key
+ * (F1, ..., F24) and `false` if it's not.
+ */
+var isFunctionKey = exports.isFunctionKey = function isFunctionKey(key) {
+ var $
+ return key[0].toLowerCase() === 'f' &&
+ ($ = parseInt(key.substr(1)), 0 < $ && $ < 25);
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/l10n/locale.js b/tools/addon-sdk-1.7/packages/api-utils/lib/l10n/locale.js
new file mode 100644
index 0000000..ed82789
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/l10n/locale.js
@@ -0,0 +1,122 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const prefs = require("preferences-service");
+const { Cu, Cc, Ci } = require("chrome");
+const { Services } = Cu.import("resource://gre/modules/Services.jsm");
+
+
+/**
+ * Gets the currently selected locale for display.
+ * Gets all usable locale that we can use sorted by priority of relevance
+ * @return Array of locales, begins with highest priority
+ */
+const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
+const PREF_SELECTED_LOCALE = "general.useragent.locale";
+const PREF_ACCEPT_LANGUAGES = "intl.accept_languages";
+exports.getPreferedLocales = function getPreferedLocales() {
+ let locales = [];
+
+ function addLocale(locale) {
+ locale = locale.toLowerCase();
+ if (locales.indexOf(locale) === -1)
+ locales.push(locale);
+ }
+
+ // Most important locale is OS one. But we use it, only if
+ // "intl.locale.matchOS" pref is set to `true`.
+ // Currently only used for multi-locales mobile builds.
+ // http://mxr.mozilla.org/mozilla-central/source/mobile/android/installer/Makefile.in#46
+ if (prefs.get(PREF_MATCH_OS_LOCALE, false)) {
+ let localeService = Cc["@mozilla.org/intl/nslocaleservice;1"].
+ getService(Ci.nsILocaleService);
+ let osLocale = localeService.getLocaleComponentForUserAgent();
+ addLocale(osLocale);
+ }
+
+ // In some cases, mainly on Fennec and on Linux version,
+ // `general.useragent.locale` is a special 'localized' value, like:
+ // "chrome://global/locale/intl.properties"
+ let browserUiLocale = prefs.getLocalized(PREF_SELECTED_LOCALE, "") ||
+ prefs.get(PREF_SELECTED_LOCALE, "");
+ if (browserUiLocale)
+ addLocale(browserUiLocale);
+
+
+ // Third priority is the list of locales used for web content
+ let contentLocales = prefs.get(PREF_ACCEPT_LANGUAGES, "");
+ if (contentLocales) {
+ // This list is a string of locales seperated by commas.
+ // There is spaces after commas, so strip each item
+ for each(let locale in contentLocales.split(","))
+ addLocale(locale.replace(/(^\s+)|(\s+$)/g, ""));
+ }
+
+ // Finally, we ensure that en-US is the final fallback if it wasn't added
+ addLocale("en-US");
+
+ return locales;
+}
+
+/**
+ * Selects the closest matching locale from a list of locales.
+ *
+ * @param aLocales
+ * An array of available locales
+ * @param aMatchLocales
+ * An array of prefered locales, ordered by priority. Most wanted first.
+ * Locales have to be in lowercase.
+ * If null, uses getPreferedLocales() results
+ * @return the best match for the currently selected locale
+ *
+ * Stolen from http://mxr.mozilla.org/mozilla-central/source/toolkit/mozapps/extensions/XPIProvider.jsm
+ */
+exports.findClosestLocale = function findClosestLocale(aLocales, aMatchLocales) {
+
+ aMatchLocales = aMatchLocales || exports.getPreferedLocales();
+
+ // Holds the best matching localized resource
+ let bestmatch = null;
+ // The number of locale parts it matched with
+ let bestmatchcount = 0;
+ // The number of locale parts in the match
+ let bestpartcount = 0;
+
+ for each (let locale in aMatchLocales) {
+ let lparts = locale.split("-");
+ for each (let localized in aLocales) {
+ let found = localized.toLowerCase();
+ // Exact match is returned immediately
+ if (locale == found)
+ return localized;
+
+ let fparts = found.split("-");
+ /* If we have found a possible match and this one isn't any longer
+ then we dont need to check further. */
+ if (bestmatch && fparts.length < bestmatchcount)
+ continue;
+
+ // Count the number of parts that match
+ let maxmatchcount = Math.min(fparts.length, lparts.length);
+ let matchcount = 0;
+ while (matchcount < maxmatchcount &&
+ fparts[matchcount] == lparts[matchcount])
+ matchcount++;
+
+ /* If we matched more than the last best match or matched the same and
+ this locale is less specific than the last best match. */
+ if (matchcount > bestmatchcount ||
+ (matchcount == bestmatchcount && fparts.length < bestpartcount)) {
+ bestmatch = localized;
+ bestmatchcount = matchcount;
+ bestpartcount = fparts.length;
+ }
+ }
+ // If we found a valid match for this locale return it
+ if (bestmatch)
+ return bestmatch;
+ }
+ return null;
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/l10n/plural-rules.js b/tools/addon-sdk-1.7/packages/api-utils/lib/l10n/plural-rules.js
new file mode 100644
index 0000000..c891870
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/l10n/plural-rules.js
@@ -0,0 +1,387 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This file is automatically generated with /python-lib/plural-rules-generator.py
+// Fetching data from: http://unicode.org/repos/cldr/trunk/common/supplemental/plurals.xml
+
+// Mapping of short locale name == to == > rule index in following list
+const LOCALES_TO_RULES = {
+ "af": 2,
+ "ak": 3,
+ "am": 3,
+ "ar": 1,
+ "asa": 2,
+ "az": 0,
+ "be": 10,
+ "bem": 2,
+ "bez": 2,
+ "bg": 2,
+ "bh": 3,
+ "bm": 0,
+ "bn": 2,
+ "bo": 0,
+ "br": 19,
+ "brx": 2,
+ "bs": 10,
+ "ca": 2,
+ "cgg": 2,
+ "chr": 2,
+ "cs": 11,
+ "cy": 16,
+ "da": 2,
+ "de": 2,
+ "dv": 2,
+ "dz": 0,
+ "ee": 2,
+ "el": 2,
+ "en": 2,
+ "eo": 2,
+ "es": 2,
+ "et": 2,
+ "eu": 2,
+ "fa": 0,
+ "ff": 4,
+ "fi": 2,
+ "fil": 3,
+ "fo": 2,
+ "fr": 4,
+ "fur": 2,
+ "fy": 2,
+ "ga": 7,
+ "gd": 23,
+ "gl": 2,
+ "gsw": 2,
+ "gu": 2,
+ "guw": 3,
+ "gv": 22,
+ "ha": 2,
+ "haw": 2,
+ "he": 2,
+ "hi": 3,
+ "hr": 10,
+ "hu": 0,
+ "id": 0,
+ "ig": 0,
+ "ii": 0,
+ "is": 2,
+ "it": 2,
+ "iu": 6,
+ "ja": 0,
+ "jmc": 2,
+ "jv": 0,
+ "ka": 0,
+ "kab": 4,
+ "kaj": 2,
+ "kcg": 2,
+ "kde": 0,
+ "kea": 0,
+ "kk": 2,
+ "kl": 2,
+ "km": 0,
+ "kn": 0,
+ "ko": 0,
+ "ksb": 2,
+ "ksh": 20,
+ "ku": 2,
+ "kw": 6,
+ "lag": 17,
+ "lb": 2,
+ "lg": 2,
+ "ln": 3,
+ "lo": 0,
+ "lt": 9,
+ "lv": 5,
+ "mas": 2,
+ "mg": 3,
+ "mk": 15,
+ "ml": 2,
+ "mn": 2,
+ "mo": 8,
+ "mr": 2,
+ "ms": 0,
+ "mt": 14,
+ "my": 0,
+ "nah": 2,
+ "naq": 6,
+ "nb": 2,
+ "nd": 2,
+ "ne": 2,
+ "nl": 2,
+ "nn": 2,
+ "no": 2,
+ "nr": 2,
+ "nso": 3,
+ "ny": 2,
+ "nyn": 2,
+ "om": 2,
+ "or": 2,
+ "pa": 2,
+ "pap": 2,
+ "pl": 12,
+ "ps": 2,
+ "pt": 2,
+ "rm": 2,
+ "ro": 8,
+ "rof": 2,
+ "ru": 10,
+ "rwk": 2,
+ "sah": 0,
+ "saq": 2,
+ "se": 6,
+ "seh": 2,
+ "ses": 0,
+ "sg": 0,
+ "sh": 10,
+ "shi": 18,
+ "sk": 11,
+ "sl": 13,
+ "sma": 6,
+ "smi": 6,
+ "smj": 6,
+ "smn": 6,
+ "sms": 6,
+ "sn": 2,
+ "so": 2,
+ "sq": 2,
+ "sr": 10,
+ "ss": 2,
+ "ssy": 2,
+ "st": 2,
+ "sv": 2,
+ "sw": 2,
+ "syr": 2,
+ "ta": 2,
+ "te": 2,
+ "teo": 2,
+ "th": 0,
+ "ti": 3,
+ "tig": 2,
+ "tk": 2,
+ "tl": 3,
+ "tn": 2,
+ "to": 0,
+ "tr": 0,
+ "ts": 2,
+ "tzm": 21,
+ "uk": 10,
+ "ur": 2,
+ "ve": 2,
+ "vi": 0,
+ "vun": 2,
+ "wa": 3,
+ "wae": 2,
+ "wo": 0,
+ "xh": 2,
+ "xog": 2,
+ "yo": 0,
+ "zh": 0,
+ "zu": 2
+};
+
+// Utility functions for plural rules methods
+function isIn(n, list) list.indexOf(n) !== -1;
+function isBetween(n, start, end) start <= n && n <= end;
+
+// List of all plural rules methods, that maps an integer to the plural form name to use
+const RULES = {
+ "0": function (n) {
+
+ return "other"
+ },
+ "1": function (n) {
+ if ((isBetween((n % 100), 3, 10)))
+ return "few";
+ if (n == 0)
+ return "zero";
+ if ((isBetween((n % 100), 11, 99)))
+ return "many";
+ if (n == 2)
+ return "two";
+ if (n == 1)
+ return "one";
+ return "other"
+ },
+ "2": function (n) {
+ if (n == 1)
+ return "one";
+ return "other"
+ },
+ "3": function (n) {
+ if ((isBetween(n, 0, 1)))
+ return "one";
+ return "other"
+ },
+ "4": function (n) {
+ if ((isBetween(n, 0, 2)) && n != 2)
+ return "one";
+ return "other"
+ },
+ "5": function (n) {
+ if (n == 0)
+ return "zero";
+ if ((n % 10) == 1 && (n % 100) != 11)
+ return "one";
+ return "other"
+ },
+ "6": function (n) {
+ if (n == 2)
+ return "two";
+ if (n == 1)
+ return "one";
+ return "other"
+ },
+ "7": function (n) {
+ if ((isBetween(n, 3, 6)))
+ return "few";
+ if ((isBetween(n, 7, 10)))
+ return "many";
+ if (n == 2)
+ return "two";
+ if (n == 1)
+ return "one";
+ return "other"
+ },
+ "8": function (n) {
+ if (n == 0 || n != 1 && (isBetween((n % 100), 1, 19)))
+ return "few";
+ if (n == 1)
+ return "one";
+ return "other"
+ },
+ "9": function (n) {
+ if ((isBetween((n % 10), 2, 9)) && !(isBetween((n % 100), 11, 19)))
+ return "few";
+ if ((n % 10) == 1 && !(isBetween((n % 100), 11, 19)))
+ return "one";
+ return "other"
+ },
+ "10": function (n) {
+ if ((isBetween((n % 10), 2, 4)) && !(isBetween((n % 100), 12, 14)))
+ return "few";
+ if ((n % 10) == 0 || (isBetween((n % 10), 5, 9)) || (isBetween((n % 100), 11, 14)))
+ return "many";
+ if ((n % 10) == 1 && (n % 100) != 11)
+ return "one";
+ return "other"
+ },
+ "11": function (n) {
+ if ((isBetween(n, 2, 4)))
+ return "few";
+ if (n == 1)
+ return "one";
+ return "other"
+ },
+ "12": function (n) {
+ if ((isBetween((n % 10), 2, 4)) && !(isBetween((n % 100), 12, 14)))
+ return "few";
+ if (n != 1 && (isBetween((n % 10), 0, 1)) || (isBetween((n % 10), 5, 9)) || (isBetween((n % 100), 12, 14)))
+ return "many";
+ if (n == 1)
+ return "one";
+ return "other"
+ },
+ "13": function (n) {
+ if ((isBetween((n % 100), 3, 4)))
+ return "few";
+ if ((n % 100) == 2)
+ return "two";
+ if ((n % 100) == 1)
+ return "one";
+ return "other"
+ },
+ "14": function (n) {
+ if (n == 0 || (isBetween((n % 100), 2, 10)))
+ return "few";
+ if ((isBetween((n % 100), 11, 19)))
+ return "many";
+ if (n == 1)
+ return "one";
+ return "other"
+ },
+ "15": function (n) {
+ if ((n % 10) == 1 && n != 11)
+ return "one";
+ return "other"
+ },
+ "16": function (n) {
+ if (n == 3)
+ return "few";
+ if (n == 0)
+ return "zero";
+ if (n == 6)
+ return "many";
+ if (n == 2)
+ return "two";
+ if (n == 1)
+ return "one";
+ return "other"
+ },
+ "17": function (n) {
+ if (n == 0)
+ return "zero";
+ if ((isBetween(n, 0, 2)) && n != 0 && n != 2)
+ return "one";
+ return "other"
+ },
+ "18": function (n) {
+ if ((isBetween(n, 2, 10)))
+ return "few";
+ if ((isBetween(n, 0, 1)))
+ return "one";
+ return "other"
+ },
+ "19": function (n) {
+ if ((isBetween((n % 10), 3, 4) || ((n % 10) == 9)) && !(isBetween((n % 100), 10, 19) || isBetween((n % 100), 70, 79) || isBetween((n % 100), 90, 99)))
+ return "few";
+ if ((n % 1000000) == 0 && n != 0)
+ return "many";
+ if ((n % 10) == 2 && !isIn((n % 100), [12, 72, 92]))
+ return "two";
+ if ((n % 10) == 1 && !isIn((n % 100), [11, 71, 91]))
+ return "one";
+ return "other"
+ },
+ "20": function (n) {
+ if (n == 0)
+ return "zero";
+ if (n == 1)
+ return "one";
+ return "other"
+ },
+ "21": function (n) {
+ if ((isBetween(n, 0, 1)) || (isBetween(n, 11, 99)))
+ return "one";
+ return "other"
+ },
+ "22": function (n) {
+ if ((isBetween((n % 10), 1, 2)) || (n % 20) == 0)
+ return "one";
+ return "other"
+ },
+ "23": function (n) {
+ if ((isBetween(n, 3, 10) || isBetween(n, 13, 19)))
+ return "few";
+ if (isIn(n, [2, 12]))
+ return "two";
+ if (isIn(n, [1, 11]))
+ return "one";
+ return "other"
+ },
+};
+
+/**
+ * Return a function that gives the plural form name for a given integer
+ * for the specified `locale`
+ * let fun = getRulesForLocale('en');
+ * fun(1) -> 'one'
+ * fun(0) -> 'other'
+ * fun(1000) -> 'other'
+ */
+exports.getRulesForLocale = function getRulesForLocale(locale) {
+ let index = LOCALES_TO_RULES[locale];
+ if (!(index in RULES))
+ throw new Error('Plural form unknown for locale "' + locale + '"');
+ return RULES[index];
+}
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/light-traits.js b/tools/addon-sdk-1.7/packages/api-utils/lib/light-traits.js
new file mode 100644
index 0000000..954d093
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/light-traits.js
@@ -0,0 +1,596 @@
+/* vim:ts=2:sts=2:sw=2:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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` is being used in the module in order to make it reusable in
+// environments in which `let` is not yet supported.
+
+// Shortcut to `Object.prototype.hasOwnProperty.call`.
+// owns(object, name) would be the same as
+// Object.prototype.hasOwnProperty.call(object, name);
+var owns = Function.prototype.call.bind(Object.prototype.hasOwnProperty);
+
+/**
+ * Whether or not given property descriptors are equivalent. They are
+ * equivalent either if both are marked as 'conflict' or 'required' property
+ * or if all the properties of descriptors are equal.
+ * @param {Object} actual
+ * @param {Object} expected
+ */
+function equivalentDescriptors(actual, expected) {
+ return (actual.conflict && expected.conflict) ||
+ (actual.required && expected.required) ||
+ equalDescriptors(actual, expected);
+}
+/**
+ * Whether or not given property descriptors define equal properties.
+ */
+function equalDescriptors(actual, expected) {
+ return actual.get === expected.get &&
+ actual.set === expected.set &&
+ actual.value === expected.value &&
+ !!actual.enumerable === !!expected.enumerable &&
+ !!actual.configurable === !!expected.configurable &&
+ !!actual.writable === !!expected.writable;
+}
+
+// Utilities that throwing exceptions for a properties that are marked
+// as "required" or "conflict" properties.
+function throwConflictPropertyError(name) {
+ throw new Error("Remaining conflicting property: `" + name + "`");
+}
+function throwRequiredPropertyError(name) {
+ throw new Error("Missing required property: `" + name + "`");
+}
+
+/**
+ * Generates custom **required** property descriptor. Descriptor contains
+ * non-standard property `required` that is equal to `true`.
+ * @param {String} name
+ * property name to generate descriptor for.
+ * @returns {Object}
+ * custom property descriptor
+ */
+function RequiredPropertyDescriptor(name) {
+ // Creating function by binding first argument to a property `name` on the
+ // `throwConflictPropertyError` function. Created function is used as a
+ // getter & setter of the created property descriptor. This way we ensure
+ // that we throw exception late (on property access) if object with
+ // `required` property was instantiated using built-in `Object.create`.
+ var accessor = throwRequiredPropertyError.bind(null, name);
+ return { get: accessor, set: accessor, required: true };
+}
+
+/**
+ * Generates custom **conflicting** property descriptor. Descriptor contains
+ * non-standard property `conflict` that is equal to `true`.
+ * @param {String} name
+ * property name to generate descriptor for.
+ * @returns {Object}
+ * custom property descriptor
+ */
+function ConflictPropertyDescriptor(name) {
+ // For details see `RequiredPropertyDescriptor` since idea is same.
+ var accessor = throwConflictPropertyError.bind(null, name);
+ return { get: accessor, set: accessor, conflict: true };
+}
+
+/**
+ * Tests if property is marked as `required` property.
+ */
+function isRequiredProperty(object, name) {
+ return !!object[name].required;
+}
+
+/**
+ * Tests if property is marked as `conflict` property.
+ */
+function isConflictProperty(object, name) {
+ return !!object[name].conflict;
+}
+
+/**
+ * Function tests whether or not method of the `source` object with a given
+ * `name` is inherited from `Object.prototype`.
+ */
+function isBuiltInMethod(name, source) {
+ var target = Object.prototype[name];
+
+ // If methods are equal then we know it's `true`.
+ return target == source ||
+ // If `source` object comes form a different sandbox `==` will evaluate
+ // to `false`, in that case we check if functions names and sources match.
+ (String(target) === String(source) && target.name === source.name);
+}
+
+/**
+ * Function overrides `toString` and `constructor` methods of a given `target`
+ * object with a same-named methods of a given `source` if methods of `target`
+ * object are inherited / copied from `Object.prototype`.
+ * @see create
+ */
+function overrideBuiltInMethods(target, source) {
+ if (isBuiltInMethod("toString", target.toString)) {
+ Object.defineProperty(target, "toString", {
+ value: source.toString,
+ configurable: true,
+ enumerable: false
+ });
+ }
+
+ if (isBuiltInMethod("constructor", target.constructor)) {
+ Object.defineProperty(target, "constructor", {
+ value: source.constructor,
+ configurable: true,
+ enumerable: false
+ });
+ }
+}
+
+/**
+ * Composes new trait with the same own properties as the original trait,
+ * except that all property names appearing in the first argument are replaced
+ * by "required" property descriptors.
+ * @param {String[]} keys
+ * Array of strings property names.
+ * @param {Object} trait
+ * A trait some properties of which should be excluded.
+ * @returns {Object}
+ * @example
+ * var newTrait = exclude(["name", ...], trait)
+ */
+function exclude(names, trait) {
+ var map = {};
+
+ Object.keys(trait).forEach(function(name) {
+
+ // If property is not excluded (the array of names does not contain it),
+ // or it is a "required" property, copy it to the property descriptor `map`
+ // that will be used for creation of resulting trait.
+ if (!~names.indexOf(name) || isRequiredProperty(trait, name))
+ map[name] = { value: trait[name], enumerable: true };
+
+ // For all the `names` in the exclude name array we create required
+ // property descriptors and copy them to the `map`.
+ else
+ map[name] = { value: RequiredPropertyDescriptor(name), enumerable: true };
+ });
+
+ return Object.create(Trait.prototype, map);
+}
+
+/**
+ * Composes new instance of `Trait` with a properties of a given `trait`,
+ * except that all properties whose name is an own property of `renames` will
+ * be renamed to `renames[name]` and a `"required"` property for name will be
+ * added instead.
+ *
+ * For each renamed property, a required property is generated. If
+ * the `renames` map two properties to the same name, a conflict is generated.
+ * If the `renames` map a property to an existing unrenamed property, a
+ * conflict is generated.
+ *
+ * @param {Object} renames
+ * An object whose own properties serve as a mapping from old names to new
+ * names.
+ * @param {Object} trait
+ * A new trait with renamed properties.
+ * @returns {Object}
+ * @example
+ *
+ * // Return trait with `bar` property equal to `trait.foo` and with
+ * // `foo` and `baz` "required" properties.
+ * var renamedTrait = rename({ foo: "bar", baz: null }), trait);
+ *
+ * // t1 and t2 are equivalent traits
+ * var t1 = rename({a: "b"}, t);
+ * var t2 = compose(exclude(["a"], t), { a: { required: true }, b: t[a] });
+ */
+function rename(renames, trait) {
+ var map = {};
+
+ // Loop over all the properties of the given `trait` and copy them to a
+ // property descriptor `map` that will be used for the creation of the
+ // resulting trait. Also, rename properties in the `map` as specified by
+ // `renames`.
+ Object.keys(trait).forEach(function(name) {
+ var alias;
+
+ // If the property is in the `renames` map, and it isn't a "required"
+ // property (which should never need to be aliased because "required"
+ // properties never conflict), then we must try to rename it.
+ if (owns(renames, name) && !isRequiredProperty(trait, name)) {
+ alias = renames[name];
+
+ // If the `map` already has the `alias`, and it isn't a "required"
+ // property, that means the `alias` conflicts with an existing name for a
+ // provided trait (that can happen if >=2 properties are aliased to the
+ // same name). In this case we mark it as a conflicting property.
+ // Otherwise, everything is fine, and we copy property with an `alias`
+ // name.
+ if (owns(map, alias) && !map[alias].value.required) {
+ map[alias] = {
+ value: ConflictPropertyDescriptor(alias),
+ enumerable: true
+ };
+ }
+ else {
+ map[alias] = {
+ value: trait[name],
+ enumerable: true
+ };
+ }
+
+ // Regardless of whether or not the rename was successful, we check to
+ // see if the original `name` exists in the map (such a property
+ // could exist if previous another property was aliased to this `name`).
+ // If it isn't, we mark it as "required", to make sure the caller
+ // provides another value for the old name, which methods of the trait
+ // might continue to reference.
+ if (!owns(map, name)) {
+ map[name] = {
+ value: RequiredPropertyDescriptor(name),
+ enumerable: true
+ };
+ }
+ }
+
+ // Otherwise, either the property isn't in the `renames` map (thus the
+ // caller is not trying to rename it) or it is a "required" property.
+ // Either way, we don't have to alias the property, we just have to copy it
+ // to the map.
+ else {
+ // The property isn't in the map yet, so we copy it over.
+ if (!owns(map, name)) {
+ map[name] = { value: trait[name], enumerable: true };
+ }
+
+ // The property is already in the map (that means another property was
+ // aliased with this `name`, which creates a conflict if the property is
+ // not marked as "required"), so we have to mark it as a "conflict"
+ // property.
+ else if (!isRequiredProperty(trait, name)) {
+ map[name] = {
+ value: ConflictPropertyDescriptor(name),
+ enumerable: true
+ };
+ }
+ }
+ });
+ return Object.create(Trait.prototype, map);
+}
+
+/**
+ * Composes new resolved trait, with all the same properties as the original
+ * `trait`, except that all properties whose name is an own property of
+ * `resolutions` will be renamed to `resolutions[name]`.
+ *
+ * If `resolutions[name]` is `null`, the value is mapped to a property
+ * descriptor that is marked as a "required" property.
+ */
+function resolve(resolutions, trait) {
+ var renames = {};
+ var exclusions = [];
+
+ // Go through each mapping in `resolutions` object and distribute it either
+ // to `renames` or `exclusions`.
+ Object.keys(resolutions).forEach(function(name) {
+
+ // If `resolutions[name]` is a truthy value then it's a mapping old -> new
+ // so we copy it to `renames` map.
+ if (resolutions[name])
+ renames[name] = resolutions[name];
+
+ // Otherwise it's not a mapping but an exclusion instead in which case we
+ // add it to the `exclusions` array.
+ else
+ exclusions.push(name);
+ });
+
+ // First `exclude` **then** `rename` and order is important since
+ // `exclude` and `rename` are not associative.
+ return rename(renames, exclude(exclusions, trait));
+}
+
+/**
+ * Create a Trait (a custom property descriptor map) that represents the given
+ * `object`'s own properties. Property descriptor map is a "custom", because it
+ * inherits from `Trait.prototype` and it's property descriptors may contain
+ * two attributes that is not part of the ES5 specification:
+ *
+ * - "required" (this property must be provided by another trait
+ * before an instance of this trait can be created)
+ * - "conflict" (when the trait is composed with another trait,
+ * a unique value for this property is provided by two or more traits)
+ *
+ * Data properties bound to the `Trait.required` singleton exported by
+ * this module will be marked as "required" properties.
+ *
+ * @param {Object} object
+ * Map of properties to compose trait from.
+ * @returns {Trait}
+ * Trait / Property descriptor map containing all the own properties of the
+ * given argument.
+ */
+function trait(object) {
+ var map;
+ var trait = object;
+
+ if (!(object instanceof Trait)) {
+ // If the passed `object` is not already an instance of `Trait`, we create
+ // a property descriptor `map` containing descriptors for the own properties
+ // of the given `object`. `map` is then used to create a `Trait` instance
+ // after all properties are mapped. Note that we can't create a trait and
+ // then just copy properties into it since that will fail for inherited
+ // read-only properties.
+ map = {};
+
+ // Each own property of the given `object` is mapped to a data property
+ // whose value is a property descriptor.
+ Object.keys(object).forEach(function (name) {
+
+ // If property of an `object` is equal to a `Trait.required`, it means
+ // that it was marked as "required" property, in which case we map it
+ // to "required" property.
+ if (Trait.required ==
+ Object.getOwnPropertyDescriptor(object, name).value) {
+ map[name] = {
+ value: RequiredPropertyDescriptor(name),
+ enumerable: true
+ };
+ }
+ // Otherwise property is mapped to it's property descriptor.
+ else {
+ map[name] = {
+ value: Object.getOwnPropertyDescriptor(object, name),
+ enumerable: true
+ };
+ }
+ });
+
+ trait = Object.create(Trait.prototype, map);
+ }
+ return trait;
+}
+
+/**
+ * Compose a property descriptor map that inherits from `Trait.prototype` and
+ * contains property descriptors for all the own properties of the passed
+ * traits.
+ *
+ * If two or more traits have own properties with the same name, the returned
+ * trait will contain a "conflict" property for that name. Composition is a
+ * commutative and associative operation, and the order of its arguments is
+ * irrelevant.
+ */
+function compose(trait1, trait2/*, ...*/) {
+ // Create a new property descriptor `map` to which all the own properties
+ // of the passed traits are copied. This map will be used to create a `Trait`
+ // instance that will be the result of this composition.
+ var map = {};
+
+ // Properties of each passed trait are copied to the composition.
+ Array.prototype.forEach.call(arguments, function(trait) {
+ // Copying each property of the given trait.
+ Object.keys(trait).forEach(function(name) {
+
+ // If `map` already owns a property with the `name` and it is not
+ // marked "required".
+ if (owns(map, name) && !map[name].value.required) {
+
+ // If the source trait's property with the `name` is marked as
+ // "required", we do nothing, as the requirement was already resolved
+ // by a property in the `map` (because it already contains a
+ // non-required property with that `name`). But if properties are just
+ // different, we have a name clash and we substitute it with a property
+ // that is marked "conflict".
+ if (!isRequiredProperty(trait, name) &&
+ !equivalentDescriptors(map[name].value, trait[name])
+ ) {
+ map[name] = {
+ value: ConflictPropertyDescriptor(name),
+ enumerable: true
+ };
+ }
+ }
+
+ // Otherwise, the `map` does not have an own property with the `name`, or
+ // it is marked "required". Either way, the trait's property is copied to
+ // the map (if the property of the `map` is marked "required", it is going
+ // to be resolved by the property that is being copied).
+ else {
+ map[name] = { value: trait[name], enumerable: true };
+ }
+ });
+ });
+
+ return Object.create(Trait.prototype, map);
+}
+
+/**
+ * `defineProperties` is like `Object.defineProperties`, except that it
+ * ensures that:
+ * - An exception is thrown if any property in a given `properties` map
+ * is marked as "required" property and same named property is not
+ * found in a given `prototype`.
+ * - An exception is thrown if any property in a given `properties` map
+ * is marked as "conflict" property.
+ * @param {Object} object
+ * Object to define properties on.
+ * @param {Object} properties
+ * Properties descriptor map.
+ * @returns {Object}
+ * `object` that was passed as a first argument.
+ */
+function defineProperties(object, properties) {
+
+ // Create a map into which we will copy each verified property from the given
+ // `properties` description map. We use it to verify that none of the
+ // provided properties is marked as a "conflict" property and that all
+ // "required" properties are resolved by a property of an `object`, so we
+ // can throw an exception before mutating object if that isn't the case.
+ var verifiedProperties = {};
+
+ // Coping each property from a given `properties` descriptor map to a
+ // verified map of property descriptors.
+ Object.keys(properties).forEach(function(name) {
+
+ // If property is marked as "required" property and we don't have a same
+ // named property in a given `object` we throw an exception. If `object`
+ // has same named property just skip this property since required property
+ // is was inherited and there for requirement was satisfied.
+ if (isRequiredProperty(properties, name)) {
+ if (!(name in object))
+ throwRequiredPropertyError(name);
+ }
+
+ // If property is marked as "conflict" property we throw an exception.
+ else if (isConflictProperty(properties, name)) {
+ throwConflictPropertyError(name);
+ }
+
+ // If property is not marked neither as "required" nor "conflict" property
+ // we copy it to verified properties map.
+ else {
+ verifiedProperties[name] = properties[name];
+ }
+ });
+
+ // If no exceptions were thrown yet, we know that our verified property
+ // descriptor map has no properties marked as "conflict" or "required",
+ // so we just delegate to the built-in `Object.defineProperties`.
+ return Object.defineProperties(object, verifiedProperties);
+}
+
+/**
+ * `create` is like `Object.create`, except that it ensures that:
+ * - An exception is thrown if any property in a given `properties` map
+ * is marked as "required" property and same named property is not
+ * found in a given `prototype`.
+ * - An exception is thrown if any property in a given `properties` map
+ * is marked as "conflict" property.
+ * @param {Object} prototype
+ * prototype of the composed object
+ * @param {Object} properties
+ * Properties descriptor map.
+ * @returns {Object}
+ * An object that inherits form a given `prototype` and implements all the
+ * properties defined by a given `properties` descriptor map.
+ */
+function create(prototype, properties) {
+
+ // Creating an instance of the given `prototype`.
+ var object = Object.create(prototype);
+
+ // Overriding `toString`, `constructor` methods if they are just inherited
+ // from `Object.prototype` with a same named methods of the `Trait.prototype`
+ // that will have more relevant behavior.
+ overrideBuiltInMethods(object, Trait.prototype);
+
+ // Trying to define given `properties` on the `object`. We use our custom
+ // `defineProperties` function instead of build-in `Object.defineProperties`
+ // that behaves exactly the same, except that it will throw if any
+ // property in the given `properties` descriptor is marked as "required" or
+ // "conflict" property.
+ return defineProperties(object, properties);
+}
+
+/**
+ * Composes new trait. If two or more traits have own properties with the
+ * same name, the new trait will contain a "conflict" property for that name.
+ * "compose" is a commutative and associative operation, and the order of its
+ * arguments is not significant.
+ *
+ * **Note:** Use `Trait.compose` instead of calling this function with more
+ * than one argument. The multiple-argument functionality is strictly for
+ * backward compatibility.
+ *
+ * @params {Object} trait
+ * Takes traits as an arguments
+ * @returns {Object}
+ * New trait containing the combined own properties of all the traits.
+ * @example
+ * var newTrait = compose(trait_1, trait_2, ..., trait_N)
+ */
+function Trait(trait1, trait2) {
+
+ // If the function was called with one argument, the argument should be
+ // an object whose properties are mapped to property descriptors on a new
+ // instance of Trait, so we delegate to the trait function.
+ // If the function was called with more than one argument, those arguments
+ // should be instances of Trait or plain property descriptor maps
+ // whose properties should be mixed into a new instance of Trait,
+ // so we delegate to the compose function.
+
+ return trait2 === undefined ? trait(trait1) : compose.apply(null, arguments);
+}
+
+Object.freeze(Object.defineProperties(Trait.prototype, {
+ toString: {
+ value: function toString() {
+ return "[object " + this.constructor.name + "]";
+ }
+ },
+
+ /**
+ * `create` is like `Object.create`, except that it ensures that:
+ * - An exception is thrown if this trait defines a property that is
+ * marked as required property and same named property is not
+ * found in a given `prototype`.
+ * - An exception is thrown if this trait contains property that is
+ * marked as "conflict" property.
+ * @param {Object}
+ * prototype of the compared object
+ * @returns {Object}
+ * An object with all of the properties described by the trait.
+ */
+ create: {
+ value: function createTrait(prototype) {
+ return create(undefined === prototype ? Object.prototype : prototype,
+ this);
+ },
+ enumerable: true
+ },
+
+ /**
+ * Composes a new resolved trait, with all the same properties as the original
+ * trait, except that all properties whose name is an own property of
+ * `resolutions` will be renamed to the value of `resolutions[name]`. If
+ * `resolutions[name]` is `null`, the property is marked as "required".
+ * @param {Object} resolutions
+ * An object whose own properties serve as a mapping from old names to new
+ * names, or to `null` if the property should be excluded.
+ * @returns {Object}
+ * New trait with the same own properties as the original trait but renamed.
+ */
+ resolve: {
+ value: function resolveTrait(resolutions) {
+ return resolve(resolutions, this);
+ },
+ enumerable: true
+ }
+}));
+
+/**
+ * @see compose
+ */
+Trait.compose = Object.freeze(compose);
+Object.freeze(compose.prototype);
+
+/**
+ * Constant singleton, representing placeholder for required properties.
+ * @type {Object}
+ */
+Trait.required = Object.freeze(Object.create(Object.prototype, {
+ toString: {
+ value: Object.freeze(function toString() {
+ return "<Trait.required>";
+ })
+ }
+}));
+Object.freeze(Trait.required.toString.prototype);
+
+exports.Trait = Object.freeze(Trait);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/list.js b/tools/addon-sdk-1.7/packages/api-utils/lib/list.js
new file mode 100644
index 0000000..4c6c126
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/list.js
@@ -0,0 +1,114 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const { Trait } = require('./traits');
+
+/**
+ * @see https://jetpack.mozillalabs.com/sdk/latest/docs/#module/api-utils/list
+ */
+const Iterable = Trait.compose({
+ /**
+ * Hash map of key-values to iterate over.
+ * Note: That this property can be a getter if you need dynamic behavior.
+ * @type {Object}
+ */
+ _keyValueMap: Trait.required,
+ /**
+ * Custom iterator providing `Iterable`s enumeration behavior.
+ * @param {Boolean} onKeys
+ */
+ __iterator__: function __iterator__(onKeys, onKeyValue) {
+ let map = this._keyValueMap;
+ for (let key in map)
+ yield onKeyValue ? [key, map[key]] : onKeys ? key : map[key];
+ }
+});
+exports.Iterable = Iterable;
+
+/**
+ * An ordered collection (also known as a sequence) disallowing duplicate
+ * elements. List is composed out of `Iterable` there for it provides custom
+ * enumeration behavior that is similar to array (enumerates only on the
+ * elements of the list). List is a base trait and is meant to be a part of
+ * composition, since all of it's API is private except length property.
+ */
+const List = Trait.resolve({ toString: null }).compose({
+ _keyValueMap: null,
+ /**
+ * List constructor can take any number of element to populate itself.
+ * @params {Object|String|Number} element
+ * @example
+ * List(1,2,3).length == 3 // true
+ */
+ constructor: function List() {
+ this._keyValueMap = [];
+ for (let i = 0, ii = arguments.length; i < ii; i++)
+ this._add(arguments[i]);
+ },
+ /**
+ * Number of elements in this list.
+ * @type {Number}
+ */
+ get length() this._keyValueMap.length,
+ /**
+ * Returns a string representing this list.
+ * @returns {String}
+ */
+ toString: function toString() 'List(' + this._keyValueMap + ')',
+ /**
+ * Returns `true` if this list contains the specified `element`.
+ * @param {Object|Number|String} element
+ * @returns {Boolean}
+ */
+ _has: function _has(element) 0 <= this._keyValueMap.indexOf(element),
+ /**
+ * Appends the specified `element` to the end of this list, if it doesn't
+ * contains it. Ignores the call if `element` is already contained.
+ * @param {Object|Number|String} element
+ */
+ _add: function _add(element) {
+ let list = this._keyValueMap,
+ index = list.indexOf(element);
+ if (0 > index)
+ list.push(this._public[list.length] = element);
+ },
+ /**
+ * Removes specified `element` from this list, if it contains it.
+ * Ignores the call if `element` is not contained.
+ * @param {Object|Number|String} element
+ */
+ _remove: function _remove(element) {
+ let list = this._keyValueMap,
+ index = list.indexOf(element);
+ if (0 <= index) {
+ delete this._public[list.length - 1];
+ list.splice(index, 1);
+ for (let length = list.length; index < length; index++)
+ this._public[index] = list[index];
+ }
+ },
+ /**
+ * Removes all of the elements from this list.
+ */
+ _clear: function _clear() {
+ for (let i = 0, ii = this._keyValueMap.length; i < ii; i ++)
+ delete this._public[i];
+ this._keyValueMap.splice(0);
+ },
+ /**
+ * Custom iterator providing `List`s enumeration behavior.
+ * We cant reuse `_iterator` that is defined by `Iterable` since it provides
+ * iteration in an arbitrary order.
+ * @see https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in
+ * @param {Boolean} onKeys
+ */
+ __iterator__: function __iterator__(onKeys, onKeyValue) {
+ let array = this._keyValueMap.slice(0),
+ i = -1;
+ for each(let element in array)
+ yield onKeyValue ? [++i, element] : onKeys ? ++i : element;
+ }
+});
+exports.List = List;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/match-pattern.js b/tools/addon-sdk-1.7/packages/api-utils/lib/match-pattern.js
new file mode 100644
index 0000000..60753d2
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/match-pattern.js
@@ -0,0 +1,103 @@
+/* -*- 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/. */
+
+"use strict";
+const { URL } = require("./url");
+
+exports.MatchPattern = MatchPattern;
+
+function MatchPattern(pattern) {
+ if (typeof pattern.test == "function") {
+
+ // For compatibility with -moz-document rules, we require the RegExp's
+ // global, ignoreCase, and multiline flags to be set to false.
+ if (pattern.global) {
+ throw new Error("A RegExp match pattern cannot be set to `global` " +
+ "(i.e. //g).");
+ }
+ if (pattern.ignoreCase) {
+ throw new Error("A RegExp match pattern cannot be set to `ignoreCase` " +
+ "(i.e. //i).");
+ }
+ if (pattern.multiline) {
+ throw new Error("A RegExp match pattern cannot be set to `multiline` " +
+ "(i.e. //m).");
+ }
+
+ this.regexp = pattern;
+ }
+ else {
+ let firstWildcardPosition = pattern.indexOf("*");
+ let lastWildcardPosition = pattern.lastIndexOf("*");
+ if (firstWildcardPosition != lastWildcardPosition)
+ throw new Error("There can be at most one '*' character in a wildcard.");
+
+ if (firstWildcardPosition == 0) {
+ if (pattern.length == 1)
+ this.anyWebPage = true;
+ else if (pattern[1] != ".")
+ throw new Error("Expected a *.<domain name> string, got: " + pattern);
+ else
+ this.domain = pattern.substr(2);
+ }
+ else {
+ if (pattern.indexOf(":") == -1) {
+ throw new Error("When not using *.example.org wildcard, the string " +
+ "supplied is expected to be either an exact URL to " +
+ "match or a URL prefix. The provided string ('" +
+ pattern + "') is unlikely to match any pages.");
+ }
+
+ if (firstWildcardPosition == -1)
+ this.exactURL = pattern;
+ else if (firstWildcardPosition == pattern.length - 1)
+ this.urlPrefix = pattern.substr(0, pattern.length - 1);
+ else {
+ throw new Error("The provided wildcard ('" + pattern + "') has a '*' " +
+ "in an unexpected position. It is expected to be the " +
+ "first or the last character in the wildcard.");
+ }
+ }
+ }
+}
+
+MatchPattern.prototype = {
+
+ test: function MatchPattern_test(urlStr) {
+ try {
+ var url = URL(urlStr);
+ }
+ catch (err) {
+ return false;
+ }
+
+ // Test the URL against a RegExp pattern. For compatibility with
+ // -moz-document rules, we require the RegExp to match the entire URL,
+ // so we not only test for a match, we also make sure the matched string
+ // is the entire URL string.
+ //
+ // Assuming most URLs don't match most match patterns, we call `test` for
+ // speed when determining whether or not the URL matches, then call `exec`
+ // for the small subset that match to make sure the entire URL matches.
+ //
+ if (this.regexp && this.regexp.test(urlStr) &&
+ this.regexp.exec(urlStr)[0] == urlStr)
+ return true;
+
+ if (this.anyWebPage && /^(https?|ftp)$/.test(url.scheme))
+ return true;
+ if (this.exactURL && this.exactURL == urlStr)
+ return true;
+ if (this.domain && url.host &&
+ url.host.slice(-this.domain.length) == this.domain)
+ return true;
+ if (this.urlPrefix && 0 == urlStr.indexOf(this.urlPrefix))
+ return true;
+
+ return false;
+ }
+
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/memory.js b/tools/addon-sdk-1.7/packages/api-utils/lib/memory.js
new file mode 100644
index 0000000..be84969
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/memory.js
@@ -0,0 +1,114 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {Cc,Ci,Cu,components} = require("chrome");
+var trackedObjects = {};
+
+var Compacter = {
+ INTERVAL: 5000,
+ notify: function(timer) {
+ var newTrackedObjects = {};
+ for (let name in trackedObjects) {
+ var oldBin = trackedObjects[name];
+ var newBin = [];
+ var strongRefs = [];
+ for (var i = 0; i < oldBin.length; i++) {
+ var strongRef = oldBin[i].weakref.get();
+ if (strongRef && strongRefs.indexOf(strongRef) == -1) {
+ strongRefs.push(strongRef);
+ newBin.push(oldBin[i]);
+ }
+ }
+ if (newBin.length)
+ newTrackedObjects[name] = newBin;
+ }
+ trackedObjects = newTrackedObjects;
+ }
+};
+
+var timer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+
+timer.initWithCallback(Compacter,
+ Compacter.INTERVAL,
+ Ci.nsITimer.TYPE_REPEATING_SLACK);
+
+var track = exports.track = function track(object, bin, stackFrameNumber) {
+ var frame = components.stack.caller;
+ var weakref = Cu.getWeakReference(object);
+ if (!bin && 'constructor' in object)
+ bin = object.constructor.name;
+ if (bin == "Object")
+ bin = frame.name;
+ if (!bin)
+ bin = "generic";
+ if (!(bin in trackedObjects))
+ trackedObjects[bin] = [];
+
+ if (stackFrameNumber > 0)
+ for (var i = 0; i < stackFrameNumber; i++)
+ frame = frame.caller;
+
+ trackedObjects[bin].push({weakref: weakref,
+ created: new Date(),
+ filename: frame.filename,
+ lineNo: frame.lineNumber,
+ bin: bin});
+};
+
+var getBins = exports.getBins = function getBins() {
+ var names = [];
+ for (let name in trackedObjects)
+ names.push(name);
+ return names;
+};
+
+var getObjects = exports.getObjects = function getObjects(bin) {
+ function getLiveObjectsInBin(bin, array) {
+ for (var i = 0; i < bin.length; i++) {
+ var object = bin[i].weakref.get();
+ if (object)
+ array.push(bin[i]);
+ }
+ }
+
+ var results = [];
+ if (bin) {
+ if (bin in trackedObjects)
+ getLiveObjectsInBin(trackedObjects[bin], results);
+ } else
+ for (let name in trackedObjects)
+ getLiveObjectsInBin(trackedObjects[name], results);
+ return results;
+};
+
+var gc = exports.gc = function gc() {
+ // Components.utils.forceGC() doesn't currently perform
+ // cycle collection, which means that e.g. DOM elements
+ // won't be collected by it. Fortunately, there are
+ // other ways...
+
+ var window = Cc["@mozilla.org/appshell/appShellService;1"]
+ .getService(Ci.nsIAppShellService)
+ .hiddenDOMWindow;
+ var test_utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils);
+ test_utils.garbageCollect();
+ Compacter.notify();
+
+ // Not sure why, but sometimes it appears that we don't get
+ // them all with just one CC, so let's do it again.
+ test_utils.garbageCollect();
+};
+
+require("./unload").when(
+ function() {
+ trackedObjects = {};
+ if (timer) {
+ timer.cancel();
+ timer = null;
+ }
+ });
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/message-manager.js b/tools/addon-sdk-1.7/packages/api-utils/lib/message-manager.js
new file mode 100644
index 0000000..235586f
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/message-manager.js
@@ -0,0 +1,203 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 BAD_LISTENER = "The event listener must be a function.";
+
+const { Cc, Ci, Cu, CC } = require("chrome");
+const { setTimeout } = require("./timer");
+
+const { ns } = require("./namespace");
+
+const { curry, invoke } = require("./functional");
+
+const Sandbox = require("./sandbox");
+
+// JSON.stringify is buggy with cross-sandbox values,
+// it may return "{}" on functions. Use a replacer to match them correctly.
+const jsonFixer = function (k, v) typeof v === "function" ? undefined : v;
+
+/**
+ * Defers invoking the function until the current call stack has cleared.
+ *
+ * @param {Function} fn
+ * The function to defer.
+ *
+ * @returns {Function}
+ * The deferred function
+ */
+const defer = function(fn) function() {
+ setTimeout(invoke, 0, fn, arguments, this)
+};
+
+/**
+ * Adds a message listener.
+ * This listener will receive messages sent from the remote frame.
+ *
+ * @param {String} name
+ * The name of the message for which to add a listener.
+ * @param {Function} listener
+ * The listener function called when the message is received.
+ */
+function addMessageListener(name, listener) {
+ if (typeof listener !== "function")
+ throw new Error(BAD_LISTENER);
+
+ let listeners = frame(this).listeners;
+
+ if (name in listeners) {
+ if (~listeners[name].indexOf(listener))
+ return;
+ } else {
+ listeners[name] = [];
+ }
+
+ listeners[name].push(listener);
+}
+
+/**
+ * Removes a message listener previously added by calling addMessageListener.
+ *
+ * @param {String} name
+ * The name of the message for which to remove a listener.
+ * @param {Function} listener
+ * The listener function has to be removed.
+ */
+function removeMessageListener(name, listener) {
+ if (typeof listener !== "function")
+ throw new Error(BAD_LISTENER);
+
+ let listeners = frame(this).listeners;
+
+ if (!(name in listeners))
+ return;
+
+ let index = listeners[name].indexOf(listener);
+
+ if (~index) {
+ listeners[name].splice(index, 1);
+ }
+}
+
+/**
+ * Sends a message to the listeners.
+ *
+ * @param {Boolean} sync
+ * Indicates if the call is synchronous or asynchronous
+ * @param {String} name
+ * The name of the message to send to the listeners.
+ * @param {Object} [data=null]
+ * A JSON object containing data to be delivered to the listeners.
+ *
+ * @returns {Array|undefined}
+ * An array with the return values of the listeners if `sync` is `true`,
+ * otherwise `undefined`.
+ */
+function sendMessage(sync, name, data) {
+ typeof data === "undefined" && (data = null);
+
+ let listeners = frame(frame(this).receiver).listeners;
+
+ let responses = [];
+
+ let returnValue = sync ? responses : undefined;
+
+ if (!(name in listeners))
+ return returnValue;
+
+ let json = JSON.parse(JSON.stringify(data, jsonFixer));
+
+ for each(let listener in listeners[name]) {
+ try {
+ let response = listener.call(null, {
+ sync : sync,
+ name : name,
+ json : json,
+ target : null
+ });
+
+ if (sync) {
+ if (typeof response === "undefined")
+ responses.push(response);
+ else
+ responses.push(JSON.parse(JSON.stringify(response, jsonFixer)));
+ }
+
+ } catch (e) {
+ console.exception(e);
+ }
+ }
+ return returnValue;
+};
+
+let sendSyncMessage = curry(sendMessage, true);
+let sendAsyncMessage = curry(defer(sendMessage), false);
+
+let frame = ns({receiver: null, listeners: null});
+
+/**
+ * The MessageManager object emulates the Message Manager API, without creating
+ * new processes. It useful in mono process context, like Fennec.
+ *
+ * @see
+ * https://developer.mozilla.org/en/The_message_manager
+ */
+function MessageManager() {
+
+ let sandbox = Sandbox.sandbox(null, { wantXrays : false });
+
+ Object.defineProperties(sandbox, {
+ addMessageListener: {value: addMessageListener.bind(sandbox)},
+
+ removeMessageListener: { value: removeMessageListener.bind(sandbox)},
+
+ sendAsyncMessage: {value: sendAsyncMessage.bind(sandbox)},
+
+ sendSyncMessage: { value: sendSyncMessage.bind(sandbox) }
+ });
+
+ frame(this).receiver = sandbox;
+ frame(sandbox).receiver = this;
+
+ frame(this).listeners = {};
+ frame(sandbox).listeners = {};
+}
+
+MessageManager.prototype = {
+ constructor: MessageManager,
+
+ addMessageListener : addMessageListener,
+
+ removeMessageListener : removeMessageListener,
+
+ sendAsyncMessage : sendAsyncMessage,
+
+ /**
+ * Loads a script into the remote frame.
+ *
+ * @param {String} uri
+ * The URL of the script to load into the frame; this must be an absolute
+ * local URL, but data: URLs are supported.
+ * @param {Boolean} allowDelayedLoad
+ * Not used.
+ */
+ loadFrameScript: function loadFrameScript(uri, async) {
+ if (arguments.length < loadFrameScript.length)
+ throw new Error("Not enough arguments");
+
+ let sandbox = frame(this).receiver;
+
+ try {
+ Sandbox.load(sandbox, uri);
+ } catch (e) {
+ console.exception(e)
+ }
+ }
+}
+
+Object.freeze(MessageManager);
+Object.freeze(MessageManager.prototype);
+
+exports.MessageManager = MessageManager;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/namespace.js b/tools/addon-sdk-1.7/packages/api-utils/lib/namespace.js
new file mode 100644
index 0000000..612910d
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/namespace.js
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// This is a temporary workaround until bug 673468 is fixed, which causes
+// entries associated with `XPCWrappedNative` wrapped keys to be GC-ed. To
+// workaround that we create a cross reference with an object from the same
+// compartment as `WeakMap` and use that as a key. Cross reference prevents
+// wrapper to be GC-ed until reference to it's value is kept.
+function handle(target) {
+ return target[handle.key] || Object.defineProperty(target, handle.key, {
+ value: { '::': target },
+ enumerable: false,
+ configurable: false,
+ writable: false
+ })[handle.key];
+}
+handle.key = '::ns::' + Math.round(Math.random() * 100000000000000000);
+
+/**
+ * Function creates a new namespace. Optionally `prototype` object may be
+ * passed, in which case namespace objects will inherit from it. Returned value
+ * is a function that can be used to get access to the namespaced properties
+ * for the passed object.
+ * @examples
+ * const ns = Namespace();
+ * ns(myObject).secret = secret;
+ */
+exports.Namespace = function Namespace(prototype) {
+ prototype = prototype || Object.prototype;
+ const map = new WeakMap();
+ return function namespace(target) {
+ let key = handle(target);
+ return map.get(key) ||
+ map.set(key, Object.create(prototype)), map.get(key);
+ };
+};
+
+// `Namespace` is a e4x function in the scope, so we export the function also as
+// `ns` as alias to avoid clashing.
+exports.ns = exports.Namespace;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/observer-service.js b/tools/addon-sdk-1.7/packages/api-utils/lib/observer-service.js
new file mode 100644
index 0000000..04222c7
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/observer-service.js
@@ -0,0 +1,175 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { Cc, Ci } = require("chrome");
+const { Unknown } = require("./xpcom");
+const { when: unload } = require("./unload");
+const memory = require("./memory");
+
+
+/**
+ * A service for adding, removing and notifying observers of notifications.
+ * Wraps the nsIObserverService interface.
+ *
+ * @version 0.2
+ */
+
+var service = Cc["@mozilla.org/observer-service;1"].
+ getService(Ci.nsIObserverService);
+
+/**
+ * A cache of observers that have been added.
+ *
+ * We use this to remove observers when a caller calls |Observers.remove|.
+ */
+var cache = [];
+
+/**
+ * Topics specifically available to Jetpack-generated extensions.
+ *
+ * Using these predefined consts instead of the platform strings is good:
+ * - allows us to scope topics specifically for Jetpacks
+ * - addons aren't dependent on strings nor behavior of core platform topics
+ * - the core platform topics are not clearly named
+ *
+ */
+exports.topics = {
+ /**
+ * A topic indicating that the application is in a state usable
+ * by add-ons.
+ */
+ get APPLICATION_READY() packaging.jetpackID + "_APPLICATION_READY"
+};
+
+/**
+ * Register the given callback as an observer of the given topic.
+ *
+ * @param topic {String}
+ * the topic to observe
+ *
+ * @param callback {Object}
+ * the callback; an Object that implements nsIObserver or a Function
+ * that gets called when the notification occurs
+ *
+ * @param target {Object} [optional]
+ * the object to use as |this| when calling a Function callback
+ *
+ * @returns the observer
+ */
+var add = exports.add = function add(topic, callback, target) {
+ var observer = Observer.new(topic, callback, target);
+ service.addObserver(observer, topic, true);
+ cache.push(observer);
+
+ return observer;
+};
+
+/**
+ * Unregister the given callback as an observer of the given topic.
+ *
+ * @param topic {String}
+ * the topic being observed
+ *
+ * @param callback {Object}
+ * the callback doing the observing
+ *
+ * @param target {Object} [optional]
+ * the object being used as |this| when calling a Function callback
+ */
+var remove = exports.remove = function remove(topic, callback, target) {
+ // This seems fairly inefficient, but I'm not sure how much better
+ // we can make it. We could index by topic, but we can't index by callback
+ // or target, as far as I know, since the keys to JavaScript hashes
+ // (a.k.a. objects) can apparently only be primitive values.
+ let observers = cache.filter(function(v) {
+ return (v.topic == topic &&
+ v.callback == callback &&
+ v.target == target);
+ });
+
+ if (observers.length) {
+ service.removeObserver(observers[0], topic);
+ cache.splice(cache.indexOf(observers[0]), 1);
+ }
+};
+
+/**
+ * Notify observers about something.
+ *
+ * @param topic {String}
+ * the topic to notify observers about
+ *
+ * @param subject {Object} [optional]
+ * some information about the topic; can be any JS object or primitive
+ *
+ * @param data {String} [optional] [deprecated]
+ * some more information about the topic; deprecated as the subject
+ * is sufficient to pass all needed information to the JS observers
+ * that this module targets; if you have multiple values to pass to
+ * the observer, wrap them in an object and pass them via the subject
+ * parameter (i.e.: { foo: 1, bar: "some string", baz: myObject })
+ */
+var notify = exports.notify = function notify(topic, subject, data) {
+ subject = (typeof subject == "undefined") ? null : Subject.new(subject);
+ data = (typeof data == "undefined") ? null : data;
+ service.notifyObservers(subject, topic, data);
+};
+
+const Observer = Unknown.extend({
+ initialize: function initialize(topic, callback, target) {
+ memory.track(this);
+ this.topic = topic;
+ this.callback = callback;
+ this.target = target;
+ },
+ interfaces: [ 'nsIObserver', 'nsISupportsWeakReference' ],
+ observe: function(subject, topic, data) {
+ // Extract the wrapped object for subjects that are one of our
+ // wrappers around a JS object. This way we support both wrapped
+ // subjects created using this module and those that are real
+ // XPCOM components.
+ if (subject && typeof subject == "object" &&
+ ("wrappedJSObject" in subject) &&
+ ("observersModuleSubjectWrapper" in subject.wrappedJSObject))
+ subject = subject.wrappedJSObject.object;
+
+ try {
+ if (typeof this.callback == "function") {
+ if (this.target)
+ this.callback.call(this.target, subject, data);
+ else
+ this.callback(subject, data);
+ } else // typeof this.callback == "object" (nsIObserver)
+ this.callback.observe(subject, topic, data);
+ } catch (e) {
+ console.exception(e);
+ }
+ }
+});
+
+const Subject = Unknown.extend({
+ initialize: function initialize(object) {
+ // Double-wrap the object and set a property identifying the
+ // wrappedJSObject as one of our wrappers to distinguish between
+ // subjects that are one of our wrappers (which we should unwrap
+ // when notifying our observers) and those that are real JS XPCOM
+ // components (which we should pass through unaltered).
+ this.wrappedJSObject = {
+ observersModuleSubjectWrapper: true,
+ object: object
+ };
+ },
+ getHelperForLanguage: function() {},
+ getInterfaces: function() {}
+});
+
+unload(function() {
+ // Make a copy of cache first, since cache will be changing as we
+ // iterate through it.
+ cache.slice().forEach(function({ topic, callback, target }) {
+ remove(topic, callback, target);
+ });
+})
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/passwords/utils.js b/tools/addon-sdk-1.7/packages/api-utils/lib/passwords/utils.js
new file mode 100644
index 0000000..5dc6098
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/passwords/utils.js
@@ -0,0 +1,101 @@
+/* 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/. */
+
+"use strict";
+
+const { Cc, Ci, components: { Constructor: CConstructor } } = require("chrome");
+const { uri: ADDON_URI } = require("self");
+const loginManager = Cc["@mozilla.org/login-manager;1"].
+ getService(Ci.nsILoginManager);
+const { URL: parseURL } = require("../url");
+const LoginInfo = CConstructor("@mozilla.org/login-manager/loginInfo;1",
+ "nsILoginInfo", "init");
+
+function filterMatchingLogins(loginInfo)
+ Object.keys(this).every(function(key) loginInfo[key] === this[key], this);
+
+/**
+ * Removes `user`, `password` and `path` fields from the given `url` if it's
+ * 'http', 'https' or 'ftp'. All other URLs are returned unchanged.
+ * @example
+ * http://user:pass@www.site.com/foo/?bar=baz#bang -> http://www.site.com
+ */
+function normalizeURL(url) {
+ let { scheme, host, port } = parseURL(url);
+ // We normalize URL only if it's `http`, `https` or `ftp`. All other types of
+ // URLs (`resource`, `chrome`, etc..) should not be normalized as they are
+ // used with add-on associated credentials path.
+ return scheme === "http" || scheme === "https" || scheme === "ftp" ?
+ scheme + "://" + (host || "") + (port ? ":" + port : "") :
+ url
+}
+
+function Login(options) {
+ let login = Object.create(Login.prototype);
+ Object.keys(options || {}).forEach(function(key) {
+ if (key === 'url')
+ login.hostname = normalizeURL(options.url);
+ else if (key === 'formSubmitURL')
+ login.formSubmitURL = options.formSubmitURL ?
+ normalizeURL(options.formSubmitURL) : null;
+ else if (key === 'realm')
+ login.httpRealm = options.realm;
+ else
+ login[key] = options[key];
+ });
+
+ return login;
+}
+Login.prototype.toJSON = function toJSON() {
+ return {
+ url: this.hostname || ADDON_URI,
+ realm: this.httpRealm || null,
+ formSubmitURL: this.formSubmitURL || null,
+ username: this.username || null,
+ password: this.password || null,
+ usernameField: this.usernameField || '',
+ passwordField: this.passwordField || '',
+ }
+};
+Login.prototype.toLoginInfo = function toLoginInfo() {
+ let { url, realm, formSubmitURL, username, password, usernameField,
+ passwordField } = this.toJSON();
+
+ return new LoginInfo(url, formSubmitURL, realm, username, password,
+ usernameField, passwordField);
+};
+
+function loginToJSON(value) Login(value).toJSON()
+
+/**
+ * Returns array of `nsILoginInfo` objects that are stored in the login manager
+ * and have all the properties with matching values as a given `options` object.
+ * @param {Object} options
+ * @returns {nsILoginInfo[]}
+ */
+exports.search = function search(options) {
+ return loginManager.getAllLogins()
+ .filter(filterMatchingLogins, Login(options))
+ .map(loginToJSON);
+};
+
+/**
+ * Stores login info created from the given `options` to the applications
+ * built-in login management system.
+ * @param {Object} options.
+ */
+exports.store = function store(options) {
+ loginManager.addLogin(Login(options).toLoginInfo());
+};
+
+/**
+ * Removes login info from the applications built-in login management system.
+ * _Please note: When removing a login info the specified properties must
+ * exactly match to the one that is already stored or exception will be thrown._
+ * @param {Object} options.
+ */
+exports.remove = function remove(options) {
+ loginManager.removeLogin(Login(options).toLoginInfo());
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/plain-text-console.js b/tools/addon-sdk-1.7/packages/api-utils/lib/plain-text-console.js
new file mode 100644
index 0000000..d76fddb
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/plain-text-console.js
@@ -0,0 +1,82 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {Cc,Ci} = require("chrome");
+
+function stringify(arg) {
+ try {
+ return String(arg);
+ }
+ catch(ex) {
+ return "<toString() error>";
+ }
+}
+
+function stringifyArgs(args) {
+ return Array.map(args, stringify).join(" ");
+}
+
+function message(print, level, args) {
+ print(level + ": " + stringifyArgs(args) + "\n", level);
+}
+
+var Console = exports.PlainTextConsole = function PlainTextConsole(print) {
+ if (!print)
+ print = dump;
+ if (print === dump) {
+ // If we're just using dump(), auto-enable preferences so
+ // that the developer actually sees the console output.
+ var prefs = Cc["@mozilla.org/preferences-service;1"]
+ .getService(Ci.nsIPrefBranch);
+ prefs.setBoolPref("browser.dom.window.dump.enabled", true);
+ }
+ this.print = print;
+
+ // Binding all the public methods to an instance so that they can be used
+ // as callback / listener functions straightaway.
+ this.log = this.log.bind(this);
+ this.info = this.info.bind(this);
+ this.warn = this.warn.bind(this);
+ this.error = this.error.bind(this);
+ this.debug = this.debug.bind(this);
+ this.exception = this.exception.bind(this);
+ this.trace = this.trace.bind(this);
+};
+
+Console.prototype = {
+ log: function log() {
+ message(this.print, "info", arguments);
+ },
+
+ info: function info() {
+ message(this.print, "info", arguments);
+ },
+
+ warn: function warn() {
+ message(this.print, "warning", arguments);
+ },
+
+ error: function error() {
+ message(this.print, "error", arguments);
+ },
+
+ debug: function debug() {
+ message(this.print, "debug", arguments);
+ },
+
+ exception: function exception(e) {
+ var fullString = ("An exception occurred.\n" +
+ require("./traceback").format(e) + "\n" + e);
+ this.error(fullString);
+ },
+
+ trace: function trace() {
+ var traceback = require("./traceback");
+ var stack = traceback.get();
+ stack.splice(-1, 1);
+ message(this.print, "info", [traceback.format(stack)]);
+ }
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/preferences-service.js b/tools/addon-sdk-1.7/packages/api-utils/lib/preferences-service.js
new file mode 100644
index 0000000..c5ffb2a
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/preferences-service.js
@@ -0,0 +1,124 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// The minimum and maximum integers that can be set as preferences.
+// The range of valid values is narrower than the range of valid JS values
+// because the native preferences code treats integers as NSPR PRInt32s,
+// which are 32-bit signed integers on all platforms.
+const MAX_INT = 0x7FFFFFFF;
+const MIN_INT = -0x80000000;
+
+const {Cc,Ci,Cr} = require("chrome");
+
+const prefService = Cc["@mozilla.org/preferences-service;1"].
+ getService(Ci.nsIPrefService);
+const prefSvc = prefService.getBranch(null);
+const defaultBranch = prefService.getDefaultBranch(null);
+
+var get = exports.get = function get(name, defaultValue) {
+ switch (prefSvc.getPrefType(name)) {
+ case Ci.nsIPrefBranch.PREF_STRING:
+ return prefSvc.getComplexValue(name, Ci.nsISupportsString).data;
+
+ case Ci.nsIPrefBranch.PREF_INT:
+ return prefSvc.getIntPref(name);
+
+ case Ci.nsIPrefBranch.PREF_BOOL:
+ return prefSvc.getBoolPref(name);
+
+ case Ci.nsIPrefBranch.PREF_INVALID:
+ return defaultValue;
+
+ default:
+ // This should never happen.
+ throw new Error("Error getting pref " + name +
+ "; its value's type is " +
+ prefSvc.getPrefType(name) +
+ ", which I don't know " +
+ "how to handle.");
+ }
+};
+
+var set = exports.set = function set(name, value) {
+ var prefType;
+ if (typeof value != "undefined" && value != null)
+ prefType = value.constructor.name;
+
+ switch (prefType) {
+ case "String":
+ {
+ var string = Cc["@mozilla.org/supports-string;1"].
+ createInstance(Ci.nsISupportsString);
+ string.data = value;
+ prefSvc.setComplexValue(name, Ci.nsISupportsString, string);
+ }
+ break;
+
+ case "Number":
+ // We throw if the number is outside the range or not an integer, since
+ // the result will not be what the consumer wanted to store.
+ if (value > MAX_INT || value < MIN_INT)
+ throw new Error("you cannot set the " + name +
+ " pref to the number " + value +
+ ", as number pref values must be in the signed " +
+ "32-bit integer range -(2^31) to 2^31-1. " +
+ "To store numbers outside that range, store " +
+ "them as strings.");
+ if (value % 1 != 0)
+ throw new Error("cannot store non-integer number: " + value);
+ prefSvc.setIntPref(name, value);
+ break;
+
+ case "Boolean":
+ prefSvc.setBoolPref(name, value);
+ break;
+
+ default:
+ throw new Error("can't set pref " + name + " to value '" + value +
+ "'; it isn't a string, integer, or boolean");
+ }
+};
+
+var has = exports.has = function has(name) {
+ return (prefSvc.getPrefType(name) != Ci.nsIPrefBranch.PREF_INVALID);
+};
+
+var isSet = exports.isSet = function isSet(name) {
+ return (has(name) && prefSvc.prefHasUserValue(name));
+};
+
+var reset = exports.reset = function reset(name) {
+ try {
+ prefSvc.clearUserPref(name);
+ } catch (e if e.result == Cr.NS_ERROR_UNEXPECTED) {
+ // The pref service throws NS_ERROR_UNEXPECTED when the caller tries
+ // to reset a pref that doesn't exist or is already set to its default
+ // value. This interface fails silently in those cases, so callers
+ // can unconditionally reset a pref without having to check if it needs
+ // resetting first or trap exceptions after the fact. It passes through
+ // other exceptions, however, so callers know about them, since we don't
+ // know what other exceptions might be thrown and what they might mean.
+ }
+};
+
+exports.getLocalized = function getLocalized(name, defaultValue) {
+ let value = null;
+ try {
+ value = prefSvc.getComplexValue(name, Ci.nsIPrefLocalizedString).data;
+ }
+ finally {
+ return value || defaultValue;
+ }
+}
+
+exports.setLocalized = function setLocalized(name, value) {
+ // We can't use `prefs.set` here as we have to use `getDefaultBranch`
+ // (instead of `getBranch`) in order to have `mIsDefault` set to true, here:
+ // http://mxr.mozilla.org/mozilla-central/source/modules/libpref/src/nsPrefBranch.cpp#233
+ // Otherwise, we do not enter into this expected condition:
+ // http://mxr.mozilla.org/mozilla-central/source/modules/libpref/src/nsPrefBranch.cpp#244
+ defaultBranch.setCharPref(name, value);
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/process.js b/tools/addon-sdk-1.7/packages/api-utils/lib/process.js
new file mode 100644
index 0000000..68ef2f9
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/process.js
@@ -0,0 +1,76 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { Cc, Ci } = require("chrome");
+const { createRemoteBrowser } = require("api-utils/window-utils");
+const { channel } = require("./channel");
+const packaging = require('@packaging');
+const { when } = require('./unload');
+const { MessageManager } = require('./message-manager');
+
+const addonService = '@mozilla.org/addon/service;1' in Cc ?
+ Cc['@mozilla.org/addon/service;1'].getService(Ci.nsIAddonService) : null
+
+const ENABLE_E10S = packaging.enable_e10s;
+
+const isFennec = require("./xul-app").is("Fennec");
+
+function loadScript(target, uri, sync) {
+ return 'loadScript' in target ? target.loadScript(uri, sync)
+ : target.loadFrameScript(uri, sync)
+}
+
+function process(target, id, path, scope) {
+ // Please note that even though `loadScript`, is executed before channel is
+ // returned, users still are able to subscribe for messages before any message
+ // will be sent. That's because `loadScript` queues script execution on the
+ // other process, which means they will execute async (on the next turn of
+ // event loop), while the channel for messages is returned immediately (in
+ // the same turn of event loop).
+
+ let load = loadScript.bind(null, target);
+
+ load(packaging.uriPrefix + packaging.loader, false);
+ load('data:,' + encodeURIComponent(
+ 'let loader = Loader.new(' + JSON.stringify(packaging) + ');\n' +
+ 'loader.main("' + id + '", "' + path + '");'), false);
+
+ when(function (reason) {
+ // Please note that it's important to unload remote loader
+ // synchronously (using synchronous frame script), to make sure that we
+ // don't stop during unload.
+ // Bug 724433: Take care to nullify all globals set by `cuddlefish.js`
+ // otherwise, we will leak any still defined global.
+ // `dump` is set in Loader.new method, `dump = globals.dump;`
+ load('data:,loader.unload("' + reason + '");' +
+ 'loader = null; Loader = null; dump = null;', true);
+ });
+
+ return {
+ channel: channel.bind(null, scope, target),
+ loadScript: load
+ };
+}
+
+exports.spawn = function spawn(id, path) {
+ return function promise(deliver) {
+ // If `nsIAddonService` is available we use it to create an add-on process,
+ // otherwise we fallback to the remote browser's message manager.
+ if (ENABLE_E10S && addonService) {
+ console.log('!!!!!!!!!!!!!!!!!!!! Using addon process !!!!!!!!!!!!!!!!!!');
+ deliver(process(addonService.createAddon(), id, path));
+ } else if (isFennec) {
+ deliver(process(new MessageManager(), id, path));
+ } else {
+ createRemoteBrowser(ENABLE_E10S)(function(browser) {
+ let messageManager = browser.QueryInterface(Ci.nsIFrameLoaderOwner).
+ frameLoader.messageManager
+ let window = browser.ownerDocument.defaultView;
+ deliver(process(messageManager, id, path, window));
+ });
+ }
+ };
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/promise.js b/tools/addon-sdk-1.7/packages/api-utils/lib/promise.js
new file mode 100644
index 0000000..7c8d7a5
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/promise.js
@@ -0,0 +1,219 @@
+/* vim:set ts=2 sw=2 sts=2 expandtab */
+/*jshint asi: true undef: true es5: true node: true devel: true
+ forin: true latedef: false supernew: true */
+/*global define: true */
+!function(factory) {
+ if (typeof(define) === 'function') { // RequireJS
+ define(factory)
+ } else if (typeof(exports) === 'object') { // CommonJS
+ factory(require, exports)
+ } else if (~String(this).indexOf('BackstagePass')) { // JSM
+ factory(undefined, this)
+ this.EXPORTED_SYMBOLS = Object.keys(this)
+ } else {
+ factory(undefined, (this.promise = {}))
+ }
+}.call(this, function(require, exports) {
+
+'use strict';
+
+function resolution(value) {
+ /**
+ Returns non-standard compliant (`then` does not returns a promise) promise
+ that resolves to a given `value`. Used just internally only.
+ **/
+ return { then: function then(resolve) { resolve(value) } }
+}
+
+function rejection(reason) {
+ /**
+ Returns non-standard compliant promise (`then` does not returns a promise)
+ that rejects with a given `reason`. This is used internally only.
+ **/
+ return { then: function then(resolve, reject) { reject(reason) } }
+}
+
+function attempt(f) {
+ /**
+ Returns wrapper function that delegates to `f`. If `f` throws then captures
+ error and returns promise that rejects with a thrown error. Otherwise returns
+ return value. (Internal utility)
+ **/
+ return function effort(options) {
+ try { return f(options) }
+ catch(error) { return rejection(error) }
+ }
+}
+
+function isPromise(value) {
+ /**
+ Returns true if given `value` is promise. Value is assumed to be promise if
+ it implements `then` method.
+ **/
+ return value && typeof(value.then) === 'function'
+}
+
+function defer(prototype) {
+ /**
+ Returns object containing following properties:
+ - `promise` Eventual value representation implementing CommonJS [Promises/A]
+ (http://wiki.commonjs.org/wiki/Promises/A) API.
+ - `resolve` Single shot function that resolves returned `promise` with a given
+ `value` argument.
+ - `reject` Single shot function that rejects returned `promise` with a given
+ `reason` argument.
+
+ Given `prototype` argument is used as a prototype of the returned `promise`
+ allowing one to implement additional API. If prototype is not passed then
+ it falls back to `Object.prototype`.
+
+ ## Examples
+
+ // Simple usage.
+ var deferred = defer()
+ deferred.promise.then(console.log, console.error)
+ deferred.resolve(value)
+
+ // Advanced usage
+ var prototype = {
+ get: function get(name) {
+ return this.then(function(value) {
+ return value[name];
+ })
+ }
+ }
+
+ var foo = defer(prototype)
+ deferred.promise.get('name').then(console.log)
+ deferred.resolve({ name: 'Foo' })
+ //=> 'Foo'
+ */
+ var pending = [], result
+ prototype = (prototype || prototype === null) ? prototype : Object.prototype
+
+ // Create an object implementing promise API.
+ var promise = Object.create(prototype, {
+ then: { value: function then(resolve, reject) {
+ // create a new deferred using a same `prototype`.
+ var deferred = defer(prototype)
+ // If `resolve / reject` callbacks are not provided.
+ resolve = resolve ? attempt(resolve) : resolution
+ reject = reject ? attempt(reject) : rejection
+
+ // Create a listeners for a enclosed promise resolution / rejection that
+ // delegate to an actual callbacks and resolve / reject returned promise.
+ function resolved(value) { deferred.resolve(resolve(value)) }
+ function rejected(reason) { deferred.resolve(reject(reason)) }
+
+ // If promise is pending register listeners. Otherwise forward them to
+ // resulting resolution.
+ if (pending) pending.push([ resolved, rejected ])
+ else result.then(resolved, rejected)
+
+ return deferred.promise
+ }}
+ })
+
+ var deferred = {
+ promise: promise,
+ resolve: function resolve(value) {
+ /**
+ Resolves associated `promise` to a given `value`, unless it's already
+ resolved or rejected.
+ **/
+ if (pending) {
+ // store resolution `value` as a promise (`value` itself may be a
+ // promise), so that all subsequent listeners can be forwarded to it,
+ // which either resolves immediately or forwards if `value` is
+ // a promise.
+ result = isPromise(value) ? value : resolution(value)
+ // forward all pending observers.
+ while (pending.length) result.then.apply(result, pending.shift())
+ // mark promise as resolved.
+ pending = null
+ }
+ },
+ reject: function reject(reason) {
+ /**
+ Rejects associated `promise` with a given `reason`, unless it's already
+ resolved / rejected.
+ **/
+ deferred.resolve(rejection(reason))
+ }
+ }
+
+ return deferred
+}
+exports.defer = defer
+
+function resolve(value, prototype) {
+ /**
+ Returns a promise resolved to a given `value`. Optionally second `prototype`
+ arguments my be provided to be used as a prototype for a returned promise.
+ **/
+ var deferred = defer(prototype)
+ deferred.resolve(value)
+ return deferred.promise
+}
+exports.resolve = resolve
+
+function reject(reason, prototype) {
+ /**
+ Returns a promise that is rejected with a given `reason`. Optionally second
+ `prototype` arguments my be provided to be used as a prototype for a returned
+ promise.
+ **/
+ var deferred = defer(prototype)
+ deferred.reject(reason)
+ return deferred.promise
+}
+exports.reject = reject
+
+var promised = (function() {
+ // Note: Define shortcuts and utility functions here in order to avoid
+ // slower property accesses and unnecessary closure creations on each
+ // call of this popular function.
+
+ var call = Function.call
+ var concat = Array.prototype.concat
+
+ // Utility function that does following:
+ // execute([ f, self, args...]) => f.apply(self, args)
+ function execute(args) { return call.apply(call, args) }
+
+ // Utility function that takes promise of `a` array and maybe promise `b`
+ // as arguments and returns promise for `a.concat(b)`.
+ function promisedConcat(promises, unknown) {
+ return promises.then(function(values) {
+ return resolve(unknown).then(function(value) {
+ return values.concat(value)
+ })
+ })
+ }
+
+ return function promised(f, prototype) {
+ /**
+ Returns a wrapped `f`, which when called returns a promise that resolves to
+ `f(...)` passing all the given arguments to it, which by the way may be
+ promises. Optionally second `prototype` argument may be provided to be used
+ a prototype for a returned promise.
+
+ ## Example
+
+ var promise = promised(Array)(1, promise(2), promise(3))
+ promise.then(console.log) // => [ 1, 2, 3 ]
+ **/
+
+ return function promised() {
+ // create array of [ f, this, args... ]
+ return concat.apply([ f, this ], arguments).
+ // reduce it via `promisedConcat` to get promised array of fulfillments
+ reduce(promisedConcat, resolve([], prototype)).
+ // finally map that to promise of `f.apply(this, args...)`
+ then(execute)
+ }
+ }
+})()
+exports.promised = promised
+
+})
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/querystring.js b/tools/addon-sdk-1.7/packages/api-utils/lib/querystring.js
new file mode 100644
index 0000000..9e29515
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/querystring.js
@@ -0,0 +1,117 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+let unescape = decodeURIComponent;
+exports.unescape = unescape;
+
+// encodes a string safely for application/x-www-form-urlencoded
+// adheres to RFC 3986
+// see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/encodeURIComponent
+function escape(query) {
+ return encodeURIComponent(query).
+ replace(/%20/g, '+').
+ replace(/!/g, '%21').
+ replace(/'/g, '%27').
+ replace(/\(/g, '%28').
+ replace(/\)/g, '%29').
+ replace(/\*/g, '%2A');
+}
+exports.escape = escape;
+
+// Converts an object of unordered key-vals to a string that can be passed
+// as part of a request
+function stringify(options, separator, assigner) {
+ separator = separator || '&';
+ assigner = assigner || '=';
+ // Explicitly return null if we have null, and empty string, or empty object.
+ if (!options)
+ return '';
+
+ // If content is already a string, just return it as is.
+ if (typeof(options) == 'string')
+ return options;
+
+ // 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
+ // `escape` 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(escape(key) + assigner + escape(val));
+ }
+
+ function make(key, value) {
+ if (value && typeof(value) === 'object')
+ Object.keys(value).forEach(function(name) {
+ make(key + '[' + name + ']', value[name]);
+ });
+ else
+ add(key, value);
+ }
+
+ Object.keys(options).forEach(function(name) { make(name, options[name]); });
+ return encodedContent.join(separator);
+
+ //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;
+}
+exports.stringify = stringify;
+
+// Exporting aliases that nodejs implements just for the sake of
+// interoperability.
+exports.encode = stringify;
+exports.serialize = stringify;
+
+// Note: That `stringify` and `parse` aren't bijective as we use `stringify`
+// as it was implement in request module, but implement `parse` to match nodejs
+// behavior.
+// TODO: Make `stringify` implement API as in nodejs and figure out backwards
+// compatibility.
+function parse(query, separator, assigner) {
+ separator = separator || '&';
+ assigner = assigner || '=';
+ let result = {};
+
+ if (typeof query !== 'string' || query.length === 0)
+ return result;
+
+ query.split(separator).forEach(function(chunk) {
+ let pair = chunk.split(assigner);
+ let key = unescape(pair[0]);
+ let value = unescape(pair.slice(1).join(assigner));
+
+ if (!(key in result))
+ result[key] = value;
+ else if (Array.isArray(result[key]))
+ result[key].push(value);
+ else
+ result[key] = [result[key], value];
+ });
+
+ return result;
+};
+exports.parse = parse;
+// Exporting aliases that nodejs implements just for the sake of
+// interoperability.
+exports.decode = parse;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/runtime.js b/tools/addon-sdk-1.7/packages/api-utils/lib/runtime.js
new file mode 100644
index 0000000..845d146
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/runtime.js
@@ -0,0 +1,15 @@
+/* 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/. */
+
+"use strict";
+
+const { Cc, Ci } = require("chrome");
+const runtime = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
+
+exports.inSafeMode = runtime.inSafeMode;
+exports.OS = runtime.OS;
+exports.processType = runtime.processType;
+exports.widgetToolkit = runtime.widgetToolkit;
+exports.XPCOMABI = runtime.XPCOMABI;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/sandbox.js b/tools/addon-sdk-1.7/packages/api-utils/lib/sandbox.js
new file mode 100644
index 0000000..6130637
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/sandbox.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: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const { Cc, Ci, CC, Cu } = require('chrome');
+const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')();
+const scriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1'].
+ getService(Ci.mozIJSSubScriptLoader);
+
+/**
+ * Make a new sandbox that inherits given `source`'s principals. Source can be
+ * URI string, DOMWindow or `null` for system principals.
+ */
+function sandbox(target, options) {
+ return Cu.Sandbox(target || systemPrincipal, options || {});
+}
+exports.sandbox = sandbox
+
+/**
+ * Evaluates given `source` in a given `sandbox` and returns result.
+ */
+function evaluate(sandbox, code, uri, line, version) {
+ return Cu.evalInSandbox(code, sandbox, version || '1.8', uri || '', line || 1);
+}
+exports.evaluate = evaluate;
+
+/**
+ * Evaluates code under the given `uri` in the given `sandbox`.
+ *
+ * @param {String} uri
+ * The URL pointing to the script to load.
+ * It must be a local chrome:, resource:, file: or data: URL.
+ */
+function load(sandbox, uri) {
+ if (uri.indexOf('data:') === 0) {
+ let source = uri.substr(uri.indexOf(',') + 1);
+
+ return evaluate(sandbox, decodeURIComponent(source), '1.8', uri, 0);
+ } else {
+ return scriptLoader.loadSubScript(uri, sandbox, 'UTF-8');
+ }
+}
+exports.load = load; \ No newline at end of file
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/self!.js b/tools/addon-sdk-1.7/packages/api-utils/lib/self!.js
new file mode 100644
index 0000000..fd69f9a
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/self!.js
@@ -0,0 +1,48 @@
+/* vim:st=2:sts=2:sw=2:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { CC } = require('chrome');
+const { jetpackID, name, manifest, metadata, uriPrefix } = require('@packaging');
+
+const XMLHttpRequest = CC('@mozilla.org/xmlextras/xmlhttprequest;1',
+ 'nsIXMLHttpRequest');
+
+// Utility function that synchronously reads local resource from the given
+// `uri` and returns content string.
+function readURI(uri) {
+ let request = XMLHttpRequest();
+ request.open('GET', uri, false);
+ request.overrideMimeType('text/plain');
+ request.send();
+ return request.responseText;
+}
+
+// Some XPCOM APIs require valid URIs as an argument for certain operations (see
+// `nsILoginManager` for example). This property represents add-on associated
+// unique URI string that can be used for that.
+const uri = 'addon:' + jetpackID
+
+function url(root, path) root + (path || "")
+function read(root, path) readURI(url(root, path))
+
+exports.create = function create(base) {
+ let moduleData = manifest[base] && manifest[base].requirements['self'];
+ let root = uriPrefix + moduleData.dataURIPrefix;
+ return Object.freeze({
+ id: 'self',
+ exports: Object.freeze({
+ id: jetpackID,
+ uri: uri,
+ name: name,
+ version: metadata[name].version,
+ data: {
+ url: url.bind(null, root),
+ load: read.bind(null, root)
+ }
+ })
+ });
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/system.js b/tools/addon-sdk-1.7/packages/api-utils/lib/system.js
new file mode 100644
index 0000000..3986645
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/system.js
@@ -0,0 +1,120 @@
+/* vim:set ts=2 sw=2 sts=2 expandtab */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+const { Cc, Ci, CC } = require('chrome');
+const options = require('@packaging');
+const file = require('./file');
+const runtime = require("./runtime.js");
+
+const appStartup = Cc['@mozilla.org/toolkit/app-startup;1'].
+ getService(Ci.nsIAppStartup);
+const appInfo = Cc["@mozilla.org/xre/app-info;1"].
+ getService(Ci.nsIXULAppInfo);
+const directoryService = Cc['@mozilla.org/file/directory_service;1'].
+ getService(Ci.nsIProperties);
+
+
+const { eAttemptQuit: E_ATTEMPT, eForceQuit: E_FORCE } = appStartup;
+
+/**
+ * Parsed JSON object that was passed via `cfx --static-args "{ foo: 'bar' }"`
+ */
+exports.staticArgs = options.staticArgs;
+
+/**
+ * Environment variables. Environment variables are non-enumerable properties
+ * of this object (key is name and value is value).
+ */
+exports.env = require('./environment').env;
+
+/**
+ * Ends the process with the specified `code`. If omitted, exit uses the
+ * 'success' code 0. To exit with failure use `1`.
+ * TODO: Improve platform to actually quit with an exit code.
+ */
+exports.exit = function exit(code) {
+ // This is used by 'cfx' to find out exit code.
+ if ('resultFile' in options) {
+ let stream = file.open(options.resultFile, 'w');
+ stream.write(code ? 'FAIL' : 'OK');
+ stream.close();
+ }
+
+ appStartup.quit(code ? E_ATTEMPT : E_FORCE);
+};
+
+/**
+ * Returns a path of the system's or application's special directory / file
+ * associated with a given `id`. For list of possible `id`s please see:
+ * https://developer.mozilla.org/en/Code_snippets/File_I%2F%2FO#Getting_special_files
+ * http://mxr.mozilla.org/mozilla-central/source/xpcom/io/nsAppDirectoryServiceDefs.h
+ * @example
+ *
+ * // get firefox profile path
+ * let profilePath = require('system').pathFor('ProfD');
+ * // get OS temp files directory (/tmp)
+ * let temps = require('system').pathFor('TmpD');
+ * // get OS desktop path for an active user (~/Desktop on linux
+ * // or C:\Documents and Settings\username\Desktop on windows).
+ * let desktopPath = require('system').pathFor('Desk');
+ */
+exports.pathFor = function pathFor(id) {
+ return directoryService.get(id, Ci.nsIFile).path;
+};
+
+/**
+ * What platform you're running on (all lower case string).
+ * For possible values see:
+ * https://developer.mozilla.org/en/OS_TARGET
+ */
+exports.platform = runtime.OS.toLowerCase();
+
+/**
+ * What processor architecture you're running on:
+ * `'arm', 'ia32', or 'x64'`.
+ */
+exports.architecture = runtime.XPCOMABI.split('_')[0];
+
+/**
+ * What compiler used for build:
+ * `'msvc', 'n32', 'gcc2', 'gcc3', 'sunc', 'ibmc'...`
+ */
+exports.compiler = runtime.XPCOMABI.split('_')[1];
+
+/**
+ * The application's build ID/date, for example "2004051604".
+ */
+exports.build = appInfo.appBuildID;
+
+/**
+ * The XUL application's UUID.
+ * This has traditionally been in the form
+ * `{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}` but for some applications it may
+ * be: "appname@vendor.tld".
+ */
+exports.id = appInfo.ID;
+
+/**
+ * The name of the application.
+ */
+exports.name = appInfo.name;
+
+/**
+ * The XUL application's version, for example "0.8.0+" or "3.7a1pre".
+ */
+exports.version = appInfo.version;
+
+/**
+ * XULRunner version.
+ */
+exports.platformVersion = appInfo.platformVersion;
+
+
+/**
+ * The name of the application vendor, for example "Mozilla".
+ */
+exports.vendor = appInfo.vendor;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/tab-browser.js b/tools/addon-sdk-1.7/packages/api-utils/lib/tab-browser.js
new file mode 100644
index 0000000..13d365a
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/tab-browser.js
@@ -0,0 +1,727 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+const {Cc,Ci,Cu} = require("chrome");
+var NetUtil = {};
+Cu.import("resource://gre/modules/NetUtil.jsm", NetUtil);
+NetUtil = NetUtil.NetUtil;
+const errors = require("./errors");
+const windowUtils = require("./window-utils");
+const apiUtils = require("./api-utils");
+const collection = require("./collection");
+
+// TODO: The hard-coding of app-specific info here isn't very nice;
+// ideally such app-specific info should be more decoupled, and the
+// module should be extensible, allowing for support of new apps at
+// runtime, perhaps by inspecting supported packages (e.g. via
+// dynamically-named modules or package-defined extension points).
+
+if (!require("./xul-app").is("Firefox")) {
+ throw new Error([
+ "The tab-browser 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(""));
+}
+
+function onBrowserLoad(callback, event) {
+ if (event.target && event.target.defaultView == this) {
+ this.removeEventListener("load", onBrowserLoad, true);
+ try {
+ require("timer").setTimeout(function () {
+ callback(event);
+ }, 10);
+ } catch (e) { console.exception(e); }
+ }
+}
+
+// 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", onBrowserLoad.bind(window, callback), true);
+
+ return window;
+}
+
+// Open a URL in a new tab
+exports.addTab = function addTab(url, options) {
+ if (!options)
+ options = {};
+ options.url = url;
+
+ options = apiUtils.validateOptions(options, {
+ // TODO: take URL object instead of string (bug 564524)
+ url: {
+ is: ["string"],
+ ok: function (v) !!v,
+ msg: "The url parameter must have be a non-empty string."
+ },
+ inNewWindow: {
+ is: ["undefined", "null", "boolean"]
+ },
+ inBackground: {
+ is: ["undefined", "null", "boolean"]
+ },
+ onLoad: {
+ is: ["undefined", "null", "function"]
+ },
+ isPinned: {
+ is: ["undefined", "boolean"]
+ }
+ });
+
+ var wm = Cc["@mozilla.org/appshell/window-mediator;1"]
+ .getService(Ci.nsIWindowMediator);
+ var win = wm.getMostRecentWindow("navigator:browser");
+ if (!win || options.inNewWindow) {
+ openBrowserWindow(function(e) {
+ if(options.isPinned) {
+ //get the active tab in the recently created window
+ let mainWindow = e.target.defaultView;
+ mainWindow.gBrowser.pinTab(mainWindow.gBrowser.selectedTab);
+ }
+ require("./errors").catchAndLog(function(e) options.onLoad(e))(e);
+ }, options.url);
+ } else {
+ let tab = win.gBrowser.addTab(options.url);
+ if (!options.inBackground)
+ win.gBrowser.selectedTab = tab;
+ if (options.onLoad) {
+ let tabBrowser = win.gBrowser.getBrowserForTab(tab);
+ tabBrowser.addEventListener("load", function onLoad(e) {
+ if (e.target.defaultView.content.location == "about:blank")
+ return;
+ // remove event handler from addTab - don't want notified
+ // for subsequent loads in same tab.
+ tabBrowser.removeEventListener("load", onLoad, true);
+ require("./errors").catchAndLog(function(e) options.onLoad(e))(e);
+ }, true);
+ }
+ }
+}
+
+// Iterate over a window's tabbrowsers
+function tabBrowserIterator(window) {
+ var browsers = window.document.querySelectorAll("tabbrowser");
+ for (var i = 0; i < browsers.length; i++)
+ yield browsers[i];
+}
+
+// Iterate over a tabbrowser's tabs
+function tabIterator(tabbrowser) {
+ var tabs = tabbrowser.tabContainer;
+ for (var i = 0; i < tabs.children.length; i++) {
+ yield tabs.children[i];
+ }
+}
+
+// Tracker for all tabbrowsers across all windows,
+// or a single tabbrowser if the window is given.
+function Tracker(delegate, window) {
+ this._delegate = delegate;
+ this._browsers = [];
+ this._window = window;
+ this._windowTracker = windowUtils.WindowTracker(this);
+
+ require("./unload").ensure(this);
+}
+Tracker.prototype = {
+ __iterator__: function __iterator__() {
+ for (var i = 0; i < this._browsers.length; i++)
+ yield this._browsers[i];
+ },
+ get: function get(index) {
+ return this._browsers[index];
+ },
+ onTrack: function onTrack(window) {
+ if (this._window && window != this._window)
+ return;
+
+ for (let browser in tabBrowserIterator(window))
+ this._browsers.push(browser);
+ if (this._delegate)
+ for (let browser in tabBrowserIterator(window))
+ this._delegate.onTrack(browser);
+ },
+ onUntrack: function onUntrack(window) {
+ if (this._window && window != this._window)
+ return;
+
+ for (let browser in tabBrowserIterator(window)) {
+ let index = this._browsers.indexOf(browser);
+ if (index != -1)
+ this._browsers.splice(index, 1);
+ else
+ console.error("internal error: browser tab not found");
+ }
+ if (this._delegate)
+ for (let browser in tabBrowserIterator(window))
+ this._delegate.onUntrack(browser);
+ },
+ get length() {
+ return this._browsers.length;
+ },
+ unload: function unload() {
+ this._windowTracker.unload();
+ }
+};
+exports.Tracker = apiUtils.publicConstructor(Tracker);
+
+// Tracker for all tabs across all windows,
+// or a single window if it's given.
+function TabTracker(delegate, window) {
+ this._delegate = delegate;
+ this._tabs = [];
+ this._tracker = new Tracker(this, window);
+ require("./unload").ensure(this);
+}
+TabTracker.prototype = {
+ _TAB_EVENTS: ["TabOpen", "TabClose"],
+ _safeTrackTab: function safeTrackTab(tab) {
+ this._tabs.push(tab);
+ try {
+ this._delegate.onTrack(tab);
+ } catch (e) {
+ console.exception(e);
+ }
+ },
+ _safeUntrackTab: function safeUntrackTab(tab) {
+ var index = this._tabs.indexOf(tab);
+ if (index == -1)
+ console.error("internal error: tab not found");
+ this._tabs.splice(index, 1);
+ try {
+ this._delegate.onUntrack(tab);
+ } catch (e) {
+ console.exception(e);
+ }
+ },
+ handleEvent: function handleEvent(event) {
+ switch (event.type) {
+ case "TabOpen":
+ this._safeTrackTab(event.target);
+ break;
+ case "TabClose":
+ this._safeUntrackTab(event.target);
+ break;
+ default:
+ throw new Error("internal error: unknown event type: " +
+ event.type);
+ }
+ },
+ onTrack: function onTrack(tabbrowser) {
+ for (let tab in tabIterator(tabbrowser))
+ this._safeTrackTab(tab);
+ var self = this;
+ this._TAB_EVENTS.forEach(
+ function(eventName) {
+ tabbrowser.tabContainer.addEventListener(eventName, self, true);
+ });
+ },
+ onUntrack: function onUntrack(tabbrowser) {
+ for (let tab in tabIterator(tabbrowser))
+ this._safeUntrackTab(tab);
+ var self = this;
+ this._TAB_EVENTS.forEach(
+ function(eventName) {
+ tabbrowser.tabContainer.removeEventListener(eventName, self, true);
+ });
+ },
+ unload: function unload() {
+ this._tracker.unload();
+ }
+};
+exports.TabTracker = apiUtils.publicConstructor(TabTracker);
+
+exports.whenContentLoaded = function whenContentLoaded(callback) {
+ var cb = require("./errors").catchAndLog(function eventHandler(event) {
+ if (event.target && event.target.defaultView)
+ callback(event.target.defaultView);
+ });
+
+ var tracker = new Tracker({
+ onTrack: function(tabBrowser) {
+ tabBrowser.addEventListener("DOMContentLoaded", cb, false);
+ },
+ onUntrack: function(tabBrowser) {
+ tabBrowser.removeEventListener("DOMContentLoaded", cb, false);
+ }
+ });
+
+ return tracker;
+};
+
+exports.__defineGetter__("activeTab", function() {
+ const wm = Cc["@mozilla.org/appshell/window-mediator;1"].
+ getService(Ci.nsIWindowMediator);
+ let mainWindow = wm.getMostRecentWindow("navigator:browser");
+ return mainWindow.gBrowser.selectedTab;
+});
+
+/******************* TabModule *********************/
+
+// Supported tab events
+const events = [
+ "onActivate",
+ "onDeactivate",
+ "onOpen",
+ "onClose",
+ "onReady",
+ "onLoad",
+ "onPaint"
+];
+exports.tabEvents = events;
+
+/**
+ * TabModule
+ *
+ * Constructor for a module that implements the tabs API
+ */
+let TabModule = exports.TabModule = function TabModule(window) {
+ let self = this;
+ /**
+ * Tab
+ *
+ * Safe object representing a tab.
+ */
+ let tabConstructor = apiUtils.publicConstructor(function(element) {
+ if (!element)
+ throw new Error("no tab element.");
+ let win = element.ownerDocument.defaultView;
+ if (!win)
+ throw new Error("element has no window.");
+ if (window && win != window)
+ throw new Error("module's window and element's window don't match.");
+ let browser = win.gBrowser.getBrowserForTab(element);
+
+ this.__defineGetter__("title", function() browser.contentDocument.title);
+ this.__defineGetter__("location", function() browser.contentDocument.location);
+ this.__defineSetter__("location", function(val) browser.contentDocument.location = val);
+ this.__defineGetter__("contentWindow", function() browser.contentWindow);
+ this.__defineGetter__("contentDocument", function() browser.contentDocument);
+ this.__defineGetter__("favicon", function() {
+ let pageURI = NetUtil.newURI(browser.contentDocument.location);
+ let fs = Cc["@mozilla.org/browser/favicon-service;1"].
+ getService(Ci.nsIFaviconService);
+ let faviconURL;
+ try {
+ let faviconURI = fs.getFaviconForPage(pageURI);
+ faviconURL = fs.getFaviconDataAsDataURL(faviconURI);
+ } catch(ex) {
+ let data = getChromeURLContents("chrome://mozapps/skin/places/defaultFavicon.png");
+ let encoded = browser.contentWindow.btoa(data);
+ faviconURL = "data:image/png;base64," + encoded;
+ }
+ return faviconURL;
+ });
+ this.__defineGetter__("style", function() null); // TODO
+ this.__defineGetter__("index", function() win.gBrowser.getBrowserIndexForDocument(browser.contentDocument));
+ this.__defineGetter__("thumbnail", function() getThumbnailCanvasForTab(element, browser.contentWindow));
+
+ this.close = function() win.gBrowser.removeTab(element);
+ this.move = function(index) {
+ win.gBrowser.moveTabTo(element, index);
+ };
+
+ this.__defineGetter__("isPinned", function() element.pinned);
+ this.pin = function() win.gBrowser.pinTab(element);
+ this.unpin = function() win.gBrowser.unpinTab(element);
+
+ // Set up the event handlers
+ let tab = this;
+ events.filter(function(e) e != "onOpen").forEach(function(e) {
+ // create a collection for each event
+ collection.addCollectionProperty(tab, e);
+ // make tabs setter for each event, for adding via property assignment
+ tab.__defineSetter__(e, function(val) tab[e].add(val));
+ });
+
+ // listen for events, filtered on this tab
+ eventsTabDelegate.addTabDelegate(this);
+ });
+
+ /**
+ * tabs.activeTab
+ */
+ this.__defineGetter__("activeTab", function() {
+ try {
+ return window ? tabConstructor(window.gBrowser.selectedTab)
+ : tabConstructor(exports.activeTab);
+ }
+ catch (e) { }
+ return null;
+ });
+ this.__defineSetter__("activeTab", function(tab) {
+ let [tabElement, win] = getElementAndWindowForTab(tab, window);
+ if (tabElement) {
+ // set as active tab
+ win.gBrowser.selectedTab = tabElement;
+ // focus the window
+ win.focus();
+ }
+ });
+
+ this.open = function TM_open(options) {
+ open(options, tabConstructor, window);
+ }
+
+ // Set up the event handlers
+ events.forEach(function(eventHandler) {
+ // create a collection for each event
+ collection.addCollectionProperty(self, eventHandler);
+ // make tabs setter for each event, for adding via property assignment
+ self.__defineSetter__(eventHandler, function(val) self[eventHandler].add(val));
+ });
+
+ // Tracker that listens for tab events, and proxies
+ // them to registered event listeners.
+ let eventsTabDelegate = {
+ selectedTab: null,
+ tabs: [],
+ addTabDelegate: function TETT_addTabDelegate(tabObj) {
+ this.tabs.push(tabObj);
+ },
+ pushTabEvent: function TETT_pushTabEvent(event, tab) {
+ for (let callback in self[event]) {
+ require("./errors").catchAndLog(function(tab) {
+ callback(new tabConstructor(tab));
+ })(tab);
+ }
+
+ if (event != "onOpen") {
+ this.tabs.forEach(function(tabObj) {
+ if (tabObj[event].length) {
+ let [tabEl,] = getElementAndWindowForTab(tabObj, window);
+ if (tabEl == tab) {
+ for (let callback in tabObj[event])
+ require("./errors").catchAndLog(function() callback())();
+ }
+ }
+ // if being closed, remove the tab object from the cache
+ // of tabs to notify about events.
+ if (event == "onClose")
+ this.tabs.splice(this.tabs.indexOf(tabObj), 1);
+ }, this);
+ }
+ },
+ unload: function() {
+ this.selectedTab = null;
+ this.tabs.splice(0);
+ }
+ };
+ require("./unload").ensure(eventsTabDelegate);
+
+ let eventsTabTracker = new ModuleTabTracker({
+ onTrack: function TETT_onTrack(tab) {
+ eventsTabDelegate.pushTabEvent("onOpen", tab);
+ },
+ onUntrack: function TETT_onUntrack(tab) {
+ eventsTabDelegate.pushTabEvent("onClose", tab);
+ },
+ onSelect: function TETT_onSelect(tab) {
+ if (eventsTabDelegate.selectedTab)
+ eventsTabDelegate.pushTabEvent("onDeactivate", tab);
+
+ eventsTabDelegate.selectedTab = new tabConstructor(tab);
+
+ eventsTabDelegate.pushTabEvent("onActivate", tab);
+ },
+ onReady: function TETT_onReady(tab) {
+ eventsTabDelegate.pushTabEvent("onReady", tab);
+ },
+ onLoad: function TETT_onLoad(tab) {
+ eventsTabDelegate.pushTabEvent("onLoad", tab);
+ },
+ onPaint: function TETT_onPaint(tab) {
+ eventsTabDelegate.pushTabEvent("onPaint", tab);
+ }
+ }, window);
+ require("./unload").ensure(eventsTabTracker);
+
+ // Iterator for all tabs
+ this.__iterator__ = function tabsIterator() {
+ for (let i = 0; i < eventsTabTracker._tabs.length; i++)
+ yield tabConstructor(eventsTabTracker._tabs[i]);
+ }
+
+ this.__defineGetter__("length", function() eventsTabTracker._tabs.length);
+
+ // Cleanup when unloaded
+ this.unload = function TM_unload() {
+ // Unregister tabs event listeners
+ events.forEach(function(e) self[e] = []);
+ }
+ require("./unload").ensure(this);
+
+} // End of TabModule constructor
+
+/**
+ * tabs.open - open a URL in a new tab
+ */
+function open(options, tabConstructor, window) {
+ if (typeof options === "string")
+ options = { url: options };
+
+ options = apiUtils.validateOptions(options, {
+ url: {
+ is: ["string"]
+ },
+ inNewWindow: {
+ is: ["undefined", "boolean"]
+ },
+ inBackground: {
+ is: ["undefined", "boolean"]
+ },
+ isPinned: {
+ is: ["undefined", "boolean"]
+ },
+ onOpen: {
+ is: ["undefined", "function"]
+ }
+ });
+
+ if (window)
+ options.inNewWindow = false;
+
+ let win = window || require("./window-utils").activeBrowserWindow;
+
+ if (!win || options.inNewWindow)
+ openURLInNewWindow(options, tabConstructor);
+ else
+ openURLInNewTab(options, win, tabConstructor);
+}
+
+function openURLInNewWindow(options, tabConstructor) {
+ let addTabOptions = {
+ inNewWindow: true
+ };
+ if (options.onOpen) {
+ addTabOptions.onLoad = function(e) {
+ let win = e.target.defaultView;
+ let tabEl = win.gBrowser.tabContainer.childNodes[0];
+ let tabBrowser = win.gBrowser.getBrowserForTab(tabEl);
+ tabBrowser.addEventListener("load", function onLoad(e) {
+ tabBrowser.removeEventListener("load", onLoad, true);
+ let tab = tabConstructor(tabEl);
+ require("./errors").catchAndLog(function(e) options.onOpen(e))(tab);
+ }, true);
+ };
+ }
+ if (options.isPinned) {
+ addTabOptions.isPinned = true;
+ }
+ exports.addTab(options.url.toString(), addTabOptions);
+}
+
+function openURLInNewTab(options, window, tabConstructor) {
+ window.focus();
+ let tabEl = window.gBrowser.addTab(options.url.toString());
+ if (!options.inBackground)
+ window.gBrowser.selectedTab = tabEl;
+ if (options.isPinned)
+ window.gBrowser.pinTab(tabEl);
+ if (options.onOpen) {
+ let tabBrowser = window.gBrowser.getBrowserForTab(tabEl);
+ tabBrowser.addEventListener("load", function onLoad(e) {
+ // remove event handler from addTab - don't want to be notified
+ // for subsequent loads in same tab.
+ tabBrowser.removeEventListener("load", onLoad, true);
+ let tab = tabConstructor(tabEl);
+ require("./timer").setTimeout(function() {
+ require("./errors").catchAndLog(function(tab) options.onOpen(tab))(tab);
+ }, 10);
+ }, true);
+ }
+}
+
+function getElementAndWindowForTab(tabObj, window) {
+ // iterate over open windows, or use single window if provided
+ let windowIterator = window ? function() { yield window; }
+ : require("./window-utils").windowIterator;
+ for (let win in windowIterator()) {
+ if (win.gBrowser) {
+ // find the tab element at tab.index
+ let index = win.gBrowser.getBrowserIndexForDocument(tabObj.contentDocument);
+ if (index > -1)
+ return [win.gBrowser.tabContainer.getItemAtIndex(index), win];
+ }
+ }
+ return [null, null];
+}
+
+// Tracker for all tabs across all windows
+// This is tab-browser.TabTracker, but with
+// support for additional events added.
+function ModuleTabTracker(delegate, window) {
+ this._delegate = delegate;
+ this._tabs = [];
+ this._tracker = new Tracker(this, window);
+ require("./unload").ensure(this);
+}
+ModuleTabTracker.prototype = {
+ _TAB_EVENTS: ["TabOpen", "TabClose", "TabSelect", "DOMContentLoaded",
+ "load", "MozAfterPaint"],
+ _safeTrackTab: function safeTrackTab(tab) {
+ tab.addEventListener("load", this, false);
+ tab.linkedBrowser.addEventListener("MozAfterPaint", this, false);
+ this._tabs.push(tab);
+ try {
+ this._delegate.onTrack(tab);
+ } catch (e) {
+ console.exception(e);
+ }
+ },
+ _safeUntrackTab: function safeUntrackTab(tab) {
+ tab.removeEventListener("load", this, false);
+ tab.linkedBrowser.removeEventListener("MozAfterPaint", this, false);
+ var index = this._tabs.indexOf(tab);
+ if (index == -1)
+ throw new Error("internal error: tab not found");
+ this._tabs.splice(index, 1);
+ try {
+ this._delegate.onUntrack(tab);
+ } catch (e) {
+ console.exception(e);
+ }
+ },
+ _safeSelectTab: function safeSelectTab(tab) {
+ var index = this._tabs.indexOf(tab);
+ if (index == -1)
+ console.error("internal error: tab not found");
+ try {
+ if (this._delegate.onSelect)
+ this._delegate.onSelect(tab);
+ } catch (e) {
+ console.exception(e);
+ }
+ },
+ _safeDOMContentLoaded: function safeDOMContentLoaded(event) {
+ let tabBrowser = event.currentTarget;
+ let tabBrowserIndex = tabBrowser.getBrowserIndexForDocument(event.target);
+ // TODO: I'm seeing this when loading data url images
+ if (tabBrowserIndex == -1)
+ return;
+ let tab = tabBrowser.tabContainer.getItemAtIndex(tabBrowserIndex);
+ let index = this._tabs.indexOf(tab);
+ if (index == -1)
+ console.error("internal error: tab not found");
+ try {
+ if (this._delegate.onReady)
+ this._delegate.onReady(tab);
+ } catch (e) {
+ console.exception(e);
+ }
+ },
+ _safeLoad: function safeLoad(event) {
+ let tab = event.target;
+ let index = this._tabs.indexOf(tab);
+ if (index == -1)
+ console.error("internal error: tab not found");
+ try {
+ if (this._delegate.onLoad)
+ this._delegate.onLoad(tab);
+ } catch (e) {
+ console.exception(e);
+ }
+ },
+ _safeMozAfterPaint: function safeMozAfterPaint(event) {
+ let win = event.currentTarget.ownerDocument.defaultView;
+ let tabIndex = win.gBrowser.getBrowserIndexForDocument(event.target.document);
+ if (tabIndex == -1)
+ return;
+ let tab = win.gBrowser.tabContainer.getItemAtIndex(tabIndex);
+ let index = this._tabs.indexOf(tab);
+ if (index == -1)
+ console.error("internal error: tab not found");
+ try {
+ if (this._delegate.onPaint)
+ this._delegate.onPaint(tab);
+ } catch (e) {
+ console.exception(e);
+ }
+ },
+ handleEvent: function handleEvent(event) {
+ switch (event.type) {
+ case "TabOpen":
+ this._safeTrackTab(event.target);
+ break;
+ case "TabClose":
+ this._safeUntrackTab(event.target);
+ break;
+ case "TabSelect":
+ this._safeSelectTab(event.target);
+ break;
+ case "DOMContentLoaded":
+ this._safeDOMContentLoaded(event);
+ break;
+ case "load":
+ this._safeLoad(event);
+ break;
+ case "MozAfterPaint":
+ this._safeMozAfterPaint(event);
+ break;
+ default:
+ throw new Error("internal error: unknown event type: " +
+ event.type);
+ }
+ },
+ onTrack: function onTrack(tabbrowser) {
+ for (let tab in tabIterator(tabbrowser))
+ this._safeTrackTab(tab);
+ tabbrowser.tabContainer.addEventListener("TabOpen", this, false);
+ tabbrowser.tabContainer.addEventListener("TabClose", this, false);
+ tabbrowser.tabContainer.addEventListener("TabSelect", this, false);
+ tabbrowser.ownerDocument.defaultView.gBrowser.addEventListener("DOMContentLoaded", this, false);
+ },
+ onUntrack: function onUntrack(tabbrowser) {
+ for (let tab in tabIterator(tabbrowser))
+ this._safeUntrackTab(tab);
+ tabbrowser.tabContainer.removeEventListener("TabOpen", this, false);
+ tabbrowser.tabContainer.removeEventListener("TabClose", this, false);
+ tabbrowser.tabContainer.removeEventListener("TabSelect", this, false);
+ tabbrowser.ownerDocument.defaultView.gBrowser.removeEventListener("DOMContentLoaded", this, false);
+ },
+ unload: function unload() {
+ this._tracker.unload();
+ }
+};
+
+// Utility to get a thumbnail canvas from a tab object
+function getThumbnailCanvasForTab(tabEl, window) {
+ var thumbnail = window.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+ thumbnail.mozOpaque = true;
+ window = tabEl.linkedBrowser.contentWindow;
+ thumbnail.width = Math.ceil(window.screen.availWidth / 5.75);
+ var aspectRatio = 0.5625; // 16:9
+ thumbnail.height = Math.round(thumbnail.width * aspectRatio);
+ var ctx = thumbnail.getContext("2d");
+ var snippetWidth = window.innerWidth * .6;
+ var scale = thumbnail.width / snippetWidth;
+ ctx.scale(scale, scale);
+ ctx.drawWindow(window, window.scrollX, window.scrollY, snippetWidth, snippetWidth * aspectRatio, "rgb(255,255,255)");
+ return thumbnail;
+}
+
+// Utility to return the contents of the target of a chrome URL
+function getChromeURLContents(chromeURL) {
+ let io = Cc["@mozilla.org/network/io-service;1"].
+ getService(Ci.nsIIOService);
+ let channel = io.newChannel(chromeURL, null, null);
+ let input = channel.open();
+ let stream = Cc["@mozilla.org/binaryinputstream;1"].
+ createInstance(Ci.nsIBinaryInputStream);
+ stream.setInputStream(input);
+ let str = stream.readBytes(input.available());
+ stream.close();
+ input.close();
+ return str;
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/tabs/events.js b/tools/addon-sdk-1.7/packages/api-utils/lib/tabs/events.js
new file mode 100644
index 0000000..efb8b45
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/tabs/events.js
@@ -0,0 +1,26 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const ON_PREFIX = "on";
+const TAB_PREFIX = "Tab";
+
+const EVENTS = {
+ ready: "DOMContentLoaded",
+ open: "TabOpen",
+ close: "TabClose",
+ activate: "TabSelect",
+ deactivate: null,
+ pinned: "TabPinned",
+ unpinned: "TabUnpinned"
+}
+exports.EVENTS = EVENTS;
+
+Object.keys(EVENTS).forEach(function(name) {
+ EVENTS[name] = {
+ name: name,
+ listener: ON_PREFIX + name.charAt(0).toUpperCase() + name.substr(1),
+ dom: EVENTS[name]
+ }
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/tabs/observer.js b/tools/addon-sdk-1.7/packages/api-utils/lib/tabs/observer.js
new file mode 100644
index 0000000..e3854cb
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/tabs/observer.js
@@ -0,0 +1,93 @@
+/* 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/. */
+
+"use strict";
+
+const { EventEmitterTrait: EventEmitter } = require("../events");
+const { DOMEventAssembler } = require("../events/assembler");
+const { Trait } = require("../light-traits");
+const { getActiveTab, getTabs, getTabContainers } = require("./utils");
+const { browserWindowIterator, isBrowser } = require("../window-utils");
+const { observer: windowObserver } = require("../windows/observer");
+
+const EVENTS = {
+ "TabOpen": "open",
+ "TabClose": "close",
+ "TabSelect": "select",
+ "TabMove": "move",
+ "TabPinned": "pinned",
+ "TabUnpinned": "unpinned"
+};
+
+
+// Event emitter objects used to register listeners and emit events on them
+// when they occur.
+const observer = Trait.compose(DOMEventAssembler, EventEmitter).create({
+ /**
+ * Method is implemented by `EventEmitter` and is used just for emitting
+ * events on registered listeners.
+ */
+ _emit: Trait.required,
+ /**
+ * Events that are supported and emitted by the module.
+ */
+ supportedEventsTypes: Object.keys(EVENTS),
+ /**
+ * Function handles all the supported events on all the windows that are
+ * observed. Method is used to proxy events to the listeners registered on
+ * this event emitter.
+ * @param {Event} event
+ * Keyboard event being emitted.
+ */
+ handleEvent: function handleEvent(event) {
+ this._emit(EVENTS[event.type], event.target, event);
+ }
+});
+
+// Currently gecko does not dispatches any event on the previously selected
+// tab before / after "TabSelect" is dispatched. In order to work around this
+// limitation we keep track of selected tab and emit "deactivate" event with
+// that before emitting "activate" on selected tab.
+var selectedTab = null;
+function onTabSelect(tab) {
+ if (selectedTab !== tab) {
+ if (selectedTab) observer._emit("deactivate", selectedTab);
+ if (tab) observer._emit("activate", selectedTab = tab);
+ }
+};
+observer.on("select", onTabSelect);
+
+// We also observe opening / closing windows in order to add / remove it's
+// containers to the observed list.
+function onWindowOpen(chromeWindow) {
+ if (!isBrowser(chromeWindow)) return; // Ignore if it's not a browser window.
+ getTabContainers(chromeWindow).forEach(function (container) {
+ observer.observe(container);
+ });
+}
+windowObserver.on("open", onWindowOpen);
+
+function onWindowClose(chromeWindow) {
+ if (!isBrowser(chromeWindow)) return; // Ignore if it's not a browser window.
+ getTabContainers(chromeWindow).forEach(function (container) {
+ observer.ignore(container);
+ });
+}
+windowObserver.on("close", onWindowClose);
+
+
+// Currently gecko does not dispatches "TabSelect" events when different
+// window gets activated. To work around this limitation we emulate "select"
+// event for this case.
+windowObserver.on("activate", function onWindowActivate(chromeWindow) {
+ if (!isBrowser(chromeWindow)) return; // Ignore if it's not a browser window.
+ observer._emit("select", getActiveTab(chromeWindow));
+});
+
+// We should synchronize state, since probably we already have at least one
+// window open.
+for each (let window in browserWindowIterator()) onWindowOpen(window);
+
+exports.observer = observer;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/tabs/tab.js b/tools/addon-sdk-1.7/packages/api-utils/lib/tabs/tab.js
new file mode 100644
index 0000000..246834f
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/tabs/tab.js
@@ -0,0 +1,264 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const { Ci } = require('chrome');
+const { Trait } = require("../traits");
+const { EventEmitter } = require("../events");
+const { validateOptions } = require("../api-utils");
+const { defer } = require("../functional");
+const { EVENTS } = require("./events");
+const { getThumbnailURIForWindow } = require("../utils/thumbnail");
+const { getFaviconURIForLocation } = require("../utils/data");
+
+
+
+// Array of the inner instances of all the wrapped tabs.
+const TABS = [];
+
+/**
+ * Trait used to create tab wrappers.
+ */
+const TabTrait = Trait.compose(EventEmitter, {
+ on: Trait.required,
+ _emit: Trait.required,
+ /**
+ * Tab DOM element that is being wrapped.
+ */
+ _tab: null,
+ /**
+ * Window wrapper whose tab this object represents.
+ */
+ window: null,
+ constructor: function Tab(options) {
+ this._onReady = this._onReady.bind(this);
+ this._tab = options.tab;
+ let window = this.window = options.window;
+ // Setting event listener if was passed.
+ for each (let type in EVENTS) {
+ let listener = options[type.listener];
+ if (listener)
+ this.on(type.name, options[type.listener]);
+ if ('ready' != type.name) // window spreads this event.
+ window.tabs.on(type.name, this._onEvent.bind(this, type.name));
+ }
+
+ this.on(EVENTS.close.name, this.destroy.bind(this));
+ this._browser.addEventListener(EVENTS.ready.dom, this._onReady, true);
+
+ if (options.isPinned)
+ this.pin();
+
+ // Since we will have to identify tabs by a DOM elements facade function
+ // is used as constructor that collects all the instances and makes sure
+ // that they more then one wrapper is not created per tab.
+ return this;
+ },
+ destroy: function destroy() {
+ this._removeAllListeners();
+ this._browser.removeEventListener(EVENTS.ready.dom, this._onReady,
+ true);
+ },
+
+ /**
+ * Internal listener that emits public event 'ready' when the page of this
+ * tab is loaded.
+ */
+ _onReady: function _onReady(event) {
+ // IFrames events will bubble so we need to ignore those.
+ if (event.target == this._contentDocument)
+ this._emit(EVENTS.ready.name, this._public);
+ },
+ /**
+ * Internal tab event router. Window will emit tab related events for all it's
+ * tabs, this listener will propagate all the events for this tab to it's
+ * listeners.
+ */
+ _onEvent: function _onEvent(type, tab) {
+ if (tab == this._public)
+ this._emit(type, tab);
+ },
+ /**
+ * Browser DOM element where page of this tab is currently loaded.
+ */
+ get _browser() this._window.gBrowser.getBrowserForTab(this._tab),
+ /**
+ * Window DOM element containing this tab.
+ */
+ get _window() this._tab.ownerDocument.defaultView,
+ /**
+ * Document object of the page that is currently loaded in this tab.
+ */
+ get _contentDocument() this._browser.contentDocument,
+ /**
+ * Window object of the page that is currently loaded in this tab.
+ */
+ get _contentWindow() this._browser.contentWindow,
+
+ /**
+ * The title of the page currently loaded in the tab.
+ * Changing this property changes an actual title.
+ * @type {String}
+ */
+ get title() this._contentDocument.title,
+ set title(value) this._contentDocument.title = String(value),
+ /**
+ * Location of the page currently loaded in this tab.
+ * Changing this property will loads page under under the specified location.
+ * @type {String}
+ */
+ get url() String(this._browser.currentURI.spec),
+ set url(value) this._changeLocation(String(value)),
+ // "TabOpen" event is fired when it's still "about:blank" is loaded in the
+ // changing `location` property of the `contentDocument` has no effect since
+ // seems to be either ignored or overridden by internal listener, there for
+ // location change is enqueued for the next turn of event loop.
+ _changeLocation: defer(function(url) this._browser.loadURI(url)),
+ /**
+ * URI of the favicon for the page currently loaded in this tab.
+ * @type {String}
+ */
+ get favicon() getFaviconURIForLocation(this.url),
+ /**
+ * The CSS style for the tab
+ */
+ get style() null, // TODO
+ /**
+ * The index of the tab relative to other tabs in the application window.
+ * Changing this property will change order of the actual position of the tab.
+ * @type {Number}
+ */
+ get index()
+ this._window.gBrowser.getBrowserIndexForDocument(this._contentDocument),
+ set index(value) this._window.gBrowser.moveTabTo(this._tab, value),
+ /**
+ * Thumbnail data URI of the page currently loaded in this tab.
+ * @type {String}
+ */
+ getThumbnail: function getThumbnail()
+ getThumbnailURIForWindow(this._contentWindow),
+ /**
+ * Whether or not tab is pinned (Is an app-tab).
+ * @type {Boolean}
+ */
+ get isPinned() this._tab.pinned,
+ pin: function pin() {
+ this._window.gBrowser.pinTab(this._tab);
+ },
+ unpin: function unpin() {
+ this._window.gBrowser.unpinTab(this._tab);
+ },
+
+ /**
+ * Create a worker for this tab, first argument is options given to Worker.
+ * @type {Worker}
+ */
+ attach: function attach(options) {
+ let { Worker } = require("../content/worker");
+ options.window = this._contentWindow;
+ let worker = Worker(options);
+ worker.once("detach", function detach() {
+ worker.destroy();
+ });
+ return worker;
+ },
+
+ /**
+ * Make this tab active.
+ * Please note: That this function is called synchronous since in E10S that
+ * will be the case. Besides this function is called from a constructor where
+ * we would like to return instance before firing a 'TabActivated' event.
+ */
+ activate: defer(function activate() {
+ if (this._window) // Ignore if window is closed by the time this is invoked.
+ this._window.gBrowser.selectedTab = this._tab;
+ }),
+ /**
+ * Close the tab
+ */
+ close: function close(callback) {
+ if (callback)
+ this.once(EVENTS.close.name, callback);
+ this._window.gBrowser.removeTab(this._tab);
+ },
+ /**
+ * Reload the tab
+ */
+ reload: function reload() {
+ this._window.gBrowser.reloadTab(this._tab);
+ }
+});
+
+function Tab(options) {
+ let chromeTab = options.tab;
+ for each (let tab in TABS) {
+ if (chromeTab == tab._tab)
+ return tab._public;
+ }
+ let tab = TabTrait(options);
+ TABS.push(tab);
+ return tab._public;
+}
+Tab.prototype = TabTrait.prototype;
+exports.Tab = Tab;
+
+function Options(options) {
+ if ("string" === typeof options)
+ options = { url: options };
+
+ return validateOptions(options, {
+ url: { is: ["string"] },
+ inBackground: { is: ["undefined", "boolean"] },
+ isPinned: { is: ["undefined", "boolean"] },
+ onOpen: { is: ["undefined", "function"] },
+ onClose: { is: ["undefined", "function"] },
+ onReady: { is: ["undefined", "function"] },
+ onActivate: { is: ["undefined", "function"] },
+ onDeactivate: { is: ["undefined", "function"] }
+ });
+}
+exports.Options = Options;
+
+
+exports.getTabForWindow = function (win) {
+ // Get browser window
+ let topWindow = win.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem)
+ .rootTreeItem
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow);
+ if (!topWindow.gBrowser) return null;
+
+ // Get top window object, in case we are in a content iframe
+ let topContentWindow;
+ try {
+ topContentWindow = win.top;
+ } catch(e) {
+ // It may throw if win is not a valid content window
+ return null;
+ }
+
+ function getWindowID(obj) {
+ return obj.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils)
+ .currentInnerWindowID;
+ }
+
+ // Search for related Tab
+ let topWindowId = getWindowID(topContentWindow);
+ for (let i = 0; i < topWindow.gBrowser.browsers.length; i++) {
+ let w = topWindow.gBrowser.browsers[i].contentWindow;
+ if (getWindowID(w) == topWindowId) {
+ return Tab({
+ // TODO: api-utils should not depend on addon-kit!
+ window: require("addon-kit/windows").BrowserWindow({ window: topWindow }),
+ tab: topWindow.gBrowser.tabs[i]
+ });
+ }
+ }
+
+ // We were unable to find the related tab!
+ return null;
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/tabs/utils.js b/tools/addon-sdk-1.7/packages/api-utils/lib/tabs/utils.js
new file mode 100644
index 0000000..1c411dc
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/tabs/utils.js
@@ -0,0 +1,59 @@
+/* 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/. */
+
+"use strict";
+
+function getTabContainer(tabBrowser) {
+ return tabBrowser.tabContainer;
+}
+exports.getTabContainer = getTabContainer;
+
+function getTabBrowsers(window) {
+ return Array.slice(window.document.getElementsByTagName("tabbrowser"));
+}
+exports.getTabBrowsers = getTabBrowsers;
+
+function getTabContainers(window) {
+ return getTabBrowsers(window).map(getTabContainer);
+}
+exports.getTabContainers = getTabContainers;
+
+function getTabs(window) {
+ return getTabContainers(window).reduce(function (tabs, container) {
+ tabs.push.apply(tabs, container.children);
+ return tabs;
+ }, []);
+}
+exports.getTabs = getTabs;
+
+function getActiveTab(window) {
+ return window.gBrowser.selectedTab;
+}
+exports.getActiveTab = getActiveTab;
+
+function getOwnerWindow(tab) {
+ return tab.ownerDocument.defaultView;
+}
+exports.getOwnerWindow = getOwnerWindow;
+
+function openTab(window, url) {
+ return window.gBrowser.addTab(url);
+}
+exports.openTab = openTab;
+
+function isTabOpen(tab) {
+ return !!tab.linkedBrowser;
+}
+exports.isTabOpen = isTabOpen;
+
+function closeTab(tab) {
+ return getOwnerWindow(tab).gBrowser.removeTab(tab);
+}
+exports.closeTab = closeTab;
+
+function activateTab(tab) {
+ getOwnerWindow(tab).gBrowser.selectedTab = tab;
+}
+exports.activateTab = activateTab;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/test.js b/tools/addon-sdk-1.7/packages/api-utils/lib/test.js
new file mode 100644
index 0000000..e69a2f4
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/test.js
@@ -0,0 +1,108 @@
+/* vim:ts=2:sts=2:sw=2:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 BaseAssert = require("./test/assert").Assert;
+const { isFunction, isObject } = require("./type");
+
+function extend(target) {
+ let descriptor = {}
+ Array.slice(arguments, 1).forEach(function(source) {
+ Object.getOwnPropertyNames(source).forEach(function onEach(name) {
+ descriptor[name] = Object.getOwnPropertyDescriptor(source, name);
+ });
+ });
+ return Object.create(target, descriptor);
+}
+
+/**
+ * Function takes test `suite` object in CommonJS format and defines all of the
+ * tests from that suite and nested suites in a jetpack format on a given
+ * `target` object. Optionally third argument `prefix` can be passed to prefix
+ * all the test names.
+ */
+function defineTestSuite(target, suite, prefix) {
+ prefix = prefix || "";
+ // If suite defines `Assert` that's what `assert` object have to be created
+ // from and passed to a test function (This allows custom assertion functions)
+ // See for details: http://wiki.commonjs.org/wiki/Unit_Testing/1.1
+ let Assert = suite.Assert || BaseAssert;
+ // Going through each item in the test suite and wrapping it into a
+ // Jetpack test format.
+ Object.keys(suite).forEach(function(key) {
+ // If name starts with test then it's a test function or suite.
+ if (key.indexOf("test") === 0) {
+ let test = suite[key];
+
+ // For each test function so we create a wrapper test function in a
+ // jetpack format and copy that to a `target` exports.
+ if (isFunction(test)) {
+
+ // Since names of the test may match across suites we use full object
+ // path as a name to avoid overriding same function.
+ target[prefix + key] = function(options) {
+
+ // Creating `assert` functions for this test.
+ let assert = Assert(options);
+
+ // If CommonJS test function expects more than one argument
+ // it means that test is async and second argument is a callback
+ // to notify that test is finished.
+ if (1 < test.length) {
+
+ // Letting test runner know that test is executed async and
+ // creating a callback function that CommonJS tests will call
+ // once it's done.
+ options.waitUntilDone();
+ test(assert, function() {
+ options.done();
+ });
+ }
+
+ // Otherwise CommonJS test is synchronous so we call it only with
+ // one argument.
+ else {
+ test(assert);
+ }
+ }
+ }
+
+ // If it's an object then it's a test suite containing test function
+ // and / or nested test suites. In that case we just extend prefix used
+ // and call this function to copy and wrap tests from nested suite.
+ else if (isObject(test)) {
+ // We need to clone `tests` instead of modifying it, since it's very
+ // likely that it is frozen (usually test suites imported modules).
+ test = extend(Object.prototype, test, {
+ Assert: test.Assert || Assert
+ });
+ defineTestSuite(target, test, prefix + key + ".");
+ }
+ }
+ });
+}
+
+/**
+ * This function is a CommonJS test runner function, but since Jetpack test
+ * runner and test format is different from CommonJS this function shims given
+ * `exports` with all its tests into a Jetpack test format so that the built-in
+ * test runner will be able to run CommonJS test without manual changes.
+ */
+exports.run = function run(exports) {
+
+ // We can't leave old properties on exports since those are test in a CommonJS
+ // format that why we move everything to a new `suite` object.
+ let suite = {};
+ Object.keys(exports).forEach(function(key) {
+ suite[key] = exports[key];
+ delete exports[key];
+ });
+
+ // Now we wrap all the CommonJS tests to a Jetpack format and define
+ // those to a given `exports` object since that where jetpack test runner
+ // will look for them.
+ defineTestSuite(exports, suite);
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/test/assert.js b/tools/addon-sdk-1.7/packages/api-utils/lib/test/assert.js
new file mode 100644
index 0000000..fa40807
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/test/assert.js
@@ -0,0 +1,331 @@
+/* vim:ts=2:sts=2:sw=2:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 { isFunction, isNull, isObject, isString, isRegExp, isArray, isDate,
+ isPrimitive, isUndefined, instanceOf, source } = require("../type");
+
+/**
+ * The `AssertionError` is defined in assert.
+ * @extends Error
+ * @example
+ * new assert.AssertionError({
+ * message: message,
+ * actual: actual,
+ * expected: expected
+ * })
+ */
+function AssertionError(options) {
+ let assertionError = Object.create(AssertionError.prototype);
+
+ if (isString(options))
+ options = { message: options };
+ if ("actual" in options)
+ assertionError.actual = options.actual;
+ if ("expected" in options)
+ assertionError.expected = options.expected;
+ if ("operator" in options)
+ assertionError.operator = options.operator;
+
+ assertionError.message = options.message;
+ assertionError.stack = new Error().stack;
+ return assertionError;
+}
+AssertionError.prototype = Object.create(Error.prototype, {
+ constructor: { value: AssertionError },
+ name: { value: "AssertionError", enumerable: true },
+ toString: { value: function toString() {
+ let value;
+ if (this.message) {
+ value = this.name + " : " + this.message;
+ }
+ else {
+ value = [
+ this.name + " : ",
+ source(this.expected),
+ this.operator,
+ source(this.actual)
+ ].join(" ");
+ }
+ return value;
+ }}
+});
+exports.AssertionError = AssertionError;
+
+function Assert(logger) {
+ return Object.create(Assert.prototype, { _log: { value: logger }});
+}
+Assert.prototype = {
+ fail: function fail(e) {
+ this._log.fail(e.message);
+ },
+ pass: function pass(message) {
+ this._log.pass(message);
+ },
+ error: function error(e) {
+ this._log.exception(e);
+ },
+ ok: function ok(value, message) {
+ if (!!!value) {
+ this.fail({
+ actual: value,
+ expected: true,
+ message: message,
+ operator: "=="
+ });
+ }
+ else {
+ this.pass(message);
+ }
+ },
+
+ /**
+ * The equality assertion tests shallow, coercive equality with `==`.
+ * @example
+ * assert.equal(1, 1, "one is one");
+ */
+ equal: function equal(actual, expected, message) {
+ if (actual == expected) {
+ this.pass(message);
+ }
+ else {
+ this.fail({
+ actual: actual,
+ expected: expected,
+ message: message,
+ operator: "=="
+ });
+ }
+ },
+
+ /**
+ * The non-equality assertion tests for whether two objects are not equal
+ * with `!=`.
+ * @example
+ * assert.notEqual(1, 2, "one is not two");
+ */
+ notEqual: function notEqual(actual, expected, message) {
+ if (actual != expected) {
+ this.pass(message);
+ }
+ else {
+ this.fail({
+ actual: actual,
+ expected: expected,
+ message: message,
+ operator: "!=",
+ });
+ }
+ },
+
+ /**
+ * The equivalence assertion tests a deep (with `===`) equality relation.
+ * @example
+ * assert.deepEqual({ a: "foo" }, { a: "foo" }, "equivalent objects")
+ */
+ deepEqual: function deepEqual(actual, expected, message) {
+ if (isDeepEqual(actual, expected)) {
+ this.pass(message);
+ }
+ else {
+ this.fail({
+ actual: actual,
+ expected: expected,
+ message: message,
+ operator: "deepEqual"
+ });
+ }
+ },
+
+ /**
+ * The non-equivalence assertion tests for any deep (with `===`) inequality.
+ * @example
+ * assert.notDeepEqual({ a: "foo" }, Object.create({ a: "foo" }),
+ * "object's inherit from different prototypes");
+ */
+ notDeepEqual: function notDeepEqual(actual, expected, message) {
+ if (!isDeepEqual(actual, expected)) {
+ this.pass(message);
+ }
+ else {
+ this.fail({
+ actual: actual,
+ expected: expected,
+ message: message,
+ operator: "notDeepEqual"
+ });
+ }
+ },
+
+ /**
+ * The strict equality assertion tests strict equality, as determined by
+ * `===`.
+ * @example
+ * assert.strictEqual(null, null, "`null` is `null`")
+ */
+ strictEqual: function strictEqual(actual, expected, message) {
+ if (actual === expected) {
+ this.pass(message);
+ }
+ else {
+ this.fail({
+ actual: actual,
+ expected: expected,
+ message: message,
+ operator: "==="
+ });
+ }
+ },
+
+ /**
+ * The strict non-equality assertion tests for strict inequality, as
+ * determined by `!==`.
+ * @example
+ * assert.notStrictEqual(null, undefined, "`null` is not `undefined`");
+ */
+ notStrictEqual: function notStrictEqual(actual, expected, message) {
+ if (actual !== expected) {
+ this.pass(message);
+ }
+ else {
+ this.fail({
+ actual: actual,
+ expected: expected,
+ message: message,
+ operator: "!=="
+ })
+ }
+ },
+
+ /**
+ * The assertion whether or not given `block` throws an exception. If optional
+ * `Error` argument is provided and it's type of function thrown error is
+ * asserted to be an instance of it, if type of `Error` is string then message
+ * of throw exception is asserted to contain it.
+ * @param {Function} block
+ * Function that is expected to throw.
+ * @param {Error|RegExp} [Error]
+ * Error constructor that is expected to be thrown or a string that
+ * must be contained by a message of the thrown exception, or a RegExp
+ * matching a message of the thrown exception.
+ * @param {String} message
+ * Description message
+ *
+ * @examples
+ *
+ * assert.throws(function block() {
+ * doSomething(4)
+ * }, "Object is expected", "Incorrect argument is passed");
+ *
+ * assert.throws(function block() {
+ * Object.create(5)
+ * }, TypeError, "TypeError is thrown");
+ */
+ throws: function throws(block, Error, message) {
+ let threw = false;
+ let exception = null;
+
+ // If third argument is not provided and second argument is a string it
+ // means that optional `Error` argument was not passed, so we shift
+ // arguments.
+ if (isString(Error) && isUndefined(message)) {
+ message = Error;
+ Error = undefined;
+ }
+
+ // Executing given `block`.
+ try {
+ block();
+ }
+ catch (e) {
+ threw = true;
+ exception = e;
+ }
+
+ // If exception was thrown and `Error` argument was not passed assert is
+ // passed.
+ if (threw && (isUndefined(Error) ||
+ // If passed `Error` is RegExp using it's test method to
+ // assert thrown exception message.
+ (isRegExp(Error) && Error.test(exception.message)) ||
+ // If passed `Error` is a constructor function testing if
+ // thrown exception is an instance of it.
+ (isFunction(Error) && instanceOf(exception, Error))))
+ {
+ this.pass(message);
+ }
+
+ // Otherwise we report assertion failure.
+ else {
+ let failure = {
+ message: message,
+ operator: "throws"
+ };
+
+ if (exception)
+ failure.actual = exception;
+
+ if (Error)
+ failure.expected = Error;
+
+ this.fail(failure);
+ }
+ }
+};
+exports.Assert = Assert;
+
+function isDeepEqual(actual, expected) {
+
+ // 7.1. All identical values are equivalent, as determined by ===.
+ if (actual === expected) {
+ return true;
+ }
+
+ // 7.2. If the expected value is a Date object, the actual value is
+ // equivalent if it is also a Date object that refers to the same time.
+ else if (isDate(actual) && isDate(expected)) {
+ return actual.getTime() === expected.getTime();
+ }
+
+ // XXX specification bug: this should be specified
+ else if (isPrimitive(actual) || isPrimitive(expected)) {
+ return expected === actual;
+ }
+
+ // 7.3. Other pairs that do not both pass typeof value == "object",
+ // equivalence is determined by ==.
+ else if (!isObject(actual) && !isObject(expected)) {
+ return actual == expected;
+ }
+
+ // 7.4. For all other Object pairs, including Array objects, equivalence is
+ // determined by having the same number of owned properties (as verified
+ // with Object.prototype.hasOwnProperty.call), the same set of keys
+ // (although not necessarily the same order), equivalent values for every
+ // corresponding key, and an identical "prototype" property. Note: this
+ // accounts for both named and indexed properties on Arrays.
+ else {
+ return actual.prototype === expected.prototype &&
+ isEquivalent(actual, expected);
+ }
+}
+
+function isEquivalent(a, b, stack) {
+ let aKeys = Object.keys(a);
+ let bKeys = Object.keys(b);
+
+ return aKeys.length === bKeys.length &&
+ isArrayEquivalent(aKeys.sort(), bKeys.sort()) &&
+ aKeys.every(function(key) {
+ return isDeepEqual(a[key], b[key], stack)
+ });
+}
+
+function isArrayEquivalent(a, b, stack) {
+ return isArray(a) && isArray(b) &&
+ a.every(function(value, index) {
+ return isDeepEqual(value, b[index]);
+ });
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/text-streams.js b/tools/addon-sdk-1.7/packages/api-utils/lib/text-streams.js
new file mode 100644
index 0000000..96217eb
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/text-streams.js
@@ -0,0 +1,240 @@
+/* -*- 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/. */
+
+"use strict";
+
+const {Cc,Ci,Cu,components} = require("chrome");
+var NetUtil = {};
+Cu.import("resource://gre/modules/NetUtil.jsm", NetUtil);
+NetUtil = NetUtil.NetUtil;
+
+// NetUtil.asyncCopy() uses this buffer length, and since we call it, for best
+// performance we use it, too.
+const BUFFER_BYTE_LEN = 0x8000;
+const PR_UINT32_MAX = 0xffffffff;
+const DEFAULT_CHARSET = "UTF-8";
+
+exports.TextReader = TextReader;
+exports.TextWriter = TextWriter;
+
+/**
+ * An input stream that reads text from a backing stream using a given text
+ * encoding.
+ *
+ * @param inputStream
+ * The stream is backed by this nsIInputStream. It must already be
+ * opened.
+ * @param charset
+ * Text in inputStream is expected to be in this character encoding. If
+ * not given, "UTF-8" is assumed. See nsICharsetConverterManager.idl for
+ * documentation on how to determine other valid values for this.
+ */
+function TextReader(inputStream, charset) {
+ const self = this;
+ charset = checkCharset(charset);
+
+ let stream = Cc["@mozilla.org/intl/converter-input-stream;1"].
+ createInstance(Ci.nsIConverterInputStream);
+ stream.init(inputStream, charset, BUFFER_BYTE_LEN,
+ Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
+
+ let manager = new StreamManager(this, stream);
+
+ /**
+ * Reads a string from the stream. If the stream is closed, an exception is
+ * thrown.
+ *
+ * @param numChars
+ * The number of characters to read. If not given, the remainder of
+ * the stream is read.
+ * @return The string read. If the stream is already at EOS, returns the
+ * empty string.
+ */
+ this.read = function TextReader_read(numChars) {
+ manager.ensureOpened();
+
+ let readAll = false;
+ if (typeof(numChars) === "number")
+ numChars = Math.max(numChars, 0);
+ else
+ readAll = true;
+
+ let str = "";
+ let totalRead = 0;
+ let chunkRead = 1;
+
+ // Read in numChars or until EOS, whichever comes first. Note that the
+ // units here are characters, not bytes.
+ while (true) {
+ let chunk = {};
+ let toRead = readAll ?
+ PR_UINT32_MAX :
+ Math.min(numChars - totalRead, PR_UINT32_MAX);
+ if (toRead <= 0 || chunkRead <= 0)
+ break;
+
+ // The converter stream reads in at most BUFFER_BYTE_LEN bytes in a call
+ // to readString, enough to fill its byte buffer. chunkRead will be the
+ // number of characters encoded by the bytes in that buffer.
+ chunkRead = stream.readString(toRead, chunk);
+ str += chunk.value;
+ totalRead += chunkRead;
+ }
+
+ return str;
+ };
+}
+
+/**
+ * A buffered output stream that writes text to a backing stream using a given
+ * text encoding.
+ *
+ * @param outputStream
+ * The stream is backed by this nsIOutputStream. It must already be
+ * opened.
+ * @param charset
+ * Text will be written to outputStream using this character encoding.
+ * If not given, "UTF-8" is assumed. See nsICharsetConverterManager.idl
+ * for documentation on how to determine other valid values for this.
+ */
+function TextWriter(outputStream, charset) {
+ const self = this;
+ charset = checkCharset(charset);
+
+ let stream = outputStream;
+
+ // Buffer outputStream if it's not already.
+ let ioUtils = Cc["@mozilla.org/io-util;1"].getService(Ci.nsIIOUtil);
+ if (!ioUtils.outputStreamIsBuffered(outputStream)) {
+ stream = Cc["@mozilla.org/network/buffered-output-stream;1"].
+ createInstance(Ci.nsIBufferedOutputStream);
+ stream.init(outputStream, BUFFER_BYTE_LEN);
+ }
+
+ // I'd like to use nsIConverterOutputStream. But NetUtil.asyncCopy(), which
+ // we use below in writeAsync(), naturally expects its sink to be an instance
+ // of nsIOutputStream, which nsIConverterOutputStream's only implementation is
+ // not. So we use uconv and manually convert all strings before writing to
+ // outputStream.
+ let uconv = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
+ createInstance(Ci.nsIScriptableUnicodeConverter);
+ uconv.charset = charset;
+
+ let manager = new StreamManager(this, stream);
+
+ /**
+ * Flushes the backing stream's buffer.
+ */
+ this.flush = function TextWriter_flush() {
+ manager.ensureOpened();
+ stream.flush();
+ };
+
+ /**
+ * Writes a string to the stream. If the stream is closed, an exception is
+ * thrown.
+ *
+ * @param str
+ * The string to write.
+ */
+ this.write = function TextWriter_write(str) {
+ manager.ensureOpened();
+ let istream = uconv.convertToInputStream(str);
+ let len = istream.available();
+ while (len > 0) {
+ stream.writeFrom(istream, len);
+ len = istream.available();
+ }
+ istream.close();
+ };
+
+ /**
+ * Writes a string on a background thread. After the write completes, the
+ * backing stream's buffer is flushed, and both the stream and the backing
+ * stream are closed, also on the background thread. If the stream is already
+ * closed, an exception is thrown immediately.
+ *
+ * @param str
+ * The string to write.
+ * @param callback
+ * An optional function. If given, it's called as callback(error) when
+ * the write completes. error is an Error object or undefined if there
+ * was no error. Inside callback, |this| is the stream object.
+ */
+ this.writeAsync = function TextWriter_writeAsync(str, callback) {
+ manager.ensureOpened();
+ let istream = uconv.convertToInputStream(str);
+ NetUtil.asyncCopy(istream, stream, function (result) {
+ let err = components.isSuccessCode(result) ? undefined :
+ new Error("An error occured while writing to the stream: " + result);
+ if (err)
+ console.error(err);
+
+ // asyncCopy() closes its output (and input) stream.
+ manager.opened = false;
+
+ if (typeof(callback) === "function") {
+ try {
+ callback.call(self, err);
+ }
+ catch (exc) {
+ console.exception(exc);
+ }
+ }
+ });
+ };
+}
+
+// This manages the lifetime of stream, a TextReader or TextWriter. It defines
+// closed and close() on stream and registers an unload listener that closes
+// rawStream if it's still opened. It also provides ensureOpened(), which
+// throws an exception if the stream is closed.
+function StreamManager(stream, rawStream) {
+ const self = this;
+ this.rawStream = rawStream;
+ this.opened = true;
+
+ /**
+ * True iff the stream is closed.
+ */
+ stream.__defineGetter__("closed", function stream_closed() {
+ return !self.opened;
+ });
+
+ /**
+ * Closes both the stream and its backing stream. If the stream is already
+ * closed, an exception is thrown. For TextWriters, this first flushes the
+ * backing stream's buffer.
+ */
+ stream.close = function stream_close() {
+ self.ensureOpened();
+ self.unload();
+ };
+
+ require("./unload").ensure(this);
+}
+
+StreamManager.prototype = {
+ ensureOpened: function StreamManager_ensureOpened() {
+ if (!this.opened)
+ throw new Error("The stream is closed and cannot be used.");
+ },
+ unload: function StreamManager_unload() {
+ // TextWriter.writeAsync() causes rawStream to close and therefore sets
+ // opened to false, so check that we're still opened.
+ if (this.opened) {
+ // Calling close() on both an nsIUnicharInputStream and
+ // nsIBufferedOutputStream closes their backing streams. It also forces
+ // nsIOutputStreams to flush first.
+ this.rawStream.close();
+ this.opened = false;
+ }
+ }
+};
+
+function checkCharset(charset) {
+ return typeof(charset) === "string" ? charset : DEFAULT_CHARSET;
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/timer.js b/tools/addon-sdk-1.7/packages/api-utils/lib/timer.js
new file mode 100644
index 0000000..55654a8
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/timer.js
@@ -0,0 +1,78 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { CC } = require("chrome");
+const { Unknown } = require("./xpcom");
+const { when: unload } = require("./unload");
+
+const Timer = CC('@mozilla.org/timer;1', 'nsITimer');
+
+// Registry for all timers.
+const timers = (function(map, id) {
+ return Object.defineProperties(function registry(key, fallback) {
+ return key in map ? map[key] : fallback;
+ }, {
+ register: { value: function(value) (map[++id] = value, id) },
+ unregister: { value: function(key) delete map[key] },
+ forEach: { value: function(callback) Object.keys(map).forEach(callback) }
+ });
+})(Object.create(null), 0);
+
+const TimerCallback = Unknown.extend({
+ interfaces: [ 'nsITimerCallback' ],
+ initialize: function initialize(id, callback, rest) {
+ this.id = id;
+ this.callback = callback;
+ this.arguments = rest;
+ }
+});
+
+const TimeoutCallback = TimerCallback.extend({
+ type: 0, // nsITimer.TYPE_ONE_SHOT
+ notify: function notify() {
+ try {
+ timers.unregister(this.id);
+ this.callback.apply(null, this.arguments);
+ }
+ catch (error) {
+ console.exception(error);
+ }
+ }
+});
+
+const IntervalCallback = TimerCallback.extend({
+ type: 1, // nsITimer.TYPE_REPEATING_SLACK
+ notify: function notify() {
+ try {
+ this.callback.apply(null, this.arguments);
+ }
+ catch (error) {
+ console.exception(error);
+ }
+ }
+});
+
+function setTimer(TimerCallback, listener, delay) {
+ let timer = Timer();
+ let id = timers.register(timer);
+ let callback = TimerCallback.new(id, listener, Array.slice(arguments, 3));
+ timer.initWithCallback(callback, delay || 0, TimerCallback.type);
+ return id;
+}
+
+function unsetTimer(id) {
+ let timer = timers(id);
+ timers.unregister(id);
+ if (timer)
+ timer.cancel();
+}
+
+exports.setTimeout = setTimer.bind(null, TimeoutCallback);
+exports.setInterval = setTimer.bind(null, IntervalCallback);
+exports.clearTimeout = unsetTimer;
+exports.clearInterval = unsetTimer;
+
+unload(function() { timers.forEach(unsetTimer); });
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/traceback.js b/tools/addon-sdk-1.7/packages/api-utils/lib/traceback.js
new file mode 100644
index 0000000..c55fd9d
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/traceback.js
@@ -0,0 +1,123 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {Cc,Ci,components} = require("chrome");
+
+// Undo the auto-parentification of URLs done in bug 418356.
+function deParentifyURL(url) {
+ return url ? url.split(" -> ").slice(-1)[0] : url;
+}
+
+// TODO: We might want to move this function to url or some similar
+// module.
+function getLocalFile(path) {
+ var ios = Cc['@mozilla.org/network/io-service;1']
+ .getService(Ci.nsIIOService);
+ var channel = ios.newChannel(path, null, null);
+ var iStream = channel.open();
+ var siStream = Cc['@mozilla.org/scriptableinputstream;1']
+ .createInstance(Ci.nsIScriptableInputStream);
+ siStream.init(iStream);
+ var data = new String();
+ data += siStream.read(-1);
+ siStream.close();
+ iStream.close();
+ return data;
+}
+
+function safeGetFileLine(path, line) {
+ try {
+ var scheme = require("./url").URL(path).scheme;
+ // TODO: There should be an easier, more accurate way to figure out
+ // what's the case here.
+ if (!(scheme == "http" || scheme == "https"))
+ return getLocalFile(path).split("\n")[line - 1];
+ } catch (e) {}
+ return null;
+}
+
+function errorStackToJSON(stack) {
+ var lines = stack.split("\n");
+
+ var frames = [];
+ lines.forEach(
+ function(line) {
+ if (!line)
+ return;
+ var atIndex = line.indexOf("@");
+ var colonIndex = line.lastIndexOf(":");
+ var filename = deParentifyURL(line.slice(atIndex + 1, colonIndex));
+ var lineNo = parseInt(line.slice(colonIndex + 1));
+ var funcSig = line.slice(0, atIndex);
+ var funcName = funcSig.slice(0, funcSig.indexOf("("));
+ frames.unshift({filename: filename,
+ funcName: funcName,
+ lineNo: lineNo});
+ });
+
+ return frames;
+};
+
+function nsIStackFramesToJSON(frame) {
+ var stack = [];
+
+ while (frame) {
+ if (frame.filename) {
+ var filename = deParentifyURL(frame.filename);
+ stack.splice(0, 0, {filename: filename,
+ lineNo: frame.lineNumber,
+ funcName: frame.name});
+ }
+ frame = frame.caller;
+ }
+
+ return stack;
+};
+
+var fromException = exports.fromException = function fromException(e) {
+ if (e instanceof Ci.nsIException)
+ return nsIStackFramesToJSON(e.location);
+ if (e.stack && e.stack.length)
+ return errorStackToJSON(e.stack);
+ if (e.fileName && typeof(e.lineNumber == "number"))
+ return [{filename: deParentifyURL(e.fileName),
+ lineNo: e.lineNumber,
+ funcName: null}];
+ return [];
+};
+
+var get = exports.get = function get() {
+ return nsIStackFramesToJSON(components.stack.caller);
+};
+
+var format = exports.format = function format(tbOrException) {
+ if (tbOrException === undefined) {
+ tbOrException = get();
+ tbOrException.splice(-1, 1);
+ }
+
+ var tb;
+ if (typeof(tbOrException) == "object" &&
+ tbOrException.constructor.name == "Array")
+ tb = tbOrException;
+ else
+ tb = fromException(tbOrException);
+
+ var lines = ["Traceback (most recent call last):"];
+
+ tb.forEach(
+ function(frame) {
+ if (!(frame.filename || frame.lineNo || frame.funcName))
+ return;
+ lines.push(' File "' + frame.filename + '", line ' +
+ frame.lineNo + ', in ' + frame.funcName);
+ var sourceLine = safeGetFileLine(frame.filename, frame.lineNo);
+ if (sourceLine)
+ lines.push(' ' + sourceLine.trim());
+ });
+
+ return lines.join("\n");
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/traits.js b/tools/addon-sdk-1.7/packages/api-utils/lib/traits.js
new file mode 100644
index 0000000..7c1c4ec
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/traits.js
@@ -0,0 +1,183 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {
+ compose: _compose,
+ override: _override,
+ resolve: _resolve,
+ trait: _trait,
+ //create: _create,
+ required,
+} = require('./traits/core');
+
+const defineProperties = Object.defineProperties,
+ freeze = Object.freeze,
+ create = Object.create;
+
+/**
+ * Work around bug 608959 by defining the _create function here instead of
+ * importing it from traits/core. For docs on this function, see the create
+ * function in that module.
+ *
+ * FIXME: remove this workaround in favor of importing the function once that
+ * bug has been fixed.
+ */
+function _create(proto, trait) {
+ let properties = {},
+ keys = Object.getOwnPropertyNames(trait);
+ for each(let key in keys) {
+ let descriptor = trait[key];
+ if (descriptor.required &&
+ !Object.prototype.hasOwnProperty.call(proto, key))
+ throw new Error('Missing required property: ' + key);
+ else if (descriptor.conflict)
+ throw new Error('Remaining conflicting property: ' + key);
+ else
+ properties[key] = descriptor;
+ }
+ return Object.create(proto, properties);
+}
+
+/**
+ * Placeholder for `Trait.prototype`
+ */
+let TraitProto = Object.prototype;
+
+function Get(key) this[key]
+function Set(key, value) this[key] = value
+
+/**
+ * Creates anonymous trait descriptor from the passed argument, unless argument
+ * is a trait constructor. In later case trait's already existing properties
+ * descriptor is returned.
+ * This is module's internal function and is used as a gateway to a trait's
+ * internal properties descriptor.
+ * @param {Function} $
+ * Composed trait's constructor.
+ * @returns {Object}
+ * Private trait property of the composition.
+ */
+function TraitDescriptor(object)
+ (
+ 'function' == typeof object &&
+ (object.prototype == TraitProto || object.prototype instanceof Trait)
+ ) ? object._trait(TraitDescriptor) : _trait(object)
+
+function Public(instance, trait) {
+ let result = {},
+ keys = Object.getOwnPropertyNames(trait);
+ for each (let key in keys) {
+ if ('_' === key.charAt(0) && '__iterator__' !== key )
+ continue;
+ let property = trait[key],
+ descriptor = {
+ configurable: property.configurable,
+ enumerable: property.enumerable
+ };
+ if (property.get)
+ descriptor.get = property.get.bind(instance);
+ if (property.set)
+ descriptor.set = property.set.bind(instance);
+ if ('value' in property) {
+ let value = property.value;
+ if ('function' === typeof value) {
+ descriptor.value = property.value.bind(instance);
+ descriptor.writable = property.writable;
+ } else {
+ descriptor.get = Get.bind(instance, key);
+ descriptor.set = Set.bind(instance, key);
+ }
+ }
+ result[key] = descriptor;
+ }
+ return result;
+}
+
+/**
+ * This is private function that composes new trait with privates.
+ */
+function Composition(trait) {
+ function Trait() {
+ let self = _create({}, trait);
+ self._public = create(Trait.prototype, Public(self, trait));
+ delete self._public.constructor;
+ if (Object === self.constructor)
+ self.constructor = Trait;
+ else
+ return self.constructor.apply(self, arguments) || self._public;
+ return self._public;
+ }
+ defineProperties(Trait, {
+ prototype: { value: freeze(create(TraitProto, {
+ constructor: { value: constructor, writable: true }
+ }))}, // writable is `true` to avoid getters in custom ES5
+ displayName: { value: (trait.constructor || constructor).name },
+ compose: { value: compose, enumerable: true },
+ override: { value: override, enumerable: true },
+ resolve: { value: resolve, enumerable: true },
+ required: { value: required, enumerable: true },
+ _trait: { value: function _trait(caller)
+ caller === TraitDescriptor ? trait : undefined
+ }
+ });
+ return freeze(Trait);
+}
+
+/**
+ * Composes new trait out of itself and traits / property maps passed as an
+ * arguments. If two or more traits / property maps have properties with the
+ * same name, the new trait will contain a "conflict" property for that name.
+ * This is a commutative and associative operation, and the order of its
+ * arguments is not significant.
+ * @params {Object|Function}
+ * List of Traits or property maps to create traits from.
+ * @returns {Function}
+ * New trait containing the combined properties of all the traits.
+ */
+function compose() {
+ let traits = Array.slice(arguments, 0);
+ traits.push(this);
+ return Composition(_compose.apply(null, traits.map(TraitDescriptor)));
+}
+
+/**
+ * Composes a new trait with all of the combined properties of `this` and the
+ * argument traits. In contrast to `compose`, `override` immediately resolves
+ * all conflicts resulting from this composition by overriding the properties of
+ * later traits. Trait priority is from left to right. I.e. the properties of
+ * the leftmost trait are never overridden.
+ * @params {Object} trait
+ * @returns {Object}
+ */
+function override() {
+ let traits = Array.slice(arguments, 0);
+ traits.push(this);
+ return Composition(_override.apply(null, traits.map(TraitDescriptor)));
+}
+
+/**
+ * Composes new resolved trait, with all the same properties as this
+ * trait, except that all properties whose name is an own property of
+ * `resolutions` will be renamed to `resolutions[name]`. If it is
+ * `resolutions[name]` is `null` value is changed into a required property
+ * descriptor.
+ */
+function resolve(resolutions)
+ Composition(_resolve(resolutions, TraitDescriptor(this)))
+
+/**
+ * Base Trait, that all the traits are composed of.
+ */
+const Trait = Composition({
+ /**
+ * Internal property holding public API of this instance.
+ */
+ _public: { value: null, configurable: true, writable: true },
+ toString: { value: function() '[object ' + this.constructor.name + ']' }
+});
+TraitProto = Trait.prototype;
+exports.Trait = Trait;
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/traits/core.js b/tools/addon-sdk-1.7/packages/api-utils/lib/traits/core.js
new file mode 100644
index 0000000..6e802c7
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/traits/core.js
@@ -0,0 +1,317 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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";
+// Design inspired by: http://www.traitsjs.org/
+
+// shortcuts
+const getOwnPropertyNames = Object.getOwnPropertyNames,
+ getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor,
+ hasOwn = Object.prototype.hasOwnProperty,
+ _create = Object.create;
+
+function doPropertiesMatch(object1, object2, name) {
+ // If `object1` has property with the given `name`
+ return name in object1 ?
+ // then `object2` should have it with the same value.
+ name in object2 && object1[name] === object2[name] :
+ // otherwise `object2` should not have property with the given `name`.
+ !(name in object2);
+}
+
+/**
+ * Compares two trait custom property descriptors if they are the same. If
+ * both are `conflict` or all the properties of descriptor are equal returned
+ * value will be `true`, otherwise it will be `false`.
+ * @param {Object} desc1
+ * @param {Object} desc2
+ */
+function areSame(desc1, desc2) {
+ return ('conflict' in desc1 && desc1.conflict &&
+ 'conflict' in desc2 && desc2.conflict) ||
+ (doPropertiesMatch(desc1, desc2, 'get') &&
+ doPropertiesMatch(desc1, desc2, 'set') &&
+ doPropertiesMatch(desc1, desc2, 'value') &&
+ doPropertiesMatch(desc1, desc2, 'enumerable') &&
+ doPropertiesMatch(desc1, desc2, 'required') &&
+ doPropertiesMatch(desc1, desc2, 'conflict'));
+}
+
+/**
+ * Converts array to an object whose own property names represent
+ * values of array.
+ * @param {String[]} names
+ * @returns {Object}
+ * @example
+ * Map(['foo', ...]) => { foo: true, ...}
+ */
+function Map(names) {
+ let map = {};
+ for each (let name in names)
+ map[name] = true;
+ return map;
+}
+
+
+const ERR_CONFLICT = 'Remaining conflicting property: ',
+ ERR_REQUIRED = 'Missing required property: ';
+/**
+ * Constant singleton, representing placeholder for required properties.
+ * @type {Object}
+ */
+const required = { toString: function()'<Trait.required>' };
+exports.required = required;
+
+/**
+ * Generates custom **required** property descriptor. Descriptor contains
+ * non-standard property `required` that is equal to `true`.
+ * @param {String} name
+ * property name to generate descriptor for.
+ * @returns {Object}
+ * custom property descriptor
+ */
+function Required(name) {
+ function required() { throw new Error(ERR_REQUIRED + name) }
+ return {
+ get: required,
+ set: required,
+ required: true
+ };
+}
+
+/**
+ * Generates custom **conflicting** property descriptor. Descriptor contains
+ * non-standard property `conflict` that is equal to `true`.
+ * @param {String} name
+ * property name to generate descriptor for.
+ * @returns {Object}
+ * custom property descriptor
+ */
+function Conflict(name) {
+ function conflict() { throw new Error(ERR_CONFLICT + name) }
+ return {
+ get: conflict,
+ set: conflict,
+ conflict: true
+ };
+}
+
+/**
+ * Function generates custom properties descriptor of the `object`s own
+ * properties. All the inherited properties are going to be ignored.
+ * Properties with values matching `required` singleton will be marked as
+ * 'required' properties.
+ * @param {Object} object
+ * Set of properties to generate trait from.
+ * @returns {Object}
+ * Properties descriptor of all of the `object`'s own properties.
+ */
+function trait(properties) {
+ let result = {},
+ keys = getOwnPropertyNames(properties);
+ for each (let key in keys) {
+ let descriptor = getOwnPropertyDescriptor(properties, key);
+ result[key] = (required === descriptor.value) ? Required(key) : descriptor;
+ }
+ return result;
+}
+exports.Trait = exports.trait = trait;
+
+/**
+ * Composes new trait. If two or more traits have own properties with the
+ * same name, the new trait will contain a 'conflict' property for that name.
+ * 'compose' is a commutative and associative operation, and the order of its
+ * arguments is not significant.
+ *
+ * @params {Object} trait
+ * Takes traits as an arguments
+ * @returns {Object}
+ * New trait containing the combined own properties of all the traits.
+ * @example
+ * var newTrait = compose(trait_1, trait_2, ..., trait_N);
+ */
+function compose(trait1, trait2) {
+ let traits = Array.slice(arguments, 0),
+ result = {};
+ for each (let trait in traits) {
+ let keys = getOwnPropertyNames(trait);
+ for each (let key in keys) {
+ let descriptor = trait[key];
+ // if property already exists and it's not a requirement
+ if (hasOwn.call(result, key) && !result[key].required) {
+ if (descriptor.required)
+ continue;
+ if (!areSame(descriptor, result[key]))
+ result[key] = Conflict(key);
+ } else {
+ result[key] = descriptor;
+ }
+ }
+ }
+ return result;
+}
+exports.compose = compose;
+
+/**
+ * Composes new trait with the same own properties as the original trait,
+ * except that all property names appearing in the first argument are replaced
+ * by 'required' property descriptors.
+ * @param {String[]} keys
+ * Array of strings property names.
+ * @param {Object} trait
+ * A trait some properties of which should be excluded.
+ * @returns {Object}
+ * @example
+ * var newTrait = exclude(['name', ...], trait)
+ */
+function exclude(keys, trait) {
+ let exclusions = Map(keys),
+ result = {};
+
+ keys = getOwnPropertyNames(trait);
+
+ for each (let key in keys) {
+ if (!hasOwn.call(exclusions, key) || trait[key].required)
+ result[key] = trait[key];
+ else
+ result[key] = Required(key);
+ }
+ return result;
+}
+
+/**
+ * Composes a new trait with all of the combined properties of the argument
+ * traits. In contrast to `compose`, `override` immediately resolves all
+ * conflicts resulting from this composition by overriding the properties of
+ * later traits. Trait priority is from left to right. I.e. the properties of
+ * the leftmost trait are never overridden.
+ * @params {Object} trait
+ * @returns {Object}
+ * @examples
+ * // override is associative:
+ * override(t1,t2,t3)
+ * // is equivalent to
+ * override(t1, override(t2, t3))
+ * // or
+ * to override(override(t1, t2), t3)
+ *
+ * // override is not commutative:
+ * override(t1,t2)
+ * // is not equivalent to
+ * override(t2,t1)
+ */
+function override() {
+ let traits = Array.slice(arguments, 0),
+ result = {};
+ for each (let trait in traits) {
+ let keys = getOwnPropertyNames(trait);
+ for each(let key in keys) {
+ let descriptor = trait[key];
+ if (!hasOwn.call(result, key) || result[key].required)
+ result[key] = descriptor;
+ }
+ }
+ return result;
+}
+exports.override = override;
+
+/**
+ * Composes a new trait with the same properties as the original trait, except
+ * that all properties whose name is an own property of map will be renamed to
+ * map[name], and a 'required' property for name will be added instead.
+ * @param {Object} map
+ * An object whose own properties serve as a mapping from old names to new
+ * names.
+ * @param {Object} trait
+ * A trait object
+ * @returns {Object}
+ * @example
+ * var newTrait = rename(map, trait);
+ */
+function rename(map, trait) {
+ let result = {},
+ keys = getOwnPropertyNames(trait);
+ for each(let key in keys) {
+ // must be renamed & it's not requirement
+ if (hasOwn.call(map, key) && !trait[key].required) {
+ let alias = map[key];
+ if (hasOwn.call(result, alias) && !result[alias].required)
+ result[alias] = Conflict(alias);
+ else
+ result[alias] = trait[key];
+ if (!hasOwn.call(result, key))
+ result[key] = Required(key);
+ } else { // must not be renamed or its a requirement
+ // property is not in result trait yet
+ if (!hasOwn.call(result, key))
+ result[key] = trait[key];
+ // property is already in resulted trait & it's not requirement
+ else if (!trait[key].required)
+ result[key] = Conflict(key);
+ }
+ }
+ return result;
+}
+
+/**
+* Composes new resolved trait, with all the same properties as the original
+* trait, except that all properties whose name is an own property of
+* resolutions will be renamed to `resolutions[name]`. If it is
+* `resolutions[name]` is `null` value is changed into a required property
+* descriptor.
+* function can be implemented as `rename(map,exclude(exclusions, trait))`
+* where map is the subset of mappings from oldName to newName and exclusions
+* is an array of all the keys that map to `null`.
+* Note: it's important to **first** `exclude`, **then** `rename`, since
+* `exclude` and rename are not associative.
+* @param {Object} resolutions
+* An object whose own properties serve as a mapping from old names to new
+* names, or to `null` if the property should be excluded.
+* @param {Object} trait
+* A trait object
+* @returns {Object}
+* Resolved trait with the same own properties as the original trait.
+*/
+function resolve(resolutions, trait) {
+ let renames = {},
+ exclusions = [],
+ keys = getOwnPropertyNames(resolutions);
+ for each (let key in keys) { // pre-process renamed and excluded properties
+ if (resolutions[key]) // old name -> new name
+ renames[key] = resolutions[key];
+ else // name -> undefined
+ exclusions.push(key);
+ }
+ return rename(renames, exclude(exclusions, trait));
+}
+exports.resolve = resolve;
+
+/**
+ * `create` is like `Object.create`, except that it ensures that:
+ * - an exception is thrown if 'trait' still contains required properties
+ * - an exception is thrown if 'trait' still contains conflicting
+ * properties
+ * @param {Object}
+ * prototype of the completed object
+ * @param {Object} trait
+ * trait object to be turned into a complete object
+ * @returns {Object}
+ * An object with all of the properties described by the trait.
+ */
+function create(proto, trait) {
+ let properties = {},
+ keys = getOwnPropertyNames(trait);
+ for each(let key in keys) {
+ let descriptor = trait[key];
+ if (descriptor.required && !hasOwn.call(proto, key))
+ throw new Error(ERR_REQUIRED + key);
+ else if (descriptor.conflict)
+ throw new Error(ERR_CONFLICT + key);
+ else
+ properties[key] = descriptor;
+ }
+ return _create(proto, properties);
+}
+exports.create = create;
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/type.js b/tools/addon-sdk-1.7/packages/api-utils/lib/type.js
new file mode 100644
index 0000000..56df14e
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/type.js
@@ -0,0 +1,340 @@
+/* vim:ts=2:sts=2:sw=2:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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";
+
+/**
+ * Returns `true` if `value` is `undefined`.
+ * @examples
+ * var foo; isUndefined(foo); // true
+ * isUndefined(0); // false
+ */
+function isUndefined(value) {
+ return value === undefined;
+}
+exports.isUndefined = isUndefined;
+
+/**
+ * Returns `true` if value is `null`.
+ * @examples
+ * isNull(null); // true
+ * isNull(undefined); // false
+ */
+function isNull(value) {
+ return value === null;
+}
+exports.isNull = isNull;
+
+/**
+ * Returns `true` if value is a string.
+ * @examples
+ * isString("moe"); // true
+ */
+function isString(value) {
+ return typeof value === "string";
+}
+exports.isString = isString;
+
+/**
+ * Returns `true` if `value` is a number.
+ * @examples
+ * isNumber(8.4 * 5); // true
+ */
+function isNumber(value) {
+ return typeof value === "number";
+}
+exports.isNumber = isNumber;
+
+/**
+ * Returns `true` if `value` is a `RegExp`.
+ * @examples
+ * isRegExp(/moe/); // true
+ */
+function isRegExp(value) {
+ return isObject(value) && instanceOf(value, RegExp);
+}
+exports.isRegExp = isRegExp;
+
+/**
+ * Returns true if `value` is a `Date`.
+ * @examples
+ * isDate(new Date()); // true
+ */
+function isDate(value) {
+ return isObject(value) && instanceOf(value, Date);
+}
+exports.isDate = isDate;
+
+/**
+ * Returns true if object is a Function.
+ * @examples
+ * isFunction(function foo(){}) // true
+ */
+function isFunction(value) {
+ return typeof value === "function";
+}
+exports.isFunction = isFunction;
+
+/**
+ * Returns `true` if `value` is an object (please note that `null` is considered
+ * to be an atom and not an object).
+ * @examples
+ * isObject({}) // true
+ * isObject(null) // false
+ */
+function isObject(value) {
+ return typeof value === "object" && value !== null;
+}
+exports.isObject = isObject;
+
+/**
+ * Returns true if `value` is an Array.
+ * @examples
+ * isArray([1, 2, 3]) // true
+ * isArray({ 0: 'foo', length: 1 }) // false
+ */
+var isArray = Array.isArray || function isArray(value) {
+ Object.prototype.toString.call(value) === "[object Array]";
+}
+exports.isArray = isArray;
+
+/**
+ * Returns `true` if `value` is an Arguments object.
+ * @examples
+ * (function(){ return isArguments(arguments); })(1, 2, 3); // true
+ * isArguments([1,2,3]); // false
+ */
+function isArguments(value) {
+ Object.prototype.toString.call(value) === "[object Arguments]";
+}
+exports.isArguments = isArguments;
+
+/**
+ * Returns true if it is a primitive `value`. (null, undefined, number,
+ * boolean, string)
+ * @examples
+ * isPrimitive(3) // true
+ * isPrimitive('foo') // true
+ * isPrimitive({ bar: 3 }) // false
+ */
+function isPrimitive(value) {
+ return !isFunction(value) && !isObject(value);
+}
+exports.isPrimitive = isPrimitive;
+
+/**
+ * Returns `true` if given `object` is flat (it is direct decedent of
+ * `Object.prototype` or `null`).
+ * @examples
+ * isFlat({}) // true
+ * isFlat(new Type()) // false
+ */
+function isFlat(object) {
+ return isObject(object) && (isNull(Object.getPrototypeOf(object)) ||
+ isNull(Object.getPrototypeOf(
+ Object.getPrototypeOf(object))));
+}
+exports.isFlat = isFlat;
+
+/**
+ * Returns `true` if object contains no values.
+ */
+function isEmpty(object) {
+ if (isObject(object)) {
+ for (var key in object)
+ return false;
+ return true;
+ }
+ return false;
+}
+exports.isEmpty = isEmpty;
+
+/**
+ * Returns `true` if `value` is an array / flat object containing only atomic
+ * values and other flat objects.
+ */
+function isJSON(value, visited) {
+ // Adding value to array of visited values.
+ (visited || (visited = [])).push(value);
+ // If `value` is an atom return `true` cause it's valid JSON.
+ return isPrimitive(value) ||
+ // If `value` is an array of JSON values that has not been visited
+ // yet.
+ (isArray(value) && value.every(function(element) {
+ return isJSON(element, visited);
+ })) ||
+ // If `value` is a plain object containing properties with a JSON
+ // values it's a valid JSON.
+ (isFlat(value) && Object.keys(value).every(function(key) {
+ var $ = Object.getOwnPropertyDescriptor(value, key);
+ // Check every proprety of a plain object to verify that
+ // it's neither getter nor setter, but a JSON value, that
+ // has not been visited yet.
+ return ((!isObject($.value) || !~visited.indexOf($.value)) &&
+ !('get' in $) && !('set' in $) &&
+ isJSON($.value, visited));
+ }));
+}
+exports.isJSON = function (value) {
+ return isJSON(value);
+};
+
+/**
+ * Returns if `value` is an instance of a given `Type`. This is exactly same as
+ * `value instanceof Type` with a difference that `Type` can be from a scope
+ * that has a different top level object. (Like in case where `Type` is a
+ * function from different iframe / jetpack module / sandbox).
+ */
+function instanceOf(value, Type) {
+ var isConstructorNameSame;
+ var isConstructorSourceSame;
+
+ // If `instanceof` returned `true` we know result right away.
+ var isInstanceOf = value instanceof Type;
+
+ // If `instanceof` returned `false` we do ducktype check since `Type` may be
+ // from a different sandbox. If a constructor of the `value` or a constructor
+ // of the value's prototype has same name and source we assume that it's an
+ // instance of the Type.
+ if (!isInstanceOf && value) {
+ isConstructorNameSame = value.constructor.name === Type.name;
+ isConstructorSourceSame = String(value.constructor) == String(Type);
+ isInstanceOf = (isConstructorNameSame && isConstructorSourceSame) ||
+ instanceOf(Object.getPrototypeOf(value), Type);
+ }
+ return isInstanceOf;
+}
+exports.instanceOf = instanceOf;
+
+/**
+ * Function returns textual representation of a value passed to it. Function
+ * takes additional `indent` argument that is used for indentation. Also
+ * optional `limit` argument may be passed to limit amount of detail returned.
+ * @param {Object} value
+ * @param {String} [indent=" "]
+ * @param {Number} [limit]
+ */
+function source(value, indent, limit, offset, visited) {
+ var result;
+ var names;
+ var nestingIndex;
+ var isCompact = !isUndefined(limit);
+
+ indent = indent || " ";
+ offset = (offset || "");
+ result = "";
+ visited = visited || [];
+
+ if (isUndefined(value)) {
+ result += "undefined";
+ }
+ else if (isNull(value)) {
+ result += "null";
+ }
+ else if (isString(value)) {
+ result += '"' + value + '"';
+ }
+ else if (isFunction(value)) {
+ value = String(value).split("\n");
+ if (isCompact && value.length > 2) {
+ value = value.splice(0, 2);
+ value.push("...}");
+ }
+ result += value.join("\n" + offset);
+ }
+ else if (isArray(value)) {
+ if ((nestingIndex = (visited.indexOf(value) + 1))) {
+ result = "#" + nestingIndex + "#";
+ }
+ else {
+ visited.push(value);
+
+ if (isCompact)
+ value = value.slice(0, limit);
+
+ result += "[\n";
+ result += value.map(function(value) {
+ return offset + indent + source(value, indent, limit, offset + indent,
+ visited);
+ }).join(",\n");
+ result += isCompact && value.length > limit ?
+ ",\n" + offset + "...]" : "\n" + offset + "]";
+ }
+ }
+ else if (isObject(value)) {
+ if ((nestingIndex = (visited.indexOf(value) + 1))) {
+ result = "#" + nestingIndex + "#"
+ }
+ else {
+ visited.push(value)
+
+ names = Object.keys(value);
+
+ result += "{ // " + value + "\n";
+ result += (isCompact ? names.slice(0, limit) : names).map(function(name) {
+ var _limit = isCompact ? limit - 1 : limit;
+ var descriptor = Object.getOwnPropertyDescriptor(value, name);
+ var result = offset + indent + "// ";
+ var accessor;
+ if (0 <= name.indexOf(" "))
+ name = '"' + name + '"';
+
+ if (descriptor.writable)
+ result += "writable ";
+ if (descriptor.configurable)
+ result += "configurable ";
+ if (descriptor.enumerable)
+ result += "enumerable ";
+
+ result += "\n";
+ if ("value" in descriptor) {
+ result += offset + indent + name + ": ";
+ result += source(descriptor.value, indent, _limit, indent + offset,
+ visited);
+ }
+ else {
+
+ if (descriptor.get) {
+ result += offset + indent + "get " + name + " ";
+ accessor = source(descriptor.get, indent, _limit, indent + offset,
+ visited);
+ result += accessor.substr(accessor.indexOf("{"));
+ }
+
+ if (descriptor.set) {
+ result += offset + indent + "set " + name + " ";
+ accessor = source(descriptor.set, indent, _limit, indent + offset,
+ visited);
+ result += accessor.substr(accessor.indexOf("{"));
+ }
+ }
+ return result;
+ }).join(",\n");
+
+ if (isCompact) {
+ if (names.length > limit && limit > 0) {
+ result += ",\n" + offset + indent + "//...";
+ }
+ }
+ else {
+ if (names.length)
+ result += ",";
+
+ result += "\n" + offset + indent + '"__proto__": ';
+ result += source(Object.getPrototypeOf(value), indent, 0,
+ offset + indent);
+ }
+
+ result += "\n" + offset + "}";
+ }
+ }
+ else {
+ result += String(value);
+ }
+ return result;
+}
+exports.source = function (value, indentation, limit) {
+ return source(value, indentation, limit);
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/unit-test-finder.js b/tools/addon-sdk-1.7/packages/api-utils/lib/unit-test-finder.js
new file mode 100644
index 0000000..993749b
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/unit-test-finder.js
@@ -0,0 +1,76 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const file = require("./file");
+const packaging = require('@packaging');
+const memory = require('api-utils/memory');
+const suites = packaging.allTestModules;
+
+
+const NOT_TESTS = ['setup', 'teardown'];
+
+var TestFinder = exports.TestFinder = function TestFinder(options) {
+ memory.track(this);
+ this.filter = options.filter;
+ this.testInProcess = options.testInProcess === false ? false : true;
+ this.testOutOfProcess = options.testOutOfProcess === true ? true : false;
+};
+
+TestFinder.prototype = {
+ _makeTest: function _makeTest(suite, name, test) {
+ function runTest(runner) {
+ console.info("executing '" + suite + "." + name + "'");
+ test(runner);
+ }
+ return runTest;
+ },
+
+ findTests: function findTests(cb) {
+ var self = this;
+ var tests = [];
+ var filter;
+ // A filter string is {fileNameRegex}[:{testNameRegex}] - ie, a colon
+ // optionally separates a regex for the test fileName from a regex for the
+ // testName.
+ if (this.filter) {
+ var colonPos = this.filter.indexOf(':');
+ var filterFileRegex, filterNameRegex;
+ if (colonPos === -1) {
+ filterFileRegex = new RegExp(self.filter);
+ } else {
+ filterFileRegex = new RegExp(self.filter.substr(0, colonPos));
+ filterNameRegex = new RegExp(self.filter.substr(colonPos + 1));
+ }
+ // This function will first be called with just the filename; if
+ // it returns true the module will be loaded then the function
+ // called again with both the filename and the testname.
+ filter = function(filename, testname) {
+ return filterFileRegex.test(filename) &&
+ ((testname && filterNameRegex) ? filterNameRegex.test(testname)
+ : true);
+ };
+ } else
+ filter = function() {return true};
+
+ suites.forEach(
+ function(suite) {
+ var module = require(suite);
+ if (self.testInProcess)
+ for each (let name in Object.keys(module).sort()) {
+ if(NOT_TESTS.indexOf(name) === -1 && filter(suite, name)) {
+ tests.push({
+ setup: module.setup,
+ teardown: module.teardown,
+ testFunction: self._makeTest(suite, name, module[name]),
+ name: suite + "." + name
+ });
+ }
+ }
+ });
+
+ cb(tests);
+ }
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/unit-test.js b/tools/addon-sdk-1.7/packages/api-utils/lib/unit-test.js
new file mode 100644
index 0000000..4018487
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/unit-test.js
@@ -0,0 +1,440 @@
+/* vim:st=2:sts=2:sw=2:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const memory = require('api-utils/memory');
+var timer = require("./timer");
+
+exports.findAndRunTests = function findAndRunTests(options) {
+ var TestFinder = require("./unit-test-finder").TestFinder;
+ var finder = new TestFinder({
+ filter: options.filter,
+ testInProcess: options.testInProcess,
+ testOutOfProcess: options.testOutOfProcess
+ });
+ var runner = new TestRunner({fs: options.fs});
+ finder.findTests(
+ function (tests) {
+ runner.startMany({tests: tests,
+ stopOnError: options.stopOnError,
+ onDone: options.onDone});
+ });
+};
+
+var TestRunner = exports.TestRunner = function TestRunner(options) {
+ if (options) {
+ this.fs = options.fs;
+ }
+ this.console = (options && "console" in options) ? options.console : console;
+ memory.track(this);
+ this.passed = 0;
+ this.failed = 0;
+ this.testRunSummary = [];
+ this.expectFailNesting = 0;
+};
+
+TestRunner.prototype = {
+ toString: function toString() "[object TestRunner]",
+
+ DEFAULT_PAUSE_TIMEOUT: 10000,
+ PAUSE_DELAY: 500,
+
+ _logTestFailed: function _logTestFailed(why) {
+ this.test.errors[why]++;
+ if (!this.testFailureLogged) {
+ this.console.error("TEST FAILED: " + this.test.name + " (" + why + ")");
+ this.testFailureLogged = true;
+ }
+ },
+
+ pass: function pass(message) {
+ if(!this.expectFailure) {
+ this.console.info("pass:", message);
+ this.passed++;
+ this.test.passed++;
+ }
+ else {
+ this.expectFailure = false;
+ this.fail('Failure Expected: ' + message);
+ }
+ },
+
+ fail: function fail(message) {
+ if(!this.expectFailure) {
+ this._logTestFailed("failure");
+ this.console.error("fail:", message);
+ this.console.trace();
+ this.failed++;
+ this.test.failed++;
+ }
+ else {
+ this.expectFailure = false;
+ this.pass(message);
+ }
+ },
+
+ expectFail: function(callback) {
+ this.expectFailure = true;
+ callback();
+ this.expectFailure = false;
+ },
+
+ exception: function exception(e) {
+ this._logTestFailed("exception");
+ this.console.exception(e);
+ this.failed++;
+ this.test.failed++;
+ },
+
+ assertMatches: function assertMatches(string, regexp, message) {
+ if (regexp.test(string)) {
+ if (!message)
+ message = uneval(string) + " matches " + uneval(regexp);
+ this.pass(message);
+ } else {
+ var no = uneval(string) + " doesn't match " + uneval(regexp);
+ if (!message)
+ message = no;
+ else
+ message = message + " (" + no + ")";
+ this.fail(message);
+ }
+ },
+
+ assertRaises: function assertRaises(func, predicate, message) {
+ try {
+ func();
+ if (message)
+ this.fail(message + " (no exception thrown)");
+ else
+ this.fail("function failed to throw exception");
+ } catch (e) {
+ var errorMessage;
+ if (typeof(e) == "string")
+ errorMessage = e;
+ else
+ errorMessage = e.message;
+ if (typeof(predicate) == "string")
+ this.assertEqual(errorMessage, predicate, message);
+ else
+ this.assertMatches(errorMessage, predicate, message);
+ }
+ },
+
+ assert: function assert(a, message) {
+ if (!a) {
+ if (!message)
+ message = "assertion failed, value is " + a;
+ this.fail(message);
+ } else
+ this.pass(message || "assertion successful");
+ },
+
+ assertNotEqual: function assertNotEqual(a, b, message) {
+ if (a != b) {
+ if (!message)
+ message = "a != b != " + uneval(a);
+ this.pass(message);
+ } else {
+ var equality = uneval(a) + " == " + uneval(b);
+ if (!message)
+ message = equality;
+ else
+ message += " (" + equality + ")";
+ this.fail(message);
+ }
+ },
+
+ assertEqual: function assertEqual(a, b, message) {
+ if (a == b) {
+ if (!message)
+ message = "a == b == " + uneval(a);
+ this.pass(message);
+ } else {
+ var inequality = uneval(a) + " != " + uneval(b);
+ if (!message)
+ message = inequality;
+ else
+ message += " (" + inequality + ")";
+ this.fail(message);
+ }
+ },
+
+ assertNotStrictEqual: function assertNotStrictEqual(a, b, message) {
+ if (a !== b) {
+ if (!message)
+ message = "a !== b !== " + uneval(a);
+ this.pass(message);
+ } else {
+ var equality = uneval(a) + " === " + uneval(b);
+ if (!message)
+ message = equality;
+ else
+ message += " (" + equality + ")";
+ this.fail(message);
+ }
+ },
+
+ assertStrictEqual: function assertStrictEqual(a, b, message) {
+ if (a === b) {
+ if (!message)
+ message = "a === b === " + uneval(a);
+ this.pass(message);
+ } else {
+ var inequality = uneval(a) + " !== " + uneval(b);
+ if (!message)
+ message = inequality;
+ else
+ message += " (" + inequality + ")";
+ this.fail(message);
+ }
+ },
+
+ assertFunction: function assertFunction(a, message) {
+ this.assertStrictEqual('function', typeof a, message);
+ },
+
+ assertUndefined: function(a, message) {
+ this.assertStrictEqual('undefined', typeof a, message);
+ },
+
+ assertNotUndefined: function(a, message) {
+ this.assertNotStrictEqual('undefined', typeof a, message);
+ },
+
+ assertNull: function(a, message) {
+ this.assertStrictEqual(null, a, message);
+ },
+
+ assertNotNull: function(a, message) {
+ this.assertNotStrictEqual(null, a, message);
+ },
+
+ assertObject: function(a, message) {
+ this.assertStrictEqual('[object Object]', Object.prototype.toString.apply(a), message);
+ },
+
+ assertString: function(a, message) {
+ this.assertStrictEqual('[object String]', Object.prototype.toString.apply(a), message);
+ },
+
+ assertArray: function(a, message) {
+ this.assertStrictEqual('[object Array]', Object.prototype.toString.apply(a), message);
+ },
+
+ assertNumber: function(a, message) {
+ this.assertStrictEqual('[object Number]', Object.prototype.toString.apply(a), message);
+ },
+
+ done: function done() {
+ if (!this.isDone) {
+ this.isDone = true;
+ if(this.test.teardown) {
+ this.test.teardown(this);
+ }
+ if (this.waitTimeout !== null) {
+ timer.clearTimeout(this.waitTimeout);
+ this.waitTimeout = null;
+ }
+ if (this.test.passed == 0 && this.test.failed == 0) {
+ this._logTestFailed("empty test");
+ this.failed++;
+ this.test.failed++;
+ }
+
+ this.testRunSummary.push({
+ name: this.test.name,
+ passed: this.test.passed,
+ failed: this.test.failed,
+ errors: [error for (error in this.test.errors)].join(", ")
+ });
+
+ if (this.onDone !== null) {
+ var onDone = this.onDone;
+ var self = this;
+ this.onDone = null;
+ timer.setTimeout(function() { onDone(self); }, 0);
+ }
+ }
+ },
+
+ // Set of assertion functions to wait for an assertion to become true
+ // These functions take the same arguments as the TestRunner.assert* methods.
+ waitUntil: function waitUntil() {
+ return this._waitUntil(this.assert, arguments);
+ },
+
+ waitUntilNotEqual: function waitUntilNotEqual() {
+ return this._waitUntil(this.assertNotEqual, arguments);
+ },
+
+ waitUntilEqual: function waitUntilEqual() {
+ return this._waitUntil(this.assertEqual, arguments);
+ },
+
+ waitUntilMatches: function waitUntilMatches() {
+ return this._waitUntil(this.assertMatches, arguments);
+ },
+
+ /**
+ * Internal function that waits for an assertion to become true.
+ * @param {Function} assertionMethod
+ * Reference to a TestRunner assertion method like test.assert,
+ * test.assertEqual, ...
+ * @param {Array} args
+ * List of arguments to give to the previous assertion method.
+ * All functions in this list are going to be called to retrieve current
+ * assertion values.
+ */
+ _waitUntil: function waitUntil(assertionMethod, args) {
+ let count = 0;
+ let maxCount = this.DEFAULT_PAUSE_TIMEOUT / this.PAUSE_DELAY;
+
+ // We need to ensure that test is asynchronous
+ if (!this.waitTimeout)
+ this.waitUntilDone(this.DEFAULT_PAUSE_TIMEOUT);
+
+ let callback = null;
+ let finished = false;
+
+ let test = this;
+
+ // capture a traceback before we go async.
+ let traceback = require("./traceback");
+ let stack = traceback.get();
+ stack.splice(-2, 2);
+ let currentWaitStack = traceback.format(stack);
+ let timeout = null;
+
+ function loop(stopIt) {
+ timeout = null;
+
+ // Build a mockup object to fake TestRunner API and intercept calls to
+ // pass and fail methods, in order to retrieve nice error messages
+ // and assertion result
+ let mock = {
+ pass: function (msg) {
+ test.pass(msg);
+ test.waitUntilCallback = null;
+ if (callback && !stopIt)
+ callback();
+ finished = true;
+ },
+ fail: function (msg) {
+ // If we are called on test timeout, we stop the loop
+ // and print which test keeps failing:
+ if (stopIt) {
+ test.console.error("test assertion never became true:\n",
+ msg + "\n",
+ currentWaitStack);
+ if (timeout)
+ timer.clearTimeout(timeout);
+ return;
+ }
+ timeout = timer.setTimeout(loop, test.PAUSE_DELAY);
+ }
+ };
+
+ // Automatically call args closures in order to build arguments for
+ // assertion function
+ let appliedArgs = [];
+ for (let i = 0, l = args.length; i < l; i++) {
+ let a = args[i];
+ if (typeof a == "function") {
+ try {
+ a = a();
+ }
+ catch(e) {
+ test.fail("Exception when calling asynchronous assertion: " + e);
+ finished = true;
+ return;
+ }
+ }
+ appliedArgs.push(a);
+ }
+
+ // Finally call assertion function with current assertion values
+ assertionMethod.apply(mock, appliedArgs);
+ }
+ loop();
+ this.waitUntilCallback = loop;
+
+ // Return an object with `then` method, to offer a way to execute
+ // some code when the assertion passed or failed
+ return {
+ then: function (c) {
+ callback = c;
+
+ // In case of immediate positive result, we need to execute callback
+ // immediately here:
+ if (finished)
+ callback();
+ }
+ };
+ },
+
+ waitUntilDone: function waitUntilDone(ms) {
+ if (ms === undefined)
+ ms = this.DEFAULT_PAUSE_TIMEOUT;
+
+ var self = this;
+
+ function tiredOfWaiting() {
+ self._logTestFailed("timed out");
+ if (self.waitUntilCallback) {
+ self.waitUntilCallback(true);
+ self.waitUntilCallback = null;
+ }
+ self.failed++;
+ self.test.failed++;
+ self.done();
+ }
+
+ // We may already have registered a timeout callback
+ if (this.waitTimeout)
+ timer.clearTimeout(this.waitTimeout);
+
+ this.waitTimeout = timer.setTimeout(tiredOfWaiting, ms);
+ },
+
+ startMany: function startMany(options) {
+ function runNextTest(self) {
+ var test = options.tests.shift();
+ if (options.stopOnError && self.test && self.test.failed) {
+ self.console.error("aborted: test failed and --stop-on-error was specified");
+ options.onDone(self);
+ } else if (test) {
+ self.start({test: test, onDone: runNextTest});
+ } else {
+ options.onDone(self);
+ }
+ }
+ runNextTest(this);
+ },
+
+ start: function start(options) {
+ this.test = options.test;
+ this.test.passed = 0;
+ this.test.failed = 0;
+ this.test.errors = {};
+
+ this.isDone = false;
+ this.onDone = options.onDone;
+ this.waitTimeout = null;
+ this.testFailureLogged = false;
+
+ try {
+ if(this.test.setup) {
+ this.test.setup(this);
+ }
+ this.test.testFunction(this);
+ } catch (e) {
+ this.exception(e);
+ }
+ if (this.waitTimeout === null)
+ this.done();
+ }
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/unload.js b/tools/addon-sdk-1.7/packages/api-utils/lib/unload.js
new file mode 100644
index 0000000..aba5923
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/unload.js
@@ -0,0 +1,63 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Parts of this module were taken from narwhal:
+//
+// http://narwhaljs.org
+
+var observers = [];
+var unloaders = [];
+
+var when = exports.when = function when(observer) {
+ if (observers.indexOf(observer) != -1)
+ return;
+ observers.unshift(observer);
+};
+
+var send = exports.send = function send(reason, onError) {
+ onError = onError || console.exception;
+ observers.forEach(function (observer) {
+ try {
+ observer(reason);
+ } catch (e) {
+ onError(e);
+ }
+ });
+};
+
+var ensure = exports.ensure = function ensure(obj, destructorName) {
+ if (!destructorName)
+ destructorName = "unload";
+ if (!(destructorName in obj))
+ throw new Error("object has no '" + destructorName + "' property");
+
+ let called = false;
+ let originalDestructor = obj[destructorName];
+
+ function unloadWrapper(reason) {
+ if (!called) {
+ called = true;
+ let index = unloaders.indexOf(unloadWrapper);
+ if (index == -1)
+ throw new Error("internal error: unloader not found");
+ unloaders.splice(index, 1);
+ originalDestructor.call(obj, reason);
+ originalDestructor = null;
+ destructorName = null;
+ obj = null;
+ }
+ };
+
+ unloaders.push(unloadWrapper);
+
+ obj[destructorName] = unloadWrapper;
+};
+
+when(
+ function(reason) {
+ unloaders.slice().forEach(
+ function(unloadWrapper) {
+ unloadWrapper(reason);
+ });
+ });
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/url.js b/tools/addon-sdk-1.7/packages/api-utils/lib/url.js
new file mode 100644
index 0000000..29fc12c
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/url.js
@@ -0,0 +1,113 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {Cc,Ci,Cr} = require("chrome");
+
+var ios = Cc['@mozilla.org/network/io-service;1']
+ .getService(Ci.nsIIOService);
+
+var resProt = ios.getProtocolHandler("resource")
+ .QueryInterface(Ci.nsIResProtocolHandler);
+
+function newURI(uriStr, base) {
+ try {
+ let baseURI = base ? ios.newURI(base, null, null) : null;
+ return ios.newURI(uriStr, null, baseURI);
+ }
+ catch (e if e.result == Cr.NS_ERROR_MALFORMED_URI) {
+ throw new Error("malformed URI: " + uriStr);
+ }
+ catch (e if (e.result == Cr.NS_ERROR_FAILURE ||
+ e.result == Cr.NS_ERROR_ILLEGAL_VALUE)) {
+ throw new Error("invalid URI: " + uriStr);
+ }
+}
+
+function resolveResourceURI(uri) {
+ var resolved;
+ try {
+ resolved = resProt.resolveURI(uri);
+ } catch (e if e.result == Cr.NS_ERROR_NOT_AVAILABLE) {
+ throw new Error("resource does not exist: " + uri.spec);
+ };
+ return resolved;
+}
+
+let fromFilename = exports.fromFilename = function fromFilename(path) {
+ var file = Cc['@mozilla.org/file/local;1']
+ .createInstance(Ci.nsILocalFile);
+ file.initWithPath(path);
+ return ios.newFileURI(file).spec;
+};
+
+let toFilename = exports.toFilename = function toFilename(url) {
+ var uri = newURI(url);
+ if (uri.scheme == "resource")
+ uri = newURI(resolveResourceURI(uri));
+ if (uri.scheme == "chrome") {
+ var channel = ios.newChannelFromURI(uri);
+ try {
+ channel = channel.QueryInterface(Ci.nsIFileChannel);
+ return channel.file.path;
+ } catch (e if e.result == Cr.NS_NOINTERFACE) {
+ throw new Error("chrome url isn't on filesystem: " + url);
+ }
+ }
+ if (uri.scheme == "file") {
+ var file = uri.QueryInterface(Ci.nsIFileURL).file;
+ return file.path;
+ }
+ throw new Error("cannot map to filename: " + url);
+};
+
+function URL(url, base) {
+ if (!(this instanceof URL)) {
+ return new URL(url, base);
+ }
+
+ var uri = newURI(url, base);
+
+ var userPass = null;
+ try {
+ userPass = uri.userPass ? uri.userPass : null;
+ } catch (e if e.result == Cr.NS_ERROR_FAILURE) {}
+
+ var host = null;
+ try {
+ host = uri.host;
+ } catch (e if e.result == Cr.NS_ERROR_FAILURE) {}
+
+ var port = null;
+ try {
+ port = uri.port == -1 ? null : uri.port;
+ } catch (e if e.result == Cr.NS_ERROR_FAILURE) {}
+
+ this.__defineGetter__("scheme", function() uri.scheme);
+ this.__defineGetter__("userPass", function() userPass);
+ this.__defineGetter__("host", function() host);
+ this.__defineGetter__("port", function() port);
+ this.__defineGetter__("path", function() uri.path);
+
+ Object.defineProperties(this, {
+ toString: {
+ value: function URL_toString() new String(uri.spec).toString(),
+ enumerable: false
+ },
+ valueOf: {
+ value: function() new String(uri.spec).valueOf(),
+ enumerable: false
+ },
+ toSource: {
+ value: function() new String(uri.spec).toSource(),
+ enumerable: false
+ }
+ });
+
+ return this;
+};
+
+URL.prototype = Object.create(String.prototype);
+exports.URL = URL;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/utils/data.js b/tools/addon-sdk-1.7/packages/api-utils/lib/utils/data.js
new file mode 100644
index 0000000..33356cd
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/utils/data.js
@@ -0,0 +1,71 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { Cc, Ci, Cu } = require("chrome");
+const IOService = Cc["@mozilla.org/network/io-service;1"].
+ getService(Ci.nsIIOService);
+const AppShellService = Cc["@mozilla.org/appshell/appShellService;1"].
+ getService(Ci.nsIAppShellService);
+
+const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm");
+const FaviconService = Cc["@mozilla.org/browser/favicon-service;1"].
+ getService(Ci.nsIFaviconService);
+
+const PNG_B64 = "data:image/png;base64,";
+const DEF_FAVICON_URI = "chrome://mozapps/skin/places/defaultFavicon.png";
+let DEF_FAVICON = null;
+
+/**
+ * Takes URI of the page and returns associated favicon URI.
+ * If page under passed uri has no favicon then base64 encoded data URI of
+ * default faveicon is returned.
+ * @param {String} uri
+ * @returns {String}
+ */
+exports.getFaviconURIForLocation = function getFaviconURIForLocation(uri) {
+ let pageURI = NetUtil.newURI(uri);
+ try {
+ return FaviconService.getFaviconDataAsDataURL(
+ FaviconService.getFaviconForPage(pageURI));
+ }
+ catch(e) {
+ if (!DEF_FAVICON) {
+ DEF_FAVICON = PNG_B64 +
+ base64Encode(getChromeURIContent(DEF_FAVICON_URI));
+ }
+ return DEF_FAVICON;
+ }
+}
+
+/**
+ * Takes chrome URI and returns content under that URI.
+ * @param {String} chromeURI
+ * @returns {String}
+ */
+function getChromeURIContent(chromeURI) {
+ let channel = IOService.newChannel(chromeURI, null, null);
+ let input = channel.open();
+ let stream = Cc["@mozilla.org/binaryinputstream;1"].
+ createInstance(Ci.nsIBinaryInputStream);
+ stream.setInputStream(input);
+ let content = stream.readBytes(input.available());
+ stream.close();
+ input.close();
+ return content;
+}
+exports.getChromeURIContent = getChromeURIContent;
+
+/**
+ * Creates a base-64 encoded ASCII string from a string of binary data.
+ */
+function base64Encode(data) AppShellService.hiddenDOMWindow.btoa(String(data));
+exports.base64Encode = base64Encode;
+
+/**
+ * Decodes a string of data which has been encoded using base-64 encoding.
+ */
+function base64Decode(data) AppShellService.hiddenDOMWindow.atob(String(data));
+exports.base64Decode = base64Decode;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/utils/object.js b/tools/addon-sdk-1.7/packages/api-utils/lib/utils/object.js
new file mode 100644
index 0000000..165aa0b
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/utils/object.js
@@ -0,0 +1,46 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/**
+ * Merges all the properties of all arguments into first argument. If two or
+ * more argument objects have own properties with the same name, the property
+ * is overridden, with precedence from right to left, implying, that properties
+ * of the object on the left are overridden by a same named property of the
+ * object on the right.
+ * @examples
+ * var a = { bar: 0, a: 'a' }
+ * var b = merge(a, { foo: 'foo', bar: 1 }, { foo: 'bar', name: 'b' });
+ * b === a // true
+ * b.a // 'a'
+ * b.foo // 'bar'
+ * b.bar // 1
+ * b.name // 'b'
+ */
+function merge(source) {
+ let descriptor = {};
+ Array.slice(arguments, 1).forEach(function onEach(properties) {
+ Object.getOwnPropertyNames(properties).forEach(function(name) {
+ descriptor[name] = Object.getOwnPropertyDescriptor(properties, name);
+ });
+ });
+ return Object.defineProperties(source, descriptor);
+}
+exports.merge = merge;
+
+/**
+ * Returns an object that inherits from the first argument and contains all the
+ * properties from all following arguments.
+ * `extend(source1, source2, source3)` is equivalent of
+ * `merge(Object.create(source1), source2, source3)`.
+ */
+function extend(source) {
+ let rest = Array.slice(arguments, 1);
+ rest.unshift(Object.create(source));
+ return merge.apply(null, rest);
+}
+exports.extend = extend;
+
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/utils/registry.js b/tools/addon-sdk-1.7/packages/api-utils/lib/utils/registry.js
new file mode 100644
index 0000000..2ce6a6e
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/utils/registry.js
@@ -0,0 +1,57 @@
+/* -*- 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/. */
+"use strict";
+
+const { EventEmitter } = require('../events');
+const unload = require('../unload');
+
+const Registry = EventEmitter.compose({
+ _registry: null,
+ _constructor: null,
+ constructor: function Registry(constructor) {
+ this._registry = [];
+ this._constructor = constructor;
+ this.on('error', this._onError = this._onError.bind(this));
+ unload.ensure(this, "_destructor");
+ },
+ _destructor: function _destructor() {
+ let _registry = this._registry.slice(0);
+ for each (let instance in _registry)
+ this._emit('remove', instance);
+ this._registry.splice(0);
+ },
+ _onError: function _onError(e) {
+ if (!this._listeners('error').length)
+ console.error(e);
+ },
+ has: function has(instance) {
+ let _registry = this._registry;
+ return (
+ (0 <= _registry.indexOf(instance)) ||
+ (instance && instance._public && 0 <= _registry.indexOf(instance._public))
+ );
+ },
+ add: function add(instance) {
+ let { _constructor, _registry } = this;
+ if (!(instance instanceof _constructor))
+ instance = new _constructor(instance);
+ if (0 > _registry.indexOf(instance)) {
+ _registry.push(instance);
+ this._emit('add', instance);
+ }
+ return instance;
+ },
+ remove: function remove(instance) {
+ let _registry = this._registry;
+ let index = _registry.indexOf(instance)
+ if (0 <= index) {
+ this._emit('remove', instance);
+ _registry.splice(index, 1);
+ }
+ }
+});
+exports.Registry = Registry;
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/utils/thumbnail.js b/tools/addon-sdk-1.7/packages/api-utils/lib/utils/thumbnail.js
new file mode 100644
index 0000000..dfd03dd
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/utils/thumbnail.js
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { Cc, Ci, Cu } = require("chrome");
+const AppShellService = Cc["@mozilla.org/appshell/appShellService;1"].
+ getService(Ci.nsIAppShellService);
+
+const NS = "http://www.w3.org/1999/xhtml";
+const COLOR = "rgb(255,255,255)";
+
+/**
+ * Creates canvas element with a thumbnail of the passed window.
+ * @param {Window} window
+ * @returns {Element}
+ */
+function getThumbnailCanvasForWindow(window) {
+ let aspectRatio = 0.5625; // 16:9
+ let thumbnail = AppShellService.hiddenDOMWindow.document
+ .createElementNS(NS, "canvas");
+ thumbnail.mozOpaque = true;
+ thumbnail.width = Math.ceil(window.screen.availWidth / 5.75);
+ thumbnail.height = Math.round(thumbnail.width * aspectRatio);
+ let ctx = thumbnail.getContext("2d");
+ let snippetWidth = window.innerWidth * .6;
+ let scale = thumbnail.width / snippetWidth;
+ ctx.scale(scale, scale);
+ ctx.drawWindow(window, window.scrollX, window.scrollY, snippetWidth,
+ snippetWidth * aspectRatio, COLOR);
+ return thumbnail;
+}
+exports.getThumbnailCanvasForWindow = getThumbnailCanvasForWindow;
+
+/**
+ * Creates Base64 encoded data URI of the thumbnail for the passed window.
+ * @param {Window} window
+ * @returns {String}
+ */
+exports.getThumbnailURIForWindow = function getThumbnailURIForWindow(window) {
+ return getThumbnailCanvasForWindow(window).toDataURL()
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/uuid.js b/tools/addon-sdk-1.7/packages/api-utils/lib/uuid.js
new file mode 100644
index 0000000..cd667ae
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/uuid.js
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { Cc, Ci, components: { ID: parseUUID } } = require('chrome');
+const { generateUUID } = Cc['@mozilla.org/uuid-generator;1'].
+ getService(Ci.nsIUUIDGenerator);
+
+// Returns `uuid`. If `id` is passed then it's parsed to `uuid` and returned
+// if not then new one is generated.
+exports.uuid = function uuid(id) id ? parseUUID(id) : generateUUID()
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/window-utils.js b/tools/addon-sdk-1.7/packages/api-utils/lib/window-utils.js
new file mode 100644
index 0000000..bd2f2d5
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/window-utils.js
@@ -0,0 +1,278 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { Cc, Ci } = require("chrome");
+const { EventEmitter } = require('./events'),
+ { Trait } = require('./traits');
+const { when } = require('./unload');
+const { getInnerId, getOuterId } = require('./window-utils');
+const errors = require("./errors");
+
+const windowWatcher = Cc["@mozilla.org/embedcomp/window-watcher;1"].
+ getService(Ci.nsIWindowWatcher);
+const appShellService = Cc["@mozilla.org/appshell/appShellService;1"].
+ getService(Ci.nsIAppShellService);
+const observers = require('api-utils/observer-service');
+
+
+const XUL = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
+
+/**
+ * An iterator for XUL windows currently in the application.
+ *
+ * @return A generator that yields XUL windows exposing the
+ * nsIDOMWindow interface.
+ */
+function windowIterator() {
+ let winEnum = windowWatcher.getWindowEnumerator();
+ while (winEnum.hasMoreElements())
+ yield winEnum.getNext().QueryInterface(Ci.nsIDOMWindow);
+};
+exports.windowIterator = windowIterator;
+
+/**
+ * An iterator for browser windows currently open in the application.
+ * @returns {Function}
+ * A generator that yields browser windows exposing the `nsIDOMWindow`
+ * interface.
+ */
+function browserWindowIterator() {
+ for each (let window in windowIterator()) {
+ if (isBrowser(window))
+ yield window;
+ }
+}
+exports.browserWindowIterator = browserWindowIterator;
+
+function WindowTracker(delegate) {
+ if (!(this instanceof WindowTracker)) {
+ return new WindowTracker(delegate);
+ }
+
+ this._delegate = delegate;
+ this._loadingWindows = [];
+
+ for (let window in windowIterator())
+ this._regWindow(window);
+ windowWatcher.registerNotification(this);
+
+ require("./unload").ensure(this);
+
+ return this;
+};
+
+WindowTracker.prototype = {
+ _regLoadingWindow: function _regLoadingWindow(window) {
+ this._loadingWindows.push(window);
+ window.addEventListener("load", this, true);
+ },
+
+ _unregLoadingWindow: function _unregLoadingWindow(window) {
+ var index = this._loadingWindows.indexOf(window);
+
+ if (index != -1) {
+ this._loadingWindows.splice(index, 1);
+ window.removeEventListener("load", this, true);
+ }
+ },
+
+ _regWindow: function _regWindow(window) {
+ if (window.document.readyState == "complete") {
+ this._unregLoadingWindow(window);
+ this._delegate.onTrack(window);
+ } else
+ this._regLoadingWindow(window);
+ },
+
+ _unregWindow: function _unregWindow(window) {
+ if (window.document.readyState == "complete") {
+ if (this._delegate.onUntrack)
+ this._delegate.onUntrack(window);
+ } else {
+ this._unregLoadingWindow(window);
+ }
+ },
+
+ unload: function unload() {
+ windowWatcher.unregisterNotification(this);
+ for (let window in windowIterator())
+ this._unregWindow(window);
+ },
+
+ handleEvent: errors.catchAndLog(function handleEvent(event) {
+ if (event.type == "load" && event.target) {
+ var window = event.target.defaultView;
+ if (window)
+ this._regWindow(window);
+ }
+ }),
+
+ observe: errors.catchAndLog(function observe(subject, topic, data) {
+ var window = subject.QueryInterface(Ci.nsIDOMWindow);
+ if (topic == "domwindowopened")
+ this._regWindow(window);
+ else
+ this._unregWindow(window);
+ })
+};
+exports.WindowTracker = WindowTracker;
+
+const WindowTrackerTrait = Trait.compose({
+ _onTrack: Trait.required,
+ _onUntrack: Trait.required,
+ constructor: function WindowTrackerTrait() {
+ WindowTracker({
+ onTrack: this._onTrack.bind(this),
+ onUntrack: this._onUntrack.bind(this)
+ });
+ }
+});
+exports.WindowTrackerTrait = WindowTrackerTrait;
+
+var gDocsToClose = [];
+
+function onDocUnload(event) {
+ var index = gDocsToClose.indexOf(event.target);
+ if (index == -1)
+ throw new Error("internal error: unloading document not found");
+ var document = gDocsToClose.splice(index, 1)[0];
+ // Just in case, let's remove the event listener too.
+ document.defaultView.removeEventListener("unload", onDocUnload, false);
+}
+
+onDocUnload = require("./errors").catchAndLog(onDocUnload);
+
+exports.closeOnUnload = function closeOnUnload(window) {
+ window.addEventListener("unload", onDocUnload, false);
+ gDocsToClose.push(window.document);
+};
+
+Object.defineProperties(exports, {
+ activeWindow: {
+ enumerable: true,
+ get: function() {
+ return Cc["@mozilla.org/appshell/window-mediator;1"]
+ .getService(Ci.nsIWindowMediator)
+ .getMostRecentWindow(null);
+ },
+ set: function(window) {
+ try { window.focus(); } catch (e) { }
+ }
+ },
+ activeBrowserWindow: {
+ enumerable: true,
+ get: function() {
+ return Cc["@mozilla.org/appshell/window-mediator;1"]
+ .getService(Ci.nsIWindowMediator)
+ .getMostRecentWindow("navigator:browser");
+ }
+ }
+});
+
+
+/**
+ * Returns the ID of the window's current inner window.
+ */
+exports.getInnerId = function(window) {
+ console.warn('require("window-utils").getInnerId is deprecated, ' +
+ 'please use require("window/utils").getInnerId instead');
+ return getInnerId(window);
+};
+
+exports.getOuterId = function(window) {
+ console.warn('require("window-utils").getOuterId is deprecated, ' +
+ 'please use require("window/utils").getOuterId instead');
+ return getOuterId(window);
+};
+
+function isBrowser(window) {
+ return window.document.documentElement.getAttribute("windowtype") ===
+ "navigator:browser";
+};
+exports.isBrowser = isBrowser;
+
+exports.hiddenWindow = appShellService.hiddenDOMWindow;
+
+function createHiddenXULFrame() {
+ return function promise(deliver) {
+ let window = appShellService.hiddenDOMWindow;
+
+ // Ensuring waiting for hidden window end of loading
+ // (The hidden window is still loading on windows/thunderbird)
+ if (window.document.readyState != "complete") {
+ window.addEventListener("load", function onload() {
+ window.removeEventListener("load", onload, false);
+ // We recurse with same arguments, when the window is ready
+ promise(deliver);
+ }, false);
+ return;
+ }
+
+ let document = window.document;
+ let isXMLDoc = (document.contentType == "application/xhtml+xml" ||
+ document.contentType == "application/vnd.mozilla.xul+xml")
+
+ if (isXMLDoc) {
+ deliver(window)
+ }
+ else {
+ let frame = document.createElement('iframe');
+ // This is ugly but we need window for XUL document in order to create
+ // browser elements.
+
+ // See bug 725323: hiddenWindow URL is different on each mozilla product
+ let prefs = Cc["@mozilla.org/preferences-service;1"].
+ getService(Ci.nsIPrefBranch);
+ let hiddenWindowURL = prefs.getCharPref("browser.hiddenWindowChromeURL", "");
+
+ frame.src = hiddenWindowURL;
+ frame.setAttribute('src', hiddenWindowURL);
+ frame.addEventListener('DOMContentLoaded', function onLoad(event) {
+ frame.removeEventListener('DOMContentLoaded', onLoad, false);
+ deliver(frame.contentWindow);
+ }, false);
+ document.documentElement.appendChild(frame);
+ }
+ }
+};
+exports.createHiddenXULFrame = createHiddenXULFrame;
+
+function createRemoteBrowser(remote) {
+ return function promise(deliver) {
+ createHiddenXULFrame()(function(hiddenWindow) {
+ let document = hiddenWindow.document;
+ let browser = document.createElementNS(XUL, "browser");
+ // Remote="true" enable everything here:
+ // http://mxr.mozilla.org/mozilla-central/source/content/base/src/nsFrameLoader.cpp#1347
+ if (remote !== false)
+ browser.setAttribute("remote","true");
+ // Type="content" is mandatory to enable stuff here:
+ // http://mxr.mozilla.org/mozilla-central/source/content/base/src/nsFrameLoader.cpp#1776
+ browser.setAttribute("type","content");
+ // We remove XBL binding to avoid execution of code that is not going to work
+ // because browser has no docShell attribute in remote mode (for example)
+ browser.setAttribute("style","-moz-binding: none;");
+ // Flex it in order to be visible (optional, for debug purpose)
+ browser.setAttribute("flex", "1");
+ document.documentElement.appendChild(browser);
+
+ // Bug 724433: do not leak this <browser> DOM node
+ when(function () {
+ document.documentElement.removeChild(browser);
+ });
+
+ // Return browser
+ deliver(browser);
+ });
+ };
+};
+exports.createRemoteBrowser = createRemoteBrowser;
+
+require("./unload").when(
+ function() {
+ gDocsToClose.slice().forEach(
+ function(doc) { doc.defaultView.close(); });
+ });
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/window/utils.js b/tools/addon-sdk-1.7/packages/api-utils/lib/window/utils.js
new file mode 100644
index 0000000..b4a8493
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/window/utils.js
@@ -0,0 +1,110 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+const { Cc, Ci } = require('chrome');
+
+const windowWatcher = Cc['@mozilla.org/embedcomp/window-watcher;1'].
+ getService(Ci.nsIWindowWatcher);
+const appShellService = Cc['@mozilla.org/appshell/appShellService;1'].
+ getService(Ci.nsIAppShellService);
+const observers = require('api-utils/observer-service');
+
+/**
+ * Returns the ID of the window's current inner window.
+ */
+function getInnerId(window) {
+ return window.QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
+};
+exports.getInnerId = getInnerId;
+
+/**
+ * Returns the ID of the window's outer window.
+ */
+function getOuterId(window) {
+ return window.QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
+};
+exports.getOuterId = getOuterId;
+
+/**
+ * Returns `nsIXULWindow` for the given `nsIDOMWindow`.
+ */
+function getXULWindow(window) {
+ return window.QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIWebNavigation).
+ QueryInterface(Ci.nsIDocShellTreeItem).
+ treeOwner.QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIXULWindow);
+};
+exports.getXULWindow = getXULWindow;
+
+/**
+ * Returns `nsIBaseWindow` for the given `nsIDOMWindow`.
+ */
+function getBaseWindow(window) {
+ return window.QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIWebNavigation).
+ QueryInterface(Ci.nsIDocShell).
+ QueryInterface(Ci.nsIDocShellTreeItem).
+ treeOwner.
+ QueryInterface(Ci.nsIBaseWindow);
+}
+exports.getBaseWindow = getBaseWindow;
+
+/**
+ * Removes given window from the application's window registry. Unless
+ * `options.close` is `false` window is automatically closed on application
+ * quit.
+ * @params {nsIDOMWindow} window
+ * @params {Boolean} options.close
+ */
+function backgroundify(window, options) {
+ let base = getBaseWindow(window);
+ base.visibility = false;
+ base.enabled = false;
+ appShellService.unregisterTopLevelWindow(getXULWindow(window));
+ if (!options || options.close !== false)
+ observers.add('quit-application-granted', window.close.bind(window));
+
+ return window;
+}
+exports.backgroundify = backgroundify;
+
+/**
+ * Takes hash of options and serializes it to a features string that
+ * can be used passed to `window.open`. For more details on features string see:
+ * https://developer.mozilla.org/en/DOM/window.open#Position_and_size_features
+ */
+function serializeFeatures(options) {
+ return Object.keys(options).reduce(function(result, name) {
+ let value = options[name];
+ return result + ',' + name + '=' +
+ (value === true ? 'yes' : value === false ? 'no' : value);
+ }, '').substr(1);
+}
+
+/**
+ * Opens a top level window and returns it's `nsIDOMWindow` representation.
+ * @params {String} uri
+ * URI of the document to be loaded into window.
+ * @params {nsIDOMWindow} options.parent
+ * Used as parent for the created window.
+ * @params {String} options.name
+ * Optional name that is assigned to the window.
+ * @params {Object} options.features
+ * Map of key, values like: `{ width: 10, height: 15, chrome: true }`.
+ */
+function open(uri, options) {
+ options = options || {};
+ return windowWatcher.
+ openWindow(options.parent || null,
+ uri,
+ options.name || null,
+ serializeFeatures(options.features || {}),
+ null);
+}
+exports.open = open;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/windows/dom.js b/tools/addon-sdk-1.7/packages/api-utils/lib/windows/dom.js
new file mode 100644
index 0000000..1ff966a
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/windows/dom.js
@@ -0,0 +1,27 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+const { Trait } = require('../traits');
+
+const WindowDom = Trait.compose({
+ _window: Trait.required,
+ get title() {
+ let window = this._window;
+ return window && window.document ? window.document.title : null
+ },
+ close: function close() {
+ let window = this._window;
+ if (window) window.close();
+ return this._public;
+ },
+ activate: function activate() {
+ let window = this._window;
+ if (window) window.focus();
+ return this._public;
+ }
+});
+exports.WindowDom = WindowDom;
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/windows/loader.js b/tools/addon-sdk-1.7/packages/api-utils/lib/windows/loader.js
new file mode 100644
index 0000000..b596e73
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/windows/loader.js
@@ -0,0 +1,120 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const { Cc, Ci } = require('chrome'),
+ { setTimeout } = require("../timer"),
+ { Trait } = require('../traits'),
+
+ WM = Cc['@mozilla.org/appshell/window-mediator;1'].
+ getService(Ci.nsIWindowMediator),
+
+ URI_BROWSER = 'chrome://browser/content/browser.xul',
+ NAME = '_blank',
+ FEATURES = 'chrome,all,dialog=no',
+ PARAMS = [ URI_BROWSER, NAME, FEATURES ],
+ ON_LOAD = 'load',
+ ON_UNLOAD = 'unload',
+ STATE_LOADED = 'complete',
+ BROWSER = 'navigator:browser';
+
+/**
+ * Trait provides private `_window` property and requires `_onLoad` property
+ * that will be called when `_window` is loaded. If `_window` property value
+ * is changed with already loaded window `_onLoad` still will be called.
+ */
+const WindowLoader = Trait.compose({
+ /**
+ * Internal listener that is called when window is loaded.
+ * Please keep in mind that this trait will not handle exceptions that may
+ * be thrown by this method so method itself should take care of
+ * handling them.
+ * @param {nsIWindow} window
+ */
+ _onLoad: Trait.required,
+ _tabOptions: Trait.required,
+ /**
+ * Internal listener that is called when `_window`'s DOM 'unload' event
+ * is dispatched. Please note that this trait will not handle exceptions that
+ * may be thrown by this method so method itself should take care of
+ * handling them.
+ */
+ _onUnload: Trait.required,
+ _load: function _load() {
+ if (this.__window) return;
+ let params = PARAMS.slice()
+ params.push(this._tabOptions.map(function(options) options.url).join("|"))
+ let browser = WM.getMostRecentWindow(BROWSER);
+ this._window = browser.openDialog.apply(browser, params);
+ },
+ /**
+ * Private window who's load event is being tracked. Once window is loaded
+ * `_onLoad` is called.
+ * @type {nsIWindow}
+ */
+ get _window() this.__window,
+ set _window(window) {
+ let _window = this.__window;
+ if (!window) window = null;
+ if (window !== _window) {
+ if (_window) {
+ _window.removeEventListener(ON_UNLOAD, this.__unloadListener, false);
+ _window.removeEventListener(ON_LOAD, this.__loadListener, false);
+ }
+ if (window) {
+ window.addEventListener(
+ ON_UNLOAD,
+ this.__unloadListener ||
+ (this.__unloadListener = this._unloadListener.bind(this))
+ ,
+ false
+ );
+ this.__window = window;
+ // If window is not loaded yet setting up a listener.
+ if (STATE_LOADED != window.document.readyState) {
+ window.addEventListener(
+ ON_LOAD,
+ this.__loadListener ||
+ (this.__loadListener = this._loadListener.bind(this))
+ ,
+ false
+ );
+ }
+ else { // If window is loaded calling listener next turn of event loop.
+ this._onLoad(window)
+ }
+ }
+ }
+ },
+ __window: null,
+ /**
+ * Internal method used for listening 'load' event on the `_window`.
+ * Method takes care of removing itself from 'load' event listeners once
+ * event is being handled.
+ */
+ _loadListener: function _loadListener(event) {
+ let window = this._window;
+ if (!event.target || event.target.defaultView != window) return;
+ window.removeEventListener(ON_LOAD, this.__loadListener, false);
+ this._onLoad(window);
+ },
+ __loadListener: null,
+ /**
+ * Internal method used for listening 'unload' event on the `_window`.
+ * Method takes care of removing itself from 'unload' event listeners once
+ * event is being handled.
+ */
+ _unloadListener: function _unloadListener(event) {
+ let window = this._window;
+ if (!event.target
+ || event.target.defaultView != window
+ || STATE_LOADED != window.document.readyState
+ ) return;
+ window.removeEventListener(ON_UNLOAD, this.__unloadListener, false);
+ this._onUnload(window);
+ },
+ __unloadListener: null
+});
+exports.WindowLoader = WindowLoader;
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/windows/observer.js b/tools/addon-sdk-1.7/packages/api-utils/lib/windows/observer.js
new file mode 100644
index 0000000..45191b7
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/windows/observer.js
@@ -0,0 +1,53 @@
+/* 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/. */
+
+"use strict";
+
+const { EventEmitterTrait: EventEmitter } = require("../events");
+const { WindowTracker, windowIterator } = require("../window-utils");
+const { DOMEventAssembler } = require("../events/assembler");
+const { Trait } = require("../light-traits");
+
+// Event emitter objects used to register listeners and emit events on them
+// when they occur.
+const observer = Trait.compose(DOMEventAssembler, EventEmitter).create({
+ /**
+ * Method is implemented by `EventEmitter` and is used just for emitting
+ * events on registered listeners.
+ */
+ _emit: Trait.required,
+ /**
+ * Events that are supported and emitted by the module.
+ */
+ supportedEventsTypes: [ "activate", "deactivate" ],
+ /**
+ * Function handles all the supported events on all the windows that are
+ * observed. Method is used to proxy events to the listeners registered on
+ * this event emitter.
+ * @param {Event} event
+ * Keyboard event being emitted.
+ */
+ handleEvent: function handleEvent(event) {
+ this._emit(event.type, event.target, event);
+ }
+});
+
+// Using `WindowTracker` to track window events.
+WindowTracker({
+ onTrack: function onTrack(chromeWindow) {
+ observer._emit("open", chromeWindow);
+ observer.observe(chromeWindow);
+ },
+ onUntrack: function onUntrack(chromeWindow) {
+ observer._emit("close", chromeWindow);
+ observer.ignore(chromeWindow);
+ }
+});
+
+// Making observer aware of already opened windows.
+for each (let window in windowIterator())
+ observer.observe(window);
+
+exports.observer = observer;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/windows/tabs.js b/tools/addon-sdk-1.7/packages/api-utils/lib/windows/tabs.js
new file mode 100644
index 0000000..231a22e
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/windows/tabs.js
@@ -0,0 +1,178 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const { Trait } = require("../traits");
+const { List } = require("../list");
+const { Tab, Options } = require("../tabs/tab");
+const { EventEmitter } = require("../events");
+const { EVENTS } = require("../tabs/events");
+const { getOwnerWindow, getActiveTab, getTabs,
+ openTab, activateTab } = require("../tabs/utils");
+const { observer: tabsObserver } = require("../tabs/observer");
+
+const TAB_BROWSER = "tabbrowser";
+
+/**
+ * This is a trait that is used in composition of window wrapper. Trait tracks
+ * tab related events of the wrapped window in order to keep track of open
+ * tabs and maintain their wrappers. Every new tab gets wrapped and jetpack
+ * type event is emitted.
+ */
+const WindowTabTracker = Trait.compose({
+ /**
+ * Chrome window whose tabs are tracked.
+ */
+ _window: Trait.required,
+ /**
+ * Function used to emit events.
+ */
+ _emit: EventEmitter.required,
+ _tabOptions: Trait.required,
+ /**
+ * Function to add event listeners.
+ */
+ on: EventEmitter.required,
+ removeListener: EventEmitter.required,
+ /**
+ * Initializes tab tracker for a browser window.
+ */
+ _initWindowTabTracker: function _initWindowTabTracker() {
+ // Ugly hack that we have to remove at some point (see Bug 658059). At this
+ // point it is necessary to invoke lazy `tabs` getter on the windows object
+ // which creates a `TabList` instance.
+ this.tabs;
+ // Binding all methods used as event listeners to the instance.
+ this._onTabReady = this._emitEvent.bind(this, "ready");
+ this._onTabOpen = this._onTabEvent.bind(this, "open");
+ this._onTabClose = this._onTabEvent.bind(this, "close");
+ this._onTabActivate = this._onTabEvent.bind(this, "activate");
+ this._onTabDeactivate = this._onTabEvent.bind(this, "deactivate");
+ this._onTabPinned = this._onTabEvent.bind(this, "pinned");
+ this._onTabUnpinned = this._onTabEvent.bind(this, "unpinned");
+
+ for each (let tab in getTabs(this._window)) {
+ // We emulate "open" events for all open tabs since gecko does not emits
+ // them on the tabs that new windows are open with. Also this is
+ // necessary to synchronize tabs lists with an actual state.
+ this._onTabOpen(tab);
+ }
+ // We also emulate "activate" event so that it's picked up by a tab list.
+ this._onTabActivate(getActiveTab(this._window));
+
+ // Setting up event listeners
+ tabsObserver.on("open", this._onTabOpen);
+ tabsObserver.on("close", this._onTabClose);
+ tabsObserver.on("activate", this._onTabActivate);
+ tabsObserver.on("deactivate", this._onTabDeactivate);
+ tabsObserver.on("pinned", this._onTabPinned);
+ tabsObserver.on("unpinned", this._onTabUnpinned);
+ },
+ _destroyWindowTabTracker: function _destroyWindowTabTracker() {
+ // We emulate close events on all tabs, since gecko does not emits such
+ // events by itself.
+ for each (let tab in this.tabs)
+ this._emitEvent("close", tab);
+
+ this._tabs._clear();
+
+ tabsObserver.removeListener("open", this._onTabOpen);
+ tabsObserver.removeListener("close", this._onTabClose);
+ tabsObserver.removeListener("activate", this._onTabActivate);
+ tabsObserver.removeListener("deactivate", this._onTabDeactivate);
+ },
+ _onTabEvent: function _onTabEvent(type, tab) {
+ if (this._window === getOwnerWindow(tab)) {
+ let options = this._tabOptions.shift() || {};
+ options.tab = tab;
+ options.window = this._public;
+ // creating tab wrapper and adding listener to "ready" events.
+ let wrappedTab = Tab(options);
+
+ // Setting up an event listener for ready events.
+ if (type === "open")
+ wrappedTab.on("ready", this._onTabReady);
+
+ this._emitEvent(type, wrappedTab);
+ }
+ },
+ _emitEvent: function _emitEvent(type, tab) {
+ // Notifies combined tab list that tab was added / removed.
+ tabs._emit(type, tab);
+ // Notifies contained tab list that window was added / removed.
+ this._tabs._emit(type, tab);
+ }
+});
+exports.WindowTabTracker = WindowTabTracker;
+
+/**
+ * This trait is used to create live representation of open tab lists. Each
+ * window wrapper's tab list is represented by an object created from this
+ * trait. It is also used to represent list of all the open windows. Trait is
+ * composed out of `EventEmitter` in order to emit 'TabOpen', 'TabClose' events.
+ * **Please note** that objects created by this trait can't be exposed outside
+ * instead you should expose it's `_public` property, see comments in
+ * constructor for details.
+ */
+const TabList = List.resolve({ constructor: "_init" }).compose(
+ // This is ugly, but necessary. Will be removed by #596248
+ EventEmitter.resolve({ toString: null }),
+ Trait.compose({
+ on: Trait.required,
+ _emit: Trait.required,
+ constructor: function TabList(options) {
+ this._window = options.window;
+ // Add new items to the list
+ this.on(EVENTS.open.name, this._add.bind(this));
+ // Remove closed items from the list
+ this.on(EVENTS.close.name, this._remove.bind(this));
+
+ // Set value whenever new tab becomes active.
+ this.on("activate", function onTabActivate(tab) {
+ this._activeTab = tab;
+ }.bind(this));
+ // Initialize list.
+ this._init();
+ // This list is not going to emit any events, object holding this list
+ // will do it instead, to make that possible we return a private API.
+ return this;
+ },
+ get activeTab() this._activeTab,
+ _activeTab: null,
+
+ open: function open(options) {
+ options = Options(options);
+ this._window._tabOptions.push(options);
+ let tab = openTab(this._window._window, options.url);
+ if (!options.inBackground)
+ activateTab(tab);
+ }
+ // This is ugly, but necessary. Will be removed by #596248
+ }).resolve({ toString: null })
+);
+
+/**
+ * Combined list of all open tabs on all the windows.
+ * type {TabList}
+ */
+var tabs = TabList({ window: null });
+exports.tabs = tabs._public;
+
+/**
+ * Trait is a part of composition that represents window wrapper. This trait is
+ * composed out of `WindowTabTracker` that allows it to keep track of open tabs
+ * on the window being wrapped.
+ */
+const WindowTabs = Trait.compose(
+ WindowTabTracker,
+ Trait.compose({
+ _window: Trait.required,
+ /**
+ * List of tabs
+ */
+ get tabs() (this._tabs || (this._tabs = TabList({ window: this })))._public,
+ _tabs: null,
+ })
+);
+exports.WindowTabs = WindowTabs;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/xhr.js b/tools/addon-sdk-1.7/packages/api-utils/lib/xhr.js
new file mode 100644
index 0000000..b7808dc
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/xhr.js
@@ -0,0 +1,150 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {Cc,Ci} = require("chrome");
+const memory = require('api-utils/memory');
+
+// ## Implementation Notes ##
+//
+// Making `XMLHttpRequest` objects available to Jetpack code involves a
+// few key principles universal to all low-level module implementations:
+//
+// * **Unloadability**. A Jetpack-based extension using this module can be
+// asked to unload itself at any time, e.g. because the user decides to
+// uninstall or disable the extension. This means we need to keep track of
+// all in-progress reqests and abort them on unload.
+//
+// * **Developer-Ergonomic Tracebacks**. Whenever an exception is raised
+// by a Jetpack-based extension, we want it to be logged in a
+// place that is specific to that extension--so that a developer
+// can distinguish it from an error on a web page or in another
+// extension, for instance. We also want it to be logged with a
+// full stack traceback, which the Mozilla platform doesn't usually
+// do.
+//
+// Because of this, we don't actually want to give the Mozilla
+// platform's "real" XHR implementation to clients, but instead provide
+// a simple wrapper that trivially delegates to the implementation in
+// all cases except where callbacks are involved: whenever Mozilla
+// platform code calls into the extension, such as during the XHR's
+// `onreadystatechange` callback, we want to wrap the client's callback
+// in a try-catch clause that traps any exceptions raised by the
+// callback and logs them via console.exception() instead of allowing
+// them to propagate back into Mozilla platform code.
+
+// This is a private list of all active requests, so we know what to
+// abort if we're asked to unload.
+var requests = [];
+
+// Events on XHRs that we should listen for, so we know when to remove
+// a request from our private list.
+const TERMINATE_EVENTS = ["load", "error", "abort"];
+
+// Read-only properties of XMLHttpRequest objects that we want to
+// directly delegate to.
+const READ_ONLY_PROPS = ["readyState", "responseText", "responseXML",
+ "status", "statusText"];
+
+// Methods of XMLHttpRequest that we want to directly delegate to.
+const DELEGATED_METHODS = ["abort", "getAllResponseHeaders",
+ "getResponseHeader", "overrideMimeType",
+ "send", "sendAsBinary", "setRequestHeader",
+ "open"];
+
+var getRequestCount = exports.getRequestCount = function getRequestCount() {
+ return requests.length;
+};
+
+var XMLHttpRequest = exports.XMLHttpRequest = function XMLHttpRequest() {
+ var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
+ .createInstance(Ci.nsIXMLHttpRequest);
+ // For the sake of simplicity, don't tie this request to any UI.
+ req.mozBackgroundRequest = true;
+
+ memory.track(req, "XMLHttpRequest");
+
+ this._req = req;
+ this._orsc = null;
+
+ requests.push(this);
+
+ var self = this;
+
+ this._boundCleanup = function _boundCleanup() {
+ self._cleanup();
+ };
+
+ TERMINATE_EVENTS.forEach(
+ function(name) {
+ self._req.addEventListener(name, self._boundCleanup, false);
+ });
+};
+
+XMLHttpRequest.prototype = {
+ _cleanup: function _cleanup() {
+ this.onreadystatechange = null;
+ var index = requests.indexOf(this);
+ if (index != -1) {
+ var self = this;
+ TERMINATE_EVENTS.forEach(
+ function(name) {
+ self._req.removeEventListener(name, self._boundCleanup, false);
+ });
+ requests.splice(index, 1);
+ }
+ },
+ _unload: function _unload() {
+ this._req.abort();
+ this._cleanup();
+ },
+ addEventListener: function addEventListener() {
+ throw new Error("not implemented");
+ },
+ removeEventListener: function removeEventListener() {
+ throw new Error("not implemented");
+ },
+ set upload(newValue) {
+ throw new Error("not implemented");
+ },
+ get onreadystatechange() {
+ return this._orsc;
+ },
+ set onreadystatechange(cb) {
+ this._orsc = cb;
+ if (cb) {
+ var self = this;
+ this._req.onreadystatechange = function() {
+ try {
+ self._orsc.apply(self, arguments);
+ } catch (e) {
+ console.exception(e);
+ }
+ };
+ } else
+ this._req.onreadystatechange = null;
+ }
+};
+
+READ_ONLY_PROPS.forEach(
+ function(name) {
+ XMLHttpRequest.prototype.__defineGetter__(
+ name,
+ function() {
+ return this._req[name];
+ });
+ });
+
+DELEGATED_METHODS.forEach(
+ function(name) {
+ XMLHttpRequest.prototype[name] = function() {
+ return this._req[name].apply(this._req, arguments);
+ };
+ });
+
+require("./unload").when(
+ function() {
+ requests.slice().forEach(function(request) { request._unload(); });
+ });
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/xpcom.js b/tools/addon-sdk-1.7/packages/api-utils/lib/xpcom.js
new file mode 100644
index 0000000..efdf873
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/xpcom.js
@@ -0,0 +1,207 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { Cc, Ci, Cr, Cm, components: { classesByID } } = require('chrome');
+const { registerFactory, unregisterFactory, isCIDRegistered } =
+ Cm.QueryInterface(Ci.nsIComponentRegistrar);
+
+const { when: unload } = require('./unload');
+const { Base } = require('./base');
+const { uuid } = require('./uuid');
+
+// This is a base prototype, that provides bare bones of XPCOM. JS based
+// components can be easily implement by extending it.
+const Unknown = Base.extend({
+ // Method `extend` is overridden so that resulting object will contain
+ // `interfaces` array property, containing elements from ancestor and all
+ // provided sources.
+ extend: function extend() {
+ let args = Array.slice(arguments);
+ return Base.extend.apply(this, args.concat([{
+ interfaces: args.reduce(function(interfaces, source) {
+ // If given source has `interfaces` property concatenate it's elements
+ // them resulting `interfaces` array, otherwise return resulting
+ // `interfaces` array.
+ return 'interfaces' in source ? source.interfaces.concat(interfaces)
+ : interfaces;
+ }, this.interfaces)
+ }]));
+ },
+ /**
+ * The `QueryInterface` method provides runtime type discovery used by XPCOM.
+ * This method return quired instance of `this` if given `iid` is listed in
+ * the `interfaces` property.
+ */
+ QueryInterface: function QueryInterface(iid) {
+ // For some reason there are cases when `iid` is `null`. In such cases we
+ // just return `this`. Otherwise we verify that component implements given
+ // `iid` interface.
+ if (iid && !this.interfaces.some(function(id) iid.equals(Ci[id])))
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ return this;
+ },
+ /**
+ * Array of `XPCOM` interfaces (as strings) implemented by this component. All
+ * components implement `nsISupports` by default which is default value here.
+ * Provide array of interfaces implemented by an object when extending, to
+ * append them to this list (Please note that there is no need to repeat
+ * interfaces implemented by super as they will be added automatically).
+ */
+ interfaces: [ 'nsISupports' ]
+});
+exports.Unknown = Unknown;
+
+// Base exemplar for creating instances implementing `nsIFactory` interface,
+// that maybe registered into runtime via `register` function. Instances of
+// this factory create instances of enclosed component on `createInstance`.
+const Factory = Unknown.extend({
+ interfaces: [ 'nsIFactory' ],
+ /**
+ * All the descendants will get auto generated `id` (also known as `classID`
+ * in XPCOM world) unless one is manually provided.
+ */
+ get id() { throw Error('Factory must implement `id` property') },
+ /**
+ * XPCOM `contractID` may optionally be provided to associate this factory
+ * with it. `contract` is a unique string that has a following format:
+ * '@vendor.com/unique/id;1'.
+ */
+ contract: null,
+ /**
+ * Class description that is being registered. This value is intended as a
+ * human-readable description for the given class and does not needs to be
+ * globally unique.
+ */
+ description: 'Jetpack generated factory',
+ /**
+ * This method is required by `nsIFactory` interfaces, but as in most
+ * implementations it does nothing interesting.
+ */
+ lockFactory: function lockFactory(lock) undefined,
+ /**
+ * If property is `true` XPCOM service / factory will be registered
+ * automatically on creation.
+ */
+ register: true,
+ /**
+ * If property is `true` XPCOM factory will be unregistered prior to add-on
+ * unload.
+ */
+ unregister: true,
+ /**
+ * Method is called on `Service.new(options)` passing given `options` to
+ * it. Options is expected to have `component` property holding XPCOM
+ * component implementation typically decedent of `Unknown` or any custom
+ * implementation with a `new` method and optional `register`, `unregister`
+ * flags. Unless `register` is `false` Service / Factory will be
+ * automatically registered. Unless `unregister` is `false` component will
+ * be automatically unregistered on add-on unload.
+ */
+ initialize: function initialize(options) {
+ options = options || {}
+ this.merge(options, { id: 'id' in options && options.id || uuid() });
+
+ // If service / factory has auto registration enabled then register.
+ if (this.register)
+ register(this);
+ },
+ /**
+ * Creates an instance of the class associated with this factory.
+ */
+ createInstance: function createInstance(outer, iid) {
+ try {
+ if (outer)
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+ return this.create().QueryInterface(iid);
+ }
+ catch (error) {
+ throw error instanceof Ci.nsIException ? error : Cr.NS_ERROR_FAILURE;
+ }
+ },
+ create: function create() this.component.new()
+});
+exports.Factory = Factory;
+
+// Exemplar for creating services that implement `nsIFactory` interface, that
+// can be registered into runtime via call to `register`. This services return
+// enclosed `component` on `getService`.
+const Service = Factory.extend({
+ description: 'Jetpack generated service',
+ /**
+ * Creates an instance of the class associated with this factory.
+ */
+ create: function create() this.component
+});
+exports.Service = Service;
+
+function isRegistered({ id }) isCIDRegistered(id)
+exports.isRegistered = isRegistered;
+
+/**
+ * Registers given `component` object to be used to instantiate a particular
+ * class identified by `component.id`, and creates an association of class
+ * name and `component.contract` with the class.
+ */
+function register(factory) {
+ registerFactory(factory.id, factory.description, factory.contract, factory);
+
+ if (factory.unregister)
+ unload(unregister.bind(null, factory));
+}
+exports.register = register;
+
+/**
+ * Unregister a factory associated with a particular class identified by
+ * `factory.classID`.
+ */
+function unregister(factory) {
+ if (isRegistered(factory))
+ unregisterFactory(factory.id, factory);
+}
+exports.unregister = unregister;
+
+function autoRegister(path) {
+ // TODO: This assumes that the url points to a directory
+ // that contains subdirectories corresponding to OS/ABI and then
+ // further subdirectories corresponding to Gecko platform version.
+ // we should probably either behave intelligently here or allow
+ // the caller to pass-in more options if e.g. there aren't
+ // Gecko-specific binaries for a component (which will be the case
+ // if only frozen interfaces are used).
+
+ var runtime = require("./runtime");
+ var osDirName = runtime.OS + "_" + runtime.XPCOMABI;
+ var platformVersion = require("./xul-app").platformVersion.substring(0, 5);
+
+ var file = Cc['@mozilla.org/file/local;1']
+ .createInstance(Ci.nsILocalFile);
+ file.initWithPath(path);
+ file.append(osDirName);
+ file.append(platformVersion);
+
+ if (!(file.exists() && file.isDirectory()))
+ throw new Error("component not available for OS/ABI " +
+ osDirName + " and platform " + platformVersion);
+
+ Cm.QueryInterface(Ci.nsIComponentRegistrar);
+ Cm.autoRegister(file);
+}
+exports.autoRegister = autoRegister;
+
+/**
+ * Returns registered factory that has a given `id` or `null` if not found.
+ */
+function factoryByID(id) classesByID[id] || null
+exports.factoryByID = factoryByID;
+
+/**
+ * Returns factory registered with a given `contract` or `null` if not found.
+ * In contrast to `Cc[contract]` that does ignores new factory registration
+ * with a given `contract` this will return a factory currently associated
+ * with a `contract`.
+ */
+function factoryByContract(contract) factoryByID(Cm.contractIDToCID(contract))
+exports.factoryByContract = factoryByContract;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/lib/xul-app.js b/tools/addon-sdk-1.7/packages/api-utils/lib/xul-app.js
new file mode 100644
index 0000000..9272d88
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/lib/xul-app.js
@@ -0,0 +1,63 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {Cc, Ci} = require("chrome");
+
+var appInfo = Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULAppInfo);
+
+var ID = exports.ID = appInfo.ID;
+var name = exports.name = appInfo.name;
+var version = exports.version = appInfo.version;
+var platformVersion = exports.platformVersion = appInfo.platformVersion;
+
+// The following mapping of application names to GUIDs was taken from:
+//
+// https://addons.mozilla.org/en-US/firefox/pages/appversions
+//
+// Using the GUID instead of the app's name is preferable because sometimes
+// re-branded versions of a product have different names: for instance,
+// Firefox, Minefield, Iceweasel, and Shiretoko all have the same
+// GUID.
+// This mapping is duplicated in `app-extensions/bootstrap.js`. They should keep
+// in sync, so if you change one, change the other too!
+
+var ids = exports.ids = {
+ Firefox: "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
+ Mozilla: "{86c18b42-e466-45a9-ae7a-9b95ba6f5640}",
+ Sunbird: "{718e30fb-e89b-41dd-9da7-e25a45638b28}",
+ SeaMonkey: "{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}",
+ Fennec: "{aa3c5121-dab2-40e2-81ca-7ea25febc110}",
+ Thunderbird: "{3550f703-e582-4d05-9a08-453d09bdfdc6}"
+};
+
+var is = exports.is = function is(name) {
+ if (!(name in ids))
+ throw new Error("Unkown Mozilla Application: " + name);
+ return ID == ids[name];
+};
+
+var isOneOf = exports.isOneOf = function isOneOf(names) {
+ for (var i = 0; i < names.length; i++)
+ if (is(names[i]))
+ return true;
+ return false;
+};
+
+/**
+ * Use this to check whether the given version (e.g. xulApp.platformVersion)
+ * is in the given range. Versions must be in version comparator-compatible
+ * format. See MDC for details:
+ * https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIVersionComparator
+ */
+var versionInRange = exports.versionInRange =
+function versionInRange(version, lowInclusive, highExclusive) {
+ var vc = Cc["@mozilla.org/xpcom/version-comparator;1"]
+ .getService(Ci.nsIVersionComparator);
+ return (vc.compare(version, lowInclusive) >= 0) &&
+ (vc.compare(version, highExclusive) < 0);
+}
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/package.json b/tools/addon-sdk-1.7/packages/api-utils/package.json
new file mode 100644
index 0000000..1e777fe
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "api-utils",
+ "description": "Foundational infrastructure and utilities.",
+ "keywords": ["javascript", "engine", "platform", "xulrunner",
+ "jetpack-low-level"],
+ "author": "Atul Varma (http://toolness.com/) <atul@mozilla.com>",
+ "contributors": [
+ "Myk Melez (http://melez.com/) <myk@mozilla.org>",
+ "Daniel Aquino <mr.danielaquino@gmail.com>"
+ ],
+ "license": "MPL 2.0",
+ "dependencies": ["addon-kit"],
+ "loader": "lib/cuddlefish.js"
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/commonjs-test-adapter/asserts.js b/tools/addon-sdk-1.7/packages/api-utils/tests/commonjs-test-adapter/asserts.js
new file mode 100644
index 0000000..8618ace
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/commonjs-test-adapter/asserts.js
@@ -0,0 +1,54 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const AssertBase = require("test/assert").Assert;
+
+/**
+ * Generates custom assertion constructors that may be bundled with a test
+ * suite.
+ * @params {String}
+ * names of assertion function to be added to the generated Assert.
+ */
+function Assert() {
+ let assertDescriptor = {};
+ Array.forEach(arguments, function(name) {
+ assertDescriptor[name] = { value: function(message) {
+ this.pass(message);
+ }}
+ });
+
+ return function Assert() {
+ return Object.create(AssertBase.apply(null, arguments), assertDescriptor);
+ };
+}
+
+exports["test suite"] = {
+ Assert: Assert("foo"),
+ "test that custom assertor is passed to test function": function(assert) {
+ assert.ok("foo" in assert, "custom assertion function `foo` is defined");
+ assert.foo("custom assertion function `foo` is called");
+ },
+ "test sub suite": {
+ "test that `Assert` is inherited by sub suits": function(assert) {
+ assert.ok("foo" in assert, "assertion function `foo` is not defined");
+ },
+ "test sub sub suite": {
+ Assert: Assert("bar"),
+ "test that custom assertor is passed to test function": function(assert) {
+ assert.ok("bar" in assert,
+ "custom assertion function `bar` is defined");
+ assert.bar("custom assertion function `bar` is called");
+ },
+ "test that `Assert` is not inherited by sub sub suits": function(assert) {
+ assert.ok(!("foo" in assert),
+ "assertion function `foo` is not defined");
+ }
+ }
+ }
+};
+
+if (module == require.main)
+ require("test").run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/fixtures/es5.js b/tools/addon-sdk-1.7/packages/api-utils/tests/fixtures/es5.js
new file mode 100644
index 0000000..746cae3
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/fixtures/es5.js
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+exports.frozen = Object.freeze({});
+exports.sealed = Object.seal({});
+exports.inextensible = Object.preventExtensions({});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/fixtures/sandbox-complex-character.js b/tools/addon-sdk-1.7/packages/api-utils/tests/fixtures/sandbox-complex-character.js
new file mode 100644
index 0000000..6ae6769
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/fixtures/sandbox-complex-character.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/. */
+
+var chars = 'გამარჯობა';
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/fixtures/sandbox-normal.js b/tools/addon-sdk-1.7/packages/api-utils/tests/fixtures/sandbox-normal.js
new file mode 100644
index 0000000..5425298
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/fixtures/sandbox-normal.js
@@ -0,0 +1,7 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var a = 1;
+this.b = 2;
+function f() { return 4; }
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/helpers.js b/tools/addon-sdk-1.7/packages/api-utils/tests/helpers.js
new file mode 100644
index 0000000..c639849
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/helpers.js
@@ -0,0 +1,24 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { Loader } = require("@loader");
+
+exports.Loader = function(module, globals, packaging) {
+ var options = packaging || JSON.parse(JSON.stringify(require("@packaging")));
+ options.globals = globals;
+
+ let loader = Loader.new(options);
+ return Object.create(loader, {
+ require: { value: Loader.require.bind(loader, module.path) },
+ sandbox: { value: function sandbox(id) {
+ let path = options.manifest[module.path].requirements[id].path;
+ return loader.sandboxes[path].sandbox;
+ }},
+ unload: { value: function unload(reason, callback) {
+ loader.unload(reason, callback);
+ }}
+ })
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/loader/fixture.js b/tools/addon-sdk-1.7/packages/api-utils/tests/loader/fixture.js
new file mode 100644
index 0000000..c2dc0f1
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/loader/fixture.js
@@ -0,0 +1,6 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.foo = foo;
+console.log('testing', 1, [2, 3, 4]);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/add.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/add.js
new file mode 100644
index 0000000..5472934
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/add.js
@@ -0,0 +1,9 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+define('modules/add', function () {
+ return function (a, b) {
+ return a + b;
+ };
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/async1.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/async1.js
new file mode 100644
index 0000000..b7b60fd
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/async1.js
@@ -0,0 +1,14 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+define(['./traditional2', './async2'], function () {
+ var traditional2 = require('./traditional2');
+ return {
+ name: 'async1',
+ traditional1Name: traditional2.traditional1Name,
+ traditional2Name: traditional2.name,
+ async2Name: require('./async2').name,
+ async2Traditional2Name: require('./async2').traditional2Name
+ };
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/async2.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/async2.js
new file mode 100644
index 0000000..802fb25
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/async2.js
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+define(['./traditional2', 'exports'], function (traditional2, exports) {
+ exports.name = 'async2';
+ exports.traditional2Name = traditional2.name;
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/badExportAndReturn.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/badExportAndReturn.js
new file mode 100644
index 0000000..35a5359
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/badExportAndReturn.js
@@ -0,0 +1,10 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This is a bad module, it asks for exports but also returns a value from
+// the define defintion function.
+define(['exports'], function (exports) {
+ return 'badExportAndReturn';
+});
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/badFirst.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/badFirst.js
new file mode 100644
index 0000000..fdb03bd
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/badFirst.js
@@ -0,0 +1,9 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+define(['./badSecond'], function (badSecond) {
+ return {
+ name: 'badFirst'
+ };
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/badSecond.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/badSecond.js
new file mode 100644
index 0000000..8321a85
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/badSecond.js
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var first = require('./badFirst');
+
+exports.name = 'badSecond';
+exports.badFirstName = first.name;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/blue.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/blue.js
new file mode 100644
index 0000000..14ab149
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/blue.js
@@ -0,0 +1,9 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+define(function () {
+ return {
+ name: 'blue'
+ };
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/castor.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/castor.js
new file mode 100644
index 0000000..9209623
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/castor.js
@@ -0,0 +1,10 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+define(['exports', './pollux'], function(exports, pollux) {
+ exports.name = 'castor';
+ exports.getPolluxName = function () {
+ return pollux.name;
+ };
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/cheetah.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/cheetah.js
new file mode 100644
index 0000000..37d12bf
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/cheetah.js
@@ -0,0 +1,9 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+define(function () {
+ return function () {
+ return 'cheetah';
+ };
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/color.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/color.js
new file mode 100644
index 0000000..0d946bd
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/color.js
@@ -0,0 +1,7 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+define({
+ type: 'color'
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/dupe.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/dupe.js
new file mode 100644
index 0000000..f87fa55
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/dupe.js
@@ -0,0 +1,15 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+define({
+ name: 'dupe'
+});
+
+// This is wrong and should not be allowed. Only one call to
+// define per file.
+define([], function () {
+ return {
+ name: 'dupe2'
+ };
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/dupeNested.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/dupeNested.js
new file mode 100644
index 0000000..703948c
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/dupeNested.js
@@ -0,0 +1,15 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+define(function () {
+ // This is wrong and should not be allowed.
+ define('dupeNested2', {
+ name: 'dupeNested2'
+ });
+
+ return {
+ name: 'dupeNested'
+ };
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/dupeSetExports.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/dupeSetExports.js
new file mode 100644
index 0000000..12a1be2
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/dupeSetExports.js
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+define({name: "dupeSetExports"});
+
+// so this should cause a failure
+module.setExports("no no no");
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/exportsEquals.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/exportsEquals.js
new file mode 100644
index 0000000..e176316
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/exportsEquals.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+module.exports = 4;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/green.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/green.js
new file mode 100644
index 0000000..ce2d134
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/green.js
@@ -0,0 +1,10 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+define('modules/green', ['./color'], function (color) {
+ return {
+ name: 'green',
+ parentType: color.type
+ };
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/lion.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/lion.js
new file mode 100644
index 0000000..9c3ac3b
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/lion.js
@@ -0,0 +1,7 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+define(function(require) {
+ return 'lion';
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/orange.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/orange.js
new file mode 100644
index 0000000..900a32b
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/orange.js
@@ -0,0 +1,10 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+define(['./color'], function (color) {
+ return {
+ name: 'orange',
+ parentType: color.type
+ };
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/pollux.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/pollux.js
new file mode 100644
index 0000000..61cf66f
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/pollux.js
@@ -0,0 +1,10 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+define(['exports', './castor'], function(exports, castor) {
+ exports.name = 'pollux';
+ exports.getCastorName = function () {
+ return castor.name;
+ };
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/red.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/red.js
new file mode 100644
index 0000000..c47b8e9
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/red.js
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+define(function (require) {
+ // comment fake-outs for require finding.
+ // require('bad1');
+ return {
+ name: 'red',
+ parentType: require('./color').type
+ };
+
+ /*
+ require('bad2');
+ */
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/setExports.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/setExports.js
new file mode 100644
index 0000000..495c301
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/setExports.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+module.setExports(5);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/subtract.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/subtract.js
new file mode 100644
index 0000000..06e1053
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/subtract.js
@@ -0,0 +1,9 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+define(function () {
+ return function (a, b) {
+ return a - b;
+ }
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/tiger.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/tiger.js
new file mode 100644
index 0000000..e332deb
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/tiger.js
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+define(function (require, exports) {
+ exports.name = 'tiger';
+ exports.type = require('modules/types/cat').type;
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/traditional1.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/traditional1.js
new file mode 100644
index 0000000..28af8ef
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/traditional1.js
@@ -0,0 +1,12 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.name = 'traditional1'
+
+var async1 = require('./async1');
+
+exports.traditional2Name = async1.traditional2Name;
+exports.traditional1Name = async1.traditional1Name;
+exports.async2Name = async1.async2Name;
+exports.async2Traditional2Name = async1.async2Traditional2Name;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/traditional2.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/traditional2.js
new file mode 100644
index 0000000..67b64ee
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/traditional2.js
@@ -0,0 +1,6 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.name = 'traditional2';
+exports.traditional1Name = require('./traditional1').name;
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/modules/types/cat.js b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/types/cat.js
new file mode 100644
index 0000000..41513d6
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/modules/types/cat.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.type = 'cat';
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-api-utils.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-api-utils.js
new file mode 100644
index 0000000..15f1f3e
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-api-utils.js
@@ -0,0 +1,241 @@
+/* -*- 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/. */
+
+const apiUtils = require("api-utils/api-utils");
+
+exports.testPublicConstructor = function (test) {
+ function PrivateCtor() {}
+ PrivateCtor.prototype = {};
+
+ let PublicCtor = apiUtils.publicConstructor(PrivateCtor);
+ test.assert(
+ PrivateCtor.prototype.isPrototypeOf(PublicCtor.prototype),
+ "PrivateCtor.prototype should be prototype of PublicCtor.prototype"
+ );
+
+ function testObj(useNew) {
+ let obj = useNew ? new PublicCtor() : PublicCtor();
+ test.assert(obj instanceof PublicCtor,
+ "Object should be instance of PublicCtor");
+ test.assert(obj instanceof PrivateCtor,
+ "Object should be instance of PrivateCtor");
+ test.assert(PublicCtor.prototype.isPrototypeOf(obj),
+ "PublicCtor's prototype should be prototype of object");
+ test.assertEqual(obj.constructor, PublicCtor,
+ "Object constructor should be PublicCtor");
+ }
+ testObj(true);
+ testObj(false);
+};
+
+exports.testValidateOptionsEmpty = function (test) {
+ let val = apiUtils.validateOptions(null, {});
+ assertObjsEqual(test, val, {});
+
+ val = apiUtils.validateOptions(null, { foo: {} });
+ assertObjsEqual(test, val, {});
+
+ val = apiUtils.validateOptions({}, {});
+ assertObjsEqual(test, val, {});
+
+ val = apiUtils.validateOptions({}, { foo: {} });
+ assertObjsEqual(test, val, {});
+};
+
+exports.testValidateOptionsNonempty = function (test) {
+ let val = apiUtils.validateOptions({ foo: 123 }, {});
+ assertObjsEqual(test, val, {});
+
+ val = apiUtils.validateOptions({ foo: 123, bar: 456 },
+ { foo: {}, bar: {}, baz: {} });
+ assertObjsEqual(test, val, { foo: 123, bar: 456 });
+};
+
+exports.testValidateOptionsMap = function (test) {
+ let val = apiUtils.validateOptions({ foo: 3, bar: 2 }, {
+ foo: { map: function (v) v * v },
+ bar: { map: function (v) undefined }
+ });
+ assertObjsEqual(test, val, { foo: 9, bar: undefined });
+};
+
+exports.testValidateOptionsMapException = function (test) {
+ let val = apiUtils.validateOptions({ foo: 3 }, {
+ foo: { map: function () { throw new Error(); }}
+ });
+ assertObjsEqual(test, val, { foo: 3 });
+};
+
+exports.testValidateOptionsOk = function (test) {
+ let val = apiUtils.validateOptions({ foo: 3, bar: 2, baz: 1 }, {
+ foo: { ok: function (v) v },
+ bar: { ok: function (v) v }
+ });
+ assertObjsEqual(test, val, { foo: 3, bar: 2 });
+
+ test.assertRaises(
+ function () apiUtils.validateOptions({ foo: 2, bar: 2 }, {
+ bar: { ok: function (v) v > 2 }
+ }),
+ 'The option "bar" is invalid.',
+ "ok should raise exception on invalid option"
+ );
+
+ test.assertRaises(
+ function () apiUtils.validateOptions(null, { foo: { ok: function (v) v }}),
+ 'The option "foo" is invalid.',
+ "ok should raise exception on invalid option"
+ );
+};
+
+exports.testValidateOptionsIs = function (test) {
+ let opts = {
+ array: [],
+ boolean: true,
+ func: function () {},
+ nul: null,
+ number: 1337,
+ object: {},
+ string: "foo",
+ undef1: undefined
+ };
+ let requirements = {
+ array: { is: ["array"] },
+ boolean: { is: ["boolean"] },
+ func: { is: ["function"] },
+ nul: { is: ["null"] },
+ number: { is: ["number"] },
+ object: { is: ["object"] },
+ string: { is: ["string"] },
+ undef1: { is: ["undefined"] },
+ undef2: { is: ["undefined"] }
+ };
+ let val = apiUtils.validateOptions(opts, requirements);
+ assertObjsEqual(test, val, opts);
+
+ test.assertRaises(
+ function () apiUtils.validateOptions(null, {
+ foo: { is: ["object", "number"] }
+ }),
+ 'The option "foo" must be one of the following types: object, number',
+ "Invalid type should raise exception"
+ );
+};
+
+exports.testValidateOptionsMapIsOk = function (test) {
+ let [map, is, ok] = [false, false, false];
+ let val = apiUtils.validateOptions({ foo: 1337 }, {
+ foo: {
+ map: function (v) v.toString(),
+ is: ["string"],
+ ok: function (v) v.length > 0
+ }
+ });
+ assertObjsEqual(test, val, { foo: "1337" });
+
+ let requirements = {
+ foo: {
+ is: ["object"],
+ ok: function () test.fail("is should have caused us to throw by now")
+ }
+ };
+ test.assertRaises(
+ function () apiUtils.validateOptions(null, requirements),
+ 'The option "foo" must be one of the following types: object',
+ "is should be used before ok is called"
+ );
+};
+
+exports.testValidateOptionsErrorMsg = function (test) {
+ test.assertRaises(
+ function () apiUtils.validateOptions(null, {
+ foo: { ok: function (v) v, msg: "foo!" }
+ }),
+ "foo!",
+ "ok should raise exception with customized message"
+ );
+};
+
+exports.testValidateMapWithMissingKey = function (test) {
+ let val = apiUtils.validateOptions({ }, {
+ foo: {
+ map: function (v) v || "bar"
+ }
+ });
+ assertObjsEqual(test, val, { foo: "bar" });
+
+ val = apiUtils.validateOptions({ }, {
+ foo: {
+ map: function (v) { throw "bar" }
+ }
+ });
+ assertObjsEqual(test, val, { });
+};
+
+exports.testAddIterator = function testAddIterator(test) {
+ let obj = {};
+ let keys = ["foo", "bar", "baz"];
+ let vals = [1, 2, 3];
+ let keysVals = [["foo", 1], ["bar", 2], ["baz", 3]];
+ apiUtils.addIterator(
+ obj,
+ function keysValsGen() {
+ for each (let keyVal in keysVals)
+ yield keyVal;
+ }
+ );
+
+ let keysItr = [];
+ for (let key in obj)
+ keysItr.push(key);
+ test.assertEqual(keysItr.length, keys.length,
+ "the keys iterator returns the correct number of items");
+ for (let i = 0; i < keys.length; i++)
+ test.assertEqual(keysItr[i], keys[i], "the key is correct");
+
+ let valsItr = [];
+ for each (let val in obj)
+ valsItr.push(val);
+ test.assertEqual(valsItr.length, vals.length,
+ "the vals iterator returns the correct number of items");
+ for (let i = 0; i < vals.length; i++)
+ test.assertEqual(valsItr[i], vals[i], "the val is correct");
+
+ let keysValsItr = [];
+ for (let keyVal in Iterator(obj))
+ keysValsItr.push(keyVal);
+ test.assertEqual(keysValsItr.length, keysVals.length, "the keys/vals " +
+ "iterator returns the correct number of items");
+ for (let i = 0; i < keysVals.length; i++) {
+ test.assertEqual(keysValsItr[i][0], keysVals[i][0], "the key is correct");
+ test.assertEqual(keysValsItr[i][1], keysVals[i][1], "the val is correct");
+ }
+
+ let keysOnlyItr = [];
+ for (let key in Iterator(obj, true /* keysonly */))
+ keysOnlyItr.push(key);
+ test.assertEqual(keysOnlyItr.length, keysVals.length, "the keys only " +
+ "iterator returns the correct number of items");
+ for (let i = 0; i < keysVals.length; i++)
+ test.assertEqual(keysOnlyItr[i], keysVals[i][0], "the key is correct");
+};
+
+function assertObjsEqual(test, obj1, obj2) {
+ var items = 0;
+ for (let [key, val] in Iterator(obj1)) {
+ items++;
+ test.assert(key in obj2, "obj1 key should be present in obj2");
+ test.assertEqual(obj2[key], val, "obj1 value should match obj2 value");
+ }
+ for (let [key, val] in Iterator(obj2)) {
+ items++;
+ test.assert(key in obj1, "obj2 key should be present in obj1");
+ test.assertEqual(obj1[key], val, "obj2 value should match obj1 value");
+ }
+ if (!items)
+ test.assertEqual(JSON.stringify(obj1), JSON.stringify(obj2),
+ "obj1 should have same JSON representation as obj2");
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-app-strings.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-app-strings.js
new file mode 100644
index 0000000..902c31f
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-app-strings.js
@@ -0,0 +1,62 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { Cc,Ci } = require("chrome");
+
+let StringBundle = require("app-strings").StringBundle;
+exports.testStringBundle = function(test) {
+ let url = "chrome://global/locale/security/caps.properties";
+
+ let strings = StringBundle(url);
+
+ test.assertEqual(strings.url, url,
+ "'url' property contains correct URL of string bundle");
+
+ let appLocale = Cc["@mozilla.org/intl/nslocaleservice;1"].
+ getService(Ci.nsILocaleService).
+ getApplicationLocale();
+
+ let stringBundle = Cc["@mozilla.org/intl/stringbundle;1"].
+ getService(Ci.nsIStringBundleService).
+ createBundle(url, appLocale);
+
+ let (name = "Yes") {
+ test.assertEqual(strings.get(name), stringBundle.GetStringFromName(name),
+ "getting a string returns the string");
+ }
+
+ let (name = "ExtensionCapability", args = ["foo"]) {
+ test.assertEqual(strings.get(name, args),
+ stringBundle.formatStringFromName(name, args, args.length),
+ "getting a formatted string returns the formatted string");
+ }
+
+ test.assertRaises(function () strings.get("nonexistentString"),
+ "String 'nonexistentString' could not be retrieved from " +
+ "the bundle due to an unknown error (it doesn't exist?).",
+ "retrieving a nonexistent string throws an exception");
+
+ let a = [], b = [];
+ let enumerator = stringBundle.getSimpleEnumeration();
+ while (enumerator.hasMoreElements()) {
+ let elem = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement);
+ a.push([elem.key, elem.value]);
+ }
+ for (let keyVal in Iterator(strings))
+ b.push(keyVal);
+
+ // Sort the arrays, because we don't assume enumeration has a set order.
+ // Sort compares [key, val] as string "key,val", which sorts the way we want
+ // it to, so there is no need to provide a custom compare function.
+ a.sort();
+ b.sort();
+
+ test.assertEqual(a.length, b.length,
+ "the iterator returns the correct number of items");
+ for (let i = 0; i < a.length; i++) {
+ test.assertEqual(a[i][0], b[i][0], "the iterated string's name is correct");
+ test.assertEqual(a[i][1], b[i][1],
+ "the iterated string's value is correct");
+ }
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-array.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-array.js
new file mode 100644
index 0000000..878a6ce
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-array.js
@@ -0,0 +1,40 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var array = require("array");
+
+exports.testHas = function(test) {
+ var testAry = [1, 2, 3];
+ test.assertEqual(array.has([1, 2, 3], 1), true);
+ test.assertEqual(testAry.length, 3);
+ test.assertEqual(testAry[0], 1);
+ test.assertEqual(testAry[1], 2);
+ test.assertEqual(testAry[2], 3);
+ test.assertEqual(array.has(testAry, 2), true);
+ test.assertEqual(array.has(testAry, 3), true);
+ test.assertEqual(array.has(testAry, 4), false);
+ test.assertEqual(array.has(testAry, "1"), false);
+};
+
+exports.testAdd = function(test) {
+ var testAry = [1];
+ test.assertEqual(array.add(testAry, 1), false);
+ test.assertEqual(testAry.length, 1);
+ test.assertEqual(testAry[0], 1);
+ test.assertEqual(array.add(testAry, 2), true);
+ test.assertEqual(testAry.length, 2);
+ test.assertEqual(testAry[0], 1);
+ test.assertEqual(testAry[1], 2);
+};
+
+exports.testRemove = function(test) {
+ var testAry = [1, 2];
+ test.assertEqual(array.remove(testAry, 3), false);
+ test.assertEqual(testAry.length, 2);
+ test.assertEqual(testAry[0], 1);
+ test.assertEqual(testAry[1], 2);
+ test.assertEqual(array.remove(testAry, 2), true);
+ test.assertEqual(testAry.length, 1);
+ test.assertEqual(testAry[0], 1);
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-base.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-base.js
new file mode 100644
index 0000000..e97b8b0
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-base.js
@@ -0,0 +1,240 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 { Base, Class } = require("api-utils/base");
+
+exports["test .isPrototypeOf"] = function(assert) {
+ assert.ok(Base.isPrototypeOf(Base.new()),
+ "Base is a prototype of Base.new()");
+ assert.ok(Base.isPrototypeOf(Base.extend()),
+ "Base is a prototype of Base.extned()");
+ assert.ok(Base.isPrototypeOf(Base.extend().new()),
+ "Base is a prototoype of Base.extend().new()");
+ assert.ok(!Base.extend().isPrototypeOf(Base.extend()),
+ "Base.extend() in not prototype of Base.extend()");
+ assert.ok(!Base.extend().isPrototypeOf(Base.new()),
+ "Base.extend() is not prototype of Base.new()");
+ assert.ok(!Base.new().isPrototypeOf(Base.extend()),
+ "Base.new() is not prototype of Base.extend()");
+ assert.ok(!Base.new().isPrototypeOf(Base.new()),
+ "Base.new() is not prototype of Base.new()");
+};
+
+exports["test inheritance"] = function(assert) {
+ var Parent = Base.extend({
+ name: "parent",
+ method: function () {
+ return "hello " + this.name;
+ }
+ });
+
+ assert.equal(Parent.name, "parent", "Parent name is parent");
+ assert.equal(Parent.method(), "hello parent", "method works on prototype");
+ assert.equal(Parent.new().name, Parent.name, "Parent instance inherits name");
+ assert.equal(Parent.new().method(), Parent.method(),
+ "method behaves same on the prototype");
+ assert.equal(Parent.extend({}).name, Parent.name,
+ "Parent decedent inherits name");
+
+ var Child = Parent.extend({ name: "child" });
+ assert.notEqual(Child.name, Parent.name, "Child overides name");
+ assert.equal(Child.new().name, Child.name, "Child intsances inherit name");
+ assert.equal(Child.extend().name, Child.name,
+ "Child decedents inherit name");
+
+ assert.equal(Child.method, Parent.method, "Child inherits method");
+ assert.equal(Child.extend().method, Parent.method,
+ "Child decedent inherit method");
+ assert.equal(Child.new().method, Parent.method,
+ "Child instances inherit method");
+
+ assert.equal(Child.method(), "hello child",
+ "method refers to instance proprety");
+ assert.equal(Child.extend({ name: "decedent" }).new().method(),
+ "hello decedent", "method may be overrided");
+};
+
+exports["test prototype immutability"] = function(assert) {
+
+ assert.throws(function() {
+ var override = function() {};
+ Base.extend = override;
+ if (Base.extend !== override)
+ throw Error("Property was not set");
+ }, "Base prototype is imutable");
+
+ assert.throws(function() {
+ Base.foo = "bar";
+ if (Base.foo !== "bar")
+ throw Error("Property was not set");
+ }, "Base prototype is non-configurabel");
+
+ assert.throws(function() {
+ delete Base.new;
+ if ('new' in Base)
+ throw Error('Property was not deleted');
+ }, "Can't delete properties on prototype");
+
+ var Foo = Base.extend({
+ name: 'hello',
+ rename: function rename(name) {
+ this.name = name;
+ }
+ });
+
+ assert.throws(function() {
+ var override = function() {};
+ Foo.extend = override;
+ if (Foo.extend !== override)
+ throw Error("Property was not set");
+ }, "Can't change prototype properties");
+
+ assert.throws(function() {
+ Foo.foo = "bar";
+ if (Foo.foo !== "bar")
+ throw Error("Property was not set");
+ }, "Can't add prototype properties");
+
+ assert.throws(function() {
+ delete Foo.name;
+ if ('new' in Foo)
+ throw Error('Property was not deleted');
+ }, "Can't remove prototype properties");
+
+ assert.throws(function() {
+ Foo.rename("new name");
+ if (Foo.name !== "new name")
+ throw Error("Property was not modified");
+ }, "Method's can't mutate prototypes");
+
+ var Bar = Foo.extend({
+ rename: function rename() {
+ return this.name;
+ }
+ });
+
+ assert.equal(Bar.rename(), Foo.name,
+ "properties may be overided on decedents");
+};
+
+exports['test instance mutability'] = function(assert) {
+ var Foo = Base.extend({
+ name: "foo",
+ init: function init(number) {
+ this.number = number;
+ }
+ });
+ var f1 = Foo.new();
+ /* V8 does not supports this yet!
+ assert.throws(function() {
+ f1.name = "f1";
+ }, "can't change prototype properties");
+ */
+ f1.alias = "f1";
+ assert.equal(f1.alias, "f1", "instance is mutable");
+ delete f1.alias;
+ assert.ok(!('alias' in f1), "own properties are deletable");
+ f1.init(1);
+ assert.equal(f1.number, 1, "method can mutate instance's own properties");
+};
+
+exports['test super'] = function(assert) {
+ var Foo = Base.extend({
+ initialize: function Foo(options) {
+ this.name = options.name;
+ }
+ });
+
+ var Bar = Foo.extend({
+ initialize: function Bar(options) {
+ Foo.initialize.call(this, options);
+ this.type = 'bar';
+ }
+ });
+
+ var bar = Bar.new({ name: 'test' });
+
+ assert.ok(Bar.isPrototypeOf(bar), 'Bar is prototype of Bar.new');
+ assert.ok(Foo.isPrototypeOf(bar), 'Foo is prototype of Bar.new');
+ assert.ok(Base.isPrototypeOf(bar), 'Base is prototype of Bar.new');
+ assert.equal(bar.type, 'bar', 'bar initializer was called');
+ assert.equal(bar.name, 'test', 'bar initializer called Foo initializer');
+};
+
+exports['test class'] = function(assert) {
+ var Foo = Base.extend({
+ type: 'Foo',
+ initialize: function(options) {
+ this.name = options.name;
+ },
+ serialize: function serialize() {
+ return '<' + this.name + ':' + this.type + '>';
+ }
+ });
+ var CFoo = Class(Foo);
+ var f1 = CFoo({ name: 'f1' });
+ var f2 = new CFoo({ name: 'f2' });
+ var f3 = CFoo.new({ name: 'f3' });
+ var f4 = Foo.new({ name: 'f4' });
+
+ assert.ok(f1 instanceof CFoo, 'correct instanceof');
+ assert.equal(f1.name, 'f1', 'property initialized');
+ assert.equal(f1.serialize(), '<f1:Foo>', 'method works');
+
+ assert.ok(f2 instanceof CFoo, 'correct instanceof when created with new')
+ assert.equal(f2.name, 'f2', 'property initialized');
+ assert.equal(f2.serialize(), '<f2:Foo>', 'method works');
+
+ assert.ok(f3 instanceof CFoo, 'correct instanceof when created with .new')
+ assert.equal(f3.name, 'f3', 'property initialized');
+ assert.equal(f3.serialize(), '<f3:Foo>', 'method works');
+
+ assert.ok(f4 instanceof CFoo, 'correct instanceof when created from prototype')
+ assert.equal(f4.name, 'f4', 'property initialized');
+ assert.equal(f4.serialize(), '<f4:Foo>', 'method works');
+
+ var Bar = Foo.extend({
+ type: 'Bar',
+ initialize: function(options) {
+ this.size = options.size;
+ Foo.initialize.call(this, options);
+ }
+ });
+ var CBar = Class(Bar);
+
+
+ var b1 = CBar({ name: 'b1', size: 1 });
+ var b2 = new CBar({ name: 'b2', size: 2 });
+ var b3 = CBar.new({ name: 'b3', size: 3 });
+ var b4 = Bar.new({ name: 'b4', size: 4 });
+
+ assert.ok(b1 instanceof CFoo, 'correct instanceof');
+ assert.ok(b1 instanceof CBar, 'correct instanceof');
+ assert.equal(b1.name, 'b1', 'property initialized');
+ assert.equal(b1.size, 1, 'property initialized');
+ assert.equal(b1.serialize(), '<b1:Bar>', 'method works');
+
+ assert.ok(b2 instanceof CFoo, 'correct instanceof when created with new');
+ assert.ok(b2 instanceof CBar, 'correct instanceof when created with new');
+ assert.equal(b2.name, 'b2', 'property initialized');
+ assert.equal(b2.size, 2, 'property initialized');
+ assert.equal(b2.serialize(), '<b2:Bar>', 'method works');
+
+ assert.ok(b3 instanceof CFoo, 'correct instanceof when created with .new');
+ assert.ok(b3 instanceof CBar, 'correct instanceof when created with .new');
+ assert.equal(b3.name, 'b3', 'property initialized');
+ assert.equal(b3.size, 3, 'property initialized');
+ assert.equal(b3.serialize(), '<b3:Bar>', 'method works');
+
+ assert.ok(b4 instanceof CFoo, 'correct instanceof when created from prototype');
+ assert.ok(b4 instanceof CBar, 'correct instanceof when created from prototype');
+ assert.equal(b4.name, 'b4', 'property initialized');
+ assert.equal(b4.size, 4, 'property initialized');
+ assert.equal(b4.serialize(), '<b4:Bar>', 'method works');
+};
+
+require("test").run(exports);
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-byte-streams.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-byte-streams.js
new file mode 100644
index 0000000..5bd1e42
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-byte-streams.js
@@ -0,0 +1,169 @@
+/* -*- 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 byteStreams = require("byte-streams");
+const file = require("file");
+const { pathFor } = require("api-utils/system");
+const { Loader } = require("./helpers");
+
+const STREAM_CLOSED_ERROR = "The stream is closed and cannot be used.";
+
+// This should match the constant of the same name in byte-streams.js.
+const BUFFER_BYTE_LEN = 0x8000;
+
+exports.testWriteRead = function (test) {
+ let fname = dataFileFilename();
+
+ // Write a small string less than the stream's buffer size...
+ let str = "exports.testWriteRead data!";
+ let stream = open(test, fname, true);
+ test.assert(!stream.closed, "stream.closed after open should be false");
+ stream.write(str);
+ stream.close();
+ test.assert(stream.closed, "Stream should be closed after stream.close");
+ test.assertRaises(function () stream.write("This shouldn't be written!"),
+ STREAM_CLOSED_ERROR,
+ "stream.write after close should raise error");
+
+ // ... and read it.
+ stream = open(test, fname);
+ test.assertEqual(stream.read(), str,
+ "stream.read should return string written");
+ test.assertEqual(stream.read(), "",
+ "stream.read at EOS should return empty string");
+ stream.close();
+ test.assert(stream.closed, "Stream should be closed after stream.close");
+ test.assertRaises(function () stream.read(),
+ STREAM_CLOSED_ERROR,
+ "stream.read after close should raise error");
+
+ file.remove(fname);
+};
+
+// Write a big string many times the size of the stream's buffer and read it.
+exports.testWriteReadBig = function (test) {
+ let str = "";
+ let bufLen = BUFFER_BYTE_LEN;
+ let fileSize = bufLen * 10;
+ for (let i = 0; i < fileSize; i++)
+ str += i % 10;
+ let fname = dataFileFilename();
+ let stream = open(test, fname, true);
+ stream.write(str);
+ stream.close();
+ stream = open(test, fname);
+ test.assertEqual(stream.read(), str,
+ "stream.read should return string written");
+ stream.close();
+ file.remove(fname);
+};
+
+// The same, but write and read in chunks.
+exports.testWriteReadChunks = function (test) {
+ let str = "";
+ let bufLen = BUFFER_BYTE_LEN;
+ let fileSize = bufLen * 10;
+ for (let i = 0; i < fileSize; i++)
+ str += i % 10;
+ let fname = dataFileFilename();
+ let stream = open(test, fname, true);
+ let i = 0;
+ while (i < str.length) {
+ // Use a chunk length that spans buffers.
+ let chunk = str.substr(i, bufLen + 1);
+ stream.write(chunk);
+ i += bufLen + 1;
+ }
+ stream.close();
+ stream = open(test, fname);
+ let readStr = "";
+ bufLen = BUFFER_BYTE_LEN;
+ let readLen = bufLen + 1;
+ do {
+ var frag = stream.read(readLen);
+ readStr += frag;
+ } while (frag);
+ stream.close();
+ test.assertEqual(readStr, str,
+ "stream.write and read in chunks should work as expected");
+ file.remove(fname);
+};
+
+exports.testReadLengths = function (test) {
+ let fname = dataFileFilename();
+ let str = "exports.testReadLengths data!";
+ let stream = open(test, fname, true);
+ stream.write(str);
+ stream.close();
+
+ stream = open(test, fname);
+ test.assertEqual(stream.read(str.length * 1000), str,
+ "stream.read with big byte length should return string " +
+ "written");
+ stream.close();
+
+ stream = open(test, fname);
+ test.assertEqual(stream.read(0), "",
+ "string.read with zero byte length should return empty " +
+ "string");
+ stream.close();
+
+ stream = open(test, fname);
+ test.assertEqual(stream.read(-1), "",
+ "string.read with negative byte length should return " +
+ "empty string");
+ stream.close();
+
+ file.remove(fname);
+};
+
+exports.testTruncate = function (test) {
+ let fname = dataFileFilename();
+ let str = "exports.testReadLengths data!";
+ let stream = open(test, fname, true);
+ stream.write(str);
+ stream.close();
+
+ stream = open(test, fname);
+ test.assertEqual(stream.read(), str,
+ "stream.read should return string written");
+ stream.close();
+
+ stream = open(test, fname, true);
+ stream.close();
+
+ stream = open(test, fname);
+ test.assertEqual(stream.read(), "",
+ "stream.read after truncate should be empty");
+ stream.close();
+
+ file.remove(fname);
+};
+
+exports.testUnload = function (test) {
+ let loader = Loader(module);
+ let file = loader.require("file");
+
+ let filename = dataFileFilename("temp-b");
+ let stream = file.open(filename, "wb");
+
+ loader.unload();
+ test.assert(stream.closed, "Stream should be closed after module unload");
+};
+
+// Returns the name of a file that should be used to test writing and reading.
+function dataFileFilename() {
+ return file.join(pathFor("ProfD"), "test-byte-streams-data");
+}
+
+// Opens and returns the given file and ensures it's of the correct class.
+function open(test, filename, forWriting) {
+ let stream = file.open(filename, forWriting ? "wb" : "b");
+ let klass = forWriting ? "ByteWriter" : "ByteReader";
+ test.assert(stream instanceof byteStreams[klass],
+ "Opened stream should be a " + klass);
+ return stream;
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-collection.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-collection.js
new file mode 100644
index 0000000..f3e5476
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-collection.js
@@ -0,0 +1,127 @@
+/* -*- 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/. */
+
+const collection = require("collection");
+
+exports.testAddRemove = function (test) {
+ let coll = new collection.Collection();
+ compare(test, coll, []);
+ addRemove(test, coll, [], false);
+};
+
+exports.testAddRemoveBackingArray = function (test) {
+ let items = ["foo"];
+ let coll = new collection.Collection(items);
+ compare(test, coll, items);
+ addRemove(test, coll, items, true);
+
+ items = ["foo", "bar"];
+ coll = new collection.Collection(items);
+ compare(test, coll, items);
+ addRemove(test, coll, items, true);
+};
+
+exports.testProperty = function (test) {
+ let obj = makeObjWithCollProp();
+ compare(test, obj.coll, []);
+ addRemove(test, obj.coll, [], false);
+
+ // Test single-value set.
+ let items = ["foo"];
+ obj.coll = items[0];
+ compare(test, obj.coll, items);
+ addRemove(test, obj.coll, items, false);
+
+ // Test array set.
+ items = ["foo", "bar"];
+ obj.coll = items;
+ compare(test, obj.coll, items);
+ addRemove(test, obj.coll, items, false);
+};
+
+exports.testPropertyBackingArray = function (test) {
+ let items = ["foo"];
+ let obj = makeObjWithCollProp(items);
+ compare(test, obj.coll, items);
+ addRemove(test, obj.coll, items, true);
+
+ items = ["foo", "bar"];
+ obj = makeObjWithCollProp(items);
+ compare(test, obj.coll, items);
+ addRemove(test, obj.coll, items, true);
+};
+
+// Adds some values to coll and then removes them. initialItems is an array
+// containing coll's initial items. isBacking is true if initialItems is coll's
+// backing array; the point is that updates to coll should affect initialItems
+// if that's the case.
+function addRemove(test, coll, initialItems, isBacking) {
+ let items = isBacking ? initialItems : initialItems.slice(0);
+ let numInitialItems = items.length;
+
+ // Test add(val).
+ let numInsertions = 5;
+ for (let i = 0; i < numInsertions; i++) {
+ compare(test, coll, items);
+ coll.add(i);
+ if (!isBacking)
+ items.push(i);
+ }
+ compare(test, coll, items);
+
+ // Add the items we just added to make sure duplicates aren't added.
+ for (let i = 0; i < numInsertions; i++)
+ coll.add(i);
+ compare(test, coll, items);
+
+ // Test remove(val). Do a kind of shuffled remove. Remove item 1, then
+ // item 0, 3, 2, 5, 4, ...
+ for (let i = 0; i < numInsertions; i++) {
+ let val = i % 2 ? i - 1 :
+ i === numInsertions - 1 ? i : i + 1;
+ coll.remove(val);
+ if (!isBacking)
+ items.splice(items.indexOf(val), 1);
+ compare(test, coll, items);
+ }
+ test.assertEqual(coll.length, numInitialItems,
+ "All inserted items should be removed");
+
+ // Remove the items we just removed. coll should be unchanged.
+ for (let i = 0; i < numInsertions; i++)
+ coll.remove(i);
+ compare(test, coll, items);
+
+ // Test add and remove([val1, val2]).
+ let newItems = [0, 1];
+ coll.add(newItems);
+ compare(test, coll, isBacking ? items : items.concat(newItems));
+ coll.remove(newItems);
+ compare(test, coll, items);
+ test.assertEqual(coll.length, numInitialItems,
+ "All inserted items should be removed");
+}
+
+// Asserts that the items in coll are the items of array.
+function compare(test, coll, array) {
+ test.assertEqual(coll.length, array.length,
+ "Collection length should be correct");
+ let numItems = 0;
+ for (let item in coll) {
+ test.assertEqual(item, array[numItems], "Items should be equal");
+ numItems++;
+ }
+ test.assertEqual(numItems, array.length,
+ "Number of items in iteration should be correct");
+}
+
+// Returns a new object with a collection property named "coll". backingArray,
+// if defined, will create the collection with that backing array.
+function makeObjWithCollProp(backingArray) {
+ let obj = {};
+ collection.addCollectionProperty(obj, "coll", backingArray);
+ return obj;
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-commonjs-test-adapter.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-commonjs-test-adapter.js
new file mode 100644
index 0000000..936bea9
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-commonjs-test-adapter.js
@@ -0,0 +1,11 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+exports["test custom `Assert`'s"] = require("./commonjs-test-adapter/asserts");
+
+// Disabling this check since it is not yet supported by jetpack.
+// if (module == require.main)
+ require("test").run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-content-loader.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-content-loader.js
new file mode 100644
index 0000000..e19e316
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-content-loader.js
@@ -0,0 +1,226 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+const { Loader } = require('content/loader');
+const self = require("self");
+
+exports['test:contentURL'] = function(test) {
+ let loader = Loader(),
+ value, emitted = 0, changes = 0;
+
+ test.assertRaises(
+ function() loader.contentURL = undefined,
+ 'The `contentURL` option must be a valid URL.',
+ 'Must throw an exception if `contentURL` is not URL.'
+ );
+ test.assertRaises(
+ function() loader.contentURL = null,
+ 'The `contentURL` option must be a valid URL.',
+ 'Must throw an exception if `contentURL` is not URL.'
+ );
+ test.assertRaises(
+ function() loader.contentURL = 4,
+ 'The `contentURL` option must be a valid URL.',
+ 'Must throw an exception if `contentURL` is not URL.'
+ );
+ test.assertRaises(
+ function() loader.contentURL = { toString: function() 'Oops' },
+ 'The `contentURL` option must be a valid URL.',
+ 'Must throw an exception if `contentURL` is not URL.'
+ );
+
+ function listener(e) {
+ emitted ++;
+ test.assert(
+ 'contentURL' in e,
+ 'emitted event must contain "content" property'
+ );
+ test.assert(
+ value,
+ '' + e.contentURL,
+ 'content property of an event must match value'
+ );
+ }
+ loader.on('propertyChange', listener);
+
+ test.assertEqual(
+ null,
+ loader.contentURL,
+ 'default value is `null`'
+ );
+ loader.contentURL = value = 'data:text/html,<html><body>Hi</body><html>';
+ test.assertEqual(
+ value,
+ '' + loader.contentURL,
+ 'data uri is ok'
+ );
+ test.assertEqual(
+ ++changes,
+ emitted,
+ 'had to emit `propertyChange`'
+ );
+ loader.contentURL = value;
+ test.assertEqual(
+ changes,
+ emitted,
+ 'must not emit `propertyChange` if same value is set'
+ );
+
+ loader.contentURL = value = 'http://google.com/';
+ test.assertEqual(
+ value,
+ '' + loader.contentURL,
+ 'value must be set'
+ );
+ test.assertEqual(
+ ++ changes,
+ emitted,
+ 'had to emit `propertyChange`'
+ );
+ loader.contentURL = value;
+ test.assertEqual(
+ changes,
+ emitted,
+ 'must not emit `propertyChange` if same value is set'
+ );
+
+ loader.removeListener('propertyChange', listener);
+ loader.contentURL = value = 'about:blank';
+ test.assertEqual(
+ value,
+ '' + loader.contentURL,
+ 'contentURL must be an actual value'
+ );
+ test.assertEqual(
+ changes,
+ emitted,
+ 'listener had to be romeved'
+ );
+};
+
+exports['test:contentScriptWhen'] = function(test) {
+ let loader = Loader();
+ test.assertEqual(
+ 'end',
+ loader.contentScriptWhen,
+ '`contentScriptWhen` defaults to "end"'
+ );
+ loader.contentScriptWhen = "end";
+ test.assertEqual(
+ "end",
+ loader.contentScriptWhen
+ );
+ try {
+ loader.contentScriptWhen = 'boom';
+ test.fail('must throw when wrong value is set');
+ } catch(e) {
+ test.assertEqual(
+ 'The `contentScriptWhen` option must be either "start", "ready" or "end".',
+ e.message
+ );
+ }
+ loader.contentScriptWhen = null;
+ test.assertEqual(
+ 'end',
+ loader.contentScriptWhen,
+ '`contentScriptWhen` defaults to "end"'
+ );
+ loader.contentScriptWhen = "ready";
+ test.assertEqual(
+ "ready",
+ loader.contentScriptWhen
+ );
+ loader.contentScriptWhen = "start";
+ test.assertEqual(
+ 'start',
+ loader.contentScriptWhen
+ );
+};
+
+exports['test:contentScript'] = function(test) {
+ let loader = Loader(), value;
+ test.assertEqual(
+ null,
+ loader.contentScript,
+ '`contentScript` defaults to `null`'
+ );
+ loader.contentScript = value = 'let test = {};';
+ test.assertEqual(
+ value,
+ loader.contentScript
+ );
+ try {
+ loader.contentScript = { 1: value }
+ test.fail('must throw when wrong value is set');
+ } catch(e) {
+ test.assertEqual(
+ 'The `contentScript` option must be a string or an array of strings.',
+ e.message
+ );
+ }
+ try {
+ loader.contentScript = ['oue', 2]
+ test.fail('must throw when wrong value is set');
+ } catch(e) {
+ test.assertEqual(
+ 'The `contentScript` option must be a string or an array of strings.',
+ e.message
+ );
+ }
+ loader.contentScript = undefined;
+ test.assertEqual(
+ null,
+ loader.contentScript
+ );
+ loader.contentScript = value = ["1;", "2;"];
+ test.assertEqual(
+ value,
+ loader.contentScript
+ );
+};
+
+exports['test:contentScriptFile'] = function(test) {
+ let loader = Loader(), value, uri = self.data.url("test-content-loader.js");
+ test.assertEqual(
+ null,
+ loader.contentScriptFile,
+ '`contentScriptFile` defaults to `null`'
+ );
+ loader.contentScriptFile = value = uri;
+ test.assertEqual(
+ value,
+ loader.contentScriptFile
+ );
+ try {
+ loader.contentScriptFile = { 1: uri }
+ test.fail('must throw when wrong value is set');
+ } catch(e) {
+ test.assertEqual(
+ 'The `contentScriptFile` option must be a local URL or an array of URLs.',
+ e.message
+ );
+ }
+
+ try {
+ loader.contentScriptFile = [ 'oue', uri ]
+ test.fail('must throw when wrong value is set');
+ } catch(e) {
+ test.assertEqual(
+ 'The `contentScriptFile` option must be a local URL or an array of URLs.',
+ e.message
+ );
+ }
+
+ loader.contentScriptFile = undefined;
+ test.assertEqual(
+ null,
+ loader.contentScriptFile
+ );
+ loader.contentScriptFile = value = [uri];
+ test.assertEqual(
+ value,
+ loader.contentScriptFile
+ );
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-content-proxy.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-content-proxy.js
new file mode 100644
index 0000000..f7f1235
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-content-proxy.js
@@ -0,0 +1,807 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 hiddenFrames = require("hidden-frame");
+const xulApp = require("xul-app");
+
+const { Loader } = require('./helpers');
+
+/*
+ * Utility function that allow to easily run a proxy test with a clean
+ * new HTML document. See first unit test for usage.
+ */
+function createProxyTest(html, callback) {
+ return function (test) {
+ test.waitUntilDone();
+
+ let url = 'data:text/html,' + encodeURI(html);
+
+ let hiddenFrame = hiddenFrames.add(hiddenFrames.HiddenFrame({
+ onReady: function () {
+
+ function onDOMReady() {
+ hiddenFrame.element.removeEventListener("DOMContentLoaded", onDOMReady,
+ false);
+
+ let xrayWindow = hiddenFrame.element.contentWindow;
+ let rawWindow = xrayWindow.wrappedJSObject;
+
+ let done = false;
+ let helper = {
+ xrayWindow: xrayWindow,
+ rawWindow: rawWindow,
+ createWorker: function (contentScript) {
+ return createWorker(test, xrayWindow, contentScript, helper.done);
+ },
+ done: function () {
+ if (done)
+ return;
+ done = true;
+ hiddenFrames.remove(hiddenFrame);
+ test.done();
+ }
+ }
+ callback(helper, test);
+ }
+
+ hiddenFrame.element.addEventListener("DOMContentLoaded", onDOMReady, false);
+ hiddenFrame.element.setAttribute("src", url);
+
+ }
+ }));
+ };
+}
+
+function createWorker(test, xrayWindow, contentScript, done) {
+ // We have to use Sandboxed loader in order to get access to the private
+ // unlock key `PRIVATE_KEY`. This key should not be used anywhere else.
+ // See `PRIVATE_KEY` definition in worker.js
+ let loader = Loader(module);
+ let Worker = loader.require("api-utils/content/worker").Worker;
+ let key = loader.sandbox("api-utils/content/worker").PRIVATE_KEY;
+ let worker = Worker({
+ exposeUnlockKey : key,
+ window: xrayWindow,
+ contentScript: [
+ 'new ' + function () {
+ assert = function assert(v, msg) {
+ self.port.emit("assert", {assertion:v, msg:msg});
+ }
+ done = function done() {
+ self.port.emit("done");
+ }
+ },
+ contentScript
+ ]
+ });
+
+ worker.port.on("done", done);
+ worker.port.on("assert", function (data) {
+ test.assert(data.assertion, data.msg);
+ });
+
+ return worker;
+}
+
+/* Examples for the `createProxyTest` uses */
+
+let html = "<script>var documentGlobal = true</script>";
+exports.testCreateProxyTest = createProxyTest(html, function (helper, test) {
+ // You can get access to regular `test` object in second argument of
+ // `createProxyTest` method:
+ test.assert(helper.rawWindow.documentGlobal,
+ "You have access to a raw window reference via `helper.rawWindow`");
+ test.assert(!("documentGlobal" in helper.xrayWindow),
+ "You have access to an XrayWrapper reference via `helper.xrayWindow`");
+
+ // If you do not create a Worker, you have to call helper.done(),
+ // in order to say when your test is finished
+ helper.done();
+});
+
+exports.testCreateProxyTestWithWorker = createProxyTest("", function (helper) {
+
+ helper.createWorker(
+ "new " + function WorkerScope() {
+ assert(true, "You can do assertions in your content script");
+ // And if you create a worker, you either have to call `done`
+ // from content script or helper.done()
+ done();
+ }
+ );
+
+});
+
+exports.testCreateProxyTestWithEvents = createProxyTest("", function (helper, test) {
+
+ let worker = helper.createWorker(
+ "new " + function WorkerScope() {
+ self.port.emit("foo");
+ }
+ );
+
+ worker.port.on("foo", function () {
+ test.pass("You can use events");
+ // And terminate your test with helper.done:
+ helper.done();
+ });
+
+});
+
+// Verify that the attribute `exposeUnlockKey`, that allow this test
+// to identify proxies, works correctly.
+// See `PRIVATE_KEY` definition in worker.js
+exports.testKeyAccess = createProxyTest("", function(helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ assert("UNWRAP_ACCESS_KEY" in window, "have access to `UNWRAP_ACCESS_KEY`");
+ done();
+ }
+ );
+
+});
+
+
+// Bug 714778: There was some issue around `toString` functions
+// that ended up being shared between content scripts
+exports.testSharedToStringProxies = createProxyTest("", function(helper) {
+
+ let worker = helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // We ensure that `toString` can't be modified so that nothing could
+ // leak to/from the document and between content scripts
+ //document.location.toString = function foo() {};
+ document.location.toString.foo = "bar";
+ assert(!("foo" in document.location.toString), "document.location.toString can't be modified");
+ assert(document.location.toString() == "data:text/html,",
+ "First document.location.toString()");
+ self.postMessage("next");
+ }
+ );
+ worker.on("message", function () {
+ helper.createWorker(
+ 'new ' + function ContentScriptScope2() {
+ assert(!("foo" in document.location.toString),
+ "document.location.toString is different for each content script");
+ assert(document.location.toString() == "data:text/html,",
+ "Second document.location.toString()");
+ done();
+ }
+ );
+ });
+});
+
+
+// Ensure that postMessage is working correctly across documents with an iframe
+let html = '<iframe id="iframe" name="test" src="data:text/html," />';
+exports.testPostMessage = createProxyTest(html, function (helper, test) {
+ let ifWindow = helper.xrayWindow.document.getElementById("iframe").contentWindow;
+ // Listen without proxies, to check that it will work in regular case
+ // simulate listening from a web document.
+ ifWindow.addEventListener("message", function listener(event) {
+ //if (event.source.wrappedJSObject == helper.rawWindow) return;
+ ifWindow.removeEventListener("message", listener, false);
+ // As we are in system principal, event is an XrayWrapper
+ test.assertEqual(event.source, ifWindow,
+ "event.source is the iframe window");
+ test.assertEqual(event.origin, "null", "origin is null");
+
+ test.assertEqual(event.data, "{\"foo\":\"bar\\n \\\"escaped\\\".\"}",
+ "message data is correct");
+
+ helper.done();
+ }, false);
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ assert(postMessage === postMessage,
+ "verify that we doesn't generate multiple functions for the same method");
+
+ var json = JSON.stringify({foo : "bar\n \"escaped\"."});
+
+ document.getElementById("iframe").contentWindow.postMessage(json, "*");
+ }
+ );
+});
+
+let html = '<input id="input2" type="checkbox" />';
+exports.testObjectListener = createProxyTest(html, function (helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // Test objects being given as event listener
+ let input = document.getElementById("input2");
+ let myClickListener = {
+ called: false,
+ handleEvent: function(event) {
+ assert(this === myClickListener, "`this` is the original object");
+ assert(!this.called, "called only once");
+ this.called = true;
+ assert(event.valueOf() !== event.valueOf(UNWRAP_ACCESS_KEY), "event is wrapped");
+ assert(event.target, input, "event.target is the wrapped window");
+ done();
+ }
+ };
+
+ window.addEventListener("click", myClickListener, true);
+ input.click();
+ window.removeEventListener("click", myClickListener, true);
+ }
+ );
+
+});
+
+exports.testObjectListener2 = createProxyTest("", function (helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // Verify object as DOM event listener
+ let myMessageListener = {
+ called: false,
+ handleEvent: function(event) {
+ window.removeEventListener("message", myMessageListener, true);
+
+ assert(this == myMessageListener, "`this` is the original object");
+ assert(!this.called, "called only once");
+ this.called = true;
+ assert(event.valueOf() !== event.valueOf(UNWRAP_ACCESS_KEY), "event is wrapped");
+ assert(event.target == document.defaultView, "event.target is the wrapped window");
+ assert(event.source == document.defaultView, "event.source is the wrapped window");
+ assert(event.origin == "null", "origin is null");
+ assert(event.data == "ok", "message data is correct");
+ done();
+ }
+ };
+
+ window.addEventListener("message", myMessageListener, true);
+ document.defaultView.postMessage("ok", '*');
+ }
+ );
+
+});
+
+let html = '<input id="input" type="text" /><input id="input3" type="checkbox" />' +
+ '<input id="input2" type="checkbox" />';
+exports.testStringOverload = createProxyTest(html, function (helper, test) {
+ // Proxy - toString error
+ let originalString = "string";
+ let p = Proxy.create({
+ get: function(receiver, name) {
+ if (name == "binded")
+ return originalString.toString.bind(originalString);
+ return originalString[name];
+ }
+ });
+ test.assertRaises(function () {
+ p.toString();
+ },
+ /String.prototype.toString called on incompatible Proxy/,
+ "toString can't be called with this being the proxy");
+ test.assertEqual(p.binded(), "string", "but it works if we bind this to the original string");
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // RightJS is hacking around String.prototype, and do similar thing:
+ // Pass `this` from a String prototype method.
+ // It is funny because typeof this == "object"!
+ // So that when we pass `this` to a native method,
+ // our proxy code can fail on another even more crazy thing.
+ // See following test to see what fails around proxies.
+ String.prototype.update = function () {
+ assert(typeof this == "object", "in update, `this` is an object");
+ assert(this.toString() == "input", "in update, `this.toString works");
+ return document.querySelectorAll(this);
+ };
+ assert("input".update().length == 3, "String.prototype overload works");
+ done();
+ }
+ );
+});
+
+exports.testMozMatchedSelector = createProxyTest("", function (helper) {
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // Check mozMatchesSelector XrayWrappers bug:
+ // mozMatchesSelector returns bad results when we are not calling it from the node itself
+ // SEE BUG 658909: mozMatchesSelector returns incorrect results with XrayWrappers
+ assert(document.createElement( "div" ).mozMatchesSelector("div"),
+ "mozMatchesSelector works while being called from the node");
+ assert(document.documentElement.mozMatchesSelector.call(
+ document.createElement( "div" ),
+ "div"
+ ),
+ "mozMatchesSelector works while being called from a " +
+ "function reference to " +
+ "document.documentElement.mozMatchesSelector.call");
+ done();
+ }
+ );
+});
+
+exports.testEventsOverload = createProxyTest("", function (helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // If we add a "____proxy" attribute on XrayWrappers in order to store
+ // the related proxy to create an unique proxy for each wrapper;
+ // we end up setting this attribute to prototype objects :x
+ // And so, instances created with such prototype will be considered
+ // as equal to the prototype ...
+ // // Internal method that return the proxy for a given XrayWrapper
+ // function proxify(obj) {
+ // if (obj._proxy) return obj._proxy;
+ // return obj._proxy = Proxy.create(...);
+ // }
+ //
+ // // Get a proxy of an XrayWrapper prototype object
+ // let proto = proxify(xpcProto);
+ //
+ // // Use this proxy as a prototype
+ // function Constr() {}
+ // Constr.proto = proto;
+ //
+ // // Try to create an instance using this prototype
+ // let xpcInstance = new Constr();
+ // let wrapper = proxify(xpcInstance)
+ //
+ // xpcProto._proxy = proto and as xpcInstance.__proto__ = xpcProto,
+ // xpcInstance._proxy = proto ... and profixy(xpcInstance) = proto :(
+ //
+ let proto = window.document.createEvent('HTMLEvents').__proto__;
+ window.Event.prototype = proto;
+ let event = document.createEvent('HTMLEvents');
+ assert(event !== proto, "Event should not be equal to its prototype");
+ event.initEvent('dataavailable', true, true);
+ assert(event.type === 'dataavailable', "Events are working fine");
+ done();
+ }
+ );
+
+});
+
+exports.testNestedAttributes = createProxyTest("", function (helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // XrayWrappers has a bug when you set an attribute on it,
+ // in some cases, it creates an unnecessary wrapper that introduces
+ // a different object that refers to the same original object
+ // Check that our wrappers don't reproduce this bug
+ // SEE BUG 658560: Fix identity problem with CrossOriginWrappers
+ let o = {sandboxObject:true};
+ window.nested = o;
+ o.foo = true;
+ assert(o === window.nested, "Nested attribute to sandbox object should not be proxified");
+ window.nested = document;
+ assert(window.nested === document, "Nested attribute to proxy should not be double proxified");
+ done();
+ }
+ );
+
+});
+
+exports.testFormNodeName = createProxyTest("", function (helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ let body = document.body;
+ // Check form[nodeName]
+ let form = document.createElement("form");
+ let input = document.createElement("input");
+ input.setAttribute("name", "test");
+ form.appendChild(input);
+ body.appendChild(form);
+ assert(form.test == input, "form[nodeName] is valid");
+ body.removeChild(form);
+ done();
+ }
+ );
+
+});
+
+exports.testLocalStorage = createProxyTest("", function (helper, test) {
+
+ let worker = helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // Check localStorage:
+ assert(window.localStorage, "has access to localStorage");
+ window.localStorage.name = 1;
+ assert(window.localStorage.name == 1, "localStorage appears to work");
+
+ self.port.on("step2", function () {
+ window.localStorage.clear();
+ assert(window.localStorage.name == undefined, "localStorage really, really works");
+ done();
+ });
+ self.port.emit("step1");
+ }
+ );
+
+ worker.port.on("step1", function () {
+ test.assertEqual(helper.rawWindow.localStorage.name, 1, "localStorage really works");
+ worker.port.emit("step2");
+ });
+
+});
+
+exports.testAutoUnwrapCustomAttributes = createProxyTest("", function (helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ let body = document.body;
+ // Setting a custom object to a proxy attribute is not wrapped when we get it afterward
+ let object = {custom: true, enumerable: false};
+ body.customAttribute = object;
+ assert(body.customAttribute.valueOf() === body.customAttribute.valueOf(UNWRAP_ACCESS_KEY), "custom JS attributes are not wrapped");
+ assert(object === body.customAttribute, "custom JS attributes are not wrapped");
+ done();
+ }
+ );
+
+});
+
+exports.testObjectTag = createProxyTest("", function (helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // <object>, <embed> and other tags return typeof 'function'
+ let flash = document.createElement("object");
+ assert(typeof flash == "function", "<object> is typeof 'function'");
+ assert(flash.toString().match(/\[object HTMLObjectElement.*\]/), "<object> is HTMLObjectElement");
+ assert("setAttribute" in flash, "<object> has a setAttribute method");
+ done();
+ }
+ );
+
+});
+
+exports.testHighlightToStringBehavior = createProxyTest("", function (helper, test) {
+ // We do not have any workaround this particular use of toString
+ // applied on <object> elements. So disable this test until we found one!
+ //test.assertEqual(helper.rawWindow.Object.prototype.toString.call(flash), "[object HTMLObjectElement]", "<object> is HTMLObjectElement");
+ function f() {};
+ test.assertMatches(Object.prototype.toString.call(f), /\[object Function.*\]/, "functions are functions 1");
+ // This is how jquery call toString:
+ test.assertMatches(helper.rawWindow.Object.prototype.toString.call(""), /\[object String.*\]/, "strings are strings");
+ test.assertMatches(helper.rawWindow.Object.prototype.toString.call({}), /\[object Object.*\]/, "objects are objects");
+
+ // Make sure to pass a function from the same compartments
+ // or toString will return [object Object] on FF8+
+ let f2 = helper.rawWindow.eval("(function () {})");
+ test.assertMatches(helper.rawWindow.Object.prototype.toString.call(f2), /\[object Function.*\]/, "functions are functions 2");
+
+ helper.done();
+});
+
+exports.testDocumentTagName = createProxyTest("", function (helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ let body = document.body;
+ // Check document[tagName]
+ let div = document.createElement("div");
+ div.setAttribute("name", "test");
+ body.appendChild(div);
+ assert(!document.test, "document[divName] is undefined");
+ body.removeChild(div);
+
+ let form = document.createElement("form");
+ form.setAttribute("name", "test");
+ body.appendChild(form);
+ assert(document.test == form, "document[formName] is valid");
+ body.removeChild(form);
+
+ let img = document.createElement("img");
+ img.setAttribute("name", "test");
+ body.appendChild(img);
+ assert(document.test == img, "document[imgName] is valid");
+ body.removeChild(img);
+ done();
+ }
+ );
+
+});
+
+let html = '<iframe id="iframe" name="test" src="data:text/html," />';
+exports.testWindowFrames = createProxyTest(html, function (helper) {
+
+ helper.createWorker(
+ 'let glob = this; new ' + function ContentScriptScope() {
+ // Check window[frameName] and window.frames[i]
+ let iframe = document.getElementById("iframe");
+ //assert(window.frames.length == 1, "The iframe is reported in window.frames check1");
+ //assert(window.frames[0] == iframe.contentWindow, "The iframe is reported in window.frames check2");
+ //console.log(window.test+ "-"+iframe.contentWindow);
+ //console.log(window);
+ assert(window.test == iframe.contentWindow, "window[frameName] is valid");
+ done();
+ }
+ );
+
+});
+
+exports.testCollections = createProxyTest("", function (helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // Highlight XPCNativeWrapper bug with HTMLCollection
+ // tds[0] is only defined on first access :o
+ let body = document.body;
+ let div = document.createElement("div");
+ body.appendChild(div);
+ div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";
+ let tds = div.getElementsByTagName("td");
+ assert(tds[0] == tds[0], "We can get array element multiple times");
+ body.removeChild(div);
+ done();
+ }
+ );
+
+});
+
+let html = '<input id="input" type="text" /><input id="input3" type="checkbox" />' +
+ '<input id="input2" type="checkbox" />';
+exports.testCollections2 = createProxyTest(html, function (helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // Verify that NodeList/HTMLCollection are working fine
+ let body = document.body;
+ let inputs = body.getElementsByTagName("input");
+ assert(body.childNodes.length == 3, "body.childNodes length is correct");
+ assert(inputs.length == 3, "inputs.length is correct");
+ assert(body.childNodes[0] == inputs[0], "body.childNodes[0] is correct");
+ assert(body.childNodes[1] == inputs[1], "body.childNodes[1] is correct");
+ assert(body.childNodes[2] == inputs[2], "body.childNodes[2] is correct");
+ let count = 0;
+ for(let i in body.childNodes) {
+ count++;
+ }
+ assert(count == 3, "body.childNodes is iterable");
+ done();
+ }
+ );
+
+});
+
+exports.testValueOf = createProxyTest("", function (helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // Check internal use of valueOf()
+ assert(window.valueOf().toString().match(/\[object Window.*\]/), "proxy.valueOf() returns the wrapped version");
+ assert(window.valueOf({}).toString().match(/\[object Window.*\]/), "proxy.valueOf({}) returns the wrapped version");
+ assert(window.valueOf(UNWRAP_ACCESS_KEY).toString().match(/\[object XrayWrapper \[object Window.*\].*\]/), "proxy.valueOf(UNWRAP_ACCESS_KEY) returns the unwrapped version");
+ done();
+ }
+ );
+
+});
+
+exports.testXMLHttpRequest = createProxyTest("", function (helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // XMLHttpRequest doesn't support XMLHttpRequest.apply,
+ // that may break our proxy code
+ assert(window.XMLHttpRequest(), "we are able to instantiate XMLHttpRequest object");
+ done();
+ }
+ );
+
+});
+
+exports.testXPathResult = createProxyTest("", function (helper, test) {
+
+ // Check XPathResult bug with constants being undefined on
+ // XPCNativeWrapper
+ let value = helper.rawWindow.XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE;
+ let xpcXPathResult = helper.xrayWindow.XPathResult;
+ test.assertEqual(xpcXPathResult.wrappedJSObject.
+ UNORDERED_NODE_SNAPSHOT_TYPE,
+ value,
+ "XPathResult's constants are valid on unwrapped node");
+
+ if (xulApp.versionInRange(xulApp.platformVersion, "10.0a1", "*")) {
+ test.assertEqual(xpcXPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, 6,
+ "XPathResult's constants are defined on " +
+ "XPCNativeWrapper (platform bug #)");
+ }
+ else {
+ test.assertEqual(xpcXPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
+ undefined,
+ "XPathResult's constants are undefined on " +
+ "XPCNativeWrapper (platform bug #665279)");
+ }
+
+ let worker = helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ self.port.on("value", function (value) {
+ // Check that our work around is working:
+ assert(window.XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE === value,
+ "XPathResult works correctly on Proxies");
+ done();
+ });
+ }
+ );
+ worker.port.emit("value", value);
+
+});
+
+exports.testPrototypeInheritance = createProxyTest("", function (helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // Verify that inherited prototype function like initEvent
+ // are handled correctly. (e2.type will return an error if it's not the case)
+ let event1 = document.createEvent( 'MouseEvents' );
+ event1.initEvent( "click", true, true );
+ let event2 = document.createEvent( 'MouseEvents' );
+ event2.initEvent( "click", true, true );
+ assert(event2.type == "click", "We are able to create an event");
+ done();
+ }
+ );
+
+});
+
+exports.testFunctions = createProxyTest("", function (helper) {
+
+ helper.rawWindow.callFunction = function (f) f();
+ helper.rawWindow.isEqual = function (a, b) a == b;
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // Check basic usage of functions
+ let closure2 = function () {return "ok";};
+ assert(window.wrappedJSObject.callFunction(closure2) == "ok", "Function references work");
+
+ // Ensure that functions are cached when being wrapped to native code
+ let closure = function () {};
+ assert(window.wrappedJSObject.isEqual(closure, closure), "Function references are cached before being wrapped to native");
+ done();
+ }
+ );
+
+});
+
+let html = '<input id="input2" type="checkbox" />';
+exports.testListeners = createProxyTest(html, function (helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // Verify listeners:
+ let input = document.getElementById("input2");
+ assert(input, "proxy.getElementById works");
+
+ function onclick() {};
+ input.onclick = onclick;
+ assert(input.onclick === onclick, "on* attributes are equal to original function set");
+
+ let addEventListenerCalled = false;
+ let expandoCalled = false;
+ input.addEventListener("click", function onclick(event) {
+ input.removeEventListener("click", onclick, true);
+
+ assert(!addEventListenerCalled, "closure given to addEventListener is called once");
+ if (addEventListenerCalled)
+ return;
+ addEventListenerCalled = true;
+
+ assert(!event.target.ownerDocument.defaultView.documentGlobal, "event object is still wrapped and doesn't expose document globals");
+ assert("__isWrappedProxy" in event.target, "event object is a proxy");
+
+ let input2 = document.getElementById("input2");
+
+ input.onclick = function (event) {
+ input.onclick = null;
+ assert(!expandoCalled, "closure set to expando is called once");
+ if (expandoCalled) return;
+ expandoCalled = true;
+
+ assert(!event.target.ownerDocument.defaultView.documentGlobal, "event object is still wrapped and doesn't expose document globals");
+ assert("__isWrappedProxy" in event.target, "event object is a proxy");
+
+ setTimeout(function () {
+ input.click();
+ done();
+ }, 0);
+
+ }
+
+ setTimeout(function () {
+ input.click();
+ }, 0);
+
+ }, true);
+
+ input.click();
+ }
+ );
+
+});
+
+exports.testMozRequestAnimationFrame = createProxyTest("", function (helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ window.mozRequestAnimationFrame(function callback() {
+ assert(callback == this, "callback is equal to `this`");
+ done();
+ });
+ }
+ );
+
+});
+
+exports.testGlobalScope = createProxyTest("", function (helper) {
+
+ helper.createWorker(
+ 'let toplevelScope = true;' +
+ 'assert(window.toplevelScope, "variables in toplevel scope are set to `window` object");' +
+ 'assert(this.toplevelScope, "variables in toplevel scope are set to `this` object");' +
+ 'done();'
+ );
+
+});
+
+// Bug 671016: Typed arrays should not be proxified
+exports.testTypedArrays = createProxyTest("", function (helper) {
+
+ helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ let canvas = document.createElement("canvas");
+ let context = canvas.getContext("2d");
+ let imageData = context.getImageData(0,0, 1, 1);
+ let unwrappedData = imageData.valueOf(UNWRAP_ACCESS_KEY).data;
+ let data = imageData.data;
+ assert(unwrappedData === data, "Typed array isn't proxified")
+ done();
+ }
+ );
+
+});
+
+// Bug 715755: proxy code throw an exception on COW
+// Create an http server in order to simulate real cross domain documents
+exports.testCrossDomainIframe = createProxyTest("", function (helper) {
+ let serverPort = 8099;
+ let server = require("httpd").startServerAsync(serverPort);
+ server.registerPathHandler("/", function handle(request, response) {
+ // Returns an empty webpage
+ response.write("");
+ });
+
+ let worker = helper.createWorker(
+ 'new ' + function ContentScriptScope() {
+ // Waits for the server page url
+ self.on("message", function (url) {
+ // Creates an iframe with this page
+ let iframe = document.createElement("iframe");
+ iframe.addEventListener("load", function onload() {
+ iframe.removeEventListener("load", onload, true);
+ try {
+ // Try accessing iframe's content that is made of COW wrappers
+ // Take care of debug builds that add object address after `Window`
+ assert(String(iframe.contentWindow).match(/\[object Window.*\]/),
+ "COW works properly");
+ } catch(e) {
+ assert(false, "COW fails : "+e.message);
+ }
+ self.port.emit("end");
+ }, true);
+ iframe.setAttribute("src", url);
+ document.body.appendChild(iframe);
+ });
+ }
+ );
+
+ worker.port.on("end", function () {
+ server.stop(helper.done);
+ });
+
+ worker.postMessage("http://localhost:" + serverPort + "/");
+
+});
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-content-symbiont.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-content-symbiont.js
new file mode 100644
index 0000000..526af05
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-content-symbiont.js
@@ -0,0 +1,190 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { Cc, Ci } = require('chrome');
+const { Symbiont } = require('content/symbiont');
+const self = require("self");
+
+function makeWindow() {
+ let content =
+ '<?xml version="1.0"?>' +
+ '<window ' +
+ 'xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">' +
+ '<iframe id="content" type="content"/>' +
+ '</window>';
+ var url = "data:application/vnd.mozilla.xul+xml," +
+ encodeURIComponent(content);
+ var features = ["chrome", "width=10", "height=10"];
+
+ return Cc["@mozilla.org/embedcomp/window-watcher;1"].
+ getService(Ci.nsIWindowWatcher).
+ openWindow(null, url, null, features.join(","), null);
+}
+
+exports['test:constructing symbiont && validating API'] = function(test) {
+ let window = makeWindow();
+ window.addEventListener("load", function onLoad() {
+ window.removeEventListener("load", onLoad, false);
+ let frame = window.document.getElementById("content");
+ // TODO: support arrays ??
+ let contentScripts = ["1;", "2;"];
+ let contentSymbiont = Symbiont({
+ frame: frame,
+ contentScriptFile: self.data.url("test-content-symbiont.js"),
+ contentScript: contentScripts,
+ contentScriptWhen: "start"
+ });
+
+ test.assertEqual(
+ self.data.url("test-content-symbiont.js"),
+ contentSymbiont.contentScriptFile,
+ "There is one contentScriptFile, as specified in options."
+ );
+ test.assertEqual(
+ contentScripts.length,
+ contentSymbiont.contentScript.length,
+ "There are two contentScripts, as specified in options."
+ );
+ test.assertEqual(
+ contentScripts[0],
+ contentSymbiont.contentScript[0],
+ "There are two contentScripts, as specified in options."
+ );
+ test.assertEqual(
+ contentScripts[1],
+ contentSymbiont.contentScript[1],
+ "There are two contentScripts, as specified in options."
+ )
+ test.assertEqual(
+ contentSymbiont.contentScriptWhen,
+ "start",
+ "contentScriptWhen is as specified in options."
+ );
+
+ test.done();
+ window.close();
+ frame.setAttribute("src", "data:text/html,<html><body></body></html>");
+ }, false);
+ test.waitUntilDone();
+};
+
+exports["test:communication with worker global scope"] = function(test) {
+ let window = makeWindow();
+ let contentSymbiont;
+
+ function onMessage1(message) {
+ test.assertEqual(message, 1, "Program gets message via onMessage.");
+ contentSymbiont.removeListener('message', onMessage1);
+ contentSymbiont.on('message', onMessage2);
+ contentSymbiont.postMessage(2);
+ };
+
+ function onMessage2(message) {
+ if (5 == message) {
+ test.done();
+ } else {
+ test.assertEqual(message, 3, "Program gets message via onMessage2.");
+ contentSymbiont.postMessage(4)
+ }
+ }
+
+ window.addEventListener("load", function onLoad() {
+ window.removeEventListener("load", onLoad, false);
+ let frame = window.document.getElementById("content");
+ contentSymbiont = Symbiont({
+ frame: frame,
+ contentScript: 'new ' + function() {
+ self.postMessage(1);
+ self.on("message", function onMessage(message) {
+ if (message === 2)
+ self.postMessage(3);
+ if (message === 4)
+ self.postMessage(5);
+ });
+ } + '()',
+ contentScriptWhen: 'ready',
+ onMessage: onMessage1
+ });
+
+ frame.setAttribute("src", "data:text/html,<html><body></body></html>");
+ }, false);
+ test.waitUntilDone();
+};
+
+exports['test:pageWorker'] = function(test) {
+ test.waitUntilDone();
+ let worker = Symbiont({
+ contentURL: 'about:buildconfig',
+ contentScript: 'new ' + function WorkerScope() {
+ self.on('message', function(data) {
+ if (data.valid)
+ self.postMessage('bye!');
+ })
+ self.postMessage(window.location.toString());
+ },
+ onMessage: function(msg) {
+ if (msg == 'bye!') {
+ test.done()
+ } else {
+ test.assertEqual(
+ worker.contentURL + '',
+ msg
+ );
+ worker.postMessage({ valid: true });
+ }
+ }
+ });
+};
+
+exports["test:document element present on 'start'"] = function(test) {
+ test.waitUntilDone();
+ let xulApp = require("xul-app");
+ let worker = Symbiont({
+ contentURL: "about:buildconfig",
+ contentScript: "self.postMessage(!!document.documentElement)",
+ contentScriptWhen: "start",
+ onMessage: function(message) {
+ if (xulApp.versionInRange(xulApp.platformVersion, "2.0b6", "*"))
+ test.assert(message, "document element present on 'start'");
+ else
+ test.pass("document element not necessarily present on 'start'");
+ test.done();
+ }
+ });
+};
+
+exports["test:direct communication with trusted document"] = function(test) {
+ test.waitUntilDone();
+
+ let worker = Symbiont({
+ contentURL: require("self").data.url("test-trusted-document.html")
+ });
+
+ worker.port.on('document-to-addon', function (arg) {
+ test.assertEqual(arg, "ok", "Received an event from the document");
+ worker.destroy();
+ test.done();
+ });
+ worker.port.emit('addon-to-document', 'ok');
+};
+
+exports["test:`addon` is not available when a content script is set"] = function(test) {
+ test.waitUntilDone();
+
+ let worker = Symbiont({
+ contentURL: require("self").data.url("test-trusted-document.html"),
+ contentScript: "new " + function ContentScriptScope() {
+ self.port.emit("cs-to-addon", "addon" in unsafeWindow);
+ }
+ });
+
+ worker.port.on('cs-to-addon', function (hasAddon) {
+ test.assertEqual(hasAddon, false,
+ "`addon` is not available");
+ worker.destroy();
+ test.done();
+ });
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-content-worker.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-content-worker.js
new file mode 100644
index 0000000..d5e95a1
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-content-worker.js
@@ -0,0 +1,465 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 stirct";
+
+const { Cc, Ci } = require('chrome');
+const timer = require('timer');
+const { Loader } = require("@loader");
+
+function makeWindow(contentURL) {
+ let content =
+ '<?xml version="1.0"?>' +
+ '<window ' +
+ 'xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">' +
+ '<iframe id="content" type="content" src="' +
+ encodeURIComponent(contentURL) + '"/>' +
+ '<script>var documentValue=true;</script>' +
+ '</window>';
+ var url = "data:application/vnd.mozilla.xul+xml," +
+ encodeURIComponent(content);
+ var features = ["chrome", "width=10", "height=10"];
+
+ return Cc["@mozilla.org/embedcomp/window-watcher;1"].
+ getService(Ci.nsIWindowWatcher).
+ openWindow(null, url, null, features.join(","), null);
+}
+
+const { Worker } = require('content/worker');
+exports['test:sample'] = function(test) {
+ let window = makeWindow();
+ test.waitUntilDone();
+
+ // As window has just being created, its document is still loading,
+ // and we have about:blank document before the expected one
+ test.assertEqual(window.document.location.href, "about:blank",
+ "window starts by loading about:blank");
+
+ // We need to wait for the load/unload of temporary about:blank
+ // or our worker is going to be automatically destroyed
+ window.addEventListener("load", function onload() {
+ window.removeEventListener("load", onload, true);
+
+ test.assertNotEqual(window.document.location.href, "about:blank",
+ "window is now on the right document");
+
+ let worker = Worker({
+ window: window,
+ contentScript: 'new ' + function WorkerScope() {
+ // window is accessible
+ let myLocation = window.location.toString();
+ self.on('message', function(data) {
+ if (data == 'hi!')
+ self.postMessage('bye!');
+ });
+ },
+ contentScriptWhen: 'ready',
+ onMessage: function(msg) {
+ test.assertEqual('bye!', msg);
+ test.assertEqual(worker.url, window.document.location.href,
+ "worker.url still works");
+ test.done();
+ }
+ });
+
+ test.assertEqual(worker.url, window.document.location.href,
+ "worker.url works");
+ worker.postMessage('hi!');
+
+ }, true);
+
+}
+
+exports['test:emit'] = function(test) {
+ let window = makeWindow();
+ test.waitUntilDone();
+
+ let worker = Worker({
+ window: window,
+ contentScript: 'new ' + function WorkerScope() {
+ // Validate self.on and self.emit
+ self.port.on('addon-to-content', function (data) {
+ self.port.emit('content-to-addon', data);
+ });
+
+ // Check for global pollution
+ //if (typeof on != "undefined")
+ // self.postMessage("`on` is in globals");
+ if (typeof once != "undefined")
+ self.postMessage("`once` is in globals");
+ if (typeof emit != "undefined")
+ self.postMessage("`emit` is in globals");
+
+ },
+ onMessage: function(msg) {
+ test.fail("Got an unexpected message : "+msg);
+ }
+ });
+
+ // Validate worker.port
+ worker.port.on('content-to-addon', function (data) {
+ test.assertEqual(data, "event data");
+ test.done();
+ });
+ worker.port.emit('addon-to-content', 'event data');
+
+}
+
+exports['test:emit hack message'] = function(test) {
+ let window = makeWindow();
+ test.waitUntilDone();
+
+ let worker = Worker({
+ window: window,
+ contentScript: 'new ' + function WorkerScope() {
+ // Validate self.port
+ self.port.on('message', function (data) {
+ self.port.emit('message', data);
+ });
+ // We should not receive message on self, but only on self.port
+ self.on('message', function (data) {
+ self.postMessage('message', data);
+ });
+ },
+ onError: function(e) {
+ test.fail("Got exception: "+e);
+ }
+ });
+
+ worker.port.on('message', function (data) {
+ test.assertEqual(data, "event data");
+ test.done();
+ });
+ worker.on('message', function (data) {
+ test.fail("Got an unexpected message : "+msg);
+ });
+ worker.port.emit('message', 'event data');
+
+}
+
+exports['test:n-arguments emit'] = function(test) {
+ let window = makeWindow();
+ test.waitUntilDone();
+
+ let worker = Worker({
+ window: window,
+ contentScript: 'new ' + function WorkerScope() {
+ // Validate self.on and self.emit
+ self.port.on('addon-to-content', function (a1, a2, a3) {
+ self.port.emit('content-to-addon', a1, a2, a3);
+ });
+ }
+ });
+
+ // Validate worker.port
+ worker.port.on('content-to-addon', function (arg1, arg2, arg3) {
+ test.assertEqual(arg1, "first argument");
+ test.assertEqual(arg2, "second");
+ test.assertEqual(arg3, "third");
+ test.done();
+ });
+ worker.port.emit('addon-to-content', 'first argument', 'second', 'third');
+}
+
+exports['test:post-json-values-only'] = function(test) {
+ let window = makeWindow("data:text/html,");
+ test.waitUntilDone();
+
+ window.addEventListener("load", function onload() {
+ window.removeEventListener("load", onload, true);
+
+ let worker = Worker({
+ window: window.document.getElementById("content").contentWindow,
+ contentScript: 'new ' + function WorkerScope() {
+ self.on('message', function (message) {
+ self.postMessage([ message.fun === undefined,
+ typeof message.w,
+ message.w && "port" in message.w,
+ message.w.url,
+ Array.isArray(message.array),
+ JSON.stringify(message.array)]);
+ });
+ }
+ });
+
+ // Validate worker.onMessage
+ let array = [1, 2, 3];
+ worker.on('message', function (message) {
+ test.assert(message[0], "function becomes undefined");
+ test.assertEqual(message[1], "object", "object stays object");
+ test.assert(message[2], "object's attributes are enumerable");
+ test.assertEqual(message[3], "about:blank", "jsonable attributes are accessible");
+ // See bug 714891, Arrays may be broken over compartements:
+ test.assert(message[4], "Array keeps being an array");
+ test.assertEqual(message[5], JSON.stringify(array),
+ "Array is correctly serialized");
+ test.done();
+ });
+ worker.postMessage({ fun: function () {}, w: worker, array: array });
+
+ }, true);
+
+};
+
+
+exports['test:emit-json-values-only'] = function(test) {
+ let window = makeWindow("data:text/html,");
+ test.waitUntilDone();
+
+ window.addEventListener("load", function onload() {
+ window.removeEventListener("load", onload, true);
+
+ let win = window.document.getElementById("content").contentWindow;
+ let worker = Worker({
+ window: win,
+ contentScript: 'new ' + function WorkerScope() {
+ // Validate self.on and self.emit
+ self.port.on('addon-to-content', function (fun, w, obj, array) {
+ self.port.emit('content-to-addon', [
+ fun === null,
+ typeof w,
+ "port" in w,
+ w.url,
+ "fun" in obj,
+ Object.keys(obj.dom).length,
+ Array.isArray(array),
+ JSON.stringify(array)
+ ]);
+ });
+ }
+ });
+
+ // Validate worker.port
+ let array = [1, 2, 3];
+ worker.port.on('content-to-addon', function (result) {
+ test.assert(result[0], "functions become null");
+ test.assertEqual(result[1], "object", "objects stay objects");
+ test.assert(result[2], "object's attributes are enumerable");
+ test.assertEqual(result[3], "about:blank", "json attribute is accessible");
+ test.assert(!result[4], "function as object attribute is removed");
+ test.assertEqual(result[5], 0, "DOM nodes are converted into empty object");
+ // See bug 714891, Arrays may be broken over compartments:
+ test.assert(result[6], "Array keeps being an array");
+ test.assertEqual(result[7], JSON.stringify(array),
+ "Array is correctly serialized");
+ test.done();
+ });
+
+ let obj = {
+ fun: function () {},
+ dom: window.document.createElement("div")
+ };
+ worker.port.emit("addon-to-content", function () {}, worker, obj, array);
+
+ }, true);
+}
+
+exports['test:content is wrapped'] = function(test) {
+ let contentURL = 'data:text/html,<script>var documentValue=true;</script>';
+ let window = makeWindow(contentURL);
+ test.waitUntilDone();
+
+ window.addEventListener("load", function onload() {
+ window.removeEventListener("load", onload, true);
+
+ let worker = Worker({
+ window: window.document.getElementById("content").contentWindow,
+ contentScript: 'new ' + function WorkerScope() {
+ self.postMessage(!window.documentValue);
+ },
+ contentScriptWhen: 'ready',
+ onMessage: function(msg) {
+ test.assert(msg,
+ "content script has a wrapped access to content document");
+ test.done();
+ }
+ });
+
+ }, true);
+
+}
+
+exports['test:chrome is unwrapped'] = function(test) {
+ let window = makeWindow();
+ test.waitUntilDone();
+
+ window.addEventListener("load", function onload() {
+ window.removeEventListener("load", onload, true);
+
+ let worker = Worker({
+ window: window,
+ contentScript: 'new ' + function WorkerScope() {
+ self.postMessage(window.documentValue);
+ },
+ contentScriptWhen: 'ready',
+ onMessage: function(msg) {
+ test.assert(msg,
+ "content script has an unwrapped access to chrome document");
+ test.done();
+ }
+ });
+
+ }, true);
+
+}
+
+exports['test:nothing is leaked to content script'] = function(test) {
+ let window = makeWindow();
+ test.waitUntilDone();
+
+ window.addEventListener("load", function onload() {
+ window.removeEventListener("load", onload, true);
+
+ let worker = Worker({
+ window: window,
+ contentScript: 'new ' + function WorkerScope() {
+ self.postMessage([
+ "ContentWorker" in window,
+ "UNWRAP_ACCESS_KEY" in window,
+ "getProxyForObject" in window
+ ]);
+ },
+ contentScriptWhen: 'ready',
+ onMessage: function(list) {
+ test.assert(!list[0], "worker API contrustor isn't leaked");
+ test.assert(!list[1], "Proxy API stuff isn't leaked 1/2");
+ test.assert(!list[2], "Proxy API stuff isn't leaked 2/2");
+ test.done();
+ }
+ });
+
+ }, true);
+
+}
+
+exports['test:ensure console.xxx works in cs'] = function(test) {
+ test.waitUntilDone(5000);
+
+ // Create a new module loader in order to be able to create a `console`
+ // module mockup:
+ let sandbox = Loader.new(require("@packaging"));
+ let sandboxRequire = Loader.require.bind(sandbox, module.path);
+ Object.defineProperty(sandbox.globals, 'console', {
+ value: {
+ log: hook.bind("log"),
+ info: hook.bind("info"),
+ warn: hook.bind("warn"),
+ error: hook.bind("error"),
+ debug: hook.bind("debug"),
+ exception: hook.bind("exception")
+ }
+ });
+
+ // Intercept all console method calls
+ let calls = [];
+ function hook(msg) {
+ test.assertEqual(this, msg,
+ "console.xxx(\"xxx\"), i.e. message is equal to the " +
+ "console method name we are calling");
+ calls.push(msg);
+ }
+
+ // Finally, create a worker that will call all console methods
+ let window = makeWindow();
+ window.addEventListener("load", function onload() {
+ window.removeEventListener("load", onload, true);
+
+ let worker = sandboxRequire('content/worker').Worker({
+ window: window,
+ contentScript: 'new ' + function WorkerScope() {
+ console.log("log");
+ console.info("info");
+ console.warn("warn");
+ console.error("error");
+ console.debug("debug");
+ console.exception("exception");
+ self.postMessage();
+ },
+ onMessage: function() {
+ // Ensure that console methods are called in the same execution order
+ test.assertEqual(JSON.stringify(calls),
+ JSON.stringify(["log", "info", "warn", "error", "debug", "exception"]),
+ "console has been called successfully, in the expected order");
+ test.done();
+ }
+ });
+ }, true);
+
+}
+
+
+exports['test:setTimeout can\'t be cancelled by content'] = function(test) {
+ let contentURL = 'data:text/html,<script>var documentValue=true;</script>';
+ let window = makeWindow(contentURL);
+ test.waitUntilDone();
+
+ window.addEventListener("load", function onload() {
+ window.removeEventListener("load", onload, true);
+
+ let worker = Worker({
+ window: window.document.getElementById("content").contentWindow,
+ contentScript: 'new ' + function WorkerScope() {
+ let id = setTimeout(function () {
+ self.postMessage("timeout");
+ }, 100);
+ unsafeWindow.eval("clearTimeout("+id+");");
+ },
+ contentScriptWhen: 'ready',
+ onMessage: function(msg) {
+ test.assert(msg,
+ "content didn't managed to cancel our setTimeout");
+ test.done();
+ }
+ });
+
+ }, true);
+
+}
+
+exports['test:setTimeout are unregistered on content unload'] = function(test) {
+ let contentURL = 'data:text/html,foo';
+ let window = makeWindow(contentURL);
+ test.waitUntilDone();
+
+ window.addEventListener("load", function onload() {
+ window.removeEventListener("load", onload, true);
+
+ let iframe = window.document.getElementById("content");
+ let originalDocument = iframe.contentDocument;
+ let worker = Worker({
+ window: iframe.contentWindow,
+ contentScript: 'new ' + function WorkerScope() {
+ document.title = "ok";
+ let i = 0;
+ setInterval(function () {
+ document.title = i++;
+ }, 10);
+ },
+ contentScriptWhen: 'ready'
+ });
+
+ // Change location so that content script is destroyed,
+ // and all setTimeout/setInterval should be unregistered.
+ // Wait some cycles in order to execute some intervals.
+ timer.setTimeout(function () {
+ // Bug 689621: Wait for the new document load so that we are sure that
+ // previous document cancelled its intervals
+ iframe.addEventListener("load", function onload() {
+ iframe.removeEventListener("load", onload, true);
+ let titleAfterLoad = originalDocument.title;
+ // Wait additional cycles to verify that intervals are really cancelled
+ timer.setTimeout(function () {
+ test.assertEqual(iframe.contentDocument.title, "final",
+ "New document has not been modified");
+ test.assertEqual(originalDocument.title, titleAfterLoad,
+ "Nor previous one");
+ test.done();
+ }, 100);
+ }, true);
+ iframe.setAttribute("src", "data:text/html,<title>final</title>");
+ }, 100);
+
+ }, true);
+
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-cortex.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-cortex.js
new file mode 100644
index 0000000..9f13f0e
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-cortex.js
@@ -0,0 +1,122 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// vim:set ts=2 sw=2 sts=2
+
+"use strict";
+
+var Cortex = require("cortex").Cortex;
+
+exports["test property changes propagate"] = function (assert) {
+ var source = {
+ _foo: "secret",
+ get foo() {
+ return this._foo;
+ },
+ set foo(value) {
+ this._foo = value;
+ },
+ get getOnly() {
+ return this._foo;
+ },
+ set setOnly(value) {
+ this._setOnly = value;
+ },
+ bar: "public",
+ method: function method(a, b) {
+ return this._foo + a + b
+ }
+ };
+ var fixture = Cortex(source);
+
+ assert.ok(!('_foo' in fixture),
+ "properties that start with `_` are omitted");
+ assert.equal(fixture.foo, "secret", "get accessor alias works");
+ fixture.foo = "new secret";
+ assert.equal(fixture.foo, "new secret", "set accessor alias works");
+ assert.equal(source.foo, "new secret", "accessor delegates to the source");
+ assert.equal(fixture.bar, "public", "data property alias works");
+ fixture.bar = "bar";
+ assert.equal(source.bar, "bar", "data property change propagates");
+ source.bar = "foo"
+ assert.equal(fixture.bar, "foo", "data property change probagets back");
+ assert.equal(fixture.method("a", "b"), "new secretab",
+ "public methods are callable");
+ assert.equal(fixture.method.call({ _foo: "test" }, " a,", "b"),
+ "new secret a,b",
+ "`this` pseudo-variable can not be passed through call.");
+ assert.equal(fixture.method.apply({ _foo: "test" }, [" a,", "b"]),
+ "new secret a,b",
+ "`this` pseudo-variable can not be passed through apply.");
+ assert.equal(fixture.getOnly, source._foo,
+ "getter returned property of wrapped object");
+ fixture.setOnly = 'bar'
+ assert.equal(source._setOnly, 'bar', "setter modified wrapped object")
+};
+
+
+exports["test immunity of inheritance"] = function(assert) {
+ function Type() {}
+ Type.prototype = {
+ constructor: Type,
+ _bar: 2,
+ bar: 3,
+ get_Foo: function getFoo() {
+ return this._foo;
+ }
+ }
+ var source = Object.create(Type.prototype, {
+ _foo: { value: 'secret' },
+ getBar: { value: function get_Bar() {
+ return this.bar
+ }},
+ get_Bar: { value: function getBar() {
+ return this._bar
+ }}
+ });
+
+ var fixture = Cortex(source);
+
+ assert.ok(Cortex({}, null, Type.prototype) instanceof Type,
+ "if custom prototype is providede cortex will inherit from it");
+ assert.ok(fixture instanceof Type,
+ "if no prototype is given cortex inherits from object's prototype");
+
+ source.bar += 1;
+ assert.notEqual(fixture.bar, source.bar,
+ "chages of properties don't propagate to non-aliases");
+ assert.equal(fixture.getBar(), source.bar,
+ "methods accessing public properties are bound to the source");
+
+ fixture._bar += 1;
+ assert.notEqual(fixture._bar, source._bar,
+ "changes of non aliased properties don't propagate");
+ assert.equal(fixture.get_Bar(), source._bar,
+ "methods accessing privates are bound to the source");
+ assert.notEqual(fixture.get_Foo(), source._foo,
+ "prototoype methods are not bound to the source");
+}
+
+exports["test customized public properties"] = function(assert) {
+ var source = {
+ _a: 'a',
+ b: 'b',
+ get: function get(name) {
+ return this[name];
+ }
+ };
+
+ var fixture = Cortex(source, ['_a', 'get']);
+ fixture._a += "#change";
+
+
+ assert.ok(!("b" in fixture), "non-public own property is not defined");
+ assert.equal(fixture.get("b"), source.b,
+ "public methods preserve access to the private properties");
+ assert.equal(fixture._a, source._a,
+ "custom public property changes propagate");
+}
+
+//if (require.main == module)
+ require("test").run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-dom.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-dom.js
new file mode 100644
index 0000000..52ab1c3
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-dom.js
@@ -0,0 +1,88 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const events = require("dom/events");
+const { activeBrowserWindow: { document } } = require("window-utils");
+const window = document.window;
+
+exports["test on / emit"] = function (assert, done) {
+ let element = document.createElement("div");
+ events.on(element, "click", function listener(event) {
+ assert.equal(event.target, element, "event has correct target");
+ events.removeListener(element, "click", listener);
+ done();
+ });
+
+ events.emit(element, "click", {
+ category: "MouseEvents",
+ settings: [
+ true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null
+ ]
+ });
+};
+
+exports["test remove"] = function (assert, done) {
+ let element = document.createElement("span");
+ let l1 = 0;
+ let l2 = 0;
+ let options = {
+ category: "MouseEvents",
+ settings: [
+ true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null
+ ]
+ };
+
+ events.on(element, "click", function listener1(event) {
+ l1 ++;
+ assert.equal(event.target, element, "event has correct target");
+ events.removeListener(element, "click", listener1);
+ });
+
+ events.on(element, "click", function listener2(event) {
+ l2 ++;
+ if (l1 < l2) {
+ assert.equal(l1, 1, "firs listener was called and then romeved");
+ events.removeListener(element, "click", listener2);
+ done();
+ }
+ events.emit(element, "click", options);
+ });
+
+ events.emit(element, "click", options);
+};
+
+exports["test once"] = function (assert, done) {
+ let element = document.createElement("h1");
+ let l1 = 0;
+ let l2 = 0;
+ let options = {
+ category: "MouseEvents",
+ settings: [
+ true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null
+ ]
+ };
+
+
+ events.once(element, "click", function listener(event) {
+ assert.equal(event.target, element, "event target is a correct element");
+ l1 ++;
+ });
+
+ events.on(element, "click", function listener(event) {
+ l2 ++;
+ if (l2 > 3) {
+ events.removeListener(element, "click", listener);
+ assert.equal(event.target, element, "event has correct target");
+ assert.equal(l1, 1, "once was called only once");
+ done();
+ }
+ events.emit(element, "click", options);
+ });
+
+ events.emit(element, "click", options);
+}
+
+require("test").run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-environment.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-environment.js
new file mode 100644
index 0000000..1d7e783
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-environment.js
@@ -0,0 +1,50 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+const { env } = require('api-utils/environment');
+const { Cc, Ci } = require('chrome');
+const { get, set, exists } = Cc['@mozilla.org/process/environment;1'].
+ getService(Ci.nsIEnvironment);
+
+exports['test exists'] = function(assert) {
+ assert.equal('PATH' in env, exists('PATH'),
+ 'PATH environment variable is defined');
+ assert.equal('FOO1' in env, exists('FOO1'),
+ 'FOO1 environment variable is not defined');
+ set('FOO1', 'foo');
+ assert.equal('FOO1' in env, true,
+ 'FOO1 environment variable was set');
+ set('FOO1', null);
+ assert.equal('FOO1' in env, false,
+ 'FOO1 environment variable was unset');
+};
+
+exports['test get'] = function(assert) {
+ assert.equal(env.PATH, get('PATH'), 'PATH env variable matches');
+ assert.equal(env.BAR2, undefined, 'BAR2 env variable is not defined');
+ set('BAR2', 'bar');
+ assert.equal(env.BAR2, 'bar', 'BAR2 env variable was set');
+ set('BAR2', null);
+ assert.equal(env.BAR2, undefined, 'BAR2 env variable was unset');
+};
+
+exports['test set'] = function(assert) {
+ assert.equal(get('BAZ3'), '', 'BAZ3 env variable is not set');
+ assert.equal(env.BAZ3, undefined, 'BAZ3 is not set');
+ env.BAZ3 = 'baz';
+ assert.equal(env.BAZ3, get('BAZ3'), 'BAZ3 env variable is set');
+ assert.equal(get('BAZ3'), 'baz', 'BAZ3 env variable was set to "baz"');
+};
+
+exports['test unset'] = function(assert) {
+ env.BLA4 = 'bla';
+ assert.equal(env.BLA4, 'bla', 'BLA4 env varibale is set');
+ delete env.BLA4;
+ assert.equal(env.BLA4, undefined, 'BLA4 env variable is unset');
+ assert.equal('BLA4' in env, false, 'BLA4 env variable no longer exists' );
+};
+
+require('test').run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-errors.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-errors.js
new file mode 100644
index 0000000..64bb6c9
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-errors.js
@@ -0,0 +1,70 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var errors = require("errors");
+
+exports.testCatchAndLog = function(test) {
+ var caught = [];
+ function dummyLog(e) { caught.push(e); }
+
+ var wrapped = errors.catchAndLog(function(x) {
+ throw Error("blah" + x + this);
+ },
+ "boop",
+ dummyLog);
+ test.assertEqual(wrapped.call("hi", 1), "boop",
+ "exceptions should be trapped, def. resp. returned");
+ test.assertEqual(caught.length, 1,
+ "logging function should be called");
+ test.assertEqual(caught[0].message, "blah1hi",
+ "args and this should be passed to wrapped func");
+};
+
+exports.testCatchAndLogProps = function(test) {
+ var caught = [];
+ function dummyLog(e) { caught.push(e); }
+
+ var thing = {
+ foo: function(x) { throw Error("nowai" + x); },
+ bar: function() { throw Error("blah"); },
+ baz: function() { throw Error("fnarg"); }
+ };
+
+ errors.catchAndLogProps(thing, "foo", "ugh", dummyLog);
+
+ test.assertEqual(thing.foo(1), "ugh",
+ "props should be wrapped");
+ test.assertEqual(caught.length, 1,
+ "logging function should be called");
+ test.assertEqual(caught[0].message, "nowai1",
+ "args should be passed to wrapped func");
+ test.assertRaises(function() { thing.bar(); },
+ "blah",
+ "non-wrapped props should be wrapped");
+
+ errors.catchAndLogProps(thing, ["bar", "baz"], "err", dummyLog);
+ test.assert((thing.bar() == thing.baz()) &&
+ (thing.bar() == "err"),
+ "multiple props should be wrapped if array passed in");
+};
+
+exports.testCatchAndReturn = function(test) {
+ var wrapped = errors.catchAndReturn(function(x) {
+ if (x == 1)
+ return "one";
+ if (x == 2)
+ throw new Error("two");
+ return this + x;
+ });
+
+ test.assertEqual(wrapped(1).returnValue, "one",
+ "arg should be passed; return value should be returned");
+ test.assert(wrapped(2).exception, "exception should be returned");
+ test.assertEqual(wrapped(2).exception.message, "two", "message is correct");
+ test.assert(wrapped(2).exception.fileName.indexOf("test-errors.js") != -1,
+ "filename is present");
+ test.assert(wrapped(2).exception.stack, "stack is available");
+ test.assertEqual(wrapped.call("hi", 3).returnValue, "hi3",
+ "`this` should be set correctly");
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-event-core.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-event-core.js
new file mode 100644
index 0000000..4b664ff
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-event-core.js
@@ -0,0 +1,217 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+const { on, once, off, emit, count, amass } = require('api-utils/event/core');
+const { Loader } = require('./helpers');
+
+exports['test add a listener'] = function(assert) {
+ let events = [ { name: 'event#1' }, 'event#2' ];
+ let target = { name: 'target' };
+
+ on(target, 'message', function(message) {
+ assert.equal(this, target, 'this is a target object');
+ assert.equal(message, events.shift(), 'message is emitted event');
+ });
+ emit(target, 'message', events[0]);
+ emit(target, 'message', events[0]);
+};
+
+exports['test that listener is unique per type'] = function(assert) {
+ let actual = []
+ let target = {}
+ function listener() { actual.push(1) }
+ on(target, 'message', listener);
+ on(target, 'message', listener);
+ on(target, 'message', listener);
+ on(target, 'foo', listener);
+ on(target, 'foo', listener);
+
+ emit(target, 'message');
+ assert.deepEqual([ 1 ], actual, 'only one message listener added');
+ emit(target, 'foo');
+ assert.deepEqual([ 1, 1 ], actual, 'same listener added for other event');
+};
+
+exports['test event type matters'] = function(assert) {
+ let target = { name: 'target' }
+ on(target, 'message', function() {
+ assert.fail('no event is expected');
+ });
+ on(target, 'done', function() {
+ assert.pass('event is emitted');
+ });
+ emit(target, 'foo')
+ emit(target, 'done');
+};
+
+exports['test all arguments are pasesd'] = function(assert) {
+ let foo = { name: 'foo' }, bar = 'bar';
+ let target = { name: 'target' };
+ on(target, 'message', function(a, b) {
+ assert.equal(a, foo, 'first argument passed');
+ assert.equal(b, bar, 'second argument passed');
+ });
+ emit(target, 'message', foo, bar);
+};
+
+exports['test no side-effects in emit'] = function(assert) {
+ let target = { name: 'target' };
+ on(target, 'message', function() {
+ assert.pass('first listener is called');
+ on(target, 'message', function() {
+ assert.fail('second listener is called');
+ });
+ });
+ emit(target, 'message');
+};
+
+exports['test order of propagation'] = function(assert) {
+ let actual = [];
+ let target = { name: 'target' };
+ on(target, 'message', function() { actual.push(1); });
+ on(target, 'message', function() { actual.push(2); });
+ on(target, 'message', function() { actual.push(3); });
+ emit(target, 'message');
+ assert.deepEqual([ 1, 2, 3 ], actual, 'called in order they were added');
+};
+
+exports['test remove a listener'] = function(assert) {
+ let target = { name: 'target' };
+ let actual = [];
+ on(target, 'message', function listener() {
+ actual.push(1);
+ on(target, 'message', function() {
+ off(target, 'message', listener);
+ actual.push(2);
+ })
+ });
+
+ emit(target, 'message');
+ assert.deepEqual([ 1 ], actual, 'first listener called');
+ emit(target, 'message');
+ assert.deepEqual([ 1, 1, 2 ], actual, 'second listener called');
+
+ emit(target, 'message');
+ assert.deepEqual([ 1, 1, 2, 2, 2 ], actual, 'first listener removed');
+};
+
+exports['test remove all listeners for type'] = function(assert) {
+ let actual = [];
+ let target = { name: 'target' }
+ on(target, 'message', function() { actual.push(1); });
+ on(target, 'message', function() { actual.push(2); });
+ on(target, 'message', function() { actual.push(3); });
+ on(target, 'bar', function() { actual.push('b') });
+ off(target, 'message');
+
+ emit(target, 'message');
+ emit(target, 'bar');
+
+ assert.deepEqual([ 'b' ], actual, 'all message listeners were removed');
+};
+
+exports['test remove all listeners'] = function(assert) {
+ let actual = [];
+ let target = { name: 'target' }
+ on(target, 'message', function() { actual.push(1); });
+ on(target, 'message', function() { actual.push(2); });
+ on(target, 'message', function() { actual.push(3); });
+ on(target, 'bar', function() { actual.push('b') });
+ off(target);
+
+ emit(target, 'message');
+ emit(target, 'bar');
+
+ assert.deepEqual([], actual, 'all listeners events were removed');
+};
+
+exports['test falsy arguments are fine'] = function(assert) {
+ let type, listener, actual = [];
+ let target = { name: 'target' }
+ on(target, 'bar', function() { actual.push(0) });
+
+ off(target, 'bar', listener);
+ emit(target, 'bar');
+ assert.deepEqual([ 0 ], actual, '3rd bad ard will keep listeners');
+
+ off(target, type);
+ emit(target, 'bar');
+ assert.deepEqual([ 0, 0 ], actual, '2nd bad arg will keep listener');
+
+ off(target, type, listener);
+ emit(target, 'bar');
+ assert.deepEqual([ 0, 0, 0 ], actual, '2nd&3rd bad args will keep listener');
+};
+
+exports['test error handling'] = function(assert) {
+ let target = Object.create(null);
+ let error = Error('boom!');
+
+ on(target, 'message', function() { throw error; })
+ on(target, 'error', function(boom) {
+ assert.equal(boom, error, 'thrown exception causes error event');
+ });
+ emit(target, 'message');
+};
+
+exports['test unhandled errors'] = function(assert) {
+ let exceptions = [];
+ let loader = Loader(module);
+ let { emit, on } = loader.require('api-utils/event/core');
+ Object.defineProperties(loader.sandbox('api-utils/event/core'), {
+ console: { value: {
+ exception: function(e) {
+ exceptions.push(e)
+ }
+ }}
+ });
+ let target = {};
+ let boom = Error('Boom!');
+ let drax = Error('Draax!!');
+
+ on(target, 'message', function() { throw boom; });
+
+ emit(target, 'message');
+ assert.ok(~String(exceptions[0]).indexOf('Boom!'),
+ 'unhandled exception is logged');
+
+ on(target, 'error', function() { throw drax; });
+ emit(target, 'message');
+ assert.ok(~String(exceptions[1]).indexOf('Draax!'),
+ 'error in error handler is logged');
+};
+
+exports['test count'] = function(assert) {
+ let target = {};
+
+ assert.equal(count(target, 'foo'), 0, 'no listeners for "foo" events');
+ on(target, 'foo', function() {});
+ assert.equal(count(target, 'foo'), 1, 'listener registered');
+ on(target, 'foo', function() {}, 2, 'another listener registered');
+ off(target)
+ assert.equal(count(target, 'foo'), 0, 'listeners unregistered');
+};
+
+exports['test emit.lazy'] = function(assert) {
+ let target = {}, boom = Error('boom!'), errors = [], actual = []
+
+ on(target, 'error', function error(e) errors.push(e))
+
+ on(target, 'a', function() 1);
+ on(target, 'a', function() {});
+ on(target, 'a', function() 2);
+ on(target, 'a', function() { throw boom });
+ on(target, 'a', function() 3);
+
+ for each (let value in emit.lazy(target, 'a'))
+ actual.push(value);
+
+ assert.deepEqual(actual, [ 1, undefined, 2, 3 ],
+ 'all results were collected');
+ assert.deepEqual(errors, [ boom ], 'errors reporetd');
+};
+
+require('test').run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-event-target.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-event-target.js
new file mode 100644
index 0000000..b8960e5
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-event-target.js
@@ -0,0 +1,167 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+const { emit } = require('api-utils/event/core');
+const { EventTarget } = require('api-utils/event/target');
+const { Loader } = require('./helpers');
+
+exports['test add a listener'] = function(assert) {
+ let events = [ { name: 'event#1' }, 'event#2' ];
+ let target = EventTarget.new();
+
+ target.on('message', function(message) {
+ assert.equal(this, target, 'this is a target object');
+ assert.equal(message, events.shift(), 'message is emitted event');
+ });
+
+ emit(target, 'message', events[0]);
+ emit(target, 'message', events[0]);
+};
+
+exports['test pass in listeners'] = function(assert) {
+ let actual = [ ];
+ let target = EventTarget.new({
+ onMessage: function onMessage(message) {
+ assert.equal(this, target, 'this is an event target');
+ actual.push(1);
+ },
+ onFoo: null,
+ onbla: function() {
+ assert.fail('`onbla` is not supposed to be called');
+ }
+ });
+ target.on('message', function(message) {
+ assert.equal(this, target, 'this is an event target');
+ actual.push(2);
+ });
+
+ emit(target, 'message');
+ emit(target, 'missing');
+
+ assert.deepEqual([ 1, 2 ], actual, 'all listeners trigerred in right order');
+};
+
+exports['test that listener is unique per type'] = function(assert) {
+ let actual = []
+ let target = EventTarget.new();
+ function listener() { actual.push(1) }
+ target.on('message', listener);
+ target.on('message', listener);
+ target.on('message', listener);
+ target.on('foo', listener);
+ target.on('foo', listener);
+
+ emit(target, 'message');
+ assert.deepEqual([ 1 ], actual, 'only one message listener added');
+ emit(target, 'foo');
+ assert.deepEqual([ 1, 1 ], actual, 'same listener added for other event');
+};
+
+exports['test event type matters'] = function(assert) {
+ let target = EventTarget.new();
+ target.on('message', function() {
+ assert.fail('no event is expected');
+ });
+ target.on('done', function() {
+ assert.pass('event is emitted');
+ });
+
+ emit(target, 'foo');
+ emit(target, 'done');
+};
+
+exports['test all arguments are pasesd'] = function(assert) {
+ let foo = { name: 'foo' }, bar = 'bar';
+ let target = EventTarget.new();
+ target.on('message', function(a, b) {
+ assert.equal(a, foo, 'first argument passed');
+ assert.equal(b, bar, 'second argument passed');
+ });
+ emit(target, 'message', foo, bar);
+};
+
+exports['test no side-effects in emit'] = function(assert) {
+ let target = EventTarget.new();
+ target.on('message', function() {
+ assert.pass('first listener is called');
+ target.on('message', function() {
+ assert.fail('second listener is called');
+ });
+ });
+ emit(target, 'message');
+};
+
+exports['test order of propagation'] = function(assert) {
+ let actual = [];
+ let target = EventTarget.new();
+ target.on('message', function() { actual.push(1); });
+ target.on('message', function() { actual.push(2); });
+ target.on('message', function() { actual.push(3); });
+ emit(target, 'message');
+ assert.deepEqual([ 1, 2, 3 ], actual, 'called in order they were added');
+};
+
+exports['test remove a listener'] = function(assert) {
+ let target = EventTarget.new();
+ let actual = [];
+ target.on('message', function listener() {
+ actual.push(1);
+ target.on('message', function() {
+ target.removeListener('message', listener);
+ actual.push(2);
+ })
+ });
+
+ target.removeListener('message'); // must do nothing.
+ emit(target, 'message');
+ assert.deepEqual([ 1 ], actual, 'first listener called');
+ emit(target, 'message');
+ assert.deepEqual([ 1, 1, 2 ], actual, 'second listener called');
+ emit(target, 'message');
+ assert.deepEqual([ 1, 1, 2, 2, 2 ], actual, 'first listener removed');
+};
+
+exports['test error handling'] = function(assert) {
+ let target = EventTarget.new();
+ let error = Error('boom!');
+
+ target.on('message', function() { throw error; })
+ target.on('error', function(boom) {
+ assert.equal(boom, error, 'thrown exception causes error event');
+ });
+ emit(target, 'message');
+};
+
+exports['test unhandled errors'] = function(assert) {
+ let exceptions = [];
+ let loader = Loader(module);
+ let { emit } = loader.require('api-utils/event/core');
+ let { EventTarget } = loader.require('api-utils/event/target');
+ Object.defineProperties(loader.sandbox('api-utils/event/core'), {
+ console: { value: {
+ exception: function(e) {
+ exceptions.push(e);
+ }
+ }}
+ });
+ let target = EventTarget.new();
+ let boom = Error('Boom!');
+ let drax = Error('Draax!!');
+
+ target.on('message', function() { throw boom; });
+
+ emit(target, 'message');
+ assert.ok(~String(exceptions[0]).indexOf('Boom!'),
+ 'unhandled exception is logged');
+
+ target.on('error', function() { throw drax; });
+ emit(target, 'message');
+ assert.ok(~String(exceptions[1]).indexOf('Draax!'),
+ 'error in error handler is logged');
+};
+
+require('test').run(exports);
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-events.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-events.js
new file mode 100644
index 0000000..3fe6f03
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-events.js
@@ -0,0 +1,267 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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';
+
+// Exposing private methods as public in order to test
+const EventEmitter = require('events').EventEmitter.compose({
+ listeners: function(type) this._listeners(type),
+ emit: function() this._emit.apply(this, arguments),
+ emitOnObject: function() this._emitOnObject.apply(this, arguments),
+ removeAllListeners: function(type) this._removeAllListeners(type)
+});
+
+exports['test:add listeners'] = function(test) {
+ let e = new EventEmitter();
+
+ let events_new_listener_emited = [];
+ let times_hello_emited = 0;
+
+ e.on("newListener", function (event, listener) {
+ events_new_listener_emited.push(event)
+ })
+
+ e.on("hello", function (a, b) {
+ times_hello_emited += 1
+ test.assertEqual("a", a)
+ test.assertEqual("b", b)
+ test.assertEqual(this, e, '`this` pseudo-variable is bound to instance');
+ })
+
+ e.emit("hello", "a", "b")
+};
+
+exports['test:removeListener'] = function(test) {
+ let count = 0;
+
+ function listener1 () {
+ count++;
+ }
+ function listener2 () {
+ count++;
+ }
+
+ // test adding and removing listener
+ let e1 = new EventEmitter();
+ test.assertEqual(0, e1.listeners('hello').length);
+ e1.on("hello", listener1);
+ test.assertEqual(1, e1.listeners('hello').length);
+ test.assertEqual(listener1, e1.listeners('hello')[0]);
+ e1.removeListener("hello", listener1);
+ test.assertEqual(0, e1.listeners('hello').length);
+ e1.emit("hello", "");
+ test.assertEqual(0, count);
+
+ // test adding one listener and removing another which was not added
+ let e2 = new EventEmitter();
+ test.assertEqual(0, e2.listeners('hello').length);
+ e2.on("hello", listener1);
+ test.assertEqual(1, e2.listeners('hello').length);
+ e2.removeListener("hello", listener2);
+ test.assertEqual(1, e2.listeners('hello').length);
+ test.assertEqual(listener1, e2.listeners('hello')[0]);
+ e2.emit("hello", "");
+ test.assertEqual(1, count);
+
+ // test adding 2 listeners, and removing one
+ let e3 = new EventEmitter();
+ test.assertEqual(0, e3.listeners('hello').length);
+ e3.on("hello", listener1);
+ test.assertEqual(1, e3.listeners('hello').length);
+ e3.on("hello", listener2);
+ test.assertEqual(2, e3.listeners('hello').length);
+ e3.removeListener("hello", listener1);
+ test.assertEqual(1, e3.listeners('hello').length);
+ test.assertEqual(listener2, e3.listeners('hello')[0]);
+ e3.emit("hello", "");
+ test.assertEqual(2, count);
+};
+
+exports['test:removeAllListeners'] = function(test) {
+ let count = 0;
+
+ function listener1 () {
+ count++;
+ }
+ function listener2 () {
+ count++;
+ }
+
+ // test adding a listener and removing all of that type
+ let e1 = new EventEmitter();
+ e1.on("hello", listener1);
+ test.assertEqual(1, e1.listeners('hello').length);
+ e1.removeAllListeners("hello");
+ test.assertEqual(0, e1.listeners('hello').length);
+ e1.emit("hello", "");
+ test.assertEqual(0, count);
+
+ // test adding a listener and removing all of another type
+ let e2 = new EventEmitter();
+ e2.on("hello", listener1);
+ test.assertEqual(1, e2.listeners('hello').length);
+ e2.removeAllListeners('goodbye');
+ test.assertEqual(1, e2.listeners('hello').length);
+ e2.emit("hello", "");
+ test.assertEqual(1, count);
+
+ // test adding 1+ listeners and removing all of that type
+ let e3 = new EventEmitter();
+ e3.on("hello", listener1);
+ test.assertEqual(1, e3.listeners('hello').length);
+ e3.on("hello", listener2);
+ test.assertEqual(2, e3.listeners('hello').length);
+ e3.removeAllListeners("hello");
+ test.assertEqual(0, e3.listeners('hello').length);
+ e3.emit("hello", "");
+ test.assertEqual(1, count);
+
+ // test adding 2 listeners for 2 types and removing all listeners
+ let e4 = new EventEmitter();
+ e4.on("hello", listener1);
+ test.assertEqual(1, e4.listeners('hello').length);
+ e4.on('goodbye', listener2);
+ test.assertEqual(1, e4.listeners('goodbye').length);
+ e4.emit("goodbye", "");
+ e4.removeAllListeners();
+ test.assertEqual(0, e4.listeners('hello').length);
+ test.assertEqual(0, e4.listeners('goodbye').length);
+ e4.emit("hello", "");
+ e4.emit("goodbye", "");
+ test.assertEqual(2, count);
+};
+
+exports['test: modify in emit'] = function(test) {
+ let callbacks_called = [ ];
+ let e = new EventEmitter();
+
+ function callback1() {
+ callbacks_called.push("callback1");
+ e.on("foo", callback2);
+ e.on("foo", callback3);
+ e.removeListener("foo", callback1);
+ }
+ function callback2() {
+ callbacks_called.push("callback2");
+ e.removeListener("foo", callback2);
+ }
+ function callback3() {
+ callbacks_called.push("callback3");
+ e.removeListener("foo", callback3);
+ }
+
+ e.on("foo", callback1);
+ test.assertEqual(1, e.listeners("foo").length);
+
+ e.emit("foo");
+ test.assertEqual(2, e.listeners("foo").length);
+ test.assertEqual(1, callbacks_called.length);
+ test.assertEqual('callback1', callbacks_called[0]);
+
+ e.emit("foo");
+ test.assertEqual(0, e.listeners("foo").length);
+ test.assertEqual(3, callbacks_called.length);
+ test.assertEqual('callback1', callbacks_called[0]);
+ test.assertEqual('callback2', callbacks_called[1]);
+ test.assertEqual('callback3', callbacks_called[2]);
+
+ e.emit("foo");
+ test.assertEqual(0, e.listeners("foo").length);
+ test.assertEqual(3, callbacks_called.length);
+ test.assertEqual('callback1', callbacks_called[0]);
+ test.assertEqual('callback2', callbacks_called[1]);
+ test.assertEqual('callback3', callbacks_called[2]);
+
+ e.on("foo", callback1);
+ e.on("foo", callback2);
+ test.assertEqual(2, e.listeners("foo").length);
+ e.removeAllListeners("foo");
+ test.assertEqual(0, e.listeners("foo").length);
+
+ // Verify that removing callbacks while in emit allows emits to propagate to
+ // all listeners
+ callbacks_called = [ ];
+
+ e.on("foo", callback2);
+ e.on("foo", callback3);
+ test.assertEqual(2, e.listeners("foo").length);
+ e.emit("foo");
+ test.assertEqual(2, callbacks_called.length);
+ test.assertEqual('callback2', callbacks_called[0]);
+ test.assertEqual('callback3', callbacks_called[1]);
+ test.assertEqual(0, e.listeners("foo").length);
+};
+
+exports['test:adding same listener'] = function(test) {
+ function foo() {}
+ let e = new EventEmitter();
+ e.on("foo", foo);
+ e.on("foo", foo);
+ test.assertEqual(
+ 1,
+ e.listeners("foo").length,
+ "listener reregistration is ignored"
+ );
+}
+
+exports['test:errors are reported if listener throws'] = function(test) {
+ let e = new EventEmitter(),
+ reported = false;
+ e.on('error', function(e) reported = true)
+ e.on('boom', function() { throw new Error('Boom!') });
+ e.emit('boom', 3);
+ test.assert(reported, 'error should be reported through event');
+};
+
+exports['test:emitOnObject'] = function(test) {
+ let e = new EventEmitter();
+
+ e.on("foo", function() {
+ test.assertEqual(this, e, "`this` should be emitter");
+ });
+ e.emitOnObject(e, "foo");
+
+ e.on("bar", function() {
+ test.assertEqual(this, obj, "`this` should be other object");
+ });
+ let obj = {};
+ e.emitOnObject(obj, "bar");
+};
+
+exports['test:once'] = function(test) {
+ let e = new EventEmitter();
+ let called = false;
+
+ e.once('foo', function(value) {
+ test.assert(!called, "listener called only once");
+ test.assertEqual(value, "bar", "correct argument was passed");
+ });
+
+ e.emit('foo', 'bar');
+ e.emit('foo', 'baz');
+};
+
+exports["test:removing once"] = function(test) {
+ let e = require("events").EventEmitterTrait.create();
+ e.once("foo", function() { test.pass("listener was called"); });
+ e.once("error", function() { test.fail("error event was emitted"); });
+ e._emit("foo", "bug-656684");
+};
+
+// Bug 726967: Ensure that `emit` doesn't do an infinite loop when `error`
+// listener throws an exception
+exports['test:emitLoop'] = function(test) {
+ let e = new EventEmitter();
+
+ e.on("foo", function() {
+ throw new Error("foo");
+ });
+
+ e.on("error", function() {
+ throw new Error("error");
+ });
+ e.emit("foo");
+
+ test.pass("emit didn't looped");
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-file.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-file.js
new file mode 100644
index 0000000..de89db8
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-file.js
@@ -0,0 +1,273 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { pathFor } = require('api-utils/system');
+const file = require("api-utils/file");
+const url = require("api-utils/url");
+
+const byteStreams = require("api-utils/byte-streams");
+const textStreams = require("api-utils/text-streams");
+
+const ERRORS = {
+ FILE_NOT_FOUND: /^path does not exist: .+$/,
+ NOT_A_DIRECTORY: /^path is not a directory: .+$/,
+ NOT_A_FILE: /^path is not a file: .+$/,
+};
+
+// Use profile directory to list / read / write files.
+const profilePath = pathFor('ProfD');
+const fileNameInProfile = 'compatibility.ini';
+const dirNameInProfile = 'extensions';
+const filePathInProfile = file.join(profilePath, fileNameInProfile);
+const dirPathInProfile = file.join(profilePath, dirNameInProfile);
+
+exports.testDirName = function(test) {
+ test.assertEqual(file.dirname(dirPathInProfile), profilePath,
+ "file.dirname() of dir should return parent dir");
+
+ test.assertEqual(file.dirname(filePathInProfile), profilePath,
+ "file.dirname() of file should return its dir");
+
+ let dir = profilePath;
+ while (dir)
+ dir = file.dirname(dir);
+
+ test.assertEqual(dir, "",
+ "dirname should return empty string when dir has no parent");
+};
+
+exports.testBasename = function(test) {
+ // Get the top-most path -- the path with no basename. E.g., on Unix-like
+ // systems this will be /. We'll use it below to build up some test paths.
+ // We have to go to this trouble because file.join() needs a legal path as a
+ // base case; join("foo", "bar") doesn't work unfortunately.
+ let topPath = profilePath;
+ let parentPath = file.dirname(topPath);
+ while (parentPath) {
+ topPath = parentPath;
+ parentPath = file.dirname(topPath);
+ }
+
+ let path = topPath;
+ test.assertEqual(file.basename(path), "",
+ "basename should work on paths with no components");
+
+ path = file.join(path, "foo");
+ test.assertEqual(file.basename(path), "foo",
+ "basename should work on paths with a single component");
+
+ path = file.join(path, "bar");
+ test.assertEqual(file.basename(path), "bar",
+ "basename should work on paths with multiple components");
+};
+
+exports.testList = function(test) {
+ let list = file.list(profilePath);
+ let found = [ true for each (name in list)
+ if (name === fileNameInProfile) ];
+
+ if (found.length > 1)
+ test.fail("a dir can't contain two files of the same name!");
+ test.assertEqual(found[0], true, "file.list() should work");
+
+ test.assertRaises(function() {
+ file.list(filePathInProfile);
+ }, ERRORS.NOT_A_DIRECTORY, "file.list() on non-dir should raise error");
+
+ test.assertRaises(function() {
+ file.list(file.join(dirPathInProfile, "does-not-exist"));
+ }, ERRORS.FILE_NOT_FOUND, "file.list() on nonexistent should raise error");
+};
+
+exports.testRead = function(test) {
+ let contents = file.read(filePathInProfile);
+ test.assertMatches(contents, /Compatibility/,
+ "file.read() should work");
+
+ test.assertRaises(function() {
+ file.read(file.join(dirPathInProfile, "does-not-exists"));
+ }, ERRORS.FILE_NOT_FOUND, "file.read() on nonexistent file should throw");
+
+ test.assertRaises(function() {
+ file.read(dirPathInProfile);
+ }, ERRORS.NOT_A_FILE, "file.read() on dir should throw");
+};
+
+exports.testJoin = function(test) {
+ let baseDir = file.dirname(filePathInProfile);
+
+ test.assertEqual(file.join(baseDir, fileNameInProfile),
+ filePathInProfile, "file.join() should work");
+};
+
+exports.testOpenNonexistentForRead = function (test) {
+ var filename = file.join(profilePath, 'does-not-exists');
+ test.assertRaises(function() {
+ file.open(filename);
+ }, ERRORS.FILE_NOT_FOUND, "file.open() on nonexistent file should throw");
+
+ test.assertRaises(function() {
+ file.open(filename, "r");
+ }, ERRORS.FILE_NOT_FOUND, "file.open('r') on nonexistent file should throw");
+
+ test.assertRaises(function() {
+ file.open(filename, "zz");
+ }, ERRORS.FILE_NOT_FOUND, "file.open('zz') on nonexistent file should throw");
+};
+
+exports.testOpenNonexistentForWrite = function (test) {
+ let filename = file.join(profilePath, 'open.txt');
+
+ let stream = file.open(filename, "w");
+ stream.close();
+
+ test.assert(file.exists(filename),
+ "file.exists() should return true after file.open('w')");
+ file.remove(filename);
+ test.assert(!file.exists(filename),
+ "file.exists() should return false after file.remove()");
+
+ stream = file.open(filename, "rw");
+ stream.close();
+
+ test.assert(file.exists(filename),
+ "file.exists() should return true after file.open('rw')");
+ file.remove(filename);
+ test.assert(!file.exists(filename),
+ "file.exists() should return false after file.remove()");
+};
+
+exports.testOpenDirectory = function (test) {
+ let dir = dirPathInProfile;
+ test.assertRaises(function() {
+ file.open(dir);
+ }, ERRORS.NOT_A_FILE, "file.open() on directory should throw");
+
+ test.assertRaises(function() {
+ file.open(dir, "w");
+ }, ERRORS.NOT_A_FILE, "file.open('w') on directory should throw");
+};
+
+exports.testOpenTypes = function (test) {
+ let filename = file.join(profilePath, 'open-types.txt');
+
+
+ // Do the opens first to create the data file.
+ var stream = file.open(filename, "w");
+ test.assert(stream instanceof textStreams.TextWriter,
+ "open(w) should return a TextWriter");
+ stream.close();
+
+ stream = file.open(filename, "wb");
+ test.assert(stream instanceof byteStreams.ByteWriter,
+ "open(wb) should return a ByteWriter");
+ stream.close();
+
+ stream = file.open(filename);
+ test.assert(stream instanceof textStreams.TextReader,
+ "open() should return a TextReader");
+ stream.close();
+
+ stream = file.open(filename, "r");
+ test.assert(stream instanceof textStreams.TextReader,
+ "open(r) should return a TextReader");
+ stream.close();
+
+ stream = file.open(filename, "b");
+ test.assert(stream instanceof byteStreams.ByteReader,
+ "open(b) should return a ByteReader");
+ stream.close();
+
+ stream = file.open(filename, "rb");
+ test.assert(stream instanceof byteStreams.ByteReader,
+ "open(rb) should return a ByteReader");
+ stream.close();
+
+ file.remove(filename);
+};
+
+exports.testMkpathRmdir = function (test) {
+ let basePath = profilePath;
+ let dirs = [];
+ for (let i = 0; i < 3; i++)
+ dirs.push("test-file-dir");
+
+ let paths = [];
+ for (let i = 0; i < dirs.length; i++) {
+ let args = [basePath].concat(dirs.slice(0, i + 1));
+ paths.unshift(file.join.apply(null, args));
+ }
+
+ for (let i = 0; i < paths.length; i++) {
+ test.assert(!file.exists(paths[i]),
+ "Sanity check: path should not exist: " + paths[i]);
+ }
+
+ file.mkpath(paths[0]);
+ test.assert(file.exists(paths[0]), "mkpath should create path: " + paths[0]);
+
+ for (let i = 0; i < paths.length; i++) {
+ file.rmdir(paths[i]);
+ test.assert(!file.exists(paths[i]),
+ "rmdir should remove path: " + paths[i]);
+ }
+};
+
+exports.testMkpathTwice = function (test) {
+ let dir = profilePath;
+ let path = file.join(dir, "test-file-dir");
+ test.assert(!file.exists(path),
+ "Sanity check: path should not exist: " + path);
+ file.mkpath(path);
+ test.assert(file.exists(path), "mkpath should create path: " + path);
+ file.mkpath(path);
+ test.assert(file.exists(path),
+ "After second mkpath, path should still exist: " + path);
+ file.rmdir(path);
+ test.assert(!file.exists(path), "rmdir should remove path: " + path);
+};
+
+exports.testMkpathExistingNondirectory = function (test) {
+ var fname = file.join(profilePath, 'conflict.txt');
+ file.open(fname, "w").close();
+ test.assert(file.exists(fname), "File should exist");
+ test.assertRaises(function() file.mkpath(fname),
+ /^The path already exists and is not a directory: .+$/,
+ "mkpath on file should raise error");
+ file.remove(fname);
+};
+
+exports.testRmdirNondirectory = function (test) {
+ var fname = file.join(profilePath, 'not-a-dir')
+ file.open(fname, "w").close();
+ test.assert(file.exists(fname), "File should exist");
+ test.assertRaises(function() {
+ file.rmdir(fname);
+ }, ERRORS.NOT_A_DIRECTORY, "rmdir on file should raise error");
+ file.remove(fname);
+ test.assert(!file.exists(fname), "File should not exist");
+ test.assertRaises(function () file.rmdir(fname),
+ ERRORS.FILE_NOT_FOUND,
+ "rmdir on non-existing file should raise error");
+};
+
+exports.testRmdirNonempty = function (test) {
+ let dir = profilePath;
+ let path = file.join(dir, "test-file-dir");
+ test.assert(!file.exists(path),
+ "Sanity check: path should not exist: " + path);
+ file.mkpath(path);
+ let filePath = file.join(path, "file");
+ file.open(filePath, "w").close();
+ test.assert(file.exists(filePath),
+ "Sanity check: path should exist: " + filePath);
+ test.assertRaises(function () file.rmdir(path),
+ /^The directory is not empty: .+$/,
+ "rmdir on non-empty directory should raise error");
+ file.remove(filePath);
+ file.rmdir(path);
+ test.assert(!file.exists(path), "Path should not exist");
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-frame-utils.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-frame-utils.js
new file mode 100644
index 0000000..268643c
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-frame-utils.js
@@ -0,0 +1,67 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+const { open } = require('api-utils/window/utils');
+const { create } = require('api-utils/frame/utils');
+
+exports['test frame creation'] = function(assert) {
+ let window = open('data:text/html,Window');
+ let frame = create(window.document);
+
+ assert.equal(frame.getAttribute('type'), 'content',
+ 'frame type is content');
+ assert.ok(frame.contentWindow, 'frame has contentWindow');
+ assert.equal(frame.contentWindow.location.href, 'about:blank',
+ 'by default "about:blank" is loaded');
+ assert.equal(frame.docShell.allowAuth, false, 'auth disabled by default');
+ assert.equal(frame.docShell.allowJavascript, false, 'js disabled by default');
+ assert.equal(frame.docShell.allowPlugins, false,
+ 'plugins disabled by default');
+ window.close();
+};
+
+exports['test fram has js disabled by default'] = function(assert, done) {
+ let window = open('data:text/html,window');
+ window.addEventListener('DOMContentLoaded', function windowReady() {
+ window.removeEventListener('DOMContentLoaded', windowReady, false);
+ let frame = create(window.document, {
+ uri: 'data:text/html,<script>document.documentElement.innerHTML' +
+ '= "J" + "S"</script>',
+ });
+ frame.contentWindow.addEventListener('DOMContentLoaded', function ready() {
+ frame.contentWindow.removeEventListener('DOMContentLoaded', ready, false);
+ assert.ok(!~frame.contentDocument.documentElement.innerHTML.indexOf('JS'),
+ 'JS was executed');
+
+ window.close();
+ done();
+ }, false);
+
+ }, false);
+};
+
+exports['test frame with js enabled'] = function(assert, done) {
+ let window = open('data:text/html,window');
+ window.addEventListener('DOMContentLoaded', function windowReady() {
+ window.removeEventListener('DOMContentLoaded', windowReady, false);
+ let frame = create(window.document, {
+ uri: 'data:text/html,<script>document.documentElement.innerHTML' +
+ '= "J" + "S"</script>',
+ allowJavascript: true
+ });
+ frame.contentWindow.addEventListener('DOMContentLoaded', function ready() {
+ frame.contentWindow.removeEventListener('DOMContentLoaded', ready, false);
+ assert.ok(~frame.contentDocument.documentElement.innerHTML.indexOf('JS'),
+ 'JS was executed');
+
+ window.close();
+ done();
+ }, false);
+
+ }, false);
+};
+
+require('test').run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-functional.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-functional.js
new file mode 100644
index 0000000..377a709
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-functional.js
@@ -0,0 +1,170 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { setTimeout } = require('api-utils/timer');
+const utils = require('api-utils/functional');
+const { invoke, defer, curry, compose, memoize, once, delay, wrap } = utils;
+
+exports['test forwardApply'] = function(assert) {
+ function sum(b, c) this.a + b + c
+ assert.equal(invoke(sum, [2, 3], { a: 1 }), 6,
+ 'passed arguments and pseoude-variable are used');
+
+ assert.equal(invoke(sum.bind({ a: 2 }), [2, 3], { a: 1 }), 7,
+ 'bounded `this` pseoudo variable is used');
+}
+
+exports['test deferred function'] = function(assert, done) {
+ let nextTurn = false;
+ function sum(b, c) {
+ assert.ok(nextTurn, 'enqueued is called in next turn of event loop');
+ assert.equal(this.a + b + c, 6,
+ 'passed arguments an pseoude-variable are used');
+ done();
+ }
+
+ let fixture = { a: 1, method: defer(sum) }
+ fixture.method(2, 3);
+ nextTurn = true;
+};
+
+exports['test curry function'] = function(assert) {
+ function sum(b, c) this.a + b + c;
+
+ let foo = { a : 5 };
+
+ foo.sum7 = curry(sum, 7);
+ foo.sum8and4 = curry(sum, 8, 4);
+
+ assert.equal(foo.sum7(2), 14, 'curry one arguments works');
+
+ assert.equal(foo.sum8and4(), 17, 'curry both arguments works');
+};
+
+exports['test compose'] = function(assert) {
+ let greet = function(name) { return 'hi: ' + name; };
+ let exclaim = function(sentence) { return sentence + '!'; };
+
+ assert.equal(compose(exclaim, greet)('moe'), 'hi: moe!',
+ 'can compose a function that takes another');
+
+ assert.equal(compose(greet, exclaim)('moe'), 'hi: moe!',
+ 'in this case, the functions are also commutative');
+
+ let target = {
+ name: 'Joe',
+ greet: compose(function exclaim(sentence) {
+ return sentence + '!'
+ }, function(title) {
+ return 'hi : ' + title + ' ' + this.name;
+ })
+ }
+
+ assert.equal(target.greet('Mr'), 'hi : Mr Joe!',
+ 'this can be passed in');
+ assert.equal(target.greet.call({ name: 'Alex' }, 'Dr'), 'hi : Dr Alex!',
+ 'this can be applied');
+
+ let single = compose(function(value) {
+ return value + ':suffix';
+ });
+
+ assert.equal(single('text'), 'text:suffix', 'works with single function');
+
+ let identity = compose();
+ assert.equal(identity('bla'), 'bla', 'works with zero functions');
+};
+
+exports['test wrap'] = function(assert) {
+ let greet = function(name) { return 'hi: ' + name; };
+ let backwards = wrap(greet, function(f, name) {
+ return f(name) + ' ' + name.split('').reverse().join('');
+ });
+
+ assert.equal(backwards('moe'), 'hi: moe eom',
+ 'wrapped the saluation function');
+
+ let inner = function () { return 'Hello '; };
+ let target = {
+ name: 'Matteo',
+ hi: wrap(inner, function(f) { return f() + this.name; })
+ };
+
+ assert.equal(target.hi(), 'Hello Matteo', 'works with this');
+
+ function noop() { };
+ let wrapped = wrap(noop, function(f) {
+ return Array.slice(arguments);
+ });
+
+ let actual = wrapped([ 'whats', 'your' ], 'vector', 'victor');
+ assert.deepEqual(actual, [ noop, ['whats', 'your'], 'vector', 'victor' ],
+ 'works with fancy stuff');
+};
+
+exports['test memoize'] = function(assert) {
+ function fib(n) n < 2 ? n : fib(n - 1) + fib(n - 2)
+ let fibnitro = memoize(fib);
+
+ assert.equal(fib(10), 55,
+ 'a memoized version of fibonacci produces identical results');
+ assert.equal(fibnitro(10), 55,
+ 'a memoized version of fibonacci produces identical results');
+
+ function o(key, value) { return value; };
+ let oo = memoize(o), v1 = {}, v2 = {};
+
+
+ assert.equal(oo(1, v1), v1, 'returns value back');
+ assert.equal(oo(1, v2), v1, 'memoized by a first argument');
+ assert.equal(oo(2, v2), v2, 'returns back value if not memoized');
+ assert.equal(oo(2), v2, 'memoized new value');
+ assert.notEqual(oo(1), oo(2), 'values do not override');
+ assert.equal(o(3, v2), oo(2, 3), 'returns same value as un-memoized');
+
+ let get = memoize(function(attribute) this[attribute])
+ let target = { name: 'Bob', get: get }
+
+ assert.equal(target.get('name'), 'Bob', 'has correct `this`');
+ assert.equal(target.get.call({ name: 'Jack' }, 'name'), 'Bob',
+ 'name is memoized')
+ assert.equal(get('name'), 'Bob', 'once memoized can be called without this');
+};
+
+exports['test delay'] = function(assert, done) {
+ let delayed = false;
+ delay(function() {
+ assert.ok(delayed, 'delayed the function');
+ done();
+ }, 1);
+ delayed = true;
+};
+
+exports['test delay with this'] = function(assert, done) {
+ let context = {}
+ delay.call(context, function(name) {
+ assert.equal(this, context, 'this was passed in');
+ assert.equal(name, 'Tom', 'argument was passed in');
+ done();
+ }, 10, 'Tom');
+}
+
+exports['test once'] = function(assert) {
+ let n = 0;
+ let increment = once(function() { n ++; });
+
+ increment();
+ increment();
+
+ assert.equal(n, 1, 'only incremented once');
+
+ let target = { state: 0, update: once(function() this.state ++ ) };
+
+ target.update();
+ target.update();
+
+ assert.equal(target.state, 1, 'this was passed in and called only once');
+};
+
+require('test').run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-globals.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-globals.js
new file mode 100644
index 0000000..1d8caf0
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-globals.js
@@ -0,0 +1,22 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+Object.defineProperty(this, "global", { value: this });
+
+exports.testGlobals = function(test) {
+ // the only globals in module scope should be:
+ // module, exports, require, dump, console
+ test.assertObject(module, "have 'module', good");
+ test.assertObject(exports, "have 'exports', good");
+ test.assertFunction(require, "have 'require', good");
+ test.assertFunction(dump, "have 'dump', good");
+ test.assertObject(console, "have 'console', good");
+
+ // in particular, these old globals should no longer be present
+ test.assert(!('packaging' in global), "no 'packaging', good");
+ test.assert(!('memory' in global), "no 'memory', good");
+
+ test.assertMatches(module.uri, /test-globals\.js$/,
+ 'should contain filename');
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-hidden-frame.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-hidden-frame.js
new file mode 100644
index 0000000..2441c3e
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-hidden-frame.js
@@ -0,0 +1,51 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 = {}, hiddenFrames, HiddenFrame;
+
+tests.testFrame = function(test) {
+ let url = "data:text/html,<!DOCTYPE%20html>";
+ test.waitUntilDone();
+ let hiddenFrame = hiddenFrames.add(HiddenFrame({
+ onReady: function () {
+ test.assertEqual(this.element.contentWindow.location, "about:blank",
+ "HiddenFrame loads about:blank by default.");
+
+ function onDOMReady() {
+ hiddenFrame.element.removeEventListener("DOMContentLoaded", onDOMReady,
+ false);
+ test.assertEqual(hiddenFrame.element.contentWindow.location, url,
+ "HiddenFrame loads the specified content.");
+ test.done();
+ }
+ this.element.addEventListener("DOMContentLoaded", onDOMReady, false);
+ this.element.setAttribute("src", url);
+ }
+ }));
+};
+
+let hiddenFrameSupported = true;
+
+try {
+ hiddenFrames = require("hidden-frame");
+ HiddenFrame = hiddenFrames.HiddenFrame;
+}
+catch(ex if ex.message == [
+ "The hidden-frame 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("")) {
+ hiddenFrameSupported = false;
+}
+
+if (hiddenFrameSupported) {
+ for (let test in tests)
+ exports[test] = tests[test];
+}
+else {
+ exports.testHiddenFrameNotSupported = function(test) {
+ test.pass("The hidden-frame module is not supported on this app.");
+ }
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-httpd.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-httpd.js
new file mode 100644
index 0000000..dc1adf1
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-httpd.js
@@ -0,0 +1,72 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const port = 8099;
+const file = require("api-utils/file");
+const { pathFor } = require("api-utils/system");
+
+exports.testBasicHTTPServer = function(test) {
+ let basePath = pathFor("TmpD");
+ let filePath = file.join(basePath, 'test-httpd.txt');
+ let content = "This is the HTTPD test file.\n";
+ let fileStream = file.open(filePath, 'w');
+ fileStream.write(content);
+ fileStream.close();
+
+ let { startServerAsync } = require("httpd");
+ let srv = startServerAsync(port, basePath);
+
+ test.waitUntilDone();
+
+ // Request this very file.
+ let Request = require('request').Request;
+ Request({
+ url: "http://localhost:" + port + "/test-httpd.txt",
+ onComplete: function (response) {
+ test.assertEqual(response.text, content);
+ done();
+ }
+ }).get();
+
+ function done() {
+ srv.stop(function() {
+ test.done();
+ });
+ }
+};
+
+exports.testDynamicServer = function (test) {
+ let content = "This is the HTTPD test file.\n";
+
+ let { startServerAsync } = require("httpd");
+ let srv = startServerAsync(port);
+
+ // See documentation here:
+ //http://doxygen.db48x.net/mozilla/html/interfacensIHttpServer.html#a81fc7e7e29d82aac5ce7d56d0bedfb3a
+ //http://doxygen.db48x.net/mozilla/html/interfacensIHttpRequestHandler.html
+ srv.registerPathHandler("/test-httpd.txt", function handle(request, response) {
+ // Add text content type, only to avoid error in `Request` API
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write(content);
+ });
+
+ test.waitUntilDone();
+
+ // Request this very file.
+ let Request = require('request').Request;
+ Request({
+ url: "http://localhost:" + port + "/test-httpd.txt",
+ onComplete: function (response) {
+ test.assertEqual(response.text, content);
+ done();
+ }
+ }).get();
+
+ function done() {
+ srv.stop(function() {
+ test.done();
+ });
+ }
+
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-keyboard-observer.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-keyboard-observer.js
new file mode 100644
index 0000000..584f01b
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-keyboard-observer.js
@@ -0,0 +1,36 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { keyPress } = require("api-utils/dom/events/keys");
+const { Loader } = require("./helpers");
+
+exports["test unload keyboard observer"] = function(assert, done) {
+ let loader = Loader(module);
+ let element = loader.require("api-utils/window-utils").
+ activeBrowserWindow.document.documentElement;
+ let observer = loader.require("api-utils/keyboard/observer").
+ observer;
+ let called = 0;
+
+ observer.on("keypress", function () { called++; });
+
+ // dispatching "keypress" event to trigger observer listeners.
+ keyPress(element, "accel-%");
+
+ // Unload the module.
+ loader.unload();
+
+ // dispatching "keypress" even once again.
+ keyPress(element, "accel-%");
+
+ // Enqueuing asserts to make sure that assertion is not performed early.
+ require("timer").setTimeout(function () {
+ assert.equal(called, 1, "observer was called before unload only.");
+ done();
+ }, 0);
+};
+
+require("test").run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-keyboard-utils.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-keyboard-utils.js
new file mode 100644
index 0000000..1146a7b
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-keyboard-utils.js
@@ -0,0 +1,62 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const utils = require("keyboard/utils");
+const runtime = require("runtime");
+
+const isMac = runtime.OS === "Darwin";
+
+exports["test toString"] = function(assert) {
+ assert.equal(utils.toString({
+ key: "B",
+ modifiers: [ "Shift", "Ctrl" ]
+ }), "Shift-Ctrl-B", "toString does not normalizes JSON");
+
+ assert.equal(utils.toString({
+ key: "C",
+ modifiers: [],
+ }), "C", "Works with objects with empty array of modifiers");
+
+ assert.equal(utils.toString(Object.create((function Type() {}).prototype, {
+ key: { value: "d" },
+ modifiers: { value: [ "alt" ] },
+ method: { value: function() {} }
+ })), "alt-d", "Works with non-json objects");
+
+ assert.equal(utils.toString({
+ modifiers: [ "shift", "alt" ]
+ }), "shift-alt-", "works with only modifiers");
+};
+
+exports["test toJSON"] = function(assert) {
+ assert.deepEqual(utils.toJSON("Shift-Ctrl-B"), {
+ key: "b",
+ modifiers: [ "control", "shift" ]
+ }, "toJSON normalizes input");
+
+ assert.deepEqual(utils.toJSON("Meta-Alt-option-C"), {
+ key: "c",
+ modifiers: [ "alt", "meta" ]
+ }, "removes dublicates");
+
+ assert.deepEqual(utils.toJSON("AccEl+sHiFt+Z", "+"), {
+ key: "z",
+ modifiers: isMac ? [ "meta", "shift" ] : [ "control", "shift" ]
+ }, "normalizes OS specific keys and adjustes seperator");
+};
+
+exports["test normalize"] = function assert(assert) {
+ assert.equal(utils.normalize("Shift Ctrl A control ctrl", " "),
+ "control shift a", "removes reapeted modifiers");
+ assert.equal(utils.normalize("shift-ctrl-left"), "control-shift-left",
+ "normilizes non printed characters");
+
+ assert.throws(function() {
+ utils.normalize("shift-alt-b-z");
+ }, "throws if contains more then on non-modifier key");
+};
+
+require("test").run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-l10n-locale.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-l10n-locale.js
new file mode 100644
index 0000000..db559ba
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-l10n-locale.js
@@ -0,0 +1,141 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { getPreferedLocales, findClosestLocale } = require("api-utils/l10n/locale");
+const prefs = require("preferences-service");
+const { Cc, Ci, Cu } = require("chrome");
+const { Services } = Cu.import("resource://gre/modules/Services.jsm");
+const BundleService = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
+
+const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
+const PREF_SELECTED_LOCALE = "general.useragent.locale";
+const PREF_ACCEPT_LANGUAGES = "intl.accept_languages";
+
+function assertPrefered(test, expected, msg) {
+ test.assertEqual(JSON.stringify(getPreferedLocales()), JSON.stringify(expected),
+ msg);
+}
+
+exports.testGetPreferedLocales = function(test) {
+ prefs.set(PREF_MATCH_OS_LOCALE, false);
+ prefs.set(PREF_SELECTED_LOCALE, "");
+ prefs.set(PREF_ACCEPT_LANGUAGES, "");
+ assertPrefered(test, ["en-us"],
+ "When all preferences are empty, we only have en-us");
+
+ prefs.set(PREF_SELECTED_LOCALE, "fr");
+ prefs.set(PREF_ACCEPT_LANGUAGES, "jp");
+ assertPrefered(test, ["fr", "jp", "en-us"],
+ "We first have useragent locale, then web one and finally en-US");
+
+ prefs.set(PREF_SELECTED_LOCALE, "en-US");
+ prefs.set(PREF_ACCEPT_LANGUAGES, "en-US");
+ assertPrefered(test, ["en-us"],
+ "We do not have duplicates");
+
+ prefs.set(PREF_SELECTED_LOCALE, "en-US");
+ prefs.set(PREF_ACCEPT_LANGUAGES, "fr");
+ assertPrefered(test, ["en-us", "fr"],
+ "en-US can be first if specified by higher priority preference");
+
+ // Reset what we changed
+ prefs.reset(PREF_MATCH_OS_LOCALE);
+ prefs.reset(PREF_SELECTED_LOCALE);
+ prefs.reset(PREF_ACCEPT_LANGUAGES);
+}
+
+// In some cases, mainly on Fennec and on Linux version,
+// `general.useragent.locale` is a special 'localized' value, like:
+// "chrome://global/locale/intl.properties"
+exports.testPreferedLocalizedLocale = function(test) {
+ prefs.set(PREF_MATCH_OS_LOCALE, false);
+ let bundleURL = "chrome://global/locale/intl.properties";
+ prefs.setLocalized(PREF_SELECTED_LOCALE, bundleURL);
+ let contentLocale = "ja";
+ prefs.set(PREF_ACCEPT_LANGUAGES, contentLocale);
+
+ // Read manually the expected locale value from the property file
+ let expectedLocale = BundleService.createBundle(bundleURL).
+ GetStringFromName(PREF_SELECTED_LOCALE).
+ toLowerCase();
+
+ // First add the useragent locale
+ let expectedLocaleList = [expectedLocale];
+
+ // Then the content locale
+ if (expectedLocaleList.indexOf(contentLocale) == -1)
+ expectedLocaleList.push(contentLocale);
+
+ // Add default "en-us" fallback if the main language is not already en-us
+ if (expectedLocaleList.indexOf("en-us") == -1)
+ expectedLocaleList.push("en-us");
+
+ assertPrefered(test, expectedLocaleList, "test localized pref value");
+
+ // Reset what we have changed
+ prefs.reset(PREF_MATCH_OS_LOCALE);
+ prefs.reset(PREF_SELECTED_LOCALE);
+ prefs.reset(PREF_ACCEPT_LANGUAGES);
+}
+
+exports.testPreferedOsLocale = function(test) {
+ prefs.set(PREF_MATCH_OS_LOCALE, true);
+ prefs.set(PREF_SELECTED_LOCALE, "");
+ prefs.set(PREF_ACCEPT_LANGUAGES, "");
+
+ let expectedLocale = Services.locale.getLocaleComponentForUserAgent().
+ toLowerCase();
+ let expectedLocaleList = [expectedLocale];
+
+ // Add default "en-us" fallback if the main language is not already en-us
+ if (expectedLocale != "en-us")
+ expectedLocaleList.push("en-us");
+
+ assertPrefered(test, expectedLocaleList, "Ensure that we select OS locale when related preference is set");
+
+ // Reset what we have changed
+ prefs.reset(PREF_MATCH_OS_LOCALE);
+ prefs.reset(PREF_SELECTED_LOCALE);
+ prefs.reset(PREF_ACCEPT_LANGUAGES);
+}
+
+exports.testFindClosestLocale = function(test) {
+ // Second param of findClosestLocale (aMatchLocales) have to be in lowercase
+ test.assertEqual(findClosestLocale([], []), null,
+ "When everything is empty we get null");
+
+ test.assertEqual(findClosestLocale(["en", "en-US"], ["en"]),
+ "en", "We always accept exact match first 1/5");
+ test.assertEqual(findClosestLocale(["en-US", "en"], ["en"]),
+ "en", "We always accept exact match first 2/5");
+ test.assertEqual(findClosestLocale(["en", "en-US"], ["en-us"]),
+ "en-US", "We always accept exact match first 3/5");
+ test.assertEqual(findClosestLocale(["ja-JP-mac", "ja", "ja-JP"], ["ja-jp"]),
+ "ja-JP", "We always accept exact match first 4/5");
+ test.assertEqual(findClosestLocale(["ja-JP-mac", "ja", "ja-JP"], ["ja-jp-mac"]),
+ "ja-JP-mac", "We always accept exact match first 5/5");
+
+ test.assertEqual(findClosestLocale(["en", "en-GB"], ["en-us"]),
+ "en", "We accept more generic locale, when there is no exact match 1/2");
+ test.assertEqual(findClosestLocale(["en-ZA", "en"], ["en-gb"]),
+ "en", "We accept more generic locale, when there is no exact match 2/2");
+
+ test.assertEqual(findClosestLocale(["ja-JP"], ["ja"]),
+ "ja-JP", "We accept more specialized locale, when there is no exact match 1/2");
+ // Better to select "ja" in this case but behave same as current AddonManager
+ test.assertEqual(findClosestLocale(["ja-JP-mac", "ja"], ["ja-jp"]),
+ "ja-JP-mac", "We accept more specialized locale, when there is no exact match 2/2");
+
+ test.assertEqual(findClosestLocale(["en-US"], ["en-us"]),
+ "en-US", "We keep the original one as result 1/2");
+ test.assertEqual(findClosestLocale(["en-us"], ["en-us"]),
+ "en-us", "We keep the original one as result 2/2");
+
+ test.assertEqual(findClosestLocale(["ja-JP-mac"], ["ja-jp-mac"]),
+ "ja-JP-mac", "We accept locale with 3 parts");
+ test.assertEqual(findClosestLocale(["ja-JP"], ["ja-jp-mac"]),
+ "ja-JP", "We accept locale with 2 parts from locale with 3 parts");
+ test.assertEqual(findClosestLocale(["ja"], ["ja-jp-mac"]),
+ "ja", "We accept locale with 1 part from locale with 3 parts");
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-l10n-plural-rules.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-l10n-plural-rules.js
new file mode 100644
index 0000000..e46ecf6
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-l10n-plural-rules.js
@@ -0,0 +1,82 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { getRulesForLocale } = require("api-utils/l10n/plural-rules");
+
+// For more information, please visit unicode website:
+// http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
+
+function map(test, f, n, form) {
+ test.assertEqual(f(n), form, n + " maps to '" + form + "'");
+}
+
+exports.testFrench = function(test) {
+ let f = getRulesForLocale("fr");
+ map(test, f, -1, "other");
+ map(test, f, 0, "one");
+ map(test, f, 1, "one");
+ map(test, f, 1.5, "one");
+ map(test, f, 2, "other");
+ map(test, f, 100, "other");
+}
+
+exports.testEnglish = function(test) {
+ let f = getRulesForLocale("en");
+ map(test, f, -1, "other");
+ map(test, f, 0, "other");
+ map(test, f, 1, "one");
+ map(test, f, 1.5, "other");
+ map(test, f, 2, "other");
+ map(test, f, 100, "other");
+}
+
+exports.testArabic = function(test) {
+ let f = getRulesForLocale("ar");
+ map(test, f, -1, "other");
+ map(test, f, 0, "zero");
+ map(test, f, 0.5, "other");
+
+ map(test, f, 1, "one");
+ map(test, f, 1.5, "other");
+
+ map(test, f, 2, "two");
+ map(test, f, 2.5, "other");
+
+ map(test, f, 3, "few");
+ map(test, f, 3.5, "few"); // I'd expect it to be 'other', but the unicode.org
+ // algorithm computes 'few'.
+ map(test, f, 5, "few");
+ map(test, f, 10, "few");
+ map(test, f, 103, "few");
+ map(test, f, 105, "few");
+ map(test, f, 110, "few");
+ map(test, f, 203, "few");
+ map(test, f, 205, "few");
+ map(test, f, 210, "few");
+
+ map(test, f, 11, "many");
+ map(test, f, 50, "many");
+ map(test, f, 99, "many");
+ map(test, f, 111, "many");
+ map(test, f, 150, "many");
+ map(test, f, 199, "many");
+
+ map(test, f, 100, "other");
+ map(test, f, 101, "other");
+ map(test, f, 102, "other");
+ map(test, f, 200, "other");
+ map(test, f, 201, "other");
+ map(test, f, 202, "other");
+}
+
+exports.testJapanese = function(test) {
+ // Japanese doesn't have plural forms.
+ let f = getRulesForLocale("ja");
+ map(test, f, -1, "other");
+ map(test, f, 0, "other");
+ map(test, f, 1, "other");
+ map(test, f, 1.5, "other");
+ map(test, f, 2, "other");
+ map(test, f, 100, "other");
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-light-traits.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-light-traits.js
new file mode 100644
index 0000000..7c5ca42
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-light-traits.js
@@ -0,0 +1,11 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+exports["test traits from objects"] = require("./traits/object-tests");
+exports["test traits from descriptors"] = require("./traits/descriptor-tests");
+exports["test inheritance"] = require("./traits/inheritance-tests");
+
+require("test").run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-list.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-list.js
new file mode 100644
index 0000000..67e67aa
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-list.js
@@ -0,0 +1,207 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+function assertList(test, array, list) {
+ for (let i = 0, ii = array.length; i < ii; i < ii, i++) {
+ test.assertEqual(
+ array.length,
+ list.length,
+ 'list must contain same amount of elements as array'
+ );
+ test.assertEqual(
+ 'List(' + array + ')',
+ list + '',
+ 'toString must output array like result'
+ );
+ test.assert(
+ i in list,
+ 'must contain element with index: ' + i
+ );
+ test.assertEqual(
+ array[i],
+ list[i],
+ 'element with index: ' + i + ' should match'
+ );
+ }
+}
+
+const { List } = require('api-utils/list');
+
+exports['test:test for'] = function(test) {
+ let fixture = List(3, 2, 1);
+
+ test.assertEqual(3, fixture.length, 'length is 3');
+ let i = 0;
+ for (let key in fixture) {
+ test.assertEqual(i++, key, 'key should match');
+ }
+};
+
+exports['test:test for each'] = function(test) {
+ let fixture = new List(3, 2, 1);
+
+ test.assertEqual(3, fixture.length, 'length is 3');
+ let i = 3;
+ for each (let value in fixture) {
+ test.assertEqual(i--, value, 'value should match');
+ }
+};
+
+exports['test: for each using Iterator'] = function(test) {
+ let fixture = new List(3, 2, 1);
+
+ test.assertEqual(3, fixture.length, 'length is 3');
+ let v = 3, k = 0;
+ for each (let [key, value] in Iterator(fixture)) {
+ test.assertEqual(k++, key, 'key should match');
+ test.assertEqual(v--, value, 'value should match');
+ }
+};
+
+exports['test:test toString'] = function(test) {
+ let fixture = List(3, 2, 1);
+
+ test.assertEqual(
+ 'List(3,2,1)',
+ fixture + '',
+ 'toString must output array like result'
+ )
+};
+
+exports['test:test constructor with apply'] = function(test) {
+ let array = ['a', 'b', 'c'];
+ let fixture = List.apply(null, array);
+
+ test.assertEqual(
+ 3,
+ fixture.length,
+ 'should have applied arguments'
+ );
+};
+
+exports['test:direct element access'] = function(test) {
+ let array = [1, 'foo', 2, 'bar', {}, 'bar', function a() {}, test, 1];
+ let fixture = List.apply(null, array);
+ array.splice(5, 1);
+ array.splice(7, 1);
+
+ test.assertEqual(
+ array.length,
+ fixture.length,
+ 'list should omit duplicate elements'
+ );
+
+ test.assertEqual(
+ 'List(' + array + ')',
+ fixture.toString(),
+ 'elements should not be rearranged'
+ );
+
+ for (let key in array) {
+ test.assert(key in fixture,'should contain key for index:' + key);
+ test.assertEqual(
+ array[key],
+ fixture[key],
+ 'values should match for: ' + key
+ );
+ }
+};
+
+exports['test:removing adding elements'] = function(test) {
+ let array = [1, 'foo', 2, 'bar', {}, 'bar', function a() {}, test, 1];
+ let fixture = List.compose({
+ add: function() this._add.apply(this, arguments),
+ remove: function() this._remove.apply(this, arguments),
+ clear: function() this._clear()
+ }).apply(null, array);
+ array.splice(5, 1);
+ array.splice(7, 1);
+
+ assertList(test, array, fixture);
+
+ array.splice(array.indexOf(2), 1);
+ fixture.remove(2);
+ assertList(test, array, fixture);
+
+ array.splice(array.indexOf('foo'), 1);
+ fixture.remove('foo');
+ array.splice(array.indexOf(1), 1);
+ fixture.remove(1);
+ array.push('foo');
+ fixture.add('foo');
+ assertList(test, array, fixture);
+
+ array.splice(0);
+ fixture.clear(0);
+ assertList(test, array, fixture);
+
+ array.push(1, 'foo', 2, 'bar', 3);
+ fixture.add(1);
+ fixture.add('foo');
+ fixture.add(2);
+ fixture.add('bar');
+ fixture.add(3);
+
+ assertList(test, array, fixture);
+};
+
+exports['test: remove does not leave invalid numerical properties'] = function(test) {
+ let fixture = List.compose({
+ remove: function() this._remove.apply(this, arguments),
+ }).apply(null, [1, 2, 3]);
+
+ fixture.remove(1);
+ test.assertEqual(fixture[fixture.length], undefined);
+}
+
+exports['test:add list item from Iterator'] = function(test) {
+ let array = [1, 2, 3, 4], sum = 0, added = false;
+
+ let fixture = List.compose({
+ add: function() this._add.apply(this, arguments),
+ }).apply(null, array);
+
+ for each (let item in fixture) {
+ sum += item;
+
+ if (!added) {
+ fixture.add(5);
+ added = true;
+ }
+ }
+
+ test.assertEqual(sum, 1 + 2 + 3 + 4);
+};
+
+exports['test:remove list item from Iterator'] = function(test) {
+ let array = [1, 2, 3, 4], sum = 0;
+
+ let fixture = List.compose({
+ remove: function() this._remove.apply(this, arguments),
+ }).apply(null, array);
+
+ for each (let item in fixture) {
+ sum += item;
+ fixture.remove(item);
+ }
+
+ test.assertEqual(sum, 1 + 2 + 3 + 4);
+};
+
+exports['test:clear list from Iterator'] = function(test) {
+ let array = [1, 2, 3, 4], sum = 0;
+
+ let fixture = List.compose({
+ clear: function() this._clear()
+ }).apply(null, array);
+
+ for each (let item in fixture) {
+ sum += item;
+ fixture.clear();
+ }
+
+ test.assertEqual(sum, 1 + 2 + 3 + 4);
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-loader.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-loader.js
new file mode 100644
index 0000000..82d97ba
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-loader.js
@@ -0,0 +1,35 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { Loader } = require("./helpers");
+
+exports.testLoader = function(test) {
+ var prints = [];
+ function print(message) {
+ prints.push(message);
+ }
+
+ var loader = Loader(module, { dump: print, foo: 1 });
+
+ var fixture = loader.require('./loader/fixture');
+
+ test.assertEqual(fixture.foo, 1, "custom globals must work.");
+
+ test.assertEqual(prints[0], "info: testing 1 2,3,4\n",
+ "global console must work.");
+
+ var unloadsCalled = '';
+
+ loader.require("unload").when(function() {
+ unloadsCalled += 'a';
+ });
+ loader.require("unload.js").when(function() {
+ unloadsCalled += 'b';
+ });
+
+ loader.unload();
+
+ test.assertEqual(unloadsCalled, 'ba',
+ "loader.unload() must call cb's in LIFO order.");
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-match-pattern.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-match-pattern.js
new file mode 100644
index 0000000..9cedf66
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-match-pattern.js
@@ -0,0 +1,127 @@
+/* -*- 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/. */
+
+const { MatchPattern } = require("match-pattern");
+
+exports.testMatchPatternTestTrue = function(test) {
+ function ok(pattern, url) {
+ let mp = new MatchPattern(pattern);
+ test.assert(mp.test(url), pattern + " should match " + url);
+ }
+
+ ok("*", "http://example.com");
+ ok("*", "https://example.com");
+ ok("*", "ftp://example.com");
+
+ ok("*.example.com", "http://example.com");
+ ok("*.example.com", "http://hamburger.example.com");
+ ok("*.example.com", "http://hotdog.hamburger.example.com");
+
+ ok("http://example.com*", "http://example.com");
+ ok("http://example.com*", "http://example.com/");
+ ok("http://example.com/*", "http://example.com/");
+ ok("http://example.com/*", "http://example.com/potato-salad");
+ ok("http://example.com/pickles/*", "http://example.com/pickles/");
+ ok("http://example.com/pickles/*", "http://example.com/pickles/lemonade");
+
+ ok("http://example.com", "http://example.com");
+ ok("http://example.com/ice-cream", "http://example.com/ice-cream");
+
+ ok(/.*zilla.*/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753");
+ ok(/https:.*zilla.*/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753");
+};
+
+exports.testMatchPatternTestFalse = function(test) {
+ function ok(pattern, url) {
+ let mp = new MatchPattern(pattern);
+ test.assert(!mp.test(url), pattern + " should not match " + url);
+ }
+
+ ok("*", null);
+ ok("*", "");
+ ok("*", "bogus");
+ ok("*", "chrome://browser/content/browser.xul");
+ ok("*", "nttp://example.com");
+
+ ok("*.example.com", null);
+ ok("*.example.com", "");
+ ok("*.example.com", "bogus");
+ ok("*.example.com", "http://example.net");
+ ok("*.example.com", "http://foo.com");
+ ok("*.example.com", "http://example.com.foo");
+ ok("*.example2.com", "http://example.com");
+
+ ok("http://example.com/*", null);
+ ok("http://example.com/*", "");
+ ok("http://example.com/*", "bogus");
+ ok("http://example.com/*", "http://example.com");
+ ok("http://example.com/*", "http://foo.com/");
+
+ ok("http://example.com", null);
+ ok("http://example.com", "");
+ ok("http://example.com", "bogus");
+ ok("http://example.com", "http://example.com/");
+
+ ok(/zilla.*/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753");
+ ok(/.*zilla/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753");
+ ok(/https:.*zilla/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753");
+};
+
+exports.testMatchPatternErrors = function(test) {
+ test.assertRaises(
+ function() new MatchPattern("*.google.com/*"),
+ /There can be at most one/,
+ "MatchPattern throws when supplied multiple '*'"
+ );
+
+ test.assertRaises(
+ function() new MatchPattern("google.com"),
+ /expected to be either an exact URL/,
+ "MatchPattern throws when the wildcard doesn't use '*' and doesn't " +
+ "look like a URL"
+ );
+
+ test.assertRaises(
+ function() new MatchPattern("http://google*.com"),
+ /expected to be the first or the last/,
+ "MatchPattern throws when a '*' is in the middle of the wildcard"
+ );
+
+ test.assertRaises(
+ function() new MatchPattern(/ /g),
+ /^A RegExp match pattern cannot be set to `global` \(i\.e\. \/\/g\)\.$/,
+ "MatchPattern throws on a RegExp set to `global` (i.e. //g)."
+ );
+
+ test.assertRaises(
+ function() new MatchPattern(/ /i),
+ /^A RegExp match pattern cannot be set to `ignoreCase` \(i\.e\. \/\/i\)\.$/,
+ "MatchPattern throws on a RegExp set to `ignoreCase` (i.e. //i)."
+ );
+
+ test.assertRaises(
+ function() new MatchPattern( / /m ),
+ /^A RegExp match pattern cannot be set to `multiline` \(i\.e\. \/\/m\)\.$/,
+ "MatchPattern throws on a RegExp set to `multiline` (i.e. //m)."
+ );
+};
+
+exports.testMatchPatternInternals = function(test) {
+ test.assertEqual(
+ new MatchPattern("http://google.com/test").exactURL,
+ "http://google.com/test"
+ );
+
+ test.assertEqual(
+ new MatchPattern("http://google.com/test/*").urlPrefix,
+ "http://google.com/test/"
+ );
+
+ test.assertEqual(
+ new MatchPattern("*.example.com").domain,
+ "example.com"
+ );
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-memory.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-memory.js
new file mode 100644
index 0000000..99e1682
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-memory.js
@@ -0,0 +1,19 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var memory = require("api-utils/memory");
+
+exports.testMemory = function(test) {
+ test.pass("Skipping this test until Gecko memory debugging issues " +
+ "are resolved (see bug 592774).");
+ return;
+
+ var obj = {};
+ memory.track(obj, "testMemory.testObj");
+ var objs = memory.getObjects("testMemory.testObj");
+ test.assertEqual(objs[0].weakref.get(), obj);
+ obj = null;
+ memory.gc();
+ test.assertEqual(objs[0].weakref.get(), null);
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-message-manager.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-message-manager.js
new file mode 100644
index 0000000..386d3bd
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-message-manager.js
@@ -0,0 +1,600 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { Loader } = require('./helpers');
+
+function createMessageManager() {
+ let loader = Loader(module);
+ let { MessageManager } = loader.require("api-utils/message-manager");
+ let frame = loader.sandbox("api-utils/message-manager").frame;
+
+ return [new MessageManager, frame];
+}
+
+
+exports["test MessageManager addMessageListener"] =
+ function(assert) {
+ let [mm, frame] = createMessageManager();
+ let remoteFrame = frame(mm).receiver;
+
+ let listeners = frame(mm).listeners;
+ let remoteListeners = frame(remoteFrame).listeners;
+
+ let topic = "message-topic";
+
+ let listener = function () {};
+
+ assert.equal(topic in listeners, false,
+ "No listeners for MessageManager");
+ assert.equal(topic in remoteListeners, false,
+ "No listeners for Remote Frame");
+
+ mm.addMessageListener(topic, listener);
+
+ assert.deepEqual(listeners[topic], [listener],
+ "Listener is added properly");
+ assert.equal(topic in remoteListeners, false,
+ "No listeners for Remote Frame");
+ }
+
+
+exports["test MessageManager addMessageListener with duplicates"] =
+ function(assert) {
+ let [mm, frame] = createMessageManager();
+ let topic = "message-topic";
+ let listeners = frame(mm).listeners;
+
+ let listener = function () {};
+
+ mm.addMessageListener(topic, listener);
+ mm.addMessageListener(topic, listener);
+ mm.addMessageListener(topic, listener);
+
+ assert.deepEqual(listeners[topic], [listener],
+ "Same listener is added only once");
+ }
+
+
+exports["test MessageManager addMessageListener exceptions"] =
+ function(assert) {
+ const BAD_LISTENER = "The event listener must be a function.";
+
+ let [mm, frame] = createMessageManager();
+ let listeners = frame(mm).listeners;
+ let topic = "message-topic";
+
+ assert.throws(
+ function() mm.addMessageListener(),
+ BAD_LISTENER
+ );
+
+ assert.throws(
+ function() mm.addMessageListener(topic),
+ BAD_LISTENER
+ );
+
+ assert.throws(
+ function() mm.addMessageListener(topic, "something-else"),
+ BAD_LISTENER
+ );
+ }
+
+exports["test MessageManager removeMessageListener"] =
+ function(assert) {
+ let [mm, frame] = createMessageManager();
+ let topic = "message-topic";
+ let listeners = frame(mm).listeners;
+
+ let listenerA = function () {};
+ let listenerB = function () {};
+ let listenerC = function () {};
+
+ mm.removeMessageListener(topic, listenerA);
+
+ assert.deepEqual(listeners, {},
+ "No listeners yet");
+
+ mm.addMessageListener(topic, listenerA);
+ mm.addMessageListener(topic, listenerB);
+ mm.addMessageListener(topic, listenerC);
+
+ mm.removeMessageListener(topic, listenerB);
+
+ assert.deepEqual(listeners[topic], [listenerA, listenerC],
+ "Listener is removed properly");
+ }
+
+
+exports["test MessageManager loadFrameScript with data URL"] =
+ function(assert) {
+ let [mm, frame] = createMessageManager();
+
+ let remoteFrame = frame(mm).receiver;
+
+ assert.equal("TEST_VALUE" in remoteFrame, false,
+ "TEST_VALUE is not defined in Remote Frame");
+
+ mm.loadFrameScript("data:, const TEST_VALUE = 77;", true);
+
+ assert.equal(remoteFrame.TEST_VALUE, 77,
+ "TEST_VALUE is properly defined in Remote Frame");
+ }
+
+
+exports["test MessageManager loadFrameScript with a File"] =
+ function(assert) {
+ let [mm, frame] = createMessageManager();
+
+ let remoteFrame = frame(mm).receiver;
+
+ assert.equal("TEST_VALUE" in remoteFrame, false,
+ "TEST_VALUE is not defined in Remote Frame");
+
+ mm.loadFrameScript(require("self").data.url("test-message-manager.js"), true);
+
+ assert.equal(remoteFrame.TEST_VALUE, 11,
+ "TEST_VALUE is properly defined in Remote Frame");
+ }
+
+exports["test MessageManager loadFrameScript exception"] =
+ function(assert) {
+ let [mm, frame] = createMessageManager();
+
+ let remoteFrame = frame(mm).receiver;
+
+
+ assert.throws(
+ function() mm.loadFrameScript("data:, const TEST_VALUE = 77;"),
+ "Not enough arguments"
+ );
+
+ }
+
+exports["test Remote Frame addMessageListener"] =
+ function(assert) {
+ let [mm, frame] = createMessageManager();
+ let remoteFrame = frame(mm).receiver;
+
+ let listeners = frame(remoteFrame).listeners;
+ let managerListeners = frame(mm).listeners;
+
+ let topic = "message-topic";
+
+ let listener = function () {};
+
+ assert.equal(topic in listeners, false,
+ "No listeners for Remote Frame");
+ assert.equal(topic in managerListeners, false,
+ "No listeners for MessageManager");
+
+ remoteFrame.addMessageListener(topic, listener);
+
+ assert.deepEqual(listeners[topic], [listener],
+ "Listener is added properly");
+ assert.equal(topic in managerListeners, false,
+ "No listeners for MessageManager");
+ }
+
+
+exports["test Remote Frame addMessageListener with duplicates"] =
+ function(assert) {
+ let [mm, frame] = createMessageManager();
+
+ let remoteFrame = frame(mm).receiver;
+ let listeners = frame(remoteFrame).listeners;
+ let topic = "message-topic";
+
+ let listener = function () {};
+
+ assert.equal(topic in listeners, false,
+ "No listeners in Remote Frame");
+
+ remoteFrame.addMessageListener(topic, listener);
+ remoteFrame.addMessageListener(topic, listener);
+ remoteFrame.addMessageListener(topic, listener);
+
+ assert.deepEqual(listeners[topic], [listener],
+ "Listener is added properly");
+ }
+
+
+exports["test Remote Frame addMessageListener exceptions"] =
+ function(assert) {
+ const BAD_LISTENER = "The event listener must be a function.";
+
+ let [mm, frame] = createMessageManager();
+ let remoteFrame = frame(mm).receiver;
+ let listeners = frame(remoteFrame).listeners;
+ let topic = "message-topic";
+
+ assert.throws(
+ function() mm.addMessageListener(),
+ BAD_LISTENER
+ );
+
+ assert.throws(
+ function() mm.addMessageListener(topic),
+ BAD_LISTENER
+ );
+
+ assert.throws(
+ function() mm.addMessageListener(topic, "something-else"),
+ BAD_LISTENER
+ );
+ }
+
+
+exports["test Remote Frame removeMessageListener"] =
+ function(assert) {
+ let [mm, frame] = createMessageManager();
+
+ let remoteFrame = frame(mm).receiver;
+ let listeners = frame(remoteFrame).listeners;
+
+ let topic = "message-topic";
+
+ let listenerA = function () {};
+ let listenerB = function () {};
+ let listenerC = function () {};
+
+ remoteFrame.removeMessageListener(topic, listenerA);
+
+ assert.deepEqual(listeners, {},
+ "No listeners yet");
+
+ remoteFrame.addMessageListener(topic, listenerA);
+ remoteFrame.addMessageListener(topic, listenerB);
+ remoteFrame.addMessageListener(topic, listenerC);
+
+ remoteFrame.removeMessageListener(topic, listenerB);
+
+ assert.deepEqual(listeners[topic], [listenerA, listenerC],
+ "Listener is removed properly");
+ }
+
+
+exports["test MessageManager sendAsyncMessage"] =
+ function(assert, done) {
+ let [mm, frame] = createMessageManager();
+
+ let remoteFrame = frame(mm).receiver;
+ let calls = 0;
+
+ let topic = "message-topic";
+
+ let listener = function(data) {
+ calls++;
+
+ assert.deepEqual(data, {
+ sync : false,
+ name : topic,
+ json : {foo : "bar"},
+ target : null
+ }, "Data received as expected");
+
+ assert.equal(calls, 1,
+ "Listener called once");
+
+ done();
+ }
+
+ remoteFrame.addMessageListener(topic, listener);
+
+ let response = mm.sendAsyncMessage(topic, {foo : "bar"});
+
+ assert.strictEqual(response, undefined,
+ "No response for async messages");
+
+ assert.equal(calls, 0,
+ "Listener not called yet");
+ }
+
+exports["test MessageManager sendAsyncMessage without listeners"] =
+ function(assert) {
+ let [mm, frame] = createMessageManager();
+
+ let remoteFrame = frame(mm).receiver;
+
+ let topic = "message-topic";
+
+ let response = mm.sendAsyncMessage(topic, {foo : "bar"});
+
+ assert.strictEqual(response, undefined,
+ "No response for async messages");
+ }
+
+
+exports["test MessageManager sendAsyncMessage without arguments"] =
+ function(assert, done) {
+ let [mm, frame] = createMessageManager();
+
+ let remoteFrame = frame(mm).receiver;
+ let calls = 0;
+
+ let topic = "message-topic";
+
+ let listener = function(data) {
+ calls++;
+
+ assert.deepEqual(data, {
+ sync : false,
+ name : topic,
+ json : null,
+ target : null
+ }, "Data received as expected");
+
+ assert.equal(calls, 1,
+ "Listener called once");
+
+ done();
+ }
+
+ remoteFrame.addMessageListener(topic, listener);
+
+ mm.sendAsyncMessage();
+
+ mm.sendAsyncMessage(topic);
+
+ assert.equal(calls, 0,
+ "Listener not called yet");
+ }
+
+
+exports["test Remote Frame sendAsyncMessage"] =
+ function(assert, done) {
+ let [mm, frame] = createMessageManager();
+
+ let remoteFrame = frame(mm).receiver;
+ let calls = 0;
+
+ let topic = "message-topic";
+
+ let listener = function(data) {
+ calls++;
+
+ assert.deepEqual(data, {
+ sync : false,
+ name : topic,
+ json : {foo : "bar"},
+ target : null
+ }, "Data received as expected");
+
+ assert.equal(calls, 1,
+ "Listener called once");
+
+ done();
+ }
+
+ mm.addMessageListener(topic, listener);
+
+ let response = remoteFrame.sendAsyncMessage(topic, {foo : "bar"});
+
+ assert.strictEqual(response, undefined,
+ "No response for async messages");
+
+ assert.equal(calls, 0,
+ "Listener not called yet");
+ }
+
+
+exports["test Remote Frame sendAsyncMessage without arguments"] =
+ function(assert, done) {
+ let [mm, frame] = createMessageManager();
+
+ let remoteFrame = frame(mm).receiver;
+ let calls = 0;
+
+ let topic = "message-topic";
+
+ let listener = function(data) {
+ calls++;
+
+ assert.deepEqual(data, {
+ sync : false,
+ name : topic,
+ json : null,
+ target : null
+ }, "Data received as expected");
+
+ assert.equal(calls, 1,
+ "Listener called once");
+
+ done();
+ }
+
+ mm.addMessageListener(topic, listener);
+
+ remoteFrame.sendAsyncMessage();
+
+ remoteFrame.sendAsyncMessage(topic);
+
+ assert.equal(calls, 0,
+ "Listener not called yet");
+ }
+
+exports["test Remote Frame sendAsyncMessage without listeners"] =
+ function(assert) {
+ let [mm, frame] = createMessageManager();
+
+ let remoteFrame = frame(mm).receiver;
+
+ let topic = "message-topic";
+
+ let response = remoteFrame.sendAsyncMessage(topic, {foo : "bar"});
+
+ assert.strictEqual(response, undefined,
+ "No response for async messages");
+ }
+
+exports["test Remote Frame sendSyncMessage"] =
+ function(assert) {
+ let [mm, frame] = createMessageManager();
+
+ let remoteFrame = frame(mm).receiver;
+
+ let topic = "message-topic";
+
+ let expectedData = {
+ sync : true,
+ name : topic,
+ json : {foo : "bar"},
+ target : null
+ }
+
+ let listenerA = function(data) {
+ assert.deepEqual(data, expectedData,
+ "Data received as expected");
+
+ return "my value";
+ }
+
+ let listenerB = function(data) {
+ assert.deepEqual(data, expectedData,
+ "Data received as expected");
+
+ return {complex : "object", method : function() "not allowed"};
+ }
+
+ let listenerC = function(data) {
+ assert.deepEqual(data, expectedData,
+ "Data received as expected");
+ }
+
+ mm.addMessageListener(topic, listenerA);
+ mm.addMessageListener(topic, listenerB);
+ mm.addMessageListener(topic, listenerC);
+
+ let response = remoteFrame.sendSyncMessage(topic, {foo : "bar"});
+
+ assert.deepEqual(response, ["my value", {complex : "object"}, undefined],
+ "Response from sync messages as expected");
+ }
+
+exports["test Remote Frame sendSyncMessage without arguments"] =
+ function(assert) {
+ let [mm, frame] = createMessageManager();
+
+ let remoteFrame = frame(mm).receiver;
+
+ let topic = "message-topic";
+
+ let expectedData = {
+ sync : true,
+ name : topic,
+ json : null,
+ target : null
+ }
+
+ let listener = function(data) {
+ assert.deepEqual(data, expectedData,
+ "Data received as expected");
+ }
+
+ mm.addMessageListener(topic, listener);
+
+ let response = remoteFrame.sendSyncMessage();
+
+ assert.deepEqual(response, [],
+ "Empty response as expected");
+
+ let response = remoteFrame.sendSyncMessage(topic);
+
+ assert.deepEqual(response, [undefined],
+ "Response from sync messages as expected");
+ }
+
+exports["test Remote Frame sendSyncMessage without listeners"] =
+ function(assert) {
+ let [mm, frame] = createMessageManager();
+
+ let remoteFrame = frame(mm).receiver;
+ let obj = {foo : "bar"};
+
+ let topic = "message-topic";
+
+ let response = remoteFrame.sendSyncMessage(topic, obj);
+
+ assert.deepEqual(response, [],
+ "Response from sync messages as expected");
+ }
+
+
+exports["test Message Manager / Remote Frame async pipeline"] =
+ function(assert, done) {
+ let [mm] = createMessageManager();
+
+ let expectedMessages = ["alpha:remote", "alpha", "omega:remote", "omega"];
+
+ mm.addMessageListener("handshake", function (data) {
+ let messages = data.json.concat("alpha");
+
+ mm.sendAsyncMessage("shutdown", messages);
+ });
+
+ mm.addMessageListener("shutdown", function (data) {
+ let messages = data.json.concat("omega");
+
+ assert.deepEqual(messages, expectedMessages,
+ "messages delivered in the expected order");
+
+ done();
+ });
+
+ mm.loadFrameScript(
+ "data:, \
+ addMessageListener('handshake', function (data) {\
+ let messages = data.json.concat('alpha:remote');\
+ sendAsyncMessage('handshake', messages);\
+ });\
+ addMessageListener('shutdown', function (data) {\
+ let messages = data.json.concat('omega:remote');\
+ sendAsyncMessage('shutdown', messages);\
+ });\
+ ", true);
+
+ mm.sendAsyncMessage("handshake", []);
+ }
+
+
+exports["test Message Manager / Remote Frame async / sync pipeline"] =
+ function(assert, done) {
+ let [mm] = createMessageManager();
+
+ let expectedMessages = ["alpha:remote", "alpha", "omega:remote", "omega"];
+
+ mm.addMessageListener("handshake",
+ function (data) data.json.concat("alpha")
+ );
+
+ mm.addMessageListener("shutdown",
+ function (data) data.json.concat("omega")
+ );
+
+ mm.addMessageListener("verify", function (data) {
+
+ assert.deepEqual(data.json, expectedMessages,
+ "messages delivered in the expected order");
+
+ done();
+ });
+
+ mm.loadFrameScript(
+ "data:, \
+ addMessageListener('handshake', function (data) {\
+ let messages = data.json.concat('alpha:remote');\
+ \
+ messages = sendSyncMessage('handshake', messages)[0];\
+ messages.push('omega:remote');\
+ messages = sendSyncMessage('shutdown', messages)[0];\
+ \
+ sendSyncMessage('verify', messages);\
+ });\
+ ", true);
+
+ mm.sendAsyncMessage("handshake", []);
+ }
+
+require("test").run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-modules.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-modules.js
new file mode 100644
index 0000000..2868560
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-modules.js
@@ -0,0 +1,148 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.testDefine = function(test) {
+ let tiger = require('./modules/tiger');
+ test.assertEqual(tiger.name, 'tiger', 'name proprety was exported properly');
+ test.assertEqual(tiger.type, 'cat', 'property form other module exported');
+};
+
+exports.testDefineInoresNonFactory = function(test) {
+ let mod = require('./modules/async2');
+ test.assertEqual(mod.name, 'async2', 'name proprety was exported properly');
+ test.assertNotEqual(mod.traditional2Name, 'traditional2', '1st is ignored');
+};
+/* Disable test that require AMD specific functionality:
+
+// define() that exports a function as the module value,
+// specifying a module name.
+exports.testDefExport = function(test) {
+ var add = require('modules/add');
+ test.assertEqual(add(1, 1), 2, 'Named define() exporting a function');
+};
+
+// define() that exports function as a value, but is anonymous
+exports.testAnonDefExport = function (test) {
+ var subtract = require('modules/subtract');
+ test.assertEqual(subtract(4, 2), 2,
+ 'Anonymous define() exporting a function');
+}
+
+// using require([], function () {}) to load modules.
+exports.testSimpleRequire = function (test) {
+ require(['modules/blue', 'modules/orange'], function (blue, orange) {
+ test.assertEqual(blue.name, 'blue', 'Simple require for blue');
+ test.assertEqual(orange.name, 'orange', 'Simple require for orange');
+ test.assertEqual(orange.parentType, 'color',
+ 'Simple require dependency check for orange');
+ });
+}
+
+// using nested require([]) calls.
+exports.testSimpleRequireNested = function (test) {
+ require(['modules/blue', 'modules/orange', 'modules/green'],
+ function (blue, orange, green) {
+
+ require(['modules/orange', 'modules/red'], function (orange, red) {
+ test.assertEqual(red.name, 'red', 'Simple require for red');
+ test.assertEqual(red.parentType, 'color',
+ 'Simple require dependency check for red');
+ test.assertEqual(blue.name, 'blue', 'Simple require for blue');
+ test.assertEqual(orange.name, 'orange', 'Simple require for orange');
+ test.assertEqual(orange.parentType, 'color',
+ 'Simple require dependency check for orange');
+ test.assertEqual(green.name, 'green', 'Simple require for green');
+ test.assertEqual(green.parentType, 'color',
+ 'Simple require dependency check for green');
+ });
+
+ });
+}
+
+// requiring a traditional module, that uses async, that use traditional and
+// async, with a circular reference
+exports.testMixedCircular = function (test) {
+ var t = require('modules/traditional1');
+ test.assertEqual(t.name, 'traditional1', 'Testing name');
+ test.assertEqual(t.traditional2Name, 'traditional2',
+ 'Testing dependent name');
+ test.assertEqual(t.traditional1Name, 'traditional1', 'Testing circular name');
+ test.assertEqual(t.async2Name, 'async2', 'Testing async2 name');
+ test.assertEqual(t.async2Traditional2Name, 'traditional2',
+ 'Testing nested traditional2 name');
+}
+
+// Testing define()(function(require) {}) with some that use exports,
+// some that use return.
+exports.testAnonExportsReturn = function (test) {
+ var lion = require('modules/lion');
+ require(['modules/tiger', 'modules/cheetah'], function (tiger, cheetah) {
+ test.assertEqual('lion', lion, 'Check lion name');
+ test.assertEqual('tiger', tiger.name, 'Check tiger name');
+ test.assertEqual('cat', tiger.type, 'Check tiger type');
+ test.assertEqual('cheetah', cheetah(), 'Check cheetah name');
+ });
+}
+
+// circular dependency
+exports.testCircular = function (test) {
+ var pollux = require('modules/pollux'),
+ castor = require('modules/castor');
+
+ test.assertEqual(pollux.name, 'pollux', 'Pollux\'s name');
+ test.assertEqual(pollux.getCastorName(),
+ 'castor', 'Castor\'s name from Pollux.');
+ test.assertEqual(castor.name, 'castor', 'Castor\'s name');
+ test.assertEqual(castor.getPolluxName(), 'pollux',
+ 'Pollux\'s name from Castor.');
+}
+
+// test a bad module that asks for exports but also does a define() return
+exports.testBadExportAndReturn = function (test) {
+ var passed = false;
+ try {
+ var bad = require('modules/badExportAndReturn');
+ } catch(e) {
+ passed = /cannot use exports and also return/.test(e.toString());
+ }
+ test.assertEqual(passed, true, 'Make sure exports and return fail');
+}
+
+// test a bad circular dependency, where an exported value is needed, but
+// the return value happens too late, a module already asked for the exported
+// value.
+exports.testBadExportAndReturnCircular = function (test) {
+ var passed = false;
+ try {
+ var bad = require('modules/badFirst');
+ } catch(e) {
+ passed = /after another module has referenced its exported value/
+ .test(e.toString());
+ }
+ test.assertEqual(passed, true, 'Make sure return after an exported ' +
+ 'value is grabbed by another module fails.');
+}
+
+// only allow one define call per file.
+exports.testOneDefine = function (test) {
+ var passed = false;
+ try {
+ var dupe = require('modules/dupe');
+ } catch(e) {
+ passed = /Only one call to define/.test(e.toString());
+ }
+ test.assertEqual(passed, true, 'Only allow one define call per module');
+}
+
+// only allow one define call per file, testing a bad nested define call.
+exports.testOneDefineNested = function (test) {
+ var passed = false;
+ try {
+ var dupe = require('modules/dupeNested');
+ } catch(e) {
+ passed = /Only one call to define/.test(e.toString());
+ }
+ test.assertEqual(passed, true, 'Only allow one define call per module');
+}
+*/
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-namespace.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-namespace.js
new file mode 100644
index 0000000..cb69bb0
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-namespace.js
@@ -0,0 +1,95 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 { Namespace, ns } = require("api-utils/namespace");
+let { Cc, Ci, Cu } = require("chrome");
+let { setTimeout } = require("api-utils/timer")
+
+exports["test post GC references"] = function (assert, done) {
+ // Test temporary workaround for a bug 673468.
+ var target = {}, local = ns()
+ local(target).there = true
+
+ assert.equal(local(target).there, true, "namespaced preserved");
+
+ setTimeout(function() {
+ Cu.forceGC();
+ assert.equal(local(target).there, true, "namespace is preserved post GC");
+ done();
+ }, 300);
+};
+
+exports["test namsepace basics"] = function(assert) {
+ var privates = Namespace();
+ var object = { foo: function foo() { return "hello foo"; } };
+
+ assert.notEqual(privates(object), object,
+ "namespaced object is not the same");
+ assert.ok(!('foo' in privates(object)),
+ "public properties are not in the namespace");
+
+ assert.equal(privates(object), privates(object),
+ "same namespaced object is returned on each call");
+};
+
+exports["test namespace overlays"] = function(assert) {
+ var _ = new Namespace();
+ var object = { foo: 'foo' };
+
+ _(object).foo = 'bar';
+
+ assert.equal(_(object).foo, "bar",
+ "namespaced property `foo` changed value");
+
+ assert.equal(object.foo, "foo",
+ "public property `foo` has original value");
+
+ object.foo = "baz";
+ assert.equal(_(object).foo, "bar",
+ "property changes do not affect namespaced properties");
+
+ object.bar = "foo";
+ assert.ok(!("bar" in _(object)),
+ "new public properties are not reflected in namespace");
+};
+
+exports["test shared namespaces"] = function(assert) {
+ var _ = new Namespace({ hello: 'hello world' });
+
+ var f1 = { hello: 1 };
+ var f2 = { foo: 'foo' };
+
+ assert.equal(_(f1).hello, _(f2).hello, "namespace can be shared");
+ assert.notEqual(f1.hello, _(f1).hello, "shared namespace can overlay");
+ assert.notEqual(f2.hello, _(f2).hello, "target is not affected");
+
+ _(f1).hello = 2;
+
+ assert.notEqual(_(f1).hello, _(f2).hello,
+ "namespaced property can be overided");
+ assert.equal(_(f2).hello, _({}).hello, "namespace does not change");
+};
+
+exports["test multi namespace"] = function(assert) {
+ var n1 = new Namespace();
+ var n2 = new Namespace();
+ var object = { baz: 1 };
+ n1(object).foo = 1;
+ n2(object).foo = 2;
+ n1(object).bar = n2(object).bar = 3;
+
+ assert.notEqual(n1(object).foo, n2(object).foo,
+ "object can have multiple namespaces");
+ assert.equal(n1(object).bar, n2(object).bar,
+ "object can have matching props in diff namespaces");
+};
+
+exports["test ns alias"] = function(assert) {
+ assert.strictEqual(ns, Namespace,
+ "ns is an alias of Namespace");
+}
+
+require("test").run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-observer-service.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-observer-service.js
new file mode 100644
index 0000000..222ae22
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-observer-service.js
@@ -0,0 +1,77 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var observers = require("api-utils/observer-service");
+var {Cc,Ci} = require("chrome");
+const { Loader } = require("./helpers");
+
+exports.testUnloadAndErrorLogging = function(test) {
+ var prints = [];
+ var loader = Loader(module, { dump: function print(message) {
+ prints.push(message);
+ }});
+ var sbobsvc = loader.require("api-utils/observer-service");
+
+ var timesCalled = 0;
+ var cb = function(subject, data) {
+ timesCalled++;
+ };
+ var badCb = function(subject, data) {
+ throw new Error("foo");
+ };
+ sbobsvc.add("blarg", cb);
+ observers.notify("blarg", "yo yo");
+ test.assertEqual(timesCalled, 1);
+ sbobsvc.add("narg", badCb);
+ observers.notify("narg", "yo yo");
+ var lines = prints[0].split("\n");
+ test.assertEqual(lines[0], "error: An exception occurred.");
+ test.assertEqual(lines[1], "Traceback (most recent call last):");
+ test.assertEqual(lines.slice(-2)[0], "Error: foo");
+
+ loader.unload();
+ observers.notify("blarg", "yo yo");
+ test.assertEqual(timesCalled, 1);
+};
+
+exports.testObserverService = function(test) {
+ var ios = Cc['@mozilla.org/network/io-service;1']
+ .getService(Ci.nsIIOService);
+ var service = Cc["@mozilla.org/observer-service;1"].
+ getService(Ci.nsIObserverService);
+ var uri = ios.newURI("http://www.foo.com", null, null);
+ var timesCalled = 0;
+ var lastSubject = null;
+ var lastData = null;
+
+ var cb = function(subject, data) {
+ timesCalled++;
+ lastSubject = subject;
+ lastData = data;
+ };
+
+ observers.add("blarg", cb);
+ service.notifyObservers(uri, "blarg", "some data");
+ test.assertEqual(timesCalled, 1,
+ "observer-service.add() should call callback");
+ test.assertEqual(lastSubject, uri,
+ "observer-service.add() should pass subject");
+ test.assertEqual(lastData, "some data",
+ "observer-service.add() should pass data");
+
+ function customSubject() {}
+ function customData() {}
+ observers.notify("blarg", customSubject, customData);
+ test.assertEqual(timesCalled, 2,
+ "observer-service.notify() should work");
+ test.assertEqual(lastSubject, customSubject,
+ "observer-service.notify() should pass+wrap subject");
+ test.assertEqual(lastData, customData,
+ "observer-service.notify() should pass data");
+
+ observers.remove("blarg", cb);
+ service.notifyObservers(null, "blarg", "some data");
+ test.assertEqual(timesCalled, 2,
+ "observer-service.remove() should work");
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-passwords-utils.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-passwords-utils.js
new file mode 100644
index 0000000..ab758ed
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-passwords-utils.js
@@ -0,0 +1,142 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { store, search, remove } = require("passwords/utils");
+
+exports["test store requires `password` field"] = function(assert) {
+ assert.throws(function() {
+ store({ username: "foo", realm: "bar" });
+ }, "`password` is required");
+};
+
+exports["test store requires `username` field"] = function(assert) {
+ assert.throws(function() {
+ store({ password: "foo", realm: "bar" });
+ }, "`username` is required");
+};
+
+exports["test store requires `realm` field"] = function(assert) {
+ assert.throws(function() {
+ store({ username: "foo", password: "bar" });
+ }, "`password` is required");
+};
+
+exports["test can't store same login twice"] = function(assert) {
+ let options = { username: "user", password: "pass", realm: "realm" };
+ store(options);
+ assert.throws(function() {
+ store(options);
+ }, "can't store same pass twice");
+ remove(options);
+};
+
+exports["test remove throws if no login found"] = function(assert) {
+ assert.throws(function() {
+ remove({ username: "foo", password: "bar", realm: "baz" });
+ }, "can't remove unstored credentials");
+};
+
+exports["test addon associated credentials"] = function(assert) {
+ let options = { username: "foo", password: "bar", realm: "baz" };
+ store(options);
+
+ assert.ok(search().length, "credential was stored");
+ assert.ok(search(options).length, "stored credential found");
+ assert.ok(search({ username: options.username }).length, "found by username");
+ assert.ok(search({ password: options.password }).length, "found by password");
+ assert.ok(search({ realm: options.realm }).length, "found by realm");
+
+ let credential = search(options)[0];
+ assert.equal(credential.url.indexOf("addon:"), 0,
+ "`addon:` uri is used for add-on associated credentials");
+ assert.equal(credential.username, options.username, "username matches");
+ assert.equal(credential.password, options.password, "password matches");
+ assert.equal(credential.realm, options.realm, "realm matches");
+ assert.equal(credential.formSubmitURL, null,
+ "`formSubmitURL` is `null` for add-on associated credentials");
+ assert.equal(credential.usernameField, "", "usernameField is empty");
+ assert.equal(credential.passwordField, "", "passwordField is empty");
+
+ remove(search(options)[0]);
+ assert.ok(!search(options).length, "remove worked");
+};
+
+exports["test web page associated credentials"] = function(assert) {
+ let options = {
+ url: "http://www.example.com",
+ formSubmitURL: "http://login.example.com",
+ username: "user",
+ password: "pass",
+ usernameField: "user-f",
+ passwordField: "pass-f"
+ };
+ store({
+ url: "http://www.example.com/login",
+ formSubmitURL: "http://login.example.com/foo/authenticate.cgi",
+ username: options.username,
+ password: options.password,
+ usernameField: options.usernameField,
+ passwordField: options.passwordField
+ });
+
+ assert.ok(search().length, "credential was stored");
+ assert.ok(search(options).length, "stored credential found");
+ assert.ok(search({ username: options.username }).length, "found by username");
+ assert.ok(search({ password: options.password }).length, "found by password");
+ assert.ok(search({ formSubmitURL: options.formSubmitURL }).length,
+ "found by formSubmitURL");
+ assert.ok(search({ usernameField: options.usernameField }).length,
+ "found by usernameField");
+ assert.ok(search({ passwordField: options.passwordField }).length,
+ "found by passwordField");
+
+ let credential = search(options)[0];
+ assert.equal(credential.url, options.url, "url matches");
+ assert.equal(credential.username, options.username, "username matches");
+ assert.equal(credential.password, options.password, "password matches");
+ assert.equal(credential.realm, null, "realm is ");
+ assert.equal(credential.formSubmitURL, options.formSubmitURL,
+ "`formSubmitURL` matches");
+ assert.equal(credential.usernameField, options.usernameField,
+ "usernameField matches");
+ assert.equal(credential.passwordField, options.passwordField,
+ "passwordField matches");
+
+ remove(search(options)[0]);
+ assert.ok(!search(options).length, "remove worked");
+};
+
+exports["test site authentication credentials"] = function(assert) {
+ let options = {
+ url: "http://test.authentication.com",
+ username: "u",
+ password: "p",
+ realm: "r"
+ };
+
+ store(options);
+ assert.ok(search().length, "credential was stored");
+ assert.ok(search(options).length, "stored credential found");
+ assert.ok(search({ username: options.username }).length, "found by username");
+ assert.ok(search({ password: options.password }).length, "found by password");
+ assert.ok(search({ realm: options.realm }).length, "found by realm");
+ assert.ok(search({ url: options.url }).length, "found by url");
+
+ let credential = search(options)[0];
+ assert.equal(credential.url, options.url, "url matches");
+ assert.equal(credential.username, options.username, "username matches");
+ assert.equal(credential.password, options.password, "password matches");
+ assert.equal(credential.realm, options.realm, "realm matches");
+ assert.equal(credential.formSubmitURL, null,
+ "`formSubmitURL` is `null` for site authentication credentials");
+ assert.equal(credential.usernameField, "", "usernameField is empty");
+ assert.equal(credential.passwordField, "", "passwordField is empty");
+
+ remove(search(options)[0]);
+ assert.ok(!search(options).length, "remove worked");
+};
+
+require("test").run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-plain-text-console.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-plain-text-console.js
new file mode 100644
index 0000000..40fb519
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-plain-text-console.js
@@ -0,0 +1,68 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.testPlainTextConsole = function(test) {
+ var prints = [];
+ function print(message) {
+ prints.push(message);
+ }
+ function lastPrint() {
+ var last = prints.slice(-1)[0];
+ prints = [];
+ return last;
+ }
+
+ var Console = require("plain-text-console").PlainTextConsole;
+ var con = new Console(print);
+
+ test.pass("PlainTextConsole instantiates");
+
+ con.log('testing', 1, [2, 3, 4]);
+ test.assertEqual(lastPrint(), "info: testing 1 2,3,4\n",
+ "PlainTextConsole.log() must work.");
+
+ con.info('testing', 1, [2, 3, 4]);
+ test.assertEqual(lastPrint(), "info: testing 1 2,3,4\n",
+ "PlainTextConsole.info() must work.");
+
+ con.warn('testing', 1, [2, 3, 4]);
+ test.assertEqual(lastPrint(), "warning: testing 1 2,3,4\n",
+ "PlainTextConsole.warn() must work.");
+
+ con.error('testing', 1, [2, 3, 4]);
+ test.assertEqual(lastPrint(), "error: testing 1 2,3,4\n",
+ "PlainTextConsole.error() must work.");
+
+ con.debug('testing', 1, [2, 3, 4]);
+ test.assertEqual(lastPrint(), "debug: testing 1 2,3,4\n",
+ "PlainTextConsole.debug() must work.");
+
+ con.log('testing', undefined);
+ test.assertEqual(lastPrint(), "info: testing undefined\n",
+ "PlainTextConsole.log() must stringify undefined.");
+
+ con.log('testing', null);
+ test.assertEqual(lastPrint(), "info: testing null\n",
+ "PlainTextConsole.log() must stringify null.");
+
+ con.log("testing", { toString: function() "obj.toString()" });
+ test.assertEqual(lastPrint(), "info: testing obj.toString()\n",
+ "PlainTextConsole.log() must stringify custom toString.");
+
+ con.log("testing", { toString: function() { throw "fail!"; } });
+ test.assertEqual(lastPrint(), "info: testing <toString() error>\n",
+ "PlainTextConsole.log() must stringify custom bad toString.");
+
+ con.exception(new Error("blah"));
+ var tbLines = prints[0].split("\n");
+ test.assertEqual(tbLines[0], "error: An exception occurred.");
+ test.assertEqual(tbLines[1], "Traceback (most recent call last):");
+ test.assertEqual(tbLines.slice(-2)[0], "Error: blah");
+
+ prints = [];
+ con.trace();
+ tbLines = prints[0].split("\n");
+ test.assertEqual(tbLines[0], "info: Traceback (most recent call last):");
+ test.assertEqual(tbLines.slice(-2)[0].trim(), "con.trace();");
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-preferences-service.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-preferences-service.js
new file mode 100644
index 0000000..857357e
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-preferences-service.js
@@ -0,0 +1,112 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const prefs = require("preferences-service");
+const { Cc, Ci, Cu } = require("chrome");
+const BundleService = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
+
+exports.testReset = function(test) {
+ prefs.reset("test_reset_pref");
+ test.assertEqual(prefs.has("test_reset_pref"), false);
+ test.assertEqual(prefs.isSet("test_reset_pref"), false);
+ prefs.set("test_reset_pref", 5);
+ test.assertEqual(prefs.has("test_reset_pref"), true);
+ test.assertEqual(prefs.isSet("test_reset_pref"), true);
+};
+
+exports.testGetAndSet = function(test) {
+ let svc = Cc["@mozilla.org/preferences-service;1"].
+ getService(Ci.nsIPrefService).
+ getBranch(null);
+ svc.setCharPref("test_get_string_pref", "a normal string");
+ test.assertEqual(prefs.get("test_get_string_pref"), "a normal string",
+ "preferences-service should read from " +
+ "application-wide preferences service");
+
+ prefs.set("test_set_get_pref.integer", 1);
+ test.assertEqual(prefs.get("test_set_get_pref.integer"), 1,
+ "set/get integer preference should work");
+
+ prefs.set("test_set_get_number_pref", 42);
+ test.assertRaises(
+ function() { prefs.set("test_set_get_number_pref", 3.14159); },
+ "cannot store non-integer number: 3.14159",
+ "setting a float preference should raise an error"
+ );
+ test.assertEqual(prefs.get("test_set_get_number_pref"), 42,
+ "bad-type write attempt should not overwrite");
+
+ // 0x80000000 (no), 0x7fffffff (yes), -0x80000000 (yes), -0x80000001 (no)
+ test.assertRaises(
+ function() { prefs.set("test_set_get_number_pref", Math.pow(2, 31)); },
+ ("you cannot set the test_set_get_number_pref pref to the number " +
+ "2147483648, as number pref values must be in the signed 32-bit " +
+ "integer range -(2^31) to 2^31-1. To store numbers outside that " +
+ "range, store them as strings."),
+ "setting an int pref outside the range -(2^31) to 2^31-1 shouldn't work"
+ );
+ test.assertEqual(prefs.get("test_set_get_number_pref"), 42,
+ "out-of-range write attempt should not overwrite 1");
+ prefs.set("test_set_get_number_pref", Math.pow(2, 31)-1);
+ test.assertEqual(prefs.get("test_set_get_number_pref"), 0x7fffffff,
+ "in-range write attempt should work 1");
+ prefs.set("test_set_get_number_pref", -Math.pow(2, 31));
+ test.assertEqual(prefs.get("test_set_get_number_pref"), -0x80000000,
+ "in-range write attempt should work 2");
+ test.assertRaises(
+ function() { prefs.set("test_set_get_number_pref", -0x80000001); },
+ ("you cannot set the test_set_get_number_pref pref to the number " +
+ "-2147483649, as number pref values must be in the signed 32-bit " +
+ "integer range -(2^31) to 2^31-1. To store numbers outside that " +
+ "range, store them as strings."),
+ "setting an int pref outside the range -(2^31) to 2^31-1 shouldn't work"
+ );
+ test.assertEqual(prefs.get("test_set_get_number_pref"), -0x80000000,
+ "out-of-range write attempt should not overwrite 2");
+
+
+ prefs.set("test_set_get_pref.string", "foo");
+ test.assertEqual(prefs.get("test_set_get_pref.string"), "foo",
+ "set/get string preference should work");
+
+ prefs.set("test_set_get_pref.boolean", true);
+ test.assertEqual(prefs.get("test_set_get_pref.boolean"), true,
+ "set/get boolean preference should work");
+
+ prefs.set("test_set_get_unicode_pref", String.fromCharCode(960));
+ test.assertEqual(prefs.get("test_set_get_unicode_pref"),
+ String.fromCharCode(960),
+ "set/get unicode preference should work");
+
+ var unsupportedValues = [null, [], undefined];
+ unsupportedValues.forEach(
+ function(value) {
+ test.assertRaises(
+ function() { prefs.set("test_set_pref", value); },
+ ("can't set pref test_set_pref to value '" + value + "'; " +
+ "it isn't a string, integer, or boolean"),
+ "Setting a pref to " + uneval(value) + " should raise error"
+ );
+ });
+};
+
+exports.testGetSetLocalized = function(test) {
+ let prefName = "general.useragent.locale";
+
+ // Ensure that "general.useragent.locale" is a 'localized' pref
+ let bundleURL = "chrome://global/locale/intl.properties";
+ prefs.setLocalized(prefName, bundleURL);
+
+ // Fetch the expected value directly from the property file
+ let expectedValue = BundleService.createBundle(bundleURL).
+ GetStringFromName(prefName).
+ toLowerCase();
+
+ test.assertEqual(prefs.getLocalized(prefName).toLowerCase(),
+ expectedValue,
+ "get localized preference");
+
+ // Undo our modification
+ prefs.reset(prefName);
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-process.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-process.js
new file mode 100644
index 0000000..43d0864
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-process.js
@@ -0,0 +1,36 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { Loader } = require("./helpers");
+const { jetpackID } = require('@packaging');
+
+// bug 707562: '#' char in packaging was causing loader to be undefined
+exports.testBug707562 = function(test) {
+ test.waitUntilDone();
+
+ let packaging = JSON.parse(JSON.stringify(require("@packaging")));
+ packaging.metadata["api-utils"].author = "###";
+
+ let loader = Loader(module, {}, packaging);
+ let process = loader.require("process");
+
+ // has spawn?
+ test.assert(process.spawn, "'process' module exports 'spawn' method.");
+
+ let promise = process.spawn("testID", "");
+ test.assertFunction(promise, "spawn makes a promise.");
+
+ promise(function(addon) {
+ addon.channel("TEST:LOADED").input(function(data) {
+ test.assert(data, "The loader was successfully created!");
+ loader.unload();
+ test.done();
+ });
+
+ addon.loadScript('data:,sendAsyncMessage("'+jetpackID+':TEST:LOADED", !!this.loader);', false);
+ test.pass("spawn's promise was delivered! (which means a addon process object is available)).");
+ });
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-promise.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-promise.js
new file mode 100644
index 0000000..fc7706f
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-promise.js
@@ -0,0 +1,311 @@
+/* vim:set ts=2 sw=2 sts=2 expandtab */
+/*jshint asi: true undef: true es5: true node: true devel: true
+ forin: true */
+/*global define: true */
+
+'use strict';
+
+var core = require('api-utils/promise'),
+ defer = core.defer, resolve = core.resolve, reject = core.reject,
+ promised = core.promised
+
+exports['test all observers are notified'] = function(assert, done) {
+ var expected = 'Taram pam param!'
+ var deferred = defer()
+ var pending = 10, i = 0
+
+ function resolved(value) {
+ assert.equal(value, expected, 'value resolved as expected: #' + pending)
+ if (!--pending) done()
+ }
+
+ while (i++ < pending) deferred.promise.then(resolved)
+
+ deferred.resolve(expected)
+}
+
+exports['test exceptions dont stop notifications'] = function(assert, done) {
+ var threw = false, boom = Error('Boom!')
+ var deferred = defer()
+
+ var promise2 = deferred.promise.then(function() {
+ threw = true
+ throw boom
+ })
+
+ deferred.promise.then(function() {
+ assert.ok(threw, 'observer is called even though previos one threw')
+ promise2.then(function() {
+ assert.fail('should not resolve')
+ }, function(reason) {
+ assert.equal(reason, boom, 'rejects to thrown error')
+ done()
+ })
+ })
+
+ deferred.resolve('go!')
+}
+
+exports['test subsequent resolves are ignored'] = function(assert, done) {
+ var deferred = defer()
+ deferred.resolve(1)
+ deferred.resolve(2)
+ deferred.reject(3)
+
+ deferred.promise.then(function(actual) {
+ assert.equal(actual, 1, 'resolves to first value')
+ }, function() {
+ assert.fail('must not reject')
+ })
+ deferred.promise.then(function(actual) {
+ assert.equal(actual, 1, 'subsequent resolutions are ignored')
+ done()
+ }, function() {
+ assert.fail('must not reject')
+ })
+}
+
+exports['test subsequent rejections are ignored'] = function(assert, done) {
+ var deferred = defer()
+ deferred.reject(1)
+ deferred.resolve(2)
+ deferred.reject(3)
+
+ deferred.promise.then(function(actual) {
+ assert.fail('must not resolve')
+ }, function(actual) {
+ assert.equal(actual, 1, 'must reject to first')
+ })
+ deferred.promise.then(function(actual) {
+ assert.fail('must not resolve')
+ }, function(actual) {
+ assert.equal(actual, 1, 'must reject to first')
+ done()
+ })
+}
+
+exports['test error recovery'] = function(assert, done) {
+ var boom = Error('Boom!')
+ var deferred = defer()
+
+ deferred.promise.then(function() {
+ assert.fail('rejected promise should not resolve')
+ }, function(reason) {
+ assert.equal(reason, boom, 'rejection reason delivered')
+ return 'recovery'
+ }).then(function(value) {
+ assert.equal(value, 'recovery', 'error handled by a handler')
+ done()
+ })
+
+ deferred.reject(boom)
+}
+
+
+exports['test error recovery with promise'] = function(assert, done) {
+ var deferred = defer()
+
+ deferred.promise.then(function() {
+ assert.fail('must reject')
+ }, function(actual) {
+ assert.equal(actual, 'reason', 'rejected')
+ var deferred = defer()
+ deferred.resolve('recovery')
+ return deferred.promise
+ }).then(function(actual) {
+ assert.equal(actual, 'recovery', 'recorvered via promise')
+ var deferred = defer()
+ deferred.reject('error')
+ return deferred.promise
+ }).then(null, function(actual) {
+ assert.equal(actual, 'error', 'rejected via promise')
+ var deferred = defer()
+ deferred.reject('end')
+ return deferred.promise
+ }).then(null, function(actual) {
+ assert.equal(actual, 'end', 'rejeced via promise')
+ done()
+ })
+
+ deferred.reject('reason')
+}
+
+exports['test propagation'] = function(assert, done) {
+ var d1 = defer(), d2 = defer(), d3 = defer()
+
+ d1.promise.then(function(actual) {
+ assert.equal(actual, 'expected', 'resolves to expected value')
+ done()
+ })
+
+ d1.resolve(d2.promise)
+ d2.resolve(d3.promise)
+ d3.resolve('expected')
+}
+
+exports['test chaining'] = function(assert, done) {
+ var boom = Error('boom'), brax = Error('braxXXx')
+ var deferred = defer()
+
+ deferred.promise.then().then().then(function(actual) {
+ assert.equal(actual, 2, 'value propagates unchanged')
+ return actual + 2
+ }).then(null, function(reason) {
+ assert.fail('should not reject')
+ }).then(function(actual) {
+ assert.equal(actual, 4, 'value propagates through if not handled')
+ throw boom
+ }).then(function(actual) {
+ assert.fail('exception must reject promise')
+ }).then().then(null, function(actual) {
+ assert.equal(actual, boom, 'reason propagates unchanged')
+ throw brax
+ }).then().then(null, function(actual) {
+ assert.equal(actual, brax, 'reason changed becase of exception')
+ return 'recovery'
+ }).then(function(actual) {
+ assert.equal(actual, 'recovery', 'recovered from error')
+ done()
+ })
+
+ deferred.resolve(2)
+}
+
+
+exports['test reject'] = function(assert, done) {
+ var expected = Error('boom')
+
+ reject(expected).then(function() {
+ assert.fail('should reject')
+ }, function(actual) {
+ assert.equal(actual, expected, 'rejected with expected reason')
+ }).then(function() {
+ done()
+ })
+}
+
+exports['test resolve to rejected'] = function(assert, done) {
+ var expected = Error('boom')
+ var deferred = defer()
+
+ deferred.promise.then(function() {
+ assert.fail('should reject')
+ }, function(actual) {
+ assert.equal(actual, expected, 'rejected with expected failure')
+ }).then(function() {
+ done()
+ })
+
+ deferred.resolve(reject(expected))
+}
+
+exports['test resolve'] = function(assert, done) {
+ var expected = 'value'
+ resolve(expected).then(function(actual) {
+ assert.equal(actual, expected, 'resolved as expected')
+ }).then(function() {
+ done()
+ })
+}
+
+exports['test resolve with prototype'] = function(assert, done) {
+ var seventy = resolve(70, {
+ subtract: function subtract(y) {
+ return this.then(function(x) { return x - y })
+ }
+ })
+
+ seventy.subtract(17).then(function(actual) {
+ assert.equal(actual, 70 - 17, 'resolves to expected')
+ done()
+ })
+}
+
+exports['test promised with normal args'] = function(assert, done) {
+ var sum = promised(function(x, y) { return x + y })
+
+ sum(7, 8).then(function(actual) {
+ assert.equal(actual, 7 + 8, 'resolves as expected')
+ done()
+ })
+}
+
+exports['test promised with promise args'] = function(assert, done) {
+ var sum = promised(function(x, y) { return x + y })
+ var deferred = defer()
+
+ sum(11, deferred.promise).then(function(actual) {
+ assert.equal(actual, 11 + 24, 'resolved as expected')
+ done()
+ })
+
+ deferred.resolve(24)
+}
+
+exports['test promised with prototype'] = function(assert, done) {
+ var deferred = defer()
+ var numeric = {}
+ numeric.subtract = promised(function(y) { return this - y }, numeric)
+
+ var sum = promised(function(x, y) { return x + y }, numeric)
+
+ sum(7, 70).
+ subtract(14).
+ subtract(deferred.promise).
+ subtract(5).
+ then(function(actual) {
+ assert.equal(actual, 7 + 70 - 14 - 23 - 5, 'resolved as expected')
+ done()
+ })
+
+ deferred.resolve(23)
+}
+
+exports['test promised error handleing'] = function(assert, done) {
+ var expected = Error('boom')
+ var f = promised(function() {
+ throw expected
+ })
+
+ f().then(function() {
+ assert.fail('should reject')
+ }, function(actual) {
+ assert.equal(actual, expected, 'rejected as expected')
+ done()
+ })
+}
+
+exports['test return promise form promised'] = function(assert, done) {
+ var f = promised(function() {
+ return resolve(17)
+ })
+
+ f().then(function(actual) {
+ assert.equal(actual, 17, 'resolves to a promise resolution')
+ done()
+ })
+}
+
+exports['test promised returning failure'] = function(assert, done) {
+ var expected = Error('boom')
+ var f = promised(function() {
+ return reject(expected)
+ })
+
+ f().then(function() {
+ assert.fail('must reject')
+ }, function(actual) {
+ assert.equal(actual, expected, 'rejects with expected reason')
+ done()
+ })
+}
+
+exports['test promised are greedy'] = function(assert, done) {
+ var runs = 0
+ var f = promised(function() { ++runs })
+ var promise = f()
+ assert.equal(runs, 1, 'promised runs task right away')
+ done()
+}
+
+require("test").run(exports)
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-querystring.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-querystring.js
new file mode 100644
index 0000000..30b2729
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-querystring.js
@@ -0,0 +1,206 @@
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+"use strict";
+
+// test using assert
+var qs = require('api-utils/querystring');
+
+// folding block, commented to pass gjslint
+// {{{
+// [ wonkyQS, canonicalQS, obj ]
+var qsTestCases = [
+ ['foo=918854443121279438895193',
+ 'foo=918854443121279438895193',
+ {'foo': '918854443121279438895193'}],
+ ['foo=bar', 'foo=bar', {'foo': 'bar'}],
+ //['foo=bar&foo=quux', 'foo=bar&foo=quux', {'foo': ['bar', 'quux']}],
+ ['foo=1&bar=2', 'foo=1&bar=2', {'foo': '1', 'bar': '2'}],
+ // ['my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F',
+ // 'my%20weird%20field=q1!2%22\'w%245%267%2Fz8)%3F',
+ // {'my weird field': 'q1!2"\'w$5&7/z8)?' }],
+ ['foo%3Dbaz=bar', 'foo%3Dbaz=bar', {'foo=baz': 'bar'}],
+ ['foo=baz=bar', 'foo=baz%3Dbar', {'foo': 'baz=bar'}],
+ /*
+ ['str=foo&arr=1&arr=2&arr=3&somenull=&undef=',
+ 'str=foo&arr=1&arr=2&arr=3&somenull=&undef=',
+ { 'str': 'foo',
+ 'arr': ['1', '2', '3'],
+ 'somenull': '',
+ 'undef': ''}],
+ */
+ //[' foo = bar ', '%20foo%20=%20bar%20', {' foo ': ' bar '}],
+ // disable test that fails ['foo=%zx', 'foo=%25zx', {'foo': '%zx'}],
+ ['foo=%EF%BF%BD', 'foo=%EF%BF%BD', {'foo': '\ufffd' }]
+];
+
+// [ wonkyQS, canonicalQS, obj ]
+var qsColonTestCases = [
+ ['foo:bar', 'foo:bar', {'foo': 'bar'}],
+ //['foo:bar;foo:quux', 'foo:bar;foo:quux', {'foo': ['bar', 'quux']}],
+ ['foo:1&bar:2;baz:quux',
+ 'foo:1%26bar%3A2;baz:quux',
+ {'foo': '1&bar:2', 'baz': 'quux'}],
+ ['foo%3Abaz:bar', 'foo%3Abaz:bar', {'foo:baz': 'bar'}],
+ ['foo:baz:bar', 'foo:baz%3Abar', {'foo': 'baz:bar'}]
+];
+
+// [wonkyObj, qs, canonicalObj]
+var extendedFunction = function() {};
+extendedFunction.prototype = {a: 'b'};
+var qsWeirdObjects = [
+ //[{regexp: /./g}, 'regexp=', {'regexp': ''}],
+ //[{regexp: new RegExp('.', 'g')}, 'regexp=', {'regexp': ''}],
+ //[{fn: function() {}}, 'fn=', {'fn': ''}],
+ //[{fn: new Function('')}, 'fn=', {'fn': ''}],
+ //[{math: Math}, 'math=', {'math': ''}],
+ //[{e: extendedFunction}, 'e=', {'e': ''}],
+ //[{d: new Date()}, 'd=', {'d': ''}],
+ //[{d: Date}, 'd=', {'d': ''}],
+ //[{f: new Boolean(false), t: new Boolean(true)}, 'f=&t=', {'f': '', 't': ''}],
+ [{f: false, t: true}, 'f=false&t=true', {'f': 'false', 't': 'true'}],
+ //[{n: null}, 'n=', {'n': ''}],
+ //[{nan: NaN}, 'nan=', {'nan': ''}],
+ //[{inf: Infinity}, 'inf=', {'inf': ''}]
+];
+// }}}
+
+var qsNoMungeTestCases = [
+ ['', {}],
+ //['foo=bar&foo=baz', {'foo': ['bar', 'baz']}],
+ ['blah=burp', {'blah': 'burp'}],
+ //['gragh=1&gragh=3&goo=2', {'gragh': ['1', '3'], 'goo': '2'}],
+ ['frappucino=muffin&goat%5B%5D=scone&pond=moose',
+ {'frappucino': 'muffin', 'goat[]': 'scone', 'pond': 'moose'}],
+ ['trololol=yes&lololo=no', {'trololol': 'yes', 'lololo': 'no'}]
+];
+
+exports['test basic'] = function(assert) {
+ assert.strictEqual('918854443121279438895193',
+ qs.parse('id=918854443121279438895193').id,
+ 'prase id=918854443121279438895193');
+};
+
+exports['test that the canonical qs is parsed properly'] = function(assert) {
+ qsTestCases.forEach(function(testCase) {
+ assert.deepEqual(testCase[2], qs.parse(testCase[0]),
+ 'parse ' + testCase[0]);
+ });
+};
+
+
+exports['test that the colon test cases can do the same'] = function(assert) {
+ qsColonTestCases.forEach(function(testCase) {
+ assert.deepEqual(testCase[2], qs.parse(testCase[0], ';', ':'),
+ 'parse ' + testCase[0] + ' -> ; :');
+ });
+};
+
+exports['test the weird objects, that they get parsed properly'] = function(assert) {
+ qsWeirdObjects.forEach(function(testCase) {
+ assert.deepEqual(testCase[2], qs.parse(testCase[1]),
+ 'parse ' + testCase[1]);
+ });
+};
+
+exports['test non munge test cases'] = function(assert) {
+ qsNoMungeTestCases.forEach(function(testCase) {
+ //console.log(testCase[0], JSON.stringify(testCase[1]), qs.stringify(testCase[1], '&', '=', false));
+ assert.deepEqual(testCase[0], qs.stringify(testCase[1], '&', '=', false),
+ 'stringify ' + JSON.stringify(testCase[1]) + ' -> & =');
+ });
+};
+
+exports['test the nested qs-in-qs case'] = function(assert) {
+ var f = qs.parse('a=b&q=x%3Dy%26y%3Dz');
+ f.q = qs.parse(f.q);
+ assert.deepEqual(f, { a: 'b', q: { x: 'y', y: 'z' } },
+ 'parse a=b&q=x%3Dy%26y%3Dz');
+};
+
+exports['test nested in colon'] = function(assert) {
+ var f = qs.parse('a:b;q:x%3Ay%3By%3Az', ';', ':');
+ f.q = qs.parse(f.q, ';', ':');
+ assert.deepEqual(f, { a: 'b', q: { x: 'y', y: 'z' } },
+ 'parse a:b;q:x%3Ay%3By%3Az -> ; :');
+};
+
+exports['test stringifying'] = function(assert) {
+ qsTestCases.forEach(function(testCase) {
+ assert.equal(testCase[1], qs.stringify(testCase[2]),
+ 'stringify ' + JSON.stringify(testCase[2]));
+ });
+
+ qsColonTestCases.forEach(function(testCase) {
+ assert.equal(testCase[1], qs.stringify(testCase[2], ';', ':'),
+ 'stringify ' + JSON.stringify(testCase[2]) + ' -> ; :');
+ });
+
+ qsWeirdObjects.forEach(function(testCase) {
+ assert.equal(testCase[1], qs.stringify(testCase[0]),
+ 'stringify ' + JSON.stringify(testCase[0]));
+ });
+};
+
+exports['test stringifying nested'] = function(assert) {
+ var f = qs.stringify({
+ a: 'b',
+ q: qs.stringify({
+ x: 'y',
+ y: 'z'
+ })
+ });
+ assert.equal(f, 'a=b&q=x%3Dy%26y%3Dz',
+ JSON.stringify({
+ a: 'b',
+ 'qs.stringify -> q': {
+ x: 'y',
+ y: 'z'
+ }
+ }));
+
+ var threw = false;
+ try { qs.parse(undefined); } catch(error) { threw = true; }
+ assert.ok(!threw, "does not throws on undefined");
+};
+
+exports['test nested in colon'] = function(assert) {
+ var f = qs.stringify({
+ a: 'b',
+ q: qs.stringify({
+ x: 'y',
+ y: 'z'
+ }, ';', ':')
+ }, ';', ':');
+ assert.equal(f, 'a:b;q:x%3Ay%3By%3Az',
+ 'stringify ' + JSON.stringify({
+ a: 'b',
+ 'qs.stringify -> q': {
+ x: 'y',
+ y: 'z'
+ }
+ }) + ' -> ; : ');
+
+
+ assert.deepEqual({}, qs.parse(), 'parse undefined');
+};
+
+require("test").run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-registry.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-registry.js
new file mode 100644
index 0000000..c5820fb
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-registry.js
@@ -0,0 +1,80 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+exports['test:add'] = function(test) {
+ function Class() {}
+ let fixture = require('utils/registry').Registry(Class);
+ let isAddEmitted = false;
+ fixture.on('add', function(item) {
+ test.assert(
+ item instanceof Class,
+ 'if object added is not an instance should construct instance from it'
+ );
+ test.assert(
+ fixture.has(item),
+ 'callback is called after instance is added'
+ );
+ test.assert(
+ !isAddEmitted,
+ 'callback should be called for the same item only once'
+ );
+ isAddEmitted = true;
+ });
+
+ let object = fixture.add({});
+ fixture.add(object);
+};
+
+exports['test:remove'] = function(test) {
+ function Class() {}
+ let fixture = require('utils/registry').Registry(Class);
+ fixture.on('remove', function(item) {
+ test.assert(
+ item instanceof Class,
+ 'if object removed can be only instance of Class'
+ );
+ test.assert(
+ fixture.has(item),
+ 'callback is called before instance is removed'
+ );
+ test.assert(
+ !isRemoveEmitted,
+ 'callback should be called for the same item only once'
+ );
+ isRemoveEmitted = true;
+ });
+
+ fixture.remove({});
+ let object = fixture.add({});
+ fixture.remove(object);
+ fixture.remove(object);
+};
+
+exports['test:items'] = function(test) {
+ function Class() {}
+ let fixture = require('utils/registry').Registry(Class),
+ actual,
+ times = 0;
+
+ function testItem(item) {
+ times ++;
+ test.assertEqual(
+ actual,
+ item,
+ 'item should match actual item being added/removed'
+ );
+ }
+
+ actual = fixture.add({});
+
+ fixture.on('add', testItem);
+ fixture.on('remove', testItem);
+
+ fixture.remove(actual);
+ fixture.remove(fixture.add(actual = new Class()));
+ test.assertEqual(3, times, 'should notify listeners on each call');
+}
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-require.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-require.js
new file mode 100644
index 0000000..7508883
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-require.js
@@ -0,0 +1,29 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const traceback = require("traceback");
+
+exports.test_no_args = function(test) {
+ var passed = false;
+ try {
+ var oops = require(); // leave this on line 6!
+ } catch(e) {
+ let msg = e.toString();
+ test.assertEqual(msg.indexOf("Error: you must provide a module name when calling require() from "), 0);
+ test.assertNotEqual(msg.indexOf("test-require"), -1, msg);
+ // we'd also like to assert that the right filename and linenumber is in
+ // the stack trace, but this currently doesn't work (see bugs 679591 and
+ // 551604)
+ if (0) {
+ let tb = traceback.fromException(e);
+ let lastFrame = tb[tb.length-1];
+ test.assertNotEqual(lastFrame.filename.indexOf("test-require.js"), -1,
+ lastFrame.filename);
+ test.assertEqual(lastFrame.lineNo, 6);
+ test.assertEqual(lastFrame.funcName, "??");
+ }
+ passed = true;
+ }
+ test.assert(passed, 'require() with no args should raise helpful error');
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-sandbox.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-sandbox.js
new file mode 100644
index 0000000..fb7a1da
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-sandbox.js
@@ -0,0 +1,113 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 { sandbox, load, evaluate } = require('api-utils/sandbox');
+const fixturesURI = module.uri.split('test-sandbox.js')[0] + 'fixtures/';
+
+
+exports['test basics'] = function(assert) {
+ let fixture = sandbox('http://example.com');
+ assert.equal(evaluate(fixture, 'var a = 1;'), undefined,
+ 'returns expression value');
+ assert.equal(evaluate(fixture, 'b = 2;'), 2,
+ 'returns expression value');
+ assert.equal(fixture.b, 2, 'global is defined as property');
+ assert.equal(fixture.a, 1, 'global is defined as property');
+ assert.equal(evaluate(fixture, 'a + b;'), 3, 'returns correct sum');
+};
+
+exports['test non-privileged'] = function(assert) {
+ let fixture = sandbox('http://example.com');
+ assert.throws(function() {
+ evaluate(fixture, 'Compo' + 'nents.utils');
+ }, 'Access to components is restricted');
+ fixture.sandbox = sandbox;
+ assert.throws(function() {
+ evaluate(fixture, sandbox('http://foo.com'));
+ }, 'Can not call privileged code');
+};
+
+exports['test injection'] = function(assert) {
+ let fixture = sandbox();
+ fixture.hi = function(name) 'Hi ' + name
+ assert.equal(evaluate(fixture, 'hi("sandbox");'), 'Hi sandbox',
+ 'injected functions are callable');
+};
+
+exports['test exceptions'] = function(assert) {
+ let fixture = sandbox();
+ try {
+ evaluate(fixture, '!' + function() {
+ var message = 'boom';
+ throw Error(message);
+ } + '();');
+ }
+ catch (error) {
+ assert.equal(error.fileName, '', 'no fileName reported');
+ assert.equal(error.lineNumber, 3, 'reports correct line number');
+ }
+
+ try {
+ evaluate(fixture, '!' + function() {
+ var message = 'boom';
+ throw Error(message);
+ } + '();', 'foo.js');
+ }
+ catch (error) {
+ assert.equal(error.fileName, 'foo.js', 'correct fileName reported');
+ assert.equal(error.lineNumber, 3, 'reports correct line number');
+ }
+
+ try {
+ evaluate(fixture, '!' + function() {
+ var message = 'boom';
+ throw Error(message);
+ } + '();', 'foo.js', 2);
+ }
+ catch (error) {
+ assert.equal(error.fileName, 'foo.js', 'correct fileName reported');
+ assert.equal(error.lineNumber, 4, 'line number was opted');
+ }
+};
+
+exports['test opt version'] = function(assert) {
+ let fixture = sandbox();
+ assert.throws(function() {
+ evaluate(fixture, 'let a = 2;', 'test.js', 1, '1.5');
+ }, 'No let in js 1.5');
+};
+
+exports['test load'] = function(assert) {
+ let fixture = sandbox();
+ load(fixture, fixturesURI + 'sandbox-normal.js');
+ assert.equal(fixture.a, 1, 'global variable defined');
+ assert.equal(fixture.b, 2, 'global via `this` property was set');
+ assert.equal(fixture.f(), 4, 'function was defined');
+};
+
+exports['test load with data: URL'] = function(assert) {
+ let code = "var a = 1; this.b = 2; function f() 4";
+ let fixture = sandbox();
+ load(fixture, "data:," + encodeURIComponent(code));
+
+ assert.equal(fixture.a, 1, 'global variable defined');
+ assert.equal(fixture.b, 2, 'global via `this` property was set');
+ assert.equal(fixture.f(), 4, 'function was defined');
+};
+
+exports['test load script with complex char'] = function(assert) {
+ let fixture = sandbox();
+ load(fixture, fixturesURI + 'sandbox-complex-character.js');
+ assert.equal(fixture.chars, 'გამარჯობა', 'complex chars were loaded correctly');
+};
+
+exports['test load script with data: URL and complex char'] = function(assert) {
+ let code = "var chars = 'გამარჯობა';";
+ let fixture = sandbox();
+ load(fixture, "data:," + encodeURIComponent(code));
+
+ assert.equal(fixture.chars, 'გამარჯობა', 'complex chars were loaded correctly');
+};
+
+require('test').run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-self.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-self.js
new file mode 100644
index 0000000..2f0e093
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-self.js
@@ -0,0 +1,54 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {Cc, Ci, Cu, Cm, components} = require('chrome');
+Cu.import("resource://gre/modules/AddonManager.jsm", this);
+
+exports.testSelf = function(test) {
+ var self = require("self");
+
+ var source = self.data.load("test-content-symbiont.js");
+ test.assert(source.match(/test-content-symbiont/), "self.data.load() works");
+
+ // Likewise, we can't assert anything about the full URL, because that
+ // depends on self.id . We can only assert that it ends in the right
+ // thing.
+ var url = self.data.url("test-content-symbiont.js");
+ test.assertEqual(typeof(url), "string", "self.data.url('x') returns string");
+ test.assertEqual(/\/test-content-symbiont\.js$/.test(url), true);
+
+ // Make sure 'undefined' is not in url when no string is provided.
+ url = self.data.url();
+ test.assertEqual(typeof(url), "string", "self.data.url() returns string");
+ test.assertEqual(/\/undefined$/.test(url), false);
+
+ // When tests are run on just the api-utils package, self.name is
+ // api-utils. When they're run as 'cfx testall', self.name is testpkgs.
+ test.assert((self.name == "api-utils") || (self.name == "testpkgs"),
+ "self.name is api-utils or testpkgs");
+};
+
+exports.testSelfID = function(test) {
+ test.waitUntilDone();
+
+ var self = require("self");
+ // We can't assert anything about the ID inside the unit test right now,
+ // because the ID we get depends upon how the test was invoked. The idea
+ // is that it is supposed to come from the main top-level package's
+ // package.json file, from the "id" key.
+ test.assertEqual(typeof(self.id), "string", "self.id is a string");
+ test.assert(self.id.length > 0);
+
+ AddonManager.getAddonByID(self.id, function(addon) {
+ if (!addon) {
+ test.fail("did not find addon with self.id");
+ }
+ else {
+ test.pass("found addon with self.id");
+ }
+ test.done();
+ });
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-set-exports.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-set-exports.js
new file mode 100644
index 0000000..5221237
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-set-exports.js
@@ -0,0 +1,35 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+let four = require("./modules/exportsEquals");
+exports.testExportsEquals = function(test) {
+ test.assertEqual(four, 4);
+}
+
+/* TODO: Discuss idea of dropping support for this feature that was alternative
+ to `module.exports = ..` that failed.
+let five = require("./modules/setExports");
+exports.testSetExports = function(test) {
+ test.assertEqual(five, 5);
+}
+
+exports.testDupeSetExports = function(test) {
+ var passed = false;
+ try {
+ var dupe = require('./modules/dupeSetExports');
+ } catch(e) {
+ passed = /define\(\) was used, so module\.exports= and module\.setExports\(\) may not be used/.test(e.toString());
+ }
+ test.assertEqual(passed, true, 'define() or setExports(), not both');
+}
+*/
+
+exports.testModule = function(test) {
+ // module.id is not cast in stone yet. In the future, it may include the
+ // package name, or may possibly be a/ URL of some sort. For now, it's a
+ // URL that starts with resource: and ends with this module name, but the
+ // part in between varies depending upon how the test is run.
+ var found = /test-set-exports$/.test(module.id);
+ test.assertEqual(found, true, module.id+" ends with test-set-exports.js");
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-tab-browser.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-tab-browser.js
new file mode 100644
index 0000000..31c5656
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-tab-browser.js
@@ -0,0 +1,493 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var timer = require("timer");
+var {Cc,Ci} = require("chrome");
+
+function onBrowserLoad(callback, event) {
+ if (event.target && event.target.defaultView == this) {
+ this.removeEventListener("load", onBrowserLoad, true);
+ let browsers = this.document.getElementsByTagName("tabbrowser");
+ try {
+ require("timer").setTimeout(function (window) {
+ callback(window, browsers[0]);
+ }, 10, this);
+ } catch (e) { console.exception(e); }
+ }
+}
+// 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", onBrowserLoad.bind(window, callback), true);
+
+ return window;
+}
+
+// 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);
+}
+
+// Helper for opening two windows at once
+function openTwoWindows(callback) {
+ openBrowserWindow(function (window1) {
+ openBrowserWindow(function (window2) {
+ callback(window1, window2);
+ });
+ });
+}
+
+// Helper for closing two windows at once
+function closeTwoWindows(window1, window2, callback) {
+ closeBrowserWindow(window1, function() {
+ closeBrowserWindow(window2, callback);
+ });
+}
+
+exports.testAddTab = function(test) {
+ test.waitUntilDone();
+ openBrowserWindow(function(window, browser) {
+ const tabBrowser = require("tab-browser");
+
+ let cache = [];
+ let windowUtils = require("window-utils");
+ new windowUtils.WindowTracker({
+ onTrack: function(win) {
+ cache.push(win);
+ },
+ onUntrack: function(win) {
+ cache.splice(cache.indexOf(win), 1)
+ }
+ });
+ let startWindowCount = cache.length;
+
+ // Test 1: add a tab
+ let firstUrl = "data:text/html,one";
+ tabBrowser.addTab(firstUrl, {
+ onLoad: function(e) {
+ let win1 = cache[startWindowCount - 1];
+ test.assertEqual(win1.content.location, firstUrl, "URL of new tab in first window matches");
+
+ // Test 2: add a tab in a new window
+ let secondUrl = "data:text/html,two";
+ tabBrowser.addTab(secondUrl, {
+ inNewWindow: true,
+ onLoad: function(e) {
+ test.assertEqual(cache.length, startWindowCount + 1, "a new window was opened");
+ let win2 = cache[startWindowCount];
+ let gBrowser = win2.gBrowser;
+ gBrowser.addEventListener("DOMContentLoaded", function onLoad(e) {
+ gBrowser.removeEventListener("DOMContentLoaded", onLoad, false);
+ test.assertEqual(win2.content.location, secondUrl, "URL of new tab in the new window matches");
+
+ closeBrowserWindow(win2, function() {
+ closeBrowserWindow(win1, function() {
+ test.done();
+ });
+ });
+ }, false);
+ }
+ });
+ }
+ });
+ });
+};
+
+exports.testTrackerWithDelegate = function(test) {
+ test.waitUntilDone();
+ const tabBrowser = require("tab-browser");
+
+ var delegate = {
+ state: "initializing",
+ onTrack: function onTrack(browser) {
+ if (this.state == "initializing") {
+ this.state = "waiting for browser window to open";
+ }
+ else if (this.state == "waiting for browser window to open") {
+ this.state = "waiting for browser window to close";
+ require("timer").setTimeout(function() {
+ closeBrowserWindow(browser.ownerDocument.defaultView, function() {
+ test.assertEqual(delegate.state, "deinitializing");
+ tb.unload();
+ test.done();
+ });
+ }, 0);
+ }
+ else
+ test.fail("invalid state");
+ },
+ onUntrack: function onUntrack(browser) {
+ if (this.state == "waiting for browser window to close") {
+ test.pass("proper state in onUntrack");
+ this.state = "deinitializing";
+ }
+ else if (this.state != "deinitializing")
+ test.fail("invalid state");
+ }
+ };
+ var tb = new tabBrowser.Tracker(delegate);
+
+ delegate.state = "waiting for browser window to open";
+
+ openBrowserWindow();
+};
+
+exports.testWhenContentLoaded = function(test) {
+ test.waitUntilDone();
+ const tabBrowser = require("tab-browser");
+
+ var tracker = tabBrowser.whenContentLoaded(
+ function(window) {
+ var item = window.document.getElementById("foo");
+ test.assertEqual(item.textContent, "bar",
+ "whenContentLoaded() works.");
+ tracker.unload();
+ closeBrowserWindow(activeWindow(), function() {
+ test.done();
+ });
+ });
+
+ openBrowserWindow(function(browserWindow, browser) {
+ var html = '<div id="foo">bar</div>';
+ browser.addTab("data:text/html," + html);
+ });
+};
+
+exports.testTrackerWithoutDelegate = function(test) {
+ test.waitUntilDone();
+ const tabBrowser = require("tab-browser");
+
+ openBrowserWindow(function(browserWindow, browser) {
+ var tb = new tabBrowser.Tracker();
+
+ if (tb.length == 0)
+ test.fail("expect at least one tab browser to exist.");
+
+ for (var i = 0; i < tb.length; i++)
+ test.assertEqual(tb.get(i).nodeName, "tabbrowser",
+ "get() method and length prop should work");
+ for (var b in tb)
+ test.assertEqual(b.nodeName, "tabbrowser",
+ "iterator should work");
+
+ var matches = [b for (b in tb)
+ if (b == browser)];
+ test.assertEqual(matches.length, 1,
+ "New browser should be in tracker.");
+ tb.unload();
+
+ closeBrowserWindow(browserWindow, function() {
+ test.done();
+ });
+ });
+};
+
+exports.testTabTracker = function(test) {
+ test.waitUntilDone();
+ const tabBrowser = require("tab-browser");
+
+ openBrowserWindow(function(browserWindow, browser) {
+ var delegate = {
+ tracked: 0,
+ onTrack: function(tab) {
+ this.tracked++;
+ },
+ onUntrack: function(tab) {
+ this.tracked--;
+ }
+ };
+
+ let tabTracker = tabBrowser.TabTracker(delegate);
+
+ let tracked = delegate.tracked;
+ let url1 = "data:text/html,1";
+ let url2 = "data:text/html,2";
+ let url3 = "data:text/html,3";
+ let tabCount = 0;
+
+ function tabLoadListener(e) {
+ let loadedURL = e.target.defaultView.location;
+ if (loadedURL == url1)
+ tabCount++;
+ else if (loadedURL == url2)
+ tabCount++;
+ else if (loadedURL == url3)
+ tabCount++;
+
+ if (tabCount == 3) {
+ test.assertEqual(delegate.tracked, tracked + 3, "delegate tracked tabs matched count");
+ tabTracker.unload();
+ closeBrowserWindow(browserWindow, function() {
+ require("timer").setTimeout(function() test.done(), 0);
+ });
+ }
+ }
+
+ tabBrowser.addTab(url1, {
+ onLoad: tabLoadListener
+ });
+ tabBrowser.addTab(url2, {
+ onLoad: tabLoadListener
+ });
+ tabBrowser.addTab(url3, {
+ onLoad: tabLoadListener
+ });
+ });
+};
+
+exports.testActiveTab = function(test) {
+ test.waitUntilDone();
+ openBrowserWindow(function(browserWindow, browser) {
+ const tabBrowser = require("tab-browser");
+ const TabModule = require("tab-browser").TabModule;
+ let tm = new TabModule(browserWindow);
+ test.assertEqual(tm.length, 1);
+ let url1 = "data:text/html,foo";
+ let url2 = "data:text/html,bar";
+
+ function tabURL(tab) tab.ownerDocument.defaultView.content.location.toString()
+
+ tabBrowser.addTab(url1, {
+ onLoad: function(e) {
+ // make sure we're running in the right window.
+ test.assertEqual(tabBrowser.activeTab.ownerDocument.defaultView, browserWindow, "active window matches");
+ browserWindow.focus();
+
+ test.assertEqual(tabURL(tabBrowser.activeTab), url1, "url1 matches");
+ let tabIndex = browser.getBrowserIndexForDocument(e.target);
+ let tabAtIndex = browser.tabContainer.getItemAtIndex(tabIndex);
+ test.assertEqual(tabAtIndex, tabBrowser.activeTab, "activeTab element matches");
+
+ tabBrowser.addTab(url2, {
+ inBackground: true,
+ onLoad: function() {
+ test.assertEqual(tabURL(tabBrowser.activeTab), url1, "url1 still matches");
+ let tabAtIndex = browser.tabContainer.getItemAtIndex(tabIndex);
+ test.assertEqual(tabAtIndex, tabBrowser.activeTab, "activeTab element matches");
+ closeBrowserWindow(browserWindow, function() {
+ test.done()
+ });
+ }
+ });
+ }
+ });
+ });
+};
+
+// TabModule tests
+exports.testEventsAndLengthStayInModule = function(test) {
+ test.waitUntilDone();
+ let TabModule = require("tab-browser").TabModule;
+
+ openTwoWindows(function(window1, window2) {
+ let tm1 = new TabModule(window1);
+ let tm2 = new TabModule(window2);
+
+ let counter1 = 0, counter2 = 0;
+ let counterTabs = 0;
+
+ function onOpenListener() {
+ ++counterTabs;
+ if (counterTabs < 5)
+ return;
+ test.assertEqual(counter1, 2, "Correct number of events fired from window 1");
+ test.assertEqual(counter2, 3, "Correct number of events fired from window 2");
+ test.assertEqual(counterTabs, 5, "Correct number of events fired from all windows");
+ test.assertEqual(tm1.length, 3, "Correct number of tabs in window 1");
+ test.assertEqual(tm2.length, 4, "Correct number of tabs in window 2");
+ closeTwoWindows(window1, window2, function() test.done());
+ }
+
+ tm1.onOpen = function() ++counter1 && onOpenListener();
+ tm2.onOpen = function() ++counter2 && onOpenListener();
+
+ let url = "data:text/html,default";
+ tm1.open(url);
+ tm1.open(url);
+
+ tm2.open(url);
+ tm2.open(url);
+ tm2.open(url);
+ });
+}
+
+exports.testTabModuleActiveTab_getterAndSetter = function(test) {
+ test.waitUntilDone();
+ let TabModule = require("tab-browser").TabModule;
+
+ openTwoWindows(function(window1, window2) {
+ let tm1 = new TabModule(window1);
+ let tm2 = new TabModule(window2);
+
+ let tab1 = null;
+ tm1.open({
+ url: "data:text/html,<title>window1,tab1</title>",
+ onOpen: function(tab) tab1 = tab,
+ });
+ tm1.open("data:text/html,<title>window1,tab2</title>");
+
+ tm1.onActivate = function onActivate() {
+ tm1.onActivate.remove(onActivate);
+ require("timer").setTimeout(function() {
+ test.assertEqual(tm1.activeTab.title, "window1,tab1", "activeTab setter works");
+ closeTwoWindows(window1, window2, function() test.done());
+ }, 1000);
+ }
+
+ tm2.open("data:text/html,<title>window2,tab1</title>");
+ tm2.open({
+ url: "data:text/html,<title>window2,tab2</title>",
+ onOpen: function(tab4) {
+ test.assertEqual(tm1.activeTab.title, "window1,tab2", "Correct active tab on window 1");
+ test.assertEqual(tm2.activeTab.title, "window2,tab2", "Correct active tab on window 2");
+
+ tm1.activeTab = tab1;
+ tm1.activeTab = tab4; // Setting activeTab from another window should have no effect
+ }
+ });
+ });
+}
+
+// test tabs iterator
+exports.testTabModuleTabsIterator = function(test) {
+ test.waitUntilDone();
+ let TabModule = require("tab-browser").TabModule;
+
+ openBrowserWindow(function(window) {
+ let tm1 = new TabModule(window);
+ let url = "data:text/html,default";
+ tm1.open(url);
+ tm1.open(url);
+ tm1.open({
+ url: url,
+ onOpen: function(tab) {
+ let count = 0;
+ for each (let t in tm1) count++;
+ test.assertEqual(count, 4, "iterated tab count matches");
+ test.assertEqual(count, tm1.length, "length tab count matches");
+ closeBrowserWindow(window, function() test.done());
+ }
+ });
+ });
+};
+
+// inNewWindow parameter is ignored on single-window modules
+exports.testTabModuleCantOpenInNewWindow = function(test) {
+ test.waitUntilDone();
+ let TabModule = require("tab-browser").TabModule;
+
+ openBrowserWindow(function(window) {
+ let tm = new TabModule(window);
+ let url = "data:text/html,default";
+ tm.open({
+ url: url,
+ inNewWindow: true,
+ onOpen: function() {
+ test.assertEqual(tm.length, 2, "Tab was open on same window");
+ closeBrowserWindow(window, function() test.done());
+ }
+ });
+ });
+};
+
+// Test that having two modules attached to the same
+// window won't duplicate events fired on each module
+exports.testModuleListenersDontInteract = function(test) {
+ test.waitUntilDone();
+ let TabModule = require("tab-browser").TabModule;
+
+ openBrowserWindow(function(window) {
+ let tm1 = new TabModule(window);
+ let tm2 = new TabModule(window);
+
+ let url = "data:text/html,foo";
+ let eventCount = 0, eventModule1 = 0, eventModule2 = 0;
+
+
+ let listener1 = function() {
+ // this should be called twice: when tab is open and when
+ // the url location is changed
+ eventCount++;
+ eventModule1++;
+ }
+ tm1.onReady = listener1;
+
+ tm2.open({
+ url: "about:blank",
+ onOpen: function(tab) {
+ // add listener via property assignment
+ let listener2 = function() {
+ eventCount++;
+ eventModule2++;
+ };
+ tab.onReady = listener2;
+
+ // add listener via collection add
+ let listener3 = function() {
+ eventCount++;
+ eventModule2++;
+ };
+ tab.onReady.add(listener3);
+
+ tab.location = url;
+
+ test.waitUntilEqual(function () eventCount, 4,
+ "Correct global number of events")
+ .then(function () {
+ test.assertEqual(eventModule1, 2,
+ "Correct number of events on module 1");
+ test.assertEqual(eventModule2, 2,
+ "Correct number of events on module 2");
+
+ tm1.onReady.remove(listener1);
+ tab.onReady.remove(listener2);
+ tab.onReady.remove(listener3);
+ closeBrowserWindow(window, function() test.done());
+ });
+ }
+ });
+ });
+};
+
+/******************* helpers *********************/
+
+// Helper for getting the active window
+function activeWindow() {
+ return Cc["@mozilla.org/appshell/window-mediator;1"].
+ getService(Ci.nsIWindowMediator).
+ getMostRecentWindow("navigator:browser");
+};
+
+// 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("tab-browser");
+}
+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 tab-browser module does not support this application.");
+ };
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-tab-observer.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-tab-observer.js
new file mode 100644
index 0000000..3a506fa
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-tab-observer.js
@@ -0,0 +1,39 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 { openTab, closeTab } = require("api-utils/tabs/utils");
+const { Loader } = require("./helpers");
+const { setTimeout } = require("timer");
+
+exports["test unload tab observer"] = function(assert, done) {
+ let loader = Loader(module);
+
+ let window = loader.require("api-utils/window-utils").activeBrowserWindow;
+ let observer = loader.require("api-utils/tabs/observer").observer;
+ let opened = 0;
+ let closed = 0;
+
+ observer.on("open", function onOpen(window) { opened++; });
+ observer.on("close", function onClose(window) { closed++; });
+
+ // Open and close tab to trigger observers.
+ closeTab(openTab(window, "data:text/html,tab-1"));
+
+ // Unload the module so that all listeners set by observer are removed.
+ loader.unload();
+
+ // Open and close tab once again.
+ closeTab(openTab(window, "data:text/html,tab-2"));
+
+ // Enqueuing asserts to make sure that assertion is not performed early.
+ setTimeout(function () {
+ assert.equal(1, opened, "observer open was called before unload only");
+ assert.equal(1, closed, "observer close was called before unload only");
+ done();
+ }, 0);
+};
+
+require("test").run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-tab.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-tab.js
new file mode 100644
index 0000000..2324fa7
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-tab.js
@@ -0,0 +1,110 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const tabAPI = require("tabs/tab");
+const tabs = require("tabs"); // From addon-kit
+const windowUtils = require("window-utils");
+
+// The primary test tab
+var primaryTab;
+
+// We have an auxiliary tab to test background tabs.
+var auxTab;
+
+// The window for the outer iframe in the primary test page
+var iframeWin;
+
+exports.testGetTabForWindow = function(test) {
+ test.waitUntilDone();
+
+ test.assertEqual(tabAPI.getTabForWindow(windowUtils.activeWindow), null,
+ "getTabForWindow return null on topwindow");
+ test.assertEqual(tabAPI.getTabForWindow(windowUtils.activeBrowserWindow), null,
+ "getTabForWindow return null on topwindow");
+
+ let subSubDocument = encodeURIComponent(
+ 'Sub iframe<br/>'+
+ '<iframe id="sub-sub-iframe" src="data:text/html,SubSubIframe" />');
+ let subDocument = encodeURIComponent(
+ 'Iframe<br/>'+
+ '<iframe id="sub-iframe" src="data:text/html,'+subSubDocument+'" />');
+ let url = 'data:text/html,' + encodeURIComponent(
+ 'Content<br/><iframe id="iframe" src="data:text/html,'+subDocument+'" />');
+
+ // Open up a new tab in the background.
+ //
+ // This lets us test whether GetTabForWindow works even when the tab in
+ // question is not active.
+ tabs.open({
+ inBackground: true,
+ url: "about:mozilla",
+ onReady: function(tab) { auxTab = tab; step2(url, test);},
+ onActivate: function(tab) { step3(test); }
+ });
+}
+
+function step2(url, test) {
+
+ tabs.open({
+ url: url,
+ onReady: function(tab) {
+ primaryTab = tab;
+ let window = windowUtils.activeBrowserWindow.content;
+
+ let matchedTab = tabAPI.getTabForWindow(window);
+ test.assertEqual(matchedTab, tab,
+ "We are able to find the tab with his content window object");
+
+ let timer = require("timer");
+ function waitForFrames() {
+ let iframe = window.document.getElementById("iframe");
+ if (!iframe) {
+ timer.setTimeout(waitForFrames, 100);
+ return;
+ }
+ iframeWin = iframe.contentWindow;
+ let subIframe = iframeWin.document.getElementById("sub-iframe");
+ if (!subIframe) {
+ timer.setTimeout(waitForFrames, 100);
+ return;
+ }
+ let subIframeWin = subIframe.contentWindow;
+ let subSubIframe = subIframeWin.document.getElementById("sub-sub-iframe");
+ if (!subSubIframe) {
+ timer.setTimeout(waitForFrames, 100);
+ return;
+ }
+ let subSubIframeWin = subSubIframe.contentWindow;
+
+ matchedTab = tabAPI.getTabForWindow(iframeWin);
+ test.assertEqual(matchedTab, tab,
+ "We are able to find the tab with an iframe window object");
+
+ matchedTab = tabAPI.getTabForWindow(subIframeWin);
+ test.assertEqual(matchedTab, tab,
+ "We are able to find the tab with a sub-iframe window object");
+
+ matchedTab = tabAPI.getTabForWindow(subSubIframeWin);
+ test.assertEqual(matchedTab, tab,
+ "We are able to find the tab with a sub-sub-iframe window object");
+
+ // Put our primary tab in the background and test again.
+ // The onActivate listener will take us to step3.
+ auxTab.activate();
+ }
+ waitForFrames();
+ }
+ });
+}
+
+function step3(test) {
+
+ let matchedTab = tabAPI.getTabForWindow(iframeWin);
+ test.assertEqual(matchedTab, primaryTab,
+ "We get the correct tab even when it's in the background");
+
+ primaryTab.close(function () {
+ auxTab.close(function () { test.done();});
+ });
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-text-streams.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-text-streams.js
new file mode 100644
index 0000000..f5aec98
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-text-streams.js
@@ -0,0 +1,156 @@
+/* -*- 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 { pathFor } = require("api-utils/system");
+const { Loader } = require("./helpers");
+
+const STREAM_CLOSED_ERROR = "The stream is closed and cannot be used.";
+
+// This should match the constant of the same name in text-streams.js.
+const BUFFER_BYTE_LEN = 0x8000;
+
+exports.testWriteRead = function (test) {
+ let fname = dataFileFilename();
+
+ // Write a small string less than the stream's buffer size...
+ let str = "exports.testWriteRead data!";
+ let stream = file.open(fname, "w");
+ test.assert(!stream.closed, "stream.closed after open should be false");
+ stream.write(str);
+ stream.close();
+ test.assert(stream.closed, "stream.closed after close should be true");
+ test.assertRaises(function () stream.close(),
+ STREAM_CLOSED_ERROR,
+ "stream.close after already closed should raise error");
+ test.assertRaises(function () stream.write("This shouldn't be written!"),
+ STREAM_CLOSED_ERROR,
+ "stream.write after close should raise error");
+
+ // ... and read it.
+ stream = file.open(fname);
+ test.assert(!stream.closed, "stream.closed after open should be false");
+ test.assertEqual(stream.read(), str,
+ "stream.read should return string written");
+ test.assertEqual(stream.read(), "",
+ "stream.read at EOS should return empty string");
+ stream.close();
+ test.assert(stream.closed, "stream.closed after close should be true");
+ test.assertRaises(function () stream.close(),
+ STREAM_CLOSED_ERROR,
+ "stream.close after already closed should raise error");
+ test.assertRaises(function () stream.read(),
+ STREAM_CLOSED_ERROR,
+ "stream.read after close should raise error");
+
+ // Write a big string many times the size of the stream's buffer and read it.
+ // Since it comes after the previous test, this also ensures that the file is
+ // truncated when it's opened for writing.
+ str = "";
+ let bufLen = BUFFER_BYTE_LEN;
+ let fileSize = bufLen * 10;
+ for (let i = 0; i < fileSize; i++)
+ str += i % 10;
+ stream = file.open(fname, "w");
+ stream.write(str);
+ stream.close();
+ stream = file.open(fname);
+ test.assertEqual(stream.read(), str,
+ "stream.read should return string written");
+ stream.close();
+
+ // The same, but write and read in chunks.
+ stream = file.open(fname, "w");
+ let i = 0;
+ while (i < str.length) {
+ // Use a chunk length that spans buffers.
+ let chunk = str.substr(i, bufLen + 1);
+ stream.write(chunk);
+ i += bufLen + 1;
+ }
+ stream.close();
+ stream = file.open(fname);
+ let readStr = "";
+ bufLen = BUFFER_BYTE_LEN;
+ let readLen = bufLen + 1;
+ do {
+ var frag = stream.read(readLen);
+ readStr += frag;
+ } while (frag);
+ stream.close();
+ test.assertEqual(readStr, str,
+ "stream.write and read in chunks should work as expected");
+
+ // Read the same file, passing in strange numbers of bytes to read.
+ stream = file.open(fname);
+ test.assertEqual(stream.read(fileSize * 100), str,
+ "stream.read with big byte length should return string " +
+ "written");
+ stream.close();
+
+ stream = file.open(fname);
+ test.assertEqual(stream.read(0), "",
+ "string.read with zero byte length should return empty " +
+ "string");
+ stream.close();
+
+ stream = file.open(fname);
+ test.assertEqual(stream.read(-1), "",
+ "string.read with negative byte length should return " +
+ "empty string");
+ stream.close();
+
+ file.remove(fname);
+};
+
+exports.testWriteAsync = function (test) {
+ test.waitUntilDone();
+
+ let fname = dataFileFilename();
+ let str = "exports.testWriteAsync data!";
+ let stream = file.open(fname, "w");
+ test.assert(!stream.closed, "stream.closed after open should be false");
+
+ // Write.
+ stream.writeAsync(str, function (err) {
+ test.assertEqual(this, stream, "|this| should be the stream object");
+ test.assertEqual(err, undefined,
+ "stream.writeAsync should not cause error");
+ test.assert(stream.closed, "stream.closed after write should be true");
+ test.assertRaises(function () stream.close(),
+ STREAM_CLOSED_ERROR,
+ "stream.close after already closed should raise error");
+ test.assertRaises(function () stream.writeAsync("This shouldn't work!"),
+ STREAM_CLOSED_ERROR,
+ "stream.writeAsync after close should raise error");
+
+ // Read.
+ stream = file.open(fname, "r");
+ test.assert(!stream.closed, "stream.closed after open should be false");
+ let readStr = stream.read();
+ test.assertEqual(readStr, str,
+ "string.read should yield string written");
+ stream.close();
+ file.remove(fname);
+ test.done();
+ });
+};
+
+exports.testUnload = function (test) {
+ let loader = Loader(module);
+ let file = loader.require("file");
+
+ let filename = dataFileFilename("temp");
+ let stream = file.open(filename, "w");
+
+ loader.unload();
+ test.assert(stream.closed, "stream should be closed after module unload");
+};
+
+// Returns the name of a file that should be used to test writing and reading.
+function dataFileFilename() {
+ return file.join(pathFor("ProfD"), "test-text-streams-data");
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-timer.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-timer.js
new file mode 100644
index 0000000..bf6917c
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-timer.js
@@ -0,0 +1,131 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var timer = require("timer");
+const { Loader } = require("./helpers");
+
+exports.testSetTimeout = function(test) {
+ timer.setTimeout(function() {
+ test.pass("testSetTimeout passed");
+ test.done();
+ }, 1);
+ test.waitUntilDone();
+};
+
+exports.testParamedSetTimeout = function(test) {
+ let params = [1, 'foo', { bar: 'test' }, null, undefined];
+ timer.setTimeout.apply(null, [function() {
+ test.assertEqual(arguments.length, params.length);
+ for (let i = 0, ii = params.length; i < ii; i++)
+ test.assertEqual(params[i], arguments[i]);
+ test.done();
+ }, 1].concat(params));
+ test.waitUntilDone();
+};
+
+exports.testClearTimeout = function(test) {
+ var myFunc = function myFunc() {
+ test.fail("myFunc() should not be called in testClearTimeout");
+ };
+ var id = timer.setTimeout(myFunc, 1);
+ timer.setTimeout(function() {
+ test.pass("testClearTimeout passed");
+ test.done();
+ }, 2);
+ timer.clearTimeout(id);
+ test.waitUntilDone();
+};
+
+exports.testParamedClearTimeout = function(test) {
+ let params = [1, 'foo', { bar: 'test' }, null, undefined];
+ var myFunc = function myFunc() {
+ test.fail("myFunc() should not be called in testClearTimeout");
+ };
+ var id = timer.setTimeout(myFunc, 1);
+ timer.setTimeout.apply(null, [function() {
+ test.assertEqual(arguments.length, params.length);
+ for (let i = 0, ii = params.length; i < ii; i++)
+ test.assertEqual(params[i], arguments[i]);
+ test.done();
+ }, 1].concat(params));
+ timer.clearTimeout(id);
+ test.waitUntilDone();
+};
+
+exports.testSetInterval = function (test) {
+ var count = 0;
+ var id = timer.setInterval(function () {
+ count++;
+ if (count >= 5) {
+ timer.clearInterval(id);
+ test.pass("testSetInterval passed");
+ test.done();
+ }
+ }, 1);
+ test.waitUntilDone();
+};
+
+exports.testParamedSetInerval = function(test) {
+ let params = [1, 'foo', { bar: 'test' }, null, undefined];
+ let count = 0;
+ let id = timer.setInterval.apply(null, [function() {
+ count ++;
+ if (count < 5) {
+ test.assertEqual(arguments.length, params.length);
+ for (let i = 0, ii = params.length; i < ii; i++)
+ test.assertEqual(params[i], arguments[i]);
+ } else {
+ timer.clearInterval(id);
+ test.done();
+ }
+ }, 1].concat(params));
+ test.waitUntilDone();
+};
+
+exports.testClearInterval = function (test) {
+ timer.clearInterval(timer.setInterval(function () {
+ test.fail("setInterval callback should not be called");
+ }, 1));
+ var id = timer.setInterval(function () {
+ timer.clearInterval(id);
+ test.pass("testClearInterval passed");
+ test.done();
+ }, 2);
+ test.waitUntilDone();
+};
+
+exports.testParamedClearInterval = function(test) {
+ timer.clearInterval(timer.setInterval(function () {
+ test.fail("setInterval callback should not be called");
+ }, 1, timer, {}, null));
+
+ let id = timer.setInterval(function() {
+ timer.clearInterval(id);
+ test.assertEqual(3, arguments.length);
+ test.done();
+ }, 2, undefined, 'test', {});
+ test.waitUntilDone();
+};
+
+
+exports.testUnload = function(test) {
+ var loader = Loader(module);
+ var sbtimer = loader.require("timer");
+
+ var myFunc = function myFunc() {
+ test.fail("myFunc() should not be called in testUnload");
+ };
+
+ sbtimer.setTimeout(myFunc, 1);
+ sbtimer.setTimeout(myFunc, 1, 'foo', 4, {}, undefined);
+ sbtimer.setInterval(myFunc, 1);
+ sbtimer.setInterval(myFunc, 1, {}, null, 'bar', undefined, 87);
+ loader.unload();
+ timer.setTimeout(function() {
+ test.pass("timer testUnload passed");
+ test.done();
+ }, 2);
+ test.waitUntilDone();
+};
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-traceback.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-traceback.js
new file mode 100644
index 0000000..e4d1737
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-traceback.js
@@ -0,0 +1,118 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var traceback = require("traceback");
+var {Cc,Ci,Cr,Cu} = require("chrome");
+
+function throwNsIException() {
+ var ios = Cc['@mozilla.org/network/io-service;1']
+ .getService(Ci.nsIIOService);
+ ios.newURI("i'm a malformed URI", null, null);
+}
+
+function throwError() {
+ throw new Error("foob");
+}
+
+exports.testFormatDoesNotFetchRemoteFiles = function(test) {
+ var observers = require("observer-service");
+ ["http", "https"].forEach(
+ function(scheme) {
+ var httpRequests = 0;
+ function onHttp() {
+ httpRequests++;
+ }
+
+ observers.add("http-on-modify-request", onHttp);
+
+ try {
+ var tb = [{filename: scheme + "://www.mozilla.org/",
+ lineNo: 1,
+ funcName: "blah"}];
+ traceback.format(tb);
+ } catch (e) {
+ test.exception(e);
+ }
+
+ observers.remove("http-on-modify-request", onHttp);
+
+ test.assertEqual(httpRequests, 0,
+ "traceback.format() does not make " +
+ scheme + " request");
+ });
+};
+
+exports.testFromExceptionWithString = function(test) {
+ try {
+ throw "foob";
+ test.fail("an exception should've been thrown");
+ } catch (e if e == "foob") {
+ var tb = traceback.fromException(e);
+ test.assertEqual(tb.length, 0);
+ }
+};
+
+exports.testFormatWithString = function(test) {
+ // This can happen if e.g. a thrown exception was
+ // a string instead of an Error instance.
+ test.assertEqual(traceback.format("blah"),
+ "Traceback (most recent call last):");
+};
+
+exports.testFromExceptionWithError = function(test) {
+ try {
+ throwError();
+ test.fail("an exception should've been thrown");
+ } catch (e if e instanceof Error) {
+ var tb = traceback.fromException(e);
+ var xulApp = require("xul-app");
+ test.assertEqual(tb.slice(-1)[0].funcName, "throwError");
+ }
+};
+
+exports.testFromExceptionWithNsIException = function(test) {
+ try {
+ throwNsIException();
+ test.fail("an exception should've been thrown");
+ } catch (e if e.result == Cr.NS_ERROR_MALFORMED_URI) {
+ var tb = traceback.fromException(e);
+ test.assertEqual(tb.slice(-1)[0].funcName,
+ "throwNsIException");
+ }
+};
+
+exports.testFormat = function(test) {
+ function getTraceback() {
+ return traceback.format();
+ }
+
+ var formatted = getTraceback();
+ test.assertEqual(typeof(formatted), "string");
+ var lines = formatted.split("\n");
+ test.assertEqual(lines.slice(-2)[0].indexOf("getTraceback") > 0,
+ true,
+ "formatted traceback should include function name");
+ test.assertEqual(lines.slice(-1)[0].trim(),
+ "return traceback.format();",
+ "formatted traceback should include source code");
+};
+
+exports.testExceptionsWithEmptyStacksAreLogged = function(test) {
+ // Ensures that our fix to bug 550368 works.
+ var sandbox = Cu.Sandbox("http://www.foo.com");
+ var excRaised = false;
+ try {
+ Cu.evalInSandbox("returns 1 + 2;", sandbox, "1.8",
+ "blah.js", 25);
+ } catch (e) {
+ excRaised = true;
+ var stack = traceback.fromException(e);
+ test.assertEqual(stack.length, 1, "stack should have one frame");
+ test.assert(stack[0].filename, "blah.js", "frame should have filename");
+ test.assert(stack[0].lineNo, 25, "frame should have line no");
+ test.assertEqual(stack[0].funcName, null, "frame should have null function name");
+ }
+ if (!excRaised)
+ test.fail("Exception should have been raised.");
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-traits-core.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-traits-core.js
new file mode 100644
index 0000000..c2bf4df
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-traits-core.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";
+
+const ERR_CONFLICT = 'Remaining conflicting property: ',
+ ERR_REQUIRED = 'Missing required property: ';
+
+function assertSametrait(test, trait1, trait2) {
+ let names1 = Object.getOwnPropertyNames(trait1),
+ names2 = Object.getOwnPropertyNames(trait2);
+
+ test.assertEqual(
+ names1.length,
+ names2.length,
+ 'equal traits must have same amount of properties'
+ );
+
+ for (let i = 0; i < names1.length; i++) {
+ let name = names1[i];
+ test.assertNotEqual(
+ -1,
+ names2.indexOf(name),
+ 'equal traits must contain same named properties: ' + name
+ );
+ assertSameDescriptor(test, name, trait1[name], trait2[name]);
+ }
+}
+
+function assertSameDescriptor(test, name, desc1, desc2) {
+ if (desc1.conflict || desc2.conflict) {
+ test.assertEqual(
+ desc1.conflict,
+ desc2.conflict,
+ 'if one of same descriptors has `conflict` another must have it: '
+ + name
+ );
+ } else if (desc1.required || desc2.required) {
+ test.assertEqual(
+ desc1.required,
+ desc2.required,
+ 'if one of same descriptors is has `required` another must have it: '
+ + name
+ );
+ } else {
+ test.assertEqual(
+ desc1.get,
+ desc2.get,
+ 'get must be the same on both descriptors: ' + name
+ );
+ test.assertEqual(
+ desc1.set,
+ desc2.set,
+ 'set must be the same on both descriptors: ' + name
+ );
+ test.assertEqual(
+ desc1.value,
+ desc2.value,
+ 'value must be the same on both descriptors: ' + name
+ );
+ test.assertEqual(
+ desc1.enumerable,
+ desc2.enumerable,
+ 'enumerable must be the same on both descriptors: ' + name
+ );
+ test.assertEqual(
+ desc1.required,
+ desc2.required,
+ 'value must be the same on both descriptors: ' + name
+ );
+ }
+}
+
+function Data(value, enumerable, confligurable, writable) {
+ return {
+ value: value,
+ enumerable: false !== enumerable,
+ confligurable: false !== confligurable,
+ writable: false !== writable
+ };
+}
+
+function Method(method, enumerable, confligurable, writable) {
+ return {
+ value: method,
+ enumerable: false !== enumerable,
+ confligurable: false !== confligurable,
+ writable: false !== writable
+ };
+}
+
+function Accessor(get, set, enumerable, confligurable) {
+ return {
+ get: get,
+ set: set,
+ enumerable: false !== enumerable,
+ confligurable: false !== confligurable,
+ };
+}
+
+function Required(name) {
+ function required() { throw new Error(ERR_REQUIRED + name) }
+ return {
+ get: required,
+ set: required,
+ required: true
+ };
+}
+
+function Conflict(name) {
+ function conflict() { throw new Error(ERR_CONFLICT + name) }
+ return {
+ get: conflict,
+ set: conflict,
+ conflict: true
+ };
+}
+
+function testMethod() {};
+
+const { trait, compose, resolve, required, override, create } =
+ require('traits/core');
+
+
+exports['test:empty trait'] = function(test) {
+ assertSametrait(
+ test,
+ trait({}),
+ {}
+ );
+};
+
+exports['test:simple trait'] = function(test) {
+ assertSametrait(
+ test,
+ trait({
+ a: 0,
+ b: testMethod
+ }),
+ {
+ a: Data(0, true, true, true),
+ b: Method(testMethod, true, true, true)
+ }
+ );
+};
+
+exports['test:simple trait with required prop'] = function(test) {
+ assertSametrait(
+ test,
+ trait({
+ a: required,
+ b: 1
+ }),
+ {
+ a: Required('a'),
+ b: Data(1)
+ }
+ );
+};
+
+exports['test:ordering of trait properties is irrelevant'] = function(test) {
+ assertSametrait(test,
+ trait({ a: 0, b: 1, c: required }),
+ trait({ b: 1, c: required, a: 0 })
+ );
+};
+
+exports['test:trait with accessor property'] = function(test) {
+ let record = { get a() {}, set a(v) {} };
+ let get = Object.getOwnPropertyDescriptor(record,'a').get;
+ let set = Object.getOwnPropertyDescriptor(record,'a').set;
+ assertSametrait(test,
+ trait(record),
+ { a: Accessor(get, set ) }
+ );
+};
+
+exports['test:simple composition'] = function(test) {
+ assertSametrait(test,
+ compose(
+ trait({ a: 0, b: 1 }),
+ trait({ c: 2, d: testMethod })
+ ),
+ {
+ a: Data(0),
+ b: Data(1),
+ c: Data(2),
+ d: Method(testMethod)
+ }
+ );
+};
+
+exports['test:composition with conflict'] = function(test) {
+ assertSametrait(test,
+ compose(
+ trait({ a: 0, b: 1 }),
+ trait({ a: 2, c: testMethod })
+ ),
+ {
+ a: Conflict('a'),
+ b: Data(1),
+ c: Method(testMethod)
+ }
+ );
+};
+
+exports['test:composition of identical props does not cause conflict'] =
+function(test) {
+ assertSametrait(test,
+ compose(
+ trait({ a: 0, b: 1 }),
+ trait({ a: 0, c: testMethod })
+ ),
+ {
+ a: Data(0),
+ b: Data(1),
+ c: Method(testMethod) }
+ )
+};
+
+exports['test:composition with identical required props'] =
+function(test) {
+ assertSametrait(test,
+ compose(
+ trait({ a: required, b: 1 }),
+ trait({ a: required, c: testMethod })
+ ),
+ {
+ a: Required(),
+ b: Data(1),
+ c: Method(testMethod)
+ }
+ );
+};
+
+exports['test:composition satisfying a required prop'] = function (test) {
+ assertSametrait(test,
+ compose(
+ trait({ a: required, b: 1 }),
+ trait({ a: testMethod })
+ ),
+ {
+ a: Method(testMethod),
+ b: Data(1)
+ }
+ );
+};
+
+exports['test:compose is neutral wrt conflicts'] = function (test) {
+ assertSametrait(test,
+ compose(
+ compose(
+ trait({ a: 1 }),
+ trait({ a: 2 })
+ ),
+ trait({ b: 0 })
+ ),
+ {
+ a: Conflict('a'),
+ b: Data(0)
+ }
+ );
+};
+
+exports['test:conflicting prop overrides required prop'] = function (test) {
+ assertSametrait(test,
+ compose(
+ compose(
+ trait({ a: 1 }),
+ trait({ a: 2 })
+ ),
+ trait({ a: required })
+ ),
+ {
+ a: Conflict('a')
+ }
+ );
+};
+
+exports['test:compose is commutative'] = function (test) {
+ assertSametrait(test,
+ compose(
+ trait({ a: 0, b: 1 }),
+ trait({ c: 2, d: testMethod })
+ ),
+ compose(
+ trait({ c: 2, d: testMethod }),
+ trait({ a: 0, b: 1 })
+ )
+ );
+};
+
+exports['test:compose is commutative, also for required/conflicting props'] =
+function (test) {
+ assertSametrait(test,
+ compose(
+ trait({ a: 0, b: 1, c: 3, e: required }),
+ trait({ c: 2, d: testMethod })
+ ),
+ compose(
+ trait({ c: 2, d: testMethod }),
+ trait({ a: 0, b: 1, c: 3, e: required })
+ )
+ );
+};
+exports['test:compose is associative'] = function (test) {
+ assertSametrait(test,
+ compose(
+ trait({ a: 0, b: 1, c: 3, d: required }),
+ compose(
+ trait({ c: 3, d: required }),
+ trait({ c: 2, d: testMethod, e: 'foo' })
+ )
+ ),
+ compose(
+ compose(
+ trait({ a: 0, b: 1, c: 3, d: required }),
+ trait({ c: 3, d: required })
+ ),
+ trait({ c: 2, d: testMethod, e: 'foo' })
+ )
+ );
+};
+
+exports['test:diamond import of same prop does not generate conflict'] =
+function (test) {
+ assertSametrait(test,
+ compose(
+ compose(
+ trait({ b: 2 }),
+ trait({ a: 1 })
+ ),
+ compose(
+ trait({ c: 3 }),
+ trait({ a: 1 })
+ ),
+ trait({ d: 4 })
+ ),
+ {
+ a: Data(1),
+ b: Data(2),
+ c: Data(3),
+ d: Data(4)
+ }
+ );
+};
+
+exports['test:resolve with empty resolutions has no effect'] =
+function (test) {
+ assertSametrait(test, resolve({}, trait({
+ a: 1,
+ b: required,
+ c: testMethod
+ })), {
+ a: Data(1),
+ b: Required(),
+ c: Method(testMethod)
+ });
+};
+
+exports['test:resolve: renaming'] = function (test) {
+ assertSametrait(test,
+ resolve(
+ { a: 'A', c: 'C' },
+ trait({ a: 1, b: required, c: testMethod })
+ ),
+ {
+ A: Data(1),
+ b: Required(),
+ C: Method(testMethod),
+ a: Required(),
+ c: Required()
+ }
+ );
+};
+
+exports['test:resolve: renaming to conflicting name causes conflict, order 1']
+= function (test) {
+ assertSametrait(test,
+ resolve(
+ { a: 'b'},
+ trait({ a: 1, b: 2 })
+ ),
+ {
+ b: Conflict('b'),
+ a: Required()
+ }
+ );
+};
+
+exports['test:resolve: renaming to conflicting name causes conflict, order 2']
+= function (test) {
+ assertSametrait(test,
+ resolve(
+ { a: 'b' },
+ trait({ b: 2, a: 1 })
+ ),
+ {
+ b: Conflict('b'),
+ a: Required()
+ }
+ );
+};
+
+exports['test:resolve: simple exclusion'] = function (test) {
+ assertSametrait(test,
+ resolve(
+ { a: undefined },
+ trait({ a: 1, b: 2 })
+ ),
+ {
+ a: Required(),
+ b: Data(2)
+ }
+ );
+};
+
+exports['test:resolve: exclusion to "empty" trait'] = function (test) {
+ assertSametrait(test,
+ resolve(
+ { a: undefined, b: undefined },
+ trait({ a: 1, b: 2 })
+ ),
+ {
+ a: Required(),
+ b: Required()
+ }
+ );
+};
+
+exports['test:resolve: exclusion and renaming of disjoint props'] =
+function (test) {
+ assertSametrait(test,
+ resolve(
+ { a: undefined, b: 'c' },
+ trait({ a: 1, b: 2 })
+ ),
+ {
+ a: Required(),
+ c: Data(2),
+ b: Required()
+ }
+ );
+};
+
+exports['test:resolve: exclusion and renaming of overlapping props'] =
+function (test) {
+ assertSametrait(test,
+ resolve(
+ { a: undefined, b: 'a' },
+ trait({ a: 1, b: 2 })
+ ),
+ {
+ a: Data(2),
+ b: Required()
+ }
+ );
+};
+
+exports['test:resolve: renaming to a common alias causes conflict'] =
+function (test) {
+ assertSametrait(test,
+ resolve(
+ { a: 'c', b: 'c' },
+ trait({ a: 1, b: 2 })
+ ),
+ {
+ c: Conflict('c'),
+ a: Required(),
+ b: Required()
+ }
+ );
+};
+
+exports['test:resolve: renaming overrides required target'] =
+function (test) {
+ assertSametrait(test,
+ resolve(
+ { b: 'a' },
+ trait({ a: required, b: 2 })
+ ),
+ {
+ a: Data(2),
+ b: Required()
+ }
+ );
+};
+
+exports['test:resolve: renaming required properties has no effect'] =
+function (test) {
+ assertSametrait(test,
+ resolve(
+ { b: 'a' },
+ trait({ a: 2, b: required })
+ ),
+ {
+ a: Data(2),
+ b: Required()
+ }
+ );
+};
+
+exports['test:resolve: renaming of non-existent props has no effect'] =
+function (test) {
+ assertSametrait(test,
+ resolve(
+ { a: 'c', d: 'c' },
+ trait({ a: 1, b: 2 })
+ ),
+ {
+ c: Data(1),
+ b: Data(2),
+ a: Required()
+ }
+ );
+};
+
+exports['test:resolve: exclusion of non-existent props has no effect'] =
+function (test) {
+ assertSametrait(test,
+ resolve(
+ { b: undefined },
+ trait({ a: 1 })
+ ),
+ {
+ a: Data(1)
+ }
+ );
+};
+
+exports['test:resolve is neutral w.r.t. required properties'] =
+function (test) {
+ assertSametrait(test,
+ resolve(
+ { a: 'c', b: undefined },
+ trait({ a: required, b: required, c: 'foo', d: 1 })
+ ),
+ {
+ a: Required(),
+ b: Required(),
+ c: Data('foo'),
+ d: Data(1)
+ }
+ );
+};
+
+exports['test:resolve supports swapping of property names, ordering 1'] =
+function (test) {
+ assertSametrait(test,
+ resolve(
+ { a: 'b', b: 'a' },
+ trait({ a: 1, b: 2 })
+ ),
+ {
+ a: Data(2),
+ b: Data(1)
+ }
+ );
+};
+
+exports['test:resolve supports swapping of property names, ordering 2'] =
+function (test) {
+ assertSametrait(test,
+ resolve(
+ { b: 'a', a: 'b' },
+ trait({ a: 1, b: 2 })
+ ),
+ {
+ a: Data(2),
+ b: Data(1)
+ }
+ );
+};
+
+exports['test:resolve supports swapping of property names, ordering 3'] =
+function (test) {
+ assertSametrait(test,
+ resolve(
+ { b: 'a', a: 'b' },
+ trait({ b: 2, a: 1 })
+ ),
+ {
+ a: Data(2),
+ b: Data(1)
+ }
+ );
+};
+
+exports['test:resolve supports swapping of property names, ordering 4'] =
+function (test) {
+ assertSametrait(test,
+ resolve(
+ { a: 'b', b: 'a' },
+ trait({ b: 2, a: 1 })
+ ),
+ {
+ a: Data(2),
+ b: Data(1)
+ }
+ );
+};
+
+exports['test:override of mutually exclusive traits'] = function (test) {
+ assertSametrait(test,
+ override(
+ trait({ a: 1, b: 2 }),
+ trait({ c: 3, d: testMethod })
+ ),
+ {
+ a: Data(1),
+ b: Data(2),
+ c: Data(3),
+ d: Method(testMethod)
+ }
+ );
+};
+
+exports['test:override of mutually exclusive traits is compose'] =
+function (test) {
+ assertSametrait(test,
+ override(
+ trait({ a: 1, b: 2 }),
+ trait({ c: 3, d: testMethod })
+ ),
+ compose(
+ trait({ d: testMethod, c: 3 }),
+ trait({ b: 2, a: 1 })
+ )
+ );
+};
+
+exports['test:override of overlapping traits'] = function (test) {
+ assertSametrait(test,
+ override(
+ trait({ a: 1, b: 2 }),
+ trait({ a: 3, c: testMethod })
+ ),
+ {
+ a: Data(1),
+ b: Data(2),
+ c: Method(testMethod)
+ }
+ );
+};
+
+exports['test:three-way override of overlapping traits'] = function (test) {
+ assertSametrait(test,
+ override(
+ trait({ a: 1, b: 2 }),
+ trait({ b: 4, c: 3 }),
+ trait({ a: 3, c: testMethod, d: 5 })
+ ),
+ {
+ a: Data(1),
+ b: Data(2),
+ c: Data(3),
+ d: Data(5)
+ }
+ );
+};
+
+exports['test:override replaces required properties'] = function (test) {
+ assertSametrait(test,
+ override(
+ trait({ a: required, b: 2 }),
+ trait({ a: 1, c: testMethod })
+ ),
+ {
+ a: Data(1),
+ b: Data(2),
+ c: Method(testMethod)
+ }
+ );
+};
+
+exports['test:override is not commutative'] = function (test) {
+ assertSametrait(test,
+ override(
+ trait({ a: 1, b: 2 }),
+ trait({ a: 3, c: 4 })
+ ),
+ {
+ a: Data(1),
+ b: Data(2),
+ c: Data(4)
+ }
+ );
+
+ assertSametrait(test,
+ override(
+ trait({ a: 3, c: 4 }),
+ trait({ a: 1, b: 2 })
+ ),
+ {
+ a: Data(3),
+ b: Data(2),
+ c: Data(4)
+ }
+ );
+};
+
+exports['test:override is associative'] = function (test) {
+ assertSametrait(test,
+ override(
+ override(
+ trait({ a: 1, b: 2 }),
+ trait({ a: 3, c: 4, d: 5 })
+ ),
+ trait({ a: 6, c: 7, e: 8 })
+ ),
+ override(
+ trait({ a: 1, b: 2 }),
+ override(
+ trait({ a: 3, c: 4, d: 5 }),
+ trait({ a: 6, c: 7, e: 8 })
+ )
+ )
+ );
+};
+
+exports['test:create simple'] = function(test) {
+ let o1 = create(
+ Object.prototype,
+ trait({ a: 1, b: function() { return this.a; } })
+ );
+
+ test.assertEqual(
+ Object.prototype,
+ Object.getPrototypeOf(o1),
+ 'o1 prototype'
+ );
+ test.assertEqual(1, o1.a, 'o1.a');
+ test.assertEqual(1, o1.b(), 'o1.b()');
+ test.assertEqual(
+ 2,
+ Object.getOwnPropertyNames(o1).length,
+ 'Object.keys(o1).length === 2'
+ );
+};
+
+exports['test:create with Array.prototype'] = function(test) {
+ let o2 = create(Array.prototype, trait({}));
+ test.assertEqual(
+ Array.prototype,
+ Object.getPrototypeOf(o2),
+ "o2 prototype"
+ );
+};
+
+exports['test:exception for incomplete required properties'] =
+function(test) {
+ try {
+ create(Object.prototype, trait({ foo: required }));
+ test.fail('expected create to complain about missing required props');
+ } catch(e) {
+ test.assertEqual(
+ 'Error: Missing required property: foo',
+ e.toString(),
+ 'required prop error'
+ );
+ }
+};
+
+exports['test:exception for unresolved conflicts'] = function(test) {
+ try {
+ create({}, compose(trait({ a: 0 }), trait({ a: 1 })));
+ test.fail('expected create to complain about unresolved conflicts');
+ } catch(e) {
+ test.assertEqual(
+ 'Error: Remaining conflicting property: a',
+ e.toString(),
+ 'conflicting prop error'
+ );
+ }
+};
+
+exports['test:verify that required properties are present but undefined'] =
+function(test) {
+ try {
+ let o4 = Object.create(Object.prototype, trait({ foo: required }));
+ test.assertEqual(true, 'foo' in o4, 'required property present');
+ try {
+ let foo = o4.foo;
+ test.fail('access to required property must throw');
+ } catch(e) {
+ test.assertEqual(
+ 'Error: Missing required property: foo',
+ e.toString(),
+ 'required prop error'
+ )
+ }
+ } catch(e) {
+ test.fail('did not expect create to complain about required props');
+ }
+};
+
+exports['test:verify that conflicting properties are present'] =
+function(test) {
+ try {
+ let o5 = Object.create(
+ Object.prototype,
+ compose(trait({ a: 0 }), trait({ a: 1 }))
+ );
+ test.assertEqual(true, 'a' in o5, 'conflicting property present');
+ try {
+ let a = o5.a; // accessors or data prop
+ test.fail('expected conflicting prop to cause exception');
+ } catch (e) {
+ test.assertEqual(
+ 'Error: Remaining conflicting property: a',
+ e.toString(),
+ 'conflicting prop access error'
+ );
+ }
+ } catch(e) {
+ test.fail('did not expect create to complain about conflicting props');
+ }
+};
+
+exports['test diamond with conflicts'] = function(test) {
+ function makeT1(x) trait({ m: function() { return x; } })
+ function makeT2(x) compose(trait({ t2: 'foo' }), makeT1(x))
+ function makeT3(x) compose(trait({ t3: 'bar' }), makeT1(x))
+
+ let T4 = compose(makeT2(5), makeT3(5));
+ try {
+ let o = create(Object.prototype, T4);
+ test.fail('expected diamond prop to cause exception');
+ } catch(e) {
+ test.assertEqual(
+ 'Error: Remaining conflicting property: m',
+ e.toString(),
+ 'diamond prop conflict'
+ );
+ }
+};
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-traits.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-traits.js
new file mode 100644
index 0000000..8b88f94
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-traits.js
@@ -0,0 +1,398 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { Trait } = require('traits');
+
+exports['test:simple compose'] = function(test) {
+ let List = Trait.compose({
+ _list: null,
+ constructor: function List() {
+ this._list = [];
+ },
+ list: function list() this._list.slice(0),
+ add: function add(item) this._list.push(item),
+ remove: function remove(item) {
+ let list = this._list;
+ let index = list.indexOf(item);
+ if (0 <= index) list.slice(index, 1);
+ }
+ });
+
+ test.assertNotEqual(undefined, List, 'should not be undefined');
+ test.assertEqual('function', typeof List, 'type should be function');
+ test.assertEqual(
+ Trait.compose,
+ List.compose,
+ 'should inherit static compose'
+ );
+ test.assertEqual(
+ Trait.override,
+ List.override,
+ 'should inherit static override'
+ );
+ test.assertEqual(
+ Trait.required,
+ List.required,
+ 'should inherit static required'
+ );
+ test.assertEqual(
+ Trait.resolve,
+ List.resolve,
+ 'should inherit static resolve'
+ );
+
+ test.assert(
+ !('_list' in List.prototype),
+ 'should not expose private API'
+ );
+}
+exports['test: compose trait instance and create instance'] = function(test) {
+ let List = Trait.compose({
+ constructor: function List(options) {
+ this._list = [];
+ this._public.publicMember = options.publicMember;
+ },
+ _privateMember: true,
+ get privateMember() this._privateMember,
+ get list() this._list.slice(0),
+ add: function add(item) this._list.push(item),
+ remove: function remove(item) {
+ let list = this._list
+ let index = list.indexOf(item)
+ if (0 <= index) list.slice(index, 1)
+ }
+ });
+ let list = List({ publicMember: true });
+
+ test.assertEqual('object', typeof list, 'should return an object')
+ test.assertEqual(
+ true,
+ list instanceof List,
+ 'should be instance of a List'
+ );
+
+ test.assertEqual(
+ undefined,
+ list._privateMember,
+ 'instance should not expose private API'
+ );
+
+ test.assertEqual(
+ true,
+ list.privateMember,
+ 'privates are accessible by public API'
+ );
+
+ list._privateMember = false;
+
+ test.assertEqual(
+ true,
+ list.privateMember,
+ 'property changes on instance must not affect privates'
+ );
+
+ test.assert(
+ !('_list' in list),
+ 'instance should not expose private members'
+ );
+
+ test.assertEqual(
+ true,
+ list.publicMember,
+ 'public members are exposed'
+ )
+ test.assertEqual(
+ 'function',
+ typeof list.add,
+ 'should be function'
+ )
+ test.assertEqual(
+ 'function',
+ typeof list.remove,
+ 'should be function'
+ );
+
+ list.add(1);
+ test.assertEqual(
+ 1,
+ list.list[0],
+ 'exposed public API should be able of modifying privates'
+ )
+};
+
+
+exports['test:instances must not be hackable'] = function(test) {
+ let SECRET = 'There is no secret!',
+ secret = null;
+
+ let Class = Trait.compose({
+ _secret: null,
+ protect: function(data) this._secret = data
+ });
+
+ let i1 = Class();
+ i1.protect(SECRET);
+
+ test.assertEqual(
+ undefined,
+ (function() this._secret).call(i1),
+ 'call / apply can\'t access private state'
+ );
+
+ let proto = Object.getPrototypeOf(i1);
+ try {
+ proto.reveal = function() this._secret;
+ secret = i1.reveal();
+ } catch(e) {}
+ test.assertNotEqual(
+ SECRET,
+ secret,
+ 'public __proto__ changes should not affect privates'
+ );
+ secret = null;
+
+ let Class2 = Trait.compose({
+ _secret: null,
+ protect: function(data) this._secret = data
+ });
+ let i2 = Class2();
+ i2.protect(SECRET);
+ try {
+ Object.prototype.reveal = function() this._secret;
+ secret = i2.reveal();
+ } catch(e) {}
+ test.assertNotEqual(
+ SECRET,
+ secret,
+ 'Object.prototype changes must not affect instances'
+ );
+}
+
+exports['test:instanceof'] = function(test) {
+ const List = Trait.compose({
+ // private API:
+ _list: null,
+ // public API
+ constructor: function List() {
+ this._list = []
+ },
+ get length() this._list.length,
+ add: function add(item) this._list.push(item),
+ remove: function remove(item) {
+ let list = this._list;
+ let index = list.indexOf(item);
+ if (0 <= index) list.slice(index, 1);
+ }
+ });
+
+ test.assert(List() instanceof List, 'Must be instance of List');
+ test.assert(new List() instanceof List, 'Must be instance of List');
+};
+
+exports['test:privates are unaccessible'] = function(test) {
+ const List = Trait.compose({
+ // private API:
+ _list: null,
+ // public API
+ constructor: function List() {
+ this._list = [];
+ },
+ get length() this._list.length,
+ add: function add(item) this._list.push(item),
+ remove: function remove(item) {
+ let list = this._list;
+ let index = list.indexOf(item);
+ if (0 <= index) list.slice(index, 1);
+ }
+ });
+
+ let list = List();
+ test.assert(!('_list' in list), 'no privates on instance');
+ test.assert(
+ !('_list' in List.prototype),
+ 'no privates on prototype'
+ );
+};
+
+exports['test:public API can access private API'] = function(test) {
+ const List = Trait.compose({
+ // private API:
+ _list: null,
+ // public API
+ constructor: function List() {
+ this._list = [];
+ },
+ get length() this._list.length,
+ add: function add(item) this._list.push(item),
+ remove: function remove(item) {
+ let list = this._list;
+ let index = list.indexOf(item);
+ if (0 <= index) list.slice(index, 1);
+ }
+ });
+ let list = List();
+
+ list.add('test');
+
+ test.assertEqual(
+ 1,
+ list.length,
+ 'should be able to add element and access it from public getter'
+ );
+};
+
+exports['test:required'] = function(test) {
+ const Enumerable = Trait.compose({
+ list: Trait.required,
+ forEach: function forEach(consumer) {
+ return this.list.forEach(consumer);
+ }
+ });
+
+ try {
+ let i = Enumerable();
+ test.fail('should throw when creating instance with required properties');
+ } catch(e) {
+ test.assertEqual(
+ 'Error: Missing required property: list',
+ e.toString(),
+ 'required prop error'
+ );
+ }
+};
+
+exports['test:compose with required'] = function(test) {
+ const List = Trait.compose({
+ // private API:
+ _list: null,
+ // public API
+ constructor: function List() {
+ this._list = [];
+ },
+ get length() this._list.length,
+ add: function add(item) this._list.push(item),
+ remove: function remove(item) {
+ let list = this._list;
+ let index = list.indexOf(item);
+ if (0 <= index) list.slice(index, 1);
+ }
+ });
+
+ const Enumerable = Trait.compose({
+ list: Trait.required,
+ forEach: function forEach(consumer) {
+ return this.list.forEach(consumer);
+ }
+ });
+
+ const EnumerableList = Enumerable.compose({
+ get list() this._list.slice(0)
+ }, List);
+
+ let array = [1,2, 'ab']
+ let l = EnumerableList(array);
+ array.forEach(function(element) l.add(element));
+ let number = 0;
+ l.forEach(function(element, index) {
+ number ++;
+ test.assertEqual(array[index], element, 'should mach array element')
+ });
+ test.assertEqual(
+ array.length,
+ number,
+ 'should perform as many asserts as elements in array'
+ );
+};
+
+exports['test:resolve'] = function(test) {
+ const List = Trait.compose({
+ // private API:
+ _list: null,
+ // public API
+ constructor: function List() {
+ this._list = [];
+ },
+ get length() this._list.length,
+ add: function add(item) this._list.push(item),
+ remove: function remove(item) {
+ let list = this._list;
+ let index = list.indexOf(item);
+ if (0 <= index) list.slice(index, 1);
+ }
+ });
+
+ const Range = List.resolve({
+ constructor: null,
+ add: '_add',
+ }).compose({
+ min: null,
+ max: null,
+ get list() this._list.slice(0),
+ constructor: function Range(min, max) {
+ this.min = min;
+ this.max = max;
+ this._list = [];
+ },
+ add: function(item) {
+ if (item <= this.max && item >= this.min)
+ this._add(item)
+ }
+ });
+
+ let r = Range(0, 10);
+
+ test.assertEqual(
+ 0,
+ r.min,
+ 'constructor must have set min'
+ );
+ test.assertEqual(
+ 10,
+ r.max,
+ 'constructor must have set max'
+ );
+
+ test.assertEqual(
+ 0,
+ r.length,
+ 'should not contain any elements'
+ );
+
+ r.add(5);
+
+ test.assertEqual(
+ 1,
+ r.length,
+ 'should add `5` to list'
+ );
+
+ r.add(12);
+
+ test.assertEqual(
+ 1,
+ r.length,
+ 'should not add `12` to list'
+ );
+};
+
+exports['test:custom iterator'] = function(test) {
+ let Sub = Trait.compose({
+ foo: "foo",
+ bar: "bar",
+ baz: "baz",
+ __iterator__: function() {
+ yield 1;
+ yield 2;
+ yield 3;
+ }
+ });
+
+ let (i = 0, sub = Sub()) {
+ for (let item in sub)
+ test.assertEqual(++i, item, "iterated item has the right value");
+ };
+};
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-type.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-type.js
new file mode 100644
index 0000000..d5b5775
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-type.js
@@ -0,0 +1,92 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 utils = require("type");
+
+exports["test function"] = function (assert) {
+ assert.ok(utils.isFunction(function(){}), "value is function");
+ assert.ok(utils.isFunction(Object), "Object is function");
+ assert.ok(utils.isFunction(new Function("")), "Genertaed value is function");
+ assert.ok(!utils.isFunction({}), "object is not a function");
+ assert.ok(!utils.isFunction(4), "number is not a function");
+};
+
+exports["test atoms"] = function (assert) {
+ assert.ok(utils.isPrimitive(2), "number is primitive");
+ assert.ok(utils.isPrimitive(NaN), "`NaN` is primitve");
+ assert.ok(utils.isPrimitive(undefined), "`undefined` is primitive");
+ assert.ok(utils.isPrimitive(null), "`null` is primitive");
+ assert.ok(utils.isPrimitive(Infinity), "`Infinity` is primitive");
+ assert.ok(utils.isPrimitive("foo"), "strings are primitive");
+ assert.ok(utils.isPrimitive(true) && utils.isPrimitive(false),
+ "booleans are primitive");
+};
+
+exports["test object"] = function (assert) {
+ assert.ok(utils.isObject({}), "`{}` is object");
+ assert.ok(!utils.isObject(null), "`null` is not an object");
+ assert.ok(!utils.isObject(Object), "functions is not an object");
+};
+
+exports["test flat objects"] = function (assert) {
+ assert.ok(utils.isFlat({}), "`{}` is a flat object");
+ assert.ok(!utils.isFlat([]), "`[]` is not a flat object");
+ assert.ok(!utils.isFlat(new function() {}), "derived objects are not flat");
+ assert.ok(utils.isFlat(Object.prototype), "Object.prototype is flat");
+};
+
+exports["test json atoms"] = function (assert) {
+ assert.ok(utils.isJSON(null), "`null` is JSON");
+ assert.ok(utils.isJSON(undefined), "`undefined` is JSON");
+ assert.ok(utils.isJSON(NaN), "`NaN` is JSON");
+ assert.ok(utils.isJSON(Infinity), "`Infinity` is JSON");
+ assert.ok(utils.isJSON(true) && utils.isJSON(false), "booleans are JSON");
+ assert.ok(utils.isJSON(4), utils.isJSON(0), "numbers are JSON");
+ assert.ok(utils.isJSON("foo bar"), "strings are JSON");
+};
+
+exports["test instanceOf"] = function (assert) {
+ assert.ok(utils.instanceOf(assert, Object),
+ "assert is object from other sandbox");
+ assert.ok(utils.instanceOf(new Date(), Date), "instance of date");
+ assert.ok(!utils.instanceOf(null, Object), "null is not an instance");
+};
+
+exports["test json"] = function (assert) {
+ assert.ok(!utils.isJSON(function(){}), "functions are not json");
+ assert.ok(utils.isJSON({}), "`{}` is JSON");
+ assert.ok(utils.isJSON({
+ a: "foo",
+ b: 3,
+ c: undefined,
+ d: null,
+ e: {
+ f: {
+ g: "bar",
+ p: [{}, "oueou", 56]
+ },
+ q: { nan: NaN, infinity: Infinity },
+ "non standard name": "still works"
+ }
+ }), "JSON can contain nested objects");
+
+ var foo = {};
+ var bar = { foo: foo };
+ foo.bar = bar;
+ assert.ok(!utils.isJSON(foo), "recursive objects are not json");
+
+
+ assert.ok(!utils.isJSON({ get foo() { return 5 } }),
+ "json can not have getter");
+
+ assert.ok(!utils.isJSON({ foo: "bar", baz: function () {} }),
+ "json can not contain functions");
+
+ assert.ok(!utils.isJSON(Object.create({})),
+ "json must be direct descendant of `Object.prototype`");
+};
+
+require("test").run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-unit-test.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-unit-test.js
new file mode 100644
index 0000000..8ee99a4
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-unit-test.js
@@ -0,0 +1,251 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 timer = require("timer");
+const { Loader } = require("./helpers");
+
+var setupCalled = false, teardownCalled = false;
+
+exports.setup = function() {
+ setupCalled = true;
+};
+
+exports.teardown = function() {
+ teardownCalled = true;
+ setupCalled = false;
+};
+
+// Important note - unit tests are run in alphabetical order. The following
+// unit tests for setup/teardown are order dependent, sometimes the result of
+// one test is checked in the next test (testing for teardown does this). When
+// tests are cohesively a single unit, they are named <test_name> - partN where
+// N is their order in the sequence. Secondly, because these tests should be
+// run before all others, they start with an A.
+exports.testASetupTeardownSyncTestPart1 = function(test) {
+ test.assertEqual(true, setupCalled, 'setup function was called before this');
+ test.assertEqual(false, teardownCalled, 'teardown function was not called before this');
+};
+
+exports.testASetupTeardownSyncTestPart2 = function(test) {
+ test.assertEqual(true, setupCalled, 'setup was re-called before this');
+ test.assertEqual(true, teardownCalled, 'teardown was called after first function');
+};
+
+exports.testATeardownAsyncTestPart1 = function(test) {
+ teardownCalled = false;
+
+ timer.setTimeout(function() {
+ test.assertEqual(false, teardownCalled, "teardown not called until done");
+ test.done();
+ }, 200);
+ test.waitUntilDone();
+};
+
+exports.testATeardownAsyncTestPart2 = function(test) {
+ test.assertEqual(true, teardownCalled, "teardown called after done");
+};
+
+exports.testWaitUntilInstant = function(test) {
+ test.waitUntilDone();
+
+ test.waitUntil(function () true, "waitUntil with instant true pass")
+ .then(function () test.done());
+}
+
+exports.testWaitUntil = function(test) {
+ test.waitUntilDone();
+ let succeed = false;
+
+ test.waitUntil(function () succeed, "waitUntil pass")
+ .then(function () test.done());
+
+ timer.setTimeout(function () {
+ succeed = true;
+ }, 200);
+}
+
+exports.testWaitUntilEqual = function(test) {
+ test.waitUntilDone();
+ let succeed = false;
+
+ test.waitUntilEqual("foo", function () succeed ? "foo" : "bar",
+ "waitUntilEqual pass")
+ .then(function () test.done());
+
+ timer.setTimeout(function () {
+ succeed = true;
+ }, 200);
+}
+
+exports.testWaitUntilNotEqual = function(test) {
+ test.waitUntilDone();
+ let succeed = false;
+
+ test.waitUntilNotEqual("foo", function () succeed ? "bar" : "foo",
+ "waitUntilNotEqual pass")
+ .then(function () test.done());
+
+ timer.setTimeout(function () {
+ succeed = true;
+ }, 200);
+}
+
+exports.testWaitUntilMatches = function(test) {
+ test.waitUntilDone();
+ let succeed = false;
+
+ test.waitUntilMatches(function () succeed ? "foo" : "bar",
+ /foo/, "waitUntilEqual pass")
+ .then(function () test.done());
+
+ timer.setTimeout(function () {
+ succeed = true;
+ }, 200);
+}
+
+exports.testWaitUntilErrorInCallback = function(test) {
+ test.waitUntilDone();
+
+ test.expectFail(function() {
+ test.waitUntil(function () {throw "oops"}, "waitUntil pass")
+ .then(function () test.done());
+ });
+}
+
+exports.testWaitUntilTimeoutInCallback = function(test) {
+ test.waitUntilDone(1000);
+
+ let runner = new (require("unit-test").TestRunner)({
+ console: {
+ calls: 0,
+ error: function(msg) {
+ this.calls++;
+ if (this.calls == 1)
+ test.assertEqual(arguments[0], "TEST FAILED: wait4ever (timed out)");
+ else if (this.calls == 2) {
+ test.assertEqual(arguments[0], "test assertion never became true:\n");
+ test.assertEqual(arguments[1], "assertion failed, value is false\n");
+ // We could additionally check that arguments[1] contains the correct
+ // stack, but it would be difficult to do so given that it contains
+ // resource: URLs with a randomly generated string embedded in them
+ // (the ID of the test addon created to run the tests). And in any
+ // case, checking the arguments seems sufficient.
+
+ test.done();
+ }
+ else {
+ test.fail("We got unexpected console.error() calls from waitUntil" +
+ " assertion callback: '" + arguments[1] + "'");
+ }
+ },
+ trace: function () {}
+ }
+ });
+
+ runner.start({
+ test: {
+ name: "wait4ever",
+ testFunction: function(test) {
+ test.waitUntilDone(100);
+ test.waitUntil(function() false);
+ }
+ },
+ onDone: function() {}
+ });
+};
+
+exports.testExpectFail = function(test) {
+ test.expectFail(function() {
+ test.fail('expectFail masking .fail');
+ });
+
+ test.expectFail(function() {
+ test.assert(false, 'expectFail masking .assert');
+ });
+
+ test.assert(true, 'assert should pass with no expectFail');
+/*
+ test.expectFail(function() {
+ test.expectFail(function() {
+ test.fail('this should blow up');
+ });
+ });
+*/
+};
+
+exports.testAssertFunction = function(test) {
+ test.assertFunction(function() {}, 'assertFunction with function');
+ test.expectFail(function() {
+ test.assertFunction(null, 'assertFunction with non-function');
+ });
+};
+
+exports.testAssertUndefined = function(test) {
+ test.assertUndefined(undefined, 'assertUndefined with undefined');
+ test.expectFail(function() {
+ test.assertUndefined(null, 'assertUndefined with null');
+ });
+ test.expectFail(function() {
+ test.assertUndefined(false, 'assertUndefined with false');
+ });
+ test.expectFail(function() {
+ test.assertUndefined(0, 'assertUndefined with 0');
+ });
+};
+
+exports.testAssertNotUndefined = function(test) {
+ test.expectFail(function() {
+ test.assertNotUndefined(undefined, 'assertNotUndefined with undefined');
+ });
+ test.assertNotUndefined(null, 'assertNotUndefined with null');
+ test.assertNotUndefined(false, 'assertNotUndefined with false');
+ test.assertNotUndefined(0, 'assertNotUndefined with 0');
+};
+
+exports.testAssertNull = function(test) {
+ test.assertNull(null, 'assertNull with null');
+ test.expectFail(function() {
+ test.assertNull(undefined, 'assertNull with undefined');
+ });
+ test.expectFail(function() {
+ test.assertNull(false, 'assertNull with false');
+ });
+ test.expectFail(function() {
+ test.assertNull(0, 'assertNull with 0');
+ });
+};
+
+exports.testAssertNotNull = function(test) {
+ test.assertNotNull(undefined, 'assertNotNull with undefined');
+ test.assertNotNull(false, 'assertNotNull with false');
+ test.assertNotNull(0, 'assertNotNull with 0');
+
+ test.expectFail(function() {
+ test.assertNotNull(null, 'testAssertNotNull with null');
+ });
+};
+
+exports.testAssertObject = function(test) {
+ test.assertObject({}, 'assertObject with {}' );
+ test.assertObject(new Object(), 'assertObject with new Object');
+ test.expectFail(function() {
+ test.assertObject('fail', 'assertObject with string');
+ });
+};
+
+exports.testAssertString = function(test) {
+ test.assertString('', 'assertString with ""');
+ test.assertString(new String(), 'assertString with new String');
+};
+
+exports.testAssertArray = function(test) {
+ test.assertArray([], 'assertArray with []');
+ test.assertArray(new Array(), 'assertArray with new Array');
+};
+
+exports.testNumber = function(test) {
+ test.assertNumber(1, 'assertNumber with 1');
+ test.assertNumber(new Number('2'), 'assertNumber with new Number("2")' );
+};
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-unload.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-unload.js
new file mode 100644
index 0000000..e05b4e0
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-unload.js
@@ -0,0 +1,161 @@
+/* -*- 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/. */
+
+var unload = require("unload");
+var { Loader } = require("./helpers");
+
+exports.testUnloading = function(test) {
+ var loader = Loader(module);
+ var ul = loader.require("unload");
+ var unloadCalled = 0;
+ var errorsReported = 0;
+ function unload() {
+ unloadCalled++;
+ throw new Error("error");
+ }
+ ul.when(unload);
+
+ // This should be ignored, as we already registered it
+ ul.when(unload);
+
+ function unload2() { unloadCalled++; }
+ ul.when(unload2);
+ loader.unload(undefined, function onError() { errorsReported++; });
+ test.assertEqual(unloadCalled, 2,
+ "Unloader functions are called on unload.");
+ test.assertEqual(errorsReported, 1,
+ "One unload handler threw exception");
+};
+
+exports.testEnsure = function(test) {
+ test.assertRaises(function() { unload.ensure({}); },
+ "object has no 'unload' property",
+ "passing obj with no unload prop should fail");
+ test.assertRaises(function() { unload.ensure({}, "destroy"); },
+ "object has no 'destroy' property",
+ "passing obj with no custom unload prop should fail");
+
+ var called = 0;
+ var obj = {unload: function() { called++; }};
+
+ unload.ensure(obj);
+ obj.unload();
+ test.assertEqual(called, 1,
+ "unload() should be called");
+ obj.unload();
+ test.assertEqual(called, 1,
+ "unload() should be called only once");
+};
+
+/**
+ * Check that destructors are called only once with Traits.
+ * - check that public API is calling the destructor and unregister it,
+ * - check that composed traits with multiple ensure calls, leads to only
+ * one destructor call.
+ */
+exports.testEnsureWithTraits = function(test) {
+
+ let { Trait } = require("traits");
+ let loader = Loader(module);
+ let ul = loader.require("unload");
+
+ let called = 0;
+ let composedCalled = 0;
+ let composedTrait = Trait.compose({
+ constructor: function () {
+ // We have to give "public interface" of this trait, as we want to
+ // call public `unload` method and ensure that we call it only once,
+ // either when we call this public function manually or on add-on unload
+ ul.ensure(this._public);
+ },
+ unload: function unload() {
+ composedCalled++;
+ }
+ });
+ let obj = Trait.compose(
+ composedTrait.resolve({
+ constructor: "_constructor",
+ unload : "_unload"
+ }), {
+ constructor: function constructor() {
+ // Same thing applies here, we need to pass public interface
+ ul.ensure(this._public);
+ this._constructor();
+ },
+ unload: function unload() {
+ called++;
+ this._unload();
+ }
+ })();
+
+ obj.unload();
+ test.assertEqual(called, 1,
+ "unload() should be called");
+
+ test.assertEqual(composedCalled, 1,
+ "composed object unload() should be called");
+
+ obj.unload();
+ test.assertEqual(called, 1,
+ "unload() should be called only once");
+ test.assertEqual(composedCalled, 1,
+ "composed object unload() should be called only once");
+
+ loader.unload();
+ test.assertEqual(called, 1,
+ "unload() should be called only once, after addon unload");
+ test.assertEqual(composedCalled, 1,
+ "composed object unload() should be called only once, " +
+ "after addon unload");
+};
+
+exports.testEnsureWithTraitsPrivate = function(test) {
+
+ let { Trait } = require("traits");
+ let loader = Loader(module);
+ let ul = loader.require("unload");
+
+ let called = 0;
+ let privateObj = null;
+ let obj = Trait.compose({
+ constructor: function constructor() {
+ // This time wa don't have to give public interface,
+ // as we want to call a private method:
+ ul.ensure(this, "_unload");
+ privateObj = this;
+ },
+ _unload: function unload() {
+ called++;
+ this._unload();
+ }
+ })();
+
+ loader.unload();
+ test.assertEqual(called, 1,
+ "unload() should be called");
+
+ privateObj._unload();
+ test.assertEqual(called, 1,
+ "_unload() should be called only once, after addon unload");
+};
+
+exports.testReason = function (test) {
+ var reason = "Reason doesn't actually have to be anything in particular.";
+ var loader = Loader(module);
+ var ul = loader.require("unload");
+ ul.when(function (rsn) {
+ test.assertEqual(rsn, reason,
+ "when() reason should be reason given to loader");
+ });
+ var obj = {
+ unload: function (rsn) {
+ test.assertEqual(rsn, reason,
+ "ensure() reason should be reason given to loader");
+ }
+ };
+ ul.ensure(obj);
+ loader.unload(reason);
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-url.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-url.js
new file mode 100644
index 0000000..f217400
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-url.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/. */
+
+var url = require("url");
+
+exports.testResolve = function(test) {
+ test.assertEqual(url.URL("bar", "http://www.foo.com/").toString(),
+ "http://www.foo.com/bar");
+
+ test.assertEqual(url.URL("bar", "http://www.foo.com"),
+ "http://www.foo.com/bar");
+
+ test.assertEqual(url.URL("http://bar.com/", "http://foo.com/"),
+ "http://bar.com/",
+ "relative should override base");
+
+ test.assertRaises(function() { url.URL("blah"); },
+ "malformed URI: blah",
+ "url.resolve() should throw malformed URI on base");
+
+ test.assertRaises(function() { url.URL("chrome://global"); },
+ "invalid URI: chrome://global",
+ "url.resolve() should throw invalid URI on base");
+
+ test.assertRaises(function() { url.URL("chrome://foo/bar"); },
+ "invalid URI: chrome://foo/bar",
+ "url.resolve() should throw on bad chrome URI");
+
+ test.assertEqual(url.URL("", "http://www.foo.com"),
+ "http://www.foo.com/",
+ "url.resolve() should add slash to end of domain");
+};
+
+exports.testParseHttp = function(test) {
+ var info = url.URL("http://foo.com/bar");
+ test.assertEqual(info.scheme, "http");
+ test.assertEqual(info.host, "foo.com");
+ test.assertEqual(info.port, null);
+ test.assertEqual(info.userPass, null);
+ test.assertEqual(info.path, "/bar");
+};
+
+exports.testParseHttpWithPort = function(test) {
+ var info = url.URL("http://foo.com:5/bar");
+ test.assertEqual(info.port, 5);
+};
+
+exports.testParseChrome = function(test) {
+ var info = url.URL("chrome://global/content/blah");
+ test.assertEqual(info.scheme, "chrome");
+ test.assertEqual(info.host, "global");
+ test.assertEqual(info.port, null);
+ test.assertEqual(info.userPass, null);
+ test.assertEqual(info.path, "/content/blah");
+};
+
+exports.testParseAbout = function(test) {
+ var info = url.URL("about:boop");
+ test.assertEqual(info.scheme, "about");
+ test.assertEqual(info.host, null);
+ test.assertEqual(info.port, null);
+ test.assertEqual(info.userPass, null);
+ test.assertEqual(info.path, "boop");
+};
+
+exports.testParseFTP = function(test) {
+ var info = url.URL("ftp://1.2.3.4/foo");
+ test.assertEqual(info.scheme, "ftp");
+ test.assertEqual(info.host, "1.2.3.4");
+ test.assertEqual(info.port, null);
+ test.assertEqual(info.userPass, null);
+ test.assertEqual(info.path, "/foo");
+};
+
+exports.testParseFTPWithUserPass = function(test) {
+ var info = url.URL("ftp://user:pass@1.2.3.4/foo");
+ test.assertEqual(info.userPass, "user:pass");
+};
+
+// rootURI is jar:file://...!/ if we're packed, and file://.../ if we're
+// unpacked. url.toFilename() is not required to work for the contents of
+// packed XPIs
+var unpacked = (require("@packaging").rootURI.indexOf("file:") == 0);
+
+exports.testToFilename = function(test) {
+ test.assertRaises(
+ function() { url.toFilename("resource://nonexistent"); },
+ "resource does not exist: resource://nonexistent/",
+ "url.toFilename() on nonexistent resources should throw"
+ );
+
+ if (unpacked)
+ test.assertMatches(url.toFilename(module.uri),
+ /.*test-url\.js$/,
+ "url.toFilename() on resource: URIs should work");
+ else
+ test.assertRaises(
+ function() { url.toFilename(module.uri); },
+ "cannot map to filename: "+module.uri,
+ "url.toFilename() can fail for packed XPIs");
+
+ test.assertRaises(
+ function() { url.toFilename("http://foo.com/"); },
+ "cannot map to filename: http://foo.com/",
+ "url.toFilename() on http: URIs should raise error"
+ );
+
+ try {
+ test.assertMatches(
+ url.toFilename("chrome://global/content/console.xul"),
+ /.*console\.xul$/,
+ "url.toFilename() w/ console.xul works when it maps to filesystem"
+ );
+ } catch (e) {
+ if (/chrome url isn\'t on filesystem/.test(e.message))
+ test.pass("accessing console.xul in jar raises exception");
+ else
+ test.fail("accessing console.xul raises " + e);
+ }
+
+ // TODO: Are there any chrome URLs that we're certain exist on the
+ // filesystem?
+ // test.assertMatches(url.toFilename("chrome://myapp/content/main.js"),
+ // /.*main\.js$/);
+};
+
+exports.testFromFilename = function(test) {
+ var profileDirName = require("system").pathFor("ProfD");
+ var fileUrl = url.fromFilename(profileDirName);
+ test.assertEqual(url.URL(fileUrl).scheme, 'file',
+ 'url.toFilename() should return a file: url');
+ test.assertEqual(url.fromFilename(url.toFilename(fileUrl)),
+ fileUrl);
+};
+
+exports.testURL = function(test) {
+ let URL = url.URL;
+ test.assert(URL("h:foo") instanceof URL, "instance is of correct type");
+ test.assertRaises(function() URL(),
+ "malformed URI: undefined",
+ "url.URL should throw on undefined");
+ test.assertRaises(function() URL(""),
+ "malformed URI: ",
+ "url.URL should throw on empty string");
+ test.assertRaises(function() URL("foo"),
+ "malformed URI: foo",
+ "url.URL should throw on invalid URI");
+ test.assert(URL("h:foo").scheme, "has scheme");
+ test.assertEqual(URL("h:foo").toString(),
+ "h:foo",
+ "toString should roundtrip");
+ // test relative + base
+ test.assertEqual(URL("mypath", "http://foo").toString(),
+ "http://foo/mypath",
+ "relative URL resolved to base");
+ // test relative + no base
+ test.assertRaises(function() URL("path").toString(),
+ "malformed URI: path",
+ "no base for relative URI should throw");
+
+ let a = URL("h:foo");
+ let b = URL(a);
+ test.assertEqual(b.toString(),
+ "h:foo",
+ "a URL can be initialized from another URL");
+ test.assertNotStrictEqual(a, b,
+ "a URL initialized from another URL is not the same object");
+ test.assert(a == "h:foo",
+ "toString is implicit when a URL is compared to a string via ==");
+ test.assertStrictEqual(a + "", "h:foo",
+ "toString is implicit when a URL is concatenated to a string");
+};
+
+exports.testStringInterface = function(test) {
+ let URL = url.URL;
+ var EM = "about:addons";
+ var a = URL(EM);
+
+ // make sure the standard URL properties are enumerable and not the String interface bits
+ test.assertEqual(Object.keys(a), "scheme,userPass,host,port,path", "enumerable key list check for URL.");
+ test.assertEqual(
+ JSON.stringify(a),
+ "{\"scheme\":\"about\",\"userPass\":null,\"host\":null,\"port\":null,\"path\":\"addons\"}",
+ "JSON.stringify should return a object with correct props and vals.");
+
+ // make sure that the String interface exists and works as expected
+ test.assertEqual(a.indexOf(":"), EM.indexOf(":"), "indexOf on URL works");
+ test.assertEqual(a.valueOf(), EM.valueOf(), "valueOf on URL works.");
+ test.assertEqual(a.toSource(), EM.toSource(), "toSource on URL works.");
+ test.assertEqual(a.lastIndexOf("a"), EM.lastIndexOf("a"), "lastIndexOf on URL works.");
+ test.assertEqual(a.match("t:").toString(), EM.match("t:").toString(), "match on URL works.");
+ test.assertEqual(a.toUpperCase(), EM.toUpperCase(), "toUpperCase on URL works.");
+ test.assertEqual(a.toLowerCase(), EM.toLowerCase(), "toLowerCase on URL works.");
+ test.assertEqual(a.split(":").toString(), EM.split(":").toString(), "split on URL works.");
+ test.assertEqual(a.charAt(2), EM.charAt(2), "charAt on URL works.");
+ test.assertEqual(a.charCodeAt(2), EM.charCodeAt(2), "charCodeAt on URL works.");
+ test.assertEqual(a.concat(EM), EM.concat(a), "concat on URL works.");
+ test.assertEqual(a.substr(2,3), EM.substr(2,3), "substr on URL works.");
+ test.assertEqual(a.substring(2,3), EM.substring(2,3), "substring on URL works.");
+ test.assertEqual(a.trim(), EM.trim(), "trim on URL works.");
+ test.assertEqual(a.trimRight(), EM.trimRight(), "trimRight on URL works.");
+ test.assertEqual(a.trimLeft(), EM.trimLeft(), "trimLeft on URL works.");
+}
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-uuid.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-uuid.js
new file mode 100644
index 0000000..5670023
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-uuid.js
@@ -0,0 +1,27 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 { uuid } = require('api-utils/uuid');
+
+exports['test generate uuid'] = function(assert) {
+ let signature = /{[0-9a-f\-]+}/
+ let first = String(uuid());
+ let second = String(uuid());
+
+ assert.ok(signature.test(first), 'first guid has a correct signature');
+ assert.ok(signature.test(second), 'second guid has a correct signature');
+ assert.notEqual(first, second, 'guid generates new guid on each call');
+};
+
+exports['test parse uuid'] = function(assert) {
+ let firefoxUUID = '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}';
+ let actual = uuid(firefoxUUID);
+
+ assert.equal(actual.number, firefoxUUID, 'uuid parsed given string');
+ assert.equal(String(actual), firefoxUUID, 'serializes to the same value');
+};
+
+require('test').run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-window-loader.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-window-loader.js
new file mode 100644
index 0000000..0a5ab84
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-window-loader.js
@@ -0,0 +1,120 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const { WindowLoader } = require('windows/loader'),
+ { Trait } = require('traits');
+
+const Loader = Trait.compose(
+ WindowLoader,
+ {
+ constructor: function Loader(options) {
+ this._onLoad = options.onLoad;
+ this._onUnload = options.onUnload;
+ if ('window' in options)
+ this._window = options.window;
+ this._load();
+ this.window = this._window;
+ },
+ window: null,
+ _onLoad: null,
+ _onUnload: null,
+ _tabOptions: []
+ }
+);
+
+exports['test compositions with missing required properties'] = function(test) {
+ test.assertRaises(
+ function() WindowLoader.compose({})(),
+ 'Missing required property: _onLoad',
+ 'should throw missing required property exception'
+ );
+ test.assertRaises(
+ function() WindowLoader.compose({ _onLoad: null, _tabOptions: null })(),
+ 'Missing required property: _onUnload',
+ 'should throw missing required property `_onUnload`'
+ );
+ test.assertRaises(
+ function() WindowLoader.compose({ _onUnload: null, _tabOptions: null })(),
+ 'Missing required property: _onLoad',
+ 'should throw missing required property `_onLoad`'
+ );
+ test.assertRaises(
+ function() WindowLoader.compose({ _onUnload: null, _onLoad: null })(),
+ 'Missing required property: _tabOptions',
+ 'should throw missing required property `_tabOptions`'
+ );
+};
+
+exports['test `load` events'] = function(test) {
+ test.waitUntilDone();
+ let onLoadCalled = false;
+ Loader({
+ onLoad: function(window) {
+ onLoadCalled = true;
+ test.assertEqual(
+ window, this._window, 'windows should match'
+ );
+ test.assertEqual(
+ window.document.readyState, 'complete', 'window must be fully loaded'
+ );
+ window.close();
+ },
+ onUnload: function(window) {
+ test.assertEqual(
+ window, this._window, 'windows should match'
+ );
+ test.assertEqual(
+ window.document.readyState, 'complete', 'window must be fully loaded'
+ );
+ test.assert(onLoadCalled, 'load callback is supposed to be called');
+ test.done();
+ }
+ });
+};
+
+exports['test removeing listeners'] = function(test) {
+ test.waitUntilDone();
+ Loader({
+ onLoad: function(window) {
+ test.assertEqual(
+ window, this._window, 'windows should match'
+ );
+ window.close();
+ },
+ onUnload: function(window) {
+ test.done();
+ }
+ });
+};
+
+exports['test create loader from opened window'] = function(test) {
+ test.waitUntilDone();
+ let onUnloadCalled = false;
+ Loader({
+ onLoad: function(window) {
+ test.assertEqual(
+ window, this._window, 'windows should match'
+ );
+ test.assertEqual(
+ window.document.readyState, 'complete', 'window must be fully loaded'
+ );
+ Loader({
+ window: window,
+ onLoad: function(win) {
+ test.assertEqual(win, window, 'windows should match');
+ window.close();
+ },
+ onUnload: function(window) {
+ test.assert(onUnloadCalled, 'first handler should be called already');
+ test.done();
+ }
+ });
+ },
+ onUnload: function(window) {
+ onUnloadCalled = true;
+ }
+ });
+};
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-window-observer.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-window-observer.js
new file mode 100644
index 0000000..687df04
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-window-observer.js
@@ -0,0 +1,48 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { Loader } = require("./helpers");
+
+exports["test unload window observer"] = function(assert, done) {
+ // Hacky way to be able to create unloadable modules via makeSandboxedLoader.
+ let loader = Loader(module);
+
+ let utils = loader.require("api-utils/window-utils");
+ let { isBrowser, activeBrowserWindow: activeWindow } = utils;
+ let observer = loader.require("api-utils/windows/observer").observer;
+ let opened = 0;
+ let closed = 0;
+
+ observer.on("open", function onOpen(window) {
+ // Ignoring non-browser windows
+ if (isBrowser(window))
+ opened++;
+ });
+ observer.on("close", function onClose(window) {
+ // Ignore non-browser windows & already opened `activeWindow` (unload will
+ // emit close on it even though it is not actually closed).
+ if (isBrowser(window) && window !== activeWindow)
+ closed++;
+ });
+
+ // Open window and close it to trigger observers.
+ activeWindow.open().close();
+
+ // Unload the module so that all listeners set by observer are removed.
+ loader.unload();
+
+ // Open and close window once again.
+ activeWindow.open().close();
+
+ // Enqueuing asserts to make sure that assertion is not performed early.
+ require("timer").setTimeout(function () {
+ assert.equal(1, opened, "observer open was called before unload only");
+ assert.equal(1, closed, "observer close was called before unload only");
+ done();
+ }, 0);
+};
+
+require("test").run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-window-utils.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-window-utils.js
new file mode 100644
index 0000000..9985458
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-window-utils.js
@@ -0,0 +1,321 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 windowUtils = require("api-utils/window-utils");
+var timer = require("api-utils/timer");
+var { Cc, Ci } = require("chrome");
+var { Loader } = require("./helpers");
+
+function toArray(iterator) {
+ let array = [];
+ for each (let item in iterator())
+ array.push(item);
+ return array;
+}
+
+function makeEmptyWindow() {
+ var xulNs = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ var blankXul = ('<?xml version="1.0"?>' +
+ '<?xml-stylesheet href="chrome://global/skin/" ' +
+ ' type="text/css"?>' +
+ '<window xmlns="' + xulNs + '" windowtype="test:window">' +
+ '</window>');
+ var url = "data:application/vnd.mozilla.xul+xml," + escape(blankXul);
+ var features = ["chrome", "width=10", "height=10"];
+
+ var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"]
+ .getService(Ci.nsIWindowWatcher);
+ return ww.openWindow(null, url, null, features.join(","), null);
+}
+
+exports['test close on unload'] = function(assert) {
+ var timesClosed = 0;
+ var fakeWindow = {
+ _listeners: [],
+ addEventListener: function(name, func, bool) {
+ this._listeners.push(func);
+ },
+ removeEventListener: function(name, func, bool) {
+ var index = this._listeners.indexOf(func);
+ if (index == -1)
+ throw new Error("event listener not found");
+ this._listeners.splice(index, 1);
+ },
+ close: function() {
+ timesClosed++;
+ this._listeners.forEach(
+ function(func) {
+ func({target: fakeWindow.document});
+ });
+ },
+ document: {
+ get defaultView() { return fakeWindow; }
+ }
+ };
+
+ let loader = Loader(module);
+ loader.require("window-utils").closeOnUnload(fakeWindow);
+ assert.equal(fakeWindow._listeners.length, 1,
+ "unload listener added on closeOnUnload()");
+ assert.equal(timesClosed, 0,
+ "window not closed when registered.");
+ loader.require("unload").send();
+ assert.equal(timesClosed, 1,
+ "window closed on module unload.");
+ assert.equal(fakeWindow._listeners.length, 0,
+ "unload event listener removed on module unload");
+
+ timesClosed = 0;
+ loader.require("window-utils").closeOnUnload(fakeWindow);
+ assert.equal(timesClosed, 0,
+ "window not closed when registered.");
+ fakeWindow.close();
+ assert.equal(timesClosed, 1,
+ "window closed when close() called.");
+ assert.equal(fakeWindow._listeners.length, 0,
+ "unload event listener removed on window close");
+ loader.require("unload").send();
+ assert.equal(timesClosed, 1,
+ "window not closed again on module unload.");
+ loader.unload();
+};
+
+exports['test window watcher'] = function(assert, done) {
+ var myWindow;
+ var finished = false;
+
+ var delegate = {
+ onTrack: function(window) {
+ if (window == myWindow) {
+ assert.pass("onTrack() called with our test window");
+ timer.setTimeout(function() { myWindow.close(); }, 1);
+ }
+ },
+ onUntrack: function(window) {
+ if (window == myWindow) {
+ assert.pass("onUntrack() called with our test window");
+ timer.setTimeout(function() {
+ if (!finished) {
+ finished = true;
+ myWindow = null;
+ wt.unload();
+ done();
+ } else
+ assert.fail("finishTest() called multiple times.");
+ }, 1);
+ }
+ }
+ };
+
+ // test bug 638007 (new is optional), using new
+ var wt = new windowUtils.WindowTracker(delegate);
+ myWindow = makeEmptyWindow();
+};
+
+exports['test window watcher untracker'] = function(assert, done) {
+ var myWindow;
+ var tracks = 0;
+ var unloadCalled = false;
+
+ var delegate = {
+ onTrack: function(window) {
+ tracks = tracks + 1;
+ if (window == myWindow) {
+ assert.pass("onTrack() called with our test window");
+ timer.setTimeout(function() {
+ myWindow.close();
+ }, 1);
+ }
+ },
+ onUntrack: function(window) {
+ tracks = tracks - 1;
+ if (window == myWindow && !unloadCalled) {
+ unloadCalled = true;
+ timer.setTimeout(function() {
+ wt.unload();
+ }, 1);
+ }
+ if (0 > tracks) {
+ assert.fail("WindowTracker onUntrack was called more times than onTrack..");
+ }
+ else if (0 == tracks) {
+ timer.setTimeout(function() {
+ myWindow = null;
+ done();
+ }, 1);
+ }
+ }
+ };
+
+ // test bug 638007 (new is optional), not using new
+ var wt = windowUtils.WindowTracker(delegate);
+ myWindow = makeEmptyWindow();
+};
+
+// test that _unregWindow calls _unregLoadingWindow
+exports['test window watcher unregs 4 loading wins'] = function(assert, done) {
+ var myWindow;
+ var finished = false;
+ let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"]
+ .getService(Ci.nsIWindowMediator)
+ .getMostRecentWindow("navigator:browser");
+ var counter = 0;
+
+ var delegate = {
+ onTrack: function(window) {
+ var type = window.document.documentElement.getAttribute("windowtype");
+ if (type == "test:window")
+ assert.fail("onTrack shouldn't have been executed.");
+ }
+ };
+ var wt = new windowUtils.WindowTracker(delegate);
+
+ // make a new window
+ myWindow = makeEmptyWindow();
+
+ // make sure that the window hasn't loaded yet
+ assert.notEqual(
+ myWindow.document.readyState,
+ "complete",
+ "window hasn't loaded yet.");
+
+ // unload WindowTracker
+ wt.unload();
+
+ // make sure that the window still hasn't loaded, which means that the onTrack
+ // would have been removed successfully assuming that it doesn't execute.
+ assert.notEqual(
+ myWindow.document.readyState,
+ "complete",
+ "window still hasn't loaded yet.");
+
+ // wait for the window to load and then close it. onTrack wouldn't be called
+ // until the window loads, so we must let it load before closing it to be
+ // certain that onTrack was removed.
+ myWindow.addEventListener("load", function() {
+ // allow all of the load handles to execute before closing
+ myWindow.setTimeout(function() {
+ myWindow.addEventListener("unload", function() {
+ // once the window unloads test is done
+ done();
+ }, false);
+ myWindow.close();
+ }, 0);
+ }, false);
+}
+
+exports['test window watcher without untracker'] = function(assert, done) {
+ var myWindow;
+ var finished = false;
+
+ var delegate = {
+ onTrack: function(window) {
+ if (window == myWindow) {
+ assert.pass("onTrack() called with our test window");
+ timer.setTimeout(function() {
+ myWindow.close();
+
+ if (!finished) {
+ finished = true;
+ myWindow = null;
+ wt.unload();
+ done();
+ } else {
+ assert.fail("onTrack() called multiple times.");
+ }
+ }, 1);
+ }
+ }
+ };
+
+ var wt = new windowUtils.WindowTracker(delegate);
+ myWindow = makeEmptyWindow();
+};
+
+exports['test active window'] = function(assert, done) {
+ let testRunnerWindow = Cc["@mozilla.org/appshell/window-mediator;1"]
+ .getService(Ci.nsIWindowMediator)
+ .getMostRecentWindow("test:runner");
+ let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"]
+ .getService(Ci.nsIWindowMediator)
+ .getMostRecentWindow("navigator:browser");
+
+ assert.equal(windowUtils.activeBrowserWindow, browserWindow,
+ "Browser window is the active browser window.");
+
+
+ let testSteps = [
+ function() {
+ windowUtils.activeWindow = browserWindow;
+ continueAfterFocus(browserWindow);
+ },
+ function() {
+ assert.equal(windowUtils.activeWindow, browserWindow,
+ "Correct active window [1]");
+ continueAfterFocus(windowUtils.activeWindow = testRunnerWindow);
+ },
+ function() {
+ assert.equal(windowUtils.activeWindow, testRunnerWindow,
+ "Correct active window [2]");
+ assert.equal(windowUtils.activeBrowserWindow, browserWindow,
+ "Correct active browser window [3]");
+ continueAfterFocus(windowUtils.activeWindow = browserWindow);
+ },
+ function() {
+ assert.equal(windowUtils.activeWindow, browserWindow,
+ "Correct active window [4]");
+ continueAfterFocus(windowUtils.activeWindow = testRunnerWindow);
+ },
+ function() {
+ assert.equal(windowUtils.activeWindow, testRunnerWindow,
+ "Correct active window [5]");
+ assert.equal(windowUtils.activeBrowserWindow, browserWindow,
+ "Correct active browser window [6]");
+ testRunnerWindow = null;
+ browserWindow = null;
+ done();
+ }
+ ];
+
+ let nextTest = function() {
+ let func = testSteps.shift();
+ if (func) {
+ func();
+ }
+ }
+
+ 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) {
+ nextTest();
+ } else {
+ childTargetWindow.addEventListener("focus", function focusListener() {
+ childTargetWindow.removeEventListener("focus", focusListener, true);
+ nextTest();
+ }, true);
+ }
+
+ }
+
+ nextTest();
+};
+
+require("test").run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-window-utils2.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-window-utils2.js
new file mode 100644
index 0000000..62f34f9
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-window-utils2.js
@@ -0,0 +1,67 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+const { Ci } = require('chrome');
+const { open, backgroundify,
+ getXULWindow, getBaseWindow } = require('api-utils/window/utils');
+const windowUtils = require('api-utils/window-utils');
+
+function windows(iterator) {
+ let array = [];
+ for each (let item in windowUtils.windowIterator())
+ array.push(item);
+ return array;
+}
+
+exports['test get nsIBaseWindow from nsIDomWindow'] = function(assert) {
+ let active = windowUtils.activeBrowserWindow;
+
+ assert.ok(!(active instanceof Ci.nsIBaseWindow),
+ 'active window is not nsIBaseWindow');
+
+ assert.ok(getBaseWindow(active) instanceof Ci.nsIBaseWindow,
+ 'base returns nsIBaseWindow');
+};
+
+exports['test get nsIXULWindow from nsIDomWindow'] = function(assert) {
+ let active = windowUtils.activeBrowserWindow;
+ assert.ok(!(active instanceof Ci.nsIXULWindow),
+ 'active window is not nsIXULWindow');
+ assert.ok(getXULWindow(active) instanceof Ci.nsIXULWindow,
+ 'base returns nsIXULWindow');
+};
+
+exports['test top window creation'] = function(assert) {
+ let window = open('data:text/html,Hello top window');
+ assert.ok(~windows().indexOf(window), 'window was opened');
+ window.close();
+};
+
+exports['test new top window with options'] = function(assert) {
+ let window = open('data:text/html,Hi custom top window', {
+ name: 'test',
+ features: { height: 100, width: 200, toolbar: true }
+ });
+ assert.ok(~windows().indexOf(window), 'window was opened');
+ assert.equal(window.name, 'test', 'name was set');
+ assert.equal(window.innerHeight, 100, 'height is set');
+ assert.equal(window.innerWidth, 200, 'height is set');
+ assert.equal(window.toolbar.visible, true, 'toolbar was set');
+ window.close();
+};
+
+exports['test backgroundify'] = function(assert) {
+ let window = open('data:text/html,backgroundy');
+ assert.ok(~windows().indexOf(window),
+ 'window is in the list of windows');
+ let backgroundy = backgroundify(window);
+ assert.equal(backgroundy, window, 'backgroundify returs give window back');
+ assert.ok(!~windows().indexOf(window),
+ 'backgroundifyied window is in the list of windows');
+ window.close();
+};
+
+require('test').run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-xhr.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-xhr.js
new file mode 100644
index 0000000..ad47e54
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-xhr.js
@@ -0,0 +1,75 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var xhr = require("xhr");
+var timer = require("timer");
+var { Loader } = require("./helpers");
+var xulApp = require("xul-app");
+
+/* Test is intentionally disabled until platform bug 707256 is fixed.
+exports.testAbortedXhr = function(test) {
+ var req = new xhr.XMLHttpRequest();
+ test.assertEqual(xhr.getRequestCount(), 1);
+ req.abort();
+ test.assertEqual(xhr.getRequestCount(), 0);
+};
+*/
+
+exports.testLocalXhr = function(test) {
+ var req = new xhr.XMLHttpRequest();
+ req.overrideMimeType("text/plain");
+ req.open("GET", module.uri);
+ req.onreadystatechange = function() {
+ if (req.readyState == 4 && req.status == 0) {
+ test.assertMatches(req.responseText,
+ /onreadystatechange/,
+ "XMLHttpRequest should get local files");
+ timer.setTimeout(
+ function() { test.assertEqual(xhr.getRequestCount(), 0);
+ test.done(); },
+ 0
+ );
+ }
+ };
+ req.send(null);
+ test.assertEqual(xhr.getRequestCount(), 1);
+ test.waitUntilDone(4000);
+};
+
+exports.testUnload = function(test) {
+ var loader = Loader(module);
+ var sbxhr = loader.require("xhr");
+ var req = new sbxhr.XMLHttpRequest();
+ req.overrideMimeType("text/plain");
+ req.open("GET", module.uri);
+ req.send(null);
+ test.assertEqual(sbxhr.getRequestCount(), 1);
+ loader.unload();
+ test.assertEqual(sbxhr.getRequestCount(), 0);
+};
+
+exports.testResponseHeaders = function(test) {
+ var req = new xhr.XMLHttpRequest();
+ req.overrideMimeType("text/plain");
+ req.open("GET", module.uri);
+ req.onreadystatechange = function() {
+ if (req.readyState == 4 && req.status == 0) {
+ var headers = req.getAllResponseHeaders();
+ if (xulApp.versionInRange(xulApp.platformVersion, "13.0a1", "*")) {
+ // Now that bug 608939 is FIXED, headers works correctly on files:
+ test.assertEqual(headers, "Content-Type: text/plain\n",
+ "XHR's headers are valid");
+ }
+ else {
+ test.assert(headers === null || headers === "",
+ "XHR's headers are empty");
+ }
+ test.done();
+ }
+ };
+ req.send(null);
+ test.assertEqual(xhr.getRequestCount(), 1);
+ test.waitUntilDone(4000);
+}
+
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-xpcom.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-xpcom.js
new file mode 100644
index 0000000..eda3565
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-xpcom.js
@@ -0,0 +1,217 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 xpcom = require("api-utils/xpcom");
+const { Cc, Ci, Cm, Cr } = require("chrome");
+const { isCIDRegistered } = Cm.QueryInterface(Ci.nsIComponentRegistrar);
+const { Loader } = require("./helpers");
+
+exports['test Unknown implements nsISupports'] = function(assert) {
+ let actual = xpcom.Unknown.new();
+ assert.equal(actual.QueryInterface(Ci.nsISupports),
+ actual,
+ 'component implements nsISupports');
+};
+
+exports['test implement xpcom interfaces'] = function(assert) {
+ let component = xpcom.Unknown.extend({
+ interfaces: [ 'nsIWeakReference' ],
+ QueryReferent: function() {}
+ })
+
+ assert.equal(component.QueryInterface(Ci.nsISupports),
+ component,
+ 'component implements nsISupports');
+ assert.equal(component.QueryInterface(Ci.nsIWeakReference),
+ component,
+ 'component implements specified interface');
+
+ assert.throws(function() {
+ component.QueryInterface(Ci.nsIObserver);
+ }, "component does not implements interface");
+
+ let actual = component.extend({
+ interfaces: [ 'nsIObserver', 'nsIRequestObserver' ],
+ observe: function() {},
+ onStartRequest: function() {},
+ onStopRequest: function() {}
+ });
+
+ assert.equal(actual.QueryInterface(Ci.nsISupports),
+ actual,
+ 'derived component implements nsISupports');
+ assert.equal(actual.QueryInterface(Ci.nsIWeakReference),
+ actual,
+ 'derived component implements supers interface');
+ assert.equal(actual.QueryInterface(Ci.nsIObserver),
+ actual.QueryInterface(Ci.nsIRequestObserver),
+ 'derived component implements specified interfaces');
+};
+
+exports['test implement factory without contract'] = function(assert) {
+ let actual = xpcom.Factory.new({
+ component: xpcom.Unknown.extend({
+ get wrappedJSObject() this,
+ })
+ });
+
+ assert.ok(isCIDRegistered(actual.id), 'factory is regiseterd');
+ xpcom.unregister(actual);
+ assert.ok(!isCIDRegistered(actual.id), 'factory is unregistered');
+};
+
+exports['test implement xpcom factory'] = function(assert) {
+ let Component = xpcom.Unknown.extend({
+ interfaces: [ 'nsIObserver' ],
+ get wrappedJSObject() this,
+ observe: function() {}
+ });
+
+ let factory = xpcom.Factory.new({
+ register: false,
+ contract: '@jetpack/test/factory;1',
+ component: Component
+ });
+
+ assert.ok(!isCIDRegistered(factory.id), 'factory is not registered');
+ xpcom.register(factory);
+ assert.ok(isCIDRegistered(factory.id), 'factory is registered');
+
+ let actual = Cc[factory.contract].createInstance(Ci.nsIObserver);
+
+ assert.ok(Component.isPrototypeOf(actual.wrappedJSObject),
+ "createInstance returnes wrapped factory instances");
+
+ assert.notEqual(Cc[factory.contract].createInstance(Ci.nsIObserver),
+ Cc[factory.contract].createInstance(Ci.nsIObserver),
+ "createInstance returns new instance each time");
+};
+
+exports['test implement xpcom service'] = function(assert) {
+ let actual = xpcom.Service.new({
+ contract: '@jetpack/test/service;1',
+ register: false,
+ component: xpcom.Unknown.extend({
+ get wrappedJSObject() this,
+ interfaces: [ 'nsIObserver'],
+ observe: function() {},
+ name: 'my-service'
+ })
+ });
+
+ assert.ok(!isCIDRegistered(actual.id), 'component is not registered');
+ xpcom.register(actual);
+ assert.ok(isCIDRegistered(actual.id), 'service is regiseterd');
+ assert.ok(Cc[actual.contract].getService(Ci.nsIObserver).observe,
+ 'service can be accessed via get service');
+ assert.equal(Cc[actual.contract].getService(Ci.nsIObserver).wrappedJSObject,
+ actual.component,
+ 'wrappedJSObject is an actual component');
+ xpcom.unregister(actual);
+ assert.ok(!isCIDRegistered(actual.id), 'service is unregistered');
+};
+
+
+function testRegister(assert, text) {
+
+ const service = xpcom.Service.new({
+ description: 'test about:boop page',
+ contract: '@mozilla.org/network/protocol/about;1?what=boop',
+ register: false,
+ component: xpcom.Unknown.extend({
+ get wrappedJSObject() this,
+ interfaces: [ 'nsIAboutModule' ],
+ newChannel : function(aURI) {
+ var ios = Cc["@mozilla.org/network/io-service;1"].
+ getService(Ci.nsIIOService);
+
+ var channel = ios.newChannel(
+ "data:text/plain," + text,
+ null,
+ null
+ );
+
+ channel.originalURI = aURI;
+ return channel;
+ },
+ getURIFlags: function(aURI) {
+ return Ci.nsIAboutModule.ALLOW_SCRIPT;
+ }
+ })
+ });
+
+ xpcom.register(service);
+
+ assert.equal(isCIDRegistered(service.id), true);
+
+ var aboutFactory = xpcom.factoryByContract(service.contract);
+ var about = aboutFactory.createInstance(Ci.nsIAboutModule);
+
+ var ios = Cc["@mozilla.org/network/io-service;1"].
+ getService(Ci.nsIIOService);
+ assert.equal(
+ about.getURIFlags(ios.newURI("http://foo.com", null, null)),
+ Ci.nsIAboutModule.ALLOW_SCRIPT
+ );
+
+ var aboutURI = ios.newURI("about:boop", null, null);
+ var channel = ios.newChannelFromURI(aboutURI);
+ var iStream = channel.open();
+ var siStream = Cc['@mozilla.org/scriptableinputstream;1']
+ .createInstance(Ci.nsIScriptableInputStream);
+ siStream.init(iStream);
+ var data = new String();
+ data += siStream.read(-1);
+ siStream.close();
+ iStream.close();
+ assert.equal(data, text);
+
+ xpcom.unregister(service);
+ assert.equal(isCIDRegistered(service.id), false);
+}
+
+exports["test register"] = function(assert) {
+ testRegister(assert, "hai2u");
+};
+
+exports["test re-register"] = function(assert) {
+ testRegister(assert, "hai2u again");
+};
+
+exports["test unload"] = function(assert) {
+ let loader = Loader(module);
+ let sbxpcom = loader.require("xpcom");
+
+ let auto = sbxpcom.Factory.new({
+ contract: "@mozilla.org/test/auto-unload;1",
+ description: "test auto",
+ component: sbxpcom.Unknown.extend({ name: 'auto' })
+ });
+
+ let manual = sbxpcom.Factory.new({
+ contract: "@mozilla.org/test/manual-unload;1",
+ description: "test manual",
+ register: false,
+ unregister: false,
+ component: sbxpcom.Unknown.extend({ name: 'manual' })
+ });
+
+ assert.equal(isCIDRegistered(auto.id), true, 'component registered');
+ assert.equal(isCIDRegistered(manual.id), false, 'component not registered');
+
+ sbxpcom.register(manual)
+ assert.equal(isCIDRegistered(manual.id), true,
+ 'component was automatically registered on first instance');
+ loader.unload();
+
+ assert.equal(isCIDRegistered(auto.id), false,
+ 'component was atumatically unregistered on unload');
+ assert.equal(isCIDRegistered(manual.id), true,
+ 'component was not automatically unregistered on unload');
+ sbxpcom.unregister(manual);
+ assert.equal(isCIDRegistered(manual.id), false,
+ 'component was manually unregistered on unload');
+};
+
+require("test").run(exports)
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/test-xul-app.js b/tools/addon-sdk-1.7/packages/api-utils/tests/test-xul-app.js
new file mode 100644
index 0000000..58aa29e
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/test-xul-app.js
@@ -0,0 +1,45 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var xulApp = require("xul-app");
+
+exports.testXulApp = function(test) {
+ test.assertEqual(typeof(xulApp.ID), "string",
+ "ID is a string");
+ test.assertEqual(typeof(xulApp.name), "string",
+ "name is a string");
+ test.assertEqual(typeof(xulApp.version), "string",
+ "version is a string");
+ test.assertEqual(typeof(xulApp.platformVersion), "string",
+ "platformVersion is a string");
+
+ test.assertRaises(function() { xulApp.is("blargy"); },
+ "Unkown Mozilla Application: blargy",
+ "is() throws error on bad app name");
+ test.assertRaises(function() { xulApp.isOneOf(["blargy"]); },
+ "Unkown Mozilla Application: blargy",
+ "isOneOf() throws error on bad app name");
+
+ function testSupport(name) {
+ var item = xulApp.is(name);
+ test.assert(item === true || item === false,
+ "is('" + name + "') is true or false.");
+ }
+
+ var apps = ["Firefox", "Mozilla", "Sunbird", "SeaMonkey",
+ "Fennec", "Thunderbird"];
+
+ apps.forEach(function(name) { testSupport(name); });
+
+ test.assert(xulApp.isOneOf(apps) == true ||
+ xulApp.isOneOf(apps) == false,
+ "isOneOf() returns true or false.");
+
+ test.assertEqual(xulApp.versionInRange(xulApp.platformVersion, "1.9", "*"),
+ true, "platformVersion in range [1.9, *)");
+ test.assertEqual(xulApp.versionInRange("3.6.4", "3.6.4", "3.6.*"),
+ true, "3.6.4 in [3.6.4, 3.6.*)");
+ test.assertEqual(xulApp.versionInRange("1.9.3", "1.9.2", "1.9.3"),
+ false, "1.9.3 not in [1.9.2, 1.9.3)");
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/traits/assert.js b/tools/addon-sdk-1.7/packages/api-utils/tests/traits/assert.js
new file mode 100644
index 0000000..7c385e4
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/traits/assert.js
@@ -0,0 +1,98 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 BaseAssert = require("test/assert").Assert;
+
+/**
+ * Whether or not given property descriptors are equivalent. They are
+ * equivalent either if both are marked as "conflict" or "required" property
+ * or if all the properties of descriptors are equal.
+ * @param {Object} actual
+ * @param {Object} expected
+ */
+function equivalentDescriptors(actual, expected) {
+ return (actual.conflict && expected.conflict) ||
+ (actual.required && expected.required) ||
+ equalDescriptors(actual, expected);
+}
+
+function equalDescriptors(actual, expected) {
+ return actual.get === expected.get &&
+ actual.set === expected.set &&
+ actual.value === expected.value &&
+ !!actual.enumerable === !!expected.enumerable &&
+ !!actual.configurable === !!expected.configurable &&
+ !!actual.writable === !!expected.writable;
+}
+
+/**
+ * Whether or not given `target` array contains all the element
+ * from a given `source` array.
+ */
+function containsSet(source, target) {
+ return source.some(function(element) {
+ return 0 > target.indexOf(element);
+ });
+}
+
+/**
+ * Whether or not given two arrays contain all elements from another.
+ */
+function equivalentSets(source, target) {
+ return containsSet(source, target) && containsSet(target, source);
+}
+
+/**
+ * Finds name of the property from `source` property descriptor map, that
+ * is not equivalent of the name named property in the `target` property
+ * descriptor map. If not found `null` is returned instead.
+ */
+function findNonEquivalentPropertyName(source, target) {
+ var value = null;
+ Object.getOwnPropertyNames(source).some(function(key) {
+ var areEquivalent = false;
+ if (!equivalentDescriptors(source[key], target[key])) {
+ value = key;
+ areEquivalent = true;
+ }
+ return areEquivalent;
+ });
+ return value;
+}
+
+var AssertDescriptor = {
+ equalTraits: {
+ value: function equivalentTraits(actual, expected, message) {
+ var difference;
+ var actualKeys = Object.getOwnPropertyNames(actual);
+ var expectedKeys = Object.getOwnPropertyNames(expected);
+
+ if (equivalentSets(actualKeys, expectedKeys)) {
+ this.fail({
+ operator: "equalTraits",
+ message: "Traits define different properties",
+ actual: actualKeys.sort().join(","),
+ expected: expectedKeys.sort().join(","),
+ });
+ }
+ else if ((difference = findNonEquivalentPropertyName(actual, expected))) {
+ this.fail({
+ operator: "equalTraits",
+ message: "Traits define non-equivalent property `" + difference + "`",
+ actual: actual[difference],
+ expected: expected[difference]
+ });
+ }
+ else {
+ this.pass(message || "Traits are equivalent.");
+ }
+ }
+ }
+};
+
+exports.Assert = function Assert() {
+ return Object.create(BaseAssert.apply(null, arguments), AssertDescriptor);
+};
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/traits/descriptor-tests.js b/tools/addon-sdk-1.7/packages/api-utils/tests/traits/descriptor-tests.js
new file mode 100644
index 0000000..b775ff4
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/traits/descriptor-tests.js
@@ -0,0 +1,335 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+var Trait = require("light-traits").Trait;
+var utils = require("./utils");
+var Data = utils.Data;
+var Method = utils.Method;
+var Accessor = utils.Accessor;
+var Required = utils.Required;
+var Conflict = utils.Conflict;
+
+function method() {}
+
+exports.Assert = require("./assert").Assert
+exports["test simple composition"] = function(assert) {
+ var actual = Trait.compose(
+ Trait({ a: 0, b: 1 }),
+ { c: { value: 2 }, d: { value: method, enumerable: true } }
+ );
+
+ var expected = {
+ a: Data(0),
+ b: Data(1),
+ c: Data(2, false, false, false),
+ d: Method(method, true, false, false)
+ };
+
+ assert.equalTraits(actual, expected);
+};
+
+exports["test composition with conflict"] = function(assert) {
+ var actual = Trait.compose(
+ Trait({ a: 0, b: 1 }),
+ {
+ a: {
+ value: 2,
+ writable: true,
+ configurable: true,
+ enumerable: true
+ },
+ c: {
+ value: method,
+ configurable: true
+ }
+ }
+ );
+
+ var expected = {
+ a: Conflict("a"),
+ b: Data(1),
+ c: Method(method, false, true, false)
+ };
+
+ assert.equalTraits(actual, expected);
+};
+
+exports["test identical props does not cause conflict"] = function(assert) {
+ var actual = Trait.compose(
+ {
+ a: {
+ value: 0,
+ writable: true,
+ configurable: true,
+ enumerable: true
+ },
+ b: {
+ value: 1
+ }
+ },
+ Trait({
+ a: 0,
+ c: method
+ })
+ );
+
+ var expected = {
+ a: Data(0),
+ b: Data(1, false, false, false),
+ c: Method(method)
+ }
+
+ assert.equalTraits(actual, expected);
+};
+
+exports["test composition with identical required props"] = function(assert) {
+ var actual = Trait.compose(
+ Trait({ a: Trait.required, b: 1 }),
+ { a: { required: true }, c: { value: method } }
+ );
+
+ var expected = {
+ a: Required(),
+ b: Data(1),
+ c: Method(method, false, false, false)
+ };
+
+ assert.equalTraits(actual, expected);
+};
+
+exports["test composition satisfying a required prop"] = function(assert) {
+ var actual = Trait.compose(
+ Trait({ a: Trait.required, b: 1 }),
+ { a: { value: method, enumerable: true } }
+ );
+
+ var expected = {
+ a: Method(method, true, false, false),
+ b: Data(1)
+ };
+
+ assert.equalTraits(actual, expected);
+};
+
+exports["test compose is neutral wrt conflicts"] = function(assert) {
+ var actual = Trait.compose(
+ Trait({ a: { value: 1 } }, Trait({ a: 2 })),
+ { b: { value: 0, writable: true, configurable: true, enumerable: false } }
+ );
+
+ var expected = { a: Conflict("a"), b: Data(0, false) };
+
+ assert.equalTraits(actual, expected);
+};
+
+exports["test conflicting prop overrides Trait.required"] = function(assert) {
+ var actual = Trait.compose(
+ Trait.compose(
+ Trait({ a: 1 }),
+ { a: { value: 2 } }
+ ),
+ { a: { value: Trait.required } }
+ );
+
+ var expected = { a: Conflict("a") };
+
+ assert.equalTraits(actual, expected);
+};
+
+exports["test compose is commutative"] = function(assert) {
+ var actual = Trait.compose(
+ Trait({ a: 0, b: 1 }),
+ { c: { value: 2 }, d: { value: method } }
+ );
+
+ var expected = Trait.compose(
+ { c: { value: 2 }, d: { value: method } },
+ Trait({ a: 0, b: 1 })
+ );
+
+ assert.equalTraits(actual, expected);
+}
+
+exports["test compose is commutative, also for required/conflicting props"] = function(assert) {
+ var actual = Trait.compose(
+ {
+ a: { value: 0 },
+ b: { value: 1 },
+ c: { value: 3 },
+ e: { value: Trait.required }
+ },
+ {
+ c: { value: 2 },
+ d: { get: method }
+ }
+ );
+
+ var expected = Trait.compose(
+ Trait({ c: 3 }),
+ {
+ c: { value: 2 },
+ d: { get: method },
+ a: { value: 0 },
+ b: { value: 1 },
+ e: { value: Trait.required },
+ }
+ );
+
+ assert.equalTraits(actual, expected);
+};
+
+exports["test compose is associative"] = function(assert) {
+ var actual = Trait.compose(
+ {
+ a: { value: 0 },
+ b: { value: 1 },
+ c: { value: 3 },
+ d: { value: Trait.required }
+ },
+ Trait.compose(
+ { c: { value: 3 }, d: { value: Trait.required } },
+ { c: { value: 2 }, d: { value: method }, e: { value: "foo" } }
+ )
+ );
+
+ var expected = Trait.compose(
+ Trait.compose(
+ {
+ a: { value: 0 },
+ b: { value: 1 },
+ c: { value: 3 },
+ d: { value: Trait.required }
+ },
+ {
+ c: { value: 3 },
+ d: { value: Trait.required }
+ }
+ ),
+ {
+ c: { value: 2 },
+ d: { value: method },
+ e: { value: "foo" }
+ }
+ );
+
+ assert.equalTraits(actual, expected);
+};
+
+exports["test diamond import of same prop do not conflict"] = function(assert) {
+ var actual = Trait.compose(
+ Trait.compose(
+ { b: { value: 2 } },
+ { a: { value: 1, enumerable: true, configurable: true, writable: true } }
+ ),
+ Trait.compose(
+ { c: { value: 3 } },
+ Trait({ a: 1 })
+ ),
+ Trait({ d: 4 })
+ );
+
+ var expected = {
+ a: Data(1),
+ b: Data(2, false, false, false),
+ c: Data(3, false, false, false),
+ d: Data(4)
+ };
+
+ assert.equalTraits(actual, expected);
+};
+
+exports["test create simple"] = function(assert) {
+ var o1 = Trait.compose(
+ Trait({ a: 1 }),
+ {
+ b: {
+ value: function() {
+ return this.a;
+ }
+ }
+ }
+ ).create(Object.prototype);
+
+ assert.equal(Object.getPrototypeOf(o1), Object.prototype, "o1 prototype");
+ assert.equal(1, o1.a, "o1.a");
+ assert.equal(1, o1.b(), "o1.b()");
+ assert.equal(Object.keys(o1).length, 1, "Object.keys(o1).length === 2");
+};
+
+exports["test create with Array.prototype"] = function(assert) {
+ var o2 = Trait.compose({}, {}).create(Array.prototype);
+ assert.equal(Object.getPrototypeOf(o2), Array.prototype, "o2 prototype");
+};
+
+exports["test exception for incomplete required properties"] = function(assert) {
+ assert.throws(function() {
+ Trait({ foo: Trait.required }).create(Object.prototype)
+ }, /Missing required property: `foo`/, "required prop error");
+}
+
+exports["test exception for unresolved conflicts"] = function(assert) {
+ assert.throws(function() {
+ Trait(Trait({ a: 0 }), Trait({ a: 1 })).create({})
+ }, /Remaining conflicting property: `a`/, "conflicting prop error");
+}
+
+exports["test conflicting properties are present"] = function(assert) {
+ var o5 = Object.create(Object.prototype, Trait.compose(
+ { a: { value: 0 } },
+ { a: { value: 1 } }
+ ));
+
+ assert.ok("a" in o5, "conflicting property present");
+ assert.throws(function() {
+ o5.a
+ }, /Remaining conflicting property: `a`/, "conflicting prop access error");
+};
+
+exports["test diamond with conflicts"] = function(assert) {
+ function makeT1(x) {
+ return {
+ m: {
+ value: function() {
+ return x
+ }
+ }
+ };
+ };
+
+ function makeT2(x) {
+ return Trait.compose(
+ Trait({ t2: "foo" }),
+ makeT1(x)
+ );
+ };
+
+ function makeT3(x) {
+ return Trait.compose(
+ {
+ t3: { value: "bar" }
+ },
+ makeT1(x)
+ );
+ };
+
+ var T4 = Trait.compose(makeT2(5), makeT3(5));
+
+ assert.throws(function() {
+ T4.create(Object.prototype);
+ }, /Remaining conflicting property: `m`/, "diamond prop conflict");
+};
+
+exports["test providing requirements through proto"] = function(assert) {
+ var t = Trait.compose(
+ {},
+ { required: { required: true } }
+ ).create({ required: "test" });
+
+ assert.equal(t.required, "test", "property from proto is inherited");
+};
+
+if (module == require.main)
+ require("test").run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/traits/inheritance-tests.js b/tools/addon-sdk-1.7/packages/api-utils/tests/traits/inheritance-tests.js
new file mode 100644
index 0000000..3b311f9
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/traits/inheritance-tests.js
@@ -0,0 +1,104 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+var Trait = require("light-traits").Trait;
+
+exports["test custom constructor and inherited toString"] = function(assert) {
+ function Type() {
+ return Object.create(Type.prototype);
+ }
+ Type.prototype = Trait({
+ method: function method() {
+ return 2;
+ }
+ }).create(Object.freeze(Type.prototype));
+
+ var fixture = Type();
+
+ assert.equal(fixture.constructor, Type, "must override constructor");
+ assert.equal(fixture.toString(), "[object Type]", "must inherit toString");
+};
+
+exports["test custom toString and inherited constructor"] = function(assert) {
+ function Type() {
+ return Object.create(Type.prototype);
+ }
+ Type.prototype = Trait({
+ toString: function toString() {
+ return "<toString>";
+ }
+ }).create();
+
+ var fixture = Type();
+
+ assert.equal(fixture.constructor, Trait, "must inherit constructor Trait");
+ assert.equal(fixture.toString(), "<toString>", "Must override toString");
+};
+
+exports["test custom toString and constructor"] = function(assert) {
+ function Type() {
+ return TypeTrait.create(Type.prototype);
+ }
+ Object.freeze(Type.prototype);
+ var TypeTrait = Trait({
+ toString: function toString() {
+ return "<toString>";
+ }
+ });
+
+ var fixture = Type();
+
+ assert.equal(fixture.constructor, Type, "constructor is provided to create");
+ assert.equal(fixture.toString(), "<toString>", "toString was overridden");
+};
+
+exports["test resolve constructor"] = function (assert) {
+ function Type() {}
+ var T1 = Trait({ constructor: Type }).resolve({ constructor: '_foo' });
+ var f1 = T1.create();
+
+ assert.equal(f1._foo, Type, "constructor was resolved");
+ assert.equal(f1.constructor, Trait, "constructor of prototype is inherited");
+ assert.equal(f1.toString(), "[object Trait]", "toString is inherited");
+};
+
+exports["test compose read-only"] = function (assert) {
+ function Type() {}
+ Type.prototype = Trait.compose(Trait({}), {
+ constructor: { value: Type },
+ a: { value: "b", enumerable: true }
+ }).resolve({ a: "b" }).create({ a: "a" });
+
+ var f1 = new Type();
+
+ assert.equal(Object.getPrototypeOf(f1), Type.prototype, "inherits correctly");
+ assert.equal(f1.constructor, Type, "constructor was overridden");
+ assert.equal(f1.toString(), "[object Type]", "toString was inherited");
+ assert.equal(f1.a, "a", "property a was resolved");
+ assert.equal(f1.b, "b", "property a was renamed to b");
+ assert.ok(!Object.getOwnPropertyDescriptor(Type.prototype, "a"),
+ "a is not on the prototype of the instance");
+
+ var proto = Object.getPrototypeOf(Type.prototype);
+ var dc = Object.getOwnPropertyDescriptor(Type.prototype, "constructor");
+ var db = Object.getOwnPropertyDescriptor(Type.prototype, "b");
+ var da = Object.getOwnPropertyDescriptor(proto, "a");
+
+ assert.ok(!dc.writable, "constructor is not writable");
+ assert.ok(!dc.enumerable, "constructor is not enumerable");
+ assert.ok(dc.configurable, "constructor inherits configurability");
+
+ assert.ok(!db.writable, "a -> b is not writable");
+ assert.ok(db.enumerable, "a -> b is enumerable");
+ assert.ok(!db.configurable, "a -> b is not configurable");
+
+ assert.ok(da.writable, "a is writable");
+ assert.ok(da.enumerable, "a is enumerable");
+ assert.ok(da.configurable, "a is configurable");
+};
+
+if (require.main == module)
+ require("test").run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/traits/object-tests.js b/tools/addon-sdk-1.7/packages/api-utils/tests/traits/object-tests.js
new file mode 100644
index 0000000..6a94ff3
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/traits/object-tests.js
@@ -0,0 +1,321 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 Trait = require("light-traits").Trait;
+var utils = require("./utils");
+var Data = utils.Data;
+var Method = utils.Method;
+var Accessor = utils.Accessor;
+var Required = utils.Required;
+var Conflict = utils.Conflict;
+
+function method() {}
+
+exports.Assert = require("./assert").Assert;
+
+exports["test empty trait"] = function (assert) {
+ assert.equalTraits(Trait({}), {});
+};
+
+exports["test simple trait"] = function (assert) {
+ var expected = {
+ a: Data(0, true, true, true),
+ b: Method(method, true, true, true)
+ };
+
+ assert.equalTraits(Trait({ a: 0, b: method }), expected);
+};
+
+exports["test simple trait with Trait.required property"] = function (assert) {
+ var actual = Trait({ a: Trait.required, b: 1 });
+ var expected = { a: Required("a"), b: Data(1) };
+
+ assert.equalTraits(actual, expected);
+};
+
+exports["test ordering of trait properties is irrelevant"] = function (assert) {
+ var actual = Trait({ a: 0, b: 1, c: Trait.required });
+ var expected = Trait({ b: 1, c: Trait.required, a: 0 });
+
+ assert.equalTraits(actual, expected);
+};
+
+exports["test trait with accessor property"] = function (assert) {
+ var record = { get a() {}, set a(v) {} };
+ var get = Object.getOwnPropertyDescriptor(record, "a").get;
+ var set = Object.getOwnPropertyDescriptor(record, "a").set;
+
+ assert.equalTraits(Trait(record), { a: Accessor(get, set) });
+};
+
+exports["test simple composition"] = function (assert) {
+ var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ c: 2, d: method }));
+ var expected = { a: Data(0), b: Data(1), c: Data(2), d: Method(method) };
+
+ assert.equalTraits(actual, expected);
+};
+
+exports["test composition with conflict"] = function (assert) {
+ var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ a: 2, c: method }));
+ var expected = { a: Conflict("a"), b: Data(1), c: Method(method) };
+
+ assert.equalTraits(actual, expected);
+};
+
+exports["test composition of identical props does not cause conflict"] = function (assert) {
+ var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ a: 0, c: method }));
+
+ assert.equalTraits(actual, { a: Data(0), b: Data(1), c: Method(method) });
+};
+
+exports["test composition with identical Trait.required props"] = function (assert) {
+ var actual = Trait.compose(Trait({ a: Trait.required, b: 1 }),
+ Trait({ a: Trait.required, c: method }));
+
+ assert.equalTraits(actual, { a: Required(), b: Data(1), c: Method(method) });
+};
+
+exports["test composition satisfying a Trait.required prop"] = function (assert) {
+ var actual = Trait.compose(Trait({ a: Trait.required, b: 1 }),
+ Trait({ a: method }));
+
+ assert.equalTraits(actual, { a: Method(method), b: Data(1) });
+};
+
+exports["test compose is neutral wrt conflicts"] = function (assert) {
+ var actual = Trait.compose(Trait.compose(Trait({ a: 1 }), Trait({ a: 2 })),
+ Trait({ b: 0 }));
+
+ assert.equalTraits(actual, { a: Conflict("a"), b: Data(0) });
+};
+
+exports["test conflicting prop overrides Trait.required prop"] = function (assert) {
+ var actual = Trait.compose(Trait.compose(Trait({ a: 1 }),
+ Trait({ a: 2 })),
+ Trait({ a: Trait.required }));
+
+ assert.equalTraits(actual, { a: Conflict("a") });
+};
+
+exports["test compose is commutative"] = function (assert) {
+ var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ c: 2, d: method }));
+ var expected = Trait.compose(Trait({ c: 2, d: method }),
+ Trait({ a: 0, b: 1 }));
+
+ assert.equalTraits(actual, expected);
+};
+
+exports["test compose is commutative, also for Trait.required/conflicting props"] = function (assert) {
+ var actual = Trait.compose(Trait({ a: 0, b: 1, c: 3, e: Trait.required }),
+ Trait({ c: 2, d: method }));
+
+ var expected = Trait.compose(Trait({ c: 2, d: method }),
+ Trait({ a: 0, b: 1, c: 3, e: Trait.required }));
+
+ assert.equalTraits(actual, expected);
+};
+
+exports["test compose is associative"] = function (assert) {
+ var actual = Trait.compose(Trait({ a: 0, b: 1, c: 3, d: Trait.required }),
+ Trait.compose(Trait({ c: 3, d: Trait.required }),
+ Trait({ c: 2, d: method,
+ e: "foo" })));
+
+ var expected = Trait.compose(
+ Trait.compose(Trait({ a: 0, b: 1, c: 3, d: Trait.required }),
+ Trait({ c: 3, d: Trait.required })),
+ Trait({ c: 2, d: method, e: "foo" }));
+
+ assert.equalTraits(actual, expected);
+};
+
+exports["test diamond import of same prop does not generate conflict"] = function (assert) {
+ var actual = Trait.compose(Trait.compose(Trait({ b: 2 }), Trait({ a: 1 })),
+ Trait.compose(Trait({ c: 3 }), Trait({ a: 1 })),
+ Trait({ d: 4 }));
+ var expected = { a: Data(1), b: Data(2), c: Data(3), d: Data(4) };
+
+ assert.equalTraits(actual, expected);
+};
+
+exports["test resolve with empty resolutions has no effect"] = function (assert) {
+ assert.equalTraits(Trait({ a: 1, b: Trait.required, c: method }).resolve({}),
+ { a: Data(1), b: Required(), c: Method(method) });
+};
+
+exports["test resolve: renaming"] = function (assert) {
+ var actual = Trait({ a: 1, b: Trait.required, c: method });
+
+ assert.equalTraits(actual.resolve({ a: "A", c: "C" }),
+ { A: Data(1), b: Required(), C: Method(method),
+ a: Required(), c: Required() });
+};
+
+exports["test resolve: renaming to conflicting name causes conflict, order 1"] = function (assert) {
+ assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "b" }),
+ { b: Conflict("b"), a: Required() });
+};
+
+exports["test resolve: renaming to conflicting name causes conflict, order 2"] = function (assert) {
+ assert.equalTraits(Trait({ b: 2, a: 1 }).resolve({ a: "b" }),
+ { b: Conflict("b"), a: Required() });
+};
+
+exports["test resolve: simple exclusion"] = function (assert) {
+ assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: undefined }),
+ { a: Required(), b: Data(2) });
+};
+
+exports["test resolve: exclusion to empty trait"] = function (assert) {
+ assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: null, b: undefined }),
+ { a: Required(), b: Required() });
+};
+
+exports["test resolve: exclusion and renaming of disjoint props"] = function (assert) {
+ assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: undefined, b: "c" }),
+ { a: Required(), c: Data(2), b: Required() });
+};
+
+exports["test resolve: exclusion and renaming of overlapping props"] = function (assert) {
+ assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: undefined, b: "a" }),
+ { a: Data(2), b: Required() });
+};
+
+exports["test resolve: renaming to a common alias causes conflict"] = function (assert) {
+ assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "c", b: "c" }),
+ { c: Conflict("c"), a: Required(), b: Required() });
+};
+
+exports["test resolve: renaming overrides Trait.required target"] = function (assert) {
+ assert.equalTraits(Trait({ a: Trait.required, b: 2 }).resolve({ b: "a" }),
+ { a: Data(2), b: Required() });
+};
+
+exports["test resolve: renaming Trait.required properties has no effect"] = function (assert) {
+ assert.equalTraits(Trait({ a: 2, b: Trait.required }).resolve({ b: "a" }),
+ { a: Data(2), b: Required() });
+};
+
+exports["test resolve: renaming of non-existent props has no effect"] = function (assert) {
+ assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "c", d: "c" }),
+ { c: Data(1), b: Data(2), a: Required() });
+};
+
+exports["test resolve: exclusion of non-existent props has no effect"] = function (assert) {
+ assert.equalTraits(Trait({ a: 1 }).resolve({ b: undefined }), { a: Data(1) });
+};
+
+exports["test resolve is neutral w.r.t. Trait.required properties"] = function (assert) {
+ var actual = Trait({ a: Trait.required, b: Trait.required, c: "foo", d: 1 });
+ var expected = { a: Required(), b: Required(), c: Data("foo"), d: Data(1) };
+ assert.equalTraits(actual.resolve({ a: "c", b: undefined }), expected);
+};
+
+exports["test resolve supports swapping of property names, ordering 1"] = function (assert) {
+ assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "b", b: "a" }),
+ { a: Data(2), b: Data(1) });
+};
+
+exports["test resolve supports swapping of property names, ordering 2"] = function (assert) {
+ assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ b: "a", a: "b" }),
+ { a: Data(2), b: Data(1) });
+};
+
+exports["test resolve supports swapping of property names, ordering 3"] = function (assert) {
+ assert.equalTraits(Trait({ b: 2, a: 1 }).resolve({ b: "a", a: "b" }),
+ { a: Data(2), b: Data(1) });
+};
+
+exports["test resolve supports swapping of property names, ordering 4"] = function (assert) {
+ assert.equalTraits(Trait({ b: 2, a: 1 }).resolve({ a: "b", b: "a" }),
+ { a: Data(2), b: Data(1) });
+};
+
+exports["test create simple"] = function (assert) {
+ var o1 = Trait({
+ a: 1,
+ b: function () {
+ return this.a;
+ }
+ }).create(Object.prototype);
+
+ assert.equal(Object.getPrototypeOf(o1), Object.prototype, "o1 prototype");
+ assert.equal(1, o1.a, "o1.a");
+ assert.equal(1, o1.b(), "o1.b()");
+ assert.equal(Object.keys(o1).length, 2, "Object.keys(o1).length === 2");
+};
+
+exports["test create with Array.prototype"] = function (assert) {
+ var o2 = Trait({}).create(Array.prototype);
+ assert.equal(Object.getPrototypeOf(o2), Array.prototype, "o2 prototype");
+};
+
+exports["test exception for incomplete required properties"] = function (assert) {
+ assert.throws(function () {
+ Trait({ foo: Trait.required }).create(Object.prototype);
+ }, /Missing required property: `foo`/, "required prop error");
+};
+
+exports["test exception for unresolved conflicts"] = function (assert) {
+ assert.throws(function () {
+ Trait.compose(Trait({ a: 0 }), Trait({ a: 1 })).create({});
+ }, /Remaining conflicting property: `a`/, "conflicting prop error");
+};
+
+exports["test verify that required properties are present but undefined"] = function (assert) {
+ var o4 = Object.create(Object.prototype, Trait({ foo: Trait.required }));
+
+ assert.ok("foo" in o4, "required property present");
+ assert.throws(function () {
+ o4.foo;
+ }, /Missing required property: `foo`/, "required prop error");
+};
+
+exports["test verify that conflicting properties are present"] = function (assert) {
+ var o5 = Object.create(Object.prototype, Trait.compose(Trait({ a: 0 }),
+ Trait({ a: 1 })));
+
+ assert.ok("a" in o5, "conflicting property present");
+ assert.throws(function () {
+ o5.a;
+ }, /Remaining conflicting property: `a`/, "conflicting prop access error");
+};
+
+exports["test diamond with conflicts"] = function (assert) {
+ function makeT1(x) {
+ return Trait({
+ m: function () {
+ return x
+ }
+ })
+ };
+
+ function makeT2(x) {
+ return Trait.compose(Trait({
+ t2: "foo"
+ }), makeT1(x));
+ };
+
+ function makeT3(x) {
+ return Trait.compose(Trait({
+ t3: "bar"
+ }), makeT1(x));
+ };
+
+ var T4 = Trait.compose(makeT2(5), makeT3(5));
+
+ assert.throws(function () {
+ T4.create(Object.prototype);
+ }, /Remaining conflicting property: `m`/, "diamond prop conflict");
+};
+
+exports["test providing requirements through proto"] = function (assert) {
+ var t = Trait({ required: Trait.required }).create({ required: "test" });
+ assert.equal(t.required, "test", "property from proto is inherited");
+};
+
+if (module == require.main)
+ require("test").run(exports);
diff --git a/tools/addon-sdk-1.7/packages/api-utils/tests/traits/utils.js b/tools/addon-sdk-1.7/packages/api-utils/tests/traits/utils.js
new file mode 100644
index 0000000..8426af7
--- /dev/null
+++ b/tools/addon-sdk-1.7/packages/api-utils/tests/traits/utils.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/. */
+
+"use strict";
+
+var ERR_CONFLICT = "Remaining conflicting property: ";
+var ERR_REQUIRED = "Missing required property: ";
+
+exports.Data = function Data(value, enumerable, configurable, writable) {
+ return ({
+ value: value,
+ enumerable: enumerable !== false,
+ configurable: configurable !== false,
+ writable: writable !== false
+ });
+};
+
+exports.Method = function Method(method, enumerable, configurable, writable) {
+ return ({
+ value: method,
+ enumerable: enumerable !== false,
+ configurable: configurable !== false,
+ writable: writable !== false
+ });
+};
+
+exports.Accessor = function Accessor(get, set, enumerable, configurable) {
+ return ({
+ get: get,
+ set: set,
+ enumerable: enumerable !== false,
+ configurable: configurable !== false
+ });
+};
+
+exports.Required = function Required(name) {
+ function required() { throw new Error(ERR_REQUIRED + name) }
+
+ return ({
+ get: required,
+ set: required,
+ required: true
+ });
+};
+
+exports.Conflict = function Conflict(name) {
+ function conflict() { throw new Error(ERR_CONFLICT + name) }
+
+ return ({
+ get: conflict,
+ set: conflict,
+ conflict: true
+ });
+};
+