diff --git a/addon-sdk/source/doc/dev-guide-source/cfx-tool.md b/addon-sdk/source/doc/dev-guide-source/cfx-tool.md index b0f4e9b781f..f07540b8822 100644 --- a/addon-sdk/source/doc/dev-guide-source/cfx-tool.md +++ b/addon-sdk/source/doc/dev-guide-source/cfx-tool.md @@ -20,12 +20,69 @@ commands (for example `--help`). `cfx` supports the following global options: -v, --verbose - enable lots of output -"Command-specific options" are only -applicable to a subset of the commands. +"Command-specific options" are documented alongside the commands. -## Supported Commands ## +There are five supported cfx commands: -### cfx docs ### + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ cfx docs + + Display the documentation for the SDK. +
+ cfx init + + Create a skeleton add-on as a starting point for your own add-on. +
+ cfx run + + Launch an instance of Firefox with your add-on installed. +
+ cfx test + + Runs your add-on's unit tests. +
+ cfx xpi + + Package your add-on as an XPI + file, which is the install file format for Firefox add-ons. +
+ +There are also a number of +[internal commands](dev-guide/cfx-tool.html#internal-commands), +which are more likely to be useful to SDK developers than to add-on developers. + +## cfx docs ## This command displays the documentation for the SDK. The documentation is shipped with the SDK in [Markdown](http://daringfireball.net/projects/markdown/) @@ -46,7 +103,8 @@ This command will regenerate only the HTML page you're reading. This is useful if you're iteratively editing a single file, and don't want to wait for cfx to regenerate the complete documentation tree. -### cfx init #### +## cfx init ## + Create a new directory called "my-addon", change into it, and run `cfx init`. This command will create an skeleton add-on, as a starting point for your @@ -73,14 +131,13 @@ own add-on development, with the following file structure:
-### cfx run ### - +## cfx run ## This command is used to run the add-on. Called with no options it looks for a file called `package.json` in the current directory, loads the corresponding add-on, and runs it under the version of Firefox it finds in the platform's default install path. -#### Supported Options ##### +### Supported Options #### You can point `cfx run` at a different `package.json` file using the `--pkgdir` option, and pass arguments to your add-on using the @@ -190,7 +247,7 @@ See -#### Experimental Options #### +### Experimental Options ### @@ -233,6 +290,25 @@ To launch the application, enter the following command: + + + + +
+ -o, --overload-modules + +

In early versions of the SDK, the SDK modules used by an add-on + were themselves included in the add-on. The SDK modules now ship as + part of Firefox. From Firefox 21 onwards, SDK add-ons built with + SDK 1.14 or higher will use the SDK modules that are built into Firefox, + even if the add-on includes its own copies of the SDK modules.

+

Use this flag to reverse that behavior: if this flag is set and + the add-on includes its own copies of the SDK modules, then the add-on + will use the SDK modules in the add-on, not the ones built into Firefox.

+

This flag is particularly useful for SDK developers or people working with + the development version of the SDK, who may want to run an add-on using newer + versions of the modules than than those shipping in Firefox.

+
--templatedir=TEMPLATEDIR @@ -249,7 +325,7 @@ To launch the application, enter the following command:
-#### Internal Options #### +### Internal Options ### @@ -291,8 +367,7 @@ To launch the application, enter the following command:
-### cfx test ### - +##
cfx test ## Run available tests for the specified package. Note the hyphen after "test" in the module name. @@ -310,7 +385,7 @@ See the [reference documentation for the `assert` module](modules/sdk/test/assert.html) for details. -#### Supported Options ##### +### Supported Options ### As with `cfx run` you can use options to control which host application binary version to use, and to select a profile. @@ -416,7 +491,7 @@ times. -#### Experimental Options #### +### Experimental Options ### @@ -461,16 +536,26 @@ To launch the application, enter the following command:
- --use-server + -o, --overload-modules - Run tests using a server previously started with cfx develop. +

In early versions of the SDK, the SDK modules used by an add-on + were themselves included in the add-on. The SDK modules now ship as + part of Firefox. From Firefox 21 onwards, SDK add-ons built with + SDK 1.14 or higher will use the SDK modules that are built into Firefox, + even if the add-on includes its own copies of the SDK modules.

+

Use this flag to reverse that behavior: if this flag is set and + the add-on includes its own copies of the SDK modules, then the add-on + will use the SDK modules in the add-on, not the ones built into Firefox.

+

This flag is particularly useful for SDK developers or people working with + the development version of the SDK, who may want to run an add-on using newer + versions of the modules than than those shipping in Firefox.

-#### Internal Options #### +### Internal Options ### @@ -545,8 +630,7 @@ To launch the application, enter the following command:
-### cfx xpi ### - +## cfx xpi ## This tool is used to package your add-on as an [XPI](https://developer.mozilla.org/en/XPI) file, which is the install file format for Mozilla add-ons. @@ -557,7 +641,7 @@ the current directory and creates the corresponding XPI file. Once you have built an XPI file you can distribute your add-on by submitting it to [addons.mozilla.org](http://addons.mozilla.org). -#### updateURL and updateLink #### +### updateURL and updateLink ### If you choose to host the XPI yourself you should enable the host application to find new versions of your add-on. @@ -597,7 +681,7 @@ So if we run the following command: * an RDF file which embeds `https://example.com/addon/latest` as the value of `updateLink`. -#### Supported Options #### +### Supported Options ### As with `cfx run` you can point `cfx` at a different `package.json` file using the `--pkgdir` option. You can also embed arguments in the XPI using the @@ -675,7 +759,7 @@ add-on whenever it is run. -#### Experimental Options #### +### Experimental Options ### @@ -699,7 +783,7 @@ add-on whenever it is run.
-#### Internal Options #### +### Internal Options ### @@ -721,35 +805,7 @@ add-on whenever it is run.
-## Experimental Commands ## - -### cfx develop ### - -This initiates an instance of a host application in development mode, -and allows you to pipe commands into it from another shell without -having to constantly restart it. Aside from convenience, for SDK -Platform developers this has the added benefit of making it easier to -detect leaks. - -For example, in shell A, type: - -
-  cfx develop
-
- -In shell B, type: - -
-  cfx test --use-server
-
- -This will send `cfx test --use-server` output to shell A. If you repeat the -command in shell B, `cfx test --use-server` output will appear again in shell A -without restarting the host application. - -`cfx develop` doesn't take any options. - -## Internal Commands ## +## Internal Commands ## ### cfx sdocs ### diff --git a/addon-sdk/source/doc/module-source/sdk/request.md b/addon-sdk/source/doc/module-source/sdk/request.md index cc439caec69..3f87aefb361 100644 --- a/addon-sdk/source/doc/module-source/sdk/request.md +++ b/addon-sdk/source/doc/module-source/sdk/request.md @@ -6,20 +6,22 @@ The `request` module lets you make simple yet powerful network requests. @class -The `Request` object is used to make `GET`, `POST` or `PUT` network requests. -It is constructed with a URL to which the request is sent. Optionally the user -may specify a collection of headers and content to send alongside the request -and a callback which will be executed once the request completes. +The `Request` object is used to make `GET`, `POST`, `PUT`, or `HEAD` +network requests. It is constructed with a URL to which the request is sent. +Optionally the user may specify a collection of headers and content to send +alongside the request and a callback which will be executed once the +request completes. Once a `Request` object has been created a `GET` request can be executed by calling its `get()` method, a `POST` request by calling its `post()` method, -or a `PUT` request by calling its `put()` method. +a `PUT` request by calling its `put()` method, or a `HEAD` request by calling +its `head()` method. When the server completes the request, the `Request` object emits a "complete" event. Registered event listeners are passed a `Response` object. -Each `Request` object is designed to be used once. Once `GET`, `POST` or `PUT` -are called, attempting to call either will throw an error. +Each `Request` object is designed to be used once. Once `GET`, `POST`, `PUT`, +or `HEAD` are called, attempting to call either will throw an error. Since the request is not being made by any particular website, requests made here are not subject to the same-domain restriction that requests made in web @@ -150,6 +152,12 @@ Make a `PUT` request. @returns {Request} + +@method +Make a `HEAD` request. +@returns {Request} + + @event The `Request` object emits this event when the request has completed and a @@ -165,8 +173,8 @@ Listener functions are passed the response to the request as a `Response` object @class The Response object contains the response to a network request issued using a -`Request` object. It is returned by the `get()`, `post()` or `put()` method of a -`Request` object. +`Request` object. It is returned by the `get()`, `post()`, `put()`, or `head()` +method of a `Request` object. All members of a `Response` object are read-only. diff --git a/addon-sdk/source/lib/sdk/addon/runner.js b/addon-sdk/source/lib/sdk/addon/runner.js index 3429e23f553..62fc97dfeff 100644 --- a/addon-sdk/source/lib/sdk/addon/runner.js +++ b/addon-sdk/source/lib/sdk/addon/runner.js @@ -11,11 +11,12 @@ module.metadata = { const { Cc, Ci } = require('chrome'); const { descriptor, Sandbox, evaluate, main, resolveURI } = require('toolkit/loader'); const { once } = require('../system/events'); -const { exit, env, staticArgs, name } = require('../system'); +const { exit, env, staticArgs } = require('../system'); const { when: unload } = require('../system/unload'); const { loadReason } = require('../self'); const { rootURI } = require("@loader/options"); const globals = require('../system/globals'); +const xulApp = require('../system/xul-app'); const appShellService = Cc['@mozilla.org/appshell/appShellService;1']. getService(Ci.nsIAppShellService); @@ -23,13 +24,20 @@ const NAME2TOPIC = { 'Firefox': 'sessionstore-windows-restored', 'Fennec': 'sessionstore-windows-restored', 'SeaMonkey': 'sessionstore-windows-restored', - 'Thunderbird': 'mail-startup-done', - '*': 'final-ui-startup' + 'Thunderbird': 'mail-startup-done' }; +// Set 'final-ui-startup' as default topic for unknown applications +let appStartup = 'final-ui-startup'; + // Gets the topic that fit best as application startup event, in according with // the current application (e.g. Firefox, Fennec, Thunderbird...) -const APP_STARTUP = NAME2TOPIC[name] || NAME2TOPIC['*']; +for (let name of Object.keys(NAME2TOPIC)) { + if (xulApp.is(name)) { + appStartup = NAME2TOPIC[name]; + break; + } +} // Initializes default preferences function setDefaultPrefs(prefsURI) { @@ -66,7 +74,7 @@ function definePseudo(loader, id, exports) { } function wait(reason, options) { - once(APP_STARTUP, function() { + once(appStartup, function() { startup(null, options); }); } @@ -145,10 +153,10 @@ function run(options) { program.main({ loadReason: loadReason, - staticArgs: staticArgs - }, { + staticArgs: staticArgs + }, { print: function print(_) { dump(_ + '\n') }, - quit: exit + quit: exit }); } } catch (error) { diff --git a/addon-sdk/source/lib/sdk/deprecated/tab-browser.js b/addon-sdk/source/lib/sdk/deprecated/tab-browser.js index 738ad88941c..a9c8da828d3 100644 --- a/addon-sdk/source/lib/sdk/deprecated/tab-browser.js +++ b/addon-sdk/source/lib/sdk/deprecated/tab-browser.js @@ -566,7 +566,7 @@ ModuleTabTracker.prototype = { _TAB_EVENTS: ["TabOpen", "TabClose", "TabSelect", "DOMContentLoaded", "load", "MozAfterPaint"], _safeTrackTab: function safeTrackTab(tab) { - tab.addEventListener("load", this, false); + tab.linkedBrowser.addEventListener("load", this, true); tab.linkedBrowser.addEventListener("MozAfterPaint", this, false); this._tabs.push(tab); try { @@ -576,7 +576,7 @@ ModuleTabTracker.prototype = { } }, _safeUntrackTab: function safeUntrackTab(tab) { - tab.removeEventListener("load", this, false); + tab.linkedBrowser.removeEventListener("load", this, true); tab.linkedBrowser.removeEventListener("MozAfterPaint", this, false); var index = this._tabs.indexOf(tab); if (index == -1) @@ -617,7 +617,11 @@ ModuleTabTracker.prototype = { } }, _safeLoad: function safeLoad(event) { - let tab = event.target; + let win = event.currentTarget.ownerDocument.defaultView; + let tabIndex = win.gBrowser.getBrowserIndexForDocument(event.target); + if (tabIndex == -1) + return; + let tab = win.gBrowser.tabContainer.getItemAtIndex(tabIndex); let index = this._tabs.indexOf(tab); if (index == -1) console.error("internal error: tab not found"); diff --git a/addon-sdk/source/lib/sdk/deprecated/window-utils.js b/addon-sdk/source/lib/sdk/deprecated/window-utils.js index 554c1513d8e..78758ecea1c 100644 --- a/addon-sdk/source/lib/sdk/deprecated/window-utils.js +++ b/addon-sdk/source/lib/sdk/deprecated/window-utils.js @@ -11,6 +11,7 @@ const { Cc, Ci } = require('chrome'); const { EventEmitter } = require('../deprecated/events'); const { Trait } = require('../deprecated/traits'); const { when } = require('../system/unload'); +const events = require('../system/events'); const { getInnerId, getOuterId, windows, isDocumentLoaded, isBrowser, getMostRecentBrowserWindow, getMostRecentWindow } = require('../window/utils'); const errors = require('../deprecated/errors'); @@ -68,6 +69,8 @@ function WindowTracker(delegate) { for each (let window in getWindows()) this._regWindow(window); windowWatcher.registerNotification(this); + this._onToplevelWindowReady = this._onToplevelWindowReady.bind(this); + events.on('toplevel-window-ready', this._onToplevelWindowReady); require('../system/unload').ensure(this); @@ -116,6 +119,7 @@ WindowTracker.prototype = { unload: function unload() { windowWatcher.unregisterNotification(this); + events.off('toplevel-window-ready', this._onToplevelWindowReady); for each (let window in getWindows()) this._unregWindow(window); }, @@ -128,14 +132,20 @@ WindowTracker.prototype = { } }), + _onToplevelWindowReady: function _onToplevelWindowReady({subject}) { + let window = subject; + // ignore private windows if they are not supported + if (ignoreWindow(window)) + return; + this._regWindow(window); + }, + observe: errors.catchAndLog(function observe(subject, topic, data) { var window = subject.QueryInterface(Ci.nsIDOMWindow); // ignore private windows if they are not supported if (ignoreWindow(window)) return; - if (topic == 'domwindowopened') - this._regWindow(window); - else + if (topic == 'domwindowclosed') this._unregWindow(window); }) }; diff --git a/addon-sdk/source/lib/sdk/panel.js b/addon-sdk/source/lib/sdk/panel.js index 68be5673573..bde1b7ea530 100644 --- a/addon-sdk/source/lib/sdk/panel.js +++ b/addon-sdk/source/lib/sdk/panel.js @@ -19,7 +19,8 @@ const { isPrivateBrowsingSupported } = require('./self'); const { isWindowPBSupported } = require('./private-browsing/utils'); const { Class } = require("./core/heritage"); const { merge } = require("./util/object"); -const { WorkerHost, Worker, detach, attach } = require("./worker/utils"); +const { WorkerHost, Worker, detach, attach, + requiresAddonGlobal } = require("./worker/utils"); const { Disposable } = require("./core/disposable"); const { contract: loaderContract } = require("./content/loader"); const { contract } = require("./util/contract"); @@ -32,24 +33,6 @@ const { filter, pipe } = require("./event/utils"); const { getNodeView, getActiveView } = require("./view/core"); const { isNil, isObject } = require("./lang/type"); -let isArray = Array.isArray; -let assetsURI = require("./self").data.url(); - -function isAddonContent({ contentURL }) { - return typeof(contentURL) === "string" && contentURL.indexOf(assetsURI) === 0; -} - -function hasContentScript({ contentScript, contentScriptFile }) { - return (isArray(contentScript) ? contentScript.length > 0 : - !!contentScript) || - (isArray(contentScriptFile) ? contentScriptFile.length > 0 : - !!contentScriptFile); -} - -function requiresAddonGlobal(model) { - return isAddonContent(model) && !hasContentScript(model); -} - function getAttachEventType(model) { let when = model.contentScriptWhen; return requiresAddonGlobal(model) ? "sdk-panel-content-changed" : diff --git a/addon-sdk/source/lib/sdk/request.js b/addon-sdk/source/lib/sdk/request.js index 514631f0384..28cced413c8 100644 --- a/addon-sdk/source/lib/sdk/request.js +++ b/addon-sdk/source/lib/sdk/request.js @@ -50,12 +50,14 @@ const { validateOptions, validateSingleOption } = new OptionsValidator({ const REUSE_ERROR = "This request object has been used already. You must " + "create a new one to make a new request." -// Utility function to prep the request since it's the same between GET and -// POST +// Utility function to prep the request since it's the same between +// request types function runRequest(mode, target) { let source = request(target) let { xhr, url, content, contentType, headers, overrideMimeType } = source; + let isGetOrHead = (mode == "GET" || mode == "HEAD"); + // If this request has already been used, then we can't reuse it. // Throw an error. if (xhr) @@ -63,11 +65,11 @@ function runRequest(mode, target) { xhr = source.xhr = new XMLHttpRequest(); - // Build the data to be set. For GET requests, we want to append that to - // the URL before opening the request. + // Build the data to be set. For GET or HEAD requests, we want to append that + // to the URL before opening the request. let data = stringify(content); // If the URL already has ? in it, then we want to just use & - if (mode == "GET" && data) + if (isGetOrHead && data) url = url + (/\?/.test(url) ? "&" : "?") + data; // open the request @@ -97,8 +99,8 @@ function runRequest(mode, target) { }; // actually send the request. - // We don't want to send data on GET requests. - xhr.send(mode !== "GET" ? data : null); + // We don't want to send data on GET or HEAD requests. + xhr.send(!isGetOrHead ? data : null); } const Request = Class({ @@ -138,6 +140,10 @@ const Request = Class({ put: function() { runRequest('PUT', this); return this; + }, + head: function() { + runRequest('HEAD', this); + return this; } }); exports.Request = Request; diff --git a/addon-sdk/source/lib/sdk/url.js b/addon-sdk/source/lib/sdk/url.js index de3caf2bf9b..87bca368697 100644 --- a/addon-sdk/source/lib/sdk/url.js +++ b/addon-sdk/source/lib/sdk/url.js @@ -12,6 +12,8 @@ const { Cc, Ci, Cr } = require("chrome"); const { Class } = require("./core/heritage"); const base64 = require("./base64"); +var tlds = Cc["@mozilla.org/network/effective-tld-service;1"] + .getService(Ci.nsIEffectiveTLDService); var ios = Cc['@mozilla.org/network/io-service;1'] .getService(Ci.nsIIOService); @@ -19,6 +21,9 @@ var ios = Cc['@mozilla.org/network/io-service;1'] var resProt = ios.getProtocolHandler("resource") .QueryInterface(Ci.nsIResProtocolHandler); +var URLParser = Cc["@mozilla.org/network/url-parser;1?auth=no"] + .getService(Ci.nsIURLParser); + function newURI(uriStr, base) { try { let baseURI = base ? ios.newURI(base, null, null) : null; @@ -92,11 +97,29 @@ function URL(url, base) { port = uri.port == -1 ? null : uri.port; } catch (e if e.result == Cr.NS_ERROR_FAILURE) {} + let uriData = [uri.path, uri.path.length, {}, {}, {}, {}, {}, {}]; + URLParser.parsePath.apply(URLParser, uriData); + let [{ value: filepathPos }, { value: filepathLen }, + { value: queryPos }, { value: queryLen }, + { value: refPos }, { value: refLen }] = uriData.slice(2); + + let hash = uri.ref ? "#" + uri.ref : ""; + let pathname = uri.path.substr(filepathPos, filepathLen); + let search = uri.path.substr(queryPos, queryLen); + search = search ? "?" + search : ""; + this.__defineGetter__("scheme", function() uri.scheme); this.__defineGetter__("userPass", function() userPass); this.__defineGetter__("host", function() host); + this.__defineGetter__("hostname", function() host); this.__defineGetter__("port", function() port); this.__defineGetter__("path", function() uri.path); + this.__defineGetter__("pathname", function() pathname); + this.__defineGetter__("hash", function() hash); + this.__defineGetter__("href", function() uri.spec); + this.__defineGetter__("origin", function() uri.prePath); + this.__defineGetter__("protocol", function() uri.scheme + ":"); + this.__defineGetter__("search", function() search); Object.defineProperties(this, { toString: { @@ -235,6 +258,17 @@ const DataURL = Class({ exports.DataURL = DataURL; +let getTLD = exports.getTLD = function getTLD (url) { + let uri = newURI(url.toString()); + let tld = null; + try { + tld = tlds.getPublicSuffix(uri); + } catch (e if + e.result == Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS || + e.result == Cr.NS_ERROR_HOST_IS_IP_ADDRESS) {} + return tld; +}; + let isValidURI = exports.isValidURI = function (uri) { try { newURI(uri); diff --git a/addon-sdk/source/lib/sdk/worker/utils.js b/addon-sdk/source/lib/sdk/worker/utils.js index a7d46767ef2..26052aa690b 100644 --- a/addon-sdk/source/lib/sdk/worker/utils.js +++ b/addon-sdk/source/lib/sdk/worker/utils.js @@ -17,10 +17,34 @@ const { Loader } = require("../content/loader"); const { merge } = require("../util/object"); const { emit } = require("../event/core"); -const LegacyWorker = WorkerTrait.resolve({ +let assetsURI = require("../self").data.url(); +let isArray = Array.isArray; + +function isAddonContent({ contentURL }) { + return typeof(contentURL) === "string" && contentURL.indexOf(assetsURI) === 0; +} + +function hasContentScript({ contentScript, contentScriptFile }) { + return (isArray(contentScript) ? contentScript.length > 0 : + !!contentScript) || + (isArray(contentScriptFile) ? contentScriptFile.length > 0 : + !!contentScriptFile); +} + +function requiresAddonGlobal(model) { + return isAddonContent(model) && !hasContentScript(model); +} +exports.requiresAddonGlobal = requiresAddonGlobal; + + +const LegacyWorker = WorkerTrait.compose(Loader).resolve({ _setListeners: "__setListeners", -}).compose(Loader, { + _injectInDocument: "__injectInDocument", + contentURL: "__contentURL" +}).compose({ _setListeners: function() {}, + get contentURL() this._window.document.URL, + get _injectInDocument() requiresAddonGlobal(this), attach: function(window) this._attach(window), detach: function() this._workerCleanup() }); diff --git a/addon-sdk/source/test/addons/private-browsing-supported/test-selection.js b/addon-sdk/source/test/addons/private-browsing-supported/test-selection.js index 51e18bd58a2..5bf033a002a 100644 --- a/addon-sdk/source/test/addons/private-browsing-supported/test-selection.js +++ b/addon-sdk/source/test/addons/private-browsing-supported/test-selection.js @@ -49,8 +49,8 @@ function open(url, options) { let tab = getActiveTab(chromeWindow); - tab.addEventListener("load", function ready(event) { - let { document } = getTabContentWindow(this); + tab.linkedBrowser.addEventListener("load", function ready(event) { + let { document } = getTabContentWindow(tab); if (document.readyState === "complete" && document.URL === url) { this.removeEventListener(event.type, ready); @@ -60,7 +60,7 @@ function open(url, options) { resolve(document.defaultView); } - }) + }, true); setTabURL(tab, url); }); diff --git a/addon-sdk/source/test/addons/privileged-panel/data/index.html b/addon-sdk/source/test/addons/privileged-panel/data/index.html new file mode 100644 index 00000000000..f37c96b2eb3 --- /dev/null +++ b/addon-sdk/source/test/addons/privileged-panel/data/index.html @@ -0,0 +1,3 @@ + diff --git a/addon-sdk/source/test/addons/privileged-panel/main.js b/addon-sdk/source/test/addons/privileged-panel/main.js new file mode 100644 index 00000000000..c010de9242b --- /dev/null +++ b/addon-sdk/source/test/addons/privileged-panel/main.js @@ -0,0 +1,26 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { Panel } = require("sdk/panel") +const { data } = require("sdk/self") + +exports["test addon global"] = function(assert, done) { + let panel = Panel({ + contentURL: //"data:text/html,now?", + data.url("./index.html"), + onMessage: function(message) { + assert.pass("got message from panel script"); + panel.destroy(); + done(); + }, + onError: function(error) { + asser.fail(Error("failed to recieve message")); + done(); + } + }); +}; + +require("sdk/test/runner").runTestsFromModule(module); diff --git a/addon-sdk/source/test/addons/privileged-panel/package.json b/addon-sdk/source/test/addons/privileged-panel/package.json new file mode 100644 index 00000000000..e35c3a0041a --- /dev/null +++ b/addon-sdk/source/test/addons/privileged-panel/package.json @@ -0,0 +1,3 @@ +{ + "id": "test-privileged-addon" +} diff --git a/addon-sdk/source/test/test-request.js b/addon-sdk/source/test/test-request.js index 9b5ed3b3279..b5751b8bb71 100644 --- a/addon-sdk/source/test/test-request.js +++ b/addon-sdk/source/test/test-request.js @@ -218,6 +218,26 @@ exports.testInvalidJSON = function (test) { }); } +exports.testHead = function (test) { + let srv = startServerAsync(port, basePath); + + srv.registerPathHandler("/test-head", + function handle(request, response) { + response.setHeader("Content-Type", "text/plain", false); + }); + + test.waitUntilDone(); + Request({ + url: "http://localhost:" + port + "/test-head", + onComplete: function (response) { + test.assertEqual(response.text, ""); + test.assertEqual(response.statusText, "OK"); + test.assertEqual(response.headers["Content-Type"], "text/plain"); + srv.stop(function() test.done()); + } + }).head(); +} + function runMultipleURLs (srv, test, options) { let urls = [options.url, URL(options.url)]; let cb = options.onComplete; diff --git a/addon-sdk/source/test/test-selection.js b/addon-sdk/source/test/test-selection.js index 4adcc74c15e..3127664711c 100644 --- a/addon-sdk/source/test/test-selection.js +++ b/addon-sdk/source/test/test-selection.js @@ -50,15 +50,15 @@ function open(url, options) { let tab = getActiveTab(chromeWindow); - tab.addEventListener("load", function ready(event) { - let { document } = getTabContentWindow(this); + tab.linkedBrowser.addEventListener("load", function ready(event) { + let { document } = getTabContentWindow(tab); if (document.readyState === "complete" && document.URL === url) { this.removeEventListener(event.type, ready); resolve(document.defaultView); } - }) + }, true); setTabURL(tab, url); }); diff --git a/addon-sdk/source/test/test-url.js b/addon-sdk/source/test/test-url.js index 6f7a2398bcd..de175240dca 100644 --- a/addon-sdk/source/test/test-url.js +++ b/addon-sdk/source/test/test-url.js @@ -3,6 +3,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var url = require("sdk/url"); +var { Loader } = require("sdk/test/loader"); +var { pathFor } = require("sdk/system"); +var file = require("sdk/io/file"); +var loader = Loader(module); +var httpd = loader.require("sdk/test/httpd"); +var port = 8099; +var tabs = require("sdk/tabs"); exports.testResolve = function(test) { test.assertEqual(url.URL("bar", "http://www.foo.com/").toString(), @@ -33,12 +40,49 @@ exports.testResolve = function(test) { }; exports.testParseHttp = function(test) { - var info = url.URL("http://foo.com/bar"); + var aUrl = "http://sub.foo.com/bar?locale=en-US&otherArg=%20x%20#myhash"; + var info = url.URL(aUrl); test.assertEqual(info.scheme, "http"); - test.assertEqual(info.host, "foo.com"); + test.assertEqual(info.protocol, "http:"); + test.assertEqual(info.host, "sub.foo.com"); + test.assertEqual(info.hostname, "sub.foo.com"); test.assertEqual(info.port, null); test.assertEqual(info.userPass, null); - test.assertEqual(info.path, "/bar"); + test.assertEqual(info.path, "/bar?locale=en-US&otherArg=%20x%20#myhash"); + test.assertEqual(info.pathname, "/bar"); + test.assertEqual(info.href, aUrl); + test.assertEqual(info.hash, "#myhash"); + test.assertEqual(info.search, "?locale=en-US&otherArg=%20x%20"); +}; + +exports.testParseHttpSearchAndHash = function (test) { + var info = url.URL("https://www.moz.com/some/page.html"); + test.assertEqual(info.hash, ""); + test.assertEqual(info.search, ""); + + var hashOnly = url.URL("https://www.sub.moz.com/page.html#justhash"); + test.assertEqual(hashOnly.search, ""); + test.assertEqual(hashOnly.hash, "#justhash"); + + var queryOnly = url.URL("https://www.sub.moz.com/page.html?my=query"); + test.assertEqual(queryOnly.search, "?my=query"); + test.assertEqual(queryOnly.hash, ""); + + var qMark = url.URL("http://www.moz.org?"); + test.assertEqual(qMark.search, ""); + test.assertEqual(qMark.hash, ""); + + var hash = url.URL("http://www.moz.org#"); + test.assertEqual(hash.search, ""); + test.assertEqual(hash.hash, ""); + + var empty = url.URL("http://www.moz.org?#"); + test.assertEqual(hash.search, ""); + test.assertEqual(hash.hash, ""); + + var strange = url.URL("http://moz.org?test1#test2?test3"); + test.assertEqual(strange.search, "?test1"); + test.assertEqual(strange.hash, "#test2?test3"); }; exports.testParseHttpWithPort = function(test) { @@ -163,10 +207,12 @@ exports.testStringInterface = function(test) { var a = URL(EM); // make sure the standard URL properties are enumerable and not the String interface bits - test.assertEqual(Object.keys(a), "scheme,userPass,host,port,path", "enumerable key list check for URL."); + test.assertEqual(Object.keys(a), + "scheme,userPass,host,hostname,port,path,pathname,hash,href,origin,protocol,search", + "enumerable key list check for URL."); test.assertEqual( JSON.stringify(a), - "{\"scheme\":\"about\",\"userPass\":null,\"host\":null,\"port\":null,\"path\":\"addons\"}", + "{\"scheme\":\"about\",\"userPass\":null,\"host\":null,\"hostname\":null,\"port\":null,\"path\":\"addons\",\"pathname\":\"addons\",\"hash\":\"\",\"href\":\"about:addons\",\"origin\":\"about:\",\"protocol\":\"about:\",\"search\":\"\"}", "JSON.stringify should return a object with correct props and vals."); // make sure that the String interface exists and works as expected @@ -261,6 +307,53 @@ exports.testURLFromURL = function(test) { test.assertEqual(aURL.toString(), bURL.toString(), 'Making a URL from a URL works'); }; +exports.testTLD = function(test) { + let urls = [ + { url: 'http://my.sub.domains.mozilla.co.uk', tld: 'co.uk' }, + { url: 'http://my.mozilla.com', tld: 'com' }, + { url: 'http://my.domains.mozilla.org.hk', tld: 'org.hk' }, + { url: 'chrome://global/content/blah', tld: 'global' }, + { url: 'data:text/plain;base64,QXdlc29tZSE=', tld: null }, + { url: 'https://1.2.3.4', tld: null } + ]; + + urls.forEach(function (uri) { + test.assertEqual(url.getTLD(uri.url), uri.tld); + test.assertEqual(url.getTLD(url.URL(uri.url)), uri.tld); + }); +} + +exports.testWindowLocationMatch = function (test) { + let srv = serve(); + let aUrl = 'http://localhost:' + port + '/index.html?q=aQuery#somehash'; + let urlObject = url.URL(aUrl); + test.waitUntilDone(); + + tabs.open({ + url: aUrl, + onReady: function (tab) { + tab.attach({ + onMessage: function (loc) { + for (let prop in loc) { + test.assertEqual(urlObject[prop], loc[prop], prop + ' matches'); + } + tab.close(); + srv.stop(test.done.bind(test)); + }, + contentScript: '(' + function () { + let res = {}; + // `origin` is `null` in this context??? + let props = 'hostname,port,pathname,hash,href,protocol,search'.split(','); + props.forEach(function (prop) { + res[prop] = window.location[prop]; + }); + self.postMessage(res); + } + ')()' + }); + } + }) +}; + function validURIs() { return [ 'http://foo.com/blah_blah', @@ -352,3 +445,16 @@ function invalidURIs () { // 'http://10.1.1.254' ]; } + +function serve () { + let basePath = pathFor("ProfD"); + let filePath = file.join(basePath, 'index.html'); + let content = "

url tests

"; + let fileStream = file.open(filePath, 'w'); + fileStream.write(content); + fileStream.close(); + + let srv = httpd.startServerAsync(port, basePath); + return srv; +} +