зеркало из https://github.com/mozilla/gecko-dev.git
Bug 875812 - Uplift Add-on SDK integration branch to Firefox
2593d0a01b...007eb037f0
This commit is contained in:
Родитель
f92addf69c
Коммит
528df617cb
|
@ -20,12 +20,69 @@ commands (for example `--help`). `cfx` supports the following global options:
|
|||
-v, --verbose - enable lots of output
|
||||
</pre>
|
||||
|
||||
"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 ###
|
||||
<table>
|
||||
<colgroup>
|
||||
<col width="10%">
|
||||
<col width="90%">
|
||||
</colgroup>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="dev-guide/cfx-tool.html#cfx-docs"><code>cfx docs</code></a>
|
||||
</td>
|
||||
<td>
|
||||
Display the documentation for the SDK.
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="dev-guide/cfx-tool.html#cfx-init"><code>cfx init</code></a>
|
||||
</td>
|
||||
<td>
|
||||
Create a skeleton add-on as a starting point for your own add-on.
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="dev-guide/cfx-tool.html#cfx-run"><code>cfx run</code></a>
|
||||
</td>
|
||||
<td>
|
||||
Launch an instance of Firefox with your add-on installed.
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="dev-guide/cfx-tool.html#cfx-test"><code>cfx test</code></a>
|
||||
</td>
|
||||
<td>
|
||||
Runs your add-on's unit tests.
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="dev-guide/cfx-tool.html#cfx-xpi"><code>cfx xpi</code></a>
|
||||
</td>
|
||||
<td>
|
||||
Package your add-on as an <a href="https://developer.mozilla.org/en/XPI">XPI</a>
|
||||
file, which is the install file format for Firefox add-ons.
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
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.
|
||||
|
||||
## <a name="cfx-docs">cfx docs</a> ##
|
||||
|
||||
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 ####
|
||||
## <a name="cfx-init">cfx init</a> ##
|
||||
|
||||
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:
|
|||
|
||||
<div style="clear:both"></div>
|
||||
|
||||
### cfx run ###
|
||||
|
||||
## <a name="cfx-run">cfx run</a> ##
|
||||
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 <a href="dev-guide/cfx-tool.html#profiledir">
|
|||
|
||||
</table>
|
||||
|
||||
#### Experimental Options ####
|
||||
### Experimental Options ###
|
||||
|
||||
<table>
|
||||
<colgroup>
|
||||
|
@ -233,6 +290,25 @@ To launch the application, enter the following command:
|
|||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<code>-o, --overload-modules</code>
|
||||
</td>
|
||||
<td>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<code>--templatedir=TEMPLATEDIR</code>
|
||||
|
@ -249,7 +325,7 @@ To launch the application, enter the following command:
|
|||
|
||||
</table>
|
||||
|
||||
#### Internal Options ####
|
||||
### Internal Options ###
|
||||
|
||||
<table>
|
||||
<colgroup>
|
||||
|
@ -291,8 +367,7 @@ To launch the application, enter the following command:
|
|||
|
||||
</table>
|
||||
|
||||
### cfx test ###
|
||||
|
||||
## <a name="cfx-test">cfx test</a> ##
|
||||
Run available tests for the specified package.
|
||||
|
||||
<span class="aside">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.
|
|||
|
||||
</table>
|
||||
|
||||
#### Experimental Options ####
|
||||
### Experimental Options ###
|
||||
|
||||
<table>
|
||||
<colgroup>
|
||||
|
@ -461,16 +536,26 @@ To launch the application, enter the following command:
|
|||
|
||||
<tr>
|
||||
<td>
|
||||
<code>--use-server</code>
|
||||
<code>-o, --overload-modules</code>
|
||||
</td>
|
||||
<td>
|
||||
Run tests using a server previously started with <code>cfx develop</code>.
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
#### Internal Options ####
|
||||
### Internal Options ###
|
||||
|
||||
<table>
|
||||
<colgroup>
|
||||
|
@ -545,8 +630,7 @@ To launch the application, enter the following command:
|
|||
|
||||
</table>
|
||||
|
||||
### cfx xpi ###
|
||||
|
||||
## <a name="cfx-xpi">cfx xpi</a> ##
|
||||
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.
|
|||
|
||||
</table>
|
||||
|
||||
#### Experimental Options ####
|
||||
### Experimental Options ###
|
||||
|
||||
<table>
|
||||
<colgroup>
|
||||
|
@ -699,7 +783,7 @@ add-on whenever it is run.
|
|||
|
||||
</table>
|
||||
|
||||
#### Internal Options ####
|
||||
### Internal Options ###
|
||||
|
||||
<table>
|
||||
<colgroup>
|
||||
|
@ -721,35 +805,7 @@ add-on whenever it is run.
|
|||
|
||||
</table>
|
||||
|
||||
## 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:
|
||||
|
||||
<pre>
|
||||
cfx develop
|
||||
</pre>
|
||||
|
||||
In shell B, type:
|
||||
|
||||
<pre>
|
||||
cfx test --use-server
|
||||
</pre>
|
||||
|
||||
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 ##
|
||||
## <a name="internal-commands">Internal Commands</a> ##
|
||||
|
||||
### cfx sdocs ###
|
||||
|
||||
|
|
|
@ -6,20 +6,22 @@ The `request` module lets you make simple yet powerful network requests.
|
|||
|
||||
<api name="Request">
|
||||
@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}
|
||||
</api>
|
||||
|
||||
<api name="head">
|
||||
@method
|
||||
Make a `HEAD` request.
|
||||
@returns {Request}
|
||||
</api>
|
||||
|
||||
<api name="complete">
|
||||
@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
|
|||
<api name="Response">
|
||||
@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.
|
||||
<api name="text">
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
})
|
||||
};
|
||||
|
|
|
@ -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" :
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<script>
|
||||
addon.postMessage("hello addon")
|
||||
</script>
|
|
@ -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);
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"id": "test-privileged-addon"
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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 = "<html><head></head><body><h1>url tests</h1></body></html>";
|
||||
let fileStream = file.open(filePath, 'w');
|
||||
fileStream.write(content);
|
||||
fileStream.close();
|
||||
|
||||
let srv = httpd.startServerAsync(port, basePath);
|
||||
return srv;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче