This commit is contained in:
Jan de Mooij 2013-03-29 15:55:22 +01:00
Родитель ef4a0edc38 111695c5e0
Коммит 3701cd327e
739 изменённых файлов: 12416 добавлений и 7334 удалений

Просмотреть файл

@ -165,9 +165,9 @@ const char* const kEventTypes[] = {
"TreeInvalidated",
// add ourself as a OpenStateChange listener (custom event fired in tree.xml)
"OpenStateChange",
// add ourself as a CheckboxStateChange listener (custom event fired in nsHTMLInputElement.cpp)
// add ourself as a CheckboxStateChange listener (custom event fired in HTMLInputElement.cpp)
"CheckboxStateChange",
// add ourself as a RadioStateChange Listener ( custom event fired in in nsHTMLInputElement.cpp & radio.xml)
// add ourself as a RadioStateChange Listener ( custom event fired in in HTMLInputElement.cpp & radio.xml)
"RadioStateChange",
"popupshown",
"popuphiding",

Просмотреть файл

@ -15,7 +15,7 @@
#include "States.h"
#include "nsContentList.h"
#include "nsHTMLInputElement.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "nsIAccessibleRelation.h"
#include "nsIDOMNSEditableElement.h"
#include "nsIDOMHTMLTextAreaElement.h"
@ -32,6 +32,7 @@
#include "mozilla/Preferences.h"
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::a11y;
////////////////////////////////////////////////////////////////////////////////
@ -91,7 +92,7 @@ HTMLCheckboxAccessible::NativeState()
uint64_t state = LeafAccessible::NativeState();
state |= states::CHECKABLE;
nsHTMLInputElement* input = nsHTMLInputElement::FromContent(mContent);
HTMLInputElement* input = HTMLInputElement::FromContent(mContent);
if (!input)
return state;
@ -131,7 +132,7 @@ HTMLRadioButtonAccessible::NativeState()
state |= states::CHECKABLE;
nsHTMLInputElement* input = nsHTMLInputElement::FromContent(mContent);
HTMLInputElement* input = HTMLInputElement::FromContent(mContent);
if (input && input->Checked())
state |= states::CHECKED;
@ -356,7 +357,7 @@ HTMLTextFieldAccessible::Value(nsString& aValue)
return;
}
nsHTMLInputElement* input = nsHTMLInputElement::FromContent(mContent);
HTMLInputElement* input = HTMLInputElement::FromContent(mContent);
if (input)
input->GetValue(aValue);
}
@ -385,7 +386,7 @@ HTMLTextFieldAccessible::NativeState()
}
// Is it an <input> or a <textarea> ?
nsHTMLInputElement* input = nsHTMLInputElement::FromContent(mContent);
HTMLInputElement* input = HTMLInputElement::FromContent(mContent);
state |= input && input->IsSingleLineTextControl() ?
states::SINGLE_LINE : states::MULTI_LINE;
@ -451,7 +452,7 @@ NS_IMETHODIMP
HTMLTextFieldAccessible::DoAction(uint8_t aIndex)
{
if (aIndex == 0) {
nsHTMLInputElement* element = nsHTMLInputElement::FromContent(mContent);
HTMLInputElement* element = HTMLInputElement::FromContent(mContent);
if (element)
return element->Focus();

Просмотреть файл

@ -57,7 +57,7 @@
function doTest()
{
enableLogging("focus");
enableLogging("focus,doclifecycle");
gQueue = new eventQueue();
gQueue.push(new openWnd());

16
addon-sdk/source/app-extension/bootstrap.js поставляемый
Просмотреть файл

@ -20,7 +20,8 @@ const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')();
const scriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1'].
getService(Ci.mozIJSSubScriptLoader);
const prefService = Cc['@mozilla.org/preferences-service;1'].
getService(Ci.nsIPrefService);
getService(Ci.nsIPrefService).
QueryInterface(Ci.nsIPrefBranch);
const appInfo = Cc["@mozilla.org/xre/app-info;1"].
getService(Ci.nsIXULAppInfo);
const vc = Cc["@mozilla.org/xpcom/version-comparator;1"].
@ -127,12 +128,21 @@ function startup(data, reasonCode) {
if (name == 'addon-sdk')
paths['tests/'] = prefixURI + name + '/tests/';
let useBundledSDK = options['force-use-bundled-sdk'];
if (!useBundledSDK) {
try {
useBundledSDK = prefService.getBoolPref("extensions.addon-sdk.useBundledSDK");
}
catch (e) {
// Pref doesn't exist, allow using Firefox shipped SDK
}
}
// Starting with Firefox 21.0a1, we start using modules shipped into firefox
// Still allow using modules from the xpi if the manifest tell us to do so.
// And only try to look for sdk modules in xpi if the xpi actually ship them
if (options['is-sdk-bundled'] &&
(vc.compare(appInfo.version, '21.0a1') < 0 ||
options['force-use-bundled-sdk'])) {
(vc.compare(appInfo.version, '21.0a1') < 0 || useBundledSDK)) {
// Maps sdk module folders to their resource folder
paths[''] = prefixURI + 'addon-sdk/lib/';
// test.js is usually found in root commonjs or SDK_ROOT/lib/ folder,

Просмотреть файл

@ -17,8 +17,8 @@
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>18.0</em:minVersion>
<em:maxVersion>21.0a1</em:maxVersion>
<em:minVersion>19.0</em:minVersion>
<em:maxVersion>22.0a1</em:maxVersion>
</Description>
</em:targetApplication>

Просмотреть файл

@ -135,7 +135,7 @@ We'd like to thank our many Jetpack project contributors! They include:
### P ###
* Robert Pankowecki
* [Jamie Phelps](https://github.com/ongaeshi)
* [Jamie Phelps](https://github.com/jxpx777)
* [Alexandre Poirot](https://github.com/ochameau)
* Nickolay Ponomarev

Просмотреть файл

@ -59,50 +59,6 @@ argument to the constructor:
}
});
## Timing Issues Using postMessage ##
Content scripts are loaded according to the value of the
[`contentScriptWhen`](dev-guide/guides/content-scripts/loading.html)
option: until that point is reached, any attempt to send a message to
the script using `postMessage()` will trigger an exception, probably
this:
<span class="aside">
This is a generic message which is emitted whenever we try to
send a message to a content script, but can't find the worker
which is supposed to receive it.
</span>
<pre>
Error: Couldn't find the worker to receive this message. The script may not be initialized yet, or may already have been unloaded.
</pre>
So code like this, where we create a panel and then
synchronously send it a message using `postMessage()`, will not work:
var data = require("sdk/self").data;
var panel = require("sdk/panel").Panel({
contentURL: "http://www.bbc.co.uk/mobile/index.html",
contentScriptFile: data.url("panel.js")
});
panel.postMessage("hi from main.js");
[`port.emit()`](dev-guide/guides/content-scripts/using-port.html)
queues messages until the content script is ready to receive them,
so the equivalent code using `port.emit()` will work:
var data = require("sdk/self").data;
var panel = require("sdk/panel").Panel({
contentURL: "http://www.bbc.co.uk/mobile/index.html",
contentScriptFile: data.url("panel.js")
});
panel.port.emit("hi from main.js");
## Message Events Versus User-Defined Events ##
You can use message events as an alternative to user-defined events:

Просмотреть файл

@ -51,19 +51,15 @@ is specific to an event emitter and is included with its documentation.
whenever the event occurs. The arguments that will be passed to the listener
are specific to an event type and are documented with the event emitter.
For example, the following add-on registers two listeners with the
[`private-browsing`](modules/sdk/private-browsing.html) module to
listen for the `start` and `stop` events, and logs a string to the console
reporting the change:
For example, the following add-on registers a listener with the
[`tabs`](modules/sdk/tabs.html) module to
listen for the `ready` event, and logs a string to the console
reporting the event:
var pb = require("sdk/private-browsing");
var tabs = require("sdk/tabs");
pb.on("start", function() {
console.log("Private browsing is on");
});
pb.on("stop", function() {
console.log("Private browsing is off");
tabs.on("ready", function () {
console.log("tab loaded");
});
It is not possible to enumerate the set of listeners for a given event.
@ -73,9 +69,8 @@ the event.
### Adding Listeners in Constructors ###
Event emitters may be modules, as is the case for the
`private-browsing` events, or they may be objects returned by
constructors.
Event emitters may be modules, as is the case for the `ready` event
above, or they may be objects returned by constructors.
In the latter case the `options` object passed to the constructor typically
defines properties whose names are the names of supported event types prefixed
@ -126,28 +121,36 @@ supplying the type of event and the listener to remove.
The listener must have been previously been added using one of the methods
described above.
In the following add-on, we add two listeners to private-browsing's `start`
event, enter and exit private browsing, then remove the first listener and
enter private browsing again.
In the following add-on, we add two listeners to the
[`tabs` module's `ready` event](modules/sdk/tabs.html#ready).
One of the handler functions removes the listener again.
var pb = require("sdk/private-browsing");
Then we open two tabs.
var tabs = require("sdk/tabs");
function listener1() {
console.log("Listener 1");
pb.removeListener("start", listener1);
tabs.removeListener("ready", listener1);
}
function listener2() {
console.log("Listener 2");
}
pb.on("start", listener1);
pb.on("start", listener2);
tabs.on("ready", listener1);
tabs.on("ready", listener2);
pb.activate();
pb.deactivate();
pb.activate();
tabs.open("https://www.mozilla.org");
tabs.open("https://www.mozilla.org");
Removing listeners is optional since they will be removed in any case
when the application or add-on is unloaded.
We should see output like this:
<pre>
info: tabevents: Listener 1
info: tabevents: Listener 2
info: tabevents: Listener 2
</pre>
Listeners will be removed automatically when the add-on is unloaded.

Просмотреть файл

@ -2,124 +2,221 @@
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Package Specification #
# package.json #
A *package* is a directory that, at minimum, contains a JSON file
called `package.json`. This file is also referred to as the
*package manifest*.
The "package.json" file contains metadata for your add-on.
## The Package Manifest ##
Some of its entries, such as [`icon`](dev-guide/package-spec.html#icon),
[`name`](dev-guide/package-spec.html#name), and
[`description`](dev-guide/package-spec.html#description), have
direct analogues in the
[install manifest](https://developer.mozilla.org/en-US/docs/Install_Manifests)
format, and entries from package.json are written into the install
manifest when the add-on is built using [`cfx xpi`](dev-guide/cfx-tool.html#cfx xpi).
Others, such as
[`lib`](dev-guide/package-spec.html#lib),
[`permissions`](dev-guide/package-spec.html#permissions),
and [`preferences`](dev-guide/package-spec.html#preferences),
represent instructions to the cfx tool itself to generate and include
particular code and data structures in your add-on.
The `package.json` file is initially generated in your add-on's root
directory the first time you run
[`cfx init`](dev-guide/cfx-tool.html#cfx init). It looks like this
(assuming the add-on's directory is "my-addon"):
<pre>
{
"name": "my-addon",
"fullName": "my-addon",
"description": "a basic add-on",
"author": "",
"license": "MPL 2.0",
"version": "0.1"
}
</pre>
`package.json` may contain the following keys:
* `name` - the name of the package. The package system will only load
one package with a given name. This name cannot contain spaces or periods.
The name defaults to the name of the parent directory. If the package is
ever built as an XPI and the `fullName` key is not present, this is
used as the add-on's `em:name` element in its `install.rdf`.
<table>
* `fullName` - the full name of the package. It can contain spaces. If
the package is ever built as an XPI, this is used as the add-on's
`em:name` element in its `install.rdf`.
<colgroup>
<col width="20%"></col>
<col width="80%"></col>
</colgroup>
* `description` - a String describing the package. If the package is
ever built as an XPI, this is used as the add-on's
`em:description` element in its `install.rdf`.
<tr>
<td id="author"><code>author</code></td>
<td><p>The original author of the package. Defaults to an empty string.
It may include a optional URL in parentheses and an email
address in angle brackets.</p>
<p>This value will be used as the add-on's
<a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#creator"><code>em:creator</code></a>
element in its "install.rdf".</p></td>
</tr>
* `author` - the original author of the package. The author may be a
String including an optional URL in parentheses and optional email
address in angle brackets. If the package is ever built as an XPI,
this is used as the add-on's `em:creator` element in its
`install.rdf`.
<tr>
<td id="contributors"><code>contributors</code></td>
<td><p>An array of additional <a href="dev-guide/package-spec.html#author"><code>author</code></a>
strings.</p>
<p>These values will be used as the add-on's
<a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#contributor"><code>em:contributor</code></a>
elements in its "install.rdf".</p></td>
</tr>
* `translators` - an Array of Strings consisted of translators of the package.
<tr>
<td id="dependencies"><code>dependencies</code></td>
<td><p>String or array of strings representing package
names that this add-on requires in order to function properly.</p></td>
</tr>
* `contributors` - may be an Array of additional author Strings.
<tr>
<td id="description"><code>description</code></td>
<td><p>The add-on's description. This defaults to the text
<code>"a basic add-on"</code>.</p>
<p>This value will be used as the add-on's
<a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#description"><code>em:description</code></a>
element in its "install.rdf".</p></td>
</tr>
* `homepage` - the URL of the package's website.
<tr>
<td id="fullName"><code>fullName</code></td>
<td><p>The full name of the package. It can contain spaces.<p></p>
If this key is present its value will be used as the add-on's
<a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#name"><code>em:name</code></a>
element in its "install.rdf".</p></td>
</tr>
* `icon` - the relative path from the root of the package to a
PNG file containing the icon for the package. By default, this
is `icon.png`. If the package is built as an XPI, this is used
as the add-on's icon to display in the Add-on Manager's add-ons list.
This key maps on to the
[`iconURL` entry in the Install Manifest](https://developer.mozilla.org/en/install_manifests#iconURL),
so the icon may be up to 48x48 pixels in size.
<tr>
<td id="harnessClassID"><code>harnessClassID</code></td>
<td><p>String in the <a href="https://developer.mozilla.org/en-US/docs/Generating_GUIDs">GUID format</a>.</p>
<p>This is used as a
<a href="https://developer.mozilla.org/en-US/docs/Creating_XPCOM_Components/An_Overview_of_XPCOM#CID"><code>classID</code></a>
of the "harness service" XPCOM component. Defaults to a random GUID generated by <code>cfx</code>.</p></td>
</tr>
* `icon64` - the relative path from the root of the package to a
PNG file containing the icon64 for the package. By default, this
is `icon64.png`. If the package is built as an XPI, this is used
as the add-on's icon to display in the Addon Manager's add-on details view.
This key maps on to the
[`icon64URL` entry in the Install Manifest](https://developer.mozilla.org/en/install_manifests#icon64URL),
so the icon should be 64x64 pixels in size.
<tr>
<td id="homepage"><code>homepage</code></td>
<td><p>The URL of the add-on's website.</p>
<p>This value will be used as the add-on's
<a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#homepageURL"><code>em:homepageURL</code></a>
element in its "install.rdf".</p></td>
</tr>
* `preferences` - *experimental*
An array of JSON objects that use the following keys `name`, `type`, `value`,
`title`, and `description`. These JSON objects will be used to automatically
create a preferences interface for the addon in the Add-ons Manager.
For more information see the documentation of [simple-prefs](modules/sdk/simple-prefs.html).
<tr>
<td id="icon"><code>icon</code></td>
<td><p>The relative path from the root of the add-on to a
PNG file containing the icon for the add-on. Defaults to
<code>"icon.png"</code>.</p>
<p>This value will be used as the add-on's
<a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#iconURL"><code>em:iconURL</code></a>
element in its "install.rdf".</p>
<p>The icon may be up to 48x48 pixels in size.</p></td>
</tr>
* `license` - the name of the license as a String, with an optional
URL in parentheses.
<tr>
<td id="icon64"><code>icon64</code></td>
<td><p>The relative path from the root of the add-on to a
PNG file containing the large icon for the add-on. Defaults to
<code>"icon64.png"</code>.</p>
<p>This value will be used as the add-on's
<a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#icon64URL"><code>em:icon64URL</code></a>
element in its "install.rdf".</p>
<p>The icon may be up to 64x64 pixels in size.</p></td>
</tr>
* `id` - a globally unique identifier for the package. When the package is
built as an XPI, this is used as the add-on's `em:id` element in its
`install.rdf`. See the
[Program ID page](dev-guide/guides/program-id.html).
<tr>
<td id="id"><code>id</code></td>
<td><p>A globally unique identifier for the add-on.</p>
<p>This value will be used as the add-on's
<a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#id"><code>em:id</code></a>
element in its "install.rdf".</p>
<p>See the <a href="dev-guide/guides/program-id.html">Program ID documentation</a>.</p></td>
</tr>
* `version` - a String representing the version of the package. If the
package is ever built as an XPI, this is used as the add-on's
`em:version` element in its `install.rdf`.
<tr>
<td id="lib"><code>lib</code></td>
<td><p>String representing the top-level module directory provided in
this add-on. Defaults to <code>"lib"</code>.</p></td>
</tr>
* `dependencies` - a String or Array of Strings representing package
names that this package requires in order to function properly.
<tr>
<td id="license"><code>license</code></td>
<td><p>The name of the license under which the add-on is distributed, with an optional
URL in parentheses. Defaults to <code>"MPL 2.0"</code>.</p></td>
</tr>
* `lib` - a String representing the top-level module directory provided in
this package. Defaults to `"lib"`.
* `tests` - a String representing the top-level module directory containing
test suites for this package. Defaults to `"tests"`.
* `packages` - a String or Array of Strings representing paths to
directories containing additional packages, defaults to
`"packages"`.
* `main` - a String representing the name of a program module that is
<tr>
<td id="main"><code>main</code></td>
<td><p>String representing the name of a program module that is
located in one of the top-level module directories specified by
`lib`. Defaults to `"main"`.
<a href="dev-guide/package-spec.html#lib"><code>lib</code></a>.
Defaults to <code>"main"</code>.</p></td>
</tr>
* `harnessClassID` - a String in the GUID format:
`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`.
<tr>
<td id="name"><code>name</code></td>
<td><p>The add-on's name. This name cannot contain spaces or periods, and
defaults to the name of the parent directory.</p><p>When the add-on is
built as an XPI, if the <a href="dev-guide/package-spec.html#fullName"><code>fullName</code></a>
key is not present, <code>name</code> is used as the add-on's
<a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#name"><code>em:name</code></a>
element in its "install.rdf".</p></td>
</tr>
* `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.
<tr>
<td id="packages"><code>packages</code></td>
<td><p>String or array of strings representing paths to
directories containing additional packages. Defaults to
<code>"packages"</code>.</p></td>
</tr>
## Documentation ##
<tr>
<td id="permissions"><code>permissions</code></td>
<td><p>A set of permissions that the add-on needs.</p>
<p><strong><code>private-browsing</code></strong>: a boolean
indicating whether or not the
add-on supports private browsing. If this value is not <code>true</code>
or is omitted, then the add-on will not see any private windows or
objects, such as tabs, that are associated with private windows. See the
documentation for the
<a href="modules/sdk/private-browsing.html"><code>private-browsing</code> module</a>.</p>
</td>
</tr>
A package may optionally contain a
[Markdown](http://daringfireball.net/projects/markdown/)-formatted file
called `README.md` in its root directory. Package-browsing tools may display
this file to developers.
<tr>
<td id="preferences"><code>preferences</code></td>
<td><p>An array of JSON objects that use the following keys:
<code>name</code>,<code>type</code>, <code>value</code>,
<code>title</code>, and <code>description</code>. These JSON objects will be used to
create a preferences interface for the add-on in the Add-ons Manager.</p>
<p>See the documentation for the
<a href="modules/sdk/simple-prefs.html"><code>simple-prefs</code> module</a>.</p></td>
</tr>
Additionally, Markdown files can be placed in an optional `docs`
directory. When package-browsing tools are asked to show the
documentation for a module, they will look in this directory for a
`.md` file with the module's name. Thus, for instance, if a user
browses to a module at `lib/foo/bar.js`, the package-browsing tool
will look for a file at `docs/foo/bar.js` to represent the module's
API documentation.
<tr>
<td id="tests"><code>tests</code></td>
<td><p>String representing the top-level module directory containing
test suites for this package. Defaults to <code>"tests"</code>.</p></td>
</tr>
## Data Resources ##
<tr>
<td id="translators"><code>translators</code></td>
<td><p>An array of strings listing translators of this add-on.</p>
<p>These values will be used as the add-on's
<a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#translator"><code>em:translator</code></a>
elements in its "install.rdf".</p></td>
</tr>
Packages may optionally contain a directory called `data` into which
arbitrary files may be placed, such as images or text files. The
URL for these resources may be reached using the
[self](modules/sdk/self.html) module.
<tr>
<td id="version"><code>version</code></td>
<td><p>String representing the version of the add-on. Defaults to
<code>"0.1"</code>.</p>
<p>This value will be used as the add-on's
<a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#version"><code>em:version</code></a>
element in its "install.rdf".</p></td>
</tr>
</table>
[Markdown]: http://daringfireball.net/projects/markdown/
[non-bootstrapped XUL extension]: #guide/xul-extensions

Просмотреть файл

@ -50,8 +50,12 @@ the matcher tells the main add-on code, which displays the annotation panel.
We'll use the `simple-storage` module to store annotations.
Because we are recording potentially sensitive information, we want to prevent
the user creating annotations when in private browsing mode, so we'll use the
`private-browsing` module for that.
the user creating annotations when in private browsing mode. The simplest way
to do this is to omit the
[`"private-browsing"` key](dev-guide/package-spec.html#permissions) from the
add-on's "package.json" file. If we do this, then the add-on won't see any
private windows, and the annotator's widget will not appear in any private
windows.
## Getting Started ##

Просмотреть файл

@ -277,93 +277,20 @@ under quota.)
## Respecting Private Browsing ##
Since annotations record the user's browsing history we should prevent the user
from creating annotations while the browser is in
[Private Browsing](http://support.mozilla.com/en-US/kb/Private%20Browsing) mode.
Since annotations record the user's browsing history we should avoid recording
annotations in private windows.
First let's import the `private-browsing` module into `main.js`:
There's a very simple way to do this: do nothing. By omitting the
[`"private-browsing"` key](dev-guide/package-spec.html#permissions) from the
annotator's "package.json" file, the annotator opts out of private browsing
altogether.
var privateBrowsing = require('sdk/private-browsing');
This means that its widget will not appear on any private windows and its
selector and matcher content scripts won't run, so the user won't be able to
enter any annotations in private windows.
We already have a variable `annotatorIsOn` that we use to indicate whether the
user can enter annotations. But we don't want to use that here, because we want
to remember the underlying state so that when they exit Private Browsing the
annotator is back in whichever state it was in before.
So we'll implement a function defining that to enter annotations, the annotator
must be active *and* Private Browsing must be off:
function canEnterAnnotations() {
return (annotatorIsOn && !privateBrowsing.isActive);
}
Next, everywhere we previously used `annotatorIsOn` directly, we'll call this
function instead:
function activateSelectors() {
selectors.forEach(
function (selector) {
selector.postMessage(canEnterAnnotations());
});
}
<br>
function toggleActivation() {
annotatorIsOn = !annotatorIsOn;
activateSelectors();
return canEnterAnnotations();
}
<br>
var selector = pageMod.PageMod({
include: ['*'],
contentScriptWhen: 'ready',
contentScriptFile: [data.url('jquery-1.4.2.min.js'),
data.url('selector.js')],
onAttach: function(worker) {
worker.postMessage(canEnterAnnotations());
selectors.push(worker);
worker.port.on('show', function(data) {
annotationEditor.annotationAnchor = data;
annotationEditor.show();
});
worker.on('detach', function () {
detachWorker(this, selectors);
});
}
});
We want to stop the user changing the underlying activation state when in
Private Browsing mode, so we'll edit `toggleActivation` again:
function toggleActivation() {
if (privateBrowsing.isActive) {
return false;
}
annotatorIsOn = !annotatorIsOn;
activateSelectors();
return canEnterAnnotations();
}
Finally, inside the `main` function, we'll add the following code to handle
changes in Private Browsing state by changing the icon and notifying the
selectors:
privateBrowsing.on('start', function() {
widget.contentURL = data.url('widget/pencil-off.png');
activateSelectors();
});
privateBrowsing.on('stop', function() {
if (canEnterAnnotations()) {
widget.contentURL = data.url('widget/pencil-on.png');
activateSelectors();
}
});
Try it: execute `cfx run`, and experiment with switching the annotator on and
off while in and out of Private Browsing mode.
Try it: execute cfx run and open a new private window: you should no longer
see the annotator's widget.
Now we can create and store annotations, the last piece is to
[display them when the user loads the
page](dev-guide/tutorials/annotator/displaying.html).
[display them when the user loads the page](dev-guide/tutorials/annotator/displaying.html).

Просмотреть файл

@ -10,7 +10,7 @@ incompatible changes to them in future releases.</span>
The [guide to event-driven programming with the SDK](dev-guide/guides/events.html)
describes how to consume events: that is, how to listen to events generated
by event targets. For example, you can listen to [`private-browsing`'s `start` event](modules/sdk/private-browsing.html#start) or the
by event targets. For example, you can listen to the [`tabs` module's `ready` event](modules/sdk/tabs.html#ready) or the
[`Panel` object's `show` event](modules/sdk/panel.html#show).
With the SDK, it's also simple to implement your own event targets.

Просмотреть файл

@ -5,12 +5,6 @@
<!-- contributed by Drew Willcoxon [adw@mozilla.com] -->
<!-- edited by Noelle Murata [fiveinchpixie@gmail.com] -->
The `context-menu` module lets you add items to Firefox's page context menu.
Introduction
------------
The `context-menu` API provides a simple, declarative way to add items to the
page's context menu. You can add items that perform an action when clicked,
submenus, and menu separators.
@ -396,6 +390,15 @@ 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.
## Private Windows ##
If your add-on has not opted into private browsing, then any menus or
menu items that you add will not appear in context menus belonging to
private browser windows.
To learn more about private windows, how to opt into private browsing, and how
to support private browsing, refer to the
[documentation for the `private-browsing` module](modules/sdk/private-browsing.html).
More Examples
-------------

Просмотреть файл

@ -259,6 +259,16 @@ The following add-on creates a widget which, when clicked, highlights all the
}
});
## Private Windows ##
If your add-on has not opted into private browsing, then your page-mods will
not attach content scripts to documents loaded into private windows, even if
their URLs match the pattern you have specified.
To learn more about private windows, how to opt into private browsing, and how
to support private browsing, refer to the
[documentation for the `private-browsing` module](modules/sdk/private-browsing.html).
<api name="PageMod">
@class
A page-mod object. Once created a page-mod will execute the supplied content

Просмотреть файл

@ -31,6 +31,15 @@ in preparation for the next time it is shown.
Your add-on can receive notifications when a panel is shown or hidden by
listening to its `show` and `hide` events.
Opening a panel will close an already opened panel.
<div class="warning">
If your add-on has
<a href="modules/sdk/private-browsing.html#Opting into private browsing">opted into private browsing</a>,
then you can't use panels in your add-on. This is due to a platform bug which we expect to
be fixed in Firefox 21.
</div>
## Panel Content ##
The panel's content is specified as HTML, which is loaded from the URL
@ -376,6 +385,18 @@ when applying your own styles. For example, if you set the panel's
`background-color` property to `white` and do not set the `color` property,
then the panel's text will be invisible on OS X although it looks fine on Ubuntu.
## Private Browsing ##
If your add-on has
[opted into private browsing](modules/sdk/private-browsing.html#Opting into private browsing),
then **you can't use panels in your add-on**. This is due to a platform bug which we expect to
be fixed in Firefox 21.
If your add-on has not opted into private browsing, and it calls `panel.show()`
when the currently active window is a
[private window](modules/sdk/private-browsing.html#Per-window private browsing),
then the panel will not be shown.
<api name="Panel">
@class
The Panel object represents a floating modal dialog that can by an add-on to
@ -400,6 +421,10 @@ Creates a panel.
The width of the panel in pixels. Optional.
@prop [height] {number}
The height of the panel in pixels. Optional.
@prop [focus] {boolean}
Set to `false` to prevent taking the focus away when the panel is shown.
Only turn this off if necessary, to prevent accessibility issue.
Optional, default to `true`.
@prop [contentURL] {string}
The URL of the content to load in the panel.
@prop [allow] {object}
@ -472,6 +497,12 @@ The height of the panel in pixels.
The width of the panel in pixels.
</api>
<api name="focus">
@property {boolean}
Whether of not focus will be taken away when the panel is shown.
This property is read-only.
</api>
<api name="contentURL">
@property {string}
The URL of content loaded into the panel. This can point to

Просмотреть файл

@ -6,90 +6,170 @@
<!-- edited by Noelle Murata [fiveinchpixie@gmail.com] -->
<!-- contributed by Irakli Gozalishvili [gozala@mozilla.com] -->
The `private-browsing` module allows you to access Firefox's private browsing
mode, detecting if it is active and when its state changes.
## Per-window private browsing ##
This module is available in all applications. However, only Firefox will ever
transition into or out of private browsing mode. For all other applications,
`pb.isActive` will always be `false`, and none of the events will be emitted.
Firefox 20 introduces per-window private browsing. This means that private
browsing status is a property of an individual browser window.
<div class="warning">
The <a href="modules/sdk/private-browsing.html#activate()"><code>activate</code></a>
and <a href="modules/sdk/private-browsing.html#deactivate()"><code>deactivate</code></a>
functions, <a href="modules/sdk/private-browsing.html#isActive"><code>isActive</code></a>
property, <a href="modules/sdk/private-browsing.html#start"><code>start</code></a>,
and <a href="modules/sdk/private-browsing.html#stop"><code>stop</code></a>
events are all
now deprecated due to per-window private browsing. They will continue to work
until version 1.13 of the SDK. From version 1.13 onwards they will still exist
but will have no effect when called.
</div>
The user enters
private browsing by opening a new private browser window. When they do this,
any existing non-private windows are kept open, so the user will typically
have both private and non-private windows open at the same time.
<api name="isActive">
@property {boolean}
This read-only boolean is `true` if global private browsing mode is turned on.
## Opting into private browsing ##
<div class="warning">
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`.
</div>
</api>
Add-ons built using the SDK must opt into private browsing by setting the
following key in their [`package.json`](dev-guide/package-spec.html) file:
<pre>"permissions": {"private-browsing": true}</pre>
If an add-on has not opted in, then the high-level SDK modules will not
expose private windows, or objects (such as tabs) that are associated
with private windows:
* the [`windows`](modules/sdk/windows.html) module will not list any
private browser windows, generate any events for private browser windows,
or let the add-on open any private browser windows
* the [`tabs`](modules/sdk/tabs.html) module will not list any tabs that
belong to private browser windows, and the add-on won't receive any events
for such tabs
* any [`widgets`](modules/sdk/widget.html) will not be displayed in
private browser windows
* any menus or menu items created using the
[`context-menu`](modules/sdk/context-menu.html) will not be shown in
context menus that belong to private browser windows
* the [`page-mod`](modules/sdk/page-mod.html) module will not attach
content scripts to documents belonging to private browser windows
* any [`panel`](modules/sdk/panel.html) objects will not be shown if the
active window is a private browser window
* the [`selection`](modules/sdk/selection.html) module will not include
any selections made in private browser windows
Add-ons that have opted in:
* will see private windows, so they will need to
use the `private-browsing` module to check whether objects are private,
so as to avoid storing data derived from such objects.
* will not be able to use panels in their code. This is due to a platform
restriction which will be fixed in Firefox 21.
Additionally, add-ons that use low-level modules such as
[`window/utils`](modules/sdk/window/utils.html) may see private browser
windows with certain functions, even if they have not explicitly opted
into private browsing.
## Respecting private browsing ##
The `private-browsing` module exports a single function
[`isPrivate()`](modules/sdk/private-browsing.html#isPrivate(object))
that takes an object, which may be a
[`BrowserWindow`](modules/sdk/windows.html#BrowserWindow),
[`tab`](modules/sdk/tabs.html#Tab), or
[`worker`](modules/sdk/content/worker.html#Worker),
as an argument. It returns `true` only if the object is:
* a private window, or
* a tab belonging to a private window, or
* a worker that's associated with a document hosted in a private window
Add-ons can use this API to decide whether or not to store user data.
For example, here's an add-on that stores the titles of tabs the user loads,
and uses `isPrivate()` to exclude the titles of tabs that were loaded into
private windows:
var simpleStorage = require("simple-storage");
if (!simpleStorage.storage.titles)
simpleStorage.storage.titles = [];
require("tabs").on("ready", function(tab) {
if (!require("private-browsing").isPrivate(tab)) {
console.log("storing...");
simpleStorage.storage.titles.push(tab.title);
}
else {
console.log("not storing, private data");
}
});
Here's an add-on that uses a [page-mod](modules/sdk/page-mod.html) to log
the content of pages loaded by the user, unless the page is private. In
the handler for the page-mod's [`attach`](modules/sdk/page-mod.html#attach)
event, it passes the worker into `isPrivate()`:
var pageMod = require("sdk/page-mod");
var privateBrowsing = require("sdk/private-browsing");
var loggingScript = "self.port.on('log-content', function() {" +
" console.log(document.body.innerHTML);" +
"});";
function logPublicPageContent(worker) {
if (privateBrowsing.isPrivate(worker)) {
console.log("private window, doing nothing");
}
else {
worker.port.emit("log-content");
}
}
pageMod.PageMod({
include: "*",
contentScript: loggingScript,
onAttach: logPublicPageContent
});
## Tracking private-browsing exit ##
Sometimes it can be useful to cache some data from private windows while they
are open, as long as you don't store it after the private browsing windows
have been closed. For example, the "Downloads" window might want to display
all downloads while there are still some private windows open, then clean out
all the private data when all private windows have closed.
To do this with the SDK, you can listen to the system event named
"last-pb-context-exited":
var events = require("sdk/system/events");
function listener(event) {
console.log("last private window closed");
}
events.on("last-pb-context-exited", listener);
## Working with Firefox 19 ##
In Firefox 19, private browsing is a global property for the entire browser.
When the user enters private browsing, the existing browsing session is
suspended and a new blank window opens. This window is private, as are any
other windows opened until the user chooses to exit private browsing, at which
point all private windows are closed and the user is returned to the original
non-private session.
When running on Firefox 19, `isPrivate()` will return true if and only if
the user has global private browsing enabled.
<api name="isPrivate">
@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`.
</api>
<api name="activate">
@function
Turns on global private browsing mode.
<div class="warning">
This function is deprecated. It will continue to work until version 1.13 of the SDK.
From version 1.13 onwards it will still exist but will have no effect when called.
</div>
</api>
<api name="deactivate">
@function
Turns off global private browsing mode.
<div class="warning">
This function is deprecated. It will continue to work until version 1.13 of the SDK.
From version 1.13 onwards it will still exist but will have no effect when called.
</div>
</api>
<api name="start">
@event
Emitted immediately after global private browsing begins.
var pb = require("sdk/private-browsing");
pb.on("start", function() {
// Do something when the browser starts private browsing mode.
});
<div class="warning">
This event is deprecated. It will continue to work until version 1.13 of the SDK.
From version 1.13 onwards this event will not be emitted.
</div>
</api>
<api name="stop">
@event
Emitted immediately after global private browsing ends.
var pb = require("sdk/private-browsing");
pb.on("stop", function() {
// Do something when the browser stops private browsing mode.
});
<div class="warning">
This event is deprecated. It will continue to work until version 1.13 of the SDK.
From version 1.13 onwards this event will not be emitted.
</div>
Function to check whether the given object is private. It takes an
object as an argument, and returns `true` only if the object is:
* a private [`BrowserWindow`](modules/sdk/windows.html#BrowserWindow) or
* a [`tab`](modules/sdk/tabs.html#Tab) belonging to a private window, or
* a [`worker`](modules/sdk/content/worker.html#Worker) that's associated
with a document hosted in a private window
@param [object] {any}
The object to check. This may be a
[`BrowserWindow`](modules/sdk/windows.html#BrowserWindow),
[`tab`](modules/sdk/tabs.html#Tab), or
[`worker`](modules/sdk/content/worker.html#Worker).
</api>

Просмотреть файл

@ -30,6 +30,14 @@ Discontiguous selections can be accessed by iterating over the `selection`
module itself. Each iteration yields a `Selection` object from which `text`,
`html`, and `isContiguous` properties can be accessed.
## Private Windows ##
If your add-on has not opted into private browsing, then you won't see any
selections made in tabs that are hosted by private browser windows.
To learn more about private windows, how to opt into private browsing, and how
to support private browsing, refer to the
[documentation for the `private-browsing` module](modules/sdk/private-browsing.html).
Examples
--------

Просмотреть файл

@ -53,8 +53,9 @@ downgrade
<api name="isPrivateBrowsingSupported">
@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.
This property indicates whether or not the add-on supports private browsing.
It comes from the [`private-browsing` key](dev-guide/package-spec.html#permissions)
in the add-on's `package.json` file.
</api>
<api name="data">

Просмотреть файл

@ -172,33 +172,13 @@ data you remove is up to you. For example:
Private Browsing
----------------
If your storage is related to your users' Web history, personal information, or
other sensitive data, your add-on should respect [private browsing mode][SUMO].
While private browsing mode is active, you should not store any sensitive data.
Because any kind of data can be placed into simple storage, support for private
browsing is not built into the module. Instead, use the
[`private-browsing`](modules/sdk/private-browsing.html) module to
check private browsing status and respond accordingly.
For example, the URLs your users visit should not be stored during private
browsing. If your add-on records the URL of the selected tab, here's how you
might handle that:
ss.storage.history = [];
var privateBrowsing = require("sdk/private-browsing");
if (!privateBrowsing.isActive) {
var url = getSelectedTabURL();
ss.storage.history.push(url);
}
For more information on supporting private browsing, see its [Mozilla Developer
Network documentation][MDN]. While that page does not apply specifically to
SDK-based add-ons (and its code samples don't apply at all), you should follow
its guidance on best practices and policies.
[SUMO]: http://support.mozilla.com/en-US/kb/Private+Browsing
[MDN]: https://developer.mozilla.org/En/Supporting_private_browsing_mode
other sensitive data, your add-on should respect
[private browsing](http://support.mozilla.com/en-US/kb/Private+Browsing).
To read about how to opt into private browsing mode and how to use the
SDK to avoid storing user data associated with private windows, refer to the
documentation for the
[`private-browsing` module](modules/sdk/private-browsing.html).
<api name="storage">
@property {object}

Просмотреть файл

@ -13,10 +13,12 @@ You can find a list of events dispatched by firefox codebase
var events = require("sdk/system/events");
var { Ci } = require("chrome");
events.on("http-on-modify-request", function (event) {
function listener(event) {
var channel = event.subject.QueryInterface(Ci.nsIHttpChannel);
channel.setRequestHeader("User-Agent", "MyBrowser/1.0", false);
});
}
events.on("http-on-modify-request", listener);
<api name="emit">
@function

Просмотреть файл

@ -68,6 +68,18 @@ content:
});
});
## Private Windows ##
If your add-on has not opted into private browsing, then you won't see any
tabs that are hosted by private browser windows.
Tabs hosted by private browser windows won't be seen if you enumerate the
`tabs` module itself, and you won't receive any events for them.
To learn more about private windows, how to opt into private browsing, and how
to support private browsing, refer to the
[documentation for the `private-browsing` module](modules/sdk/private-browsing.html).
<api name="activeTab">
@property {Tab}
@ -140,8 +152,8 @@ If present and true, the new tab will be opened to the right of the active tab
and will not be active. This is an optional property.
@prop isPrivate {boolean}
Boolean which will determine if a private tab should be opened.
Private browsing mode must be supported in order to do this.
Boolean which will determine whether the new tab should be private or not.
If your add-on does not support private browsing this will have no effect.
See the [private-browsing](modules/sdk/private-browsing.html) documentation for more information.
@prop [isPinned] {boolean}

Просмотреть файл

@ -39,8 +39,17 @@ A browser window.
<api name="getTabs">
@function
Returns the tabs for the specified `window`, or the tabs
across all the browser's windows if `window` is omitted.
Returns the tabs for the specified `window`.
If you omit `window`, this function will return tabs
across all the browser's windows. However, if your add-on
has not opted into private browsing, then the function will
exclude all tabs that are hosted by private browser windows.
To learn more about private windows, how to opt into private browsing, and how
to support private browsing, refer to the
[documentation for the `private-browsing` module](modules/sdk/private-browsing.html).
@param window {nsIWindow}
Optional.
@returns {Array}

Просмотреть файл

@ -346,6 +346,15 @@ listener, the panel will not be anchored:
See [bug 638142](https://bugzilla.mozilla.org/show_bug.cgi?id=638142).
## Private Windows ##
If your add-on has not opted into private browsing, then your widget will
not appear in any private browser windows.
To learn more about private windows, how to opt into private browsing, and how
to support private browsing, refer to the
[documentation for the `private-browsing` module](modules/sdk/private-browsing.html).
## Examples ##
For conciseness, these examples create their content scripts as strings and use
@ -685,6 +694,9 @@ The related `WidgetView` object.
<api name="click">
@event
This event is emitted when the widget is clicked.
@argument {WidgetView}
Listeners are passed a single argument which is the `WidgetView` that triggered the click event.
</api>
<api name="message">

Просмотреть файл

@ -5,6 +5,21 @@
The `window/utils` module provides helper functions for working with
application windows.
## Private Windows ##
With this module your add-on will see private browser windows
even if it has not explicitly opted into private browsing, so you need
to take care not to store any user data derived from private browser windows.
The exception is the [`windows()`](modules/sdk/window/utils.html#windows())
function which returns an array of currently opened windows. Private windows
will not be included in this list if your add-on has not opted into private
browsing.
To learn more about private windows, how to opt into private browsing, and how
to support private browsing, refer to the
[documentation for the `private-browsing` module](modules/sdk/private-browsing.html).
<api name="getMostRecentBrowserWindow">
@function
Get the topmost browser window, as an
@ -169,6 +184,11 @@ element.
@function
Returns an array of all currently opened windows.
Note that these windows may still be loading.
If your add-on has not
[opted into private browsing](modules/sdk/private-browsing.html),
any private browser windows will not be included in the array.
@returns {Array}
Array of `nsIDOMWindow` instances.
</api>

Просмотреть файл

@ -4,21 +4,29 @@
<!-- contributed by Felipe Gomes [felipc@gmail.com] -->
The `windows` module provides basic functions for working with browser
windows. With this module, you can:
The `windows` module provides easy access to browser windows, their
tabs, and open/close related functions and events.
* [enumerate the currently opened browser windows](modules/sdk/windows.html#browserWindows)
* [open new browser windows](modules/sdk/windows.html#open(options))
* [listen for common window events such as open and close](modules/sdk/windows.html#Events)
This module currently only supports browser windows and does not provide
access to non-browser windows such as the Bookmarks Library, preferences
or other non-browser windows created via add-ons.
## Private Windows ##
If your add-on has not opted into private browsing, then you won't see any
private browser windows. Private browser windows won't appear in the
[`browserWindows`](modules/sdk/windows.html#browserWindows) property, you
won't receive any window events, and you won't be able to open private
windows.
To learn more about private windows, how to opt into private browsing, and how
to support private browsing, refer to the
[documentation for the `private-browsing` module](modules/sdk/private-browsing.html).
<api name="browserWindows">
@property {List}
An object that contains various properties and methods to access
functionality from browser windows, such as opening new windows, accessing
their tabs or switching the current active window.
`browserWindows` provides access to all the currently open browser windows:
`browserWindows` provides access to all the currently open browser windows as
[BrowserWindow](modules/sdk/windows.html#BrowserWindow) objects.
var windows = require("sdk/windows");
for each (var window in windows.browserWindows) {
@ -27,9 +35,7 @@ their tabs or switching the current active window.
console.log(windows.browserWindows.length);
Object emits all the events listed under "Events" section.
####Examples####
This object emits all the events listed under the "Events" section:
var windows = require("sdk/windows").browserWindows;
@ -119,8 +125,8 @@ String URL to be opened in the new window.
This is a required property.
@prop isPrivate {boolean}
Boolean which will determine if a private window should be opened.
Private browsing mode must be supported in order to do this.
Boolean which will determine whether the new window should be private or not.
If your add-on does not support private browsing this will have no effect.
See the [private-browsing](modules/sdk/private-browsing.html) documentation for more information.
@prop [onOpen] {function}
@ -185,8 +191,7 @@ Returns `true` if the window is in private browsing mode, and `false` otherwise.
<div class="warning">
This property is deprecated.
From version 1.14, please consider using following code instead:<br/>
<code>require("private-browsing").isPrivate(browserWindow)</code>
From version 1.14, use the <a href="modules/sdk/private-browsing.html#isPrivate()">private-browsing module's <code>isPrivate()</code></a> function instead.
</div>
</api>

Просмотреть файл

@ -8,7 +8,6 @@ var data = require('self').data;
var panels = require('panel');
var simpleStorage = require('simple-storage');
var notifications = require("notifications");
var privateBrowsing = require('private-browsing');
/*
Global variables
@ -32,13 +31,6 @@ function updateMatchers() {
});
}
/*
You can add annotations iff the add-on is on AND private browsing is off
*/
function canEnterAnnotations() {
return (annotatorIsOn && !privateBrowsing.isActive);
}
/*
Constructor for an Annotation object
*/
@ -66,21 +58,17 @@ Function to tell the selector page mod that the add-on has become (in)active
function activateSelectors() {
selectors.forEach(
function (selector) {
selector.postMessage(canEnterAnnotations());
selector.postMessage(annotatorIsOn);
});
}
/*
Toggle activation: update the on/off state and notify the selectors.
Toggling activation is disabled when private browsing is on.
*/
function toggleActivation() {
if (privateBrowsing.isActive) {
return false;
}
annotatorIsOn = !annotatorIsOn;
activateSelectors();
return canEnterAnnotations();
return annotatorIsOn;
}
function detachWorker(worker, workerArray) {
@ -138,7 +126,7 @@ display it.
contentScriptFile: [data.url('jquery-1.4.2.min.js'),
data.url('selector.js')],
onAttach: function(worker) {
worker.postMessage(canEnterAnnotations());
worker.postMessage(annotatorIsOn);
selectors.push(worker);
worker.port.on('show', function(data) {
annotationEditor.annotationAnchor = data;
@ -217,22 +205,6 @@ recent annotations until we are back in quota.
simpleStorage.storage.annotations.pop();
});
/*
We listen for private browsing start/stop events to change the widget icon
and to notify the selectors of the change in state.
*/
privateBrowsing.on('start', function() {
widget.contentURL = data.url('widget/pencil-off.png');
activateSelectors();
});
privateBrowsing.on('stop', function() {
if (canEnterAnnotations()) {
widget.contentURL = data.url('widget/pencil-on.png');
activateSelectors();
}
});
/*
The matcher page-mod locates anchors on web pages and prepares for the
annotation to be displayed.

Просмотреть файл

@ -224,7 +224,7 @@ const ContentWorker = Object.freeze({
});
},
injectMessageAPI: function injectMessageAPI(exports, pipe) {
injectMessageAPI: function injectMessageAPI(exports, pipe, console) {
let { eventEmitter: port, emit : portEmit } =
ContentWorker.createEventEmitter(pipe.emit.bind(null, "event"));
@ -293,7 +293,7 @@ const ContentWorker = Object.freeze({
ContentWorker.injectConsole(exports, pipe);
ContentWorker.injectTimers(exports, chromeAPI, pipe, exports.console);
ContentWorker.injectMessageAPI(exports, pipe);
ContentWorker.injectMessageAPI(exports, pipe, exports.console);
if ( options !== undefined ) {
ContentWorker.injectOptions(exports, options);
}

Просмотреть файл

@ -378,6 +378,13 @@ const Worker = EventEmitter.compose({
on: Trait.required,
_removeAllListeners: Trait.required,
// List of messages fired before worker is initialized
get _earlyEvents() {
delete this._earlyEvents;
this._earlyEvents = [];
return this._earlyEvents;
},
/**
* Sends a message to the worker's global scope. Method takes single
* argument, which represents data to be sent to the worker. The data may
@ -390,13 +397,13 @@ const Worker = EventEmitter.compose({
* implementing `onMessage` function in the global scope of this worker.
* @param {Number|String|JSON} data
*/
postMessage: function postMessage(data) {
if (!this._contentWorker)
throw new Error(ERR_DESTROYED);
if (this._frozen)
throw new Error(ERR_FROZEN);
this._contentWorker.emit("message", data);
postMessage: function (data) {
let args = ['message'].concat(Array.slice(arguments));
if (!this._inited) {
this._earlyEvents.push(args);
return;
}
processMessage.apply(this, args);
},
/**
@ -410,9 +417,8 @@ const Worker = EventEmitter.compose({
// before Worker.constructor gets called. (For ex: Panel)
// create an event emitter that receive and send events from/to the worker
let self = this;
this._port = EventEmitterTrait.create({
emit: function () self._emitEventToContent(Array.slice(arguments))
emit: this._emitEventToContent.bind(this)
});
// expose wrapped port, that exposes only public properties:
@ -438,24 +444,13 @@ const Worker = EventEmitter.compose({
* Emit a custom event to the content script,
* i.e. emit this event on `self.port`
*/
_emitEventToContent: function _emitEventToContent(args) {
// We need to save events that are emitted before the worker is
// initialized
_emitEventToContent: function () {
let args = ['event'].concat(Array.slice(arguments));
if (!this._inited) {
this._earlyEvents.push(args);
return;
}
if (this._frozen)
throw new Error(ERR_FROZEN);
// We throw exception when the worker has been destroyed
if (!this._contentWorker) {
throw new Error(ERR_DESTROYED);
}
// Forward the event to the WorkerSandbox object
this._contentWorker.emit.apply(null, ["event"].concat(args));
processMessage.apply(this, args);
},
// Is worker connected to the content worker sandbox ?
@ -465,13 +460,6 @@ const Worker = EventEmitter.compose({
// Content script should not be reachable if frozen.
_frozen: true,
// List of custom events fired before worker is initialized
get _earlyEvents() {
delete this._earlyEvents;
this._earlyEvents = [];
return this._earlyEvents;
},
constructor: function Worker(options) {
options = options || {};
@ -525,9 +513,11 @@ const Worker = EventEmitter.compose({
this._inited = true;
this._frozen = false;
// Flush all events that have been fired before the worker is initialized.
this._earlyEvents.forEach((function (args) this._emitEventToContent(args)).
bind(this));
// Process all events and messages that were fired before the
// worker was initialized.
this._earlyEvents.forEach((function (args) {
processMessage.apply(this, args);
}).bind(this));
},
_documentUnload: function _documentUnload(subject, topic, data) {
@ -590,7 +580,7 @@ const Worker = EventEmitter.compose({
if (this._windowID) {
this._windowID = null;
observers.remove("inner-window-destroyed", this._documentUnload);
this._earlyEvents.slice(0, this._earlyEvents.length);
this._earlyEvents.length = 0;
this._emit("detach");
}
},
@ -622,4 +612,20 @@ const Worker = EventEmitter.compose({
*/
_injectInDocument: false
});
/**
* Fired from postMessage and _emitEventToContent, or from the _earlyMessage
* queue when fired before the content is loaded. Sends arguments to
* contentWorker if able
*/
function processMessage () {
if (!this._contentWorker)
throw new Error(ERR_DESTROYED);
if (this._frozen)
throw new Error(ERR_FROZEN);
this._contentWorker.emit.apply(null, Array.slice(arguments));
}
exports.Worker = Worker;

Просмотреть файл

@ -15,7 +15,7 @@ const { getInnerId, getOuterId, windows, isDocumentLoaded, isBrowser,
getMostRecentBrowserWindow, getMostRecentWindow } = require('../window/utils');
const errors = require('../deprecated/errors');
const { deprecateFunction } = require('../util/deprecate');
const { ignoreWindow } = require('sdk/private-browsing/utils');
const { ignoreWindow, isGlobalPBSupported } = require('sdk/private-browsing/utils');
const { isPrivateBrowsingSupported } = require('../self');
const windowWatcher = Cc['@mozilla.org/embedcomp/window-watcher;1'].
@ -24,7 +24,7 @@ const appShellService = Cc['@mozilla.org/appshell/appShellService;1'].
getService(Ci.nsIAppShellService);
// Bug 834961: ignore private windows when they are not supported
function getWindows() windows(null, { includePrivate: isPrivateBrowsingSupported });
function getWindows() windows(null, { includePrivate: isPrivateBrowsingSupported || isGlobalPBSupported });
/**
* An iterator for XUL windows currently in the application.

Просмотреть файл

@ -94,20 +94,23 @@ function emit(target, type, message /*, ...*/) {
* arguments.
*/
emit.lazy = function lazy(target, type, message /*, ...*/) {
let args = Array.slice(arguments, 2)
let listeners = observers(target, type).slice()
while (listeners.length) {
try {
yield listeners.shift().apply(target, args);
}
let args = Array.slice(arguments, 2);
let listeners = observers(target, type).slice();
let index = 0;
let count = listeners.length;
// If error event and there are no handlers then print error message
// into a console.
if (count === 0 && type === 'error') console.exception(message);
while (index < count) {
try { yield listeners[index].apply(target, args); }
catch (error) {
// If exception is not thrown by a error listener and error listener is
// registered emit `error` event. Otherwise dump exception to the console.
if (type !== 'error' && observers(target, 'error').length)
emit(target, 'error', error);
else
console.exception(error);
if (type !== 'error') emit(target, 'error', error);
else console.exception(error);
}
index = index + 1;
}
}
exports.emit = emit;

Просмотреть файл

@ -21,10 +21,11 @@ const { defer } = require("../core/promise");
const { when: unload } = require("../system/unload");
const { validateOptions, getTypeOf } = require("../deprecated/api-utils");
const { window } = require("../addon/window");
const { fromIterator } = require("../util/array");
// This cache is used to access friend properties between functions
// without exposing them on the public API.
let cache = [];
let cache = new Set();
let elements = new WeakMap();
function contentLoaded(target) {
@ -75,20 +76,13 @@ var HiddenFrame = Class({
});
exports.HiddenFrame = HiddenFrame
function isFrameCached(frame) {
// Function returns `true` if frame was already cached.
return cache.some(function(value) {
return value === frame
})
}
function addHidenFrame(frame) {
if (!(frame instanceof HiddenFrame))
throw Error("The object to be added must be a HiddenFrame.");
// This instance was already added.
if (isFrameCached(frame)) return frame;
else cache.push(frame);
if (cache.has(frame)) return frame;
else cache.add(frame);
let element = makeFrame(window.document, {
nodeName: "iframe",
@ -111,14 +105,14 @@ function removeHiddenFrame(frame) {
if (!(frame instanceof HiddenFrame))
throw Error("The object to be removed must be a HiddenFrame.");
if (!isFrameCached(frame)) return;
if (!cache.has(frame)) return;
// Remove from cache before calling in order to avoid loop
cache.splice(cache.indexOf(frame), 1);
cache.delete(frame);
emit(frame, "unload")
let element = frame.element
if (element) element.parentNode.removeChild(element)
}
exports.remove = removeHiddenFrame;
unload(function() cache.splice(0).forEach(removeHiddenFrame));
unload(function() fromIterator(cache).forEach(removeHiddenFrame));

Просмотреть файл

@ -17,16 +17,20 @@ const { validateOptions: valid } = require('./deprecated/api-utils');
const { Symbiont } = require('./content/content');
const { EventEmitter } = require('./deprecated/events');
const { setTimeout } = require('./timers');
const { on, off, emit } = require('./system/events');
const runtime = require('./system/runtime');
const { getDocShell } = require("./frame/utils");
const { getWindow } = require('./panel/window');
const { isPrivateBrowsingSupported } = require('./self');
const { isWindowPBSupported } = require('./private-browsing/utils');
const { getNodeView } = require('./view/core');
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
ON_SHOW = 'popupshown',
ON_HIDE = 'popuphidden',
validNumber = { is: ['number', 'undefined', 'null'] };
validNumber = { is: ['number', 'undefined', 'null'] },
validBoolean = { is: ['boolean', 'undefined', 'null'] },
ADDON_ID = require('./self').id;
if (isPrivateBrowsingSupported && isWindowPBSupported) {
throw Error('The panel module cannot be used with per-window private browsing at the moment, see Bug 816257');
@ -68,6 +72,9 @@ const Panel = Symbiont.resolve({
constructor: function Panel(options) {
this._onShow = this._onShow.bind(this);
this._onHide = this._onHide.bind(this);
this._onAnyPanelShow = this._onAnyPanelShow.bind(this);
on('sdk-panel-show', this._onAnyPanelShow);
this.on('inited', this._onSymbiontInit.bind(this));
this.on('propertyChange', this._onChange.bind(this));
@ -82,6 +89,12 @@ const Panel = Symbiont.resolve({
this.height = options.height;
if ('contentURL' in options)
this.contentURL = options.contentURL;
if ('focus' in options) {
var value = options.focus;
var validatedValue = valid({ $: value }, { $: validBoolean }).$;
this._focus =
(typeof validatedValue == 'boolean') ? validatedValue : this._focus;
}
this._init(options);
},
@ -91,6 +104,7 @@ const Panel = Symbiont.resolve({
this._removeAllListeners('hide');
this._removeAllListeners('propertyChange');
this._removeAllListeners('inited');
off('sdk-panel-show', this._onAnyPanelShow);
// defer cleanup to be performed after panel gets hidden
this._xulPanel = null;
this._symbiontDestructor(this);
@ -109,13 +123,16 @@ const Panel = Symbiont.resolve({
set height(value)
this._height = valid({ $: value }, { $: validNumber }).$ || this._height,
_height: 240,
/* Public API: Panel.focus */
get focus() this._focus,
_focus: true,
/* Public API: Panel.isShowing */
get isShowing() !!this._xulPanel && this._xulPanel.state == "open",
/* Public API: Panel.show */
show: function show(anchor) {
anchor = anchor || null;
anchor = anchor ? getNodeView(anchor) : null;
let anchorWindow = getWindow(anchor);
// If there is no open window, or the anchor is in a private window
@ -126,6 +143,7 @@ const Panel = Symbiont.resolve({
let document = anchorWindow.document;
let xulPanel = this._xulPanel;
let panel = this;
if (!xulPanel) {
xulPanel = this._xulPanel = document.createElementNS(XUL_NS, 'panel');
xulPanel.setAttribute("type", "arrow");
@ -165,7 +183,7 @@ const Panel = Symbiont.resolve({
xulPanel.appendChild(frame);
document.getElementById("mainPopupSet").appendChild(xulPanel);
}
let { width, height } = this, x, y, position;
let { width, height, focus } = this, x, y, position;
if (!anchor) {
// Open the popup in the middle of the window.
@ -210,13 +228,25 @@ const Panel = Symbiont.resolve({
xulPanel.firstChild.style.width = width + "px";
xulPanel.firstChild.style.height = height + "px";
// Only display xulPanel if Panel.hide() was not called
// after Panel.show(), but before xulPanel.openPopup
// was loaded
emit('sdk-panel-show', { data: ADDON_ID, subject: xulPanel });
// Prevent the panel from getting focus when showing up
// if focus is set to false
xulPanel.setAttribute("noautofocus",!focus);
// Wait for the XBL binding to be constructed
function waitForBinding() {
if (!xulPanel.openPopup) {
setTimeout(waitForBinding, 50);
return;
}
xulPanel.openPopup(anchor, position, x, y);
if (xulPanel.state !== 'hiding') {
xulPanel.openPopup(anchor, position, x, y);
}
}
waitForBinding();
@ -289,6 +319,8 @@ const Panel = Symbiont.resolve({
* text color.
*/
_applyStyleToDocument: function _applyStyleToDocument() {
if (this._defaultStyleApplied)
return;
try {
let win = this._xulPanel.ownerDocument.defaultView;
let node = win.document.getAnonymousElementByAttribute(
@ -309,6 +341,7 @@ const Panel = Symbiont.resolve({
container.insertBefore(style, container.firstChild);
else
container.appendChild(style);
this._defaultStyleApplied = true;
}
catch(e) {
console.error("Unable to apply panel style");
@ -333,6 +366,16 @@ const Panel = Symbiont.resolve({
this._emit('error', e);
}
},
/**
* When any panel is displayed, system-wide, close `this`
* panel unless it's the most recently displayed panel
*/
_onAnyPanelShow: function _onAnyPanelShow(e) {
if (e.subject !== this._xulPanel)
this.hide();
},
/**
* Notification that panel was fully initialized.
*/

Просмотреть файл

@ -6,11 +6,12 @@
const { getMostRecentBrowserWindow, windows: getWindows } = require('../window/utils');
const { ignoreWindow } = require('../private-browsing/utils');
const { isPrivateBrowsingSupported } = require('../self');
const { isGlobalPBSupported } = require('../private-browsing/utils');
function getWindow(anchor) {
let window;
let windows = getWindows("navigator:browser", {
includePrivate: isPrivateBrowsingSupported
includePrivate: isPrivateBrowsingSupported || isGlobalPBSupported
});
if (anchor) {

Просмотреть файл

@ -54,7 +54,7 @@ let isTabPBSupported = exports.isTabPBSupported =
!pbService && !!PrivateBrowsingUtils && is('Fennec') && satisfiesVersion(version, '>=20.0*');
function ignoreWindow(window) {
return !isPrivateBrowsingSupported && isWindowPrivate(window);
return !isPrivateBrowsingSupported && isWindowPrivate(window) && !isGlobalPBSupported;
}
exports.ignoreWindow = ignoreWindow;

Просмотреть файл

@ -7,7 +7,7 @@ const { Cc, Ci } = require('chrome');
const { Class } = require('../core/heritage');
const { tabNS, rawTabNS } = require('./namespace');
const { EventTarget } = require('../event/target');
const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL, getContentWindowForTab,
const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL, getTabContentWindow,
getTabForBrowser,
setTabURL, getOwnerWindow, getTabContentType, getTabId } = require('./utils');
const { emit } = require('../event/core');
@ -129,7 +129,7 @@ const Tab = Class({
// BUG 792946 https://bugzilla.mozilla.org/show_bug.cgi?id=792946
// TODO: fix this circular dependency
let { Worker } = require('./worker');
return Worker(options, tabNS(this).tab.browser.contentWindow);
return Worker(options, getTabContentWindow(tabNS(this).tab));
},
/**
@ -194,5 +194,5 @@ function onTabClose(event) {
};
getPBOwnerWindow.define(Tab, function(tab) {
return getContentWindowForTab(tabNS(tab).tab);
return getTabContentWindow(tabNS(tab).tab);
});

Просмотреть файл

@ -16,9 +16,10 @@ const { Ci } = require('chrome');
const { defer } = require("../lang/functional");
const { windows, isBrowser } = require('../window/utils');
const { isPrivateBrowsingSupported } = require('../self');
const { isGlobalPBSupported } = require('../private-browsing/utils');
// Bug 834961: ignore private windows when they are not supported
function getWindows() windows(null, { includePrivate: isPrivateBrowsingSupported });
function getWindows() windows(null, { includePrivate: isPrivateBrowsingSupported || isGlobalPBSupported });
function activateTab(tab, window) {
let gBrowser = getTabBrowserForTab(tab);
@ -166,12 +167,6 @@ 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

Просмотреть файл

@ -51,7 +51,11 @@ exports.LoaderWithHookedConsole = function (module, callback) {
warn: hook.bind("warn"),
error: hook.bind("error"),
debug: hook.bind("debug"),
exception: hook.bind("exception")
exception: hook.bind("exception"),
__exposedProps__: {
log: "rw", info: "rw", warn: "rw", error: "rw", debug: "rw",
exception: "rw"
}
}
}),
messages: messages

Просмотреть файл

@ -90,8 +90,14 @@ exports.flatten = function flatten(array){
function fromIterator(iterator) {
let array = [];
for each (let item in iterator)
array.push(item);
if (iterator.__iterator__) {
for each (let item in iterator)
array.push(item);
}
else {
for (let item of iterator)
array.push(item);
}
return array;
}
exports.fromIterator = fromIterator;

Просмотреть файл

@ -0,0 +1,46 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
module.metadata = {
"stability": "unstable"
};
var { Ci } = require("chrome");
/**
Temporarily emulate method so we don't have to uplift whole method
implementation.
var method = require("method/core");
// Returns DOM node associated with a view for
// the given `value`. If `value` has no view associated
// it returns `null`. You can implement this method for
// this type to define what the result should be for it.
let getNodeView = method("getNodeView");
getNodeView.define(function(value) {
if (value instanceof Ci.nsIDOMNode)
return value;
return null;
});
**/
let implementations = new WeakMap();
function getNodeView(value) {
if (value instanceof Ci.nsIDOMNode)
return value;
if (implementations.has(value))
return implementations.get(value)(value);
return null;
}
getNodeView.implement = function(value, implementation) {
implementations.set(value, implementation)
}
exports.getNodeView = getNodeView;

Просмотреть файл

@ -48,6 +48,7 @@ const { isBrowser } = require("./window/utils");
const { setTimeout } = require("./timers");
const unload = require("./system/unload");
const { uuid } = require("./util/uuid");
const { getNodeView } = require("./view/core");
// Data types definition
const valid = {
@ -362,7 +363,6 @@ const Widget = function Widget(options) {
exports.Widget = Widget;
/**
* WidgetView is an instance of a widget for a specific window.
*
@ -432,7 +432,10 @@ const WidgetViewTrait = LightTrait.compose(EventEmitterTrait, LightTrait({
// Special case for click events: if the widget doesn't have a click
// handler, but it does have a panel, display the panel.
if ("click" == type && !this._listeners("click").length && this.panel)
this.panel.show(domNode);
// This kind of ugly workaround, instead we should implement
// `getNodeView` for the `Widget` class itself, but that's kind of
// hard without cleaning things up.
this.panel.show(getNodeView.implement({}, function() domNode));
},
_isInWindow: function WidgetView__isInWindow(window) {
@ -472,6 +475,7 @@ const WidgetViewTrait = LightTrait.compose(EventEmitterTrait, LightTrait({
}));
const WidgetView = function WidgetView(baseWidget) {
let w = WidgetViewTrait.create(WidgetView.prototype);
w._initWidgetView(baseWidget);
@ -479,7 +483,6 @@ const WidgetView = function WidgetView(baseWidget) {
}
/**
* Keeps track of all browser windows.
* Exposes methods for adding/removing widgets

Просмотреть файл

@ -9,7 +9,7 @@ const { on, off, once } = require('../event/core');
const { method } = require('../lang/functional');
const { getWindowTitle } = require('./utils');
const unload = require('../system/unload');
const { isWindowPrivate } = require('../private-browsing/utils');
const { isWindowPrivate } = require('../window/utils');
const { EventTarget } = require('../event/target');
const { getOwnerWindow: getPBOwnerWindow } = require('../private-browsing/window/utils');
const { deprecateUsage } = require('../util/deprecate');

Просмотреть файл

@ -5,9 +5,8 @@
const { Class } = require('../core/heritage');
const { BrowserWindow } = require('../window/browser');
const windowUtils = require('../deprecated/window-utils');
const { WindowTracker } = windowUtils;
const { isBrowser } = require('../window/utils');
const { WindowTracker } = require('../deprecated/window-utils');
const { isBrowser, getMostRecentBrowserWindow } = require('../window/utils');
const { windowNS } = require('../window/namespace');
const { on, off, once, emit } = require('../event/core');
const { method } = require('../lang/functional');
@ -25,7 +24,7 @@ let BrowserWindows = Class({
List.prototype.initialize.apply(this);
},
get activeWindow() {
let window = windowUtils.activeBrowserWindow;
let window = getMostRecentBrowserWindow();
return window ? getBrowserWindow({window: window}) : null;
},
open: function open(options) {

Просмотреть файл

@ -8,7 +8,8 @@ const { Tab } = require('../tabs/tab');
const { browserWindows } = require('./fennec');
const { windowNS } = require('../window/namespace');
const { tabsNS, tabNS } = require('../tabs/namespace');
const { openTab, getTabs, getSelectedTab, getTabForBrowser: getRawTabForBrowser } = require('../tabs/utils');
const { openTab, getTabs, getSelectedTab, getTabForBrowser: getRawTabForBrowser,
getTabContentWindow } = require('../tabs/utils');
const { Options } = require('../tabs/common');
const { getTabForBrowser, getTabForRawTab } = require('../tabs/helpers');
const { on, once, off, emit } = require('../event/core');
@ -18,8 +19,8 @@ const { EventTarget } = require('../event/target');
const { when: unload } = require('../system/unload');
const { windowIterator } = require('../deprecated/window-utils');
const { List, addListItem, removeListItem } = require('../util/list');
const { isPrivateBrowsingSupported } = require('sdk/self');
const { isTabPBSupported } = require('sdk/private-browsing/utils');
const { isPrivateBrowsingSupported } = require('../self');
const { isTabPBSupported, ignoreWindow } = require('../private-browsing/utils');
const mainWindow = windowNS(browserWindows.activeWindow).window;
@ -113,6 +114,10 @@ function removeTab(tab) {
function onTabOpen(event) {
let browser = event.target;
// Eventually ignore private tabs
if (ignoreWindow(browser.contentWindow))
return;
let tab = getTabForBrowser(browser);
if (tab === null) {
let rawTab = getRawTabForBrowser(browser);
@ -132,8 +137,14 @@ function onTabOpen(event) {
// TabSelect
function onTabSelect(event) {
let browser = event.target;
// Eventually ignore private tabs
if (ignoreWindow(browser.contentWindow))
return;
// Set value whenever new tab becomes active.
let tab = getTabForBrowser(event.target);
let tab = getTabForBrowser(browser);
emit(tab, 'activate', tab);
emit(gTabs, 'activate', tab);

Просмотреть файл

@ -16,7 +16,7 @@ const { getOwnerWindow, getActiveTab, getTabs,
openTab } = require("../tabs/utils");
const { Options } = require("../tabs/common");
const { observer: tabsObserver } = require("../tabs/observer");
const { ignoreWindow, isWindowPrivate } = require("../private-browsing/utils");
const { ignoreWindow } = require("../private-browsing/utils");
const TAB_BROWSER = "tabbrowser";

Просмотреть файл

@ -11,6 +11,9 @@ DEFAULT_COMMON_PREFS = {
'javascript.options.strict': True,
'javascript.options.showInConsole': True,
# Allow remote connections to the debugger
'devtools.debugger.remote-enabled' : True,
'extensions.sdk.console.logLevel': 'info',
'extensions.checkCompatibility.nightly' : False,
@ -51,6 +54,7 @@ DEFAULT_FIREFOX_PREFS = {
'browser.startup.homepage' : 'about:blank',
'startup.homepage_welcome_url' : 'about:blank',
'devtools.errorconsole.enabled' : True,
'devtools.chrome.enabled' : True,
# Disable the feedback extension
'extensions.testpilot.runStudies' : False,

Просмотреть файл

@ -165,11 +165,11 @@ def gen_manifest(template_root_dir, target_cfg, jid,
ta_desc.appendChild(elem)
elem = dom.createElement("em:minVersion")
elem.appendChild(dom.createTextNode("18.0"))
elem.appendChild(dom.createTextNode("19.0"))
ta_desc.appendChild(elem)
elem = dom.createElement("em:maxVersion")
elem.appendChild(dom.createTextNode("21.0a1"))
elem.appendChild(dom.createTextNode("22.0a1"))
ta_desc.appendChild(elem)
if target_cfg.get("homepage"):

Просмотреть файл

@ -141,16 +141,15 @@ class TestInit(unittest.TestCase):
class TestCfxQuits(unittest.TestCase):
def run_cfx(self, addon_name, command):
def run_cfx(self, addon_path, command):
old_cwd = os.getcwd()
addon_path = os.path.join(tests_path,
"addons", addon_name)
os.chdir(addon_path)
import sys
old_stdout = sys.stdout
old_stderr = sys.stderr
sys.stdout = out = StringIO()
sys.stderr = err = StringIO()
rc = 0
try:
import cuddlefish
args = list(command)
@ -182,17 +181,45 @@ class TestCfxQuits(unittest.TestCase):
container)
self.fail(standardMsg)
def test_run(self):
rc, out, err = self.run_cfx("simplest-test", ["run"])
def test_cfx_run(self):
addon_path = os.path.join(tests_path,
"addons", "simplest-test")
rc, out, err = self.run_cfx(addon_path, ["run"])
self.assertEqual(rc, 0)
self.assertIn("Program terminated successfully.", err)
def test_test(self):
rc, out, err = self.run_cfx("simplest-test", ["test"])
def test_cfx_test(self):
addon_path = os.path.join(tests_path,
"addons", "simplest-test")
rc, out, err = self.run_cfx(addon_path, ["test"])
self.assertEqual(rc, 0)
self.assertIn("1 of 1 tests passed.", err)
self.assertIn("Program terminated successfully.", err)
def test_cfx_init(self):
# Create an empty test directory
addon_path = os.path.abspath(os.path.join(".test_tmp", "test-cfx-init"))
if os.path.isdir(addon_path):
shutil.rmtree(addon_path)
os.makedirs(addon_path)
# Fake a call to cfx init
old_cwd = os.getcwd()
os.chdir(addon_path)
out, err = StringIO(), StringIO()
rc = initializer(None, ["init"], out, err)
os.chdir(old_cwd)
out, err = out.getvalue(), err.getvalue()
self.assertEqual(rc["result"], 0)
self.assertTrue("Have fun!" in out)
self.assertEqual(err,"")
# run cfx test
rc, out, err = self.run_cfx(addon_path, ["test"])
self.assertEqual(rc, 0)
self.assertIn("2 of 2 tests passed.", err)
self.assertIn("Program terminated successfully.", err)
if __name__ == "__main__":
unittest.main()

Просмотреть файл

@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const app = require("sdk/system/xul-app");
// This test makes sure that require statements used by all AMO hosted
// add-ons will be able to use old require statements.
@ -16,14 +17,18 @@ exports["test compatibility"] = function(assert) {
assert.equal(require("tabs"),
require("sdk/tabs"), "sdk/tabs -> tabs");
assert.equal(require("widget"),
require("sdk/widget"), "sdk/widget -> widget");
if (app.is("Firefox")) {
assert.equal(require("widget"),
require("sdk/widget"), "sdk/widget -> widget");
}
assert.equal(require("page-mod"),
require("sdk/page-mod"), "sdk/page-mod -> page-mod");
assert.equal(require("panel"),
require("sdk/panel"), "sdk/panel -> panel");
if (app.is("Firefox")) {
assert.equal(require("panel"),
require("sdk/panel"), "sdk/panel -> panel");
}
assert.equal(require("request"),
require("sdk/request"), "sdk/request -> request");
@ -34,8 +39,10 @@ exports["test compatibility"] = function(assert) {
assert.equal(require("simple-storage"),
require("sdk/simple-storage"), "sdk/simple-storage -> simple-storage");
assert.equal(require("context-menu"),
require("sdk/context-menu"), "sdk/context-menu -> context-menu");
if (app.is("Firefox")) {
assert.equal(require("context-menu"),
require("sdk/context-menu"), "sdk/context-menu -> context-menu");
}
assert.equal(require("notifications"),
require("sdk/notifications"), "sdk/notifications -> notifications");
@ -49,8 +56,10 @@ exports["test compatibility"] = function(assert) {
assert.equal(require("url"),
require("sdk/url"), "sdk/url -> url");
assert.equal(require("selection"),
require("sdk/selection"), "sdk/selection -> selection");
if (app.is("Firefox")) {
assert.equal(require("selection"),
require("sdk/selection"), "sdk/selection -> selection");
}
assert.equal(require("timers"),
require("sdk/timers"), "sdk/timers -> timers");
@ -97,8 +106,10 @@ exports["test compatibility"] = function(assert) {
assert.equal(require("match-pattern"),
require("sdk/page-mod/match-pattern"), "sdk/page-mod/match-pattern -> match-pattern");
assert.equal(require("tab-browser"),
require("sdk/deprecated/tab-browser"), "sdk/deprecated/tab-browser -> tab-browser");
if (app.is("Firefox")) {
assert.equal(require("tab-browser"),
require("sdk/deprecated/tab-browser"), "sdk/deprecated/tab-browser -> tab-browser");
}
assert.equal(require("file"),
require("sdk/io/file"), "sdk/io/file -> file");
@ -154,8 +165,12 @@ exports["test compatibility"] = function(assert) {
assert.equal(require("environment"),
require("sdk/system/environment"), "sdk/system/environment -> environment");
assert.equal(require("utils/data"),
require("sdk/io/data"), "sdk/io/data -> utils/data");
if (app.is("Firefox")) {
// This module fails on fennec because of favicon xpcom component
// being not implemented on it.
assert.equal(require("utils/data"),
require("sdk/io/data"), "sdk/io/data -> utils/data");
}
assert.equal(require("test/assert"),
require("sdk/test/assert"), "sdk/test/assert -> test/assert");
@ -174,14 +189,4 @@ exports["test compatibility"] = function(assert) {
"api-utils/cortex -> sdk/deprecated/cortex");
};
if (require("sdk/system/xul-app").is("Fennec")) {
module.exports = {
"test Unsupported Test": function UnsupportedTest (assert) {
assert.pass(
"Skipping this test until Fennec support is implemented." +
"See bug 809352");
}
}
}
require("sdk/test/runner").runTestsFromModule(module);

Просмотреть файл

@ -3,173 +3,22 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
const { Ci } = require('chrome');
const { isPrivateBrowsingSupported } = require('sdk/self');
const tabs = require('sdk/tabs');
const { browserWindows: windows } = require('sdk/windows');
const { isPrivate } = require('sdk/private-browsing');
const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
const { is } = require('sdk/system/xul-app');
const { isWindowPBSupported, isTabPBSupported } = require('sdk/private-browsing/utils');
const { merge } = require('sdk/util/object');
const TAB_URL = 'data:text/html;charset=utf-8,TEST-TAB';
exports.testIsPrivateBrowsingTrue = function(assert) {
assert.ok(isPrivateBrowsingSupported,
'isPrivateBrowsingSupported property is true');
};
// test tab.open with isPrivate: true
// test isPrivate on a tab
// test getOwnerWindow on windows and tabs
exports.testGetOwnerWindow = function(assert, done) {
let window = windows.activeWindow;
let chromeWindow = getOwnerWindow(window);
assert.ok(chromeWindow instanceof Ci.nsIDOMWindow, 'associated window is found');
tabs.open({
url: 'about:blank',
isPrivate: true,
onOpen: function(tab) {
// test that getOwnerWindow works as expected
if (is('Fennec')) {
assert.notStrictEqual(chromeWindow, getOwnerWindow(tab));
assert.ok(getOwnerWindow(tab) instanceof Ci.nsIDOMWindow);
}
else {
if (isWindowPBSupported) {
assert.notStrictEqual(chromeWindow,
getOwnerWindow(tab),
'associated window is not the same for window and window\'s tab');
}
else {
assert.strictEqual(chromeWindow,
getOwnerWindow(tab),
'associated window is the same for window and window\'s tab');
}
}
let pbSupported = isTabPBSupported || isWindowPBSupported;
// test that the tab is private if it should be
assert.equal(isPrivate(tab), pbSupported);
assert.equal(isPrivate(getOwnerWindow(tab)), pbSupported);
tab.close(function() done());
}
});
};
// test that it is possible to open a private tab
exports.testTabOpenPrivate = function(assert, done) {
tabs.open({
url: TAB_URL,
isPrivate: true,
onReady: function(tab) {
assert.equal(tab.url, TAB_URL, 'opened correct tab');
assert.equal(isPrivate(tab), (isWindowPBSupported || isTabPBSupported));
tab.close(function() {
done();
});
}
});
}
// test that it is possible to open a non private tab
exports.testTabOpenPrivateDefault = function(assert, done) {
tabs.open({
url: TAB_URL,
onReady: function(tab) {
assert.equal(tab.url, TAB_URL, 'opened correct tab');
assert.equal(isPrivate(tab), false);
tab.close(function() {
done();
});
}
});
}
// test that it is possible to open a non private tab in explicit case
exports.testTabOpenPrivateOffExplicit = function(assert, done) {
tabs.open({
url: TAB_URL,
isPrivate: false,
onReady: function(tab) {
assert.equal(tab.url, TAB_URL, 'opened correct tab');
assert.equal(isPrivate(tab), false);
tab.close(function() {
done();
});
}
});
}
// test windows.open with isPrivate: true
// test isPrivate on a window
if (!is('Fennec')) {
// test that it is possible to open a private window
exports.testWindowOpenPrivate = function(assert, done) {
windows.open({
url: TAB_URL,
isPrivate: true,
onOpen: function(window) {
let tab = window.tabs[0];
tab.once('ready', function() {
assert.equal(tab.url, TAB_URL, 'opened correct tab');
assert.equal(isPrivate(tab), isWindowPBSupported, 'tab is private');
window.close(function() {
done();
});
});
}
});
};
exports.testIsPrivateOnWindowOn = function(assert, done) {
windows.open({
isPrivate: true,
onOpen: function(window) {
assert.equal(isPrivate(window), isWindowPBSupported, 'isPrivate for a window is true when it should be');
assert.equal(isPrivate(window.tabs[0]), isWindowPBSupported, 'isPrivate for a tab is false when it should be');
window.close(done);
}
});
};
exports.testIsPrivateOnWindowOffImplicit = 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);
}
})
}
exports.testIsPrivateOnWindowOffExplicit = function(assert, done) {
windows.open({
isPrivate: false,
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);
}
})
}
}
const app = require("sdk/system/xul-app");
const { isGlobalPBSupported } = require('sdk/private-browsing/utils');
merge(module.exports,
require('./test-windows'),
require('./test-tabs'),
require('./test-page-mod'),
require('./test-selection'),
require('./test-panel')
require('./test-panel'),
require('./test-private-browsing'),
isGlobalPBSupported ? require('./test-global-private-browsing') : {}
);
// Doesn't make sense to test window-utils and windows on fennec,
// as there is only one window which is never private
if (!app.is("Fennec"))
merge(module.exports, require('./test-windows'));
require('sdk/test/runner').runTestsFromModule(module);

Просмотреть файл

@ -0,0 +1,150 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
const windowUtils = require('sdk/deprecated/window-utils');
const { isWindowPBSupported, isGlobalPBSupported } = require('sdk/private-browsing/utils');
const { getFrames, getWindowTitle, onFocus, isWindowPrivate, windows, isBrowser } = require('sdk/window/utils');
const { open, close, focus } = require('sdk/window/helpers');
const { isPrivate } = require('sdk/private-browsing');
const { Panel } = require('sdk/panel');
const { Widget } = require('sdk/widget');
const { fromIterator: toArray } = require('sdk/util/array');
let { Loader } = require('sdk/test/loader');
let loader = Loader(module, {
console: Object.create(console, {
error: {
value: function(e) !/DEPRECATED:/.test(e) ? console.error(e) : undefined
}
})
});
const pb = loader.require('sdk/private-browsing');
function makeEmptyBrowserWindow(options) {
options = options || {};
return open('chrome://browser/content/browser.xul', {
features: {
chrome: true,
private: !!options.private,
toolbar: true
}
});
}
exports.testShowPanelAndWidgetOnPrivateWindow = function(assert, done) {
var myPrivateWindow;
var finished = false;
var privateWindow;
var privateWindowClosed = false;
pb.once('start', function() {
assert.pass('private browsing mode started');
// make a new private window
makeEmptyBrowserWindow().then(function(window) {
myPrivateWindow = window;
let wt = windowUtils.WindowTracker({
onTrack: function(window) {
if (!isBrowser(window) || window !== myPrivateWindow) return;
assert.ok(isWindowPrivate(window), 'window is private onTrack!');
let panel = Panel({
onShow: function() {
assert.ok(this.isShowing, 'the panel is showing on the private window');
let count = 0;
let widget = Widget({
id: "testShowPanelAndWidgetOnPrivateWindow-id",
label: "My Hello Widget",
content: "Hello!",
onAttach: function(mod) {
count++;
if (count == 2) {
panel.destroy();
widget.destroy();
close(window);
}
}
});
}
}).show(window.gBrowser);
},
onUntrack: function(window) {
if (window === myPrivateWindow) {
wt.unload();
pb.once('stop', function() {
assert.pass('private browsing mode end');
done();
});
pb.deactivate();
}
}
});
assert.equal(isWindowPrivate(window), true, 'the opened window is private');
assert.equal(isPrivate(window), true, 'the opened window is private');
assert.ok(getFrames(window).length > 1, 'there are frames for private window');
assert.equal(getWindowTitle(window), window.document.title,
'getWindowTitle works');
});
});
pb.activate();
};
exports.testWindowTrackerDoesNotIgnorePrivateWindows = function(assert, done) {
var myPrivateWindow;
var count = 0;
let wt = windowUtils.WindowTracker({
onTrack: function(window) {
if (!isBrowser(window) || !isWindowPrivate(window)) return;
assert.ok(isWindowPrivate(window), 'window is private onTrack!');
if (++count == 1)
close(window);
},
onUntrack: function(window) {
if (count == 1 && isWindowPrivate(window)) {
wt.unload();
pb.once('stop', function() {
assert.pass('private browsing mode end');
done();
});
pb.deactivate();
}
}
});
pb.once('start', function() {
assert.pass('private browsing mode started');
makeEmptyBrowserWindow();
});
pb.activate();
}
exports.testWindowIteratorDoesNotIgnorePrivateWindows = function(assert, done) {
pb.once('start', function() {
// make a new private window
makeEmptyBrowserWindow().then(function(window) {
assert.ok(isWindowPrivate(window), "window is private");
assert.equal(isPrivate(window), true, 'the opened window is private');
assert.ok(toArray(windowUtils.windowIterator()).indexOf(window) > -1,
"window is in windowIterator()");
assert.ok(windows(null, { includePrivate: true }).indexOf(window) > -1,
"window is in windows()");
close(window).then(function() {
pb.once('stop', function() {
done();
});
pb.deactivate();
});
});
});
pb.activate();
};

Просмотреть файл

@ -0,0 +1,164 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
const { Ci } = require('chrome');
const { isPrivateBrowsingSupported } = require('sdk/self');
const tabs = require('sdk/tabs');
const { browserWindows: windows } = require('sdk/windows');
const { isPrivate } = require('sdk/private-browsing');
const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
const { is } = require('sdk/system/xul-app');
const { isWindowPBSupported, isTabPBSupported } = require('sdk/private-browsing/utils');
const TAB_URL = 'data:text/html;charset=utf-8,TEST-TAB';
exports.testIsPrivateBrowsingTrue = function(assert) {
assert.ok(isPrivateBrowsingSupported,
'isPrivateBrowsingSupported property is true');
};
// test tab.open with isPrivate: true
// test isPrivate on a tab
// test getOwnerWindow on windows and tabs
exports.testGetOwnerWindow = function(assert, done) {
let window = windows.activeWindow;
let chromeWindow = getOwnerWindow(window);
assert.ok(chromeWindow instanceof Ci.nsIDOMWindow, 'associated window is found');
tabs.open({
url: 'about:blank',
isPrivate: true,
onOpen: function(tab) {
// test that getOwnerWindow works as expected
if (is('Fennec')) {
assert.notStrictEqual(chromeWindow, getOwnerWindow(tab));
assert.ok(getOwnerWindow(tab) instanceof Ci.nsIDOMWindow);
}
else {
if (isWindowPBSupported) {
assert.notStrictEqual(chromeWindow,
getOwnerWindow(tab),
'associated window is not the same for window and window\'s tab');
}
else {
assert.strictEqual(chromeWindow,
getOwnerWindow(tab),
'associated window is the same for window and window\'s tab');
}
}
let pbSupported = isTabPBSupported || isWindowPBSupported;
// test that the tab is private if it should be
assert.equal(isPrivate(tab), pbSupported);
assert.equal(isPrivate(getOwnerWindow(tab)), pbSupported);
tab.close(function() done());
}
});
};
// test that it is possible to open a private tab
exports.testTabOpenPrivate = function(assert, done) {
tabs.open({
url: TAB_URL,
isPrivate: true,
onReady: function(tab) {
assert.equal(tab.url, TAB_URL, 'opened correct tab');
assert.equal(isPrivate(tab), (isWindowPBSupported || isTabPBSupported));
tab.close(function() {
done();
});
}
});
}
// test that it is possible to open a non private tab
exports.testTabOpenPrivateDefault = function(assert, done) {
tabs.open({
url: TAB_URL,
onReady: function(tab) {
assert.equal(tab.url, TAB_URL, 'opened correct tab');
assert.equal(isPrivate(tab), false);
tab.close(function() {
done();
});
}
});
}
// test that it is possible to open a non private tab in explicit case
exports.testTabOpenPrivateOffExplicit = function(assert, done) {
tabs.open({
url: TAB_URL,
isPrivate: false,
onReady: function(tab) {
assert.equal(tab.url, TAB_URL, 'opened correct tab');
assert.equal(isPrivate(tab), false);
tab.close(function() {
done();
});
}
});
}
// test windows.open with isPrivate: true
// test isPrivate on a window
if (!is('Fennec')) {
// test that it is possible to open a private window
exports.testWindowOpenPrivate = function(assert, done) {
windows.open({
url: TAB_URL,
isPrivate: true,
onOpen: function(window) {
let tab = window.tabs[0];
tab.once('ready', function() {
assert.equal(tab.url, TAB_URL, 'opened correct tab');
assert.equal(isPrivate(tab), isWindowPBSupported, 'tab is private');
window.close(function() {
done();
});
});
}
});
};
exports.testIsPrivateOnWindowOn = function(assert, done) {
windows.open({
isPrivate: true,
onOpen: function(window) {
assert.equal(isPrivate(window), isWindowPBSupported, 'isPrivate for a window is true when it should be');
assert.equal(isPrivate(window.tabs[0]), isWindowPBSupported, 'isPrivate for a tab is false when it should be');
window.close(done);
}
});
};
exports.testIsPrivateOnWindowOffImplicit = 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);
}
})
}
exports.testIsPrivateOnWindowOffExplicit = function(assert, done) {
windows.open({
isPrivate: false,
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);
}
})
}
}

Просмотреть файл

@ -12,13 +12,13 @@ exports["test local vs sdk module"] = function (assert) {
}
exports["test 3rd party vs sdk module"] = function (assert) {
// We are testing with a 3rd party package called `panel` with 3 modules
// We are testing with a 3rd party package called `tabs` with 3 modules
// main, page-mod and third-party
// the only way to require 3rd party package modules are to use absolute paths
// require("panel/main"), require("panel/page-mod"),
// require("panel/third-party") and also require("panel") which will refer
// to panel's main package module.
// require("tabs/main"), require("tabs/page-mod"),
// require("tabs/third-party") and also require("tabs") which will refer
// to tabs's main package module.
// So require(page-mod) shouldn't map the 3rd party
assert.equal(require("page-mod"),
@ -27,27 +27,27 @@ exports["test 3rd party vs sdk module"] = function (assert) {
assert.ok(require("page-mod").PageMod,
"page-mod module is really the sdk one");
assert.equal(require("panel/page-mod").id, "page-mod",
"panel/page-mod is the 3rd party");
assert.equal(require("tabs/page-mod").id, "page-mod",
"tabs/page-mod is the 3rd party");
// But require(panel) will map to 3rd party main module
// But require(tabs) will map to 3rd party main module
// *and* overload the sdk module
// and also any local module with the same name
assert.equal(require("panel").id, "panel-main",
assert.equal(require("tabs").id, "tabs-main",
"Third party main module overload sdk modules");
assert.equal(require("panel"),
require("panel/main"),
"require(panel) maps to require(panel/main)");
assert.equal(require("tabs"),
require("tabs/main"),
"require(tabs) maps to require(tabs/main)");
// So that you have to use relative path to ensure getting the local module
assert.equal(require("./panel").id,
"local-panel",
"require(./panel) maps to the local module");
assert.equal(require("./tabs").id,
"local-tabs",
"require(./tabs) maps to the local module");
// It should still be possible to require sdk module with absolute path
assert.ok(require("sdk/panel").Panel,
assert.ok(require("sdk/tabs").open,
"We can bypass this overloading with absolute path to sdk modules");
assert.equal(require("sdk/panel"),
require("addon-kit/panel"),
assert.equal(require("sdk/tabs"),
require("addon-kit/tabs"),
"Old and new layout both work");
}
@ -70,8 +70,8 @@ exports.testMultipleRequirePerLine = function (assert) {
}
exports.testSDKRequire = function (assert) {
assert.deepEqual(Object.keys(require('sdk/widget')), ['Widget']);
assert.equal(require('widget'), require('sdk/widget'));
assert.deepEqual(Object.keys(require('sdk/page-worker')), ['Page']);
assert.equal(require('page-worker'), require('sdk/page-worker'));
}
require("sdk/test/runner").runTestsFromModule(module);

Просмотреть файл

@ -0,0 +1,5 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
exports.id = "tabs-main";

Просмотреть файл

@ -0,0 +1,3 @@
{
"id": "test-panel"
}

Просмотреть файл

@ -0,0 +1,5 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
exports.id = "page-mod";

Просмотреть файл

@ -0,0 +1,5 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
exports.id = "local-tabs";

Просмотреть файл

@ -6,6 +6,7 @@
const timer = require("sdk/timers");
const { LoaderWithHookedConsole, deactivate, pb, pbUtils } = require("./helper");
const tabs = require("sdk/tabs");
const { getMostRecentBrowserWindow, isWindowPrivate } = require('sdk/window/utils');
exports["test activate private mode via handler"] = function(test) {
test.waitUntilDone();
@ -232,3 +233,18 @@ exports.testUnloadWhileActive = function(test) {
pb.activate();
};
exports.testIgnoreWindow = function(test) {
test.waitUntilDone();
let window = getMostRecentBrowserWindow();
pb.once('start', function() {
test.assert(isWindowPrivate(window), 'window is private');
test.assert(!pbUtils.ignoreWindow(window), 'window is not ignored');
pb.once('stop', function() {
test.done();
});
pb.deactivate();
});
pb.activate();
};

Просмотреть файл

@ -9,6 +9,11 @@ const { loader } = LoaderWithHookedConsole(module);
const pb = loader.require('sdk/private-browsing');
const pbUtils = loader.require('sdk/private-browsing/utils');
const xulApp = require("sdk/system/xul-app");
const { openDialog, getMostRecentBrowserWindow } = require('sdk/window/utils');
const { openTab, getTabContentWindow, getActiveTab, setTabURL, closeTab } = require('sdk/tabs/utils');
const promise = require("sdk/core/promise");
const windowHelpers = require('sdk/window/helpers');
function LoaderWithHookedConsole(module) {
let globals = {};
@ -45,3 +50,40 @@ exports.deactivate = deactivate;
exports.pb = pb;
exports.pbUtils = pbUtils;
exports.LoaderWithHookedConsole = LoaderWithHookedConsole;
exports.openWebpage = function openWebpage(url, enablePrivate) {
if (xulApp.is("Fennec")) {
let chromeWindow = getMostRecentBrowserWindow();
let rawTab = openTab(chromeWindow, url, {
isPrivate: enablePrivate
});
return {
ready: promise.resolve(getTabContentWindow(rawTab)),
close: function () {
closeTab(rawTab);
// Returns a resolved promise as there is no need to wait
return promise.resolve();
}
};
}
else {
let win = openDialog({
private: enablePrivate
});
let deferred = promise.defer();
win.addEventListener("load", function onLoad() {
win.removeEventListener("load", onLoad, false);
let rawTab = getActiveTab(win);
setTabURL(rawTab, url);
deferred.resolve(getTabContentWindow(rawTab));
});
return {
ready: deferred.promise,
close: function () {
return windowHelpers.close(win);
}
};
}
return null;
}

Просмотреть файл

@ -2,14 +2,17 @@
const { Ci } = require('chrome');
const { openTab, closeTab } = require('sdk/tabs/utils');
const browserWindows = require('sdk/windows');
const { browserWindows } = require('sdk/windows');
const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
const { isPrivate } = require('sdk/private-browsing');
exports.testIsPrivateOnTab = function(test) {
let window = browserWindows.activeWindow;
let chromeWindow = getOwnerWindow(window);
test.assert(chromeWindow instanceof Ci.nsIDOMWindow, 'associated window is found');
test.assert(!pb.isPrivate(chromeWindow), 'the top level window is not private');
test.assert(!isPrivate(chromeWindow), 'the top level window is not private');
let rawTab = openTab(chromeWindow, 'data:text/html,<h1>Hi!</h1>', {
isPrivate: true
@ -17,8 +20,8 @@ exports.testIsPrivateOnTab = function(test) {
// 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));
test.assert(isPrivate(rawTab.browser.contentWindow));
test.assert(isPrivate(rawTab.browser));
closeTab(rawTab);
}

Просмотреть файл

@ -175,6 +175,7 @@ exports["test:emit hack message"] = WorkerTest(
exports["test:n-arguments emit"] = WorkerTest(
DEFAULT_CONTENT_URL,
function(assert, browser, done) {
let repeat = 0;
let worker = Worker({
window: browser.contentWindow,
contentScript: "new " + function WorkerScope() {
@ -187,10 +188,14 @@ exports["test:n-arguments emit"] = WorkerTest(
// Validate worker.port
worker.port.on("content-to-addon", function (arg1, arg2, arg3) {
assert.equal(arg1, "first argument");
assert.equal(arg2, "second");
assert.equal(arg3, "third");
done();
if (!repeat++) {
this.emit("addon-to-content", "first argument", "second", "third");
} else {
assert.equal(arg1, "first argument");
assert.equal(arg2, "second");
assert.equal(arg3, "third");
done();
}
});
worker.port.emit("addon-to-content", "first argument", "second", "third");
}

Просмотреть файл

@ -157,10 +157,10 @@ exports['test error handling'] = function(assert) {
emit(target, 'message');
};
exports['test unhandled errors'] = function(assert) {
exports['test unhandled exceptions'] = function(assert) {
let exceptions = [];
let { loader, messages } = LoaderWithHookedConsole(module);
let { emit, on } = loader.require('sdk/event/core');
let target = {};
let boom = Error('Boom!');
@ -182,6 +182,22 @@ exports['test unhandled errors'] = function(assert) {
'error in error handler is logged');
};
exports['test unhandled errors'] = function(assert) {
let exceptions = [];
let { loader, messages } = LoaderWithHookedConsole(module);
let { emit, on } = loader.require('sdk/event/core');
let target = {};
let boom = Error('Boom!');
emit(target, 'error', boom);
assert.equal(messages.length, 1, 'Error was logged');
assert.equal(messages[0].type, 'exception', 'The console message is exception');
assert.ok(~String(messages[0].msg).indexOf('Boom!'),
'unhandled exception is logged');
};
exports['test count'] = function(assert) {
let target = {};

Просмотреть файл

@ -4,6 +4,7 @@
"use strict";
const { Loader } = require("sdk/test/loader");
const hiddenFrames = require("sdk/frame/hidden-frame");
const { HiddenFrame } = hiddenFrames;
@ -29,4 +30,46 @@ exports["test Frame"] = function(assert, done) {
}));
};
exports["test frame removed properly"] = function(assert, done) {
let url = "data:text/html;charset=utf-8,<!DOCTYPE%20html>";
let hiddenFrame = hiddenFrames.add(HiddenFrame({
onReady: function () {
let frame = this.element;
assert.ok(frame.parentNode, "frame has a parent node");
hiddenFrames.remove(hiddenFrame);
assert.ok(!frame.parentNode, "frame no longer has a parent node");
done();
}
}));
};
exports["test unload detaches panels"] = function(assert, done) {
let loader = Loader(module);
let { add, remove, HiddenFrame } = loader.require("sdk/frame/hidden-frame");
let frames = []
function ready() {
frames.push(this.element);
if (frames.length === 2) complete();
}
add(HiddenFrame({ onReady: ready }));
add(HiddenFrame({ onReady: ready }));
function complete() {
frames.forEach(function(frame) {
assert.ok(frame.parentNode, "frame is in the document");
})
loader.unload();
frames.forEach(function(frame) {
assert.ok(!frame.parentNode, "frame isn't in the document'");
});
done();
}
};
require("test").run(exports);

Просмотреть файл

@ -9,15 +9,16 @@ const { Loader } = require('sdk/test/loader');
const tabs = require("sdk/tabs");
const timer = require("sdk/timers");
const { Cc, Ci } = require("chrome");
const { open, openDialog, getFrames, getMostRecentBrowserWindow } = require('sdk/window/utils');
const { open, getFrames, getMostRecentBrowserWindow } = require('sdk/window/utils');
const windowUtils = require('sdk/deprecated/window-utils');
const windowHelpers = require('sdk/window/helpers');
const { getTabContentWindow, getActiveTab, setTabURL, openTab, closeTab } = require('sdk/tabs/utils');
const xulApp = require("sdk/system/xul-app");
const { data } = require('sdk/self');
const { data, isPrivateBrowsingSupported } = require('sdk/self');
const { isPrivate } = require('sdk/private-browsing');
const { isTabPBSupported, isWindowPBSupported } = require('sdk/private-browsing/utils');
const { openWebpage } = require('./private-browsing/helper');
const { isTabPBSupported, isWindowPBSupported, isGlobalPBSupported } = require('sdk/private-browsing/utils');
const promise = require("sdk/core/promise");
const { pb } = require('./private-browsing/helper');
/* 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
@ -1055,36 +1056,6 @@ exports.testEvents = function(test) {
);
};
function openWebpage(url, enablePrivate) {
if (xulApp.is("Fennec")) {
let chromeWindow = getMostRecentBrowserWindow();
let rawTab = openTab(chromeWindow, url, {
isPrivate: enablePrivate
});
return {
close: function () {
closeTab(rawTab)
// Returns a resolved promise as there is no need to wait
return promise.resolve();
}
};
}
else {
let win = openDialog({
private: enablePrivate
});
win.addEventListener("load", function onLoad() {
win.removeEventListener("load", onLoad, false);
setTabURL(getActiveTab(win), url);
});
return {
close: function () {
return windowHelpers.close(win);
}
};
}
}
exports["test page-mod on private tab"] = function (test) {
test.waitUntilDone();
let privateUri = "data:text/html;charset=utf-8," +
@ -1113,3 +1084,46 @@ exports["test page-mod on private tab"] = function (test) {
let page1 = openWebpage(privateUri, true);
let page2 = openWebpage(nonPrivateUri, false);
}
exports["test page-mod on private tab in global pb"] = function (test) {
test.waitUntilDone();
if (!isGlobalPBSupported) {
test.pass();
return test.done();
}
let privateUri = "data:text/html;charset=utf-8," +
"<iframe%20src=\"data:text/html;charset=utf-8,frame\"/>";
let pageMod = new PageMod({
include: privateUri,
onAttach: function(worker) {
test.assertEqual(worker.tab.url,
privateUri,
"page-mod should attach");
test.assertEqual(isPrivateBrowsingSupported,
false,
"private browsing is not supported");
test.assert(isPrivate(worker),
"The worker is really non-private");
test.assert(isPrivate(worker.tab),
"The document is really non-private");
pageMod.destroy();
worker.tab.close(function() {
pb.once('stop', function() {
test.pass('global pb stop');
test.done();
});
pb.deactivate();
});
}
});
let page1;
pb.once('start', function() {
test.pass('global pb start');
tabs.open({ url: privateUri });
});
pb.activate();
}

Просмотреть файл

@ -325,6 +325,20 @@ exports.testContentScriptOptionsOption = function(assert, done) {
});
};
exports.testMessageQueue = function (assert, done) {
let page = new Page({
contentScript: 'self.on("message", function (m) {' +
'self.postMessage(m);' +
'});',
contentURL: 'data:text/html;charset=utf-8,',
});
page.postMessage('ping');
page.on('message', function (m) {
assert.equal(m, 'ping', 'postMessage should queue messages');
done();
});
};
function isDestroyed(page) {
try {
page.postMessage("foo");

Просмотреть файл

@ -10,9 +10,11 @@ const timer = require("sdk/timers");
const self = require('sdk/self');
const { open, close, focus } = require('sdk/window/helpers');
const { isPrivate } = require('sdk/private-browsing');
const { isWindowPBSupported } = require('sdk/private-browsing/utils');
const { isWindowPBSupported, isGlobalPBSupported } = require('sdk/private-browsing/utils');
const { defer } = require('sdk/core/promise');
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
const { getWindow } = require('sdk/panel/window');
const { pb } = require('./private-browsing/helper');
const SVG_URL = self.data.url('mofo_logo.SVG');
@ -287,7 +289,10 @@ exports["test Anchor And Arrow"] = function(assert, done) {
const { Panel } = require('sdk/panel');
let count = 0;
function newPanel(tab, anchor) {
let queue = [];
let tab;
function newPanel(anchor) {
let panel = Panel({
contentURL: "data:text/html;charset=utf-8,<html><body style='padding: 0; margin: 0; " +
"background: gray; text-align: center;'>Anchor: " +
@ -295,16 +300,22 @@ exports["test Anchor And Arrow"] = function(assert, done) {
width: 200,
height: 100,
onShow: function () {
count++;
panel.destroy();
if (count==5) {
assert.pass("All anchored panel test displayed");
tab.close(function () {
done();
});
}
next();
}
});
queue.push({ panel: panel, anchor: anchor });
}
function next () {
if (!queue.length) {
assert.pass("All anchored panel test displayed");
tab.close(function () {
done();
});
return;
}
let { panel, anchor } = queue.shift();
panel.show(anchor);
}
@ -321,22 +332,105 @@ exports["test Anchor And Arrow"] = function(assert, done) {
tabs.open({
url: url,
onReady: function(tab) {
onReady: function(_tab) {
tab = _tab;
let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"].
getService(Ci.nsIWindowMediator).
getMostRecentWindow("navigator:browser");
let window = browserWindow.content;
newPanel(tab, window.document.getElementById('tl'));
newPanel(tab, window.document.getElementById('tr'));
newPanel(tab, window.document.getElementById('bl'));
newPanel(tab, window.document.getElementById('br'));
newPanel(window.document.getElementById('tl'));
newPanel(window.document.getElementById('tr'));
newPanel(window.document.getElementById('bl'));
newPanel(window.document.getElementById('br'));
let anchor = browserWindow.document.getElementById("identity-box");
newPanel(tab, anchor);
newPanel(anchor);
next();
}
});
};
exports["test Panel Focus True"] = function(assert, done) {
const { Panel } = require('sdk/panel');
const FM = Cc["@mozilla.org/focus-manager;1"].
getService(Ci.nsIFocusManager);
let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"].
getService(Ci.nsIWindowMediator).
getMostRecentWindow("navigator:browser");
// Make sure there is a focused element
browserWindow.document.documentElement.focus();
// Get the current focused element
let focusedElement = FM.focusedElement;
let panel = Panel({
contentURL: "about:buildconfig",
focus: true,
onShow: function () {
assert.ok(focusedElement !== FM.focusedElement,
"The panel takes the focus away.");
done();
}
});
panel.show();
};
exports["test Panel Focus False"] = function(assert, done) {
const { Panel } = require('sdk/panel');
const FM = Cc["@mozilla.org/focus-manager;1"].
getService(Ci.nsIFocusManager);
let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"].
getService(Ci.nsIWindowMediator).
getMostRecentWindow("navigator:browser");
// Make sure there is a focused element
browserWindow.document.documentElement.focus();
// Get the current focused element
let focusedElement = FM.focusedElement;
let panel = Panel({
contentURL: "about:buildconfig",
focus: false,
onShow: function () {
assert.ok(focusedElement === FM.focusedElement,
"The panel does not take the focus away.");
done();
}
});
panel.show();
};
exports["test Panel Focus Not Set"] = function(assert, done) {
const { Panel } = require('sdk/panel');
const FM = Cc["@mozilla.org/focus-manager;1"].
getService(Ci.nsIFocusManager);
let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"].
getService(Ci.nsIWindowMediator).
getMostRecentWindow("navigator:browser");
// Make sure there is a focused element
browserWindow.document.documentElement.focus();
// Get the current focused element
let focusedElement = FM.focusedElement;
let panel = Panel({
contentURL: "about:buildconfig",
onShow: function () {
assert.ok(focusedElement !== FM.focusedElement,
"The panel takes the focus away.");
done();
}
});
panel.show();
};
exports["test Panel Text Color"] = function(assert, done) {
@ -518,7 +612,7 @@ exports["test console.log in Panel"] = function(assert, done) {
});
panel.show();
function onMessage(type, message) {
assert.equal(type, 'log', 'console.log() works');
assert.equal(message, text, 'console.log() works');
@ -658,6 +752,106 @@ function testShowPanel(assert, panel) {
return promise;
}
exports['test Style Applied Only Once'] = function (assert, done) {
let loader = Loader(module);
let panel = loader.require("sdk/panel").Panel({
contentURL: "data:text/html;charset=utf-8,",
contentScript:
'self.port.on("check",function() { self.port.emit("count", document.getElementsByTagName("style").length); });' +
'self.port.on("ping", function (count) { self.port.emit("pong", count); });'
});
panel.port.on('count', function (styleCount) {
assert.equal(styleCount, 1, 'should only have one style');
done();
});
panel.port.on('pong', function (counter) {
panel[--counter % 2 ? 'hide' : 'show']();
panel.port.emit(!counter ? 'check' : 'ping', counter);
});
panel.on('show', init);
panel.show();
function init () {
panel.removeListener('show', init);
panel.port.emit('ping', 10);
}
};
exports['test Only One Panel Open Concurrently'] = function (assert, done) {
const loader = Loader(module);
const { Panel } = loader.require('sdk/panel')
let panelA = Panel({
contentURL: 'about:buildconfig'
});
let panelB = Panel({
contentURL: 'about:buildconfig',
onShow: function () {
// When loading two panels simulataneously, only the second
// should be shown, never showing the first
assert.equal(panelA.isShowing, false, 'First panel is hidden');
assert.equal(panelB.isShowing, true, 'Second panel is showing');
panelC.show();
}
});
let panelC = Panel({
contentURL: 'about:buildconfig',
onShow: function () {
assert.equal(panelA.isShowing, false, 'First panel is hidden');
assert.equal(panelB.isShowing, false, 'Second panel is hidden');
assert.equal(panelC.isShowing, true, 'Third panel is showing');
done();
}
});
panelA.show();
panelB.show();
};
if (isWindowPBSupported) {
exports.testGetWindow = function(assert, done) {
let activeWindow = getMostRecentBrowserWindow();
open(null, { features: {
toolbar: true,
chrome: true,
private: true
} }).then(function(window) {
assert.ok(isPrivate(window), 'window is private');
assert.equal(getWindow(window.gBrowser), null, 'private window elements returns null');
assert.equal(getWindow(activeWindow.gBrowser), activeWindow, 'non-private window elements returns window');
close(window).then(done);
})
}
}
else if (isGlobalPBSupported) {
exports.testGetWindow = function(assert, done) {
let activeWindow = getMostRecentBrowserWindow();
assert.equal(getWindow(activeWindow.gBrowser), activeWindow, 'non-private window elements returns window');
pb.once('start', function() {
assert.ok(isPrivate(activeWindow), 'window is private');
assert.equal(getWindow(activeWindow.gBrowser), activeWindow, 'private window elements returns window');
open(null, { features: {
toolbar: true,
chrome: true
} }).then(function(window) {
assert.ok(isPrivate(window), 'window is private');
assert.equal(getWindow(window.gBrowser), window, 'private window elements returns window');
assert.equal(getWindow(activeWindow.gBrowser), activeWindow, 'active window elements returns window');
pb.once('stop', done);
pb.deactivate();
})
});
pb.activate();
}
}
try {
require("sdk/panel");
}

Просмотреть файл

@ -0,0 +1,70 @@
'use strict';
const { getTabs } = require('sdk/tabs/utils');
const { isGlobalPBSupported, isWindowPBSupported, isTabPBSupported } = require('sdk/private-browsing/utils');
const { browserWindows } = require('sdk/windows');
const tabs = require('sdk/tabs');
const { pb } = require('./private-browsing/helper');
const { isPrivate } = require('sdk/private-browsing');
const { openTab } = require('sdk/tabs/utils');
const { open, close } = require('sdk/window/helpers');
const { windows } = require('sdk/window/utils');
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
const { fromIterator } = require('sdk/util/array');
if (isGlobalPBSupported) {
exports.testGetTabs = function(assert, done) {
pb.once('start', function() {
tabs.open({
url: 'about:blank',
inNewWindow: true,
onOpen: function(tab) {
assert.equal(getTabs().length, 2, 'there are two tabs');
assert.equal(browserWindows.length, 2, 'there are two windows');
pb.once('stop', function() {
done();
});
pb.deactivate();
}
});
});
pb.activate();
};
}
else if (isWindowPBSupported) {
exports.testGetTabs = function(assert, done) {
open(null, {
features: {
private: true,
toolbar: true,
chrome: true
}
}).then(function(window) {
assert.ok(isPrivate(window), 'new tab is private');
assert.equal(getTabs().length, 1, 'there is one tab found');
assert.equal(browserWindows.length, 1, 'there is one window found');
fromIterator(browserWindows).forEach(function(window) {
assert.ok(!isPrivate(window), 'all found windows are not private');
});
assert.equal(windows(null, {includePrivate: true}).length, 2, 'there are really two windows');
close(window).then(done);
});
};
}
else if (isTabPBSupported) {
exports.testGetTabs = function(assert, done) {
tabs.once('open', function(tab) {
assert.ok(isPrivate(tab), 'new tab is private');
assert.equal(getTabs().length, 2, 'there are two tabs found');
assert.equal(browserWindows.length, 1, 'there is one window');
tab.close(function() {
done();
});
});
openTab(getMostRecentBrowserWindow(), 'about:blank', {
isPrivate: true
});
};
}
require('test').run(exports);

Просмотреть файл

@ -8,9 +8,10 @@ const { browserWindows } = require('sdk/windows');
const tabs = require('sdk/tabs');
const { isPrivate } = require('sdk/private-browsing');
const { openDialog } = require('sdk/window/utils');
const pbUtils = require('sdk/private-browsing/utils');
const { isWindowPrivate } = require('sdk/window/utils');
const { setTimeout } = require('sdk/timers');
const { openWebpage } = require('./private-browsing/helper');
const { isTabPBSupported, isWindowPBSupported } = require('sdk/private-browsing/utils');
const URL = 'data:text/html;charset=utf-8,<html><head><title>#title#</title></head></html>';
@ -323,34 +324,27 @@ exports.testTabOpenPrivate = function(test) {
// We need permission flag in order to see private window's tabs
exports.testPrivateAreNotListed = function (test) {
test.waitUntilDone();
let originalTabCount = tabs.length;
let win = openDialog({
private: true
});
let page = openWebpage("about:blank", true);
if (!page) {
test.pass("Private browsing isn't supported in this release");
return;
}
win.addEventListener("load", function onload() {
win.removeEventListener("load", onload);
// PWPB case
if (pbUtils.isWindowPBSupported) {
test.assert(isWindowPrivate(win), "window is private");
test.waitUntilDone();
page.ready.then(function (win) {
if (isTabPBSupported || isWindowPBSupported) {
test.assert(isWindowPrivate(win), "the window is private");
test.assertEqual(tabs.length, originalTabCount,
'New private window\'s tab isn\'t visible in tabs list');
'but the tab is *not* visible in tabs list');
}
else {
// Global case, openDialog didn't opened a private window/tab
test.assert(!isWindowPrivate(win), "window is private");
test.assert(!isWindowPrivate(win), "the window isn't private");
test.assertEqual(tabs.length, originalTabCount + 1,
'New non-private window\'s tab is visible in tabs list');
'so that the tab is visible is tabs list');
}
win.addEventListener("unload", function onunload() {
win.removeEventListener('unload', onunload);
test.done();
});
win.close();
page.close().then(test.done.bind(test));
});
}

Просмотреть файл

@ -0,0 +1,152 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.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 windowUtils = require('sdk/deprecated/window-utils');
const { isWindowPBSupported, isGlobalPBSupported } = require('sdk/private-browsing/utils');
const { getFrames, getWindowTitle, onFocus, isWindowPrivate, windows, isBrowser } = require('sdk/window/utils');
const { open, close, focus } = require('sdk/window/helpers');
const { isPrivate } = require('sdk/private-browsing');
const { pb } = require('./private-browsing/helper');
const { fromIterator: toArray } = require('sdk/util/array');
function makeEmptyBrowserWindow(options) {
options = options || {};
return open('chrome://browser/content/browser.xul', {
features: {
chrome: true,
private: !!options.private,
toolbar: true
}
});
}
exports.testShowPanelAndWidgetOnPrivateWindow = function(assert, done) {
var myPrivateWindow;
var finished = false;
var privateWindow;
var privateWindowClosed = false;
var { Panel } = require('sdk/panel');
var { Widget } = require('sdk/widget');
pb.once('start', function() {
assert.pass('private browsing mode started');
// make a new private window
makeEmptyBrowserWindow().then(function(window) {
myPrivateWindow = window;
let wt = windowUtils.WindowTracker({
onTrack: function(window) {
if (!isBrowser(window) || window !== myPrivateWindow) return;
assert.ok(isWindowPrivate(window), 'window is private onTrack!');
let panel = Panel({
onShow: function() {
assert.ok(this.isShowing, 'the panel is showing on the private window');
let count = 0;
let widget = Widget({
id: "testShowPanelAndWidgetOnPrivateWindow-id",
label: "My Hello Widget",
content: "Hello!",
onAttach: function(mod) {
count++;
if (count == 2) {
panel.destroy();
widget.destroy();
close(window);
}
}
});
}
}).show(window.gBrowser);
},
onUntrack: function(window) {
if (window === myPrivateWindow) {
wt.unload();
pb.once('stop', function() {
assert.pass('private browsing mode end');
done();
});
pb.deactivate();
}
}
});
assert.equal(isWindowPrivate(window), true, 'the opened window is private');
assert.equal(isPrivate(window), true, 'the opened window is private');
assert.ok(getFrames(window).length > 1, 'there are frames for private window');
assert.equal(getWindowTitle(window), window.document.title,
'getWindowTitle works');
});
});
pb.activate();
};
exports.testWindowTrackerDoesNotIgnorePrivateWindows = function(assert, done) {
var myPrivateWindow;
var count = 0;
let wt = windowUtils.WindowTracker({
onTrack: function(window) {
if (!isBrowser(window) || !isWindowPrivate(window)) return;
assert.ok(isWindowPrivate(window), 'window is private onTrack!');
if (++count == 1)
close(window);
},
onUntrack: function(window) {
if (count == 1 && isWindowPrivate(window)) {
wt.unload();
pb.once('stop', function() {
assert.pass('private browsing mode end');
done();
});
pb.deactivate();
}
}
});
pb.once('start', function() {
assert.pass('private browsing mode started');
makeEmptyBrowserWindow();
});
pb.activate();
}
exports.testWindowIteratorDoesNotIgnorePrivateWindows = function(assert, done) {
pb.once('start', function() {
// make a new private window
makeEmptyBrowserWindow().then(function(window) {
assert.ok(isWindowPrivate(window), "window is private");
assert.equal(isPrivate(window), true, 'the opened window is private');
assert.ok(toArray(windowUtils.windowIterator()).indexOf(window) > -1,
"window is in windowIterator()");
assert.ok(windows(null, { includePrivate: true }).indexOf(window) > -1,
"window is in windows()");
close(window).then(function() {
pb.once('stop', function() {
done();
});
pb.deactivate();
});
});
});
pb.activate();
};
if (!isGlobalPBSupported) {
module.exports = {
"test Unsupported Test": function UnsupportedTest (assert) {
assert.pass(
"Skipping global private browsing tests");
}
}
}
require("test").run(exports);

Просмотреть файл

@ -30,14 +30,10 @@ exports.testWindowTrackerIgnoresPrivateWindows = function(assert, done) {
let wt = windowUtils.WindowTracker({
onTrack: function(window) {
if (isWindowPrivate(window)) {
assert.fail('private window was tracked!');
}
assert.ok(!isWindowPrivate(window), 'private window was not tracked!');
},
onUntrack: function(window) {
if (isWindowPrivate(window)) {
assert.fail('private window was tracked!');
}
assert.ok(!isWindowPrivate(window), 'private window was not tracked!');
// PWPB case
if (window === myPrivateWindow && isWindowPBSupported) {
privateWindowClosed = true;

Просмотреть файл

@ -1,4 +1,6 @@
ManifestDestiny==0.5.6
mozprocess==0.9
mozprofile==0.6
mozrunner==5.15
mozdevice==0.21
mozcrash==0.5

Просмотреть файл

@ -2745,6 +2745,8 @@ let BrowserOnClick = {
let isMalware = /e=malwareBlocked/.test(aOwnerDoc.documentURI);
let bucketName = isMalware ? "WARNING_MALWARE_PAGE_":"WARNING_PHISHING_PAGE_";
let nsISecTel = Ci.nsISecurityUITelemetry;
let isIframe = (aOwnerDoc.defaultView.parent === aOwnerDoc.defaultView);
bucketName += isIframe ? "TOP_" : "FRAME_";
switch (elmId) {
case "getMeOutButton":

Просмотреть файл

@ -13,7 +13,9 @@ function test() {
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "https://example.com/browser/browser/base/content/test/moz.png"
};
let oldwidth = window.outerWidth; // we futz with this, so we restore it
let oldwidth = window.outerWidth; // we futz with these, so we restore them
let oldleft = window.screenX;
window.moveTo(0, window.screenY)
let postSubTest = function(cb) {
let chats = document.getElementById("pinnedchats");
ok(chats.children.length == 0, "no chatty children left behind");
@ -21,6 +23,7 @@ function test() {
};
runSocialTestWithProvider(manifest, function (finishcb) {
runSocialTests(tests, undefined, postSubTest, function() {
window.moveTo(oldleft, window.screenY)
window.resizeTo(oldwidth, window.outerHeight);
finishcb();
});
@ -336,21 +339,23 @@ var tests = {
ok(popupWidth, "have a popupwidth");
info("starting resize tests - each chat's width is " + chatWidth +
" and the popup width is " + popupWidth);
// Note that due to a difference between "device", "app" and "css" pixels
// we allow use 2 pixels as the minimum size difference.
resizeAndCheckWidths(first, second, third, [
[chatWidth-1, false, false, true, "to < 1 chat width - only last should be visible."],
[chatWidth+1, false, false, true, "one pixel more then one fully exposed (not counting popup) - still only 1."],
[chatWidth+popupWidth+1, false, false, true, "one pixel more than one fully exposed (including popup) - still only 1."],
[chatWidth*2-1, false, false, true, "second not showing by 1 pixel (not counting popup) - only 1 exposed."],
[chatWidth*2+popupWidth-1, false, false, true, "second not showing by 1 pixel (including popup) - only 1 exposed."],
[chatWidth*2+popupWidth+1, false, true, true, "big enough to fit 2 - nub remains visible as first is still hidden"],
[chatWidth*3+popupWidth-1, false, true, true, "one smaller than the size necessary to display all three - first still hidden"],
[chatWidth*3+popupWidth+1, true, true, true, "big enough to fit all - all exposed (which removes the nub)"],
[chatWidth*3, true, true, true, "now the nub is hidden we can resize back down to chatWidth*3 before overflow."],
[chatWidth*3-1, false, true, true, "one pixel less and the first is again collapsed (and the nub re-appears)"],
[chatWidth*2+popupWidth+1, false, true, true, "back down to just big enough to fit 2"],
[chatWidth*2+popupWidth-1, false, false, true, "back down to just not enough to fit 2"],
[chatWidth*3+popupWidth+1, true, true, true, "now a large jump to make all 3 visible (ie, affects 2)"],
[chatWidth*1.5, false, false, true, "and a large jump back down to 1 visible (ie, affects 2)"],
[chatWidth-2, 1, "to < 1 chat width - only last should be visible."],
[chatWidth+2, 1, "2 pixels more then one fully exposed (not counting popup) - still only 1."],
[chatWidth+popupWidth+2, 1, "2 pixels more than one fully exposed (including popup) - still only 1."],
[chatWidth*2-2, 1, "second not showing by 2 pixels (not counting popup) - only 1 exposed."],
[chatWidth*2+popupWidth-2, 1, "second not showing by 2 pixelx (including popup) - only 1 exposed."],
[chatWidth*2+popupWidth+2, 2, "big enough to fit 2 - nub remains visible as first is still hidden"],
[chatWidth*3+popupWidth-2, 2, "one smaller than the size necessary to display all three - first still hidden"],
[chatWidth*3+popupWidth+2, 3, "big enough to fit all - all exposed (which removes the nub)"],
[chatWidth*3+2, 3, "now the nub is hidden we can resize back down to chatWidth*3 before overflow."],
[chatWidth*3-2, 2, "2 pixels less and the first is again collapsed (and the nub re-appears)"],
[chatWidth*2+popupWidth+2, 2, "back down to just big enough to fit 2"],
[chatWidth*2+popupWidth-2, 1, "back down to just not enough to fit 2"],
[chatWidth*3+popupWidth+2, 3, "now a large jump to make all 3 visible (ie, affects 2)"],
[chatWidth*1.5, 1, "and a large jump back down to 1 visible (ie, affects 2)"],
], function() {
closeAllChats();
port.close();
@ -636,10 +641,10 @@ function resizeWindowToChatAreaWidth(desired, cb) {
+ current + ". Screen avail is " + window.screen.availWidth
+ ", current outer width is " + window.outerWidth);
// WTF? Some test boxes will resize to fractional values - eg: we
// request 660px but actually get 659.5!?
// WTF? Sometimes we will get fractional values due to the - err - magic
// of DevPointsPerCSSPixel etc, so we allow a couple of pixels difference.
let widthDeltaCloseEnough = function(d) {
return Math.abs(d) <= 0.5;
return Math.abs(d) < 2;
}
// attempting to resize by (0,0), unsurprisingly, doesn't cause a resize
@ -676,15 +681,14 @@ function resizeAndCheckWidths(first, second, third, checks, cb) {
cb(); // nothing more to check!
return;
}
let [width, firstVisible, secondVisible, thirdVisible, why] = checks.shift();
let [width, numExpectedVisible, why] = checks.shift();
info("Check: " + why);
info("resizing window to " + width + ", expect visibility of " + firstVisible + "/" + secondVisible + "/" + thirdVisible);
info("resizing window to " + width + ", expect " + numExpectedVisible + " visible items");
resizeWindowToChatAreaWidth(width, function(sizedOk) {
checkPopup();
if (sizedOk) {
is(!first.collapsed, firstVisible, "first should be " + (firstVisible ? "visible" : "hidden"));
is(!second.collapsed, secondVisible, "second should be " + (secondVisible ? "visible" : "hidden"));
is(!third.collapsed, thirdVisible, "third should be " + (thirdVisible ? "visible" : "hidden"));
let numVisible = [first, second, third].filter(function(item) !item.collapsed).length;
is(numVisible, numExpectedVisible, "correct number of chats visible");
}
resizeAndCheckWidths(first, second, third, checks, cb);
});

Просмотреть файл

@ -12,6 +12,9 @@
const TEST_HTTPS_URI = "https://example.com/browser/browser/devtools/webconsole/test/test-bug-737873-mixedcontent.html";
var origBlockDisplay;
var origBlockActive;
function test() {
addTab("data:text/html;charset=utf8,Web Console mixed content test");
browser.addEventListener("load", onLoad, true);
@ -19,6 +22,10 @@ function test() {
function onLoad(aEvent) {
browser.removeEventListener("load", onLoad, true);
origBlockDisplay = Services.prefs.getBoolPref("security.mixed_content.block_display_content");
origBlockActive = Services.prefs.getBoolPref("security.mixed_content.block_active_content")
Services.prefs.setBoolPref("security.mixed_content.block_display_content", false);
Services.prefs.setBoolPref("security.mixed_content.block_active_content", false);
openConsole(null, testMixedContent);
}
@ -45,10 +52,10 @@ function testMixedContent(hud) {
is(warningNode.value, "[Mixed Content]", "Message text is accurate." );
testClickOpenNewTab(warningNode);
finishTest();
endTest();
},
failureFn: finishTest,
failureFn: endTest,
}
);
@ -81,3 +88,9 @@ function testClickOpenNewTab(warningNode) {
window.openUILinkIn = oldOpenUILinkIn;
}
function endTest() {
Services.prefs.setBoolPref("security.mixed_content.block_display_content", origBlockDisplay);
Services.prefs.setBoolPref("security.mixed_content.block_active_content", origBlockActive);
finishTest();
}

Просмотреть файл

@ -507,6 +507,8 @@
@BINPATH@/components/nsDOMIdentity.js
@BINPATH@/components/nsIDService.js
@BINPATH@/components/Identity.manifest
@BINPATH@/components/recording-cmdline.js
@BINPATH@/components/recording-cmdline.manifest
@BINPATH@/components/PermissionSettings.js
@BINPATH@/components/PermissionSettings.manifest
@ -563,6 +565,8 @@
#endif
@BINPATH@/chrome/toolkit@JAREXT@
@BINPATH@/chrome/toolkit.manifest
@BINPATH@/chrome/recording.manifest
@BINPATH@/chrome/recording/*
#ifdef MOZ_GTK2
@BINPATH@/browser/chrome/icons/default/default16.png
@BINPATH@/browser/chrome/icons/default/default32.png

Просмотреть файл

@ -343,6 +343,7 @@ MenuPopup.prototype = {
window.addEventListener("keypress", this, true);
window.addEventListener("mousedown", this, true);
Elements.stack.addEventListener("PopupChanged", this, false);
this._panel.hidden = false;
this._position(aPositionOptions || {});
@ -372,6 +373,7 @@ MenuPopup.prototype = {
window.removeEventListener("keypress", this, true);
window.removeEventListener("mousedown", this, true);
Elements.stack.removeEventListener("PopupChanged", this, false);
let self = this;
this._panel.addEventListener("transitionend", function () {
@ -494,6 +496,11 @@ MenuPopup.prototype = {
this.hide();
}
break;
case "PopupChanged":
if (aEvent.detail) {
this.hide();
}
break;
}
}
};

Просмотреть файл

@ -452,6 +452,10 @@ class Automation(object):
self.setupPermissionsDatabase(profileDir,
{'allowXULXBL':[(l.host, 'noxul' not in l.options) for l in locations]});
# NOTE: For refactoring purposes we are temporarily storing these prefs
# in two locations. If you update a pref below, please also update
# it in source/testing/profiles/prefs_general.js.
# See bug 830430 for more details.
part = """\
user_pref("browser.console.showInPanel", true);
user_pref("browser.dom.window.dump.enabled", true);

Просмотреть файл

@ -331,7 +331,7 @@ class B2GRemoteAutomation(Automation):
self.stdout_proc.run()
if hasattr(self.stdout_proc, 'processOutput'):
self.stdout_proc.processOutput()
self.stdout_proc.waitForFinish()
self.stdout_proc.wait()
self.stdout_proc = None
@property

Просмотреть файл

@ -40,9 +40,8 @@ public class FennecNativeElement implements Element {
private boolean mClickSuccess;
public boolean click() {
final SynchronousQueue syncQueue = new SynchronousQueue();
mClickSuccess = false;
mActivity.runOnUiThread(
RobocopUtils.runOnUiThreadSync(mActivity,
new Runnable() {
public void run() {
View view = (View)mActivity.findViewById(mId);
@ -57,32 +56,16 @@ public class FennecNativeElement implements Element {
FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR,
"click: unable to find view "+mId);
}
try {
syncQueue.put(new Object());
} catch (InterruptedException e) {
FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR, e);
}
}
});
try {
// Wait for the UiThread code to finish running
if (syncQueue.poll(MAX_WAIT_MS, TimeUnit.MILLISECONDS) == null) {
FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR,
"click: time-out waiting for UI thread");
FennecNativeDriver.logAllStackTraces(FennecNativeDriver.LogLevel.ERROR);
}
} catch (InterruptedException e) {
FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR, e);
}
return mClickSuccess;
}
private Object mText;
public String getText() {
final SynchronousQueue syncQueue = new SynchronousQueue();
mText = null;
mActivity.runOnUiThread(
RobocopUtils.runOnUiThreadSync(mActivity,
new Runnable() {
public void run() {
View v = mActivity.findViewById(mId);
@ -109,24 +92,9 @@ public class FennecNativeElement implements Element {
FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR,
"getText: unhandled type for view "+mId);
}
try {
syncQueue.put(new Object());
} catch (InterruptedException e) {
FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR, e);
}
} // end of run() method definition
} // end of anonymous Runnable object instantiation
);
try {
// Wait for the UiThread code to finish running
if (syncQueue.poll(MAX_WAIT_MS, TimeUnit.MILLISECONDS) == null) {
FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR,
"getText: time-out waiting for UI thread");
FennecNativeDriver.logAllStackTraces(FennecNativeDriver.LogLevel.ERROR);
}
} catch (InterruptedException e) {
FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR, e);
}
if (mText == null) {
FennecNativeDriver.log(FennecNativeDriver.LogLevel.WARN,
"getText: Text is null for view "+mId);
@ -138,33 +106,16 @@ public class FennecNativeElement implements Element {
private boolean mDisplayed;
public boolean isDisplayed() {
final SynchronousQueue syncQueue = new SynchronousQueue();
mDisplayed = false;
mActivity.runOnUiThread(
RobocopUtils.runOnUiThreadSync(mActivity,
new Runnable() {
public void run() {
View view = (View)mActivity.findViewById(mId);
if (view != null) {
mDisplayed = true;
}
try {
syncQueue.put(new Object());
} catch (InterruptedException e) {
FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR, e);
}
}
});
try {
// Wait for the UiThread code to finish running
if (syncQueue.poll(MAX_WAIT_MS, TimeUnit.MILLISECONDS) == null) {
FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR,
"isDisplayed: time-out waiting for UI thread");
FennecNativeDriver.logAllStackTraces(FennecNativeDriver.LogLevel.ERROR);
}
} catch (InterruptedException e) {
FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR, e);
}
return mDisplayed;
}
}

Просмотреть файл

@ -35,6 +35,7 @@ _JAVA_HARNESS = \
FennecNativeDriver.java \
FennecNativeElement.java \
RoboCopException.java \
RobocopUtils.java \
PaintedSurface.java \
$(NULL)

Просмотреть файл

@ -0,0 +1,43 @@
#filter substitution
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package @ANDROID_PACKAGE_NAME@;
import android.app.Activity;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
public final class RobocopUtils {
private static final int MAX_WAIT_MS = 3000;
private RobocopUtils() {}
public static void runOnUiThreadSync(Activity activity, final Runnable runnable) {
final SynchronousQueue syncQueue = new SynchronousQueue();
activity.runOnUiThread(
new Runnable() {
public void run() {
runnable.run();
try {
syncQueue.put(new Object());
} catch (InterruptedException e) {
FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR, e);
}
}
});
try {
// Wait for the UiThread code to finish running
if (syncQueue.poll(MAX_WAIT_MS, TimeUnit.MILLISECONDS) == null) {
FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR,
"time-out waiting for UI thread");
FennecNativeDriver.logAllStackTraces(FennecNativeDriver.LogLevel.ERROR);
}
} catch (InterruptedException e) {
FennecNativeDriver.log(FennecNativeDriver.LogLevel.ERROR, e);
}
}
}

Просмотреть файл

@ -482,7 +482,8 @@ nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(JSContext *cx)
return JS_TRUE;
bool evalOK = true;
rv = csp->GetAllowsEval(&evalOK);
bool reportViolation = false;
rv = csp->GetAllowsEval(&reportViolation, &evalOK);
if (NS_FAILED(rv))
{
@ -490,9 +491,7 @@ nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(JSContext *cx)
return JS_TRUE; // fail open to not break sites.
}
if (!evalOK) {
// get the script filename, script sample, and line number
// to log with the violation
if (reportViolation) {
nsAutoString fileName;
unsigned lineNum = 0;
NS_NAMED_LITERAL_STRING(scriptSample, "call to eval() or related function blocked by CSP");
@ -503,7 +502,6 @@ nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(JSContext *cx)
CopyUTF8toUTF16(nsDependentCString(file), fileName);
}
}
csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
fileName,
scriptSample,

Просмотреть файл

@ -4242,6 +4242,7 @@ MOZ_PEERCONNECTION=
MOZ_SRTP=
MOZ_WEBRTC_SIGNALING=
MOZ_WEBRTC_IN_LIBXUL=
MOZ_WEBRTC_ASSERT_ALWAYS=1
MOZ_SCTP=
MOZ_MEDIA_PLUGINS=
MOZ_MEDIA_NAVIGATOR=
@ -5311,6 +5312,9 @@ MOZ_ARG_DISABLE_BOOL(webrtc,
if test -n "$MOZ_WEBRTC"; then
AC_DEFINE(MOZ_WEBRTC)
dnl MOZ_WEBRTC_ASSERT_ALWAYS turns on a number of safety asserts in
dnl opt/production builds (via MOZ_CRASH())
AC_DEFINE(MOZ_WEBRTC_ASSERT_ALWAYS)
MOZ_MEDIA=1
MOZ_RAW=1
MOZ_VP8=1
@ -5352,6 +5356,7 @@ AC_SUBST(MOZ_WEBRTC_LEAKING_TESTS)
AC_SUBST(MOZ_WEBRTC_SIGNALING)
AC_SUBST(MOZ_PEERCONNECTION)
AC_SUBST(MOZ_WEBRTC_IN_LIBXUL)
AC_SUBST(MOZ_WEBRTC_ASSERT_ALWAYS)
AC_SUBST(MOZ_SCTP)
AC_SUBST(MOZ_SRTP)
@ -5999,7 +6004,7 @@ if test "$MOZ_GAMEPAD"; then
if ! test -f "$MOZ_DIRECTX_SDK_PATH"/lib/$MOZ_DIRECTX_SDK_CPU_SUFFIX/dxguid.lib ; then
MOZ_GAMEPAD=
fi
else
elif test "$GCC" != "yes"; then
MOZ_GAMEPAD=
fi
if test -z "$MOZ_GAMEPAD"; then

Просмотреть файл

@ -673,7 +673,7 @@ public:
* If you also need to determine whether the parser is the one creating your
* element (through createElement() or cloneNode() generally) then add a
* uint32_t aFromParser to the NS_NewXXX() constructor for your element and
* have the parser pass the appropriate flags. See nsHTMLInputElement.cpp and
* have the parser pass the appropriate flags. See HTMLInputElement.cpp and
* nsHTMLContentSink::MakeContentObject().
*
* DO NOT USE THIS METHOD to get around the fact that it's hard to deal with
@ -709,7 +709,7 @@ public:
* If you also need to determine whether the parser is the one creating your
* element (through createElement() or cloneNode() generally) then add a
* boolean aFromParser to the NS_NewXXX() constructor for your element and
* have the parser pass true. See nsHTMLInputElement.cpp and
* have the parser pass true. See HTMLInputElement.cpp and
* nsHTMLContentSink::MakeContentObject().
*
* @param aHaveNotified Whether there has been a

Просмотреть файл

@ -9,11 +9,11 @@ interface nsIHttpChannel;
interface nsIDocShell;
/**
* nsIContentSecurityPolicy
* nsIContentSecurityPolicy
* Describes an XPCOM component used to model an enforce CSPs.
*/
[scriptable, uuid(d1680bb4-1ac0-4772-9437-1188375e44f2)]
[scriptable, uuid(91E1F257-914C-4D4F-902C-F67F772839AB)]
interface nsIContentSecurityPolicy : nsISupports
{
@ -37,14 +37,30 @@ interface nsIContentSecurityPolicy : nsISupports
/**
* Whether this policy allows in-page script.
* @param shouldReportViolation
* Whether or not the use of inline script should be reported.
* This function always returns "true" for report-only policies, but when
* the report-only policy is violated, shouldReportViolation is true as
* well.
* @return
* Whether or not the effects of the inline script should be allowed
* (block the compilation if false).
*/
readonly attribute boolean allowsInlineScript;
boolean getAllowsInlineScript(out boolean shouldReportViolation);
/**
* whether this policy allows eval and eval-like functions
* such as setTimeout("code string", time).
* @param shouldReportViolation
* Whether or not the use of eval should be reported.
* This function always returns "true" for report-only policies, but when
* the report-only policy is violated, shouldReportViolation is true as
* well.
* @return
* Whether or not the effects of the eval call should be allowed
* (block the call if false).
*/
readonly attribute boolean allowsEval;
boolean getAllowsEval(out boolean shouldReportViolation);
/**
* Log policy violation on the Error Console and send a report if a report-uri
@ -78,7 +94,7 @@ interface nsIContentSecurityPolicy : nsISupports
* a sample of the violating inline script
* @param lineNum
* source line number of the violation (if available)
* @return
* @return
* nothing.
*/
void sendReports(in AString blockedURI,

Просмотреть файл

@ -282,7 +282,7 @@ public:
// - HTMLFieldSetElement: mElements, mDependentElements, mFirstLegend
// - nsHTMLFormElement: many!
// - HTMLFrameSetElement: mRowSpecs, mColSpecs
// - nsHTMLInputElement: mInputData, mFiles, mFileList, mStaticDocfileList
// - HTMLInputElement: mInputData, mFiles, mFileList, mStaticDocfileList
// - nsHTMLMapElement: mAreas
// - HTMLMediaElement: many!
// - nsHTMLOutputElement: mDefaultValue, mTokenList
@ -1812,6 +1812,12 @@ extern const nsIID kThisPtrOffsetsSID;
NS_OFFSET_AND_INTERFACE_TABLE_BEGIN_AMBIGUOUS(_class, nsINode) \
NS_INTERFACE_TABLE_ENTRY(_class, nsINode)
#define NS_NODE_INTERFACE_TABLE1(_class, _i1) \
NS_NODE_OFFSET_AND_INTERFACE_TABLE_BEGIN(_class) \
NS_INTERFACE_TABLE_ENTRY(_class, _i1) \
NS_OFFSET_AND_INTERFACE_TABLE_END \
NS_OFFSET_AND_INTERFACE_TABLE_TO_MAP_SEGUE
#define NS_NODE_INTERFACE_TABLE2(_class, _i1, _i2) \
NS_NODE_OFFSET_AND_INTERFACE_TABLE_BEGIN(_class) \
NS_INTERFACE_TABLE_ENTRY(_class, _i1) \

Просмотреть файл

@ -302,7 +302,7 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
// parse "allow" as equivalent to "default-src", at least until the spec
// stabilizes, at which time we can stop parsing "allow"
if (dirname === CSPRep.ALLOW_DIRECTIVE) {
cspWarn(aCSPR, CSPLocalizer.getStr("allowDirectiveDeprecated"));
cspWarn(aCSPR, CSPLocalizer.getStr("allowDirectiveIsDeprecated"));
if (aCSPR._directives.hasOwnProperty(SD.DEFAULT_SRC)) {
// Check for duplicate default-src and allow directives
cspError(aCSPR, CSPLocalizer.getFormatStr("duplicateDirective",

Просмотреть файл

@ -128,11 +128,19 @@ ContentSecurityPolicy.prototype = {
return this._policy.toString();
},
get allowsInlineScript() {
getAllowsInlineScript: function(shouldReportViolation) {
// report it?
shouldReportViolation.value = !this._policy.allowsInlineScripts;
// allow it to execute?
return this._reportOnlyMode || this._policy.allowsInlineScripts;
},
get allowsEval() {
getAllowsEval: function(shouldReportViolation) {
// report it?
shouldReportViolation.value = !this._policy.allowsEvalInScripts;
// allow it to execute?
return this._reportOnlyMode || this._policy.allowsEvalInScripts;
},

Просмотреть файл

@ -420,7 +420,7 @@ TraceActiveWindowGlobal(const uint64_t& aId, nsGlobalWindow*& aWindow, void* aCl
if (aWindow->GetDocShell() && aWindow->IsOuterWindow()) {
TraceClosure* closure = static_cast<TraceClosure*>(aClosure);
if (JSObject* global = aWindow->FastGetGlobalJSObject()) {
JS_CALL_OBJECT_TRACER(closure->mTrc, global, "active window global");
JS_CallObjectTracer(closure->mTrc, global, "active window global");
}
#ifdef MOZ_XUL
nsIDocument* doc = aWindow->GetExtantDoc();

Просмотреть файл

@ -1693,7 +1693,7 @@ nsContentUtils::TraceSafeJSContext(JSTracer* aTrc)
return;
}
if (JSObject* global = JS_GetGlobalObject(cx)) {
JS_CALL_OBJECT_TRACER(aTrc, global, "safe context");
JS_CallObjectTracer(aTrc, global, "safe context");
}
}

Просмотреть файл

@ -63,6 +63,7 @@ public:
// one) once we block GC until all the (appropriate) onXxxx handlers
// are dropped. (See WebRTC spec)
mDataChannel->SetListener(nullptr, nullptr);
mDataChannel->Close();
}
nsresult Init(nsPIDOMWindow* aDOMWindow);

Просмотреть файл

@ -134,14 +134,15 @@ public:
}
~nsIdentifierMapEntry();
void SetInvalidName();
bool IsInvalidName();
void AddNameElement(nsIDocument* aDocument, Element* aElement);
void RemoveNameElement(Element* aElement);
bool IsEmpty();
nsBaseContentList* GetNameContentList() {
return mNameContentList;
}
bool HasNameElement() const {
return mNameContentList && !mNameContentList->Length() != 0;
}
/**
* Returns the element if we know the element associated with this

Просмотреть файл

@ -637,7 +637,6 @@ GK_ATOM(onalerting, "onalerting")
GK_ATOM(onanimationend, "onanimationend")
GK_ATOM(onanimationiteration, "onanimationiteration")
GK_ATOM(onanimationstart, "onanimationstart")
GK_ATOM(onauthorize, "onauthorize")
GK_ATOM(onAppCommand, "onAppCommand")
GK_ATOM(onbeforecopy, "onbeforecopy")
GK_ATOM(onbeforecut, "onbeforecut")
@ -651,7 +650,6 @@ GK_ATOM(onbroadcast, "onbroadcast")
GK_ATOM(onbusy, "onbusy")
GK_ATOM(oncached, "oncached")
GK_ATOM(oncallschanged, "oncallschanged")
GK_ATOM(oncancel, "oncancel")
GK_ATOM(oncardstatechange, "oncardstatechange")
GK_ATOM(oncfstatechange, "oncfstatechange")
GK_ATOM(onchange, "onchange")
@ -676,8 +674,6 @@ GK_ATOM(ondataerror, "ondataerror")
GK_ATOM(ondblclick, "ondblclick")
GK_ATOM(ondeliverysuccess, "ondeliverysuccess")
GK_ATOM(ondeliveryerror, "ondeliveryerror")
GK_ATOM(ondevicecreated, "ondevicecreated")
GK_ATOM(ondevicedisappeared, "ondevicedisappeared")
GK_ATOM(ondevicefound, "ondevicefound")
GK_ATOM(ondialing, "ondialing")
GK_ATOM(ondisabled, "ondisabled")
@ -762,12 +758,8 @@ GK_ATOM(onpopuphidden, "onpopuphidden")
GK_ATOM(onpopuphiding, "onpopuphiding")
GK_ATOM(onpopupshowing, "onpopupshowing")
GK_ATOM(onpopupshown, "onpopupshown")
GK_ATOM(onpropertychanged, "onpropertychanged")
GK_ATOM(onreadystatechange, "onreadystatechange")
GK_ATOM(onreceived, "onreceived")
GK_ATOM(onrequestconfirmation, "onrequestconfirmation")
GK_ATOM(onrequestpasskey, "onrequestpasskey")
GK_ATOM(onrequestpincode, "onrequestpincode")
GK_ATOM(onRequest, "onRequest")
GK_ATOM(onreset, "onreset")
GK_ATOM(onresuming, "onresuming")

Просмотреть файл

@ -23,6 +23,7 @@
#include "nsIScriptObjectPrincipal.h"
#include "nsISecureBrowserUI.h"
#include "nsIDocumentLoader.h"
#include "nsIWebNavigation.h"
#include "nsLoadGroup.h"
#include "prlog.h"
@ -353,8 +354,6 @@ nsMixedContentBlocker::ShouldLoad(uint32_t aContentType,
return NS_OK;
}
// If we are here we have mixed content.
// Determine if the rootDoc is https and if the user decided to allow Mixed Content
nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(aRequestingContext);
NS_ENSURE_TRUE(docShell, NS_OK);
@ -366,10 +365,53 @@ nsMixedContentBlocker::ShouldLoad(uint32_t aContentType,
return rv;
}
// Get the root document from the docshell
// Get the sameTypeRoot tree item from the docshell
nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
docShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!");
NS_ASSERTION(sameTypeRoot, "No root tree item from docshell!");
// When navigating an iframe, the iframe may be https
// but its parents may not be. Check the parents to see if any of them are https.
// If none of the parents are https, allow the load.
if (aContentType == TYPE_SUBDOCUMENT && !rootHasSecureConnection) {
bool httpsParentExists = false;
nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
parentTreeItem = docShell;
while(!httpsParentExists && parentTreeItem) {
nsCOMPtr<nsIWebNavigation> parentAsNav(do_QueryInterface(parentTreeItem));
NS_ASSERTION(parentAsNav, "No web navigation object from parent's docshell tree item");
nsCOMPtr<nsIURI> parentURI;
parentAsNav->GetCurrentURI(getter_AddRefs(parentURI));
if (!parentURI || NS_FAILED(parentURI->SchemeIs("https", &httpsParentExists))) {
// if getting the URI or the scheme fails, assume there is a https parent and break.
httpsParentExists = true;
break;
}
// When the parent and the root are the same, we have traversed all the way up
// the same type docshell tree. Break out of the while loop.
if(sameTypeRoot == parentTreeItem) {
break;
}
// update the parent to the grandparent.
nsCOMPtr<nsIDocShellTreeItem> newParentTreeItem;
parentTreeItem->GetSameTypeParent(getter_AddRefs(newParentTreeItem));
parentTreeItem = newParentTreeItem;
} // end while loop.
if (!httpsParentExists) {
*aDecision = nsIContentPolicy::ACCEPT;
return NS_OK;
}
}
// Get the root document from the sameTypeRoot
nsCOMPtr<nsIDocument> rootDoc = do_GetInterface(sameTypeRoot);
NS_ASSERTION(rootDoc, "No root document from document shell root tree item.");

Просмотреть файл

@ -613,12 +613,12 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
if (csp) {
PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("New ScriptLoader i ****with CSP****"));
bool inlineOK;
rv = csp->GetAllowsInlineScript(&inlineOK);
bool inlineOK = true;
bool reportViolations = false;
rv = csp->GetAllowsInlineScript(&reportViolations, &inlineOK);
NS_ENSURE_SUCCESS(rv, false);
if (!inlineOK) {
PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSP blocked inline scripts (2)"));
if (reportViolations) {
// gather information to log with violation report
nsIURI* uri = mDocument->GetDocumentURI();
nsAutoCString asciiSpec;
@ -636,6 +636,10 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
NS_ConvertUTF8toUTF16(asciiSpec),
scriptText,
aElement->GetScriptLineNumber());
}
if (!inlineOK) {
PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSP blocked inline scripts (2)"));
return false;
}
}

Просмотреть файл

@ -594,10 +594,17 @@ MOCHITEST_FILES_C= \
test_mixed_content_blocker.html \
file_mixed_content_main.html \
file_mixed_content_server.sjs \
test_mixed_content_blocker_bug803225.html \
file_mixed_content_main_bug803225.html \
file_mixed_content_main_bug803225_websocket_wsh.py \
bug803225_test_mailto.html \
test_mixed_content_blocker_bug803225.html \
file_mixed_content_main_bug803225.html \
file_mixed_content_main_bug803225_websocket_wsh.py \
bug803225_test_mailto.html \
test_mixed_content_blocker_frameNavigation.html \
file_mixed_content_frameNavigation.html \
file_mixed_content_frameNavigation_innermost.html \
file_mixed_content_frameNavigation_grandchild.html \
file_mixed_content_frameNavigation_secure.html \
file_mixed_content_frameNavigation_secure_grandchild.html \
file_mixed_content_frameNavigation_blankTarget.html \
test_bug789856.html \
file_bug804395.jar \
test_bug804395.html \

Просмотреть файл

@ -214,7 +214,9 @@ var gTestRunner = runTest();
SpecialPowers.addPermission("browser", true, "https://example.com");
SpecialPowers.pushPrefEnv({'set': [["dom.mozBrowserFramesEnabled", true],
["security.apps.privileged.CSP.default", DEFAULT_CSP_PRIV],
["security.apps.certified.CSP.default", DEFAULT_CSP_CERT]]},
["security.apps.certified.CSP.default", DEFAULT_CSP_CERT],
["security.mixed_content.block_active_content", false],
["security.mixed_content.block_display_content", false]]},
function() { gTestRunner.next(); });

Просмотреть файл

@ -1,2 +1,2 @@
Cache-Control: no-cache
X-Content-Security-Policy: allow 'self'
X-Content-Security-Policy: default-src 'self'

Просмотреть файл

@ -8,10 +8,12 @@ function logResult(str, passed) {
document.body.appendChild(elt);
}
window._testResults = {};
// callback for when stuff is allowed by CSP
var onevalexecuted = (function(window) {
return function(shouldrun, what, data) {
window._testResults[what] = "ran";
window.parent.scriptRan(shouldrun, what, data);
logResult((shouldrun ? "PASS: " : "FAIL: ") + what + " : " + data, shouldrun);
};})(window);
@ -19,6 +21,7 @@ var onevalexecuted = (function(window) {
// callback for when stuff is blocked
var onevalblocked = (function(window) {
return function(shouldrun, what, data) {
window._testResults[what] = "blocked";
window.parent.scriptBlocked(shouldrun, what, data);
logResult((shouldrun ? "FAIL: " : "PASS: ") + what + " : " + data, !shouldrun);
};})(window);
@ -28,26 +31,38 @@ var onevalblocked = (function(window) {
// out.
addEventListener('load', function() {
// setTimeout(String) test -- should pass
try {
setTimeout('onevalexecuted(false, "setTimeout(String)", "setTimeout with a string was enabled.");', 10);
} catch (e) {
onevalblocked(false, "setTimeout(String)",
"setTimeout with a string was blocked");
// setTimeout(String) test -- mutate something in the window._testResults
// obj, then check it.
{
var str_setTimeoutWithStringRan = 'onevalexecuted(false, "setTimeout(String)", "setTimeout with a string was enabled.");';
function fcn_setTimeoutWithStringCheck() {
if (this._testResults["setTimeout(String)"] !== "ran") {
onevalblocked(false, "setTimeout(String)",
"setTimeout with a string was blocked");
}
}
setTimeout(fcn_setTimeoutWithStringCheck.bind(window), 10);
setTimeout(str_setTimeoutWithStringRan, 10);
}
// setTimeout(function) test -- should pass
try {
setTimeout(function() {
onevalexecuted(true, "setTimeout(function)",
"setTimeout with a function was enabled.")
}, 10);
} catch (e) {
onevalblocked(true, "setTimeout(function)",
"setTimeout with a function was blocked");
// setTimeout(function) test -- mutate something in the window._testResults
// obj, then check it.
{
function fcn_setTimeoutWithFunctionRan() {
onevalexecuted(true, "setTimeout(function)",
"setTimeout with a function was enabled.")
}
function fcn_setTimeoutWithFunctionCheck() {
if (this._testResults["setTimeout(function)"] !== "ran") {
onevalblocked(true, "setTimeout(function)",
"setTimeout with a function was blocked");
}
}
setTimeout(fcn_setTimeoutWithFunctionRan.bind(window), 10);
setTimeout(fcn_setTimeoutWithFunctionCheck.bind(window), 10);
}
// eval() test
// eval() test -- should throw exception as per spec
try {
eval('onevalexecuted(false, "eval(String)", "eval() was enabled.");');
} catch (e) {
@ -55,7 +70,7 @@ addEventListener('load', function() {
"eval() was blocked");
}
// eval(foo,bar) test
// eval(foo,bar) test -- should throw exception as per spec
try {
eval('onevalexecuted(false, "eval(String,scope)", "eval() was enabled.");',1);
} catch (e) {
@ -63,7 +78,7 @@ addEventListener('load', function() {
"eval() with scope was blocked");
}
// [foo,bar].sort(eval) test
// [foo,bar].sort(eval) test -- should throw exception as per spec
try {
['onevalexecuted(false, "[String, obj].sort(eval)", "eval() was enabled.");',1].sort(eval);
} catch (e) {
@ -71,7 +86,7 @@ addEventListener('load', function() {
"eval() with scope via sort was blocked");
}
// [].sort.call([foo,bar], eval) test
// [].sort.call([foo,bar], eval) test -- should throw exception as per spec
try {
[].sort.call(['onevalexecuted(false, "[String, obj].sort(eval)", "eval() was enabled.");',1], eval);
} catch (e) {
@ -79,7 +94,7 @@ addEventListener('load', function() {
"eval() with scope via sort/call was blocked");
}
// new Function() test
// new Function() test -- should throw exception as per spec
try {
var fcn = new Function('onevalexecuted(false, "new Function(String)", "new Function(String) was enabled.");');
fcn();

Просмотреть файл

@ -0,0 +1,74 @@
<!DOCTYPE HTML>
<html>
<!--
Tests for Mixed Content Blocker related to navigating children, grandchildren, etc
https://bugzilla.mozilla.org/show_bug.cgi?id=840388
-->
<head>
<meta charset="utf-8">
<title>Tests for Mixed Content Frame Navigation</title>
</head>
<body>
<div id="testContent"></div>
<script>
var baseUrlHttps = "https://example.com/tests/content/base/test/file_mixed_content_frameNavigation_innermost.html";
// For tests that require setTimeout, set the maximum polling time to 50 x 100ms = 5 seconds.
var MAX_COUNT = 50;
var TIMEOUT_INTERVAL = 100;
var testContent = document.getElementById("testContent");
// Test 1: Navigate secure iframe to insecure iframe on an insecure page
var iframe_test1 = document.createElement("iframe");
var counter_test1 = 0;
iframe_test1.src = baseUrlHttps + "?insecurePage_navigate_child";
iframe_test1.setAttribute("id", "test1");
iframe_test1.onerror = function() {
parent.postMessage({"test": "insecurePage_navigate_child", "msg": "got an onerror alert when loading or navigating testing iframe"}, "http://mochi.test:8888");
};
testContent.appendChild(iframe_test1);
function navigationStatus(iframe_test1)
{
// When the page is navigating, it goes through about:blank and we will get a permission denied for loc.
// Catch that specific exception and return
try {
var loc = document.getElementById("test1").contentDocument.location;
} catch(e) {
if (e.name === "SecurityError") {
// We received an exception we didn't expect.
throw e;
}
counter_test1++;
return;
}
if (loc == "http://example.com/tests/content/base/test/file_mixed_content_frameNavigation_innermost.html?insecurePage_navigate_child_response") {
return;
}
else {
if(counter_test1 < MAX_COUNT) {
counter_test1++;
setTimeout(navigationStatus, TIMEOUT_INTERVAL, iframe_test1);
}
else {
// After we have called setTimeout the maximum number of times, assume navigating the iframe is blocked
parent.postMessage({"test": "insecurePage_navigate_child", "msg": "navigating to insecure iframe blocked on insecure page"}, "http://mochi.test:8888");
}
}
}
setTimeout(navigationStatus, TIMEOUT_INTERVAL, iframe_test1);
// Test 2: Navigate secure grandchild iframe to insecure grandchild iframe on a page that has no secure parents
var iframe_test2 = document.createElement("iframe");
iframe_test2.src = "http://example.com/tests/content/base/test/file_mixed_content_frameNavigation_grandchild.html"
iframe_test2.onerror = function() {
parent.postMessage({"test": "insecurePage_navigate_grandchild", "msg": "got an on error alert when loading or navigating testing iframe"}, "http://mochi.test:8888");
};
testContent.appendChild(iframe_test2);
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,32 @@
<!DOCTYPE HTML>
<html>
<!--
Tests for Mixed Content Blocker - Opening link with _blank target in an https iframe.
https://bugzilla.mozilla.org/show_bug.cgi?id=841850
-->
<head>
<meta charset="utf-8">
<title>Tests for Mixed Content Frame Navigation</title>
</head>
<body>
<a href="http://example.com/tests/content/base/test/file_mixed_content_frameNavigation_innermost.html?blankTarget" id="blankTarget" target="_blank">Go to http site</a>
<script>
var blankTarget = document.getElementById("blankTarget");
blankTarget.click();
var os = SpecialPowers.Cc["@mozilla.org/observer-service;1"].
getService(SpecialPowers.Components.interfaces.nsIObserverService);
var observer = {
observe: function(subject, topic, data) {
if(topic == "content-document-global-created" && data =="http://example.com") {
parent.parent.postMessage({"test": "blankTarget", "msg": "opened an http link with target=_blank from a secure page"}, "http://mochi.test:8888");
os.removeObserver(observer, "content-document-global-created");
}
}
}
os.addObserver(observer, "content-document-global-created", false);
</script>
</body>
</html>

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше