mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 19:35:51 +00:00
Merge m-c to s-c.
This commit is contained in:
commit
c37ec8d740
@ -35,7 +35,7 @@ DIST_GARBAGE = config.cache config.log config.status* config-defs.h \
|
||||
netwerk/necko-config.h xpcom/xpcom-config.h xpcom/xpcom-private.h \
|
||||
$(topsrcdir)/.mozconfig.mk $(topsrcdir)/.mozconfig.out
|
||||
|
||||
default alldep all:: $(topsrcdir)/configure config.status
|
||||
default alldep all:: CLOBBER $(topsrcdir)/configure config.status
|
||||
$(RM) -r $(DIST)/sdk
|
||||
$(RM) -r $(DIST)/include
|
||||
$(RM) -r $(DIST)/private
|
||||
@ -43,6 +43,12 @@ default alldep all:: $(topsrcdir)/configure config.status
|
||||
$(RM) $(DIST)/bin/chrome.manifest $(DIST)/bin/components/components.manifest
|
||||
$(RM) -r _tests
|
||||
|
||||
CLOBBER: $(topsrcdir)/CLOBBER
|
||||
@echo "STOP! The CLOBBER file has changed."
|
||||
@echo "Please run the build through a sanctioned build wrapper, such as"
|
||||
@echo "'mach build' or client.mk."
|
||||
@exit 1
|
||||
|
||||
$(topsrcdir)/configure: $(topsrcdir)/configure.in
|
||||
@echo "STOP! configure.in has changed, and your configure is out of date."
|
||||
@echo "Please rerun autoconf and re-configure your build directory."
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 sw=2 et tw=78: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
@ -1272,27 +1273,48 @@ HyperTextAccessible::NativeAttributes()
|
||||
return attributes.forget();
|
||||
|
||||
// For the html landmark elements we expose them like we do aria landmarks to
|
||||
// make AT navigation schemes "just work". Note html:header is redundant as
|
||||
// a landmark since it usually contains headings. We're not yet sure how the
|
||||
// web will use html:footer but our best bet right now is as contentinfo.
|
||||
if (mContent->Tag() == nsGkAtoms::nav)
|
||||
// make AT navigation schemes "just work".
|
||||
nsIAtom* tag = mContent->Tag();
|
||||
if (tag == nsGkAtoms::nav) {
|
||||
nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
|
||||
NS_LITERAL_STRING("navigation"));
|
||||
else if (mContent->Tag() == nsGkAtoms::section)
|
||||
} else if (tag == nsGkAtoms::section) {
|
||||
nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
|
||||
NS_LITERAL_STRING("region"));
|
||||
else if (mContent->Tag() == nsGkAtoms::footer)
|
||||
} else if (tag == nsGkAtoms::header || tag == nsGkAtoms::footer) {
|
||||
// Only map header and footer if they are not descendants
|
||||
// of an article or section tag.
|
||||
nsIContent* parent = mContent->GetParent();
|
||||
while (parent) {
|
||||
if (parent->Tag() == nsGkAtoms::article ||
|
||||
parent->Tag() == nsGkAtoms::section)
|
||||
break;
|
||||
parent = parent->GetParent();
|
||||
}
|
||||
|
||||
// No article or section elements found.
|
||||
if (!parent) {
|
||||
if (tag == nsGkAtoms::header) {
|
||||
nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
|
||||
NS_LITERAL_STRING("banner"));
|
||||
} else if (tag == nsGkAtoms::footer) {
|
||||
nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
|
||||
NS_LITERAL_STRING("contentinfo"));
|
||||
}
|
||||
}
|
||||
} else if (tag == nsGkAtoms::footer) {
|
||||
nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
|
||||
NS_LITERAL_STRING("contentinfo"));
|
||||
else if (mContent->Tag() == nsGkAtoms::aside)
|
||||
} else if (tag == nsGkAtoms::aside) {
|
||||
nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
|
||||
NS_LITERAL_STRING("complementary"));
|
||||
else if (mContent->Tag() == nsGkAtoms::article)
|
||||
} else if (tag == nsGkAtoms::article) {
|
||||
nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
|
||||
NS_LITERAL_STRING("article"));
|
||||
else if (mContent->Tag() == nsGkAtoms::main)
|
||||
} else if (tag == nsGkAtoms::main) {
|
||||
nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
|
||||
NS_LITERAL_STRING("main"));
|
||||
}
|
||||
|
||||
return attributes.forget();
|
||||
}
|
||||
|
@ -21,7 +21,12 @@
|
||||
{
|
||||
// Some AT may look for this
|
||||
testAttrs("nav", {"xml-roles" : "navigation"}, true);
|
||||
testAttrs("header", {"xml-roles" : "banner"}, true);
|
||||
testAbsentAttrs("article_header", {"xml-roles" : "banner"});
|
||||
testAbsentAttrs("section_header", {"xml-roles" : "banner"});
|
||||
testAttrs("footer", {"xml-roles" : "contentinfo"}, true);
|
||||
testAbsentAttrs("article_footer", {"xml-roles" : "contentinfo"});
|
||||
testAbsentAttrs("section_footer", {"xml-roles" : "contentinfo"});
|
||||
testAttrs("aside", {"xml-roles" : "complementary"}, true);
|
||||
testAttrs("section", {"xml-roles" : "region"}, true);
|
||||
testAttrs("main", {"xml-roles" : "main"}, true); // // ARIA override
|
||||
@ -68,13 +73,27 @@
|
||||
title="HTML5 article element should expose xml-roles:article object attribute">
|
||||
Bug 761891
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=849624"
|
||||
title="modify HTML5 header and footer accessibility API mapping">
|
||||
Bug 849624
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<nav id="nav">a nav</nav>
|
||||
<header id="header">a header</header>
|
||||
<footer id="footer">a footer</footer>
|
||||
<article id="article_with_header_and_footer">
|
||||
<header id="article_header">a header within an article</header>
|
||||
<footer id="article_footer">a footer within an article</footer>
|
||||
</article>
|
||||
<section id="section_with_header_and_footer">
|
||||
<header id="section_header">a header within an section</header>
|
||||
<footer id="section_footer">a footer within an section</footer>
|
||||
</section>
|
||||
<aside id="aside">by the way I am an aside</aside>
|
||||
<section id="section">a section</section>
|
||||
<article id="main" role="main">a main area</article>
|
||||
|
@ -96,10 +96,8 @@
|
||||
}
|
||||
|
||||
if (MAC && (navigator.userAgent.indexOf("Mac OS X 10.6") != -1)) {
|
||||
|
||||
todo(false,
|
||||
"Re-enable on Mac OS 10.6 after fixing bug 845095 - intermittent orange");
|
||||
SimpleTest.finish();
|
||||
} else {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTest);
|
||||
|
16
addon-sdk/source/app-extension/bootstrap.js
vendored
16
addon-sdk/source/app-extension/bootstrap.js
vendored
@ -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;
|
||||
|
46
addon-sdk/source/lib/sdk/view/core.js
Normal file
46
addon-sdk/source/lib/sdk/view/core.js
Normal file
@ -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";
|
5
addon-sdk/source/test/addons/require/tabs.js
Normal file
5
addon-sdk/source/test/addons/require/tabs.js
Normal file
@ -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");
|
||||
}
|
||||
|
70
addon-sdk/source/test/test-tab-utils.js
Normal file
70
addon-sdk/source/test/test-tab-utils.js
Normal file
@ -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
|
||||
|
@ -1,6 +1,6 @@
|
||||
[{
|
||||
"size": 622597962,
|
||||
"digest": "31e1e74c621b98ee0970ed656636b495c4c90e82060442e8f19d35bc1839e55030736b4aa689c8325f9b9b80aa1cc6b1981dd95b88c8c63ca8e5b0db9957ac0a",
|
||||
"size": 621007059,
|
||||
"digest": "7ab61ca7a6c25297bbe3ec8328b2a8b4298cb0656747292e86a8caeef08500f2c68c00b5be02f2d83afaa2ae2fb7240e4d8a321c786fd1b0d57aeaa6e257dad5",
|
||||
"algorithm": "sha512",
|
||||
"filename": "emulator.zip"
|
||||
}]
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0"?>
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1363988948000">
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1364316540000">
|
||||
<emItems>
|
||||
<emItem blockID="i58" id="webmaster@buzzzzvideos.info">
|
||||
<versionRange minVersion="0" maxVersion="*">
|
||||
@ -372,6 +372,10 @@
|
||||
<versionRange minVersion="2.0" maxVersion="2.0">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i326" id="/^((support2_en@adobe14\.com)|(XN4Xgjw7n4@yUWgc\.com)|(C7yFVpIP@WeolS3acxgS\.com)|(Kbeu4h0z@yNb7QAz7jrYKiiTQ3\.com)|(aWQzX@a6z4gWdPu8FF\.com)|(CBSoqAJLYpCbjTP90@JoV0VMywCjsm75Y0toAd\.com)|(zZ2jWZ1H22Jb5NdELHS@o0jQVWZkY1gx1\.com))$/">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i162" id="{EB7508CA-C7B2-46E0-8C04-3E94A035BD49}">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
</versionRange>
|
||||
@ -395,10 +399,6 @@
|
||||
<versionRange minVersion="0" maxVersion="*">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i326" id="/^((support2_en@adobe14\.com)|(XN4Xgjw7n4@yUWgc\.com)|(C7yFVpIP@WeolS3acxgS\.com)|(Kbeu4h0z@yNb7QAz7jrYKiiTQ3\.com)|(aWQzX@a6z4gWdPu8FF\.com)|(CBSoqAJLYpCbjTP90@JoV0VMywCjsm75Y0toAd\.com))$/">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i312" id="extension21804@extension21804.com">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||
</versionRange>
|
||||
@ -803,7 +803,28 @@
|
||||
<pluginItem blockID="p316">
|
||||
<match name="filename" exp="(NPSWF[0-9_]*\.dll)|(Flash\ Player\.plugin)" /> <versionRange minVersion="11.2.202.275" maxVersion="11.4.402.286.9999" severity="0" vulnerabilitystatus="1">
|
||||
<targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
|
||||
<versionRange minVersion="20.0a1" maxVersion="*" />
|
||||
<versionRange minVersion="21.0a1" maxVersion="*" />
|
||||
</targetApplication>
|
||||
</versionRange>
|
||||
</pluginItem>
|
||||
<pluginItem blockID="p328">
|
||||
<match name="filename" exp="Silverlight\.plugin" /> <versionRange minVersion="5.1" maxVersion="5.1.20124.9999" severity="0" vulnerabilitystatus="1">
|
||||
<targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
|
||||
<versionRange minVersion="19.0a1" maxVersion="*" />
|
||||
</targetApplication>
|
||||
</versionRange>
|
||||
</pluginItem>
|
||||
<pluginItem blockID="p330">
|
||||
<match name="description" exp="^Shockwave Flash (([1-9]\.[0-9]+)|(10\.([0-2]|(3 r(([0-9][0-9]?)|1(([0-7][0-9])|8[0-2]))))))( |$)" /> <match name="filename" exp="libflashplayer\.so" /> <versionRange severity="0" vulnerabilitystatus="1">
|
||||
<targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
|
||||
<versionRange minVersion="19.0a1" maxVersion="*" />
|
||||
</targetApplication>
|
||||
</versionRange>
|
||||
</pluginItem>
|
||||
<pluginItem blockID="p332">
|
||||
<match name="description" exp="^Shockwave Flash 11.(0|1) r[0-9]{1,3}$" /> <match name="filename" exp="libflashplayer\.so" /> <versionRange severity="0" vulnerabilitystatus="1">
|
||||
<targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
|
||||
<versionRange minVersion="19.0a1" maxVersion="*" />
|
||||
</targetApplication>
|
||||
</versionRange>
|
||||
</pluginItem>
|
||||
|
@ -10,15 +10,6 @@
|
||||
// From nsEventStateManager.cpp.
|
||||
const MOUSE_SCROLL_ZOOM = 3;
|
||||
|
||||
Cu.import('resource://gre/modules/ContentPrefInstance.jsm');
|
||||
|
||||
function getContentPrefs(aWindow) {
|
||||
let context = aWindow ? aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsILoadContext) : null;
|
||||
return new ContentPrefInstance(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls the "full zoom" setting and its site-specific preferences.
|
||||
*/
|
||||
@ -26,17 +17,6 @@ var FullZoom = {
|
||||
// Identifies the setting in the content prefs database.
|
||||
name: "browser.content.full-zoom",
|
||||
|
||||
// The global value (if any) for the setting. Lazily loaded from the service
|
||||
// when first requested, then updated by the pref change listener as it changes.
|
||||
// If there is no global value, then this should be undefined.
|
||||
get globalValue() {
|
||||
var globalValue = getContentPrefs(gBrowser.contentDocument.defaultView).getPref(null, this.name);
|
||||
if (typeof globalValue != "undefined")
|
||||
globalValue = this._ensureValid(globalValue);
|
||||
delete this.globalValue;
|
||||
return this.globalValue = globalValue;
|
||||
},
|
||||
|
||||
// browser.zoom.siteSpecific preference cache
|
||||
_siteSpecificPref: undefined,
|
||||
|
||||
@ -64,7 +44,9 @@ var FullZoom = {
|
||||
window.addEventListener("DOMMouseScroll", this, false);
|
||||
|
||||
// Register ourselves with the service so we know when our pref changes.
|
||||
getContentPrefs().addObserver(this.name, this);
|
||||
this._cps2 = Cc["@mozilla.org/content-pref/service;1"].
|
||||
getService(Ci.nsIContentPrefService2);
|
||||
this._cps2.addObserverForName(this.name, this);
|
||||
|
||||
this._siteSpecificPref =
|
||||
gPrefService.getBoolPref("browser.zoom.siteSpecific");
|
||||
@ -77,7 +59,7 @@ var FullZoom = {
|
||||
|
||||
destroy: function FullZoom_destroy() {
|
||||
gPrefService.removeObserver("browser.zoom.", this);
|
||||
getContentPrefs().removeObserver(this.name, this);
|
||||
this._cps2.removeObserverForName(this.name, this);
|
||||
window.removeEventListener("DOMMouseScroll", this, false);
|
||||
},
|
||||
|
||||
@ -133,7 +115,14 @@ var FullZoom = {
|
||||
// We have to call _applySettingToPref in a timeout because we handle
|
||||
// the event before the event state manager has a chance to apply the zoom
|
||||
// during nsEventStateManager::PostHandleEvent.
|
||||
window.setTimeout(function (self) { self._applySettingToPref() }, 0, this);
|
||||
//
|
||||
// Since the zoom will have already been updated by the time
|
||||
// _applySettingToPref is called, pass true to suppress updating the zoom
|
||||
// again. Note that this is not an optimization: due to asynchronicity of
|
||||
// the content preference service, the zoom may have been updated again by
|
||||
// the time that onContentPrefSet is called as a result of this call to
|
||||
// _applySettingToPref.
|
||||
window.setTimeout(function (self) self._applySettingToPref(true), 0, this);
|
||||
},
|
||||
|
||||
// nsIObserver
|
||||
@ -158,33 +147,53 @@ var FullZoom = {
|
||||
// nsIContentPrefObserver
|
||||
|
||||
onContentPrefSet: function FullZoom_onContentPrefSet(aGroup, aName, aValue) {
|
||||
let contentPrefs = getContentPrefs(gBrowser.contentDocument.defaultView);
|
||||
if (aGroup == contentPrefs.grouper.group(gBrowser.currentURI))
|
||||
this._applyPrefToSetting(aValue);
|
||||
else if (aGroup == null) {
|
||||
this.globalValue = this._ensureValid(aValue);
|
||||
|
||||
// If the current page doesn't have a site-specific preference,
|
||||
// then its zoom should be set to the new global preference now that
|
||||
// the global preference has changed.
|
||||
if (!contentPrefs.hasPref(gBrowser.currentURI, this.name))
|
||||
this._applyPrefToSetting();
|
||||
if (this._ignoreNextOnContentPrefSet) {
|
||||
delete this._ignoreNextOnContentPrefSet;
|
||||
return;
|
||||
}
|
||||
this._onContentPrefChanged(aGroup, aValue);
|
||||
},
|
||||
|
||||
onContentPrefRemoved: function FullZoom_onContentPrefRemoved(aGroup, aName) {
|
||||
let contentPrefs = getContentPrefs(gBrowser.contentDocument.defaultView);
|
||||
if (aGroup == contentPrefs.grouper.group(gBrowser.currentURI))
|
||||
this._applyPrefToSetting();
|
||||
else if (aGroup == null) {
|
||||
this.globalValue = undefined;
|
||||
this._onContentPrefChanged(aGroup, undefined);
|
||||
},
|
||||
|
||||
// If the current page doesn't have a site-specific preference,
|
||||
// then its zoom should be set to the default preference now that
|
||||
// the global preference has changed.
|
||||
if (!contentPrefs.hasPref(gBrowser.currentURI, this.name))
|
||||
this._applyPrefToSetting();
|
||||
/**
|
||||
* Appropriately updates the zoom level after a content preference has
|
||||
* changed.
|
||||
*
|
||||
* @param aGroup The group of the changed preference.
|
||||
* @param aValue The new value of the changed preference. Pass undefined to
|
||||
* indicate the preference's removal.
|
||||
*/
|
||||
_onContentPrefChanged: function FullZoom__onContentPrefChanged(aGroup, aValue) {
|
||||
if (!gBrowser.currentURI)
|
||||
return;
|
||||
|
||||
let domain = this._cps2.extractDomain(gBrowser.currentURI.spec);
|
||||
if (aGroup) {
|
||||
if (aGroup == domain)
|
||||
this._applyPrefToSetting(aValue);
|
||||
return;
|
||||
}
|
||||
|
||||
this._globalValue = aValue === undefined ? aValue :
|
||||
this._ensureValid(aValue);
|
||||
|
||||
// If the current page doesn't have a site-specific preference, then its
|
||||
// zoom should be set to the new global preference now that the global
|
||||
// preference has changed.
|
||||
let hasPref = false;
|
||||
let ctxt = this._loadContextFromWindow(gBrowser.contentWindow);
|
||||
this._cps2.getByDomainAndName(gBrowser.currentURI.spec, this.name, ctxt, {
|
||||
handleResult: function () hasPref = true,
|
||||
handleCompletion: function () {
|
||||
if (!hasPref &&
|
||||
gBrowser.currentURI &&
|
||||
this._cps2.extractDomain(gBrowser.currentURI.spec) == domain)
|
||||
this._applyPrefToSetting();
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
|
||||
// location change observer
|
||||
@ -201,12 +210,16 @@ var FullZoom = {
|
||||
* (optional) browser object displaying the document
|
||||
*/
|
||||
onLocationChange: function FullZoom_onLocationChange(aURI, aIsTabSwitch, aBrowser) {
|
||||
if (!aURI || (aIsTabSwitch && !this.siteSpecific))
|
||||
if (!aURI || (aIsTabSwitch && !this.siteSpecific)) {
|
||||
this._notifyOnLocationChange();
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid the cps roundtrip and apply the default/global pref.
|
||||
if (aURI.spec == "about:blank") {
|
||||
this._applyPrefToSetting(undefined, aBrowser);
|
||||
this._applyPrefToSetting(undefined, aBrowser, function () {
|
||||
this._notifyOnLocationChange();
|
||||
}.bind(this));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -215,24 +228,32 @@ var FullZoom = {
|
||||
// Media documents should always start at 1, and are not affected by prefs.
|
||||
if (!aIsTabSwitch && browser.contentDocument.mozSyntheticDocument) {
|
||||
ZoomManager.setZoomForBrowser(browser, 1);
|
||||
this._notifyOnLocationChange();
|
||||
return;
|
||||
}
|
||||
|
||||
let contentPrefs = getContentPrefs(gBrowser.contentDocument.defaultView);
|
||||
if (contentPrefs.hasCachedPref(aURI, this.name)) {
|
||||
let zoomValue = contentPrefs.getPref(aURI, this.name);
|
||||
this._applyPrefToSetting(zoomValue, browser);
|
||||
} else {
|
||||
var self = this;
|
||||
contentPrefs.getPref(aURI, this.name, function (aResult) {
|
||||
// Check that we're still where we expect to be in case this took a while.
|
||||
// Null check currentURI, since the window may have been destroyed before
|
||||
// we were called.
|
||||
if (browser.currentURI && aURI.equals(browser.currentURI)) {
|
||||
self._applyPrefToSetting(aResult, browser);
|
||||
}
|
||||
});
|
||||
let ctxt = this._loadContextFromWindow(browser.contentWindow);
|
||||
let pref = this._cps2.getCachedByDomainAndName(aURI.spec, this.name, ctxt);
|
||||
if (pref) {
|
||||
this._applyPrefToSetting(pref.value, browser, function () {
|
||||
this._notifyOnLocationChange();
|
||||
}.bind(this));
|
||||
return;
|
||||
}
|
||||
|
||||
let value = undefined;
|
||||
this._cps2.getByDomainAndName(aURI.spec, this.name, ctxt, {
|
||||
handleResult: function (resultPref) value = resultPref.value,
|
||||
handleCompletion: function () {
|
||||
if (browser.currentURI &&
|
||||
this._cps2.extractDomain(browser.currentURI.spec) ==
|
||||
this._cps2.extractDomain(aURI.spec)) {
|
||||
this._applyPrefToSetting(value, browser, function () {
|
||||
this._notifyOnLocationChange();
|
||||
}.bind(this));
|
||||
}
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
|
||||
// update state of zoom type menu item
|
||||
@ -246,23 +267,43 @@ var FullZoom = {
|
||||
//**************************************************************************//
|
||||
// Setting & Pref Manipulation
|
||||
|
||||
reduce: function FullZoom_reduce() {
|
||||
/**
|
||||
* Reduces the zoom level of the page in the current browser.
|
||||
*
|
||||
* @param callback Optional. If given, it's asynchronously called when the
|
||||
* zoom update completes.
|
||||
*/
|
||||
reduce: function FullZoom_reduce(callback) {
|
||||
ZoomManager.reduce();
|
||||
this._applySettingToPref();
|
||||
this._applySettingToPref(false, callback);
|
||||
},
|
||||
|
||||
enlarge: function FullZoom_enlarge() {
|
||||
/**
|
||||
* Enlarges the zoom level of the page in the current browser.
|
||||
*
|
||||
* @param callback Optional. If given, it's asynchronously called when the
|
||||
* zoom update completes.
|
||||
*/
|
||||
enlarge: function FullZoom_enlarge(callback) {
|
||||
ZoomManager.enlarge();
|
||||
this._applySettingToPref();
|
||||
this._applySettingToPref(false, callback);
|
||||
},
|
||||
|
||||
reset: function FullZoom_reset() {
|
||||
if (typeof this.globalValue != "undefined")
|
||||
ZoomManager.zoom = this.globalValue;
|
||||
else
|
||||
ZoomManager.reset();
|
||||
|
||||
this._removePref();
|
||||
/**
|
||||
* Sets the zoom level of the page in the current browser to the global zoom
|
||||
* level.
|
||||
*
|
||||
* @param callback Optional. If given, it's asynchronously called when the
|
||||
* zoom update completes.
|
||||
*/
|
||||
reset: function FullZoom_reset(callback) {
|
||||
this._getGlobalValue(gBrowser.contentWindow, function (value) {
|
||||
if (value === undefined)
|
||||
ZoomManager.reset();
|
||||
else
|
||||
ZoomManager.zoom = value;
|
||||
this._removePref(callback);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@ -283,38 +324,91 @@ var FullZoom = {
|
||||
* So when we apply new zoom values to the browser, we simply set the zoom.
|
||||
* We don't check first to see if the new value is the same as the current
|
||||
* one.
|
||||
**/
|
||||
_applyPrefToSetting: function FullZoom__applyPrefToSetting(aValue, aBrowser) {
|
||||
if ((!this.siteSpecific) || gInPrintPreviewMode)
|
||||
*
|
||||
* @param aValue The zoom level value.
|
||||
* @param aBrowser The browser containing the page whose zoom level is to be
|
||||
* set. If falsey, the currently selected browser is used.
|
||||
* @param aCallback Optional. If given, it's asynchronously called when
|
||||
* complete.
|
||||
*/
|
||||
_applyPrefToSetting: function FullZoom__applyPrefToSetting(aValue, aBrowser, aCallback) {
|
||||
if (!this.siteSpecific || gInPrintPreviewMode) {
|
||||
this._executeSoon(aCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
var browser = aBrowser || (gBrowser && gBrowser.selectedBrowser);
|
||||
try {
|
||||
if (browser.contentDocument.mozSyntheticDocument)
|
||||
return;
|
||||
|
||||
if (typeof aValue != "undefined")
|
||||
ZoomManager.setZoomForBrowser(browser, this._ensureValid(aValue));
|
||||
else if (typeof this.globalValue != "undefined")
|
||||
ZoomManager.setZoomForBrowser(browser, this.globalValue);
|
||||
else
|
||||
ZoomManager.setZoomForBrowser(browser, 1);
|
||||
}
|
||||
catch(ex) {}
|
||||
},
|
||||
|
||||
_applySettingToPref: function FullZoom__applySettingToPref() {
|
||||
if (!this.siteSpecific || gInPrintPreviewMode ||
|
||||
content.document.mozSyntheticDocument)
|
||||
if (browser.contentDocument.mozSyntheticDocument) {
|
||||
this._executeSoon(aCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
var zoomLevel = ZoomManager.zoom;
|
||||
getContentPrefs(gBrowser.contentDocument.defaultView).setPref(gBrowser.currentURI, this.name, zoomLevel);
|
||||
if (aValue !== undefined) {
|
||||
ZoomManager.setZoomForBrowser(browser, this._ensureValid(aValue));
|
||||
this._executeSoon(aCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
this._getGlobalValue(browser.contentWindow, function (value) {
|
||||
if (gBrowser.selectedBrowser == browser)
|
||||
ZoomManager.setZoomForBrowser(browser, value === undefined ? 1 : value);
|
||||
this._executeSoon(aCallback);
|
||||
});
|
||||
},
|
||||
|
||||
_removePref: function FullZoom__removePref() {
|
||||
if (!(content.document.mozSyntheticDocument))
|
||||
getContentPrefs(gBrowser.contentDocument.defaultView).removePref(gBrowser.currentURI, this.name);
|
||||
/**
|
||||
* Saves the zoom level of the page in the current browser to the content
|
||||
* prefs store.
|
||||
*
|
||||
* @param suppressZoomChange Because this method sets a content preference,
|
||||
* our onContentPrefSet method is called as a
|
||||
* result, which updates the current zoom level.
|
||||
* If suppressZoomChange is true, then the next
|
||||
* call to onContentPrefSet will not update the
|
||||
* zoom level.
|
||||
* @param callback Optional. If given, it's asynchronously called
|
||||
* when done.
|
||||
*/
|
||||
_applySettingToPref: function FullZoom__applySettingToPref(suppressZoomChange, callback) {
|
||||
if (!this.siteSpecific ||
|
||||
gInPrintPreviewMode ||
|
||||
content.document.mozSyntheticDocument) {
|
||||
this._executeSoon(callback);
|
||||
return;
|
||||
}
|
||||
|
||||
this._cps2.set(gBrowser.currentURI.spec, this.name, ZoomManager.zoom,
|
||||
this._loadContextFromWindow(gBrowser.contentWindow), {
|
||||
handleCompletion: function () {
|
||||
if (suppressZoomChange)
|
||||
// The content preference service calls observers after callbacks, and
|
||||
// it calls both in the same turn of the event loop. onContentPrefSet
|
||||
// will therefore be called next, in the same turn of the event loop,
|
||||
// and it will check this flag.
|
||||
this._ignoreNextOnContentPrefSet = true;
|
||||
if (callback)
|
||||
callback();
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes from the content prefs store the zoom level of the current browser.
|
||||
*
|
||||
* @param callback Optional. If given, it's asynchronously called when done.
|
||||
*/
|
||||
_removePref: function FullZoom__removePref(callback) {
|
||||
if (content.document.mozSyntheticDocument) {
|
||||
this._executeSoon(callback);
|
||||
return;
|
||||
}
|
||||
let ctxt = this._loadContextFromWindow(gBrowser.contentWindow);
|
||||
this._cps2.removeByDomainAndName(gBrowser.currentURI.spec, this.name, ctxt, {
|
||||
handleCompletion: function () {
|
||||
if (callback)
|
||||
callback();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@ -322,6 +416,8 @@ var FullZoom = {
|
||||
// Utilities
|
||||
|
||||
_ensureValid: function FullZoom__ensureValid(aValue) {
|
||||
// Note that undefined is a valid value for aValue that indicates a known-
|
||||
// not-to-exist value.
|
||||
if (isNaN(aValue))
|
||||
return 1;
|
||||
|
||||
@ -332,5 +428,68 @@ var FullZoom = {
|
||||
return ZoomManager.MAX;
|
||||
|
||||
return aValue;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the global browser.content.full-zoom content preference.
|
||||
*
|
||||
* WARNING: callback may be called synchronously or asynchronously. The
|
||||
* reason is that it's usually desirable to avoid turns of the event loop
|
||||
* where possible, since they can lead to visible, jarring jumps in zoom
|
||||
* level. It's not always possible to avoid them, though. As a convenience,
|
||||
* then, this method takes a callback and returns nothing.
|
||||
*
|
||||
* @param window The content window pertaining to the zoom.
|
||||
* @param callback Synchronously or asynchronously called when done. It's
|
||||
* bound to this object (FullZoom) and passed the preference
|
||||
* value.
|
||||
*/
|
||||
_getGlobalValue: function FullZoom__getGlobalValue(window, callback) {
|
||||
// * !("_globalValue" in this) => global value not yet cached.
|
||||
// * this._globalValue === undefined => global value known not to exist.
|
||||
// * Otherwise, this._globalValue is a number, the global value.
|
||||
if ("_globalValue" in this) {
|
||||
callback.call(this, this._globalValue);
|
||||
return;
|
||||
}
|
||||
let value = undefined;
|
||||
this._cps2.getGlobal(this.name, this._loadContextFromWindow(window), {
|
||||
handleResult: function (pref) value = pref.value,
|
||||
handleCompletion: function () {
|
||||
this._globalValue = this._ensureValid(value);
|
||||
callback.call(this, this._globalValue);
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the load context from the given window.
|
||||
*
|
||||
* @param window The window whose load context will be returned.
|
||||
* @return The nsILoadContext of the given window.
|
||||
*/
|
||||
_loadContextFromWindow: function FullZoom__loadContextFromWindow(window) {
|
||||
return window.
|
||||
QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIWebNavigation).
|
||||
QueryInterface(Ci.nsILoadContext);
|
||||
},
|
||||
|
||||
/**
|
||||
* Asynchronously broadcasts a "browser-fullZoom:locationChange" notification
|
||||
* so that tests can select tabs, load pages, etc. and be notified when the
|
||||
* zoom levels on those pages change. The notification is always asynchronous
|
||||
* so that observers are guaranteed a consistent behavior.
|
||||
*/
|
||||
_notifyOnLocationChange: function FullZoom__notifyOnLocationChange() {
|
||||
this._executeSoon(function () {
|
||||
Services.obs.notifyObservers(null, "browser-fullZoom:locationChange", "");
|
||||
});
|
||||
},
|
||||
|
||||
_executeSoon: function FullZoom__executeSoon(callback) {
|
||||
if (!callback)
|
||||
return;
|
||||
Services.tm.mainThread.dispatch(callback, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
},
|
||||
};
|
||||
|
@ -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":
|
||||
|
@ -360,8 +360,8 @@ Sanitizer.prototype = {
|
||||
|
||||
// Clear site-specific settings like page-zoom level
|
||||
var cps = Components.classes["@mozilla.org/content-pref/service;1"]
|
||||
.getService(Components.interfaces.nsIContentPrefService);
|
||||
cps.removeGroupedPrefs(null);
|
||||
.getService(Components.interfaces.nsIContentPrefService2);
|
||||
cps.removeAllDomains(null);
|
||||
|
||||
// Clear "Never remember passwords for this site", which is not handled by
|
||||
// the permission manager
|
||||
|
@ -8,136 +8,82 @@ const FORWARD = 1;
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
gTab1 = gBrowser.addTab(gTestPage);
|
||||
gTab2 = gBrowser.addTab();
|
||||
gTab3 = gBrowser.addTab();
|
||||
gBrowser.selectedTab = gTab1;
|
||||
Task.spawn(function () {
|
||||
gTab1 = gBrowser.addTab(gTestPage);
|
||||
gTab2 = gBrowser.addTab();
|
||||
gTab3 = gBrowser.addTab();
|
||||
|
||||
load(gTab1, gTestPage, function () {
|
||||
load(gTab2, gTestPage, secondPageLoaded);
|
||||
});
|
||||
yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab1);
|
||||
yield FullZoomHelper.load(gTab1, gTestPage);
|
||||
yield FullZoomHelper.load(gTab2, gTestPage);
|
||||
}).then(secondPageLoaded, FullZoomHelper.failAndContinue(finish));
|
||||
}
|
||||
|
||||
function secondPageLoaded() {
|
||||
zoomTest(gTab1, 1, "Initial zoom of tab 1 should be 1");
|
||||
zoomTest(gTab2, 1, "Initial zoom of tab 2 should be 1");
|
||||
zoomTest(gTab3, 1, "Initial zoom of tab 3 should be 1");
|
||||
Task.spawn(function () {
|
||||
FullZoomHelper.zoomTest(gTab1, 1, "Initial zoom of tab 1 should be 1");
|
||||
FullZoomHelper.zoomTest(gTab2, 1, "Initial zoom of tab 2 should be 1");
|
||||
FullZoomHelper.zoomTest(gTab3, 1, "Initial zoom of tab 3 should be 1");
|
||||
|
||||
// Now have three tabs, two with the test page, one blank. Tab 1 is selected
|
||||
// Zoom tab 1
|
||||
FullZoom.enlarge();
|
||||
gLevel = ZoomManager.getZoomForBrowser(gBrowser.getBrowserForTab(gTab1));
|
||||
// Now have three tabs, two with the test page, one blank. Tab 1 is selected
|
||||
// Zoom tab 1
|
||||
yield FullZoomHelper.enlarge();
|
||||
gLevel = ZoomManager.getZoomForBrowser(gBrowser.getBrowserForTab(gTab1));
|
||||
|
||||
ok(gLevel > 1, "New zoom for tab 1 should be greater than 1");
|
||||
zoomTest(gTab2, 1, "Zooming tab 1 should not affect tab 2");
|
||||
zoomTest(gTab3, 1, "Zooming tab 1 should not affect tab 3");
|
||||
ok(gLevel > 1, "New zoom for tab 1 should be greater than 1");
|
||||
FullZoomHelper.zoomTest(gTab2, 1, "Zooming tab 1 should not affect tab 2");
|
||||
FullZoomHelper.zoomTest(gTab3, 1, "Zooming tab 1 should not affect tab 3");
|
||||
|
||||
load(gTab3, gTestPage, thirdPageLoaded);
|
||||
yield FullZoomHelper.load(gTab3, gTestPage);
|
||||
}).then(thirdPageLoaded, FullZoomHelper.failAndContinue(finish));
|
||||
}
|
||||
|
||||
function thirdPageLoaded() {
|
||||
zoomTest(gTab1, gLevel, "Tab 1 should still be zoomed");
|
||||
zoomTest(gTab2, 1, "Tab 2 should still not be affected");
|
||||
zoomTest(gTab3, gLevel, "Tab 3 should have zoomed as it was loading in the background");
|
||||
Task.spawn(function () {
|
||||
FullZoomHelper.zoomTest(gTab1, gLevel, "Tab 1 should still be zoomed");
|
||||
FullZoomHelper.zoomTest(gTab2, 1, "Tab 2 should still not be affected");
|
||||
FullZoomHelper.zoomTest(gTab3, gLevel, "Tab 3 should have zoomed as it was loading in the background");
|
||||
|
||||
// Switching to tab 2 should update its zoom setting.
|
||||
afterZoom(function() {
|
||||
zoomTest(gTab1, gLevel, "Tab 1 should still be zoomed");
|
||||
zoomTest(gTab2, gLevel, "Tab 2 should be zoomed now");
|
||||
zoomTest(gTab3, gLevel, "Tab 3 should still be zoomed");
|
||||
// Switching to tab 2 should update its zoom setting.
|
||||
yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab2);
|
||||
FullZoomHelper.zoomTest(gTab1, gLevel, "Tab 1 should still be zoomed");
|
||||
FullZoomHelper.zoomTest(gTab2, gLevel, "Tab 2 should be zoomed now");
|
||||
FullZoomHelper.zoomTest(gTab3, gLevel, "Tab 3 should still be zoomed");
|
||||
|
||||
load(gTab1, gTestImage, imageLoaded);
|
||||
});
|
||||
|
||||
gBrowser.selectedTab = gTab2;
|
||||
yield FullZoomHelper.load(gTab1, gTestImage);
|
||||
}).then(imageLoaded, FullZoomHelper.failAndContinue(finish));
|
||||
}
|
||||
|
||||
function imageLoaded() {
|
||||
zoomTest(gTab1, 1, "Zoom should be 1 when image was loaded in the background");
|
||||
gBrowser.selectedTab = gTab1;
|
||||
zoomTest(gTab1, 1, "Zoom should still be 1 when tab with image is selected");
|
||||
|
||||
executeSoon(imageZoomSwitch);
|
||||
Task.spawn(function () {
|
||||
FullZoomHelper.zoomTest(gTab1, 1, "Zoom should be 1 when image was loaded in the background");
|
||||
yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab1);
|
||||
FullZoomHelper.zoomTest(gTab1, 1, "Zoom should still be 1 when tab with image is selected");
|
||||
}).then(imageZoomSwitch, FullZoomHelper.failAndContinue(finish));
|
||||
}
|
||||
|
||||
function imageZoomSwitch() {
|
||||
navigate(BACK, function () {
|
||||
navigate(FORWARD, function () {
|
||||
zoomTest(gTab1, 1, "Tab 1 should not be zoomed when an image loads");
|
||||
Task.spawn(function () {
|
||||
yield FullZoomHelper.navigate(BACK);
|
||||
yield FullZoomHelper.navigate(FORWARD);
|
||||
FullZoomHelper.zoomTest(gTab1, 1, "Tab 1 should not be zoomed when an image loads");
|
||||
|
||||
afterZoom(function() {
|
||||
zoomTest(gTab1, 1, "Tab 1 should still not be zoomed when deselected");
|
||||
finishTest();
|
||||
});
|
||||
gBrowser.selectedTab = gTab2;
|
||||
});
|
||||
});
|
||||
yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab2);
|
||||
FullZoomHelper.zoomTest(gTab1, 1, "Tab 1 should still not be zoomed when deselected");
|
||||
}).then(finishTest, FullZoomHelper.failAndContinue(finish));
|
||||
}
|
||||
|
||||
var finishTestStarted = false;
|
||||
function finishTest() {
|
||||
ok(!finishTestStarted, "finishTest called more than once");
|
||||
finishTestStarted = true;
|
||||
gBrowser.selectedTab = gTab1;
|
||||
FullZoom.reset();
|
||||
gBrowser.removeTab(gTab1);
|
||||
FullZoom.reset();
|
||||
gBrowser.removeTab(gTab2);
|
||||
FullZoom.reset();
|
||||
gBrowser.removeTab(gTab3);
|
||||
finish();
|
||||
}
|
||||
|
||||
function zoomTest(tab, val, msg) {
|
||||
is(ZoomManager.getZoomForBrowser(tab.linkedBrowser), val, msg);
|
||||
}
|
||||
|
||||
function load(tab, url, cb) {
|
||||
let didLoad = false;
|
||||
let didZoom = false;
|
||||
tab.linkedBrowser.addEventListener("load", function (event) {
|
||||
event.currentTarget.removeEventListener("load", arguments.callee, true);
|
||||
didLoad = true;
|
||||
if (didZoom)
|
||||
executeSoon(cb);
|
||||
}, true);
|
||||
|
||||
afterZoom(function() {
|
||||
didZoom = true;
|
||||
if (didLoad)
|
||||
executeSoon(cb);
|
||||
});
|
||||
|
||||
tab.linkedBrowser.loadURI(url);
|
||||
}
|
||||
|
||||
function navigate(direction, cb) {
|
||||
let didPs = false;
|
||||
let didZoom = false;
|
||||
gBrowser.addEventListener("pageshow", function (event) {
|
||||
gBrowser.removeEventListener("pageshow", arguments.callee, true);
|
||||
didPs = true;
|
||||
if (didZoom)
|
||||
executeSoon(cb);
|
||||
}, true);
|
||||
|
||||
afterZoom(function() {
|
||||
didZoom = true;
|
||||
if (didPs)
|
||||
executeSoon(cb);
|
||||
});
|
||||
|
||||
if (direction == BACK)
|
||||
gBrowser.goBack();
|
||||
else if (direction == FORWARD)
|
||||
gBrowser.goForward();
|
||||
}
|
||||
|
||||
function afterZoom(cb) {
|
||||
let oldSZFB = ZoomManager.setZoomForBrowser;
|
||||
ZoomManager.setZoomForBrowser = function(browser, value) {
|
||||
oldSZFB.call(ZoomManager, browser, value);
|
||||
ZoomManager.setZoomForBrowser = oldSZFB;
|
||||
executeSoon(cb);
|
||||
};
|
||||
Task.spawn(function () {
|
||||
ok(!finishTestStarted, "finishTest called more than once");
|
||||
finishTestStarted = true;
|
||||
yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab1);
|
||||
yield FullZoomHelper.reset();
|
||||
gBrowser.removeTab(gTab1);
|
||||
yield FullZoomHelper.reset();
|
||||
gBrowser.removeTab(gTab2);
|
||||
yield FullZoomHelper.reset();
|
||||
gBrowser.removeTab(gTab3);
|
||||
}).then(finish, FullZoomHelper.failAndContinue(finish));
|
||||
}
|
||||
|
@ -1,62 +1,42 @@
|
||||
var tabElm, zoomLevel;
|
||||
function start_test_prefNotSet() {
|
||||
is(ZoomManager.zoom, 1, "initial zoom level should be 1");
|
||||
FullZoom.enlarge();
|
||||
Task.spawn(function () {
|
||||
is(ZoomManager.zoom, 1, "initial zoom level should be 1");
|
||||
yield FullZoomHelper.enlarge();
|
||||
|
||||
//capture the zoom level to test later
|
||||
zoomLevel = ZoomManager.zoom;
|
||||
isnot(zoomLevel, 1, "zoom level should have changed");
|
||||
//capture the zoom level to test later
|
||||
zoomLevel = ZoomManager.zoom;
|
||||
isnot(zoomLevel, 1, "zoom level should have changed");
|
||||
|
||||
afterZoomAndLoad(continue_test_prefNotSet);
|
||||
content.location =
|
||||
"http://mochi.test:8888/browser/browser/base/content/test/moz.png";
|
||||
yield FullZoomHelper.load(gBrowser.selectedTab, "http://mochi.test:8888/browser/browser/base/content/test/moz.png");
|
||||
}).then(continue_test_prefNotSet, FullZoomHelper.failAndContinue(finish));
|
||||
}
|
||||
|
||||
function continue_test_prefNotSet () {
|
||||
is(ZoomManager.zoom, 1, "zoom level pref should not apply to an image");
|
||||
FullZoom.reset();
|
||||
Task.spawn(function () {
|
||||
is(ZoomManager.zoom, 1, "zoom level pref should not apply to an image");
|
||||
yield FullZoomHelper.reset();
|
||||
|
||||
afterZoomAndLoad(end_test_prefNotSet);
|
||||
content.location =
|
||||
"http://mochi.test:8888/browser/browser/base/content/test/zoom_test.html";
|
||||
yield FullZoomHelper.load(gBrowser.selectedTab, "http://mochi.test:8888/browser/browser/base/content/test/zoom_test.html");
|
||||
}).then(end_test_prefNotSet, FullZoomHelper.failAndContinue(finish));
|
||||
}
|
||||
|
||||
function end_test_prefNotSet() {
|
||||
is(ZoomManager.zoom, zoomLevel, "the zoom level should have persisted");
|
||||
Task.spawn(function () {
|
||||
is(ZoomManager.zoom, zoomLevel, "the zoom level should have persisted");
|
||||
|
||||
// Reset the zoom so that other tests have a fresh zoom level
|
||||
FullZoom.reset();
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
// Reset the zoom so that other tests have a fresh zoom level
|
||||
yield FullZoomHelper.reset();
|
||||
gBrowser.removeCurrentTab();
|
||||
}).then(finish, FullZoomHelper.failAndContinue(finish));
|
||||
}
|
||||
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
tabElm = gBrowser.addTab();
|
||||
gBrowser.selectedTab = tabElm;
|
||||
|
||||
afterZoomAndLoad(start_test_prefNotSet);
|
||||
content.location =
|
||||
"http://mochi.test:8888/browser/browser/base/content/test/zoom_test.html";
|
||||
}
|
||||
|
||||
function afterZoomAndLoad(cb) {
|
||||
let didLoad = false;
|
||||
let didZoom = false;
|
||||
tabElm.linkedBrowser.addEventListener("load", function() {
|
||||
tabElm.linkedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
didLoad = true;
|
||||
if (didZoom)
|
||||
executeSoon(cb);
|
||||
}, true);
|
||||
let oldSZFB = ZoomManager.setZoomForBrowser;
|
||||
ZoomManager.setZoomForBrowser = function(browser, value) {
|
||||
oldSZFB.call(ZoomManager, browser, value);
|
||||
ZoomManager.setZoomForBrowser = oldSZFB;
|
||||
didZoom = true;
|
||||
if (didLoad)
|
||||
executeSoon(cb);
|
||||
};
|
||||
Task.spawn(function () {
|
||||
tabElm = gBrowser.addTab();
|
||||
yield FullZoomHelper.selectTabAndWaitForLocationChange(tabElm);
|
||||
yield FullZoomHelper.load(tabElm, "http://mochi.test:8888/browser/browser/base/content/test/zoom_test.html");
|
||||
}).then(start_test_prefNotSet, FullZoomHelper.failAndContinue(finish));
|
||||
}
|
||||
|
@ -1,49 +1,32 @@
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let testPage = "http://example.org/browser/browser/base/content/test/dummy_page.html";
|
||||
let tab1 = gBrowser.selectedTab = gBrowser.addTab();
|
||||
tab1.linkedBrowser.addEventListener("load", (function(event) {
|
||||
event.currentTarget.removeEventListener("load", arguments.callee, true);
|
||||
Task.spawn(function () {
|
||||
let testPage = "http://example.org/browser/browser/base/content/test/dummy_page.html";
|
||||
let tab1 = gBrowser.addTab();
|
||||
yield FullZoomHelper.selectTabAndWaitForLocationChange(tab1);
|
||||
yield FullZoomHelper.load(tab1, testPage);
|
||||
|
||||
let tab2 = gBrowser.addTab();
|
||||
tab2.linkedBrowser.addEventListener("load", (function(event) {
|
||||
event.currentTarget.removeEventListener("load", arguments.callee, true);
|
||||
yield FullZoomHelper.load(tab2, testPage);
|
||||
|
||||
FullZoom.enlarge();
|
||||
let tab1Zoom = ZoomManager.getZoomForBrowser(tab1.linkedBrowser);
|
||||
yield FullZoomHelper.enlarge();
|
||||
let tab1Zoom = ZoomManager.getZoomForBrowser(tab1.linkedBrowser);
|
||||
|
||||
afterZoom(function() {
|
||||
let tab2Zoom = ZoomManager.getZoomForBrowser(tab2.linkedBrowser);
|
||||
is(tab2Zoom, tab1Zoom, "Zoom should affect background tabs");
|
||||
yield FullZoomHelper.selectTabAndWaitForLocationChange(tab2);
|
||||
let tab2Zoom = ZoomManager.getZoomForBrowser(tab2.linkedBrowser);
|
||||
is(tab2Zoom, tab1Zoom, "Zoom should affect background tabs");
|
||||
|
||||
gPrefService.setBoolPref("browser.zoom.updateBackgroundTabs", false);
|
||||
FullZoom.reset();
|
||||
gBrowser.selectedTab = tab1;
|
||||
tab1Zoom = ZoomManager.getZoomForBrowser(tab1.linkedBrowser);
|
||||
tab2Zoom = ZoomManager.getZoomForBrowser(tab2.linkedBrowser);
|
||||
isnot(tab1Zoom, tab2Zoom, "Zoom should not affect background tabs");
|
||||
gPrefService.setBoolPref("browser.zoom.updateBackgroundTabs", false);
|
||||
yield FullZoomHelper.reset();
|
||||
gBrowser.selectedTab = tab1;
|
||||
tab1Zoom = ZoomManager.getZoomForBrowser(tab1.linkedBrowser);
|
||||
tab2Zoom = ZoomManager.getZoomForBrowser(tab2.linkedBrowser);
|
||||
isnot(tab1Zoom, tab2Zoom, "Zoom should not affect background tabs");
|
||||
|
||||
if (gPrefService.prefHasUserValue("browser.zoom.updateBackgroundTabs"))
|
||||
gPrefService.clearUserPref("browser.zoom.updateBackgroundTabs");
|
||||
gBrowser.removeTab(tab1);
|
||||
gBrowser.removeTab(tab2);
|
||||
finish();
|
||||
});
|
||||
gBrowser.selectedTab = tab2;
|
||||
}), true);
|
||||
tab2.linkedBrowser.loadURI(testPage);
|
||||
}), true);
|
||||
content.location = testPage;
|
||||
}
|
||||
|
||||
function afterZoom(cb) {
|
||||
let oldAPTS = FullZoom._applyPrefToSetting;
|
||||
FullZoom._applyPrefToSetting = function(value, browser) {
|
||||
if (!value)
|
||||
value = undefined;
|
||||
oldAPTS.call(FullZoom, value, browser);
|
||||
FullZoom._applyPrefToSetting = oldAPTS;
|
||||
executeSoon(cb);
|
||||
};
|
||||
if (gPrefService.prefHasUserValue("browser.zoom.updateBackgroundTabs"))
|
||||
gPrefService.clearUserPref("browser.zoom.updateBackgroundTabs");
|
||||
gBrowser.removeTab(tab1);
|
||||
gBrowser.removeTab(tab2);
|
||||
}).then(finish, FullZoomHelper.failAndContinue(finish));
|
||||
}
|
||||
|
@ -13,19 +13,22 @@ function test() {
|
||||
const TEST_PAGE_URL = 'data:text/html,<body><iframe src=""></iframe></body>';
|
||||
const TEST_IFRAME_URL = "http://test2.example.org/";
|
||||
|
||||
// Prepare the test tab
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
let testBrowser = gBrowser.selectedBrowser;
|
||||
Task.spawn(function () {
|
||||
// Prepare the test tab
|
||||
let tab = gBrowser.addTab();
|
||||
yield FullZoomHelper.selectTabAndWaitForLocationChange(tab);
|
||||
|
||||
testBrowser.addEventListener("load", function () {
|
||||
testBrowser.removeEventListener("load", arguments.callee, true);
|
||||
let testBrowser = tab.linkedBrowser;
|
||||
|
||||
yield FullZoomHelper.load(tab, TEST_PAGE_URL);
|
||||
|
||||
// Change the zoom level and then save it so we can compare it to the level
|
||||
// after loading the sub-document.
|
||||
FullZoom.enlarge();
|
||||
yield FullZoomHelper.enlarge();
|
||||
var zoomLevel = ZoomManager.zoom;
|
||||
|
||||
// Start the sub-document load.
|
||||
let deferred = Promise.defer();
|
||||
executeSoon(function () {
|
||||
testBrowser.addEventListener("load", function (e) {
|
||||
testBrowser.removeEventListener("load", arguments.callee, true);
|
||||
@ -34,11 +37,10 @@ function test() {
|
||||
is(ZoomManager.zoom, zoomLevel, "zoom is retained after sub-document load");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
deferred.resolve();
|
||||
}, true);
|
||||
content.document.querySelector("iframe").src = TEST_IFRAME_URL;
|
||||
});
|
||||
}, true);
|
||||
|
||||
content.location = TEST_PAGE_URL;
|
||||
yield deferred.promise;
|
||||
}).then(finish, FullZoomHelper.failAndContinue(finish));
|
||||
}
|
||||
|
@ -4,59 +4,36 @@
|
||||
const TEST_PAGE = "/browser/browser/base/content/test/dummy_page.html";
|
||||
var gTestTab, gBgTab, gTestZoom;
|
||||
|
||||
function afterZoomAndLoad(aCallback, aTab) {
|
||||
let didLoad = false;
|
||||
let didZoom = false;
|
||||
aTab.linkedBrowser.addEventListener("load", function() {
|
||||
aTab.linkedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
didLoad = true;
|
||||
if (didZoom)
|
||||
executeSoon(aCallback);
|
||||
}, true);
|
||||
let oldAPTS = FullZoom._applyPrefToSetting;
|
||||
FullZoom._applyPrefToSetting = function(value, browser) {
|
||||
if (!value)
|
||||
value = undefined;
|
||||
oldAPTS.call(FullZoom, value, browser);
|
||||
// Don't reset _applyPrefToSetting until we've seen the about:blank load(s)
|
||||
if (browser && browser.currentURI.spec.startsWith("http:")) {
|
||||
FullZoom._applyPrefToSetting = oldAPTS;
|
||||
didZoom = true;
|
||||
}
|
||||
if (didLoad && didZoom)
|
||||
executeSoon(aCallback);
|
||||
};
|
||||
}
|
||||
|
||||
function testBackgroundLoad() {
|
||||
is(ZoomManager.zoom, gTestZoom, "opening a background tab should not change foreground zoom");
|
||||
Task.spawn(function () {
|
||||
is(ZoomManager.zoom, gTestZoom, "opening a background tab should not change foreground zoom");
|
||||
|
||||
gBrowser.removeTab(gBgTab);
|
||||
gBrowser.removeTab(gBgTab);
|
||||
|
||||
FullZoom.reset();
|
||||
gBrowser.removeTab(gTestTab);
|
||||
|
||||
finish();
|
||||
yield FullZoomHelper.reset();
|
||||
gBrowser.removeTab(gTestTab);
|
||||
}).then(finish, FullZoomHelper.failAndContinue(finish));
|
||||
}
|
||||
|
||||
function testInitialZoom() {
|
||||
is(ZoomManager.zoom, 1, "initial zoom level should be 1");
|
||||
FullZoom.enlarge();
|
||||
Task.spawn(function () {
|
||||
is(ZoomManager.zoom, 1, "initial zoom level should be 1");
|
||||
yield FullZoomHelper.enlarge();
|
||||
|
||||
gTestZoom = ZoomManager.zoom;
|
||||
isnot(gTestZoom, 1, "zoom level should have changed");
|
||||
gTestZoom = ZoomManager.zoom;
|
||||
isnot(gTestZoom, 1, "zoom level should have changed");
|
||||
|
||||
afterZoomAndLoad(testBackgroundLoad,
|
||||
gBgTab = gBrowser.loadOneTab("http://mochi.test:8888" + TEST_PAGE,
|
||||
{inBackground: true}));
|
||||
gBgTab = gBrowser.addTab();
|
||||
yield FullZoomHelper.load(gBgTab, "http://mochi.test:8888" + TEST_PAGE);
|
||||
}).then(testBackgroundLoad, FullZoomHelper.failAndContinue(finish));
|
||||
}
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
gTestTab = gBrowser.addTab();
|
||||
gBrowser.selectedTab = gTestTab;
|
||||
|
||||
afterZoomAndLoad(testInitialZoom, gTestTab);
|
||||
content.location = "http://example.org" + TEST_PAGE;
|
||||
Task.spawn(function () {
|
||||
gTestTab = gBrowser.addTab();
|
||||
yield FullZoomHelper.selectTabAndWaitForLocationChange(gTestTab);
|
||||
yield FullZoomHelper.load(gTestTab, "http://example.org" + TEST_PAGE);
|
||||
}).then(testInitialZoom, FullZoomHelper.failAndContinue(finish));
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
var tab;
|
||||
|
||||
function test() {
|
||||
|
||||
// ----------
|
||||
@ -5,53 +7,34 @@ function test() {
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
let oldOLC = FullZoom.onLocationChange;
|
||||
FullZoom.onLocationChange = function(aURI, aIsTabSwitch, aBrowser) {
|
||||
// Ignore calls that are not about tab switching on this test
|
||||
if (aIsTabSwitch)
|
||||
oldOLC.call(FullZoom, aURI, aIsTabSwitch, aBrowser);
|
||||
};
|
||||
|
||||
gPrefService.setBoolPref("browser.zoom.updateBackgroundTabs", true);
|
||||
gPrefService.setBoolPref("browser.zoom.siteSpecific", true);
|
||||
|
||||
let oldAPTS = FullZoom._applyPrefToSetting;
|
||||
let uri = "http://example.org/browser/browser/base/content/test/dummy_page.html";
|
||||
|
||||
let tab = gBrowser.addTab();
|
||||
tab.linkedBrowser.addEventListener("load", function(event) {
|
||||
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
Task.spawn(function () {
|
||||
tab = gBrowser.addTab();
|
||||
yield FullZoomHelper.load(tab, uri);
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Test - Trigger a tab switch that should update the zoom level
|
||||
FullZoom._applyPrefToSetting = function() {
|
||||
ok(true, "applyPrefToSetting was called");
|
||||
endTest();
|
||||
}
|
||||
gBrowser.selectedTab = tab;
|
||||
|
||||
}, true);
|
||||
tab.linkedBrowser.loadURI(uri);
|
||||
|
||||
// -------------
|
||||
// Test clean-up
|
||||
function endTest() {
|
||||
FullZoom._applyPrefToSetting = oldAPTS;
|
||||
FullZoom.onLocationChange = oldOLC;
|
||||
gBrowser.removeTab(tab);
|
||||
|
||||
oldAPTS = null;
|
||||
oldOLC = null;
|
||||
tab = null;
|
||||
|
||||
if (gPrefService.prefHasUserValue("browser.zoom.updateBackgroundTabs"))
|
||||
gPrefService.clearUserPref("browser.zoom.updateBackgroundTabs");
|
||||
|
||||
if (gPrefService.prefHasUserValue("browser.zoom.siteSpecific"))
|
||||
gPrefService.clearUserPref("browser.zoom.siteSpecific");
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
yield FullZoomHelper.selectTabAndWaitForLocationChange(tab);
|
||||
ok(true, "applyPrefToSetting was called");
|
||||
}).then(endTest, FullZoomHelper.failAndContinue(endTest));
|
||||
}
|
||||
|
||||
// -------------
|
||||
// Test clean-up
|
||||
function endTest() {
|
||||
gBrowser.removeTab(tab);
|
||||
|
||||
tab = null;
|
||||
|
||||
if (gPrefService.prefHasUserValue("browser.zoom.updateBackgroundTabs"))
|
||||
gPrefService.clearUserPref("browser.zoom.updateBackgroundTabs");
|
||||
|
||||
if (gPrefService.prefHasUserValue("browser.zoom.siteSpecific"))
|
||||
gPrefService.clearUserPref("browser.zoom.siteSpecific");
|
||||
|
||||
finish();
|
||||
}
|
||||
|
@ -13,25 +13,22 @@ function test() {
|
||||
gBrowser.removeTab(tab2);
|
||||
});
|
||||
|
||||
tab1 = gBrowser.addTab(TEST_IMAGE);
|
||||
tab2 = gBrowser.addTab();
|
||||
gBrowser.selectedTab = tab1;
|
||||
Task.spawn(function () {
|
||||
tab1 = gBrowser.addTab();
|
||||
tab2 = gBrowser.addTab();
|
||||
yield FullZoomHelper.selectTabAndWaitForLocationChange(tab1);
|
||||
yield FullZoomHelper.load(tab1, TEST_IMAGE);
|
||||
|
||||
tab1.linkedBrowser.addEventListener("load", function onload() {
|
||||
tab1.linkedBrowser.removeEventListener("load", onload, true);
|
||||
is(ZoomManager.zoom, 1, "initial zoom level for first should be 1");
|
||||
|
||||
FullZoom.enlarge();
|
||||
yield FullZoomHelper.enlarge();
|
||||
let zoom = ZoomManager.zoom;
|
||||
isnot(zoom, 1, "zoom level should have changed");
|
||||
|
||||
gBrowser.selectedTab = tab2;
|
||||
yield FullZoomHelper.selectTabAndWaitForLocationChange(tab2);
|
||||
is(ZoomManager.zoom, 1, "initial zoom level for second tab should be 1");
|
||||
|
||||
gBrowser.selectedTab = tab1;
|
||||
yield FullZoomHelper.selectTabAndWaitForLocationChange(tab1);
|
||||
is(ZoomManager.zoom, zoom, "zoom level for first tab should not have changed");
|
||||
|
||||
finish();
|
||||
}, true);
|
||||
}).then(finish, FullZoomHelper.failAndContinue(finish));
|
||||
}
|
||||
|
||||
|
@ -11,131 +11,71 @@ var gTab1, gTab2, gLevel1, gLevel2;
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
gTab1 = gBrowser.addTab();
|
||||
gTab2 = gBrowser.addTab();
|
||||
gBrowser.selectedTab = gTab1;
|
||||
Task.spawn(function () {
|
||||
gTab1 = gBrowser.addTab();
|
||||
gTab2 = gBrowser.addTab();
|
||||
|
||||
load(gTab1, TEST_PAGE, function() {
|
||||
load(gTab2, TEST_VIDEO, zoomTab1);
|
||||
});
|
||||
yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab1);
|
||||
yield FullZoomHelper.load(gTab1, TEST_PAGE);
|
||||
yield FullZoomHelper.load(gTab2, TEST_VIDEO);
|
||||
}).then(zoomTab1, FullZoomHelper.failAndContinue(finish));
|
||||
}
|
||||
|
||||
function zoomTab1() {
|
||||
is(gBrowser.selectedTab, gTab1, "Tab 1 is selected");
|
||||
zoomTest(gTab1, 1, "Initial zoom of tab 1 should be 1");
|
||||
zoomTest(gTab2, 1, "Initial zoom of tab 2 should be 1");
|
||||
Task.spawn(function () {
|
||||
is(gBrowser.selectedTab, gTab1, "Tab 1 is selected");
|
||||
FullZoomHelper.zoomTest(gTab1, 1, "Initial zoom of tab 1 should be 1");
|
||||
FullZoomHelper.zoomTest(gTab2, 1, "Initial zoom of tab 2 should be 1");
|
||||
|
||||
FullZoom.enlarge();
|
||||
gLevel1 = ZoomManager.getZoomForBrowser(gBrowser.getBrowserForTab(gTab1));
|
||||
yield FullZoomHelper.enlarge();
|
||||
gLevel1 = ZoomManager.getZoomForBrowser(gBrowser.getBrowserForTab(gTab1));
|
||||
|
||||
ok(gLevel1 > 1, "New zoom for tab 1 should be greater than 1");
|
||||
zoomTest(gTab2, 1, "Zooming tab 1 should not affect tab 2");
|
||||
ok(gLevel1 > 1, "New zoom for tab 1 should be greater than 1");
|
||||
FullZoomHelper.zoomTest(gTab2, 1, "Zooming tab 1 should not affect tab 2");
|
||||
|
||||
gBrowser.selectedTab = gTab2;
|
||||
zoomTest(gTab2, 1, "Tab 2 is still unzoomed after it is selected");
|
||||
zoomTest(gTab1, gLevel1, "Tab 1 is still zoomed");
|
||||
|
||||
zoomTab2();
|
||||
yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab2);
|
||||
FullZoomHelper.zoomTest(gTab2, 1, "Tab 2 is still unzoomed after it is selected");
|
||||
FullZoomHelper.zoomTest(gTab1, gLevel1, "Tab 1 is still zoomed");
|
||||
}).then(zoomTab2, FullZoomHelper.failAndContinue(finish));
|
||||
}
|
||||
|
||||
function zoomTab2() {
|
||||
is(gBrowser.selectedTab, gTab2, "Tab 2 is selected");
|
||||
Task.spawn(function () {
|
||||
is(gBrowser.selectedTab, gTab2, "Tab 2 is selected");
|
||||
|
||||
FullZoom.reduce();
|
||||
let gLevel2 = ZoomManager.getZoomForBrowser(gBrowser.getBrowserForTab(gTab2));
|
||||
yield FullZoomHelper.reduce();
|
||||
let gLevel2 = ZoomManager.getZoomForBrowser(gBrowser.getBrowserForTab(gTab2));
|
||||
|
||||
ok(gLevel2 < 1, "New zoom for tab 2 should be less than 1");
|
||||
zoomTest(gTab1, gLevel1, "Zooming tab 2 should not affect tab 1");
|
||||
ok(gLevel2 < 1, "New zoom for tab 2 should be less than 1");
|
||||
FullZoomHelper.zoomTest(gTab1, gLevel1, "Zooming tab 2 should not affect tab 1");
|
||||
|
||||
afterZoom(function() {
|
||||
zoomTest(gTab1, gLevel1, "Tab 1 should have the same zoom after it's selected");
|
||||
|
||||
testNavigation();
|
||||
});
|
||||
gBrowser.selectedTab = gTab1;
|
||||
yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab1);
|
||||
FullZoomHelper.zoomTest(gTab1, gLevel1, "Tab 1 should have the same zoom after it's selected");
|
||||
}).then(testNavigation, FullZoomHelper.failAndContinue(finish));
|
||||
}
|
||||
|
||||
function testNavigation() {
|
||||
load(gTab1, TEST_VIDEO, function() {
|
||||
zoomTest(gTab1, 1, "Zoom should be 1 when a video was loaded");
|
||||
navigate(BACK, function() {
|
||||
zoomTest(gTab1, gLevel1, "Zoom should be restored when a page is loaded");
|
||||
navigate(FORWARD, function() {
|
||||
zoomTest(gTab1, 1, "Zoom should be 1 again when navigating back to a video");
|
||||
finishTest();
|
||||
});
|
||||
});
|
||||
});
|
||||
Task.spawn(function () {
|
||||
yield FullZoomHelper.load(gTab1, TEST_VIDEO);
|
||||
FullZoomHelper.zoomTest(gTab1, 1, "Zoom should be 1 when a video was loaded");
|
||||
yield FullZoomHelper.navigate(FullZoomHelper.BACK);
|
||||
FullZoomHelper.zoomTest(gTab1, gLevel1, "Zoom should be restored when a page is loaded");
|
||||
yield FullZoomHelper.navigate(FullZoomHelper.FORWARD);
|
||||
FullZoomHelper.zoomTest(gTab1, 1, "Zoom should be 1 again when navigating back to a video");
|
||||
}).then(finishTest, FullZoomHelper.failAndContinue(finish));
|
||||
}
|
||||
|
||||
var finishTestStarted = false;
|
||||
function finishTest() {
|
||||
ok(!finishTestStarted, "finishTest called more than once");
|
||||
finishTestStarted = true;
|
||||
Task.spawn(function () {
|
||||
ok(!finishTestStarted, "finishTest called more than once");
|
||||
finishTestStarted = true;
|
||||
|
||||
gBrowser.selectedTab = gTab1;
|
||||
FullZoom.reset();
|
||||
gBrowser.removeTab(gTab1);
|
||||
|
||||
gBrowser.selectedTab = gTab2;
|
||||
FullZoom.reset();
|
||||
gBrowser.removeTab(gTab2);
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
function zoomTest(tab, val, msg) {
|
||||
is(ZoomManager.getZoomForBrowser(tab.linkedBrowser), val, msg);
|
||||
}
|
||||
|
||||
function load(tab, url, cb) {
|
||||
let didLoad = false;
|
||||
let didZoom = false;
|
||||
tab.linkedBrowser.addEventListener("load", function onload(event) {
|
||||
event.currentTarget.removeEventListener("load", onload, true);
|
||||
didLoad = true;
|
||||
if (didZoom)
|
||||
executeSoon(cb);
|
||||
}, true);
|
||||
|
||||
afterZoom(function() {
|
||||
didZoom = true;
|
||||
if (didLoad)
|
||||
executeSoon(cb);
|
||||
});
|
||||
|
||||
tab.linkedBrowser.loadURI(url);
|
||||
}
|
||||
|
||||
const BACK = 0;
|
||||
const FORWARD = 1;
|
||||
function navigate(direction, cb) {
|
||||
let didPs = false;
|
||||
let didZoom = false;
|
||||
gBrowser.addEventListener("pageshow", function onpageshow(event) {
|
||||
gBrowser.removeEventListener("pageshow", onpageshow, true);
|
||||
didPs = true;
|
||||
if (didZoom)
|
||||
executeSoon(cb);
|
||||
}, true);
|
||||
|
||||
afterZoom(function() {
|
||||
didZoom = true;
|
||||
if (didPs)
|
||||
executeSoon(cb);
|
||||
});
|
||||
|
||||
if (direction == BACK)
|
||||
gBrowser.goBack();
|
||||
else if (direction == FORWARD)
|
||||
gBrowser.goForward();
|
||||
}
|
||||
|
||||
function afterZoom(cb) {
|
||||
let oldSZFB = ZoomManager.setZoomForBrowser;
|
||||
ZoomManager.setZoomForBrowser = function(browser, value) {
|
||||
oldSZFB.call(ZoomManager, browser, value);
|
||||
ZoomManager.setZoomForBrowser = oldSZFB;
|
||||
executeSoon(cb);
|
||||
};
|
||||
yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab1);
|
||||
yield FullZoomHelper.reset();
|
||||
gBrowser.removeTab(gTab1);
|
||||
yield FullZoomHelper.selectTabAndWaitForLocationChange(gTab2);
|
||||
yield FullZoomHelper.reset();
|
||||
gBrowser.removeTab(gTab2);
|
||||
}).then(finish, FullZoomHelper.failAndContinue(finish));
|
||||
}
|
||||
|
@ -261,3 +261,107 @@ function promiseHistoryClearedState(aURIs, aShouldBeCleared) {
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
let FullZoomHelper = {
|
||||
|
||||
selectTabAndWaitForLocationChange: function selectTabAndWaitForLocationChange(tab) {
|
||||
let deferred = Promise.defer();
|
||||
if (tab && gBrowser.selectedTab == tab) {
|
||||
deferred.resolve();
|
||||
return deferred.promise;
|
||||
}
|
||||
if (tab)
|
||||
gBrowser.selectedTab = tab;
|
||||
Services.obs.addObserver(function obs() {
|
||||
Services.obs.removeObserver(obs, "browser-fullZoom:locationChange");
|
||||
deferred.resolve();
|
||||
}, "browser-fullZoom:locationChange", false);
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
load: function load(tab, url) {
|
||||
let deferred = Promise.defer();
|
||||
let didLoad = false;
|
||||
let didZoom = false;
|
||||
|
||||
tab.linkedBrowser.addEventListener("load", function (event) {
|
||||
event.currentTarget.removeEventListener("load", arguments.callee, true);
|
||||
didLoad = true;
|
||||
if (didZoom)
|
||||
deferred.resolve();
|
||||
}, true);
|
||||
|
||||
// Don't select background tabs. That way tests can use this method on
|
||||
// background tabs without having them automatically be selected. Just wait
|
||||
// for the zoom to change on the current tab if it's `tab`.
|
||||
if (tab == gBrowser.selectedTab) {
|
||||
this.selectTabAndWaitForLocationChange(null).then(function () {
|
||||
didZoom = true;
|
||||
if (didLoad)
|
||||
deferred.resolve();
|
||||
});
|
||||
}
|
||||
else
|
||||
didZoom = true;
|
||||
|
||||
tab.linkedBrowser.loadURI(url);
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
zoomTest: function zoomTest(tab, val, msg) {
|
||||
is(ZoomManager.getZoomForBrowser(tab.linkedBrowser), val, msg);
|
||||
},
|
||||
|
||||
enlarge: function enlarge() {
|
||||
let deferred = Promise.defer();
|
||||
FullZoom.enlarge(function () deferred.resolve());
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
reduce: function reduce() {
|
||||
let deferred = Promise.defer();
|
||||
FullZoom.reduce(function () deferred.resolve());
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
reset: function reset() {
|
||||
let deferred = Promise.defer();
|
||||
FullZoom.reset(function () deferred.resolve());
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
BACK: 0,
|
||||
FORWARD: 1,
|
||||
navigate: function navigate(direction) {
|
||||
let deferred = Promise.defer();
|
||||
let didPs = false;
|
||||
let didZoom = false;
|
||||
|
||||
gBrowser.addEventListener("pageshow", function (event) {
|
||||
gBrowser.removeEventListener("pageshow", arguments.callee, true);
|
||||
didPs = true;
|
||||
if (didZoom)
|
||||
deferred.resolve();
|
||||
}, true);
|
||||
|
||||
if (direction == this.BACK)
|
||||
gBrowser.goBack();
|
||||
else if (direction == this.FORWARD)
|
||||
gBrowser.goForward();
|
||||
|
||||
this.selectTabAndWaitForLocationChange(null).then(function () {
|
||||
didZoom = true;
|
||||
if (didPs)
|
||||
deferred.resolve();
|
||||
});
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
failAndContinue: function failAndContinue(func) {
|
||||
return function (err) {
|
||||
ok(false, err);
|
||||
func();
|
||||
};
|
||||
},
|
||||
};
|
||||
|
@ -274,29 +274,30 @@ var tests = [
|
||||
function test_forget_site() {
|
||||
// click "Forget About This Site" button
|
||||
gBrowser.contentDocument.getElementById("forget-site-button").doCommand();
|
||||
waitForClearHistory(function() {
|
||||
is(gSiteLabel.value, "", "site label cleared");
|
||||
|
||||
is(gSiteLabel.value, "", "site label cleared");
|
||||
let allSitesItem = gBrowser.contentDocument.getElementById("all-sites-item");
|
||||
is(gSitesList.selectedItem, allSitesItem,
|
||||
"all sites item selected after forgetting selected site");
|
||||
|
||||
let allSitesItem = gBrowser.contentDocument.getElementById("all-sites-item");
|
||||
is(gSitesList.selectedItem, allSitesItem,
|
||||
"all sites item selected after forgetting selected site");
|
||||
// check to make sure site is gone from sites list
|
||||
let testSiteItem = getSiteItem(TEST_URI_2.host);
|
||||
ok(!testSiteItem, "site removed from sites list");
|
||||
|
||||
// check to make sure site is gone from sites list
|
||||
let testSiteItem = getSiteItem(TEST_URI_2.host);
|
||||
ok(!testSiteItem, "site removed from sites list");
|
||||
|
||||
// check to make sure we forgot all permissions corresponding to site
|
||||
for (let type in TEST_PERMS) {
|
||||
if (type == "password") {
|
||||
ok(Services.logins.getLoginSavingEnabled(TEST_URI_2.prePath),
|
||||
"password saving should be enabled by default");
|
||||
} else {
|
||||
is(Services.perms.testPermissionFromPrincipal(TEST_PRINCIPAL_2, type), PERM_UNKNOWN,
|
||||
type + " permission should not be set for test site 2");
|
||||
// check to make sure we forgot all permissions corresponding to site
|
||||
for (let type in TEST_PERMS) {
|
||||
if (type == "password") {
|
||||
ok(Services.logins.getLoginSavingEnabled(TEST_URI_2.prePath),
|
||||
"password saving should be enabled by default");
|
||||
} else {
|
||||
is(Services.perms.testPermissionFromPrincipal(TEST_PRINCIPAL_2, type), PERM_UNKNOWN,
|
||||
type + " permission should not be set for test site 2");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
runNextTest();
|
||||
});
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -25,27 +25,29 @@ function test() {
|
||||
let mozillaZoom = aWindow.ZoomManager.zoom;
|
||||
|
||||
// change the zoom on the mozilla page
|
||||
aWindow.FullZoom.enlarge();
|
||||
// make sure the zoom level has been changed
|
||||
isnot(aWindow.ZoomManager.zoom, mozillaZoom, "Zoom level can be changed");
|
||||
mozillaZoom = aWindow.ZoomManager.zoom;
|
||||
aWindow.FullZoom.enlarge(function () {
|
||||
// make sure the zoom level has been changed
|
||||
isnot(aWindow.ZoomManager.zoom, mozillaZoom, "Zoom level can be changed");
|
||||
mozillaZoom = aWindow.ZoomManager.zoom;
|
||||
|
||||
// switch to about: tab
|
||||
aWindow.gBrowser.selectedTab = tabAbout;
|
||||
// switch to about: tab
|
||||
aWindow.gBrowser.selectedTab = tabAbout;
|
||||
|
||||
// switch back to mozilla tab
|
||||
aWindow.gBrowser.selectedTab = tabMozilla;
|
||||
// switch back to mozilla tab
|
||||
aWindow.gBrowser.selectedTab = tabMozilla;
|
||||
|
||||
// make sure the zoom level has not changed
|
||||
is(aWindow.ZoomManager.zoom, mozillaZoom,
|
||||
"Entering private browsing should not reset the zoom on a tab");
|
||||
// make sure the zoom level has not changed
|
||||
is(aWindow.ZoomManager.zoom, mozillaZoom,
|
||||
"Entering private browsing should not reset the zoom on a tab");
|
||||
|
||||
// cleanup
|
||||
aWindow.FullZoom.reset();
|
||||
aWindow.gBrowser.removeTab(tabMozilla);
|
||||
aWindow.gBrowser.removeTab(tabAbout);
|
||||
aWindow.close();
|
||||
aCallback();
|
||||
// cleanup
|
||||
aWindow.FullZoom.reset(function () {
|
||||
aWindow.gBrowser.removeTab(tabMozilla);
|
||||
aWindow.gBrowser.removeTab(tabAbout);
|
||||
aWindow.close();
|
||||
aCallback();
|
||||
});
|
||||
});
|
||||
}, true);
|
||||
mozillaBrowser.contentWindow.location = "about:mozilla";
|
||||
}, true);
|
||||
|
@ -16,12 +16,14 @@ function test() {
|
||||
aWindow.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
|
||||
if (aIsZoomedWindow) {
|
||||
// change the zoom on the blank page
|
||||
aWindow.FullZoom.enlarge();
|
||||
isnot(aWindow.ZoomManager.zoom, 1, "Zoom level for about:blank should be changed");
|
||||
} else {
|
||||
// make sure the zoom level is set to 1
|
||||
is(aWindow.ZoomManager.zoom, 1, "Zoom level for about:privatebrowsing should be reset");
|
||||
aWindow.FullZoom.enlarge(function () {
|
||||
isnot(aWindow.ZoomManager.zoom, 1, "Zoom level for about:blank should be changed");
|
||||
aCallback();
|
||||
});
|
||||
return;
|
||||
}
|
||||
// make sure the zoom level is set to 1
|
||||
is(aWindow.ZoomManager.zoom, 1, "Zoom level for about:privatebrowsing should be reset");
|
||||
|
||||
aCallback();
|
||||
}, true);
|
||||
@ -31,10 +33,18 @@ function test() {
|
||||
|
||||
function finishTest() {
|
||||
// cleanup
|
||||
let numWindows = windowsToReset.length;
|
||||
if (!numWindows) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
windowsToReset.forEach(function(win) {
|
||||
win.FullZoom.reset();
|
||||
win.FullZoom.reset(function onReset() {
|
||||
numWindows--;
|
||||
if (!numWindows)
|
||||
finish();
|
||||
});
|
||||
});
|
||||
finish();
|
||||
}
|
||||
|
||||
function testOnWindow(options, callback) {
|
||||
|
@ -4,7 +4,18 @@
|
||||
|
||||
Components.utils.import("resource://gre/modules/ForgetAboutSite.jsm");
|
||||
|
||||
function waitForClearHistory(aCallback) {
|
||||
let observer = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
Services.obs.removeObserver(this, "browser:purge-domain-data");
|
||||
setTimeout(aCallback, 0);
|
||||
}
|
||||
};
|
||||
Services.obs.addObserver(observer, "browser:purge-domain-data", false);
|
||||
}
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
// utility functions
|
||||
function countClosedTabsByTitle(aClosedTabList, aTitle)
|
||||
aClosedTabList.filter(function (aData) aData.title == aTitle).length;
|
||||
@ -80,36 +91,38 @@ function test() {
|
||||
|
||||
// purge domain & check that we purged correctly for closed windows
|
||||
ForgetAboutSite.removeDataFromDomain("mozilla.org");
|
||||
waitForClearHistory(function() {
|
||||
let closedWindowData = JSON.parse(ss.getClosedWindowData());
|
||||
|
||||
let closedWindowData = JSON.parse(ss.getClosedWindowData());
|
||||
// First set of tests for _closedWindows[0] - tests basics
|
||||
let win = closedWindowData[0];
|
||||
is(win.tabs.length, 1, "1 tab was removed");
|
||||
is(countOpenTabsByTitle(win.tabs, FORGET), 0,
|
||||
"The correct tab was removed");
|
||||
is(countOpenTabsByTitle(win.tabs, REMEMBER), 1,
|
||||
"The correct tab was remembered");
|
||||
is(win.selected, 1, "Selected tab has changed");
|
||||
is(win.title, REMEMBER, "The window title was correctly updated");
|
||||
|
||||
// First set of tests for _closedWindows[0] - tests basics
|
||||
let win = closedWindowData[0];
|
||||
is(win.tabs.length, 1, "1 tab was removed");
|
||||
is(countOpenTabsByTitle(win.tabs, FORGET), 0,
|
||||
"The correct tab was removed");
|
||||
is(countOpenTabsByTitle(win.tabs, REMEMBER), 1,
|
||||
"The correct tab was remembered");
|
||||
is(win.selected, 1, "Selected tab has changed");
|
||||
is(win.title, REMEMBER, "The window title was correctly updated");
|
||||
// Test more complicated case
|
||||
win = closedWindowData[1];
|
||||
is(win.tabs.length, 3, "2 tabs were removed");
|
||||
is(countOpenTabsByTitle(win.tabs, FORGET), 0,
|
||||
"The correct tabs were removed");
|
||||
is(countOpenTabsByTitle(win.tabs, REMEMBER), 3,
|
||||
"The correct tabs were remembered");
|
||||
is(win.selected, 3, "Selected tab has changed");
|
||||
is(win.title, REMEMBER, "The window title was correctly updated");
|
||||
|
||||
// Test more complicated case
|
||||
win = closedWindowData[1];
|
||||
is(win.tabs.length, 3, "2 tabs were removed");
|
||||
is(countOpenTabsByTitle(win.tabs, FORGET), 0,
|
||||
"The correct tabs were removed");
|
||||
is(countOpenTabsByTitle(win.tabs, REMEMBER), 3,
|
||||
"The correct tabs were remembered");
|
||||
is(win.selected, 3, "Selected tab has changed");
|
||||
is(win.title, REMEMBER, "The window title was correctly updated");
|
||||
// Tests handling of _closedTabs
|
||||
win = closedWindowData[2];
|
||||
is(countClosedTabsByTitle(win._closedTabs, REMEMBER), 1,
|
||||
"The correct number of tabs were removed, and the correct ones");
|
||||
is(countClosedTabsByTitle(win._closedTabs, FORGET), 0,
|
||||
"All tabs to be forgotten were indeed removed");
|
||||
|
||||
// Tests handling of _closedTabs
|
||||
win = closedWindowData[2];
|
||||
is(countClosedTabsByTitle(win._closedTabs, REMEMBER), 1,
|
||||
"The correct number of tabs were removed, and the correct ones");
|
||||
is(countClosedTabsByTitle(win._closedTabs, FORGET), 0,
|
||||
"All tabs to be forgotten were indeed removed");
|
||||
|
||||
// restore pre-test state
|
||||
ss.setBrowserState(oldState);
|
||||
// restore pre-test state
|
||||
ss.setBrowserState(oldState);
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
@ -4,6 +4,16 @@
|
||||
|
||||
Components.utils.import("resource://gre/modules/ForgetAboutSite.jsm");
|
||||
|
||||
function waitForClearHistory(aCallback) {
|
||||
let observer = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
Services.obs.removeObserver(this, "browser:purge-domain-data");
|
||||
setTimeout(aCallback, 0);
|
||||
}
|
||||
};
|
||||
Services.obs.addObserver(observer, "browser:purge-domain-data", false);
|
||||
}
|
||||
|
||||
function test() {
|
||||
/** Test for Bug 464199 **/
|
||||
|
||||
@ -59,18 +69,19 @@ function test() {
|
||||
"Everything is set up.");
|
||||
|
||||
ForgetAboutSite.removeDataFromDomain("example.net");
|
||||
waitForClearHistory(function() {
|
||||
closedTabs = JSON.parse(ss.getClosedTabData(newWin));
|
||||
is(closedTabs.length, remember_count,
|
||||
"The correct amout of tabs was removed");
|
||||
is(countByTitle(closedTabs, FORGET), 0,
|
||||
"All tabs to be forgotten were indeed removed");
|
||||
is(countByTitle(closedTabs, REMEMBER), remember_count,
|
||||
"... and tabs to be remembered weren't.");
|
||||
|
||||
closedTabs = JSON.parse(ss.getClosedTabData(newWin));
|
||||
is(closedTabs.length, remember_count,
|
||||
"The correct amout of tabs was removed");
|
||||
is(countByTitle(closedTabs, FORGET), 0,
|
||||
"All tabs to be forgotten were indeed removed");
|
||||
is(countByTitle(closedTabs, REMEMBER), remember_count,
|
||||
"... and tabs to be remembered weren't.");
|
||||
|
||||
// clean up
|
||||
newWin.close();
|
||||
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
|
||||
finish();
|
||||
// clean up
|
||||
newWin.close();
|
||||
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
|
||||
finish();
|
||||
});
|
||||
}, false);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -14,6 +14,14 @@ var FindHelperUI = {
|
||||
_open: false,
|
||||
_status: null,
|
||||
|
||||
/*
|
||||
* Properties
|
||||
*/
|
||||
|
||||
get isActive() {
|
||||
return this._open;
|
||||
},
|
||||
|
||||
get status() {
|
||||
return this._status;
|
||||
},
|
||||
@ -94,9 +102,12 @@ var FindHelperUI = {
|
||||
},
|
||||
|
||||
show: function findHelperShow() {
|
||||
|
||||
// Hide any menus
|
||||
ContextUI.dismiss();
|
||||
|
||||
// Shutdown selection related ui
|
||||
SelectionHelperUI.closeEditSession();
|
||||
|
||||
this._container.show(this);
|
||||
this.search(this._textbox.value);
|
||||
this._textbox.select();
|
||||
|
@ -146,7 +146,7 @@ var TouchModule = {
|
||||
// once we get omtc and the apzc. Currently though dblclick is delivered to
|
||||
// content and triggers selection of text, so fire up the SelectionHelperUI
|
||||
// once selection is present.
|
||||
if (!SelectionHelperUI.isActive) {
|
||||
if (!SelectionHelperUI.isActive && !FindHelperUI.isActive) {
|
||||
setTimeout(function () {
|
||||
SelectionHelperUI.attachEditSession(Browser.selectedTab.browser,
|
||||
aEvent.clientX, aEvent.clientY);
|
||||
|
@ -124,8 +124,8 @@ Sanitizer.prototype = {
|
||||
Services.perms.removeAll();
|
||||
|
||||
// Clear site-specific settings like page-zoom level
|
||||
var cps = Cc["@mozilla.org/content-pref/service;1"].getService(Ci.nsIContentPrefService);
|
||||
cps.removeGroupedPrefs();
|
||||
var cps = Cc["@mozilla.org/content-pref/service;1"].getService(Ci.nsIContentPrefService2);
|
||||
cps.removeAllDomains(null);
|
||||
|
||||
// Clear "Never remember passwords for this site", which is not handled by
|
||||
// the permission manager
|
||||
|
@ -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)
|
||||
|
||||
|
43
build/mobile/robocop/RobocopUtils.java.in
Normal file
43
build/mobile/robocop/RobocopUtils.java.in
Normal file
@ -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,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user