diff --git a/accessible/src/html/HTMLFormControlAccessible.cpp b/accessible/src/html/HTMLFormControlAccessible.cpp index 09691fd2f7e6..f940f5f9fdf3 100644 --- a/accessible/src/html/HTMLFormControlAccessible.cpp +++ b/accessible/src/html/HTMLFormControlAccessible.cpp @@ -172,8 +172,8 @@ HTMLRadioButtonAccessible::GetPositionAndSizeInternal(int32_t* aPosInSet, if (inputElm->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, type, eCaseMatters) && inputElm->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, - name, eCaseMatters)) { - count++; + name, eCaseMatters) && mDoc->HasAccessible(inputElm)) { + count++; if (inputElm == mContent) indexOf = count; } diff --git a/accessible/tests/mochitest/attributes/test_obj_group.html b/accessible/tests/mochitest/attributes/test_obj_group.html index 4b222ab28d17..979605152684 100644 --- a/accessible/tests/mochitest/attributes/test_obj_group.html +++ b/accessible/tests/mochitest/attributes/test_obj_group.html @@ -40,6 +40,10 @@ testGroupAttrs("radio3", 1, 2); testGroupAttrs("radio4", 2, 2); + ////////////////////////////////////////////////////////////////////////// + // Hidden HTML input@type="radio" + testGroupAttrs("radio5", 1, 1); + ////////////////////////////////////////////////////////////////////////// // HTML ul/ol testGroupAttrs("li1", 1, 3); @@ -319,5 +323,9 @@
  • JoeSentMe
  • +
    + + +
    diff --git a/addon-sdk/source/README b/addon-sdk/source/README index aa29f3ef1ce0..26210590d3f3 100644 --- a/addon-sdk/source/README +++ b/addon-sdk/source/README @@ -38,8 +38,8 @@ Bugs * file a bug: https://bugzilla.mozilla.org/enter_bug.cgi?product=Add-on%20SDK + Style Guidelines -------------------- -* https://github.com/mozilla/addon-sdk/wiki/Coding-style-guide - +* https://github.com/mozilla/addon-sdk/wiki/Coding-style-guide diff --git a/addon-sdk/source/app-extension/bootstrap.js b/addon-sdk/source/app-extension/bootstrap.js index 6a6e6230ce95..d5203ff0b198 100644 --- a/addon-sdk/source/app-extension/bootstrap.js +++ b/addon-sdk/source/app-extension/bootstrap.js @@ -55,29 +55,6 @@ function readURI(uri) { return data; } -// Utility function that converts cfx-py generated paths to a -// module ids. -function path2id(path) { - // Strips out `/lib` and `.js` from package/lib/path.js - return path.replace(/([^\/]*)\/lib/, '$1').replace(/.js$/, ''); -} -// Utility function that takes old manifest format and creates a manifest -// in a new format: https://github.com/mozilla/addon-sdk/wiki/JEP-Linker -function manifestV2(manifest) { - return Object.keys(manifest).reduce(function(result, path) { - let entry = manifest[path]; - let id = path2id(path); - let requirements = entry.requirements || {}; - result[id] = { - requirements: Object.keys(requirements).reduce(function(result, path) { - result[path] = path2id(requirements[path].path); - return result; - }, {}) - }; - return result - }, {}); -} - // We don't do anything on install & uninstall yet, but in a future // we should allow add-ons to cleanup after uninstall. function install(data, reason) {} @@ -97,6 +74,15 @@ function startup(data, reasonCode) { let id = options.jetpackID; let name = options.name; + + // Clean the metadata + options.metadata[name]['permissions'] = options.metadata[name]['permissions'] || {}; + + // freeze the permissionss + Object.freeze(options.metadata[name]['permissions']); + // freeze the metadata + Object.freeze(options.metadata[name]); + // Register a new resource 'domain' for this addon which is mapping to // XPI's `resources` folder. // Generate the domain name by using jetpack ID, which is the extension ID @@ -115,19 +101,35 @@ function startup(data, reasonCode) { resourceHandler.setSubstitution(domain, resourcesURI); // Create path to URLs mapping supported by loader. - let paths = Object.keys(options.metadata).reduce(function(result, name) { - result[name + '/'] = prefixURI + name + '/lib/' - result[name + '/tests/'] = prefixURI + name + '/tests/' - return result - }, { + let paths = { // Relative modules resolve to add-on package lib './': prefixURI + name + '/lib/', - 'toolkit/': 'resource://gre/modules/toolkit/', - '': 'resources:///modules/' - }); + './tests/': prefixURI + name + '/tests/', + '': 'resource://gre/modules/commonjs/' + }; + + // Maps addon lib and tests ressource folders for each package + paths = Object.keys(options.metadata).reduce(function(result, name) { + result[name + '/'] = prefixURI + name + '/lib/' + result[name + '/tests/'] = prefixURI + name + '/tests/' + return result; + }, paths); + + // We need to map tests folder when we run sdk tests whose package name + // is stripped + if (name == 'addon-sdk') + paths['tests/'] = prefixURI + name + '/tests/'; + + // Maps sdk module folders to their resource folder + paths['sdk/'] = prefixURI + 'addon-sdk/lib/sdk/'; + paths['toolkit/'] = prefixURI + 'addon-sdk/lib/toolkit/'; + // test.js is usually found in root commonjs or SDK_ROOT/lib/ folder, + // so that it isn't shipped in the xpi. Keep a copy of it in sdk/ folder + // until we no longer support SDK modules in XPI: + paths['test'] = prefixURI + 'addon-sdk/lib/sdk/test.js'; // Make version 2 of the manifest - let manifest = manifestV2(options.manifest); + let manifest = options.manifest; // Import `cuddlefish.js` module using a Sandbox and bootstrap loader. let cuddlefishURI = prefixURI + options.loader; @@ -136,7 +138,7 @@ function startup(data, reasonCode) { // Normalize `options.mainPath` so that it looks like one that will come // in a new version of linker. - let main = path2id(options.mainPath); + let main = options.mainPath; unload = cuddlefish.unload; loader = cuddlefish.Loader({ @@ -180,7 +182,7 @@ function startup(data, reasonCode) { } }); - let module = cuddlefish.Module('addon-sdk/sdk/loader/cuddlefish', cuddlefishURI); + let module = cuddlefish.Module('sdk/loader/cuddlefish', cuddlefishURI); let require = cuddlefish.Require(loader, module); require('sdk/addon/runner').startup(reason, { @@ -189,7 +191,8 @@ function startup(data, reasonCode) { prefsURI: rootURI + 'defaults/preferences/prefs.js' }); } catch (error) { - dump('Bootstrap error: ' + error.message + '\n' + + dump('Bootstrap error: ' + + (error.message ? error.message : String(error)) + '\n' + (error.stack || error.fileName + ': ' + error.lineNumber) + '\n'); throw error; } @@ -236,12 +239,16 @@ function shutdown(data, reasonCode) { if (loader) { unload(loader, reason); unload = null; - // Avoid leaking all modules when something goes wrong with one particular - // module. Do not clean it up immediatly in order to allow executing some - // actions on addon disabling. - // We need to keep a reference to the timer, otherwise it is collected - // and won't ever fire. - nukeTimer = setTimeout(nukeModules, 1000); + + // Don't waste time cleaning up if the application is shutting down + if (reason != "shutdown") { + // Avoid leaking all modules when something goes wrong with one particular + // module. Do not clean it up immediatly in order to allow executing some + // actions on addon disabling. + // We need to keep a reference to the timer, otherwise it is collected + // and won't ever fire. + nukeTimer = setTimeout(nukeModules, 1000); + } } }; diff --git a/addon-sdk/source/app-extension/install.rdf b/addon-sdk/source/app-extension/install.rdf index 7926b836681c..b096e07af1d7 100644 --- a/addon-sdk/source/app-extension/install.rdf +++ b/addon-sdk/source/app-extension/install.rdf @@ -18,7 +18,7 @@ {ec8030f7-c20a-464f-9b0e-13a3a9e97384} 18.0 - 20.* + 21.0a1 diff --git a/addon-sdk/source/data/test-trusted-document.html b/addon-sdk/source/data/test-trusted-document.html index e9f9169066bc..c31e055cbc7a 100644 --- a/addon-sdk/source/data/test-trusted-document.html +++ b/addon-sdk/source/data/test-trusted-document.html @@ -10,9 +10,11 @@

    Lorem ipsum dolor sit amet.

    diff --git a/addon-sdk/source/doc/dev-guide-source/credits.md b/addon-sdk/source/doc/dev-guide-source/credits.md index 20eb016d6a2f..316df27bd910 100644 --- a/addon-sdk/source/doc/dev-guide-source/credits.md +++ b/addon-sdk/source/doc/dev-guide-source/credits.md @@ -11,58 +11,144 @@ We'd like to thank our many Jetpack project contributors! They include: * arky * [Heather Arthur](https://github.com/harthur) * Dietrich Ayala + + + * [Romain B](https://github.com/Niamor) +* [Louis-Rémi Babé](https://github.com/louisremi) * Will Bamberg +* Thomas Bassetto +* Tomaz Bevec * Zbigniew Braniecki * Daniel Buchner * James Burke + + + * [Shane Caraveo](https://github.com/mixedpuppy) * [Matěj Cepl](https://github.com/mcepl) +* Marc Chevrier * Hernán Rodriguez Colmeiro * [David Creswick](https://github.com/dcrewi) + + + * dexter +* Christopher Dorn +* Connor Dunn +* dynamis + + + * [Matteo Ferretti (ZER0)](https://github.com/ZER0) * fuzzykiller + + + * [Marcio Galli](https://github.com/taboca) * [Ben Gillbanks](http://www.iconfinder.com/browse/iconset/circular_icons/) * Felipe Gomes * Irakli Gozalishvili * Luca Greco +* Jeff Griffiths +* [David Guo](https://github.com/dglol) + + + * Mark Hammond +* Mark A. Hershberger * Lloyd Hilaiel * Bobby Holley + + + +* Shun Ikejima + + + * Eric H. Jung + + + * Hrishikesh Kale * Wes Kocher +* Lajos Koszti + + + * Edward Lee +* Gregg Lind + + + +* [Nils Maier](https://github.com/nmaier) +* Gervase Markham +* Dave Mason * Myk Melez * Zandr Milewski * Noelle Murata + + + +* Siavash Askari Nasr * Joe R. Nassimian ([placidrage](https://github.com/placidrage)) +* Dương H. Nguyễn * Nick Nguyen + + + * [ongaeshi](https://github.com/ongaeshi) * Paul O’Shannessy -* l.m.orchard +* Les Orchard + + + +* Robert Pankowecki * Alexandre Poirot * Nickolay Ponomarev + + + * Aza Raskin + + + * Till Schneidereit * Justin Scott * Ayan Shah * [skratchdot](https://github.com/skratchdot) +* Henrik Skupin +* slash +* Markus Stange +* Dan Stevens * [Mihai Sucan](https://github.com/mihaisucan) + + + +* taku0 * Clint Talbert -* Thomas +* Tim Taubert +* Shane Tomlinson * Dave Townsend +* [Matthias Tylkowski](https://github.com/tylkomat) + + + * Peter Van der Beken +* Sander van Veen * Atul Varma * [Erik Vold](https://github.com/erikvold) * Vladimir Vukicevic + + + * Brian Warner * [Henri Wiechers](https://github.com/hwiechers) * Drew Willcoxon +* Blake Winton +* Michal Wojciechowski + + + * Piotr Zalewa -* [David Guo](https://github.com/dglol) -* [Nils Maier](https://github.com/nmaier) -* [Louis-Rémi Babé](https://github.com/louisremi) -* [Matthias Tylkowski](https://github.com/tylkomat) +* Brett Zamir diff --git a/addon-sdk/source/doc/dev-guide-source/guides/content-scripts/using-port.md b/addon-sdk/source/doc/dev-guide-source/guides/content-scripts/using-port.md index 6e184def1901..3f91cb7bffed 100644 --- a/addon-sdk/source/doc/dev-guide-source/guides/content-scripts/using-port.md +++ b/addon-sdk/source/doc/dev-guide-source/guides/content-scripts/using-port.md @@ -6,23 +6,18 @@ # Communicating using "port" # To enable add-on scripts and content scripts to communicate with each other, -each end of the conversation has access to a `port` object which defines two -functions: +each end of the conversation has access to a `port` object. -**`emit()`** is used to emit an event. It may be called with any number of -parameters, but is most likely to be called with a name for the event and -an optional payload. The payload can be any value that is -serializable to JSON +* to send messages from one side to the other, use `port.emit()` +* to receive messages sent from the other side, use `port.on()` - port.emit("myEvent", myEventPayload); + -**`on()`** takes two parameters: the name of the event and a function to handle it: +Messages are asynchronous: that is, the sender does not wait for a reply from +the recipient but just emits the message and continues processing. - port.on("myEvent", function handleMyEvent(myEventPayload) { - // Handle the event - }); - -Here's simple add-on that sends a message to a content script using `port`: +Here's a simple add-on that sends a message to a content script using `port`: var tabs = require("sdk/tabs"); @@ -39,45 +34,49 @@ Here's simple add-on that sends a message to a content script using `port`: tabs.open("http://www.mozilla.org"); -We could depict the interface between add-on code and content script code like -this: +In total, the `port` object defines four functions: - +* [`emit()`](dev-guide/guides/content-scripts/using-port.html#port.emit()): +emit a message. +* [`on()`](dev-guide/guides/content-scripts/using-port.html#port.on()): +listen to a message. +* [`removeListener()`](dev-guide/guides/content-scripts/using-port.html#port.removeListener()): +stop listening to a message. +* [`once()`](dev-guide/guides/content-scripts/using-port.html#port.once()): +listen to only the first occurrence of a message. -Events are asynchronous: that is, the sender does not wait for a reply from -the recipient but just emits the event and continues processing. +## Accessing `port` ## -## Accessing `port` in the Content Script ## +### Accessing `port` in the Content Script ### Note that the global `self` object is completely different from the [`self` module](modules/sdk/self.html), which provides an API for an add-on to access its data files and ID. In the content script the `port` object is available as a property of the -global `self` object. Thus, to emit an event from a content script: +global `self` object. Thus, to emit a message from a content script: - self.port.emit("myContentScriptEvent", myContentScriptEventPayload); + self.port.emit("myContentScriptMessage", myContentScriptMessagePayload); -To receive an event from the add-on code: +To receive a message from the add-on code: - self.port.on("myAddonEvent", function(myAddonEventPayload) { - // Handle the event + self.port.on("myAddonMessage", function(myAddonMessagePayload) { + // Handle the message }); -Compare this to the technique used to receive _built-in_ events in the -content script. For example, to receive the `context` event in a content script +Compare this to the technique used to receive _built-in_ messages in the +content script. For example, to receive the `context` message in a content script associated with a [context menu](modules/sdk/context-menu.html) object, you would call the `on` function attached to the global `self` object: self.on("context", function() { - // Handle the event + // Handle the message }); So the `port` property is essentially used here as a namespace for -user-defined events. +user-defined messages. -## Accessing `port` in the Add-on Script ## +### Accessing `port` in the Add-on Script ### In the add-on code, the channel of communication between the add-on and a particular content script context is encapsulated by the `worker` object. Thus @@ -86,7 +85,7 @@ corresponding `worker` object. However, the worker is not exposed to add-on code in quite the same way in all modules. The `panel` and `page-worker` objects integrate the -worker API directly. So to receive events from a content script associated +worker API directly. So to receive messages from a content script associated with a panel you use `panel.port.on()`: var panel = require("sdk/panel").Panel({ @@ -99,7 +98,7 @@ with a panel you use `panel.port.on()`: panel.show(); -Conversely, to emit user-defined events from your add-on you can just call +Conversely, to emit user-defined messages from your add-on you can just call `panel.port.emit()`: var panel = require("sdk/panel").Panel({ @@ -121,8 +120,8 @@ So `page-mod` does not integrate the worker API directly: instead, each time a content script is attached to a page, the worker associated with the page is supplied to the page-mod in its `onAttach` function. By supplying a target for this function in the page-mod's constructor you can register to receive -events from the content script, and take a reference to the worker so as to -emit events to it. +messages from the content script, and take a reference to the worker so as to +emit messages to the content script. var pageModScript = "window.addEventListener('click', function(event) {" + " self.port.emit('click', event.target.toString());" + @@ -143,18 +142,126 @@ emit events to it. } }); -In the add-on above there are two user-defined events: +In the add-on above there are two user-defined messages: * `click` is sent from the page-mod to the add-on, when the user clicks an element in the page * `warning` sends a silly string back to the page-mod +## port.emit() ## + +The `port.emit()` function sends a message from the "main.js", or another +add-on module, to a content script, or vice versa. + +It may be called with any number of parameters, but is most likely to be +called with a name for the message and an optional payload. +The payload can be any value that is +serializable to JSON. + +From the content script to the main add-on code: + + var myMessagePayload = "some data"; + self.port.emit("myMessage", myMessagePayload); + +From the main add-on code (in this case a panel instance) +to the content script: + + var myMessagePayload = "some data"; + panel.port.emit("myMessage", myMessagePayload); + +## port.on() ## + +The `port.on()` function registers a function as a listener for a specific +named message sent from the other side using `port.emit()`. + +It takes two parameters: the name of the message and a function to handle it. + +In a content script, to listen for "myMessage" sent from the main +add-on code: + + self.port.on("myMessage", function handleMyMessage(myMessagePayload) { + // Handle the message + }); + +In the main add-on code (in this case a panel instance), to listen for +"myMessage" sent from a a content script: + + panel.port.on("myMessage", function handleMyMessage(myMessagePayload) { + // Handle the message + }); + +## port.removeListener() ## + +You can uses `port.on()` to listen for messages. To stop listening for a +particular message, use `port.removeListener()`. This takes the +same two parameters as `port.on()`: the name of the message, and the name +of the listener function. + +For example, here's an add-on that creates a page-worker and a widget. +The page-worker loads +[http://en.wikipedia.org/wiki/Chalk](http://en.wikipedia.org/wiki/Chalk) +alongside a content script "listener.js". The widget sends the content script +a message called "get-first-para" when it is clicked: + + pageWorker = require("sdk/page-worker").Page({ + contentScriptFile: require("sdk/self").data.url("listener.js"), + contentURL: "http://en.wikipedia.org/wiki/Chalk" + }); + + require("sdk/widget").Widget({ + id: "mozilla-icon", + label: "My Mozilla Widget", + contentURL: "http://www.mozilla.org/favicon.ico", + onClick: function() { + console.log("sending 'get-first-para'"); + pageWorker.port.emit("get-first-para"); + } + }); + +The content script "listener.js" listens for "get-first-para". When it +receives this message, the script logs the first paragraph of the document +and then calls `removeListener()` to stop listening. + + function getFirstParagraph() { + var paras = document.getElementsByTagName('p'); + console.log(paras[0].textContent); + self.port.removeListener("get-first-para", getFirstParagraph); + } + + self.port.on("get-first-para", getFirstParagraph); + +The result is that the paragraph is only logged the first time the widget +is clicked. + +Due to [bug 816272](https://bugzilla.mozilla.org/show_bug.cgi?id=816272) +the [`page-mod`](modules/sdk/page-mod.html)'s `removeListener()` function +does not prevent the listener from receiving messages that are already queued. +This means that if "main.js" sends the message twice in successive lines, and +the listener stops listening as soon as it receives the first message, then +the listener will still receive the second message. + +## port.once() ## + +Often you'll want to receive a message just once, then stop listening. The +`port` object offers a shortcut to do this: the `once()` method. + +This example rewrites the "listener.js" content script in the +[`port.removeListener()` example](dev-guide/guides/content-scripts/using-port.html#port.removeListener()) +so that it uses `once()`: + + function getFirstParagraph() { + var paras = document.getElementsByTagName('p'); + console.log(paras[0].textContent); + } + + self.port.once("get-first-para", getFirstParagraph); + ## JSON-Serializable Values ## -The payload for an event can be any JSON-serializable value. When events are -sent their payloads are automatically serialized, and when events are received -their payloads are automatically deserialized, so you don't need to worry -about serialization. +The payload for an message can be any JSON-serializable value. When messages +are sent their payloads are automatically serialized, and when messages are +received their payloads are automatically deserialized, so you don't need to +worry about serialization. However, you _do_ have to ensure that the payload can be serialized to JSON. This means that it needs to be a string, number, boolean, null, array of diff --git a/addon-sdk/source/doc/dev-guide-source/guides/stability.md b/addon-sdk/source/doc/dev-guide-source/guides/stability.md index 6dde06c4946a..eedbb8314448 100644 --- a/addon-sdk/source/doc/dev-guide-source/guides/stability.md +++ b/addon-sdk/source/doc/dev-guide-source/guides/stability.md @@ -24,7 +24,7 @@ their code. The stability index is adopted from [node.js](http://nodejs.org/api/documentation.html#documentation_stability_index). -The SDK uses only three of the six values defined by node.js: +The SDK uses only four of the six values defined by node.js: @@ -33,6 +33,14 @@ The SDK uses only three of the six values defined by node.js: You can try it out and provide feedback, but we may change or remove it in future versions without having to pass through a formal deprecation process. + + + + diff --git a/addon-sdk/source/doc/dev-guide-source/package-spec.md b/addon-sdk/source/doc/dev-guide-source/package-spec.md index ab6011fe275f..039578ef5e0d 100644 --- a/addon-sdk/source/doc/dev-guide-source/package-spec.md +++ b/addon-sdk/source/doc/dev-guide-source/package-spec.md @@ -93,7 +93,11 @@ called `package.json`. This file is also referred to as the `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`, where `x` represents a single hexadecimal digit. It is used as a `classID` (CID) of the "harness service" XPCOM component. Defaults to a random GUID generated by `cfx`. - + +* `permissions` - a set of permissions that the add-on needs. + * `private-browsing` - A Boolean indicating whether or not the + package supports private browsing. If this value is not `true` + then the package will not see private windows. ## Documentation ## diff --git a/addon-sdk/source/doc/dev-guide-source/tutorials/mobile.md b/addon-sdk/source/doc/dev-guide-source/tutorials/mobile.md index 806d963025a0..3b8ac85cc43b 100644 --- a/addon-sdk/source/doc/dev-guide-source/tutorials/mobile.md +++ b/addon-sdk/source/doc/dev-guide-source/tutorials/mobile.md @@ -240,111 +240,96 @@ rm my-addon.xpi ## Module Compatibility -Modules not yet supported in Firefox Mobile are highlighted in the tables below. +Modules not yet supported in Firefox Mobile are +**marked** in the tables below. ### High-Level APIs ### - + - [**addon-page**](modules/sdk/addon-page.html) + - [base64](modules/sdk/clipboard.html) + - [**clipboard**](modules/sdk/clipboard.html) + - [**context-menu**](modules/sdk/context-menu.html) + - [hotkeys](modules/sdk/hotkeys.html) + - [indexed-db](modules/sdk/indexed-db.html) + - [l10n](modules/sdk/l10n.html) + - [notifications](modules/sdk/notifications.html) + - [page-mod](modules/sdk/notifications.html) + - [page-worker](modules/sdk/page-worker.html) + - [**panel**](modules/sdk/panel.html) + - [passwords](modules/sdk/passwords.html) + - [**private-browsing**](modules/sdk/private-browsing.html) + - [querystring](modules/sdk/querystring.html) + - [request](modules/sdk/request.html) + - [**selection**](modules/sdk/selection.html) + - [self](modules/sdk/self.html) + - [simple-prefs](modules/sdk/simple-prefs.html) + - [simple-storage](modules/sdk/simple-storage.html) + - [system](modules/sdk/system.html) + - [tabs](modules/sdk/tabs.html) + - [timers](modules/sdk/timers.html) + - [url](modules/sdk/url.html) + - [**widget**](modules/sdk/widget.html) + - [windows](modules/sdk/windows.html) ### Low-Level APIs ### - + - [/loader](modules/toolkit/loader.html) + - [chrome](dev-guide/tutorials/chrome.html) + - [console/plain-text](modules/sdk/console/plain-text.html) + - [console/traceback](modules/sdk/console/traceback.html) + - [**content/content**](modules/sdk/content/content.html) + - [content/loader](modules/sdk/content/loader.html) + - [**content/symbiont**](modules/sdk/content/symbiont.html) + - [**content/worker**](modules/sdk/content/worker.html) + - core/disposable + - [core/heritage](modules/sdk/core/heritage.html) + - [core/namespace](modules/sdk/core/namespace.html) + - [core/promise](modules/sdk/core/promise.html) + - [deprecated/api-utils](modules/sdk/deprecated/api-utils.html) + - [deprecated/app-strings](modules/sdk/deprecated/app-strings.html) + - [deprecated/cortex](modules/sdk/deprecated/cortex.html) + - [deprecated/errors](modules/sdk/deprecated/errors.html) + - [deprecated/events](modules/sdk/deprecated/events.html) + - [deprecated/light-traits](modules/sdk/deprecated/light-traits.html) + - deprecated/list + - [deprecated/observer-service](modules/sdk/deprecated/observer-service.html) + - [**deprecated/tab-browser**](modules/sdk/deprecated/tab-browser.html) + - [deprecated/traits](modules/sdk/deprecated/traits.html) + - [**deprecated/window-utils**](modules/sdk/deprecated/window-utils.html) + - dom/events + - [event/core](modules/sdk/event/core.html) + - [event/target](modules/sdk/event/target.html) + - [frame/hidden-frame](modules/sdk/frame/hidden-frame.html) + - [frame/utils](modules/sdk/frame/utils.html) + - [io/byte-streams](modules/sdk/io/byte-streams.html) + - [io/file](modules/sdk/io/file.html) + - [io/text-streams](modules/sdk/io/text-streams.html) + - keyboard/observer + - keyboard/utils + - lang/functional + - lang/type + - [loader/cuddlefish](modules/sdk/loader/cuddlefish.html) + - [loader/sandbox](modules/sdk/loader/sandbox.html) + - [net/url](modules/sdk/net/url.html) + - [net/xhr](modules/sdk/net/xhr.html) + - [page-mod/match-pattern](modules/sdk/page-mod/match-pattern.html) + - [platform/xpcom](modules/sdk/platform/xpcom.html) + - [preferences/service](modules/sdk/preferences/service.html) + - [system/environment](modules/sdk/system/environment.html) + - [system/events](modules/sdk/system/events.html) + - system/globals + - [system/runtime](modules/sdk/system/runtime.html) + - [system/unload](modules/sdk/system/unload.html) + - [system/xul-app](modules/sdk/system/xul-app.html) + - [test/assert](modules/sdk/test/assert.html) + - [test/harness](modules/sdk/test/harness.html) + - [test/httpd](modules/sdk/test/httpd.html) + - [test/runner](modules/sdk/test/runner.html) + - test/tmp-file + - util/array + - [util/collection](modules/sdk/util/collection.html) + - [util/deprecate](modules/sdk/util/deprecate.html) + - [util/list](modules/sdk/util/list.html) + - util/registry + - [util/uuid](modules/sdk/util/uuid.html) + - [window/utils](modules/sdk/window/utils.html) diff --git a/addon-sdk/source/doc/module-source/sdk/context-menu.md b/addon-sdk/source/doc/module-source/sdk/context-menu.md index 650ff66e46af..0d9e9daed18b 100644 --- a/addon-sdk/source/doc/module-source/sdk/context-menu.md +++ b/addon-sdk/source/doc/module-source/sdk/context-menu.md @@ -12,23 +12,23 @@ Introduction ------------ The `context-menu` API provides a simple, declarative way to add items to the -page's context menu. You can add items that perform an action when clicked, +page's context menu. You can add items that perform an action when clicked, submenus, and menu separators. Instead of manually adding items when particular contexts occur and then removing them when those contexts go away, you *bind* items to contexts, and the -adding and removing is automatically handled for you. Items are bound to -contexts in much the same way that event listeners are bound to events. When +adding and removing is automatically handled for you. Items are bound to +contexts in much the same way that event listeners are bound to events. When the user invokes the context menu, all of the items bound to the current context -are automatically added to the menu. If no items are bound, none are added. +are automatically added to the menu. If no items are bound, none are added. Likewise, any items that were previously in the menu but are not bound to the -current context are automatically removed from the menu. You never need to +current context are automatically removed from the menu. You never need to manually remove your items from the menu unless you want them to never appear again. For example, if your add-on needs to add a context menu item whenever the user visits a certain page, don't create the item when that page loads, and -don't remove it when the page unloads. Rather, create your item only once and +don't remove it when the page unloads. Rather, create your item only once and supply a context that matches the target URL. Context menu items are displayed in the order created or in the case of sub @@ -43,27 +43,27 @@ Specifying Contexts ------------------- As its name implies, the context menu should be reserved for the occurrence of -specific contexts. Contexts can be related to page content or the page itself, +specific contexts. Contexts can be related to page content or the page itself, but they should never be external to the page. For example, a good use of the menu would be to show an "Edit Image" item when -the user right-clicks an image in the page. A bad use would be to show a +the user right-clicks an image in the page. A bad use would be to show a submenu that listed all the user's tabs, since tabs aren't related to the page or the node the user clicked to open the menu. ### The Page Context -First of all, you may not need to specify a context at all. When a top-level -item does not specify a context, the page context applies. An item that is in a +First of all, you may not need to specify a context at all. When a top-level +item does not specify a context, the page context applies. An item that is in a submenu is visible unless you specify a context. The *page context* occurs when the user invokes the context menu on a -non-interactive portion of the page. Try right-clicking a blank spot in this -page, or on text. Make sure that no text is selected. The menu that appears -should contain the items "Back", "Forward", "Reload", "Stop", and so on. This +non-interactive portion of the page. Try right-clicking a blank spot in this +page, or on text. Make sure that no text is selected. The menu that appears +should contain the items "Back", "Forward", "Reload", "Stop", and so on. This is the page context. -The page context is appropriate when your item acts on the page as a whole. It +The page context is appropriate when your item acts on the page as a whole. It does not occur when the user invokes the context menu on a link, image, or other non-text node, or while a selection exists. @@ -79,7 +79,7 @@ like this: context: cm.URLContext("*.mozilla.org") }); -These contexts may be specified by calling the following constructors. Each is +These contexts may be specified by calling the following constructors. Each is exported by the `context-menu` module.
    UnstableThe API is in the process of settling, but has not yet had sufficient +real-world testing to be considered stable. +Backwards-compatibility will be maintained if reasonable. +If we do have to make backwards-incompatible changes, we will not guarantee +to go through the formal deprecation process.
    Stable
    @@ -121,10 +121,10 @@ exported by the `context-menu` module.
    This context occurs when the menu is invoked on pages with particular - URLs. matchPattern is a match pattern string or an array of - match pattern strings. When matchPattern is an array, the + URLs. matchPattern is a match pattern string or an array of + match pattern strings. When matchPattern is an array, the context occurs when the menu is invoked on a page whose URL matches any of - the patterns. These are the same match pattern strings that you use with + the patterns. These are the same match pattern strings that you use with the page-mod include property. Read more about patterns. @@ -135,14 +135,14 @@ exported by the `context-menu` module. array - An array of any of the other types. This context occurs when all contexts + An array of any of the other types. This context occurs when all contexts in the array occur.
    Menu items also have a `context` property that can be used to add and remove -declarative contexts after construction. For example: +declarative contexts after construction. For example: var context = require("sdk/context-menu").SelectorContext("img"); myMenuItem.context.add(context); @@ -153,19 +153,19 @@ all of those contexts occur. ### In Content Scripts -The declarative contexts are handy but not very powerful. For instance, you +The declarative contexts are handy but not very powerful. For instance, you might want your menu item to appear for any page that has at least one image, but declarative contexts won't help you there. -When you need more control control over the context in which your menu items are -shown, you can use content scripts. Like other APIs in the SDK, the +When you need more control over the context in which your menu items are +shown, you can use content scripts. Like other APIs in the SDK, the `context-menu` API uses [content scripts](dev-guide/guides/content-scripts/index.html) to let your -add-on interact with pages in the browser. Each menu item you create in the +add-on interact with pages in the browser. Each menu item you create in the top-level context menu can have a content script. A special event named `"context"` is emitted in your content scripts whenever -the context menu is about to be shown. If you register a listener function for +the context menu is about to be shown. If you register a listener function for this event and it returns true, the menu item associated with the listener's content script is shown in the menu. @@ -179,16 +179,18 @@ that contains at least one image: '});' }); -Note that the listener function has a parameter called `node`. This is the node -in the page that the user context-clicked to invoke the menu. You can use it to +Note that the listener function has a parameter called `node`. This is the node +in the page that the user context-clicked to invoke the menu. You can use it to determine whether your item should be shown. You can both specify declarative contexts and listen for contexts in a content -script. In that case, the declarative contexts are evaluated first. If they -are not current, then your context listener is never called. +script. In that case, the declarative contexts are evaluated first, and your +item is shown only when all declarative contexts are current and your +context listener returns true. -This example takes advantage of that fact. The listener can be assured that -`node` will always be an image: +If any declarative contexts are not current, then your context listener +is never called. This example takes advantage of that fact. The listener +can be assured that `node` will always be an image: var cm = require("sdk/context-menu"); cm.Item({ @@ -199,8 +201,26 @@ This example takes advantage of that fact. The listener can be assured that '});' }); -Your item is shown only when all declarative contexts are current and your -context listener returns true. +However, if you do combine `SelectorContext` and the `"context"` event, +be aware that the `node` argument passed to the `"context"` event will +not always match the type specified in `SelectorContext`. + +`SelectorContext` will match if the menu is invoked on the node specified +*or any descendant of that node*, but the `"context"` event handler is +passed *the actual node* on which the menu was invoked. The example above +works because `` elements can't contain other elements, but in the +example below, `node.nodeName` is not guaranteed to be "P" - for example, +it won't be "P" if the user context-clicked a link inside a paragraph: + + var cm = require("sdk/context-menu"); + cm.Item({ + label: "A Paragraph", + context: cm.SelectorContext("p"), + contentScript: 'self.on("context", function (node) {' + + ' console.log(node.nodeName);' + + ' return true;' + + '});' + }); The content script is executed for every page that a context menu is shown for. It will be executed the first time it is needed (i.e. when the context menu is @@ -212,7 +232,7 @@ Handling Menu Item Clicks ------------------------- In addition to using content scripts to listen for the `"context"` event as -described above, you can use content scripts to handle item clicks. When the +described above, you can use content scripts to handle item clicks. When the user clicks your menu item, an event named `"click"` is emitted in the item's content script. @@ -226,10 +246,68 @@ item's content script like so: '});' }); -Note that the listener function has parameters called `node` and `data`. `node` -is the node that the user context-clicked to invoke the menu. You can use it -when performing some action. `data` is the `data` property of the menu item -that was clicked. Note that when you have a hierarchy of menu items the click +Note that the listener function has parameters called `node` and `data`. + +### The "node" Argument ### + +`node` is the node that the user context-clicked to invoke the menu. + +* If you did not use `SelectorContext` to decide whether to show the menu item, +then this is the actual node clicked. +* If you did use `SelectorContext`, then this is the node that matched your +selector. + +For example, suppose your add-on looks like this: + + var script = "self.on('click', function (node, data) {" + + " console.log('clicked: ' + node.nodeName);" + + "});"; + + var cm = require("sdk/context-menu"); + + cm.Item({ + label: "body context", + context: cm.SelectorContext("body"), + contentScript: script + }); + +This add-on creates a context-menu item that uses `SelectorContext` to display +the item whenever the context menu is activated on any descendant of the +`` element. When clicked, the item just logs the +[`nodeName`](https://developer.mozilla.org/en-US/docs/DOM/Node.nodeName) +property for the node passed to the click handler. + +If you run this add-on you'll see that it always logs "BODY", even if you +click on a paragraph element inside the page: + +
    +info: contextmenu-example: clicked: BODY
    +
    + +By contrast, this add-on uses the `PageContext`: + + var script = "self.on('click', function (node, data) {" + + " console.log('clicked: ' + node.nodeName);" + + "});"; + + var cm = require("sdk/context-menu"); + + cm.Item({ + label: "body context", + context: cm.PageContext(), + contentScript: script + }); + +It will log the name of the actual node clicked: + +
    +info: contextmenu-example: clicked: P
    +
    + +### The "data" Argument ### + +`data` is the `data` property of the menu item +that was clicked. Note that when you have a hierarchy of menu items the click event will be sent to the content script of the item clicked and all ancestors so be sure to verify that the `data` value passed matches the item you expect. You can use this to simplify click handling by providing just a single click @@ -248,11 +326,13 @@ listener on a `Menu` that reacts to clicks for any child items.: ] }); +### Communicating With the Add-on ### + Often you will need to collect some kind of information in the click listener -and perform an action unrelated to content. To communicate to the menu item +and perform an action unrelated to content. To communicate to the menu item associated with the content script, the content script can call the `postMessage` function attached to the global `self` object, passing it some -JSON-able data. The menu item's `"message"` event listener will be called with +JSON-able data. The menu item's `"message"` event listener will be called with that data. var cm = require("sdk/context-menu"); @@ -274,7 +354,7 @@ Updating a Menu Item's Label Each menu item must be created with a label, but you can change its label later using a couple of methods. -The simplest method is to set the menu item's `label` property. This example +The simplest method is to set the menu item's `label` property. This example updates the item's label based on the number of times it's been clicked: var numClicks = 0; @@ -288,13 +368,13 @@ updates the item's label based on the number of times it's been clicked: } }); -Sometimes you might want to update the label based on the context. For +Sometimes you might want to update the label based on the context. For instance, if your item performs a search with the user's selected text, it would -be nice to display the text in the item to provide feedback to the user. In -these cases you can use the second method. Recall that your content scripts can +be nice to display the text in the item to provide feedback to the user. In +these cases you can use the second method. Recall that your content scripts can listen for the `"context"` event and if your listeners return true, the items -associated with the content scripts are shown in the menu. In addition to -returning true, your `"context"` listeners can also return strings. When a +associated with the content scripts are shown in the menu. In addition to +returning true, your `"context"` listeners can also return strings. When a `"context"` listener returns a string, it becomes the item's new label. This item implements the aforementioned search example: @@ -312,7 +392,7 @@ This item implements the aforementioned search example: }); The `"context"` listener gets the window's current selection, truncating it if -it's too long, and includes it in the returned string. When the item is shown, +it's too long, and includes it in the returned string. When the item is shown, its label will be "Search Google for `text`", where `text` is the truncated selection. @@ -321,9 +401,9 @@ More Examples ------------- For conciseness, these examples create their content scripts as strings and use -the `contentScript` property. In your own add-on, you will probably want to +the `contentScript` property. In your own add-on, you will probably want to create your content scripts in separate files and pass their URLs using the -`contentScriptFile` property. See +`contentScriptFile` property. See [Working with Content Scripts](dev-guide/guides/content-scripts/index.html) for more information. @@ -436,14 +516,14 @@ A labeled menu item that can perform an action when clicked. @param options {object} An object with the following keys: @prop label {string} - The item's label. It must either be a string or an object that implements + The item's label. It must either be a string or an object that implements `toString()`. @prop [image] {string} - The item's icon, a string URL. The URL can be remote, a reference to an + The item's icon, a string URL. The URL can be remote, a reference to an image in the add-on's `data` directory, or a data URI. @prop [data] {string} - An optional arbitrary value to associate with the item. It must be either a - string or an object that implements `toString()`. It will be passed to + An optional arbitrary value to associate with the item. It must be either a + string or an object that implements `toString()`. It will be passed to click listeners. @prop [context] {value} If the item is contained in the top-level context menu, this declaratively @@ -459,43 +539,43 @@ A labeled menu item that can perform an action when clicked. use to interact with the page. @prop [onMessage] {function} If the item is contained in the top-level context menu, this function will - be called when the content script calls `self.postMessage`. It will be + be called when the content script calls `self.postMessage`. It will be passed the data that was passed to `postMessage`. @property {string} - The menu item's label. You can set this after creating the item to update its + The menu item's label. You can set this after creating the item to update its label later. @property {string} - The item's icon, a string URL. The URL can be remote, a reference to an image - in the add-on's `data` directory, or a data URI. You can set this after - creating the item to update its image later. To remove the item's image, set + The item's icon, a string URL. The URL can be remote, a reference to an image + in the add-on's `data` directory, or a data URI. You can set this after + creating the item to update its image later. To remove the item's image, set it to `null`. @property {string} - An optional arbitrary value to associate with the item. It must be either a - string or an object that implements `toString()`. It will be passed to - click listeners. You can set this after creating the item to update its data + An optional arbitrary value to associate with the item. It must be either a + string or an object that implements `toString()`. It will be passed to + click listeners. You can set this after creating the item to update its data later. @property {list} A list of declarative contexts for which the menu item will appear in the - context menu. Contexts can be added by calling `context.add()` and removed by + context menu. Contexts can be added by calling `context.add()` and removed by called `context.remove()`. @property {Menu} The item's parent `Menu`, or `null` if the item is contained in the top-level - context menu. This property is read-only. To add the item to a new menu, + context menu. This property is read-only. To add the item to a new menu, call that menu's `addItem()` method. @@ -514,7 +594,7 @@ A labeled menu item that can perform an action when clicked. @method Permanently removes the item from its parent menu and frees its resources. - The item must not be used afterward. If you need to remove the item from its + The item must not be used afterward. If you need to remove the item from its parent menu but use it afterward, call `removeItem()` on the parent menu instead. @@ -544,13 +624,13 @@ A labeled menu item that expands into a submenu. @param options {object} An object with the following keys: @prop label {string} - The item's label. It must either be a string or an object that implements + The item's label. It must either be a string or an object that implements `toString()`. @prop items {array} - An array of menu items that the menu will contain. Each must be an `Item`, + An array of menu items that the menu will contain. Each must be an `Item`, `Menu`, or `Separator`. @prop [image] {string} - The menu's icon, a string URL. The URL can be remote, a reference to an + The menu's icon, a string URL. The URL can be remote, a reference to an image in the add-on's `data` directory, or a data URI. @prop [context] {value} If the menu is contained in the top-level context menu, this declaratively @@ -566,43 +646,43 @@ A labeled menu item that expands into a submenu. use to interact with the page. @prop [onMessage] {function} If the menu is contained in the top-level context menu, this function will - be called when the content script calls `self.postMessage`. It will be + be called when the content script calls `self.postMessage`. It will be passed the data that was passed to `postMessage`. @property {string} - The menu's label. You can set this after creating the menu to update its + The menu's label. You can set this after creating the menu to update its label later. @property {array} - An array containing the items in the menu. The array is read-only, meaning - that modifications to it will not affect the menu. However, setting this + An array containing the items in the menu. The array is read-only, meaning + that modifications to it will not affect the menu. However, setting this property to a new array will replace all the items currently in the menu with the items in the new array. @property {string} - The menu's icon, a string URL. The URL can be remote, a reference to an image - in the add-on's `data` directory, or a data URI. You can set this after - creating the menu to update its image later. To remove the menu's image, set + The menu's icon, a string URL. The URL can be remote, a reference to an image + in the add-on's `data` directory, or a data URI. You can set this after + creating the menu to update its image later. To remove the menu's image, set it to `null`. @property {list} A list of declarative contexts for which the menu will appear in the context - menu. Contexts can be added by calling `context.add()` and removed by called + menu. Contexts can be added by calling `context.add()` and removed by called `context.remove()`. @property {Menu} The menu's parent `Menu`, or `null` if the menu is contained in the top-level - context menu. This property is read-only. To add the menu to a new menu, + context menu. This property is read-only. To add the menu to a new menu, call that menu's `addItem()` method. @@ -620,9 +700,9 @@ A labeled menu item that expands into a submenu. @method - Appends a menu item to the end of the menu. If the item is already contained + Appends a menu item to the end of the menu. If the item is already contained in another menu or in the top-level context menu, it's automatically removed - first. If the item is already contained in this menu it will just be moved + first. If the item is already contained in this menu it will just be moved to the end of the menu. @param item {Item,Menu,Separator} The `Item`, `Menu`, or `Separator` to add to the menu. @@ -630,7 +710,7 @@ A labeled menu item that expands into a submenu. @method - Removes the given menu item from the menu. If the menu does not contain the + Removes the given menu item from the menu. If the menu does not contain the item, this method does nothing. @param item {Item,Menu,Separator} The menu item to remove from the menu. @@ -639,7 +719,7 @@ A labeled menu item that expands into a submenu. @method Permanently removes the menu from its parent menu and frees its resources. - The menu must not be used afterward. If you need to remove the menu from its + The menu must not be used afterward. If you need to remove the menu from its parent menu but use it afterward, call `removeItem()` on the parent menu instead. @@ -661,7 +741,7 @@ from the content script. The message can be any @class -A menu separator. Separators can be contained only in `Menu`s, not in the +A menu separator. Separators can be contained only in `Menu`s, not in the top-level context menu. @@ -671,14 +751,14 @@ top-level context menu. @property {Menu} - The separator's parent `Menu`. This property is read-only. To add the + The separator's parent `Menu`. This property is read-only. To add the separator to a new menu, call that menu's `addItem()` method. @method Permanently removes the separator from its parent menu and frees its - resources. The separator must not be used afterward. If you need to remove + resources. The separator must not be used afterward. If you need to remove the separator from its parent menu but use it afterward, call `removeItem()` on the parent menu instead. @@ -689,7 +769,7 @@ top-level context menu. @class @constructor - Creates a page context. See Specifying Contexts above. + Creates a page context. See Specifying Contexts above. @@ -697,7 +777,7 @@ top-level context menu. @class @constructor - Creates a context that occurs when a page contains a selection. See + Creates a context that occurs when a page contains a selection. See Specifying Contexts above. @@ -706,7 +786,7 @@ top-level context menu. @class @constructor - Creates a context that matches a given CSS selector. See Specifying Contexts + Creates a context that matches a given CSS selector. See Specifying Contexts above. @param selector {string} A CSS selector. @@ -717,7 +797,7 @@ top-level context menu. @class @constructor - Creates a context that matches pages with particular URLs. See Specifying + Creates a context that matches pages with particular URLs. See Specifying Contexts above. @param matchPattern {string,array} A [match pattern](modules/sdk/page-mod/match-pattern.html) string, regexp or an diff --git a/addon-sdk/source/doc/module-source/sdk/core/promise.md b/addon-sdk/source/doc/module-source/sdk/core/promise.md index 9792ddf116fb..37d8127917eb 100644 --- a/addon-sdk/source/doc/module-source/sdk/core/promise.md +++ b/addon-sdk/source/doc/module-source/sdk/core/promise.md @@ -201,11 +201,11 @@ functions to asynchronous by making it aware of promises. Module exports `promised` function to do exactly that: const { promised } = require('sdk/core/promise'); - function sum(x, y) { return x + y } - var sumAsync = promised(sum); + function sum(x, y) { return x + y }; + var asyncSum = promised(sum); var c = sum(a, b); - var cAsync = asyncSum(aAsync(), bAsinc()); + var cAsync = asyncSum(aAsync(), bAsync()); `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 diff --git a/addon-sdk/source/doc/module-source/sdk/preferences/service.md b/addon-sdk/source/doc/module-source/sdk/preferences/service.md index 9af26c6b32a4..b033ca3388bb 100644 --- a/addon-sdk/source/doc/module-source/sdk/preferences/service.md +++ b/addon-sdk/source/doc/module-source/sdk/preferences/service.md @@ -7,7 +7,7 @@ -The `preferences-service` module provides access to the +The `preferences/service` module provides access to the application-wide preferences service singleton. diff --git a/addon-sdk/source/doc/module-source/sdk/private-browsing.md b/addon-sdk/source/doc/module-source/sdk/private-browsing.md index b993e4367132..b0020247a2fe 100644 --- a/addon-sdk/source/doc/module-source/sdk/private-browsing.md +++ b/addon-sdk/source/doc/module-source/sdk/private-browsing.md @@ -27,14 +27,23 @@ but will have no effect when called. @property {boolean} - This read-only boolean is true if global private browsing mode is turned on. + This read-only boolean is `true` if global private browsing mode is turned on.
    This property is deprecated. It will continue to work until version 1.13 of the SDK. - From version 1.13 onwards it will always return false. + From version 1.13 onwards it will always return `false`.
    + +@function + Returns `true` if the argument is a private window or tab. +@param [thing] {any} + The thing to check if it is private, only handles windows and tabs at the moment. + Everything else returns `false` automatically. + In global private browsing mode, this method returns the same value as `isActive`. + + @function Turns on global private browsing mode. diff --git a/addon-sdk/source/doc/module-source/sdk/self.md b/addon-sdk/source/doc/module-source/sdk/self.md index 3c75935865d2..6bb2fec7194b 100644 --- a/addon-sdk/source/doc/module-source/sdk/self.md +++ b/addon-sdk/source/doc/module-source/sdk/self.md @@ -51,6 +51,12 @@ downgrade + +@property {boolean} +This property indicates add-on's support for private browsing. It comes from the +`private-browsing` property set in the `package.json` file in the main package. + + @property {object} The `data` object is used to access data that was bundled with the add-on. @@ -92,3 +98,5 @@ as the Panel: @returns {String}
    + + diff --git a/addon-sdk/source/doc/static-files/base.html b/addon-sdk/source/doc/static-files/base.html index ddc294f7e334..86fe6f0eba43 100644 --- a/addon-sdk/source/doc/static-files/base.html +++ b/addon-sdk/source/doc/static-files/base.html @@ -45,7 +45,6 @@ - @@ -163,6 +162,8 @@ + + diff --git a/addon-sdk/source/doc/static-files/css/sdk-docs.css b/addon-sdk/source/doc/static-files/css/sdk-docs.css index e91c76e6f601..6c7df87e2a40 100644 --- a/addon-sdk/source/doc/static-files/css/sdk-docs.css +++ b/addon-sdk/source/doc/static-files/css/sdk-docs.css @@ -441,8 +441,8 @@ ul.tree ul li:last-child { background: #fff url("../media/icons/lastnode.png") no-repeat; } -ul.module-list, -ul#module-index { +ul#module-index, +h2[id="Module Compatibility"] ~ ul { border-color: #a0d0fb; border: solid 1px #a0d0fb; padding: 1em; @@ -455,8 +455,8 @@ ul#module-index { column-gap: 1em; } -span.unsupported-on-mobile, -ul.module-list li.unsupported-on-mobile, -ul.module-list li.unsupported-on-mobile > a { - color: maroon; +span.unsupported-on-mobile strong, +h2[id="Module Compatibility"] ~ ul strong { + font-weight: normal; + text-decoration: line-through; } diff --git a/addon-sdk/source/lib/sdk/addon/runner.js b/addon-sdk/source/lib/sdk/addon/runner.js index 0552de28d177..585b4da43c54 100644 --- a/addon-sdk/source/lib/sdk/addon/runner.js +++ b/addon-sdk/source/lib/sdk/addon/runner.js @@ -86,8 +86,7 @@ function startup(reason, options) { then(function onLocalizationReady(data) { // Exports data to a pseudo module so that api-utils/l10n/core // can get access to it - if (data) - definePseudo(options.loader, '@l10n/data', data); + definePseudo(options.loader, '@l10n/data', data ? data : null); run(options); }); } diff --git a/addon-sdk/source/lib/sdk/console/plain-text.js b/addon-sdk/source/lib/sdk/console/plain-text.js index 52775b4f3d7b..445cebd82932 100644 --- a/addon-sdk/source/lib/sdk/console/plain-text.js +++ b/addon-sdk/source/lib/sdk/console/plain-text.js @@ -69,11 +69,15 @@ function message(print, level) { } function errorMessage(print, e) { + // Some platform exception doesn't have name nor message but + // can be stringified to a meaningfull message var fullString = ("An exception occurred.\n" + - e.name + ": " + e.message + "\n" + - traceback.sourceURI(e.fileName) + " " + - e.lineNumber + "\n" + - traceback.format(e)); + (e.name ? e.name + ": " : "") + + (e.message ? e.message : e.toString()) + "\n" + + (e.fileName ? traceback.sourceURI(e.fileName) + " " + + e.lineNumber + "\n" + : "") + + traceback.format(e)); message(print, "error", fullString); } diff --git a/addon-sdk/source/lib/sdk/context-menu.js b/addon-sdk/source/lib/sdk/context-menu.js index 9f04abdb85cd..d0fa332fb66d 100644 --- a/addon-sdk/source/lib/sdk/context-menu.js +++ b/addon-sdk/source/lib/sdk/context-menu.js @@ -204,6 +204,9 @@ function removeItemFromArray(array, item) { return array.filter(function(i) i !== item); } +// Converts anything that isn't false, null or undefined into a string +function stringOrNull(val) val ? String(val) : val; + // Shared option validation rules for Item and Menu let baseItemRules = { parentMenu: { @@ -254,13 +257,13 @@ let baseItemRules = { let labelledItemRules = mix(baseItemRules, { label: { - map: String, + map: stringOrNull, is: ["string"], ok: function (v) !!v, msg: "The item must have a non-empty string label." }, image: { - map: String, + map: stringOrNull, is: ["string", "undefined", "null"] } }); @@ -268,8 +271,8 @@ let labelledItemRules = mix(baseItemRules, { // Additional validation rules for Item let itemRules = mix(labelledItemRules, { data: { - map: String, - is: ["string", "undefined"] + map: stringOrNull, + is: ["string", "undefined", "null"] } }); diff --git a/addon-sdk/source/lib/sdk/deprecated/unit-test-finder.js b/addon-sdk/source/lib/sdk/deprecated/unit-test-finder.js index 880f5544d60a..0d13265cafba 100644 --- a/addon-sdk/source/lib/sdk/deprecated/unit-test-finder.js +++ b/addon-sdk/source/lib/sdk/deprecated/unit-test-finder.js @@ -11,7 +11,8 @@ module.metadata = { const file = require("../io/file"); const memory = require('./memory'); const suites = require('@test/options').allTestModules; - +const { Loader } = require("sdk/test/loader"); +const cuddlefish = require("sdk/loader/cuddlefish"); const NOT_TESTS = ['setup', 'teardown']; @@ -52,7 +53,11 @@ TestFinder.prototype = { suites.forEach( function(suite) { - var module = require(suite); + // Load each test file as a main module in its own loader instance + // `suite` is defined by cuddlefish/manifest.py:ManifestBuilder.build + var loader = Loader(module); + var module = cuddlefish.main(loader, suite); + if (self.testInProcess) for each (let name in Object.keys(module).sort()) { if(NOT_TESTS.indexOf(name) === -1 && filter(suite, name)) { diff --git a/addon-sdk/source/lib/sdk/indexed-db.js b/addon-sdk/source/lib/sdk/indexed-db.js index e6e85f36e0e2..faaad92bfe30 100644 --- a/addon-sdk/source/lib/sdk/indexed-db.js +++ b/addon-sdk/source/lib/sdk/indexed-db.js @@ -10,7 +10,7 @@ module.metadata = { const { Cc, Ci } = require("chrome"); const { extend } = require("./core/heritage"); -const { id } = require("self"); +const { id } = require("./self"); // placeholder, copied from bootstrap.js let sanitizeId = function(id){ diff --git a/addon-sdk/source/lib/sdk/l10n/html.js b/addon-sdk/source/lib/sdk/l10n/html.js index 506eaf83221b..7a0b0a25644a 100644 --- a/addon-sdk/source/lib/sdk/l10n/html.js +++ b/addon-sdk/source/lib/sdk/l10n/html.js @@ -8,11 +8,15 @@ module.metadata = { "stability": "unstable" }; -const { Ci } = require("chrome"); +const { Ci, Cu } = require("chrome"); const events = require("../system/events"); const core = require("./core"); const assetsURI = require('../self').data.url(); +const { Services } = Cu.import("resource://gre/modules/Services.jsm"); + +const hideContentStyle = "data:text/css,:root {visibility: hidden !important;}"; +const hideSheetUri = Services.io.newURI(hideContentStyle, null, null); // Taken from Gaia: // https://github.com/andreasgal/gaia/blob/04fde2640a7f40314643016a5a6c98bf3755f5fd/webapi.js#L1470 @@ -41,8 +45,15 @@ function onDocumentReady2Translate(event) { translateElement(document); - // Finally display document when we finished replacing all text content - document.documentElement.style.visibility = "visible"; + try { + // Finally display document when we finished replacing all text content + let winUtils = document.defaultView.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + winUtils.removeSheet(hideSheetUri, winUtils.USER_SHEET); + } + catch(e) { + console.exception(e); + } } function onContentWindow(event) { @@ -61,12 +72,16 @@ function onContentWindow(event) { if (document.location.href.indexOf(assetsURI) !== 0) return; - // First hide content of the document in order to have content blinking - // between untranslated and translated states - // TODO: use result of bug 737003 discussion in order to avoid any conflict - // with document CSS - document.documentElement.style.visibility = "hidden"; - + try { + // First hide content of the document in order to have content blinking + // between untranslated and translated states + let winUtils = document.defaultView.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + winUtils.loadSheet(hideSheetUri, winUtils.USER_SHEET); + } + catch(e) { + console.exception(e); + } // Wait for DOM tree to be built before applying localization document.addEventListener("DOMContentLoaded", onDocumentReady2Translate, false); @@ -92,4 +107,4 @@ function disable() { } exports.disable = disable; -require("api-utils/unload").when(disable); +require("sdk/system/unload").when(disable); diff --git a/addon-sdk/source/lib/sdk/loader/cuddlefish.js b/addon-sdk/source/lib/sdk/loader/cuddlefish.js index 816ef3a868dd..51355cc75d8a 100644 --- a/addon-sdk/source/lib/sdk/loader/cuddlefish.js +++ b/addon-sdk/source/lib/sdk/loader/cuddlefish.js @@ -88,8 +88,8 @@ function CuddlefishLoader(options) { // cache to avoid subsequent loads via `require`. modules: override({ 'toolkit/loader': loaderModule, - 'addon-sdk/sdk/loader/cuddlefish': exports, - 'addon-sdk/sdk/system/xul-app': xulappModule + 'sdk/loader/cuddlefish': exports, + 'sdk/system/xul-app': xulappModule }, options.modules), resolve: function resolve(id, requirer) { let entry = requirer && requirer in manifest && manifest[requirer]; @@ -103,15 +103,14 @@ function CuddlefishLoader(options) { // If requirer entry is in manifest and it's requirement is not, than // it has no authority to load since linker was not able to find it. if (!requirement) - throw Error('Module: ' + requirer.id + ' located at ' + requirer.uri - + ' has no authority to load: ' + id, requirer.uri); + throw Error('Module: ' + requirer + ' has no authority to load: ' + + id, requirer); uri = requirement; - } - // If requirer is off manifest than it's a system module and we allow it - // to go off manifest. - else { - uri = id; + } else { + // If requirer is off manifest than it's a system module and we allow it + // to go off manifest by resolving a relative path. + uri = loaderModule.resolve(id, requirer); } return uri; }, diff --git a/addon-sdk/source/lib/sdk/net/xhr.js b/addon-sdk/source/lib/sdk/net/xhr.js index b4fc1ef90bf4..7fc829970a38 100644 --- a/addon-sdk/source/lib/sdk/net/xhr.js +++ b/addon-sdk/source/lib/sdk/net/xhr.js @@ -114,6 +114,10 @@ XMLHttpRequest.prototype = { set upload(newValue) { throw new Error("not implemented"); }, + forceAllowThirdPartyCookie: function forceAllowThirdPartyCookie() { + if (this._req.channel instanceof Ci.nsIHttpChannelInternal) + this._req.channel.forceAllowThirdPartyCookie = true; + }, get onreadystatechange() { return this._orsc; }, diff --git a/addon-sdk/source/lib/sdk/private-browsing.js b/addon-sdk/source/lib/sdk/private-browsing.js index dba69748e35a..aa1aa23f6be7 100644 --- a/addon-sdk/source/lib/sdk/private-browsing.js +++ b/addon-sdk/source/lib/sdk/private-browsing.js @@ -7,10 +7,11 @@ module.metadata = { "stability": "stable" }; -const { setMode, getMode, on: onStateChange } = require('./private-browsing/utils'); +const { setMode, getMode, on: onStateChange, isWindowPrivate } = require('./private-browsing/utils'); const { emit, on, once, off } = require('./event/core'); const { when: unload } = require('./system/unload'); const { deprecateUsage, deprecateFunction, deprecateEvent } = require('./util/deprecate'); +const { getOwnerWindow } = require('./private-browsing/window/utils'); onStateChange('start', function onStart() { emit(exports, 'start'); @@ -36,6 +37,19 @@ exports.removeListener = deprecateEvents(function removeListener(type, listener) off(exports, type, listener); }); +exports.isPrivate = function(thing) { + if (!!thing) { + if (isWindowPrivate(thing)) { + return true; + } + + let window = getOwnerWindow(thing); + if (window) + return isWindowPrivate(window); + } + return getMode(); +}; + function deprecateEvents(func) deprecateEvent( func, 'The require("private-browsing") module\'s "start" and "stop" events are deprecated.', diff --git a/addon-sdk/source/lib/sdk/private-browsing/utils.js b/addon-sdk/source/lib/sdk/private-browsing/utils.js index dc23f7b07926..da739f3e3f76 100644 --- a/addon-sdk/source/lib/sdk/private-browsing/utils.js +++ b/addon-sdk/source/lib/sdk/private-browsing/utils.js @@ -15,13 +15,14 @@ const { getWindowLoadingContext, windows } = require('../window/utils'); const { WindowTracker } = require("../deprecated/window-utils"); const events = require('../system/events'); const { deprecateFunction } = require('../util/deprecate'); +const { isOneOf, is, satisfiesVersion, version } = require('../system/xul-app'); let deferredEmit = defer(emit); let pbService; let PrivateBrowsingUtils; // Private browsing is only supported in Fx -if (require("../system/xul-app").is("Firefox")) { +if (isOneOf(['Firefox', 'Fennec'])) { // get the nsIPrivateBrowsingService if it exists try { pbService = Cc["@mozilla.org/privatebrowsing;1"]. @@ -41,20 +42,36 @@ if (require("../system/xul-app").is("Firefox")) { } function isWindowPrivate(win) { + if (!PrivateBrowsingUtils || !win) + return false; + // if the pbService is undefined, the PrivateBrowsingUtils.jsm is available, // and the app is Firefox, then assume per-window private browsing is // enabled. - return win instanceof Ci.nsIDOMWindow && - isWindowPBSupported && - PrivateBrowsingUtils.isWindowPrivate(win); + if (win instanceof Ci.nsIDOMWindow) { + return PrivateBrowsingUtils.isWindowPrivate(win); + } + + // Sometimes the input is not a nsIDOMWindow.. but it is still a winodw. + try { + return !!win.docShell.QueryInterface(Ci.nsILoadContext).usePrivateBrowsing; + } + catch (e) {} + + return false; } exports.isWindowPrivate = isWindowPrivate; // checks that global private browsing is implemented -let isGlobalPBSupported = exports.isGlobalPBSupported = !!pbService; +let isGlobalPBSupported = exports.isGlobalPBSupported = !!pbService && is('Firefox'); // checks that per-window private browsing is implemented -let isWindowPBSupported = exports.isWindowPBSupported = !isGlobalPBSupported && !!PrivateBrowsingUtils; +let isWindowPBSupported = exports.isWindowPBSupported = + !pbService && !!PrivateBrowsingUtils && is('Firefox'); + +// checks that per-tab private browsing is implemented +let isTabPBSupported = exports.isTabPBSupported = + !pbService && !!PrivateBrowsingUtils && is('Fennec') && satisfiesVersion(version, '>=20.0*'); function onChange() { // Emit event with in next turn of event loop. diff --git a/addon-sdk/source/lib/sdk/private-browsing/window/utils.js b/addon-sdk/source/lib/sdk/private-browsing/window/utils.js new file mode 100644 index 000000000000..b6fb4e71bd12 --- /dev/null +++ b/addon-sdk/source/lib/sdk/private-browsing/window/utils.js @@ -0,0 +1,32 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +module.metadata = { + 'stability': 'unstable' +}; + +const privateNS = require('../../core/namespace').ns(); + +function getOwnerWindow(thing) { + try { + // check for and return associated window + let fn = (privateNS(thing.prototype) || privateNS(thing) || {}).getOwnerWindow; + if (fn) + return fn.apply(fn, [thing].concat(arguments)); + } + // stuff like numbers and strings throw errors with namespaces + catch(e) {} + // default + return undefined; +} +getOwnerWindow.define = function(Type, fn) { + privateNS(Type.prototype).getOwnerWindow = fn; +} + +getOwnerWindow.implement = function(instance, fn) { + privateNS(instance).getOwnerWindow = fn; +} + +exports.getOwnerWindow = getOwnerWindow; diff --git a/addon-sdk/source/lib/sdk/request.js b/addon-sdk/source/lib/sdk/request.js index da5be7fc99b5..ac891012fc59 100644 --- a/addon-sdk/source/lib/sdk/request.js +++ b/addon-sdk/source/lib/sdk/request.js @@ -71,6 +71,8 @@ function runRequest(mode, target) { // open the request xhr.open(mode, url); + xhr.forceAllowThirdPartyCookie(); + // request header must be set after open, but before send xhr.setRequestHeader("Content-Type", contentType); diff --git a/addon-sdk/source/lib/sdk/selection.js b/addon-sdk/source/lib/sdk/selection.js index c97f0e6f3c83..c9e81ec83065 100644 --- a/addon-sdk/source/lib/sdk/selection.js +++ b/addon-sdk/source/lib/sdk/selection.js @@ -20,7 +20,7 @@ const { Ci, Cc } = require("chrome"), { when: unload } = require("./system/unload"), { getTabs, getTabContentWindow, getTabForContentWindow, getAllTabContentWindows } = require('./tabs/utils'), - { getInnerId, getMostRecentBrowserWindow, + { getMostRecentBrowserWindow, windows, getFocusedWindow, getFocusedElement } = require("./window/utils"), events = require("./system/events"); @@ -321,17 +321,6 @@ function addSelectionListener(window) { window.addEventListener("select", selectionListener.onSelect, true); selections(window).selection = selection; - - let innerId = getInnerId(window); - - events.on("inner-window-destroyed", function destroyed (event) { - let destroyedId = event.subject.QueryInterface(Ci.nsISupportsPRUint64).data; - - if (destroyedId === innerId) { - removeSelectionListener(window); - events.off("inner-window-destroyed", destroyed); - } - }); }; /** @@ -382,7 +371,7 @@ getAllTabContentWindows().forEach(addSelectionListener); // // See bug 665386 for further details. -events.on("document-shown", function (event) { +function onShown(event) { let window = event.subject.defaultView; // We are not interested in documents without valid defaultView. @@ -408,17 +397,21 @@ events.on("document-shown", function (event) { if (currentSelection instanceof Ci.nsISelectionPrivate && currentSelection !== selection) { + window.addEventListener("select", selectionListener.onSelect, true); currentSelection.addSelectionListener(selectionListener); selections(window).selection = currentSelection; } } -}); +} + +events.on("document-shown", onShown, true); // Removes Selection listeners when the add-on is unloaded unload(function(){ getAllTabContentWindows().forEach(removeSelectionListener); events.off("document-element-inserted", onContent); + events.off("document-shown", onShown); off(exports); }); diff --git a/addon-sdk/source/lib/sdk/self.js b/addon-sdk/source/lib/sdk/self.js index 9aa7cd572933..f792ac8c71dd 100644 --- a/addon-sdk/source/lib/sdk/self.js +++ b/addon-sdk/source/lib/sdk/self.js @@ -2,16 +2,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/. */ - "use strict"; module.metadata = { "stability": "stable" }; - const { CC } = require('chrome'); -const { id, name, prefixURI, rootURI, +const { id, name, prefixURI, rootURI, metadata, version, loadReason } = require('@loader/options'); const { readURISync } = require('./net/url'); @@ -39,3 +37,5 @@ exports.data = Object.freeze({ return readURISync(uri(path)); } }); +exports.isPrivateBrowsingSupported = ((metadata.permissions || {})['private-browsing'] === true) ? + true : false; diff --git a/addon-sdk/source/lib/sdk/system/runtime.js b/addon-sdk/source/lib/sdk/system/runtime.js index e64b6633cc46..25f7552b0bc8 100644 --- a/addon-sdk/source/lib/sdk/system/runtime.js +++ b/addon-sdk/source/lib/sdk/system/runtime.js @@ -16,4 +16,13 @@ exports.inSafeMode = runtime.inSafeMode; exports.OS = runtime.OS; exports.processType = runtime.processType; exports.widgetToolkit = runtime.widgetToolkit; -exports.XPCOMABI = runtime.XPCOMABI; + +// Attempt to access `XPCOMABI` may throw exception, in which case exported +// `XPCOMABI` will be set to `null`. +// https://mxr.mozilla.org/mozilla-central/source/toolkit/xre/nsAppRunner.cpp#732 +try { + exports.XPCOMABI = runtime.XPCOMABI; +} +catch (error) { + exports.XPCOMABI = null; +} diff --git a/addon-sdk/source/lib/sdk/system/xul-app.js b/addon-sdk/source/lib/sdk/system/xul-app.js index 35cd3ae657e9..27803296a4a2 100644 --- a/addon-sdk/source/lib/sdk/system/xul-app.js +++ b/addon-sdk/source/lib/sdk/system/xul-app.js @@ -1,7 +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/. */ - "use strict"; module.metadata = { @@ -103,7 +102,6 @@ function normalizeRange(range) { * A version to compare */ function compareVersion(version, comparison, compareVersion) { - let hasWildcard = compareVersion.indexOf("*") !== -1; comparison = comparison || "="; @@ -181,5 +179,4 @@ function satisfiesVersion(version, versionRange) { : true); }); } - exports.satisfiesVersion = satisfiesVersion; diff --git a/addon-sdk/source/lib/sdk/tabs/tab-fennec.js b/addon-sdk/source/lib/sdk/tabs/tab-fennec.js index 88a6f033dfc8..63017cef47e1 100644 --- a/addon-sdk/source/lib/sdk/tabs/tab-fennec.js +++ b/addon-sdk/source/lib/sdk/tabs/tab-fennec.js @@ -7,9 +7,10 @@ const { Cc, Ci } = require('chrome'); const { Class } = require('../core/heritage'); const { tabNS } = require('./namespace'); const { EventTarget } = require('../event/target'); -const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL, +const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL, getContentWindowForTab, setTabURL, getOwnerWindow, getTabContentType, getTabId } = require('./utils'); const { emit } = require('../event/core'); +const { getOwnerWindow: getPBOwnerWindow } = require('../private-browsing/window/utils'); const { when: unload } = require('../system/unload'); const { EVENTS } = require('./events'); @@ -143,3 +144,7 @@ const Tab = Class({ } }); exports.Tab = Tab; + +getPBOwnerWindow.define(Tab, function(tab) { + return getContentWindowForTab(tabNS(tab).tab); +}); diff --git a/addon-sdk/source/lib/sdk/tabs/tab-firefox.js b/addon-sdk/source/lib/sdk/tabs/tab-firefox.js index a906768d7df2..897fd53d6fdf 100644 --- a/addon-sdk/source/lib/sdk/tabs/tab-firefox.js +++ b/addon-sdk/source/lib/sdk/tabs/tab-firefox.js @@ -11,6 +11,8 @@ const { getThumbnailURIForWindow } = require("../content/thumbnail"); const { getFaviconURIForLocation } = require("../io/data"); const { activateTab, getOwnerWindow, getBrowserForTab, getTabTitle, setTabTitle, getTabURL, setTabURL, getTabContentType, getTabId } = require('./utils'); +const { getOwnerWindow: getPBOwnerWindow } = require('../private-browsing/window/utils'); +const viewNS = require('sdk/core/namespace').ns(); // Array of the inner instances of all the wrapped tabs. const TABS = []; @@ -50,6 +52,9 @@ const TabTrait = Trait.compose(EventEmitter, { if (options.isPinned) this.pin(); + viewNS(this._public).tab = this._tab; + getPBOwnerWindow.implement(this._public, getChromeTab); + // 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. @@ -212,6 +217,10 @@ const TabTrait = Trait.compose(EventEmitter, { } }); +function getChromeTab(tab) { + return getOwnerWindow(viewNS(tab).tab); +} + function Tab(options) { let chromeTab = options.tab; for each (let tab in TABS) { diff --git a/addon-sdk/source/lib/sdk/tabs/utils.js b/addon-sdk/source/lib/sdk/tabs/utils.js index 417246549aff..1a38cb3ba700 100644 --- a/addon-sdk/source/lib/sdk/tabs/utils.js +++ b/addon-sdk/source/lib/sdk/tabs/utils.js @@ -80,7 +80,8 @@ exports.getOwnerWindow = getOwnerWindow; // fennec function getWindowHoldingTab(rawTab) { for each (let window in windows()) { - // this function may be called when not using fennec + // this function may be called when not using fennec, + // but BrowserApp is only defined on Fennec if (!window.BrowserApp) continue; @@ -100,7 +101,8 @@ function openTab(window, url, options) { if (window.BrowserApp) { return window.BrowserApp.addTab(url, { selected: options.inBackground ? false : true, - pinned: options.isPinned || false + pinned: options.isPinned || false, + isPrivate: options.private || false }); } return window.gBrowser.addTab(url); @@ -150,6 +152,12 @@ function getBrowserForTab(tab) { } exports.getBrowserForTab = getBrowserForTab; + +function getContentWindowForTab(tab) { + return getBrowserForTab(tab).contentWindow; +} +exports.getContentWindowForTab = getContentWindowForTab; + function getTabId(tab) { if (tab.browser) // fennec return tab.id @@ -158,7 +166,6 @@ function getTabId(tab) { } exports.getTabId = getTabId; - function getTabTitle(tab) { return getBrowserForTab(tab).contentDocument.title || tab.label || ""; } diff --git a/addon-sdk/source/lib/sdk/test.js b/addon-sdk/source/lib/sdk/test.js index 0a322d6991f8..37314023eca6 100644 --- a/addon-sdk/source/lib/sdk/test.js +++ b/addon-sdk/source/lib/sdk/test.js @@ -9,8 +9,8 @@ module.metadata = { "stability": "unstable" }; -const BaseAssert = require("./test/assert").Assert; -const { isFunction, isObject } = require("./lang/type"); +const BaseAssert = require("sdk/test/assert").Assert; +const { isFunction, isObject } = require("sdk/lang/type"); function extend(target) { let descriptor = {} diff --git a/addon-sdk/source/lib/sdk/test/harness.js b/addon-sdk/source/lib/sdk/test/harness.js index caf8f471d203..3d41987f13fd 100644 --- a/addon-sdk/source/lib/sdk/test/harness.js +++ b/addon-sdk/source/lib/sdk/test/harness.js @@ -20,7 +20,7 @@ const system = require("../system"); // Trick manifest builder to make it think we need these modules ? const unit = require("../deprecated/unit-test"); -const test = require("../test"); +const test = require("../../test"); const url = require("../url"); var cService = Cc['@mozilla.org/consoleservice;1'].getService() diff --git a/addon-sdk/source/lib/sdk/test/runner.js b/addon-sdk/source/lib/sdk/test/runner.js index 3a3abda6c930..2da3bd3baaaa 100644 --- a/addon-sdk/source/lib/sdk/test/runner.js +++ b/addon-sdk/source/lib/sdk/test/runner.js @@ -105,7 +105,7 @@ exports.runTestsFromModule = function runTestsFromModule(module) { runTests(function findAndRunTests(loader, nextIteration) { // Consider that all these tests are CommonJS ones - loader.require('../test').run(exports); + loader.require('../../test').run(exports); // Reproduce what is done in unit-test-finder.findTests() let tests = []; diff --git a/addon-sdk/source/lib/sdk/window/browser.js b/addon-sdk/source/lib/sdk/window/browser.js index 2b63a83e9fc0..eef0e92ac657 100644 --- a/addon-sdk/source/lib/sdk/window/browser.js +++ b/addon-sdk/source/lib/sdk/window/browser.js @@ -11,6 +11,7 @@ const { getWindowTitle } = require('./utils'); const unload = require('../system/unload'); const { getMode } = require('../private-browsing/utils'); const { EventTarget } = require('../event/target'); +const { getOwnerWindow: getPBOwnerWindow } = require('../private-browsing/window/utils'); const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec, consider using require("tabs") instead'; @@ -39,3 +40,7 @@ const BrowserWindow = Class({ get isPrivateBrowsing() getMode(windowNS(this).window), }); exports.BrowserWindow = BrowserWindow; + +getPBOwnerWindow.define(BrowserWindow, function(window) { + return windowNS(window).window; +}); diff --git a/addon-sdk/source/lib/sdk/windows/firefox.js b/addon-sdk/source/lib/sdk/windows/firefox.js index 646d7e9fea81..79ccd3c049b8 100644 --- a/addon-sdk/source/lib/sdk/windows/firefox.js +++ b/addon-sdk/source/lib/sdk/windows/firefox.js @@ -17,7 +17,9 @@ const { Cc, Ci, Cr } = require('chrome'), windowUtils = require('../deprecated/window-utils'), { WindowTrackerTrait } = windowUtils, { ns } = require('../core/namespace'), - { observer: windowObserver } = require('./observer'); + { observer: windowObserver } = require('./observer'), + { getOwnerWindow } = require('../private-browsing/window/utils'), + viewNS = require('sdk/core/namespace').ns(); /** * Window trait composes safe wrappers for browser window that are E10S @@ -69,6 +71,10 @@ const BrowserWindowTrait = Trait.compose( this._private = !!options.private; this._load(); + + viewNS(this._public).window = this._window; + getOwnerWindow.implement(this._public, getChromeWindow); + return this; }, destroy: function () this._onUnload(), @@ -239,4 +245,8 @@ const browserWindows = Trait.resolve({ toString: null }).compose( }).resolve({ toString: null }) )(); +function getChromeWindow(window) { + return viewNS(window).window; +} + exports.browserWindows = browserWindows; diff --git a/addon-sdk/source/lib/test.js b/addon-sdk/source/lib/test.js new file mode 100644 index 000000000000..37314023eca6 --- /dev/null +++ b/addon-sdk/source/lib/test.js @@ -0,0 +1,112 @@ +/* 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"; + +module.metadata = { + "stability": "unstable" +}; + +const BaseAssert = require("sdk/test/assert").Assert; +const { isFunction, isObject } = require("sdk/lang/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/addon-sdk/source/lib/toolkit/loader.js b/addon-sdk/source/lib/toolkit/loader.js index 5a52e67cdd56..8d94ea64934a 100644 --- a/addon-sdk/source/lib/toolkit/loader.js +++ b/addon-sdk/source/lib/toolkit/loader.js @@ -206,7 +206,6 @@ const evaluate = iced(function evaluate(sandbox, uri, options) { source: null }, options); - return source ? Cu.evalInSandbox(source, sandbox, version, uri, line) : loadSubScript(uri, sandbox, encoding); }); @@ -281,6 +280,7 @@ function normalize(uri) { return uri.substr(-3) === '.js' ? uri : uri + '.js'; } // `requirer.uri` but in some cases it may be `baseURI`. In order to // avoid complexity we require `baseURI` with a trailing `/`. const resolve = iced(function resolve(id, base) { + if (!isRelative(id)) return id; let paths = id.split('/'); let result = base.split('/'); result.pop(); @@ -320,7 +320,6 @@ const Require = iced(function Require(loader, requirer) { // Resolve `id` to its requirer if it's relative. let requirement = requirer ? resolve(id, requirer.id) : id; - // Resolves `uri` of module using loaders resolve function. let uri = resolveURI(requirement, mapping); diff --git a/addon-sdk/source/python-lib/cuddlefish/__init__.py b/addon-sdk/source/python-lib/cuddlefish/__init__.py index 63a31374aaab..fe4be4a172db 100644 --- a/addon-sdk/source/python-lib/cuddlefish/__init__.py +++ b/addon-sdk/source/python-lib/cuddlefish/__init__.py @@ -769,7 +769,7 @@ def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None, # This should be contained in the test runner package. # maybe just do: target_cfg.main = 'test-harness/run-tests' harness_options['main'] = 'sdk/test/runner' - harness_options['mainPath'] = manifest.get_manifest_entry("addon-sdk", "lib", "sdk/test/runner").get_path() + harness_options['mainPath'] = 'sdk/test/runner' else: harness_options['main'] = target_cfg.get('main') harness_options['mainPath'] = manifest.top_path @@ -832,10 +832,12 @@ def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None, if command == 'xpi': from cuddlefish.xpi import build_xpi + # Generate extra options extra_harness_options = {} for kv in options.extra_harness_option_args: key,value = kv.split("=", 1) extra_harness_options[key] = value + # Generate xpi filepath xpi_path = XPI_FILENAME % target_cfg.name print >>stdout, "Exporting extension to %s." % xpi_path build_xpi(template_root_dir=app_extension_dir, diff --git a/addon-sdk/source/python-lib/cuddlefish/docs/generate.py b/addon-sdk/source/python-lib/cuddlefish/docs/generate.py index 3438738ab792..6140951e99c2 100644 --- a/addon-sdk/source/python-lib/cuddlefish/docs/generate.py +++ b/addon-sdk/source/python-lib/cuddlefish/docs/generate.py @@ -196,4 +196,3 @@ def replace_file(env_root, dest_path, file_contents, must_rewrite_links): if must_rewrite_links and dest_path.endswith(".html"): file_contents = rewrite_links(env_root, get_sdk_docs_path(env_root), file_contents, dest_path) open(dest_path, "w").write(file_contents) - diff --git a/addon-sdk/source/python-lib/cuddlefish/manifest.py b/addon-sdk/source/python-lib/cuddlefish/manifest.py index 672d8edf6ef1..94f2faa680df 100644 --- a/addon-sdk/source/python-lib/cuddlefish/manifest.py +++ b/addon-sdk/source/python-lib/cuddlefish/manifest.py @@ -58,11 +58,22 @@ class ManifestEntry: self.datamap = None def get_path(self): - path = "%s/%s/%s" % \ - (self.packageName, self.sectionName, self.moduleName) - if not path.endswith(".js"): - path += ".js" - return path + name = self.moduleName + + if name.endswith(".js"): + name = name[:-3] + items = [] + # Only add package name for addons, so that system module paths match + # the path from the commonjs root directory and also match the loader + # mappings. + if self.packageName != "addon-sdk": + items.append(self.packageName) + # And for the same reason, do not append `lib/`. + if self.sectionName == "tests": + items.append(self.sectionName) + items.append(name) + + return "/".join(items) def get_entry_for_manifest(self): entry = { "packageName": self.packageName, @@ -75,13 +86,13 @@ class ManifestEntry: for req in self.requirements: if isinstance(self.requirements[req], ManifestEntry): them = self.requirements[req] # this is another ManifestEntry - them_path = them.get_path() - entry["requirements"][req] = {"path": them_path} + entry["requirements"][req] = them.get_path() else: # something magic. The manifest entry indicates that they're # allowed to require() it entry["requirements"][req] = self.requirements[req] - assert isinstance(entry["requirements"][req], dict) + assert isinstance(entry["requirements"][req], unicode) or \ + isinstance(entry["requirements"][req], str) return entry def add_js(self, js_filename): @@ -226,7 +237,8 @@ class ManifestBuilder: # search for all tests. self.test_modules will be passed # through the harness-options.json file in the # .allTestModules property. - self.test_modules.append(testname) + # Pass the absolute module path. + self.test_modules.append(tme.get_path()) # include files used by the loader for em in self.extra_modules: @@ -378,7 +390,7 @@ class ManifestBuilder: # If requirement is chrome or a pseudo-module (starts with @) make # path a requirement name. if reqname == "chrome" or reqname.startswith("@"): - me.add_requirement(reqname, {"path": reqname}) + me.add_requirement(reqname, reqname) else: # when two modules require() the same name, do they get a # shared instance? This is a deep question. For now say yes. diff --git a/addon-sdk/source/python-lib/cuddlefish/packaging.py b/addon-sdk/source/python-lib/cuddlefish/packaging.py index 13c6054fb597..da93448b15ac 100644 --- a/addon-sdk/source/python-lib/cuddlefish/packaging.py +++ b/addon-sdk/source/python-lib/cuddlefish/packaging.py @@ -23,7 +23,7 @@ DEFAULT_ICON64 = 'icon64.png' METADATA_PROPS = ['name', 'description', 'keywords', 'author', 'version', 'translators', 'contributors', 'license', 'homepage', 'icon', - 'icon64', 'main', 'directories'] + 'icon64', 'main', 'directories', 'permissions'] RESOURCE_HOSTNAME_RE = re.compile(r'^[a-z0-9_\-]+$') diff --git a/addon-sdk/source/python-lib/cuddlefish/rdf.py b/addon-sdk/source/python-lib/cuddlefish/rdf.py index 1ba802ed5c85..6ce69f11de00 100644 --- a/addon-sdk/source/python-lib/cuddlefish/rdf.py +++ b/addon-sdk/source/python-lib/cuddlefish/rdf.py @@ -169,7 +169,7 @@ def gen_manifest(template_root_dir, target_cfg, jid, ta_desc.appendChild(elem) elem = dom.createElement("em:maxVersion") - elem.appendChild(dom.createTextNode("20.*")) + elem.appendChild(dom.createTextNode("21.0a1")) ta_desc.appendChild(elem) if target_cfg.get("homepage"): diff --git a/addon-sdk/source/python-lib/cuddlefish/tests/test_linker.py b/addon-sdk/source/python-lib/cuddlefish/tests/test_linker.py index 792af6f540f2..317494ec6b16 100644 --- a/addon-sdk/source/python-lib/cuddlefish/tests/test_linker.py +++ b/addon-sdk/source/python-lib/cuddlefish/tests/test_linker.py @@ -46,15 +46,15 @@ class Basic(unittest.TestCase): m = m.get_harness_options_manifest() def assertReqIs(modname, reqname, path): - reqs = m["one/lib/%s.js" % modname]["requirements"] - self.failUnlessEqual(reqs[reqname]["path"], path) - assertReqIs("main", "panel", "addon-sdk/lib/sdk/panel.js") - assertReqIs("main", "two.js", "one/lib/two.js") - assertReqIs("main", "./two", "one/lib/two.js") - assertReqIs("main", "sdk/tabs.js", "addon-sdk/lib/sdk/tabs.js") - assertReqIs("main", "./subdir/three", "one/lib/subdir/three.js") - assertReqIs("two", "main", "one/lib/main.js") - assertReqIs("subdir/three", "../main", "one/lib/main.js") + reqs = m["one/%s" % modname]["requirements"] + self.failUnlessEqual(reqs[reqname], path) + assertReqIs("main", "panel", "sdk/panel") + assertReqIs("main", "two.js", "one/two") + assertReqIs("main", "./two", "one/two") + assertReqIs("main", "sdk/tabs.js", "sdk/tabs") + assertReqIs("main", "./subdir/three", "one/subdir/three") + assertReqIs("two", "main", "one/main") + assertReqIs("subdir/three", "../main", "one/main") target_cfg.dependencies = [] # now, because .dependencies *is* provided, we won't search 'deps', @@ -74,11 +74,11 @@ class Basic(unittest.TestCase): m = manifest.build_manifest(target_cfg, pkg_cfg, deps, scan_tests=False) m = m.get_harness_options_manifest() def assertReqIs(modname, reqname, path): - reqs = m["three/lib/%s.js" % modname]["requirements"] - self.failUnlessEqual(reqs[reqname]["path"], path) - assertReqIs("main", "three-a", "three-a/lib/main.js") - assertReqIs("main", "three-b", "three-b/lib/main.js") - assertReqIs("main", "three-c", "three-c/lib/main.js") + reqs = m["three/%s" % modname]["requirements"] + self.failUnlessEqual(reqs[reqname], path) + assertReqIs("main", "three-a", "three-a/main") + assertReqIs("main", "three-b", "three-b/main") + assertReqIs("main", "three-c", "three-c/main") def test_relative_main_in_top(self): target_cfg = self.get_pkg("five") @@ -91,7 +91,7 @@ class Basic(unittest.TestCase): # all we care about is that this next call doesn't raise an exception m = manifest.build_manifest(target_cfg, pkg_cfg, deps, scan_tests=False) m = m.get_harness_options_manifest() - reqs = m["five/lib/main.js"]["requirements"] + reqs = m["five/main"]["requirements"] self.failUnlessEqual(reqs, {}); def test_unreachable_relative_main_in_top(self): diff --git a/addon-sdk/source/python-lib/cuddlefish/tests/test_xpi.py b/addon-sdk/source/python-lib/cuddlefish/tests/test_xpi.py index ec5eaa3b6824..66adf3dcd26a 100644 --- a/addon-sdk/source/python-lib/cuddlefish/tests/test_xpi.py +++ b/addon-sdk/source/python-lib/cuddlefish/tests/test_xpi.py @@ -328,7 +328,7 @@ class SmallXPI(unittest.TestCase): [target_cfg.name, "addon-sdk"]) m = manifest.build_manifest(target_cfg, pkg_cfg, deps, scan_tests=True) self.failUnlessEqual(sorted(m.get_all_test_modules()), - sorted(["test-one", "test-two"])) + sorted(["three/tests/test-one", "three/tests/test-two"])) # the current __init__.py code omits limit_to=used_files for 'cfx # test', so all test files are included in the XPI. But the test # runner will only execute the tests that m.get_all_test_modules() @@ -371,7 +371,7 @@ class SmallXPI(unittest.TestCase): m = manifest.build_manifest(target_cfg, pkg_cfg, deps, scan_tests=True, test_filter_re=FILTER) self.failUnlessEqual(sorted(m.get_all_test_modules()), - sorted(["test-one"])) + sorted(["three/tests/test-one"])) # the current __init__.py code omits limit_to=used_files for 'cfx # test', so all test files are included in the XPI. But the test # runner will only execute the tests that m.get_all_test_modules() diff --git a/addon-sdk/source/python-lib/cuddlefish/xpi.py b/addon-sdk/source/python-lib/cuddlefish/xpi.py index 3943e56f22c6..724eea55e1da 100644 --- a/addon-sdk/source/python-lib/cuddlefish/xpi.py +++ b/addon-sdk/source/python-lib/cuddlefish/xpi.py @@ -29,6 +29,7 @@ def build_xpi(template_root_dir, manifest, xpi_path, zf.write('.install.rdf', 'install.rdf') os.remove('.install.rdf') + # Handle add-on icon if 'icon' in harness_options: zf.write(str(harness_options['icon']), 'icon.png') del harness_options['icon'] @@ -37,6 +38,7 @@ def build_xpi(template_root_dir, manifest, xpi_path, zf.write(str(harness_options['icon64']), 'icon64.png') del harness_options['icon64'] + # Handle simple-prefs if 'preferences' in harness_options: from options_xul import parse_options, validate_prefs @@ -133,7 +135,7 @@ def build_xpi(template_root_dir, manifest, xpi_path, parentpath = ZIPSEP.join(bits[0:i]) dirs_to_create.add(parentpath) - # create zipfile in alphabetical order, with each directory before its + # Create zipfile in alphabetical order, with each directory before its # files for name in sorted(dirs_to_create.union(set(files_to_copy))): if name in dirs_to_create: @@ -141,12 +143,15 @@ def build_xpi(template_root_dir, manifest, xpi_path, if name in files_to_copy: zf.write(files_to_copy[name], name) + # Add extra harness options harness_options = harness_options.copy() for key,value in extra_harness_options.items(): if key in harness_options: msg = "Can't use --harness-option for existing key '%s'" % key raise HarnessOptionAlreadyDefinedError(msg) harness_options[key] = value + + # Write harness-options.json open('.options.json', 'w').write(json.dumps(harness_options, indent=1, sort_keys=True)) zf.write('.options.json', 'harness-options.json') diff --git a/addon-sdk/source/python-lib/markdown/__init__.py b/addon-sdk/source/python-lib/markdown/__init__.py index 0d1c50497920..09a225e88946 100644 --- a/addon-sdk/source/python-lib/markdown/__init__.py +++ b/addon-sdk/source/python-lib/markdown/__init__.py @@ -182,7 +182,7 @@ class Markdown: def __init__(self, extensions=[], extension_configs={}, - safe_mode = False, + safe_mode = False, output_format=DEFAULT_OUTPUT_FORMAT): """ Creates a new Markdown instance. @@ -200,12 +200,12 @@ class Markdown: * "xhtml": Outputs latest supported version of XHTML (currently XHTML 1.1). * "html4": Outputs HTML 4 * "html": Outputs latest supported version of HTML (currently HTML 4). - Note that it is suggested that the more specific formats ("xhtml1" + Note that it is suggested that the more specific formats ("xhtml1" and "html4") be used as "xhtml" or "html" may change in the future - if it makes sense at that time. + if it makes sense at that time. """ - + self.safeMode = safe_mode self.registeredExtensions = [] self.docType = "" @@ -300,9 +300,9 @@ class Markdown: # Map format keys to serializers self.output_formats = { - 'html' : html4.to_html_string, + 'html' : html4.to_html_string, 'html4' : html4.to_html_string, - 'xhtml' : etree.tostring, + 'xhtml' : etree.tostring, 'xhtml1': etree.tostring, } @@ -332,7 +332,7 @@ class Markdown: except AttributeError: message(ERROR, "Incorrect type! Extension '%s' is " "neither a string or an Extension." %(repr(ext))) - + def registerExtension(self, extension): """ This gets called by the extension """ @@ -574,15 +574,15 @@ def markdown(text, * "xhtml": Outputs latest supported version of XHTML (currently XHTML 1.1). * "html4": Outputs HTML 4 * "html": Outputs latest supported version of HTML (currently HTML 4). - Note that it is suggested that the more specific formats ("xhtml1" + Note that it is suggested that the more specific formats ("xhtml1" and "html4") be used as "xhtml" or "html" may change in the future - if it makes sense at that time. + if it makes sense at that time. Returns: An HTML document as a string. """ md = Markdown(extensions=load_extensions(extensions), - safe_mode=safe_mode, + safe_mode=safe_mode, output_format=output_format) return md.convert(text) @@ -594,7 +594,7 @@ def markdownFromFile(input = None, safe_mode = False, output_format = DEFAULT_OUTPUT_FORMAT): """Read markdown code from a file and write it to a file or a stream.""" - md = Markdown(extensions=load_extensions(extensions), + md = Markdown(extensions=load_extensions(extensions), safe_mode=safe_mode, output_format=output_format) md.convertFile(input, output, encoding) diff --git a/addon-sdk/source/python-lib/mozrunner/__init__.py b/addon-sdk/source/python-lib/mozrunner/__init__.py index b88c7347e888..2e5c57abd908 100644 --- a/addon-sdk/source/python-lib/mozrunner/__init__.py +++ b/addon-sdk/source/python-lib/mozrunner/__init__.py @@ -453,8 +453,10 @@ class Runner(object): if binary is None: for bin in [(program_files, 'Mozilla Firefox', 'firefox.exe'), (os.environ.get("ProgramFiles(x86)"),'Mozilla Firefox', 'firefox.exe'), - (program_files,'Nightly', 'firefox.exe'), - (os.environ.get("ProgramFiles(x86)"),'Nightly', 'firefox.exe') + (program_files, 'Nightly', 'firefox.exe'), + (os.environ.get("ProgramFiles(x86)"),'Nightly', 'firefox.exe'), + (program_files, 'Aurora', 'firefox.exe'), + (os.environ.get("ProgramFiles(x86)"),'Aurora', 'firefox.exe') ]: path = os.path.join(*bin) if os.path.isfile(path): diff --git a/addon-sdk/source/test/test-layout-change.js b/addon-sdk/source/test/addons/layout-change/main.js similarity index 98% rename from addon-sdk/source/test/test-layout-change.js rename to addon-sdk/source/test/addons/layout-change/main.js index 244336482d1e..c031afdf7286 100644 --- a/addon-sdk/source/test/test-layout-change.js +++ b/addon-sdk/source/test/addons/layout-change/main.js @@ -1,4 +1,4 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public +/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -184,4 +184,4 @@ if (require("sdk/system/xul-app").is("Fennec")) { } } -require("test").run(exports); +require("sdk/test/runner").runTestsFromModule(module); diff --git a/addon-sdk/source/test/addons/layout-change/package.json b/addon-sdk/source/test/addons/layout-change/package.json new file mode 100644 index 000000000000..5d49f26e24e8 --- /dev/null +++ b/addon-sdk/source/test/addons/layout-change/package.json @@ -0,0 +1,3 @@ +{ + "id": "test-layout-change" +} \ No newline at end of file diff --git a/addon-sdk/source/test/addons/packed/main.js b/addon-sdk/source/test/addons/packed/main.js new file mode 100644 index 000000000000..3a7a813a7200 --- /dev/null +++ b/addon-sdk/source/test/addons/packed/main.js @@ -0,0 +1,20 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { packed } = require("sdk/self"); +const url = require("sdk/url"); + +exports["test self.packed"] = function (assert) { + assert.ok(packed, "require('sdk/self').packed is correct"); +} + +exports["test url.toFilename"] = function (assert) { + assert.throws( + function() { url.toFilename(module.uri); }, + /cannot map to filename: /, + "url.toFilename() can fail for packed XPIs"); +} + +require("sdk/test/runner").runTestsFromModule(module); diff --git a/addon-sdk/source/test/addons/packed/package.json b/addon-sdk/source/test/addons/packed/package.json new file mode 100644 index 000000000000..65190c13c2fd --- /dev/null +++ b/addon-sdk/source/test/addons/packed/package.json @@ -0,0 +1,4 @@ +{ + "id": "test-url", + "unpack": false +} \ No newline at end of file diff --git a/addon-sdk/source/test/addons/private-browsing-supported/main.js b/addon-sdk/source/test/addons/private-browsing-supported/main.js new file mode 100644 index 000000000000..77a3e5644f8e --- /dev/null +++ b/addon-sdk/source/test/addons/private-browsing-supported/main.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 { isPrivateBrowsingSupported } = require('sdk/self'); + +exports.testIsPrivateBrowsingTrue = function(assert) { + assert.ok(isPrivateBrowsingSupported, + 'isPrivateBrowsingSupported property is true'); +}; + +require('sdk/test/runner').runTestsFromModule(module); diff --git a/addon-sdk/source/test/addons/private-browsing-supported/package.json b/addon-sdk/source/test/addons/private-browsing-supported/package.json new file mode 100644 index 000000000000..5b1d40b5b722 --- /dev/null +++ b/addon-sdk/source/test/addons/private-browsing-supported/package.json @@ -0,0 +1,6 @@ +{ + "id": "private-browsing-mode-test@jetpack", + "permissions": { + "private-browsing": true + } +} diff --git a/addon-sdk/source/test/addons/unpacked/main.js b/addon-sdk/source/test/addons/unpacked/main.js new file mode 100644 index 000000000000..a9caf7078af5 --- /dev/null +++ b/addon-sdk/source/test/addons/unpacked/main.js @@ -0,0 +1,18 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { packed } = require("sdk/self"); +const url = require("sdk/url"); + +exports["test self.packed"] = function (assert) { + assert.ok(!packed, "require('sdk/self').packed is correct"); +} + +exports["test url.toFilename"] = function (assert) { + assert.ok(/.*main\.js$/.test(url.toFilename(module.uri)), + "url.toFilename() on resource: URIs should work"); +} + +require("sdk/test/runner").runTestsFromModule(module); diff --git a/addon-sdk/source/test/addons/unpacked/package.json b/addon-sdk/source/test/addons/unpacked/package.json new file mode 100644 index 000000000000..9ac9eb2a9709 --- /dev/null +++ b/addon-sdk/source/test/addons/unpacked/package.json @@ -0,0 +1,4 @@ +{ + "id": "test-url", + "unpack": true +} \ No newline at end of file diff --git a/addon-sdk/source/test/commonjs-test-adapter/asserts.js b/addon-sdk/source/test/commonjs-test-adapter/asserts.js index 8618ace538d6..c0d0e1ca5115 100644 --- a/addon-sdk/source/test/commonjs-test-adapter/asserts.js +++ b/addon-sdk/source/test/commonjs-test-adapter/asserts.js @@ -4,7 +4,7 @@ "use strict"; -const AssertBase = require("test/assert").Assert; +const AssertBase = require("sdk/test/assert").Assert; /** * Generates custom assertion constructors that may be bundled with a test diff --git a/addon-sdk/source/test/modules/tiger.js b/addon-sdk/source/test/modules/tiger.js index e332deb712b2..80479d0199e3 100644 --- a/addon-sdk/source/test/modules/tiger.js +++ b/addon-sdk/source/test/modules/tiger.js @@ -4,5 +4,5 @@ define(function (require, exports) { exports.name = 'tiger'; - exports.type = require('modules/types/cat').type; + exports.type = require('./types/cat').type; }); diff --git a/addon-sdk/source/test/private-browsing/global.js b/addon-sdk/source/test/private-browsing/global.js index 527501cad429..c4fa15190270 100644 --- a/addon-sdk/source/test/private-browsing/global.js +++ b/addon-sdk/source/test/private-browsing/global.js @@ -47,10 +47,14 @@ exports.testGetIsActive = function (test) { test.assertEqual(pb.isActive, false, "private-browsing.isActive is correct without modifying PB service"); + test.assertEqual(pb.isPrivate(), false, + "private-browsing.sPrivate() is correct without modifying PB service"); pb.once("start", function() { test.assert(pb.isActive, "private-browsing.isActive is correct after modifying PB service"); + test.assert(pb.isPrivate(), + "private-browsing.sPrivate() is correct after modifying PB service"); // Switch back to normal mode. pb.deactivate(); }); @@ -59,6 +63,8 @@ exports.testGetIsActive = function (test) { pb.once("stop", function() { test.assert(!pb.isActive, "private-browsing.isActive is correct after modifying PB service"); + test.assert(!pb.isPrivate(), + "private-browsing.sPrivate() is correct after modifying PB service"); test.done(); }); }; @@ -72,6 +78,8 @@ exports.testStart = function(test) { 'private mode is active when "start" event is emitted'); test.assert(pb.isActive, '`isActive` is `true` when "start" event is emitted'); + test.assert(pb.isPrivate(), + '`isPrivate` is `true` when "start" event is emitted'); pb.removeListener("start", onStart); deactivate(function() test.done()); }); @@ -86,6 +94,8 @@ exports.testStop = function(test) { "private mode is disabled when stop event is emitted"); test.assertEqual(pb.isActive, false, "`isActive` is `false` when stop event is emitted"); + test.assertEqual(pb.isPrivate(), false, + "`isPrivate()` is `false` when stop event is emitted"); test.done(); }); pb.activate(); @@ -106,6 +116,8 @@ exports.testBothListeners = function(test) { "private mode is disabled when stop event is emitted"); test.assertEqual(pb.isActive, false, "`isActive` is `false` when stop event is emitted"); + test.assertEqual(pb.isPrivate(), false, + "`isPrivate()` is `false` when stop event is emitted"); pb.on("start", finish); pb.removeListener("start", onStart); @@ -121,6 +133,8 @@ exports.testBothListeners = function(test) { "private mode is active when start event is emitted"); test.assert(pb.isActive, "`isActive` is `true` when start event is emitted"); + test.assert(pb.isPrivate(), + "`isPrivate()` is `true` when start event is emitted"); pb.on("stop", onStop); pb.deactivate(); @@ -137,6 +151,8 @@ exports.testBothListeners = function(test) { "private mode is active when start event is emitted"); test.assert(pb.isActive, "`isActive` is `true` when start event is emitted"); + test.assert(pb.isPrivate(), + "`isPrivate()` is `true` when start event is emitted"); pb.removeListener("start", finish); pb.removeListener("stop", onStop); @@ -145,6 +161,7 @@ exports.testBothListeners = function(test) { pb.once("stop", function () { test.assertEqual(pbUtils.getMode(), false); test.assertEqual(pb.isActive, false); + test.assertEqual(pb.isPrivate(), false); test.done(); }); diff --git a/addon-sdk/source/test/private-browsing/helper.js b/addon-sdk/source/test/private-browsing/helper.js index 0ffe011c84f1..6a032fbcfa6a 100644 --- a/addon-sdk/source/test/private-browsing/helper.js +++ b/addon-sdk/source/test/private-browsing/helper.js @@ -7,11 +7,15 @@ let { Cc,Ci } = require('chrome'); const unload = require("sdk/system/unload"); const { Loader } = require('sdk/test/loader'); const { windows: windowsIterator } = require("sdk/window/utils"); -const windows = require("windows").browserWindows; +const windows = require("sdk/windows").browserWindows; let { loader } = LoaderWithHookedConsole(); const pb = loader.require('sdk/private-browsing'); const pbUtils = loader.require('sdk/private-browsing/utils'); +const { getOwnerWindow } = require('sdk/private-browsing/window/utils'); + +require('sdk/tabs/utils'); +require('sdk/windows'); function LoaderWithHookedConsole() { let errors = []; @@ -40,6 +44,8 @@ function deactivate(callback) { } exports.deactivate = deactivate; +exports.loader = loader; exports.pb = pb; exports.pbUtils = pbUtils; +exports.getOwnerWindow = getOwnerWindow; exports.LoaderWithHookedConsole = LoaderWithHookedConsole; diff --git a/addon-sdk/source/test/private-browsing/tabs.js b/addon-sdk/source/test/private-browsing/tabs.js new file mode 100644 index 000000000000..7af35588fdde --- /dev/null +++ b/addon-sdk/source/test/private-browsing/tabs.js @@ -0,0 +1,24 @@ +'use strict'; + +const { Ci } = require('chrome'); +const { pb, pbUtils, loader: pbLoader, getOwnerWindow } = require('./helper'); + +exports.testIsPrivateOnTab = function(test) { + const { openTab, closeTab } = pbLoader.require('sdk/tabs/utils'); + + let window = pbLoader.require('sdk/windows').browserWindows.activeWindow; + let chromeWindow = pbLoader.require('sdk/private-browsing/window/utils').getOwnerWindow(window); + test.assert(chromeWindow instanceof Ci.nsIDOMWindow, 'associated window is found'); + test.assert(!pb.isPrivate(chromeWindow), 'the top level window is not private'); + + let rawTab = openTab(chromeWindow, 'data:text/html,

    Hi!

    ', { + private: true + }); + + // test that the tab is private + test.assert(rawTab.browser.docShell.QueryInterface(Ci.nsILoadContext).usePrivateBrowsing); + test.assert(pb.isPrivate(rawTab.browser.contentWindow)); + test.assert(pb.isPrivate(rawTab.browser)); + + closeTab(rawTab); +} diff --git a/addon-sdk/source/test/private-browsing/windows.js b/addon-sdk/source/test/private-browsing/windows.js index 472e7fd21853..ff78cf00240b 100644 --- a/addon-sdk/source/test/private-browsing/windows.js +++ b/addon-sdk/source/test/private-browsing/windows.js @@ -4,9 +4,11 @@ 'use strict'; const { pb, pbUtils } = require('./helper'); -const { openDialog } = require('window/utils'); +const { openDialog } = require('sdk/window/utils'); +const { isPrivate } = require('sdk/private-browsing'); +const { browserWindows: windows } = require('sdk/windows'); -exports["test Per Window Private Browsing getter"] = function(assert, done) { +exports.testPerWindowPrivateBrowsingGetter = function(assert, done) { let win = openDialog({ private: true }); @@ -23,8 +25,30 @@ exports["test Per Window Private Browsing getter"] = function(assert, done) { assert.equal(pb.isActive, false, 'PB mode is not active'); done(); }, false); + win.close(); }, false); } +exports.testIsPrivateOnWindowOn = function(assert, done) { + windows.open({ + private: true, + onOpen: function(window) { + assert.equal(isPrivate(window), true, 'isPrivate for a window is true when it should be'); + assert.equal(isPrivate(window.tabs[0]), true, 'isPrivate for a tab is false when it should be'); + window.close(done); + } + }); +} + +exports.testIsPrivateOnWindowOff = function(assert, done) { + windows.open({ + onOpen: function(window) { + assert.equal(isPrivate(window), false, 'isPrivate for a window is false when it should be'); + assert.equal(isPrivate(window.tabs[0]), false, 'isPrivate for a tab is false when it should be'); + window.close(done); + } + }) +} + require("test").run(exports); diff --git a/addon-sdk/source/test/tabs/test-firefox-tabs.js b/addon-sdk/source/test/tabs/test-firefox-tabs.js index 8b91d10cb57e..5c7caf0e4475 100644 --- a/addon-sdk/source/test/tabs/test-firefox-tabs.js +++ b/addon-sdk/source/test/tabs/test-firefox-tabs.js @@ -918,7 +918,7 @@ exports['test ready event on new window tab'] = function(test) { exports['test unique tab ids'] = function(test) { test.waitUntilDone(); - var windows = require('windows').browserWindows, + var windows = require('sdk/windows').browserWindows, tabIds = {}, win1, win2; let steps = [ diff --git a/addon-sdk/source/test/test-addon-page.js b/addon-sdk/source/test/test-addon-page.js index 2ef7fc9ccbd3..9117b5ed9619 100644 --- a/addon-sdk/source/test/test-addon-page.js +++ b/addon-sdk/source/test/test-addon-page.js @@ -21,7 +21,7 @@ function isChromeVisible(window) { exports['test that add-on page has no chrome'] = function(assert, done) { let loader = Loader(module); - loader.require('addon-kit/addon-page'); + loader.require('sdk/addon-page'); let window = windows.activeBrowserWindow; let tab = openTab(window, uri); @@ -44,7 +44,7 @@ exports['test that add-on page has no chrome'] = function(assert, done) { exports['test that add-on page with hash has no chrome'] = function(assert, done) { let loader = Loader(module); - loader.require('addon-kit/addon-page'); + loader.require('sdk/addon-page'); let window = windows.activeBrowserWindow; let tab = openTab(window, uri + "#foo"); @@ -67,7 +67,7 @@ exports['test that add-on page with hash has no chrome'] = function(assert, done exports['test that add-on page with querystring has no chrome'] = function(assert, done) { let loader = Loader(module); - loader.require('addon-kit/addon-page'); + loader.require('sdk/addon-page'); let window = windows.activeBrowserWindow; let tab = openTab(window, uri + '?foo=bar'); @@ -90,7 +90,7 @@ exports['test that add-on page with querystring has no chrome'] = function(asser exports['test that add-on page with hash and querystring has no chrome'] = function(assert, done) { let loader = Loader(module); - loader.require('addon-kit/addon-page'); + loader.require('sdk/addon-page'); let window = windows.activeBrowserWindow; let tab = openTab(window, uri + '#foo?foo=bar'); @@ -113,7 +113,7 @@ exports['test that add-on page with hash and querystring has no chrome'] = funct exports['test that malformed uri is not an addon-page'] = function(assert, done) { let loader = Loader(module); - loader.require('addon-kit/addon-page'); + loader.require('sdk/addon-page'); let window = windows.activeBrowserWindow; let tab = openTab(window, uri + 'anguage'); @@ -146,4 +146,4 @@ exports['test that add-on pages are closed on unload'] = function(assert, done) }); }; -require('sdk/test').run(exports); +require('test').run(exports); diff --git a/addon-sdk/source/test/test-context-menu.js b/addon-sdk/source/test/test-context-menu.js index 17de24db9598..1ca3877d8797 100644 --- a/addon-sdk/source/test/test-context-menu.js +++ b/addon-sdk/source/test/test-context-menu.js @@ -1360,7 +1360,7 @@ exports.testMultipleModulesOrderOverflow = function (test) { let loader0 = test.newLoader(); let loader1 = test.newLoader(); - let prefs = loader0.loader.require("preferences-service"); + let prefs = loader0.loader.require("sdk/preferences/service"); prefs.set(OVERFLOW_THRESH_PREF, 0); // Use each module to add an item, then unload each module in turn. @@ -1401,7 +1401,7 @@ exports.testMultipleModulesOverflowHidden = function (test) { let loader0 = test.newLoader(); let loader1 = test.newLoader(); - let prefs = loader0.loader.require("preferences-service"); + let prefs = loader0.loader.require("sdk/preferences/service"); prefs.set(OVERFLOW_THRESH_PREF, 0); // Use each module to add an item, then unload each module in turn. @@ -1426,7 +1426,7 @@ exports.testMultipleModulesOverflowHidden2 = function (test) { let loader0 = test.newLoader(); let loader1 = test.newLoader(); - let prefs = loader0.loader.require("preferences-service"); + let prefs = loader0.loader.require("sdk/preferences/service"); prefs.set(OVERFLOW_THRESH_PREF, 0); // Use each module to add an item, then unload each module in turn. @@ -1450,7 +1450,7 @@ exports.testOverflowIgnoresHidden = function (test) { test = new TestHelper(test); let loader = test.newLoader(); - let prefs = loader.loader.require("preferences-service"); + let prefs = loader.loader.require("sdk/preferences/service"); prefs.set(OVERFLOW_THRESH_PREF, 2); let allItems = [ @@ -1481,7 +1481,7 @@ exports.testOverflowIgnoresHiddenMultipleModules1 = function (test) { let loader0 = test.newLoader(); let loader1 = test.newLoader(); - let prefs = loader0.loader.require("preferences-service"); + let prefs = loader0.loader.require("sdk/preferences/service"); prefs.set(OVERFLOW_THRESH_PREF, 2); let allItems = [ @@ -1516,7 +1516,7 @@ exports.testOverflowIgnoresHiddenMultipleModules2 = function (test) { let loader0 = test.newLoader(); let loader1 = test.newLoader(); - let prefs = loader0.loader.require("preferences-service"); + let prefs = loader0.loader.require("sdk/preferences/service"); prefs.set(OVERFLOW_THRESH_PREF, 2); let allItems = [ @@ -1551,7 +1551,7 @@ exports.testOverflowIgnoresHiddenMultipleModules3 = function (test) { let loader0 = test.newLoader(); let loader1 = test.newLoader(); - let prefs = loader0.loader.require("preferences-service"); + let prefs = loader0.loader.require("sdk/preferences/service"); prefs.set(OVERFLOW_THRESH_PREF, 2); let allItems = [ @@ -1585,7 +1585,7 @@ exports.testOverflowTransition = function (test) { test = new TestHelper(test); let loader = test.newLoader(); - let prefs = loader.loader.require("preferences-service"); + let prefs = loader.loader.require("sdk/preferences/service"); prefs.set(OVERFLOW_THRESH_PREF, 2); let pItems = [ @@ -1718,7 +1718,6 @@ exports.testMenuCommand = function (test) { let topMenu = new loader.cm.Menu({ label: "top menu", contentScript: 'self.on("click", function (node, data) {' + - ' let Ci = Components["interfaces"];' + ' self.postMessage({' + ' tagName: node.tagName,' + ' data: data' + @@ -1798,7 +1797,6 @@ exports.testItemClick = function (test) { label: "item", data: "item data", contentScript: 'self.on("click", function (node, data) {' + - ' let Ci = Components["interfaces"];' + ' self.postMessage({' + ' tagName: node.tagName,' + ' data: data' + @@ -1846,7 +1844,6 @@ exports.testMenuClick = function (test) { let topMenu = new loader.cm.Menu({ label: "top menu", contentScript: 'self.on("click", function (node, data) {' + - ' let Ci = Components["interfaces"];' + ' self.postMessage({' + ' tagName: node.tagName,' + ' data: data' + @@ -2480,6 +2477,124 @@ exports.testAlreadyOpenIframe = function (test) { }; +// Tests that a missing label throws an exception +exports.testItemNoLabel = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + try { + new loader.cm.Item({}); + test.assert(false, "Should have seen exception"); + } + catch (e) { + test.assert(true, "Should have seen exception"); + } + + try { + new loader.cm.Item({ label: null }); + test.assert(false, "Should have seen exception"); + } + catch (e) { + test.assert(true, "Should have seen exception"); + } + + try { + new loader.cm.Item({ label: undefined }); + test.assert(false, "Should have seen exception"); + } + catch (e) { + test.assert(true, "Should have seen exception"); + } + + try { + new loader.cm.Item({ label: "" }); + test.assert(false, "Should have seen exception"); + } + catch (e) { + test.assert(true, "Should have seen exception"); + } + + test.done(); +} + + +// Tests that items can have an empty data property +exports.testItemNoData = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + function checkData(data) { + test.assertEqual(data, undefined, "Data should be undefined"); + } + + let item1 = new loader.cm.Item({ + label: "item 1", + contentScript: 'self.on("click", function(node, data) self.postMessage(data))', + onMessage: checkData + }); + let item2 = new loader.cm.Item({ + label: "item 2", + data: null, + contentScript: 'self.on("click", function(node, data) self.postMessage(data))', + onMessage: checkData + }); + let item3 = new loader.cm.Item({ + label: "item 3", + data: undefined, + contentScript: 'self.on("click", function(node, data) self.postMessage(data))', + onMessage: checkData + }); + + test.assertEqual(item1.data, undefined, "Should be no defined data"); + test.assertEqual(item2.data, null, "Should be no defined data"); + test.assertEqual(item3.data, undefined, "Should be no defined data"); + + test.showMenu(null, function (popup) { + test.checkMenu([item1, item2, item3], [], []); + + let itemElt = test.getItemElt(popup, item1); + itemElt.click(); + + test.hideMenu(function() { + test.showMenu(null, function (popup) { + let itemElt = test.getItemElt(popup, item2); + itemElt.click(); + + test.hideMenu(function() { + test.showMenu(null, function (popup) { + let itemElt = test.getItemElt(popup, item3); + itemElt.click(); + + test.done(); + }); + }); + }); + }); + }); +} + + +// Tests that items without an image don't attempt to show one +exports.testItemNoImage = function (test) { + test = new TestHelper(test); + let loader = test.newLoader(); + + let item1 = new loader.cm.Item({ label: "item 1" }); + let item2 = new loader.cm.Item({ label: "item 2", image: null }); + let item3 = new loader.cm.Item({ label: "item 3", image: undefined }); + + test.assertEqual(item1.image, undefined, "Should be no defined image"); + test.assertEqual(item2.image, null, "Should be no defined image"); + test.assertEqual(item3.image, undefined, "Should be no defined image"); + + test.showMenu(null, function (popup) { + test.checkMenu([item1, item2, item3], [], []); + + test.done(); + }); +} + + // Test image support. exports.testItemImage = function (test) { test = new TestHelper(test); @@ -2490,6 +2605,8 @@ exports.testItemImage = function (test) { let menu = new loader.cm.Menu({ label: "menu", image: imageURL, items: [ loader.cm.Item({ label: "subitem" }) ]}); + test.assertEqual(item.image, imageURL, "Should have set the image correctly"); + test.assertEqual(menu.image, imageURL, "Should have set the image correctly"); test.showMenu(null, function (popup) { test.checkMenu([item, menu], [], []); @@ -2497,10 +2614,14 @@ exports.testItemImage = function (test) { let imageURL2 = require("sdk/self").data.url("dummy.ico"); item.image = imageURL2; menu.image = imageURL2; + test.assertEqual(item.image, imageURL2, "Should have set the image correctly"); + test.assertEqual(menu.image, imageURL2, "Should have set the image correctly"); test.checkMenu([item, menu], [], []); item.image = null; menu.image = null; + test.assertEqual(item.image, null, "Should have set the image correctly"); + test.assertEqual(menu.image, null, "Should have set the image correctly"); test.checkMenu([item, menu], [], []); test.done(); diff --git a/addon-sdk/source/test/test-httpd.js b/addon-sdk/source/test/test-httpd.js index eeb9dd4064b9..cbf261e5226a 100644 --- a/addon-sdk/source/test/test-httpd.js +++ b/addon-sdk/source/test/test-httpd.js @@ -5,6 +5,13 @@ const port = 8099; const file = require("sdk/io/file"); const { pathFor } = require("sdk/system"); +const { Loader } = require("sdk/test/loader"); +const options = require("@test/options"); + +const loader = Loader(module); +const httpd = loader.require("sdk/test/httpd"); +if (options.parseable || options.verbose) + loader.sandbox("sdk/test/httpd").DEBUG = true; exports.testBasicHTTPServer = function(test) { // Use the profile directory for the temporary file as that will be deleted @@ -16,8 +23,7 @@ exports.testBasicHTTPServer = function(test) { fileStream.write(content); fileStream.close(); - let { startServerAsync } = require("sdk/test/httpd"); - let srv = startServerAsync(port, basePath); + let srv = httpd.startServerAsync(port, basePath); test.waitUntilDone(); @@ -41,8 +47,7 @@ exports.testBasicHTTPServer = function(test) { exports.testDynamicServer = function (test) { let content = "This is the HTTPD test file.\n"; - let { startServerAsync } = require("sdk/test/httpd"); - let srv = startServerAsync(port); + let srv = httpd.startServerAsync(port); // See documentation here: //http://doxygen.db48x.net/mozilla/html/interfacensIHttpServer.html#a81fc7e7e29d82aac5ce7d56d0bedfb3a diff --git a/addon-sdk/source/test/test-indexed-db.js b/addon-sdk/source/test/test-indexed-db.js index dffcdf85ab9a..beead7e3aaee 100644 --- a/addon-sdk/source/test/test-indexed-db.js +++ b/addon-sdk/source/test/test-indexed-db.js @@ -4,14 +4,14 @@ "use strict"; -let xulApp = require("api-utils/xul-app"); +let xulApp = require("sdk/system/xul-app"); if (xulApp.versionInRange(xulApp.platformVersion, "16.0a1", "*")) { new function tests() { const { indexedDB, IDBKeyRange, DOMException, IDBCursor, IDBTransaction, IDBOpenDBRequest, IDBVersionChangeEvent, IDBDatabase, IDBFactory, IDBIndex, IDBObjectStore, IDBRequest - } = require("indexed-db"); + } = require("sdk/indexed-db"); exports["test indexedDB is frozen"] = function(assert){ let original = indexedDB.open; diff --git a/addon-sdk/source/test/test-loader.js b/addon-sdk/source/test/test-loader.js index 0dcf44af89d6..2af782499b0b 100644 --- a/addon-sdk/source/test/test-loader.js +++ b/addon-sdk/source/test/test-loader.js @@ -6,10 +6,10 @@ let { Loader, main, unload, parseStack } = require('toolkit/loader'); -exports['test dependency cycles'] = function(assert) { - let uri = module.uri.substr(0, module.uri.lastIndexOf('/')) + - '/fixtures/loader/cycles/' +let root = module.uri.substr(0, module.uri.lastIndexOf('/')) +exports['test dependency cycles'] = function(assert) { + let uri = root + '/fixtures/loader/cycles/'; let loader = Loader({ paths: { '': uri } }); let program = main(loader, 'main'); @@ -19,12 +19,10 @@ exports['test dependency cycles'] = function(assert) { assert.equal(program.c.main, program, 'module `c` gets correct `main`'); unload(loader); -}; +} exports['test syntax errors'] = function(assert) { - let uri = module.uri.substr(0, module.uri.lastIndexOf('/')) + - '/fixtures/loader/syntax-error/'; - + let uri = root + '/fixtures/loader/syntax-error/'; let loader = Loader({ paths: { '': uri } }); try { @@ -43,12 +41,10 @@ exports['test syntax errors'] = function(assert) { } finally { unload(loader); } -}; +} exports['test missing module'] = function(assert) { - let uri = module.uri.substr(0, module.uri.lastIndexOf('/')) + - '/fixtures/loader/missing/' - + let uri = root + '/fixtures/loader/missing/' let loader = Loader({ paths: { '': uri } }); try { @@ -75,8 +71,7 @@ exports['test missing module'] = function(assert) { } exports['test exceptions in modules'] = function(assert) { - let uri = module.uri.substr(0, module.uri.lastIndexOf('/')) + - '/fixtures/loader/exceptions/' + let uri = root + '/fixtures/loader/exceptions/' let loader = Loader({ paths: { '': uri } }); @@ -112,9 +107,7 @@ exports['test exceptions in modules'] = function(assert) { } exports['test early errors in module'] = function(assert) { - let uri = module.uri.substr(0, module.uri.lastIndexOf('/')) + - '/fixtures/loader/errors/' - + let uri = root + '/fixtures/loader/errors/'; let loader = Loader({ paths: { '': uri } }); try { diff --git a/addon-sdk/source/test/test-net-url.js b/addon-sdk/source/test/test-net-url.js index 302c402ca3dd..986ea5ebf180 100644 --- a/addon-sdk/source/test/test-net-url.js +++ b/addon-sdk/source/test/test-net-url.js @@ -5,7 +5,7 @@ "use strict"; const { readURI, readURISync } = require("sdk/net/url"); -const { data } = require("self"); +const { data } = require("sdk/self"); const utf8text = "Hello, ゼロ!"; const latin1text = "Hello, ゼロ!"; diff --git a/addon-sdk/source/test/test-packaging.js b/addon-sdk/source/test/test-packaging.js index b7a96cdbd5ce..a31e92be60d3 100644 --- a/addon-sdk/source/test/test-packaging.js +++ b/addon-sdk/source/test/test-packaging.js @@ -1,15 +1,44 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + "use strict"; -var url = require("sdk/url"); -var file = require("sdk/io/file"); -var {Cm,Ci} = require("chrome"); var options = require("@loader/options"); exports.testPackaging = function(test) { - test.assertEqual(options.metadata.description, "Add-on development made easy.", "packaging metadata should be available"); + try { + options.metadata.description = 'new description'; + test.fail('should not have been able to set options.metadata property'); + } + catch (e) {} + + test.assertEqual(options.metadata.description, + "Add-on development made easy.", + "packaging metadata should be frozen"); + + test.assertEqual(options.metadata.permissions['private-browsing'], undefined, + "private browsing metadata should be undefined"); + test.assertEqual(options.metadata['private-browsing'], undefined, + "private browsing metadata should be be frozen"); + test.assertEqual(options['private-browsing'], undefined, + "private browsing metadata should be be frozen"); + + try { + options.metadata['private-browsing'] = true; + test.fail('should not have been able to set options.metadata property'); + } + catch(e) {} + test.assertEqual(options.metadata['private-browsing'], undefined, + "private browsing metadata should be be frozen"); + + try { + options.metadata.permissions['private-browsing'] = true; + test.fail('should not have been able to set options.metadata.permissions property'); + } + catch (e) {} + test.assertEqual(options.metadata.permissions['private-browsing'], undefined, + "private browsing metadata should be be frozen"); }; diff --git a/addon-sdk/source/test/test-page-mod.js b/addon-sdk/source/test/test-page-mod.js index 77c9bd0bdf3c..6e6f40df74a7 100644 --- a/addon-sdk/source/test/test-page-mod.js +++ b/addon-sdk/source/test/test-page-mod.js @@ -12,7 +12,7 @@ const { Cc, Ci } = require("chrome"); const { open, getFrames, getMostRecentBrowserWindow } = require('sdk/window/utils'); const windowUtils = require('sdk/deprecated/window-utils'); const { getTabContentWindow, getActiveTab, openTab, closeTab } = require('sdk/tabs/utils'); -const { data } = require('self'); +const { data } = require('sdk/self'); /* XXX This can be used to delay closing the test Firefox instance for interactive * testing or visual inspection. This test is registered first so that it runs @@ -357,6 +357,27 @@ exports.testRelatedTab = function(test) { }); }; +exports.testRelatedTabNoRequireTab = function(test) { + test.waitUntilDone(); + + let loader = Loader(module); + let tab; + let url = "data:text/html;charset=utf-8," + encodeURI("Test related worker tab 2"); + let { PageMod } = loader.require("sdk/page-mod"); + let pageMod = new PageMod({ + include: url, + onAttach: function(worker) { + test.assertEqual(worker.tab.url, url, "Worker.tab.url is valid"); + worker.tab.close(); + pageMod.destroy(); + loader.unload(); + test.done(); + } + }); + + tabs.open(url); +}; + exports.testRelatedTabNoOtherReqs = function(test) { test.waitUntilDone(); @@ -633,7 +654,7 @@ exports['test111 attachTo [frame]'] = function(test) { this.destroy(); if (++messageCount == 2) { mod.destroy(); - require('tabs').activeTab.close(function() { + require('sdk/tabs').activeTab.close(function() { test.done(); }); } @@ -792,7 +813,7 @@ exports.testPageModCssAutomaticDestroy = function(test) { test.waitUntilDone(); let loader = Loader(module); - let pageMod = loader.require("page-mod").PageMod({ + let pageMod = loader.require("sdk/page-mod").PageMod({ include: "data:*", contentStyle: "div { width: 100px!important; }" }); @@ -832,7 +853,7 @@ exports.testPageModTimeout = function(test) { test.waitUntilDone(); let tab = null let loader = Loader(module); - let { PageMod } = loader.require("page-mod"); + let { PageMod } = loader.require("sdk/page-mod"); let mod = PageMod({ include: "data:*", @@ -867,7 +888,7 @@ exports.testPageModcancelTimeout = function(test) { test.waitUntilDone(); let tab = null let loader = Loader(module); - let { PageMod } = loader.require("page-mod"); + let { PageMod } = loader.require("sdk/page-mod"); let mod = PageMod({ include: "data:*", @@ -1029,4 +1050,3 @@ if (require("sdk/system/xul-app").is("Fennec")) { } } } - diff --git a/addon-sdk/source/test/test-panel.js b/addon-sdk/source/test/test-panel.js index b1b81a05620c..540d46e26191 100644 --- a/addon-sdk/source/test/test-panel.js +++ b/addon-sdk/source/test/test-panel.js @@ -5,7 +5,7 @@ let { Cc, Ci } = require("chrome"); const { Loader } = require('sdk/test/loader'); const timer = require("sdk/timers"); -const self = require('self'); +const self = require('sdk/self'); exports["test Panel"] = function(assert, done) { const { Panel } = require('sdk/panel'); diff --git a/addon-sdk/source/test/test-plain-text-console.js b/addon-sdk/source/test/test-plain-text-console.js index ac89dcdd5555..5742a36c6197 100644 --- a/addon-sdk/source/test/test-plain-text-console.js +++ b/addon-sdk/source/test/test-plain-text-console.js @@ -4,6 +4,9 @@ const prefs = require("sdk/preferences/service"); const { id, name } = require("sdk/self"); +const { Cc, Cu, Ci } = require("chrome"); +const { loadSubScript } = Cc['@mozilla.org/moz/jssubscript-loader;1']. + getService(Ci.mozIJSSubScriptLoader); const ADDON_LOG_LEVEL_PREF = "extensions." + id + ".sdk.console.logLevel"; const SDK_LOG_LEVEL_PREF = "extensions.sdk.console.logLevel"; @@ -73,9 +76,22 @@ exports.testPlainTextConsole = function(test) { var tbLines = prints[0].split("\n"); test.assertEqual(tbLines[0], "error: " + name + ": An exception occurred."); test.assertEqual(tbLines[1], "Error: blah"); - test.assertEqual(tbLines[2], module.uri + " 71"); + test.assertEqual(tbLines[2], module.uri + " 74"); test.assertEqual(tbLines[3], "Traceback (most recent call last):"); + prints = []; + try { + loadSubScript("invalid-url", {}); + test.fail("successed in calling loadSubScript with invalid-url"); + } + catch(e) { + con.exception(e); + } + var tbLines = prints[0].split("\n"); + test.assertEqual(tbLines[0], "error: " + name + ": An exception occurred."); + test.assertEqual(tbLines[1], "Error creating URI (invalid URL scheme?)"); + test.assertEqual(tbLines[2], "Traceback (most recent call last):"); + prints = []; con.trace(); tbLines = prints[0].split("\n"); diff --git a/addon-sdk/source/test/test-private-browsing.js b/addon-sdk/source/test/test-private-browsing.js index 1fb61293d89c..e2ae41bd20cb 100644 --- a/addon-sdk/source/test/test-private-browsing.js +++ b/addon-sdk/source/test/test-private-browsing.js @@ -3,18 +3,47 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 'use strict'; -const { pb, pbUtils } = require('./private-browsing/helper'); +const { Ci } = require('chrome'); +const { pb, pbUtils, getOwnerWindow } = require('./private-browsing/helper'); const { merge } = require('sdk/util/object'); const windows = require('sdk/windows').browserWindows; const winUtils = require('sdk/window/utils'); +const { isPrivateBrowsingSupported } = require('sdk/self'); +const { is } = require('sdk/system/xul-app'); +const { isPrivate } = require('sdk/private-browsing'); // is global pb is enabled? if (pbUtils.isGlobalPBSupported) { merge(module.exports, require('./private-browsing/global')); + + exports.testGlobalOnlyOnFirefox = function(test) { + test.assert(is("Firefox"), "isGlobalPBSupported is only true on Firefox"); + } } else if (pbUtils.isWindowPBSupported) { merge(module.exports, require('./private-browsing/windows')); + + exports.testPWOnlyOnFirefox = function(test) { + test.assert(is("Firefox"), "isWindowPBSupported is only true on Firefox"); + } } +// only on Fennec +else if (pbUtils.isTabPBSupported) { + merge(module.exports, require('./private-browsing/tabs')); + + exports.testPTOnlyOnFennec = function(test) { + test.assert(is("Fennec"), "isTabPBSupported is only true on Fennec"); + } +} + +exports.testIsPrivateDefaults = function(test) { + test.assertEqual(pb.isPrivate(), false, 'undefined is not private'); + test.assertEqual(pb.isPrivate('test'), false, 'strings are not private'); + test.assertEqual(pb.isPrivate({}), false, 'random objects are not private'); + test.assertEqual(pb.isPrivate(4), false, 'numbers are not private'); + test.assertEqual(pb.isPrivate(/abc/), false, 'regex are not private'); + test.assertEqual(pb.isPrivate(function() {}), false, 'functions are not private'); +}; exports.testWindowDefaults = function(test) { test.assertEqual(windows.activeWindow.isPrivateBrowsing, false, 'window is not private browsing by default'); @@ -29,3 +58,36 @@ exports.testIsActiveDefault = function(test) { 'pb.isActive returns false when private browsing isn\'t supported'); }; +exports.testIsPrivateBrowsingFalseDefault = function(test) { + test.assertEqual(isPrivateBrowsingSupported, false, + 'usePrivateBrowsing property is false by default'); +}; + +exports.testGetOwnerWindow = function(test) { + test.waitUntilDone(); + + let window = windows.activeWindow; + let chromeWindow = getOwnerWindow(window); + test.assert(chromeWindow instanceof Ci.nsIDOMWindow, 'associated window is found'); + + window.tabs.open({ + url: 'about:blank', + private: true, // should be ignored in this case + onOpen: function(tab) { + // test that getOwnerWindow works as expected + if (is('Fennec')) { + test.assertNotStrictEqual(chromeWindow, getOwnerWindow(tab)); + test.assert(getOwnerWindow(tab) instanceof Ci.nsIDOMWindow); + } + else { + test.assertStrictEqual(chromeWindow, getOwnerWindow(tab), 'associated window is the same for window and window\'s tab'); + } + + // test that the tab is not private + // private flag should be ignored by default + test.assert(!isPrivate(tab)); + + tab.close(function() test.done()); + } + }); +}; diff --git a/addon-sdk/source/test/test-request.js b/addon-sdk/source/test/test-request.js index 250121b44403..6d1924a79abf 100644 --- a/addon-sdk/source/test/test-request.js +++ b/addon-sdk/source/test/test-request.js @@ -15,6 +15,9 @@ if (options.parseable || options.verbose) loader.sandbox("sdk/test/httpd").DEBUG = true; const { startServerAsync } = httpd; +const { Cc, Ci, Cu } = require("chrome"); +const { Services } = Cu.import("resource://gre/modules/Services.jsm"); + // Use the profile directory for the temporary files as that will be deleted // when tests are complete const basePath = pathFor("ProfD") @@ -140,6 +143,55 @@ exports.testComplexHeader = function (test) { }).get(); } +// Force Allow Third Party cookies +exports.test3rdPartyCookies = function (test) { + let srv = startServerAsync(port, basePath); + + let basename = "test-request-3rd-party-cookies.sjs"; + + // Function to handle the requests in the server + let content = function handleRequest(request, response) { + var cookiePresent = request.hasHeader("Cookie"); + // If no cookie, set it + if(!cookiePresent) { + response.setHeader("Set-Cookie", "cookie=monster;", "true"); + response.setHeader("x-jetpack-3rd-party", "false", "true"); + } else { + // We got the cookie, say so + response.setHeader("x-jetpack-3rd-party", "true", "true"); + } + + response.write("This tests 3rd party cookies."); + }.toString() + + prepareFile(basename, content); + + // Disable the 3rd party cookies + Services.prefs.setIntPref("network.cookie.cookieBehavior", 1); + + test.waitUntilDone(); + Request({ + url: "http://localhost:" + port + "/test-request-3rd-party-cookies.sjs", + onComplete: function (response) { + // Check that the server created the cookie + test.assertEqual(response.headers['Set-Cookie'], 'cookie=monster;'); + + // Check it wasn't there before + test.assertEqual(response.headers['x-jetpack-3rd-party'], 'false'); + + // Make a second request, and check that the server this time + // got the cookie + Request({ + url: "http://localhost:" + port + "/test-request-3rd-party-cookies.sjs", + onComplete: function (response) { + test.assertEqual(response.headers['x-jetpack-3rd-party'], 'true'); + srv.stop(function() test.done()); + } + }).get(); + } + }).get(); +} + exports.testSimpleJSON = function (test) { let srv = startServerAsync(port, basePath); let json = { foo: "bar" }; diff --git a/addon-sdk/source/test/test-selection.js b/addon-sdk/source/test/test-selection.js index 4a2cb80d01ed..f4e5719825df 100644 --- a/addon-sdk/source/test/test-selection.js +++ b/addon-sdk/source/test/test-selection.js @@ -14,12 +14,16 @@ const HTML = "\ const URL = "data:text/html;charset=utf-8," + encodeURIComponent(HTML); +const FRAME_HTML = "