Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2014-05-12 13:48:01 +02:00
commit 364ad99c8f
138 changed files with 4059 additions and 3303 deletions

View File

@ -50,7 +50,8 @@ function PlainTextConsole(print, innerID) {
prefix: self.name + ": ",
maxLogLevel: logLevel,
dump: print,
innerID: innerID
innerID: innerID,
consoleID: "addon/" + self.id
};
let console = new ConsoleAPI(consoleOptions);

View File

@ -292,7 +292,7 @@ const ContentWorker = Object.freeze({
"function, which works the same. Replace calls to `on()` " +
"with calls to `self.on()`" +
"For more info on `self.on`, see " +
"<https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/addon-development/web-content.html>.");
"<https://developer.mozilla.org/en-US/Add-ons/SDK/Guides/Content_Scripts/using_postMessage>.");
return self.on.apply(null, arguments);
};
@ -309,7 +309,7 @@ const ContentWorker = Object.freeze({
"definitions with calls to `self.on('message', " +
"function (data){})`. " +
"For more info on `self.on`, see " +
"<https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/addon-development/web-content.html>.");
"<https://developer.mozilla.org/en-US/Add-ons/SDK/Guides/Content_Scripts/using_postMessage>.");
onMessage = v;
if (typeof onMessage == "function")
self.on("message", onMessage);

View File

@ -20,6 +20,7 @@ const events = require('../system/events');
const { getInnerId } = require("../window/utils");
const { WorkerSandbox } = require('./sandbox');
const { getTabForWindow } = require('../tabs/helpers');
const { isPrivate } = require('../private-browsing/utils');
// A weak map of workers to hold private attributes that
// should not be exposed
@ -37,7 +38,7 @@ const ERR_FROZEN = "The page is currently hidden and can no longer be used " +
/**
* Message-passing facility for communication between code running
* in the content and add-on process.
* @see https://addons.mozilla.org/en-US/developers/docs/sdk/latest/modules/sdk/content/worker.html
* @see https://developer.mozilla.org/en-US/Add-ons/SDK/Low-Level_APIs/content_worker
*/
const Worker = Class({
implements: [EventTarget],
@ -186,6 +187,8 @@ detach.define(Worker, function (worker, reason) {
model.inited = false;
});
isPrivate.define(Worker, ({ tab }) => isPrivate(tab));
/**
* Tells content worker to unload itself and
* removes all the references from itself.

View File

@ -11,7 +11,7 @@ const { Trait } = require('../deprecated/traits');
const { iteratorSymbol } = require('../util/iteration');
/**
* @see https://jetpack.mozillalabs.com/sdk/latest/docs/#module/api-utils/list
* @see https://developer.mozilla.org/en-US/Add-ons/SDK/Low-Level_APIs/util_list
*/
const Iterable = Trait.compose({
/**

View File

@ -393,7 +393,7 @@ const WorkerSandbox = EventEmitter.compose({
/**
* Message-passing facility for communication between code running
* in the content and add-on process.
* @see https://addons.mozilla.org/en-US/developers/docs/sdk/latest/modules/sdk/content/worker.html
* @see https://developer.mozilla.org/en-US/Add-ons/SDK/Low-Level_APIs/content_worker
*/
const Worker = EventEmitter.compose({
on: Trait.required,

View File

@ -11,6 +11,8 @@ module.metadata = {
const memory = require('./memory');
var timer = require("../timers");
var cfxArgs = require("@test/options");
const { getTabs, getURI } = require("../tabs/utils");
const { windows, isBrowser } = require("../window/utils");
exports.findAndRunTests = function findAndRunTests(options) {
var TestFinder = require("./unit-test-finder").TestFinder;
@ -278,14 +280,39 @@ TestRunner.prototype = {
this.failed++;
this.test.failed++;
}
let wins = windows(null, { includePrivate: true });
let tabs = [];
for (let win of wins.filter(isBrowser)) {
for (let tab of getTabs(win)) {
tabs.push(tab);
}
}
if (wins.length != 1)
this.fail("Should not be any unexpected windows open");
if (tabs.length != 1)
this.fail("Should not be any unexpected tabs open");
if (tabs.length != 1 || wins.length != 1) {
console.log("Windows open:");
for (let win of wins) {
if (isBrowser(win)) {
tabs = getTabs(win);
console.log(win.location + " - " + tabs.map(getURI).join(", "));
}
else {
console.log(win.location);
}
}
}
this.testRunSummary.push({
name: this.test.name,
passed: this.test.passed,
failed: this.test.failed,
errors: [error for (error in this.test.errors)].join(", ")
});
if (this.onDone !== null) {
var onDone = this.onDone;
var self = this;

View File

@ -25,7 +25,7 @@ const { windowIterator } = require('./deprecated/window-utils');
const { isBrowser, getFrames } = require('./window/utils');
const { getTabs, getTabContentWindow, getTabForContentWindow,
getURI: getTabURI } = require('./tabs/utils');
const { ignoreWindow } = require('sdk/private-browsing/utils');
const { ignoreWindow } = require('./private-browsing/utils');
const { Style } = require("./stylesheet/style");
const { attach, detach } = require("./content/mod");
const { has, hasAny } = require("./util/array");
@ -167,22 +167,8 @@ function onContentWindow({ subject: document }) {
}
}
// Returns all tabs on all currently opened windows
function getAllTabs() {
let tabs = [];
// Iterate over all chrome windows
for (let window in windowIterator()) {
if (!isBrowser(window))
continue;
tabs = tabs.concat(getTabs(window));
}
return tabs;
}
function applyOnExistingDocuments (mod) {
let tabs = getAllTabs();
tabs.forEach(function (tab) {
getTabs().forEach(tab => {
// Fake a newly created document
let window = getTabContentWindow(tab);
if (has(mod.attachTo, "top") && mod.include.matchesAny(getTabURI(tab)))

View File

@ -165,7 +165,7 @@ function register(factory) {
throw new Error("xpcom.register() expect a Factory instance.\n" +
"Please refactor your code to new xpcom module if you" +
" are repacking an addon from SDK <= 1.5:\n" +
"https://addons.mozilla.org/en-US/developers/docs/sdk/latest/packages/api-utils/xpcom.html");
"https://developer.mozilla.org/en-US/Add-ons/SDK/Low-Level_APIs/platform_xpcom");
}
registerFactory(factory.id, factory.description, factory.contract, factory);

View File

@ -7,13 +7,10 @@ module.metadata = {
"stability": "stable"
};
const { Ci } = require('chrome');
const { setMode, getMode, on: onStateChange, isPermanentPrivateBrowsing } = require('./private-browsing/utils');
const { isWindowPrivate } = require('./window/utils');
const { setMode, getMode, on: onStateChange, isPrivate } = require('./private-browsing/utils');
const { emit, on, once, off } = require('./event/core');
const { when: unload } = require('./system/unload');
const { deprecateUsage, deprecateFunction, deprecateEvent } = require('./util/deprecate');
const { getOwnerWindow } = require('./private-browsing/window/utils');
const { deprecateFunction, deprecateEvent } = require('./util/deprecate');
onStateChange('start', function onStart() {
emit(exports, 'start');
@ -39,48 +36,7 @@ exports.removeListener = deprecateEvents(function removeListener(type, listener)
off(exports, type, listener);
});
exports.isPrivate = function(thing) {
// if thing is defined, and we can find a window for it
// then check if the window is private
if (!!thing) {
// if the thing is a window, and the window is private
// then return true
if (isWindowPrivate(thing)) {
return true;
}
// does the thing have an associated tab?
// page-mod instances do..
if (thing.tab) {
let tabWindow = getOwnerWindow(thing.tab);
if (tabWindow) {
let isThingPrivate = isWindowPrivate(tabWindow);
if (isThingPrivate)
return isThingPrivate;
}
}
// can we find an associated window?
let window = getOwnerWindow(thing);
if (window)
return isWindowPrivate(window);
try {
let { isChannelPrivate } = thing.QueryInterface(Ci.nsIPrivateBrowsingChannel);
if (isChannelPrivate)
return true;
} catch(e) {}
}
// check if the post pwpb, global pb service is enabled.
if (isPermanentPrivateBrowsing())
return true;
// if we get here, and global private browsing
// is available, and it is true, then return
// true otherwise false is returned here
return getMode();
};
exports.isPrivate = isPrivate;
function deprecateEvents(func) deprecateEvent(
func,

View File

@ -16,6 +16,7 @@ const { deprecateFunction } = require('../util/deprecate');
const { isOneOf, is, satisfiesVersion, version } = require('../system/xul-app');
const { isWindowPrivate } = require('../window/utils');
const { isPrivateBrowsingSupported } = require('../self');
const { dispatcher } = require("../util/dispatcher");
let deferredEmit = defer(emit);
let pbService;
@ -53,10 +54,11 @@ let isWindowPBSupported = exports.isWindowPBSupported =
let isTabPBSupported = exports.isTabPBSupported =
!pbService && !!PrivateBrowsingUtils && is('Fennec') && satisfiesVersion(version, '>=20.0*');
exports.isPermanentPrivateBrowsing = function() {
function isPermanentPrivateBrowsing() {
return !!(PrivateBrowsingUtils && PrivateBrowsingUtils.permanentPrivateBrowsing);
}
exports.isPermanentPrivateBrowsing = isPermanentPrivateBrowsing;
function ignoreWindow(window) {
return !isPrivateBrowsingSupported && isWindowPrivate(window) && !isGlobalPBSupported;
}
@ -90,14 +92,25 @@ exports.setMode = deprecateFunction(
);
let getMode = function getMode(chromeWin) {
if (isWindowPrivate(chromeWin))
if (chromeWin !== undefined && isWindowPrivate(chromeWin))
return true;
// default
return pbService ? pbService.privateBrowsingEnabled : false;
return isGlobalPrivateBrowsing();
};
exports.getMode = getMode;
function isGlobalPrivateBrowsing() {
return pbService ? pbService.privateBrowsingEnabled : false;
}
const isPrivate = dispatcher("isPrivate");
isPrivate.when(isPermanentPrivateBrowsing, _ => true);
isPrivate.when(x => x instanceof Ci.nsIDOMWindow, isWindowPrivate);
isPrivate.when(x => Ci.nsIPrivateBrowsingChannel && x instanceof Ci.nsIPrivateBrowsingChannel, x => x.isChannelPrivate);
isPrivate.define(isGlobalPrivateBrowsing);
exports.isPrivate = isPrivate;
exports.on = on.bind(null, exports);
// Make sure listeners are cleaned up.

View File

@ -1,33 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
module.metadata = {
'stability': 'unstable'
};
const privateNS = require('../../core/namespace').ns();
function getOwnerWindow(thing) {
try {
// check for and return associated window
let fn = (privateNS(thing.prototype) || privateNS(thing) || {}).getOwnerWindow;
if (fn)
return fn.apply(fn, [thing].concat(arguments));
}
// stuff like numbers and strings throw errors with namespaces
catch(e) {}
// default
return undefined;
}
getOwnerWindow.define = function(Type, fn) {
privateNS(Type.prototype).getOwnerWindow = fn;
}
getOwnerWindow.implement = function(instance, fn) {
privateNS(instance).getOwnerWindow = fn;
}
exports.getOwnerWindow = getOwnerWindow;

View File

@ -11,7 +11,8 @@ const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL, getTabConten
getTabForBrowser,
setTabURL, getOwnerWindow, getTabContentType, getTabId } = require('./utils');
const { emit } = require('../event/core');
const { getOwnerWindow: getPBOwnerWindow } = require('../private-browsing/window/utils');
const { isPrivate } = require('../private-browsing/utils');
const { isWindowPrivate } = require('../window/utils');
const { when: unload } = require('../system/unload');
const { viewFor } = require('../view/core');
const { EVENTS } = require('./events');
@ -240,6 +241,6 @@ function onTabClose(event) {
cleanupTab(this);
};
getPBOwnerWindow.define(Tab, function(tab) {
return getTabContentWindow(tabNS(tab).tab);
isPrivate.implement(Tab, tab => {
return isWindowPrivate(getTabContentWindow(tabNS(tab).tab));
});

View File

@ -12,11 +12,13 @@ const { getThumbnailURIForWindow } = require("../content/thumbnail");
const { getFaviconURIForLocation } = require("../io/data");
const { activateTab, getOwnerWindow, getBrowserForTab, getTabTitle, setTabTitle,
getTabURL, setTabURL, getTabContentType, getTabId } = require('./utils');
const { getOwnerWindow: getPBOwnerWindow } = require('../private-browsing/window/utils');
const { isPrivate } = require('../private-browsing/utils');
const { isWindowPrivate } = require('../window/utils');
const viewNS = require('../core/namespace').ns();
const { deprecateUsage } = require('../util/deprecate');
const { getURL } = require('../url/utils');
const { viewFor } = require('../view/core');
const { observer } = require('./observer');
// Array of the inner instances of all the wrapped tabs.
const TABS = [];
@ -64,8 +66,8 @@ const TabTrait = Trait.compose(EventEmitter, {
this.pin();
viewNS(this._public).tab = this._tab;
getPBOwnerWindow.implement(this._public, getChromeTab);
viewFor.implement(this._public, getTabView);
isPrivate.implement(this._public, tab => isWindowPrivate(getChromeTab(tab)));
// Add tabs to getURL method
getURL.implement(this._public, (function (obj) this._public.url).bind(this));
@ -256,8 +258,12 @@ const TabTrait = Trait.compose(EventEmitter, {
callback();
return;
}
if (callback)
this.once(EVENTS.close.name, callback);
if (callback) {
if (this.window.tabs.activeTab && (this.window.tabs.activeTab.id == this.id))
observer.once('select', callback);
else
this.once(EVENTS.close.name, callback);
}
this._window.gBrowser.removeTab(this._tab);
},
/**

View File

@ -81,9 +81,9 @@ exports.getTabContainer = getTabContainer;
*/
function getTabs(window) {
if (arguments.length === 0) {
return getWindows().filter(isBrowser).reduce(function(tabs, window) {
return tabs.concat(getTabs(window))
}, []);
return getWindows().
filter(isBrowser).
reduce((tabs, window) => tabs.concat(getTabs(window)), []);
}
// fennec
@ -91,7 +91,7 @@ function getTabs(window) {
return window.BrowserApp.tabs;
// firefox - default
return Array.slice(getTabContainer(window).children);
return Array.filter(getTabContainer(window).children, function(t) !t.closing);
}
exports.getTabs = getTabs;

View File

@ -9,11 +9,10 @@ const { on, off, once } = require('../event/core');
const { method } = require('../lang/functional');
const { getWindowTitle } = require('./utils');
const unload = require('../system/unload');
const { isWindowPrivate } = require('../window/utils');
const { EventTarget } = require('../event/target');
const { getOwnerWindow: getPBOwnerWindow } = require('../private-browsing/window/utils');
const { isPrivate } = require('../private-browsing/utils');
const { isWindowPrivate } = require('../window/utils');
const { viewFor } = require('../view/core');
const { deprecateUsage } = require('../util/deprecate');
const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec, consider using require("sdk/tabs") instead';
@ -38,18 +37,11 @@ const BrowserWindow = Class({
get activeTab() require('../tabs').activeTab,
on: method(on),
removeListener: method(off),
once: method(once),
get isPrivateBrowsing() {
deprecateUsage('`browserWindow.isPrivateBrowsing` is deprecated, please ' +
'consider using ' +
'`require("sdk/private-browsing").isPrivate(browserWindow)` ' +
'instead.');
return isWindowPrivate(windowNS(this).window);
}
once: method(once)
});
exports.BrowserWindow = BrowserWindow;
const getWindowView = window => windowNS(window).window;
getPBOwnerWindow.define(BrowserWindow, getWindowView);
isPrivate.define(BrowserWindow, window => isWindowPrivate(windowNS(this).window));
viewFor.define(BrowserWindow, getWindowView);

View File

@ -10,18 +10,18 @@ const { Cc, Ci, Cr } = require('chrome'),
{ WindowTabs, WindowTabTracker } = require('./tabs-firefox'),
{ WindowDom } = require('./dom'),
{ WindowLoader } = require('./loader'),
{ isBrowser, getWindowDocShell, windows: windowIterator } = require('../window/utils'),
{ isBrowser, getWindowDocShell,
windows: windowIterator, isWindowPrivate } = require('../window/utils'),
{ Options } = require('../tabs/common'),
apiUtils = require('../deprecated/api-utils'),
unload = require('../system/unload'),
windowUtils = require('../deprecated/window-utils'),
{ WindowTrackerTrait } = windowUtils,
{ ns } = require('../core/namespace'),
{ observer: windowObserver } = require('./observer'),
{ getOwnerWindow } = require('../private-browsing/window/utils');
{ observer: windowObserver } = require('./observer');
const { windowNS } = require('../window/namespace');
const { isPrivateBrowsingSupported } = require('../self');
const { ignoreWindow } = require('sdk/private-browsing/utils');
const { ignoreWindow, isPrivate } = require('sdk/private-browsing/utils');
const { viewFor } = require('../view/core');
/**
@ -76,7 +76,8 @@ const BrowserWindowTrait = Trait.compose(
this._load();
windowNS(this._public).window = this._window;
getOwnerWindow.implement(this._public, getChromeWindow);
isPrivate.implement(this._public, window => isWindowPrivate(getChromeWindow(window)));
viewFor.implement(this._public, getChromeWindow);
return this;

View File

@ -40,7 +40,7 @@ const { notifyObservers } = Cc['@mozilla.org/observer-service;1'].
getService(Ci.nsIObserverService);
const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
const { Reflect } = Cu.import("resource://gre/modules/reflect.jsm", {});
const { console } = Cu.import("resource://gre/modules/devtools/Console.jsm");
const { ConsoleAPI } = Cu.import("resource://gre/modules/devtools/Console.jsm");
const { join: pathJoin, normalize, dirname } = Cu.import("resource://gre/modules/osfile/ospath_unix.jsm");
// Define some shortcuts.
@ -56,7 +56,7 @@ const NODE_MODULES = ["assert", "buffer_ieee754", "buffer", "child_process", "cl
const COMPONENT_ERROR = '`Components` is not available in this context.\n' +
'Functionality provided by Components may be available in an SDK\n' +
'module: https://jetpack.mozillalabs.com/sdk/latest/docs/ \n\n' +
'module: https://developer.mozilla.org/en-US/Add-ons/SDK \n\n' +
'However, if you still need to import Components, you may use the\n' +
'`chrome` module\'s properties for shortcuts to Component properties:\n\n' +
'Shortcuts: \n' +
@ -688,6 +688,10 @@ exports.unload = unload;
// If `resolve` does not returns `uri` string exception will be thrown by
// an associated `require` call.
const Loader = iced(function Loader(options) {
let console = new ConsoleAPI({
consoleID: options.id ? "addon/" + options.id : ""
});
let {
modules, globals, resolve, paths, rootURI, manifest, requireMap, isNative
} = override({

View File

@ -264,10 +264,11 @@ parser_groups = (
cmds=['test', 'run', 'xpi', 'testex',
'testpkgs', 'testall'])),
(("", "--e10s",), dict(dest="enable_e10s",
help="enable out-of-process Jetpacks",
help="enable remote windows",
action="store_true",
default=False,
cmds=['test', 'run', 'testex', 'testpkgs'])),
cmds=['test', 'run', 'testex', 'testpkgs',
'testaddons', 'testcfx', 'testall'])),
(("", "--logfile",), dict(dest="logfile",
help="log console output to file",
metavar=None,
@ -902,6 +903,8 @@ def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None,
if options.addons is not None:
options.addons = options.addons.split(",")
enable_e10s = options.enable_e10s or target_cfg.get('e10s', False)
try:
retval = run_app(harness_root_dir=app_extension_dir,
manifest_rdf=manifest_rdf,
@ -924,7 +927,8 @@ def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None,
is_running_tests=(command == "test"),
overload_modules=options.overload_modules,
bundle_sdk=options.bundle_sdk,
pkgdir=options.pkgdir)
pkgdir=options.pkgdir,
enable_e10s=enable_e10s)
except ValueError, e:
print ""
print "A given cfx option has an inappropriate value:"

View File

@ -418,7 +418,8 @@ def run_app(harness_root_dir, manifest_rdf, harness_options,
is_running_tests=False,
overload_modules=False,
bundle_sdk=True,
pkgdir=""):
pkgdir="",
enable_e10s=False):
if binary:
binary = os.path.expanduser(binary)
@ -430,6 +431,9 @@ def run_app(harness_root_dir, manifest_rdf, harness_options,
cmdargs = []
preferences = dict(DEFAULT_COMMON_PREFS)
if enable_e10s:
preferences['browser.tabs.remote.autostart'] = True
# For now, only allow running on Mobile with --force-mobile argument
if app_type in ["fennec", "fennec-on-device"] and not enable_mobile:
print """

View File

@ -60,7 +60,7 @@ def welcome():
print ("Your SDK may not work properly.")
return
print ("Welcome to the Add-on SDK. For the docs, visit https://addons.mozilla.org/en-US/developers/docs/sdk/latest/")
print ("Welcome to the Add-on SDK. For the docs, visit https://developer.mozilla.org/en-US/Add-ons/SDK")
if __name__ == '__main__':
welcome()

View File

@ -0,0 +1,10 @@
<!-- 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/. -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body></body>
</html>

View File

@ -0,0 +1,8 @@
/* 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';
self.port.on('echo', _ => {
self.port.emit('echo', '');
});

View File

@ -7,6 +7,8 @@ const { Cu, Cc, Ci } = require('chrome');
const Request = require('sdk/request').Request;
const { WindowTracker } = require('sdk/deprecated/window-utils');
const { close, open } = require('sdk/window/helpers');
const { data } = require('sdk/self');
const { Panel } = require('sdk/panel');
const XUL_URL = 'chrome://test/content/new-window.xul'
@ -65,4 +67,26 @@ exports.testChromeLocale = function(assert) {
'locales en-US folder was copied correctly');
}
exports.testChromeInPanel = function(assert, done) {
let panel = Panel({
contentURL: 'chrome://test/content/panel.html',
contentScriptWhen: 'start',
contentScriptFile: data.url('panel.js')
});
panel.once('show', _ => {
assert.pass('panel shown');
panel.port.once('echo', _ => {
assert.pass('got echo');
panel.once('hide', _ => {
panel.destroy();
assert.pass('panel is destroyed');
done();
});
panel.hide();
});
panel.port.emit('echo');
});
panel.show();
}
require('sdk/test/runner').runTestsFromModule(module);

View File

@ -0,0 +1,29 @@
/* 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 { get: getPref } = require('sdk/preferences/service');
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
const { openTab, closeTab, getBrowserForTab } = require('sdk/tabs/utils');
exports.testRemotePrefIsSet = function(assert) {
assert.ok(getPref('browser.tabs.remote.autostart'),
"Electrolysis remote tabs pref should be set");
}
exports.testTabIsRemote = function(assert, done) {
const url = 'data:text/html,test-tab-is-remote';
let tab = openTab(getMostRecentBrowserWindow(), url);
assert.ok(tab.getAttribute('remote'), "The new tab should be remote");
// can't simply close a remote tab before it is loaded, bug 1006043
let mm = getBrowserForTab(tab).messageManager;
mm.addMessageListener(7, function() {
closeTab(tab);
done();
})
mm.loadFrameScript('data:,sendAsyncMessage(7)', false);
}
require('sdk/test/runner').runTestsFromModule(module);

View File

@ -0,0 +1,10 @@
{
"name": "e10s-flag",
"title": "e10s-flag",
"id": "jid1-DYaXFHAPlHwbgw",
"description": "a basic e10s test add-on",
"author": "Tomislav Jovanovic",
"license": "MPL 2.0",
"version": "0.1",
"e10s": true
}

View File

@ -6,6 +6,7 @@
const { Cu } = require('chrome');
const { PageMod } = require('sdk/page-mod');
const tabs = require('sdk/tabs');
const { closeTab } = require('sdk/tabs/utils');
const promise = require('sdk/core/promise')
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
const { data } = require('sdk/self');
@ -47,6 +48,7 @@ exports.testDebugger = function(assert, done) {
then(_ => { assert.pass('testDebuggerStatement called') }).
then(closeConnection).
then(_ => { assert.pass('closeConnection called') }).
then(_ => { tab.close() }).
then(done).
then(null, aError => {
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);

View File

@ -6,6 +6,7 @@
const { Cu } = require('chrome');
const { PageMod } = require('sdk/page-mod');
const tabs = require('sdk/tabs');
const { closeTab } = require('sdk/tabs/utils');
const promise = require('sdk/core/promise')
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
const { data } = require('sdk/self');
@ -54,6 +55,7 @@ exports.testDebugger = function(assert, done) {
then(_ => { assert.pass('testDebuggerStatement called') }).
then(closeConnection).
then(_ => { assert.pass('closeConnection called') }).
then(_ => { tab.close() }).
then(done).
then(null, aError => {
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);

View File

@ -8,7 +8,6 @@ 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');
@ -19,47 +18,6 @@ exports.testIsPrivateBrowsingTrue = function(assert) {
'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({
@ -68,10 +26,7 @@ exports.testTabOpenPrivate = function(assert, done) {
onReady: function(tab) {
assert.equal(tab.url, TAB_URL, 'opened correct tab');
assert.equal(isPrivate(tab), (isWindowPBSupported || isTabPBSupported));
tab.close(function() {
done();
});
tab.close(done);
}
});
}
@ -84,10 +39,7 @@ exports.testTabOpenPrivateDefault = function(assert, done) {
onReady: function(tab) {
assert.equal(tab.url, TAB_URL, 'opened correct tab');
assert.equal(isPrivate(tab), false);
tab.close(function() {
done();
});
tab.close(done);
}
});
}
@ -100,10 +52,7 @@ exports.testTabOpenPrivateOffExplicit = function(assert, done) {
onReady: function(tab) {
assert.equal(tab.url, TAB_URL, 'opened correct tab');
assert.equal(isPrivate(tab), false);
tab.close(function() {
done();
});
tab.close(done);
}
});
}
@ -121,10 +70,7 @@ if (!is('Fennec')) {
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();
});
window.close(done);
});
}
});

View File

@ -86,25 +86,6 @@ function open(url, options) {
return promise;
};
/**
* Close the Active Tab
*/
function close(window) {
let { promise, resolve } = defer();
if (window && typeof(window.close) === "function") {
closeWindow(window).then(function() resolve());
}
else {
// Here we assuming that the most recent browser window is the one we're
// doing the test, and the active tab is the one we just opened.
closeTab(getActiveTab(getMostRecentBrowserWindow()));
resolve();
}
return promise;
}
/**
* Reload the window given and return a promise, that will be resolved with the
* content window after a small delay.
@ -249,7 +230,7 @@ exports["test PWPB Selection Listener"] = function(assert, done) {
assert.equal(selection.text, "fo");
close(window).
closeWindow(window).
then(loader.unload).
then(done).
then(null, assert.fail);
@ -279,7 +260,7 @@ exports["test PWPB Textarea OnSelect Listener"] = function(assert, done) {
focus(window).then(function() {
assert.equal(selection.text, "noodles");
close(window).
closeWindow(window).
then(loader.unload).
then(done).
then(null, assert.fail);
@ -298,7 +279,7 @@ exports["test PWPB Single DOM Selection"] = function(assert, done) {
open(URL, {private: true, title: "PWPB Single DOM Selection"}).
then(selectFirstDiv).
then(focus).then(function() {
then(focus).then(function(window) {
assert.equal(selection.isContiguous, true,
"selection.isContiguous with single DOM Selection works.");
@ -321,7 +302,9 @@ exports["test PWPB Single DOM Selection"] = function(assert, done) {
assert.equal(selectionCount, 1,
"One iterable selection");
}).then(close).then(loader.unload).then(done).then(null, assert.fail);
return closeWindow(window);
}).then(loader.unload).then(done).then(null, assert.fail);
}
exports["test PWPB Textarea Selection"] = function(assert, done) {
@ -331,7 +314,7 @@ exports["test PWPB Textarea Selection"] = function(assert, done) {
open(URL, {private: true, title: "PWPB Textarea Listener"}).
then(selectTextarea).
then(focus).
then(function() {
then(function(window) {
assert.equal(selection.isContiguous, true,
"selection.isContiguous with Textarea Selection works.");
@ -355,7 +338,9 @@ exports["test PWPB Textarea Selection"] = function(assert, done) {
assert.equal(selectionCount, 1,
"One iterable selection");
}).then(close).then(loader.unload).then(done).then(null, assert.fail);
return closeWindow(window);
}).then(loader.unload).then(done).then(null, assert.fail);
};
exports["test PWPB Set HTML in Multiple DOM Selection"] = function(assert, done) {
@ -365,7 +350,7 @@ exports["test PWPB Set HTML in Multiple DOM Selection"] = function(assert, done)
open(URL, {private: true, title: "PWPB Set HTML in Multiple DOM Selection"}).
then(selectAllDivs).
then(focus).
then(function() {
then(function(window) {
let html = "<span>b<b>a</b>r</span>";
let expectedText = ["bar", "and"];
@ -393,7 +378,9 @@ exports["test PWPB Set HTML in Multiple DOM Selection"] = function(assert, done)
assert.equal(selectionCount, 2,
"Two iterable selections");
}).then(close).then(loader.unload).then(done).then(null, assert.fail);
return closeWindow(window);
}).then(loader.unload).then(done).then(null, assert.fail);
};
exports["test PWPB Set Text in Textarea Selection"] = function(assert, done) {
@ -403,7 +390,7 @@ exports["test PWPB Set Text in Textarea Selection"] = function(assert, done) {
open(URL, {private: true, title: "test PWPB Set Text in Textarea Selection"}).
then(selectTextarea).
then(focus).
then(function() {
then(function(window) {
let text = "bar";
@ -429,7 +416,8 @@ exports["test PWPB Set Text in Textarea Selection"] = function(assert, done) {
assert.equal(selectionCount, 1,
"One iterable selection");
}).then(close).then(loader.unload).then(done).then(null, assert.fail);
return closeWindow(window);
}).then(loader.unload).then(done).then(null, assert.fail);
};
// If the platform doesn't support the PBPW, we're replacing PBPW tests

View File

@ -1,9 +1,11 @@
/* 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 tabs = require('sdk/tabs');
const { isPrivate } = require('sdk/private-browsing');
const pbUtils = require('sdk/private-browsing/utils');
const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
exports.testPrivateTabsAreListed = function (assert, done) {
let originalTabCount = tabs.length;
@ -12,7 +14,6 @@ exports.testPrivateTabsAreListed = function (assert, done) {
url: 'about:blank',
isPrivate: true,
onOpen: function(tab) {
let win = getOwnerWindow(tab);
// PWPB case
if (pbUtils.isWindowPBSupported || pbUtils.isTabPBSupported) {
assert.ok(isPrivate(tab), "tab is private");

View File

@ -1,8 +1,10 @@
/* 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 tabs = require('sdk/tabs');
const { isPrivate } = require('sdk/private-browsing');
const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
const { promise: windowPromise, close, focus } = require('sdk/window/helpers');
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
@ -16,9 +18,6 @@ exports.testOpenTabWithPrivateActiveWindowNoIsPrivateOption = function(assert, d
url: 'about:blank',
onOpen: function(tab) {
assert.ok(isPrivate(tab), 'new tab is private');
assert.ok(isPrivate(getOwnerWindow(tab)), 'new tab window is private');
assert.strictEqual(getOwnerWindow(tab), window, 'the tab window and the private window are the same');
close(window).then(done).then(null, assert.fail);
}
})
@ -35,9 +34,6 @@ exports.testOpenTabWithNonPrivateActiveWindowNoIsPrivateOption = function(assert
url: 'about:blank',
onOpen: function(tab) {
assert.equal(isPrivate(tab), false, 'new tab is not private');
assert.equal(isPrivate(getOwnerWindow(tab)), false, 'new tab window is not private');
assert.strictEqual(getOwnerWindow(tab), window, 'the tab window and the new window are the same');
close(window).then(done).then(null, assert.fail);
}
})
@ -55,9 +51,6 @@ exports.testOpenTabWithPrivateActiveWindowWithIsPrivateOptionTrue = function(ass
isPrivate: true,
onOpen: function(tab) {
assert.ok(isPrivate(tab), 'new tab is private');
assert.ok(isPrivate(getOwnerWindow(tab)), 'new tab window is private');
assert.strictEqual(getOwnerWindow(tab), window, 'the tab window and the private window are the same');
close(window).then(done).then(null, assert.fail);
}
})
@ -75,9 +68,6 @@ exports.testOpenTabWithNonPrivateActiveWindowWithIsPrivateOptionFalse = function
isPrivate: false,
onOpen: function(tab) {
assert.equal(isPrivate(tab), false, 'new tab is not private');
assert.equal(isPrivate(getOwnerWindow(tab)), false, 'new tab window is not private');
assert.strictEqual(getOwnerWindow(tab), window, 'the tab window and the new window are the same');
close(window).then(done).then(null, assert.fail);
}
})

View File

@ -3,15 +3,10 @@
const { Ci } = require('chrome');
const { openTab, closeTab } = require('sdk/tabs/utils');
const { browserWindows } = require('sdk/windows');
const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
const { isPrivate } = require('sdk/private-browsing');
exports.testIsPrivateOnTab = function(assert) {
let window = browserWindows.activeWindow;
let chromeWindow = getOwnerWindow(window);
assert.ok(chromeWindow instanceof Ci.nsIDOMWindow, 'associated window is found');
assert.ok(!isPrivate(chromeWindow), 'the top level window is not private');
let rawTab = openTab(chromeWindow, 'data:text/html,<h1>Hi!</h1>', {

View File

@ -6,7 +6,8 @@
const { Cc, Ci } = require('chrome');
const { Loader } = require('sdk/test/loader');
const { setTimeout } = require('sdk/timers');
const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
const { viewFor } = require('sdk/view/core');
const { getOwnerWindow } = require('sdk/tabs/utils');
const { windows, onFocus, getMostRecentBrowserWindow } = require('sdk/window/utils');
const { open, focus, close } = require('sdk/window/helpers');
const tabs = require('sdk/tabs');
@ -165,12 +166,12 @@ exports.testTabPropertiesInNewWindow = function(assert, done) {
});
let tabs = loader.require('sdk/tabs');
let { getOwnerWindow } = loader.require('sdk/private-browsing/window/utils');
let { viewFor } = loader.require('sdk/view/core');
let count = 0;
function onReadyOrLoad (tab) {
if (count++) {
close(getOwnerWindow(tab)).then(done).then(null, assert.fail);
close(getOwnerWindow(viewFor(tab))).then(done).then(null, assert.fail);
}
}
@ -368,6 +369,33 @@ exports.testTabMove = function(assert, done) {
}).then(null, assert.fail);
};
exports.testIgnoreClosing = function(assert, done) {
let originalWindow = browserWindows.activeWindow;
openBrowserWindow(function(window, browser) {
let url = "data:text/html;charset=utf-8,foobar";
assert.equal(tabs.length, 2, "should be two windows open each with one tab");
tabs.on('ready', function onReady(tab) {
tabs.removeListener('ready', onReady);
let win = tab.window;
assert.equal(win.tabs.length, 2, "should be two tabs in the new window");
assert.equal(tabs.length, 3, "should be three tabs in total");
tab.close(function() {
assert.equal(win.tabs.length, 1, "should be one tab in the new window");
assert.equal(tabs.length, 2, "should be two tabs in total");
originalWindow.once("activate", done);
close(window);
});
});
tabs.open(url);
});
};
// TEST: open tab with default options
exports.testOpen = function(assert, done) {
let url = "data:text/html;charset=utf-8,default";
@ -413,6 +441,8 @@ exports.testPinUnpin = function(assert, done) {
// TEST: open tab in background
exports.testInBackground = function(assert, done) {
assert.equal(tabs.length, 1, "Should be one tab");
let window = getMostRecentBrowserWindow();
let activeUrl = tabs.activeTab.url;
let url = "data:text/html;charset=utf-8,background";
@ -441,7 +471,7 @@ exports.testOpenInNewWindow = function(assert, done) {
url: url,
inNewWindow: true,
onReady: function(tab) {
let newWindow = getOwnerWindow(tab);
let newWindow = getOwnerWindow(viewFor(tab));
assert.equal(windows().length, startWindowCount + 1, "a new window was opened");
onFocus(newWindow).then(function() {
@ -466,7 +496,7 @@ exports.testOpenInNewWindowOnOpen = function(assert, done) {
url: url,
inNewWindow: true,
onOpen: function(tab) {
let newWindow = getOwnerWindow(tab);
let newWindow = getOwnerWindow(viewFor(tab));
onFocus(newWindow).then(function() {
assert.equal(windows().length, startWindowCount + 1, "a new window was opened");
@ -942,9 +972,11 @@ exports['test unique tab ids'] = function(assert, done) {
var one = openWindow(), two = openWindow();
all([one, two]).then(function(results) {
assert.notEqual(results[0].id, results[1].id, "tab Ids should not be equal.");
results[0].win.close();
results[1].win.close();
done();
results[0].win.close(function() {
results[1].win.close(function () {
done();
});
});
});
}

View File

@ -66,7 +66,7 @@ exports.testExecOptionsEnvironment = function (assert, done) {
'receives environment option');
done();
});
});
}).then(null, assert.fail);
};
exports.testExecOptionsTimeout = function (assert, done) {
@ -104,7 +104,7 @@ exports.testExecOptionsTimeout = function (assert, done) {
child.off('close', closeHandler);
done();
}
});
}).then(null, assert.fail);
};
exports.testExecFileCallbackSuccess = function (assert, done) {
@ -116,7 +116,7 @@ exports.testExecFileCallbackSuccess = function (assert, done) {
assert.equal(stdout.trim(), '--myargs -j -s'.trim(), 'passes in correct arguments');
done();
});
});
}).then(null, assert.fail);
};
exports.testExecFileCallbackError = function (assert, done) {
@ -143,7 +143,7 @@ exports.testExecFileOptionsEnvironment = function (assert, done) {
'receives environment option');
done();
});
});
}).then(null, assert.fail);
};
exports.testExecFileOptionsTimeout = function (assert, done) {
@ -181,7 +181,7 @@ exports.testExecFileOptionsTimeout = function (assert, done) {
child.off('close', closeHandler);
done();
}
});
}).then(null, assert.fail);
};
/**
@ -204,7 +204,7 @@ exports.testExecFileOptionsMaxBufferLargeStdOut = function (assert, done) {
});
stdoutChild.on('exit', exitHandler);
stdoutChild.on('close', closeHandler);
});
}).then(null, assert.fail);
function exitHandler (code, signal) {
assert.equal(code, null, 'Exit code is null in exit handler');
@ -239,7 +239,7 @@ exports.testExecFileOptionsMaxBufferLargeStdOErr = function (assert, done) {
});
stderrChild.on('exit', exitHandler);
stderrChild.on('close', closeHandler);
});
}).then(null, assert.fail);
function exitHandler (code, signal) {
assert.equal(code, null, 'Exit code is null in exit handler');
@ -280,7 +280,7 @@ exports.testExecFileOptionsMaxBufferSmallStdOut = function (assert, done) {
});
stdoutChild.on('exit', exitHandler);
stdoutChild.on('close', closeHandler);
});
}).then(null, assert.fail);
function exitHandler (code, signal) {
// Sometimes the buffer limit is hit before the process closes successfully
@ -331,7 +331,7 @@ exports.testExecFileOptionsMaxBufferSmallStdErr = function (assert, done) {
});
stderrChild.on('exit', exitHandler);
stderrChild.on('close', closeHandler);
});
}).then(null, assert.fail);
function exitHandler (code, signal) {
// Sometimes the buffer limit is hit before the process closes successfully
@ -377,7 +377,7 @@ exports.testChildExecFileKillSignal = function (assert, done) {
assert.equal(err.signal, 'beepbeep', 'correctly used custom killSignal');
done();
});
});
}).then(null, assert.fail);
};
exports.testChildProperties = function (assert, done) {
@ -390,8 +390,7 @@ exports.testChildProperties = function (assert, done) {
assert.ok(true, 'Windows environment does not have `pid`');
else
assert.ok(child.pid > 0, 'Child has a pid');
done();
});
}).then(done, assert.fail);
};
exports.testChildStdinStreamLarge = function (assert, done) {
@ -509,7 +508,7 @@ exports.testSpawnOptions = function (assert, done) {
envChild.on('close', envClose);
cwdChild.on('close', cwdClose);
});
}).then(null, assert.fail);
function envClose () {
assert.equal(envStdout.trim(), 'my-value-test', 'spawn correctly passed in ENV');

View File

@ -90,8 +90,7 @@ function comparePixelImages(imageA, imageB, callback) {
compared = pixels;
this.emit("draw-image", imageB);
} else {
callback(compared === pixels);
tab.close()
tab.close(callback.bind(null, compared === pixels))
}
});

View File

@ -986,7 +986,7 @@ exports['test panel CSS'] = function(assert, done) {
getActiveView(panel).querySelector('iframe').contentWindow;
let panel = Panel({
contentURL: 'data:text/html;charset=utf-8,' +
contentURL: 'data:text/html;charset=utf-8,' +
'<div style="background: silver">css test</div>',
contentStyle: 'div { height: 100px; }',
contentStyleFile: CSS_URL,
@ -999,7 +999,7 @@ exports['test panel CSS'] = function(assert, done) {
loader.unload();
done();
}).then(null, assert.fail);
}).then(null, assert.fail);
}
});
@ -1016,7 +1016,7 @@ exports['test panel CSS list'] = function(assert, done) {
getActiveView(panel).querySelector('iframe').contentWindow;
let panel = Panel({
contentURL: 'data:text/html;charset=utf-8,' +
contentURL: 'data:text/html;charset=utf-8,' +
'<div style="width:320px; max-width: 480px!important">css test</div>',
contentStyleFile: [
// Highlight evaluation order in this list
@ -1049,8 +1049,7 @@ exports['test panel CSS list'] = function(assert, done) {
'add-on author/page author stylesheet !important precedence works');
loader.unload();
done();
}).then(null, assert.fail);
}).then(done, assert.fail);
}
});
@ -1065,12 +1064,12 @@ if (isWindowPBSupported) {
toolbar: true,
chrome: true,
private: true
} }).then(function(window) {
} }).then(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);
})
return window;
}).then(close).then(done).then(null, assert.fail);
}
}
else if (isGlobalPBSupported) {
@ -1084,7 +1083,7 @@ else if (isGlobalPBSupported) {
open(null, { features: {
toolbar: true,
chrome: true
} }).then(function(window) {
} }).then(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');

View File

@ -12,7 +12,6 @@ const { isWindowPrivate } = winUtils;
const { isPrivateBrowsingSupported } = require('sdk/self');
const { is } = require('sdk/system/xul-app');
const { isPrivate } = require('sdk/private-browsing');
const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
const { LoaderWithHookedConsole } = require("sdk/test/loader");
const { getMode, isGlobalPBSupported,
isWindowPBSupported, isTabPBSupported } = require('sdk/private-browsing/utils');
@ -84,34 +83,6 @@ exports.testIsPrivateBrowsingFalseDefault = function(assert) {
'isPrivateBrowsingSupported property is false by default');
};
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 {
assert.strictEqual(chromeWindow, getOwnerWindow(tab), 'associated window is the same for window and window\'s tab');
}
// test that the tab is not private
// private flag should be ignored by default
assert.ok(!isPrivate(tab));
assert.ok(!isPrivate(getOwnerWindow(tab)));
tab.close(done);
}
});
};
exports.testNSIPrivateBrowsingChannel = function(assert) {
let channel = Services.io.newChannel("about:blank", null, null);
channel.QueryInterface(Ci.nsIPrivateBrowsingChannel);

View File

@ -28,7 +28,7 @@ const tabs = require("sdk/tabs");
const { setTabURL } = require("sdk/tabs/utils");
const { getActiveTab, getTabContentWindow, closeTab } = require("sdk/tabs/utils")
const { getMostRecentBrowserWindow } = require("sdk/window/utils");
const { open: openNewWindow } = require("sdk/window/helpers");
const { open: openNewWindow, close: closeWindow } = require("sdk/window/helpers");
const { Loader } = require("sdk/test/loader");
const { setTimeout } = require("sdk/timers");
const { Cu } = require("chrome");
@ -698,13 +698,13 @@ exports["test Selection Listener"] = function(assert, done) {
selection.once("select", function() {
assert.equal(selection.text, "fo");
close();
loader.unload();
done();
});
open(URL).then(selectContentFirstDiv).
then(dispatchSelectionEvent).
then(close).
then(loader.unload, assert.fail);
then(dispatchSelectionEvent, assert.fail);
};
exports["test Textarea OnSelect Listener"] = function(assert, done) {
@ -713,13 +713,13 @@ exports["test Textarea OnSelect Listener"] = function(assert, done) {
selection.once("select", function() {
assert.equal(selection.text, "noodles");
close();
loader.unload();
done();
});
open(URL).then(selectTextarea).
then(dispatchOnSelectEvent).
then(close).
then(loader.unload, assert.fail);
then(dispatchOnSelectEvent, assert.fail);
};
exports["test Selection listener removed on unload"] = function(assert, done) {
@ -769,14 +769,14 @@ exports["test Selection Listener on existing document"] = function(assert, done)
selection.once("select", function() {
assert.equal(selection.text, "fo");
close();
loader.unload();
done();
});
return window;
}).then(selectContentFirstDiv).
then(dispatchSelectionEvent).
then(close).
then(loader.unload, assert.fail);
then(dispatchSelectionEvent, assert.fail);
};
@ -788,14 +788,14 @@ exports["test Textarea OnSelect Listener on existing document"] = function(asser
selection.once("select", function() {
assert.equal(selection.text, "noodles");
close();
loader.unload();
done();
});
return window;
}).then(selectTextarea).
then(dispatchOnSelectEvent).
then(close).
then(loader.unload, assert.fail);
then(dispatchOnSelectEvent, assert.fail);
};
exports["test Selection Listener on document reload"] = function(assert, done) {
@ -804,15 +804,15 @@ exports["test Selection Listener on document reload"] = function(assert, done) {
selection.once("select", function() {
assert.equal(selection.text, "fo");
close();
loader.unload();
done();
});
open(URL).
then(reload).
then(selectContentFirstDiv).
then(dispatchSelectionEvent).
then(close).
then(loader.unload, assert.fail);
then(dispatchSelectionEvent, assert.fail);
};
exports["test Textarea OnSelect Listener on document reload"] = function(assert, done) {
@ -821,15 +821,15 @@ exports["test Textarea OnSelect Listener on document reload"] = function(assert,
selection.once("select", function() {
assert.equal(selection.text, "noodles");
close();
loader.unload();
done();
});
open(URL).
then(reload).
then(selectTextarea).
then(dispatchOnSelectEvent).
then(close).
then(loader.unload, assert.fail);
then(dispatchOnSelectEvent, assert.fail);
};
exports["test Selection Listener on frame"] = function(assert, done) {
@ -884,7 +884,7 @@ exports["test PBPW Selection Listener"] = function(assert, done) {
open(URL, {private: true}).
then(selectContentFirstDiv).
then(dispatchSelectionEvent).
then(close).
then(closeWindow).
then(loader.unload).
then(done, assert.fail);
};
@ -902,7 +902,7 @@ exports["test PBPW Textarea OnSelect Listener"] = function(assert, done) {
open(URL, {private: true}).
then(selectTextarea).
then(dispatchOnSelectEvent).
then(close).
then(closeWindow).
then(loader.unload).
then(done, assert.fail);
};
@ -931,7 +931,7 @@ exports["test PBPW Single DOM Selection"] = function(assert, done) {
"No iterable selection in PBPW");
return window;
}).then(close).then(loader.unload).then(done, assert.fail);
}).then(closeWindow).then(loader.unload).then(done, assert.fail);
};
exports["test PBPW Textarea Selection"] = function(assert, done) {
@ -964,7 +964,7 @@ exports["test PBPW Textarea Selection"] = function(assert, done) {
"No iterable selection in PBPW");
return window;
}).then(close).then(loader.unload).then(done, assert.fail);
}).then(closeWindow).then(loader.unload).then(done, assert.fail);
};
// TODO: test Selection Listener on long-held connection (Bug 661884)

View File

@ -172,4 +172,4 @@ exports["test modelFor(xulTab)"] = (assert, done) => {
});
};
require("test").run(exports);
require("sdk/test").run(exports);

View File

@ -563,33 +563,34 @@ exports.testDestroyEdgeCaseBug = function(assert, done) {
sidebar.show();
assert.pass('showing the sidebar');
});
});
}
exports.testClickingACheckedMenuitem = function(assert, done) {
const { Sidebar } = require('sdk/ui/sidebar');
let testName = 'testClickingACheckedMenuitem';
let window = getMostRecentBrowserWindow();
const testName = 'testClickingACheckedMenuitem';
let sidebar = Sidebar({
id: testName,
title: testName,
url: 'data:text/html;charset=utf-8,'+testName,
});
assert.pass('sidebar was created');
sidebar.show().then(function() {
assert.pass('the show callback works');
open().then(focus).then(window => {
return sidebar.show().then(_ => {
assert.pass('the show callback works');
sidebar.once('hide', function() {
assert.pass('clicking the menuitem after the sidebar has shown hides it.');
sidebar.destroy();
done();
sidebar.once('hide', _ => {
assert.pass('clicking the menuitem after the sidebar has shown hides it.');
sidebar.destroy();
close(window).then(done, assert.fail);
});
let menuitem = window.document.getElementById(makeID(sidebar.id));
simulateCommand(menuitem);
});
let menuitem = window.document.getElementById(makeID(sidebar.id));
simulateCommand(menuitem);
});
}).catch(assert.fail);
};
exports.testTitleSetter = function(assert, done) {

View File

@ -90,37 +90,30 @@ exports['test close on unload'] = function(assert) {
};
exports.testWindowTracker = function(assert, done) {
var myWindow;
var finished = false;
var myWindow = makeEmptyWindow();
assert.pass('window was created');
var delegate = {
onTrack: function(window) {
if (window == myWindow) {
assert.pass("onTrack() called with our test window");
timer.setTimeout(function() myWindow.close());
}
},
onUntrack: function(window) {
if (window == myWindow) {
assert.pass("onUntrack() called with our test window");
timer.setTimeout(function() {
if (!finished) {
finished = true;
myWindow = null;
wt.unload();
done();
}
else {
assert.fail("finishTest() called multiple times.");
}
});
}
}
};
myWindow.addEventListener("load", function onload() {
myWindow.removeEventListener("load", onload, false);
assert.pass("test window has opened");
// test bug 638007 (new is optional), using new
var wt = new windowUtils.WindowTracker(delegate);
myWindow = makeEmptyWindow();
// test bug 638007 (new is optional), using new
var wt = new windowUtils.WindowTracker({
onTrack: window => {
if (window === myWindow) {
assert.pass("onTrack() called with our test window");
close(window);
}
},
onUntrack: window => {
if (window === myWindow) {
assert.pass("onUntrack() called with our test window");
wt.unload();
timer.setTimeout(done);
}
}
});
}, false);
};
exports['test window watcher untracker'] = function(assert, done) {

View File

@ -562,7 +562,12 @@ setUpdateTrackingId();
enabled = Services.prefs.getBoolPref('layers.composer2d.enabled');
} else {
#ifdef MOZ_WIDGET_GONK
enabled = (libcutils.property_get('ro.display.colorfill') === '1');
let androidVersion = libcutils.property_get("ro.build.version.sdk");
if (androidVersion >= 17 ) {
enabled = true;
} else {
enabled = (libcutils.property_get('ro.display.colorfill') === '1');
}
#endif
}
navigator.mozSettings.createLock().set({'layers.composer2d.enabled': enabled });

View File

@ -19,13 +19,13 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c1a8cbaac1d921cfb50e3a2600720b75cf5afabd"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e8a1eedb848382323254edbd234baecebc5dc5ca"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="ca283b9db2b151d465cfd2e19346cf58fe89e413"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="d5800c36b2d5822fc3fe1899b9280401de466e1e"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3691614d0045f7968addce45d4140fb360c3ceaf"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3512d982f336887a73283bb8d1147a8f7b822077"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>

View File

@ -17,10 +17,10 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c1a8cbaac1d921cfb50e3a2600720b75cf5afabd"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="e8a1eedb848382323254edbd234baecebc5dc5ca"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3691614d0045f7968addce45d4140fb360c3ceaf"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3512d982f336887a73283bb8d1147a8f7b822077"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things -->

View File

@ -15,14 +15,14 @@
<project name="platform_build" path="build" remote="b2g" revision="65fba428f8d76336b33ddd9e15900357953600ba">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c1a8cbaac1d921cfb50e3a2600720b75cf5afabd"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="e8a1eedb848382323254edbd234baecebc5dc5ca"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3691614d0045f7968addce45d4140fb360c3ceaf"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3512d982f336887a73283bb8d1147a8f7b822077"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>

View File

@ -19,13 +19,13 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c1a8cbaac1d921cfb50e3a2600720b75cf5afabd"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e8a1eedb848382323254edbd234baecebc5dc5ca"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="ca283b9db2b151d465cfd2e19346cf58fe89e413"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="d5800c36b2d5822fc3fe1899b9280401de466e1e"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3691614d0045f7968addce45d4140fb360c3ceaf"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3512d982f336887a73283bb8d1147a8f7b822077"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>

View File

@ -18,10 +18,10 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c1a8cbaac1d921cfb50e3a2600720b75cf5afabd"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="e8a1eedb848382323254edbd234baecebc5dc5ca"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3691614d0045f7968addce45d4140fb360c3ceaf"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3512d982f336887a73283bb8d1147a8f7b822077"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things -->

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "95766b5f4b8108d3524431422ccf744e11797497",
"revision": "3207323b68e65e12f8281ccb22e88fb3a59bb391",
"repo_path": "/integration/gaia-central"
}

View File

@ -17,12 +17,12 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c1a8cbaac1d921cfb50e3a2600720b75cf5afabd"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e8a1eedb848382323254edbd234baecebc5dc5ca"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3691614d0045f7968addce45d4140fb360c3ceaf"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3512d982f336887a73283bb8d1147a8f7b822077"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
<project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>

View File

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c1a8cbaac1d921cfb50e3a2600720b75cf5afabd"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e8a1eedb848382323254edbd234baecebc5dc5ca"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>

View File

@ -19,12 +19,12 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c1a8cbaac1d921cfb50e3a2600720b75cf5afabd"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e8a1eedb848382323254edbd234baecebc5dc5ca"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3691614d0045f7968addce45d4140fb360c3ceaf"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3512d982f336887a73283bb8d1147a8f7b822077"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
<project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>

View File

@ -17,12 +17,12 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c1a8cbaac1d921cfb50e3a2600720b75cf5afabd"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e8a1eedb848382323254edbd234baecebc5dc5ca"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3691614d0045f7968addce45d4140fb360c3ceaf"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3512d982f336887a73283bb8d1147a8f7b822077"/>
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>

View File

@ -17,10 +17,10 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c1a8cbaac1d921cfb50e3a2600720b75cf5afabd"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="e8a1eedb848382323254edbd234baecebc5dc5ca"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3691614d0045f7968addce45d4140fb360c3ceaf"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3512d982f336887a73283bb8d1147a8f7b822077"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things -->

View File

@ -17,12 +17,12 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c1a8cbaac1d921cfb50e3a2600720b75cf5afabd"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e8a1eedb848382323254edbd234baecebc5dc5ca"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f313e6d3aaaefe8c82eaed15912a09b120fb7260"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3691614d0045f7968addce45d4140fb360c3ceaf"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3512d982f336887a73283bb8d1147a8f7b822077"/>
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>

View File

@ -119,8 +119,8 @@
<command id="Social:SharePage" oncommand="SocialShare.sharePage();" disabled="true"/>
<command id="Social:ToggleSidebar" oncommand="SocialSidebar.toggleSidebar();" hidden="true"/>
<command id="Social:ToggleNotifications" oncommand="Social.toggleNotifications();" hidden="true"/>
<command id="Social:FocusChat" oncommand="SocialChatBar.focus();" hidden="true" disabled="true"/>
<command id="Social:Addons" oncommand="BrowserOpenAddonsMgr('addons://list/service');"/>
<command id="Chat:Focus" oncommand="Cu.import('resource:///modules/Chat.jsm', {}).Chat.focus(window);"/>
</commandset>
<commandset id="placesCommands">
@ -380,7 +380,15 @@
#endif
<!--<key id="markPage" key="&markPageCmd.commandkey;" command="Social:TogglePageMark" modifiers="accel,shift"/>-->
<key id="focusChatBar" key="&social.chatBar.commandkey;" command="Social:FocusChat" modifiers="accel,shift"/>
<key id="focusChatBar" key="&social.chatBar.commandkey;" command="Chat:Focus"
#ifdef XP_MACOSX
# Sadly the devtools uses shift-accel-c on non-mac and alt-accel-c everywhere else
# So we just use the other
modifiers="accel,shift"
#else
modifiers="accel,alt"
#endif
/>
<key id="key_stop" keycode="VK_ESCAPE" command="Browser:Stop"/>

View File

@ -4,7 +4,6 @@
// the "exported" symbols
let SocialUI,
SocialChatBar,
SocialFlyout,
SocialMarks,
SocialShare,
@ -173,7 +172,6 @@ SocialUI = {
_providersChanged: function() {
SocialSidebar.clearProviderMenus();
SocialSidebar.update();
SocialChatBar.update();
SocialShare.populateProviderMenu();
SocialStatus.populateToolbarPalette();
SocialMarks.populateToolbarPalette();
@ -297,45 +295,6 @@ SocialUI = {
}
}
SocialChatBar = {
get chatbar() {
return document.getElementById("pinnedchats");
},
// Whether the chatbar is available for this window. Note that in full-screen
// mode chats are available, but not shown.
get isAvailable() {
return SocialUI.enabled;
},
// Does this chatbar have any chats (whether minimized, collapsed or normal)
get hasChats() {
return !!this.chatbar.firstElementChild;
},
openChat: function(aProvider, aURL, aCallback, aMode) {
this.update();
if (!this.isAvailable)
return false;
this.chatbar.openChat(aProvider, aURL, aCallback, aMode);
// We only want to focus the chat if it is as a result of user input.
let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
if (dwu.isHandlingUserInput)
this.chatbar.focus();
return true;
},
update: function() {
let command = document.getElementById("Social:FocusChat");
if (!this.isAvailable) {
this.chatbar.hidden = command.hidden = true;
} else {
this.chatbar.hidden = command.hidden = false;
}
command.setAttribute("disabled", command.hidden ? "true" : "false");
},
focus: function SocialChatBar_focus() {
this.chatbar.focus();
}
}
SocialFlyout = {
get panel() {
return document.getElementById("social-flyout-panel");

View File

@ -1061,9 +1061,6 @@ var gBrowserInit = {
LightweightThemeListener.init();
WebrtcIndicator.init();
// Ensure login manager is up and running.
Services.logins;
#ifdef MOZ_CRASHREPORTER
if (gMultiProcessBrowser)
TabCrashReporter.init();

View File

@ -21,7 +21,7 @@
<xul:panel id="sponsored-panel" orient="vertical" type="arrow">
<xul:description id="sponsored-panel-release-descr">&newtab.sponsored.release.message;</xul:description>
<xul:description id="sponsored-panel-trial-descr">&newtab.sponsored.trial.message;</xul:description>
<xul:description id="sponsored-panel-trial-descr">&newtab.sponsored.trial.message2;</xul:description>
<xul:label class="text-link"
href="https://support.mozilla.org/kb/how-do-sponsored-tiles-work"
value="&newtab.panel.link.text;" />

View File

@ -30,50 +30,43 @@
<implementation implements="nsIDOMEventListener">
<constructor><![CDATA[
let Social = Components.utils.import("resource:///modules/Social.jsm", {}).Social;
this.content.__defineGetter__("popupnotificationanchor",
() => document.getAnonymousElementByAttribute(this, "anonid", "notification-icon"));
Social.setErrorListener(this.content, function(aBrowser) {
aBrowser.webNavigation.loadURI("about:socialerror?mode=compactInfo&origin=" +
encodeURIComponent(aBrowser.getAttribute("origin")),
null, null, null, null);
});
if (!this.chatbar) {
document.getAnonymousElementByAttribute(this, "anonid", "minimize").hidden = true;
document.getAnonymousElementByAttribute(this, "anonid", "close").hidden = true;
}
let contentWindow = this.contentWindow;
// process this._callbacks, then set to null so the chatbox creator
// knows to make new callbacks immediately.
if (this._callbacks) {
for (let callback of this._callbacks) {
callback(this);
}
this._callbacks = null;
}
this.addEventListener("DOMContentLoaded", function DOMContentLoaded(event) {
if (event.target != this.contentDocument)
return;
this.removeEventListener("DOMContentLoaded", DOMContentLoaded, true);
this.isActive = !this.minimized;
// process this._callbacks, then set to null so the chatbox creator
// knows to make new callbacks immediately.
if (this._callbacks) {
for (let callback of this._callbacks) {
if (callback)
callback(contentWindow);
}
this._callbacks = null;
}
// content can send a socialChatActivity event to have the UI update.
let chatActivity = function() {
this.setAttribute("activity", true);
if (this.chatbar)
this.chatbar.updateTitlebar(this);
}.bind(this);
contentWindow.addEventListener("socialChatActivity", chatActivity);
contentWindow.addEventListener("unload", function unload() {
contentWindow.removeEventListener("unload", unload);
contentWindow.removeEventListener("socialChatActivity", chatActivity);
});
this._deferredChatLoaded.resolve(this);
}, true);
if (this.src)
this.setAttribute("src", this.src);
]]></constructor>
<field name="_deferredChatLoaded" readonly="true">
Promise.defer();
</field>
<property name="promiseChatLoaded">
<getter>
return this._deferredChatLoaded.promise;
</getter>
</property>
<field name="content" readonly="true">
document.getAnonymousElementByAttribute(this, "anonid", "content");
</field>
@ -133,7 +126,7 @@
this.contentDocument.documentElement.dispatchEvent(evt);
</setter>
</property>
<method name="showNotifications">
<body><![CDATA[
PopupNotifications._reshowNotifications(this.content.popupnotificationanchor,
@ -148,15 +141,7 @@
aTarget.src = this.src;
aTarget.content.setAttribute("origin", this.content.getAttribute("origin"));
aTarget.content.popupnotificationanchor.className = this.content.popupnotificationanchor.className;
this.content.socialErrorListener.remove();
aTarget.content.socialErrorListener.remove();
this.content.swapDocShells(aTarget.content);
Social.setErrorListener(this.content, function(aBrowser) {}); // 'this' will be destroyed soon.
Social.setErrorListener(aTarget.content, function(aBrowser) {
aBrowser.webNavigation.loadURI("about:socialerror?mode=compactInfo&origin=" +
encodeURIComponent(aBrowser.getAttribute("origin")),
null, null, null, null);
});
]]></body>
</method>
@ -186,31 +171,40 @@
<method name="swapWindows">
<body><![CDATA[
let provider = Social._getProviderFromOrigin(this.content.getAttribute("origin"));
let deferred = Promise.defer();
let title = this.getAttribute("label");
if (this.chatbar) {
this.chatbar.detachChatbox(this, { "centerscreen": "yes" }, win => {
win.document.title = provider.name;
});
this.chatbar.detachChatbox(this, { "centerscreen": "yes" }).then(
chatbox => {
chatbox.contentWindow.document.title = title;
deferred.resolve(chatbox);
}
);
} else {
// attach this chatbox to the topmost browser window
let findChromeWindowForChats = Cu.import("resource://gre/modules/MozSocialAPI.jsm").findChromeWindowForChats;
let win = findChromeWindowForChats();
let chatbar = win.SocialChatBar.chatbar;
chatbar.openChat(provider, "about:blank", win => {
let cb = chatbar.selectedChat;
this.swapDocShells(cb);
let Chat = Cu.import("resource:///modules/Chat.jsm").Chat;
let win = Chat.findChromeWindowForChats();
let chatbar = win.document.getElementById("pinnedchats");
let origin = this.content.getAttribute("origin");
let cb = chatbar.openChat(origin, title, "about:blank");
cb.promiseChatLoaded.then(
() => {
this.swapDocShells(cb);
// chatboxForURL is a map of URL -> chatbox used to avoid opening
// duplicate chat windows. Ensure reattached chat windows aren't
// registered with about:blank as their URL, otherwise reattaching
// more than one chat window isn't possible.
chatbar.chatboxForURL.delete("about:blank");
chatbar.chatboxForURL.set(this.src, Cu.getWeakReference(cb));
// chatboxForURL is a map of URL -> chatbox used to avoid opening
// duplicate chat windows. Ensure reattached chat windows aren't
// registered with about:blank as their URL, otherwise reattaching
// more than one chat window isn't possible.
chatbar.chatboxForURL.delete("about:blank");
chatbar.chatboxForURL.set(this.src, Cu.getWeakReference(cb));
chatbar.focus();
this.close();
});
chatbar.focus();
this.close();
deferred.resolve(cb);
}
);
}
return deferred.promise;
]]></body>
</method>
@ -510,7 +504,6 @@
<method name="_remove">
<parameter name="aChatbox"/>
<body><![CDATA[
aChatbox.content.socialErrorListener.remove();
this.removeChild(aChatbox);
// child might have been collapsed.
let menuitem = this.menuitemMap.get(aChatbox);
@ -522,22 +515,12 @@
]]></body>
</method>
<method name="removeAll">
<body><![CDATA[
this.selectedChat = null;
while (this.firstElementChild) {
this._remove(this.firstElementChild);
}
// and the nub/popup must also die.
this.nub.collapsed = true;
]]></body>
</method>
<method name="openChat">
<parameter name="aProvider"/>
<parameter name="aOrigin"/>
<parameter name="aTitle"/>
<parameter name="aURL"/>
<parameter name="aCallback"/>
<parameter name="aMode"/>
<parameter name="aCallback"/>
<body><![CDATA[
let cb = this.chatboxForURL.get(aURL);
if (cb) {
@ -546,30 +529,35 @@
this.showChat(cb, aMode);
if (aCallback) {
if (cb._callbacks == null) {
// DOMContentLoaded has already fired, so callback now.
aCallback(cb.contentWindow);
// Chatbox has already been created, so callback now.
aCallback(cb);
} else {
// DOMContentLoaded for this chat is yet to fire...
// Chatbox is yet to have bindings created...
cb._callbacks.push(aCallback);
}
}
return;
return cb;
}
this.chatboxForURL.delete(aURL);
}
cb = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "chatbox");
// _callbacks is a javascript property instead of a <field> as it
// must exist before the (possibly delayed) bindings are created.
cb._callbacks = [aCallback];
cb._callbacks = [];
if (aCallback) {
// _callbacks is a javascript property instead of a <field> as it
// must exist before the (possibly delayed) bindings are created.
cb._callbacks.push(aCallback);
}
// src also a javascript property; the src attribute is set in the ctor.
cb.src = aURL;
if (aMode == "minimized")
cb.setAttribute("minimized", "true");
cb.setAttribute("origin", aProvider.origin);
cb.setAttribute("origin", aOrigin);
cb.setAttribute("label", aTitle);
this.insertBefore(cb, this.firstChild);
this.selectedChat = cb;
this.chatboxForURL.set(aURL, Cu.getWeakReference(cb));
this.resize();
return cb;
]]></body>
</method>
@ -648,12 +636,14 @@
]]></body>
</method>
<!-- Moves a chatbox to a new window. -->
<!-- Moves a chatbox to a new window. Returns a promise that is resolved
once the move to the other window is complete.
-->
<method name="detachChatbox">
<parameter name="aChatbox"/>
<parameter name="aOptions"/>
<parameter name="aCallback"/>
<body><![CDATA[
let deferred = Promise.defer();
let options = "";
for (let name in aOptions)
options += "," + name + "=" + aOptions[name];
@ -669,9 +659,9 @@
let otherChatbox = otherWin.document.getElementById("chatter");
aChatbox.swapDocShells(otherChatbox);
aChatbox.close();
if (aCallback)
aCallback(otherWin);
deferred.resolve(otherChatbox);
}, true);
return deferred.promise;
]]></body>
</method>
@ -750,11 +740,12 @@
let top = Math.min(Math.max(eY, sY.value),
sY.value + sHeight.value - winHeight);
let provider = Social._getProviderFromOrigin(draggedChat.content.getAttribute("origin"));
this.detachChatbox(draggedChat, { screenX: left, screenY: top }, win => {
win.document.title = provider.name;
});
let title = draggedChat.content.getAttribute("title");
this.detachChatbox(draggedChat, { screenX: left, screenY: top }).then(
chatbox => {
chatbox.contentWindow.document.title = title;
}
);
event.stopPropagation();
]]></handler>
</handlers>

View File

@ -0,0 +1,8 @@
[DEFAULT]
support-files =
head.js
chat.html
[browser_chatwindow.js]
[browser_focus.js]
[browser_tearoff.js]

View File

@ -0,0 +1,135 @@
/* 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/. */
let chatbar = document.getElementById("pinnedchats");
add_chat_task(function* testOpenCloseChat() {
let chatbox = yield promiseOpenChat("http://example.com");
Assert.strictEqual(chatbox, chatbar.selectedChat);
// we requested a "normal" chat, so shouldn't be minimized
Assert.ok(!chatbox.minimized, "chat is not minimized");
Assert.equal(chatbar.childNodes.length, 1, "should be 1 chat open");
// now request the same URL again - we should get the same chat.
let chatbox2 = yield promiseOpenChat("http://example.com");
Assert.strictEqual(chatbox2, chatbox, "got the same chat");
Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
chatbox.toggle();
is(chatbox.minimized, true, "chat is now minimized");
// was no other chat to select, so selected becomes null.
is(chatbar.selectedChat, null);
// We check the content gets an unload event as we close it.
let promiseClosed = promiseOneEvent(chatbox.content, "unload", true);
chatbox.close();
yield promiseClosed;
});
// In this case we open a chat minimized, then request the same chat again
// without specifying minimized. On that second call the chat should open,
// selected, and no longer minimized.
add_chat_task(function* testMinimized() {
let chatbox = yield promiseOpenChat("http://example.com", "minimized");
Assert.strictEqual(chatbox, chatbar.selectedChat);
Assert.ok(chatbox.minimized, "chat is minimized");
Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
yield promiseOpenChat("http://example.com");
Assert.ok(!chatbox.minimized, false, "chat is no longer minimized");
});
// open enough chats to overflow the window, then check
// if the menupopup is visible
add_chat_task(function* testManyChats() {
Assert.ok(chatbar.menupopup.parentNode.collapsed, "popup nub collapsed at start");
// we should *never* find a test box that needs more than this to cause
// an overflow!
let maxToOpen = 20;
let numOpened = 0;
for (let i = 0; i < maxToOpen; i++) {
yield promiseOpenChat("http://example.com#" + i);
if (!chatbar.menupopup.parentNode.collapsed) {
info("the menu popup appeared");
return;
}
}
Assert.ok(false, "We didn't find a collapsed chat after " + maxToOpen + "chats!");
});
// Check that closeAll works as expected.
add_chat_task(function* testOpenTwiceCallbacks() {
yield promiseOpenChat("http://example.com#1");
yield promiseOpenChat("http://example.com#2");
yield promiseOpenChat("http://test2.example.com");
Assert.equal(numChatsInWindow(window), 3, "should be 3 chats open");
Chat.closeAll("http://example.com");
Assert.equal(numChatsInWindow(window), 1, "should have closed 2 chats");
Chat.closeAll("http://test2.example.com");
Assert.equal(numChatsInWindow(window), 0, "should have closed last chat");
});
// Check that when we open the same chat twice, the callbacks are called back
// twice.
add_chat_task(function* testOpenTwiceCallbacks() {
yield promiseOpenChatCallback("http://example.com");
yield promiseOpenChatCallback("http://example.com");
});
// Bug 817782 - check chats work in new top-level windows.
add_chat_task(function* testSecondTopLevelWindow() {
const chatUrl = "http://example.com";
let secondWindow = OpenBrowserWindow();
yield promiseOneEvent(secondWindow, "load");
yield promiseOpenChat(chatUrl);
// the chat was created - let's make sure it was created in the second window.
Assert.equal(numChatsInWindow(window), 0, "main window has no chats");
Assert.equal(numChatsInWindow(secondWindow), 1, "second window has 1 chat");
secondWindow.close();
});
// Test that chats are created in the correct window.
add_chat_task(function* testChatWindowChooser() {
let chat = yield promiseOpenChat("http://example.com");
Assert.equal(numChatsInWindow(window), 1, "first window has the chat");
// create a second window - this will be the "most recent" and will
// therefore be the window that hosts the new chat (see bug 835111)
let secondWindow = OpenBrowserWindow();
yield promiseOneEvent(secondWindow, "load");
Assert.equal(numChatsInWindow(secondWindow), 0, "second window starts with no chats");
yield promiseOpenChat("http://example.com#2");
Assert.equal(numChatsInWindow(secondWindow), 1, "second window now has chats");
Assert.equal(numChatsInWindow(window), 1, "first window still has 1 chat");
chat.close();
Assert.equal(numChatsInWindow(window), 0, "first window now has no chats");
// now open another chat - it should still open in the second.
yield promiseOpenChat("http://example.com#3");
Assert.equal(numChatsInWindow(window), 0, "first window still has no chats");
Assert.equal(numChatsInWindow(secondWindow), 2, "second window has both chats");
// focus the first window, and open yet another chat - it
// should open in the first window.
window.focus();
yield promiseWaitForFocus();
chat = yield promiseOpenChat("http://example.com#4");
Assert.equal(numChatsInWindow(window), 1, "first window got new chat");
chat.close();
Assert.equal(numChatsInWindow(window), 0, "first window has no chats");
let privateWindow = OpenBrowserWindow({private: true});
yield promiseOneEvent(privateWindow, "load")
// open a last chat - the focused window can't accept
// chats (it's a private window), so the chat should open
// in the window that was selected before. This is known
// to be broken on Linux.
chat = yield promiseOpenChat("http://example.com#5");
let os = Services.appinfo.OS;
const BROKEN_WM_Z_ORDER = os != "WINNT" && os != "Darwin";
let fn = BROKEN_WM_Z_ORDER ? todo : ok;
fn(numChatsInWindow(window) == 1, "first window got the chat");
chat.close();
privateWindow.close();
secondWindow.close();
});

View File

@ -0,0 +1,230 @@
/* 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/. */
// Tests the focus functionality.
const CHAT_URL = "https://example.com/browser/browser/base/content/test/chat/chat.html";
// Is the currently opened tab focused?
function isTabFocused() {
let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab);
return Services.focus.focusedWindow == tabb.contentWindow;
}
// Is the specified chat focused?
function isChatFocused(chat) {
return chat.chatbar._isChatFocused(chat);
}
let chatbar = document.getElementById("pinnedchats");
function* setUp() {
// Note that (probably) due to bug 604289, if a tab is focused but the
// focused element is null, our chat windows can "steal" focus. This is
// avoided if we explicitly focus an element in the tab.
// So we load a page with an <input> field and focus that before testing.
let html = '<input id="theinput"><button id="chat-opener"></button>';
let url = "data:text/html;charset=utf-8," + encodeURI(html);
let tab = gBrowser.selectedTab = gBrowser.addTab(url, {skipAnimation: true});
yield promiseOneEvent(tab.linkedBrowser, "load", true);
tab.linkedBrowser.contentDocument.getElementById("theinput").focus();
registerCleanupFunction(function() {
gBrowser.removeTab(tab);
});
}
// Test default focus - not user input.
add_chat_task(function* testDefaultFocus() {
yield setUp();
let chat = yield promiseOpenChat("http://example.com");
// we used the default focus behaviour, which means that because this was
// not the direct result of user action the chat should not be focused.
Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
Assert.ok(isTabFocused(), "the tab should remain focused.");
Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
});
// Test default focus via user input.
add_chat_task(function* testDefaultFocus() {
yield setUp();
let tab = gBrowser.selectedTab;
let deferred = Promise.defer();
let button = tab.linkedBrowser.contentDocument.getElementById("chat-opener");
button.addEventListener("click", function onclick() {
button.removeEventListener("click", onclick);
promiseOpenChat("http://example.com").then(
chat => deferred.resolve(chat)
);
})
// Note we must use synthesizeMouseAtCenter() rather than calling
// .click() directly as this causes nsIDOMWindowUtils.isHandlingUserInput
// to be true.
EventUtils.synthesizeMouseAtCenter(button, {}, button.ownerDocument.defaultView);
let chat = yield deferred.promise;
// we use the default focus behaviour but the chat was opened via user input,
// so the chat should be focused.
Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
Assert.ok(!isTabFocused(), "the tab should have lost focus.");
Assert.ok(isChatFocused(chat), "the chat should have got focus.");
});
// We explicitly ask for the chat to be focused.
add_chat_task(function* testExplicitFocus() {
yield setUp();
let chat = yield promiseOpenChat("http://example.com", undefined, true);
// we use the default focus behaviour, which means that because this was
// not the direct result of user action the chat should not be focused.
Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
Assert.ok(!isTabFocused(), "the tab should have lost focus.");
Assert.ok(isChatFocused(chat), "the chat should have got focus.");
});
// Open a minimized chat via default focus behaviour - it will open and not
// have focus. Then open the same chat without 'minimized' - it will be
// restored but should still not have grabbed focus.
add_chat_task(function* testNoFocusOnAutoRestore() {
yield setUp();
let chat = yield promiseOpenChat("http://example.com", "minimized");
Assert.ok(chat.minimized, "chat is minimized");
Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
Assert.ok(isTabFocused(), "the tab should remain focused.");
Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
yield promiseOpenChat("http://example.com");
Assert.ok(!chat.minimized, "chat should be restored");
Assert.ok(isTabFocused(), "the tab should remain focused.");
Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
});
// Here we open a chat, which will not be focused. Then we minimize it and
// restore it via a titlebar clock - it should get focus at that point.
add_chat_task(function* testFocusOnExplicitRestore() {
yield setUp();
let chat = yield promiseOpenChat("http://example.com");
Assert.ok(!chat.minimized, "chat should have been opened restored");
Assert.ok(isTabFocused(), "the tab should remain focused.");
Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
chat.minimized = true;
Assert.ok(isTabFocused(), "tab should still be focused");
Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
let promise = promiseOneEvent(chat.contentWindow, "focus");
// pretend we clicked on the titlebar
chat.onTitlebarClick({button: 0});
yield promise; // wait for focus event.
Assert.ok(!chat.minimized, "chat should have been restored");
Assert.ok(isChatFocused(chat), "chat should be focused");
Assert.strictEqual(chat, chatbar.selectedChat, "chat is marked selected");
});
// Open 2 chats and give 1 focus. Minimize the focused one - the second
// should get focus.
add_chat_task(function* testMinimizeFocused() {
yield setUp();
let chat1 = yield promiseOpenChat("http://example.com#1");
let chat2 = yield promiseOpenChat("http://example.com#2");
Assert.equal(numChatsInWindow(window), 2, "2 chats open");
Assert.strictEqual(chatbar.selectedChat, chat2, "chat2 is selected");
let promise = promiseOneEvent(chat1.contentWindow, "focus");
chatbar.selectedChat = chat1;
chatbar.focus();
yield promise; // wait for chat1 to get focus.
Assert.strictEqual(chat1, chatbar.selectedChat, "chat1 is marked selected");
Assert.notStrictEqual(chat2, chatbar.selectedChat, "chat2 is not marked selected");
promise = promiseOneEvent(chat2.contentWindow, "focus");
chat1.minimized = true;
yield promise; // wait for chat2 to get focus.
Assert.notStrictEqual(chat1, chatbar.selectedChat, "chat1 is not marked selected");
Assert.strictEqual(chat2, chatbar.selectedChat, "chat2 is marked selected");
});
// Open 2 chats, select and focus the second. Pressing the TAB key should
// cause focus to move between all elements in our chat window before moving
// to the next chat window.
add_chat_task(function* testTab() {
yield setUp();
function sendTabAndWaitForFocus(chat, eltid) {
let doc = chat.contentDocument;
EventUtils.sendKey("tab");
// ideally we would use the 'focus' event here, but that doesn't work
// as expected for the iframe - the iframe itself never gets the focus
// event (apparently the sub-document etc does.)
// So just poll for the correct element getting focus...
let deferred = Promise.defer();
let tries = 0;
let interval = setInterval(function() {
if (tries >= 30) {
clearInterval(interval);
deferred.reject("never got focus");
return;
}
tries ++;
let elt = eltid ? doc.getElementById(eltid) : doc.documentElement;
if (doc.activeElement == elt) {
clearInterval(interval);
deferred.resolve();
}
info("retrying wait for focus: " + tries);
info("(the active element is " + doc.activeElement + "/" + doc.activeElement.getAttribute("id") + ")");
}, 100);
info("waiting for element " + eltid + " to get focus");
return deferred.promise;
}
let chat1 = yield promiseOpenChat(CHAT_URL + "#1");
let chat2 = yield promiseOpenChat(CHAT_URL + "#2");
chatbar.selectedChat = chat2;
let promise = promiseOneEvent(chat2.contentWindow, "focus");
chatbar.focus();
info("waiting for second chat to get focus");
yield promise;
// Our chats have 3 focusable elements, so it takes 4 TABs to move
// to the new chat.
yield sendTabAndWaitForFocus(chat2, "input1");
Assert.equal(chat2.contentDocument.activeElement.getAttribute("id"), "input1",
"first input field has focus");
Assert.ok(isChatFocused(chat2), "new chat still focused after first tab");
yield sendTabAndWaitForFocus(chat2, "input2");
Assert.ok(isChatFocused(chat2), "new chat still focused after tab");
Assert.equal(chat2.contentDocument.activeElement.getAttribute("id"), "input2",
"second input field has focus");
yield sendTabAndWaitForFocus(chat2, "iframe");
Assert.ok(isChatFocused(chat2), "new chat still focused after tab");
Assert.equal(chat2.contentDocument.activeElement.getAttribute("id"), "iframe",
"iframe has focus");
// this tab now should move to the next chat, but focus the
// document element itself (hence the null eltid)
yield sendTabAndWaitForFocus(chat1, null);
Assert.ok(isChatFocused(chat1), "first chat is focused");
});
// Open a chat and focus an element other than the first. Move focus to some
// other item (the tab itself in this case), then focus the chatbar - the
// same element that was previously focused should still have focus.
add_chat_task(function* testFocusedElement() {
yield setUp();
// open a chat with focus requested.
let chat = yield promiseOpenChat(CHAT_URL, undefined, true);
chat.contentDocument.getElementById("input2").focus();
// set focus to the tab.
let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab);
let promise = promiseOneEvent(tabb.contentWindow, "focus");
Services.focus.moveFocus(tabb.contentWindow, null, Services.focus.MOVEFOCUS_ROOT, 0);
yield promise;
promise = promiseOneEvent(chat.contentWindow, "focus");
chatbar.focus();
yield promise;
Assert.equal(chat.contentDocument.activeElement.getAttribute("id"), "input2",
"correct input field still has focus");
});

View File

@ -0,0 +1,128 @@
/* 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/. */
let chatbar = document.getElementById("pinnedchats");
function promiseNewWindowLoaded() {
let deferred = Promise.defer();
Services.wm.addListener({
onWindowTitleChange: function() {},
onCloseWindow: function(xulwindow) {},
onOpenWindow: function(xulwindow) {
var domwindow = xulwindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindow);
Services.wm.removeListener(this);
// wait for load to ensure the window is ready for us to test
domwindow.addEventListener("load", function _load(event) {
let doc = domwindow.document;
if (event.target != doc)
return;
domwindow.removeEventListener("load", _load);
deferred.resolve(domwindow);
});
},
});
return deferred.promise;
}
add_chat_task(function* testTearoffChat() {
let chatbox = yield promiseOpenChat("http://example.com");
Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
let chatDoc = chatbox.contentDocument;
let chatTitle = chatDoc.title;
Assert.equal(chatbox.getAttribute("label"), chatTitle,
"the new chatbox should show the title of the chat window");
// mutate the chat document a bit before we tear it off.
let div = chatDoc.createElement("div");
div.setAttribute("id", "testdiv");
div.setAttribute("test", "1");
chatDoc.body.appendChild(div);
// chatbox is open, lets detach. The new chat window will be caught in
// the window watcher below
let promise = promiseNewWindowLoaded();
let swap = document.getAnonymousElementByAttribute(chatbox, "anonid", "swap");
swap.click();
// and wait for the new window.
let domwindow = yield promise;
Assert.equal(domwindow.document.documentElement.getAttribute("windowtype"), "Social:Chat", "Social:Chat window opened");
Assert.equal(numChatsInWindow(window), 0, "should be no chats in the chat bar");
// get the chatbox from the new window.
chatbox = domwindow.document.getElementById("chatter")
Assert.equal(chatbox.getAttribute("label"), chatTitle, "window should have same title as chat");
div = chatbox.contentDocument.getElementById("testdiv");
Assert.equal(div.getAttribute("test"), "1", "docshell should have been swapped");
div.setAttribute("test", "2");
// swap the window back to the chatbar
promise = promiseOneEvent(domwindow, "unload");
swap = domwindow.document.getAnonymousElementByAttribute(chatbox, "anonid", "swap");
swap.click();
yield promise;
Assert.equal(numChatsInWindow(window), 1, "chat should be docked back in the window");
chatbox = chatbar.selectedChat;
Assert.equal(chatbox.getAttribute("label"), chatTitle,
"the new chatbox should show the title of the chat window again");
div = chatbox.contentDocument.getElementById("testdiv");
Assert.equal(div.getAttribute("test"), "2", "docshell should have been swapped");
});
// Similar test but with 2 chats.
add_chat_task(function* testReattachTwice() {
let chatbox1 = yield promiseOpenChat("http://example.com#1");
let chatbox2 = yield promiseOpenChat("http://example.com#2");
Assert.equal(numChatsInWindow(window), 2, "both chats should be docked in the window");
info("chatboxes are open, detach from window");
let promise = promiseNewWindowLoaded();
document.getAnonymousElementByAttribute(chatbox1, "anonid", "swap").click();
let domwindow1 = yield promise;
chatbox1 = domwindow1.document.getElementById("chatter");
Assert.equal(numChatsInWindow(window), 1, "only second chat should be docked in the window");
promise = promiseNewWindowLoaded();
document.getAnonymousElementByAttribute(chatbox2, "anonid", "swap").click();
let domwindow2 = yield promise;
chatbox2 = domwindow2.document.getElementById("chatter");
Assert.equal(numChatsInWindow(window), 0, "should be no docked chats");
promise = promiseOneEvent(domwindow2, "unload");
domwindow2.document.getAnonymousElementByAttribute(chatbox2, "anonid", "swap").click();
yield promise;
Assert.equal(numChatsInWindow(window), 1, "one chat should be docked back in the window");
promise = promiseOneEvent(domwindow1, "unload");
domwindow1.document.getAnonymousElementByAttribute(chatbox1, "anonid", "swap").click();
yield promise;
Assert.equal(numChatsInWindow(window), 2, "both chats should be docked back in the window");
});
// Check that Chat.closeAll() also closes detached windows.
add_chat_task(function* testCloseAll() {
let chatbox1 = yield promiseOpenChat("http://example.com#1");
let chatbox2 = yield promiseOpenChat("http://example.com#2");
let promise = promiseNewWindowLoaded();
document.getAnonymousElementByAttribute(chatbox1, "anonid", "swap").click();
let domwindow = yield promise;
chatbox1 = domwindow.document.getElementById("chatter");
let promiseWindowUnload = promiseOneEvent(domwindow, "unload");
Assert.equal(numChatsInWindow(window), 1, "second chat should still be docked");
Chat.closeAll("http://example.com");
yield promiseWindowUnload;
Assert.equal(numChatsInWindow(window), 0, "should be no chats left");
});

View File

@ -0,0 +1,14 @@
<html>
<head>
<meta charset="utf-8">
<title>test chat window</title>
</head>
<body>
<p>This is a test chat window.</p>
<!-- a couple of input fields to help with focus testing -->
<input id="input1"/>
<input id="input2"/>
<!-- an iframe here so this one page generates multiple load events -->
<iframe id="iframe" src="data:text/plain:this is an iframe"></iframe>
</body>
</html>

View File

@ -0,0 +1,91 @@
/* 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/. */
// Utility functions for Chat tests.
let Chat = Cu.import("resource:///modules/Chat.jsm", {}).Chat;
function promiseOpenChat(url, mode, focus) {
let uri = Services.io.newURI(url, null, null);
let origin = uri.prePath;
let title = origin;
let deferred = Promise.defer();
// we just through a few hoops to ensure the content document is fully
// loaded, otherwise tests that rely on that content may intermittently fail.
let callback = function(chatbox) {
if (chatbox.contentDocument.readyState == "complete") {
// already loaded.
deferred.resolve(chatbox);
return;
}
chatbox.addEventListener("load", function onload(event) {
if (event.target != chatbox.contentDocument || chatbox.contentDocument.location.href == "about:blank") {
return;
}
chatbox.removeEventListener("load", onload, true);
deferred.resolve(chatbox);
}, true);
}
let chatbox = Chat.open(null, origin, title, url, mode, focus, callback);
return deferred.promise;
}
// Opens a chat, returns a promise resolved when the chat callback fired.
function promiseOpenChatCallback(url, mode) {
let uri = Services.io.newURI(url, null, null);
let origin = uri.prePath;
let title = origin;
let deferred = Promise.defer();
let callback = deferred.resolve;
Chat.open(null, origin, title, url, mode, undefined, callback);
return deferred.promise;
}
// Opens a chat, returns the chat window's promise which fires when the chat
// starts loading.
function promiseOneEvent(target, eventName, capture) {
let deferred = Promise.defer();
target.addEventListener(eventName, function handler(event) {
target.removeEventListener(eventName, handler, capture);
deferred.resolve();
}, capture);
return deferred.promise;
}
// Return the number of chats in a browser window.
function numChatsInWindow(win) {
let chatbar = win.document.getElementById("pinnedchats");
return chatbar.childElementCount;
}
function promiseWaitForFocus() {
let deferred = Promise.defer();
waitForFocus(deferred.resolve);
return deferred.promise;
}
// A simple way to clean up after each test.
function add_chat_task(genFunction) {
add_task(function* () {
info("Starting chat test " + genFunction.name);
try {
yield genFunction();
} finally {
info("Finished chat test " + genFunction.name + " - cleaning up.");
// close all docked chats.
while (chatbar.childNodes.length) {
chatbar.childNodes[0].close();
}
// and non-docked chats.
let winEnum = Services.wm.getEnumerator("Social:Chat");
while (winEnum.hasMoreElements()) {
let win = winEnum.getNext();
if (win.closed) {
continue;
}
win.close();
}
}
});
}

View File

@ -26,7 +26,6 @@ support-files =
[browser_addons.js]
[browser_blocklist.js]
[browser_chat_tearoff.js]
[browser_defaults.js]
[browser_share.js]
[browser_social_activation.js]

View File

@ -1,308 +0,0 @@
/* 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/. */
function test() {
requestLongerTimeout(2); // only debug builds seem to need more time...
waitForExplicitFinish();
let manifest = { // normal provider
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
};
let postSubTest = function(cb) {
let chats = document.getElementById("pinnedchats");
ok(chats.children.length == 0, "no chatty children left behind");
cb();
};
runSocialTestWithProvider(manifest, function (finishcb) {
SocialSidebar.show();
ok(SocialSidebar.provider, "sidebar provider exists");
runSocialTests(tests, undefined, postSubTest, function() {
finishcb();
});
});
}
var tests = {
testTearoffChat: function(next) {
let chats = document.getElementById("pinnedchats");
let chatTitle;
let port = SocialSidebar.provider.getWorkerPort();
ok(port, "provider has a port");
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "got-sidebar-message":
port.postMessage({topic: "test-chatbox-open"});
break;
case "got-chatbox-visibility":
// chatbox is open, lets detach. The new chat window will be caught in
// the window watcher below
let doc = chats.selectedChat.contentDocument;
// This message is (sometimes!) received a second time
// before we start our tests from the onCloseWindow
// callback.
if (doc.location == "about:blank")
return;
chatTitle = doc.title;
ok(chats.selectedChat.getAttribute("label") == chatTitle,
"the new chatbox should show the title of the chat window");
let div = doc.createElement("div");
div.setAttribute("id", "testdiv");
div.setAttribute("test", "1");
doc.body.appendChild(div);
let swap = document.getAnonymousElementByAttribute(chats.selectedChat, "anonid", "swap");
swap.click();
port.close();
break;
case "got-chatbox-message":
ok(true, "got chatbox message");
ok(e.data.result == "ok", "got chatbox windowRef result: "+e.data.result);
chats.selectedChat.toggle();
break;
}
}
Services.wm.addListener({
onWindowTitleChange: function() {},
onCloseWindow: function(xulwindow) {},
onOpenWindow: function(xulwindow) {
var domwindow = xulwindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindow);
Services.wm.removeListener(this);
// wait for load to ensure the window is ready for us to test
domwindow.addEventListener("load", function _load(event) {
let doc = domwindow.document;
if (event.target != doc)
return;
domwindow.removeEventListener("load", _load, false);
domwindow.addEventListener("unload", function _close(event) {
if (event.target != doc)
return;
domwindow.removeEventListener("unload", _close, false);
info("window has been closed");
waitForCondition(function() {
return chats.selectedChat && chats.selectedChat.contentDocument &&
chats.selectedChat.contentDocument.readyState == "complete";
},function () {
ok(chats.selectedChat, "should have a chatbox in our window again");
ok(chats.selectedChat.getAttribute("label") == chatTitle,
"the new chatbox should show the title of the chat window again");
let testdiv = chats.selectedChat.contentDocument.getElementById("testdiv");
is(testdiv.getAttribute("test"), "2", "docshell should have been swapped");
chats.selectedChat.close();
waitForCondition(function() {
return chats.children.length == 0;
},function () {
next();
});
});
}, false);
is(doc.documentElement.getAttribute("windowtype"), "Social:Chat", "Social:Chat window opened");
// window is loaded, but the docswap does not happen until after load,
// and we have no event to wait on, so we'll wait for document state
// to be ready
let chatbox = doc.getElementById("chatter");
waitForCondition(function() {
return chats.selectedChat == null &&
chatbox.contentDocument &&
chatbox.contentDocument.readyState == "complete";
},function() {
ok(chatbox.getAttribute("label") == chatTitle,
"detached window should show the title of the chat window");
let testdiv = chatbox.contentDocument.getElementById("testdiv");
is(testdiv.getAttribute("test"), "1", "docshell should have been swapped");
testdiv.setAttribute("test", "2");
// swap the window back to the chatbar
let swap = doc.getAnonymousElementByAttribute(chatbox, "anonid", "swap");
swap.click();
}, domwindow);
}, false);
}
});
port.postMessage({topic: "test-init", data: { id: 1 }});
},
testCloseOnLogout: function(next) {
let chats = document.getElementById("pinnedchats");
const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
let port = SocialSidebar.provider.getWorkerPort();
ok(port, "provider has a port");
port.postMessage({topic: "test-init"});
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "got-chatbox-visibility":
// chatbox is open, lets detach. The new chat window will be caught in
// the window watcher below
let doc = chats.selectedChat.contentDocument;
// This message is (sometimes!) received a second time
// before we start our tests from the onCloseWindow
// callback.
if (doc.location == "about:blank")
return;
info("chatbox is open, detach from window");
let swap = document.getAnonymousElementByAttribute(chats.selectedChat, "anonid", "swap");
swap.click();
break;
}
}
Services.wm.addListener({
onWindowTitleChange: function() {},
onCloseWindow: function(xulwindow) {},
onOpenWindow: function(xulwindow) {
let domwindow = xulwindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindow);
Services.wm.removeListener(this);
// wait for load to ensure the window is ready for us to test, make sure
// we're not getting called for about:blank
domwindow.addEventListener("load", function _load(event) {
let doc = domwindow.document;
if (event.target != doc)
return;
domwindow.removeEventListener("load", _load, false);
domwindow.addEventListener("unload", function _close(event) {
if (event.target != doc)
return;
domwindow.removeEventListener("unload", _close, false);
ok(true, "window has been closed");
next();
}, false);
is(doc.documentElement.getAttribute("windowtype"), "Social:Chat", "Social:Chat window opened");
// window is loaded, but the docswap does not happen until after load,
// and we have no event to wait on, so we'll wait for document state
// to be ready
let chatbox = doc.getElementById("chatter");
waitForCondition(function() {
return chats.children.length == 0 &&
chatbox.contentDocument &&
chatbox.contentDocument.readyState == "complete";
},function() {
// logout, we should get unload next
port.postMessage({topic: "test-logout"});
port.close();
}, domwindow);
}, false);
}
});
port.postMessage({topic: "test-worker-chat", data: chatUrl});
},
testReattachTwice: function(next) {
let chats = document.getElementById("pinnedchats");
const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
let chatBoxCount = 0, reattachCount = 0;
let port = SocialSidebar.provider.getWorkerPort();
ok(port, "provider has a port");
port.postMessage({topic: "test-init"});
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "got-chatbox-visibility":
// chatbox is open, lets detach. The new chat window will be caught in
// the window watcher below
let doc = chats.selectedChat.contentDocument;
// This message is (sometimes!) received a second time
// before we start our tests from the onCloseWindow
// callback.
if (doc.location == "about:blank")
return;
if (++chatBoxCount != 2) {
// open the second chat window
port.postMessage({topic: "test-worker-chat", data: chatUrl + "?id=2"});
return;
}
info("chatbox is open, detach from window");
let chat1 = chats.firstChild;
let chat2 = chat1.nextSibling;
document.getAnonymousElementByAttribute(chat1, "anonid", "swap").click();
document.getAnonymousElementByAttribute(chat2, "anonid", "swap").click();
break;
}
};
let firstChatWindowDoc;
Services.wm.addListener({
onWindowTitleChange: function() {},
onCloseWindow: function(xulwindow) {},
onOpenWindow: function(xulwindow) {
let listener = this;
let domwindow = xulwindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindow);
// wait for load to ensure the window is ready for us to test, make sure
// we're not getting called for about:blank
domwindow.addEventListener("load", function _load(event) {
let doc = domwindow.document;
if (event.target != doc)
return;
domwindow.removeEventListener("load", _load, false);
domwindow.addEventListener("unload", function _close(event) {
if (event.target != doc)
return;
domwindow.removeEventListener("unload", _close, false);
ok(true, "window has been closed");
waitForCondition(function() {
return chats.selectedChat && chats.selectedChat.contentDocument &&
chats.selectedChat.contentDocument.readyState == "complete";
}, function () {
++reattachCount;
if (reattachCount == 1) {
info("reattaching second chat window");
let chatbox = firstChatWindowDoc.getElementById("chatter");
firstChatWindowDoc.getAnonymousElementByAttribute(chatbox, "anonid", "swap").click();
firstChatWindowDoc = null;
}
else if (reattachCount == 2) {
is(chats.children.length, 2, "both chat windows should be reattached");
chats.removeAll();
waitForCondition(() => chats.children.length == 0, function () {
info("no chat window left");
is(chats.chatboxForURL.size, 0, "chatboxForURL map should be empty");
next();
});
}
}, "waited too long for the window to reattach");
}, false);
is(doc.documentElement.getAttribute("windowtype"), "Social:Chat", "Social:Chat window opened");
if (!firstChatWindowDoc) {
firstChatWindowDoc = doc;
return;
}
Services.wm.removeListener(listener);
// window is loaded, but the docswap does not happen until after load,
// and we have no event to wait on, so we'll wait for document state
// to be ready
let chatbox = doc.getElementById("chatter");
waitForCondition(function() {
return chats.children.length == 0 &&
chatbox.contentDocument &&
chatbox.contentDocument.readyState == "complete";
},function() {
info("reattaching chat window");
doc.getAnonymousElementByAttribute(chatbox, "anonid", "swap").click();
}, "waited too long for the chat window to be detached");
}, false);
}
});
port.postMessage({topic: "test-worker-chat", data: chatUrl + "?id=1"});
}
};

View File

@ -44,6 +44,10 @@ function openChat(provider, callback) {
gURLsNotRemembered.push(url);
}
function windowHasChats(win) {
return !!getChatBar().firstElementChild;
}
function test() {
requestLongerTimeout(2); // only debug builds seem to need more time...
waitForExplicitFinish();
@ -107,85 +111,6 @@ var tests = {
}
port.postMessage({topic: "test-init", data: { id: 1 }});
},
testOpenMinimized: function(next) {
// In this case the sidebar opens a chat (without specifying minimized).
// We then minimize it and have the sidebar reopen the chat (again without
// minimized). On that second call the chat should open and no longer
// be minimized.
let chats = document.getElementById("pinnedchats");
let port = SocialSidebar.provider.getWorkerPort();
let seen_opened = false;
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "test-init-done":
port.postMessage({topic: "test-chatbox-open"});
break;
case "chatbox-opened":
is(e.data.result, "ok", "the sidebar says it got a chatbox");
if (!seen_opened) {
// first time we got the opened message, so minimize the chat then
// re-request the same chat to be opened - we should get the
// message again and the chat should be restored.
ok(!chats.selectedChat.minimized, "chat not initially minimized")
chats.selectedChat.minimized = true
seen_opened = true;
port.postMessage({topic: "test-chatbox-open"});
} else {
// This is the second time we've seen this message - there should
// be exactly 1 chat open and it should no longer be minimized.
let chats = document.getElementById("pinnedchats");
ok(!chats.selectedChat.minimized, "chat no longer minimized")
chats.selectedChat.close();
is(chats.selectedChat, null, "should only have been one chat open");
port.close();
next();
}
}
}
port.postMessage({topic: "test-init", data: { id: 1 }});
},
testManyChats: function(next) {
// open enough chats to overflow the window, then check
// if the menupopup is visible
let port = SocialSidebar.provider.getWorkerPort();
let chats = document.getElementById("pinnedchats");
ok(port, "provider has a port");
ok(chats.menupopup.parentNode.collapsed, "popup nub collapsed at start");
port.postMessage({topic: "test-init"});
// we should *never* find a test box that needs more than this to cause
// an overflow!
let maxToOpen = 20;
let numOpened = 0;
let maybeOpenAnother = function() {
if (numOpened++ >= maxToOpen) {
ok(false, "We didn't find a collapsed chat after " + maxToOpen + "chats!");
closeAllChats();
next();
}
port.postMessage({topic: "test-chatbox-open", data: { id: numOpened }});
}
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "got-chatbox-message":
if (!chats.menupopup.parentNode.collapsed) {
maybeOpenAnother();
break;
}
ok(true, "popup nub became visible");
// close our chats now
while (chats.selectedChat) {
chats.selectedChat.close();
}
ok(!chats.selectedChat, "chats are all closed");
port.close();
next();
break;
}
}
maybeOpenAnother();
},
testWorkerChatWindow: function(next) {
const chatUrl = SocialSidebar.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
let chats = document.getElementById("pinnedchats");
@ -239,61 +164,10 @@ var tests = {
}
port.postMessage({topic: "test-init", data: { id: 1 }});
},
testSameChatCallbacks: function(next) {
let chats = document.getElementById("pinnedchats");
let port = SocialSidebar.provider.getWorkerPort();
let seen_opened = false;
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "test-init-done":
port.postMessage({topic: "test-chatbox-open"});
break;
case "chatbox-opened":
is(e.data.result, "ok", "the sidebar says it got a chatbox");
if (seen_opened) {
// This is the second time we've seen this message - there should
// be exactly 1 chat open.
let chats = document.getElementById("pinnedchats");
chats.selectedChat.close();
is(chats.selectedChat, null, "should only have been one chat open");
port.close();
next();
} else {
// first time we got the opened message, so re-request the same
// chat to be opened - we should get the message again.
seen_opened = true;
port.postMessage({topic: "test-chatbox-open"});
}
}
}
port.postMessage({topic: "test-init", data: { id: 1 }});
},
// check removeAll does the right thing
testRemoveAll: function(next, mode) {
let port = SocialSidebar.provider.getWorkerPort();
port.postMessage({topic: "test-init"});
get3ChatsForCollapsing(mode || "normal", function() {
let chatbar = window.SocialChatBar.chatbar;
chatbar.removeAll();
// should be no evidence of any chats left.
is(chatbar.childNodes.length, 0, "should be no chats left");
checkPopup();
is(chatbar.selectedChat, null, "nothing should be selected");
is(chatbar.chatboxForURL.size, 0, "chatboxForURL map should be empty");
port.close();
next();
});
},
testRemoveAllMinimized: function(next) {
this.testRemoveAll(next, "minimized");
},
// Check what happens when you close the only visible chat.
testCloseOnlyVisible: function(next) {
let chatbar = window.SocialChatBar.chatbar;
let chatbar = getChatBar();
let chatWidth = undefined;
let num = 0;
is(chatbar.childNodes.length, 0, "chatbar starting empty");
@ -331,7 +205,7 @@ var tests = {
let port = SocialSidebar.provider.getWorkerPort();
port.postMessage({topic: "test-init"});
get3ChatsForCollapsing("normal", function(first, second, third) {
let chatbar = window.SocialChatBar.chatbar;
let chatbar = getChatBar();
chatbar.showChat(first);
ok(!first.collapsed, "first should no longer be collapsed");
ok(second.collapsed || third.collapsed, false, "one of the others should be collapsed");
@ -341,61 +215,6 @@ var tests = {
});
},
testActivity: function(next) {
let port = SocialSidebar.provider.getWorkerPort();
port.postMessage({topic: "test-init"});
get3ChatsForCollapsing("normal", function(first, second, third) {
let chatbar = window.SocialChatBar.chatbar;
is(chatbar.selectedChat, third, "third chat should be selected");
ok(!chatbar.selectedChat.hasAttribute("activity"), "third chat should have no activity");
// send an activity message to the second.
ok(!second.hasAttribute("activity"), "second chat should have no activity");
let chat2 = second.content;
let evt = chat2.contentDocument.createEvent("CustomEvent");
evt.initCustomEvent("socialChatActivity", true, true, {});
chat2.contentDocument.documentElement.dispatchEvent(evt);
// second should have activity.
ok(second.hasAttribute("activity"), "second chat should now have activity");
// select the second - it should lose "activity"
chatbar.selectedChat = second;
ok(!second.hasAttribute("activity"), "second chat should no longer have activity");
// Now try the first - it is collapsed, so the 'nub' also gets activity attr.
ok(!first.hasAttribute("activity"), "first chat should have no activity");
let chat1 = first.content;
let evt = chat1.contentDocument.createEvent("CustomEvent");
evt.initCustomEvent("socialChatActivity", true, true, {});
chat1.contentDocument.documentElement.dispatchEvent(evt);
ok(first.hasAttribute("activity"), "first chat should now have activity");
ok(chatbar.nub.hasAttribute("activity"), "nub should also have activity");
// first is collapsed, so use openChat to get it.
chatbar.openChat(SocialSidebar.provider, first.getAttribute("src"));
ok(!first.hasAttribute("activity"), "first chat should no longer have activity");
// The nub should lose the activity flag here too
todo(!chatbar.nub.hasAttribute("activity"), "Bug 806266 - nub should no longer have activity");
// TODO: tests for bug 806266 should arrange to have 2 chats collapsed
// then open them checking the nub is updated correctly.
// Now we will go and change the embedded browser in the second chat and
// ensure the activity magic still works (ie, check that the unload for
// the browser didn't cause our event handlers to be removed.)
ok(!second.hasAttribute("activity"), "second chat should have no activity");
let subiframe = chat2.contentDocument.getElementById("iframe");
subiframe.contentWindow.addEventListener("unload", function subunload() {
subiframe.contentWindow.removeEventListener("unload", subunload);
// ensure all other unload listeners have fired.
executeSoon(function() {
let evt = chat2.contentDocument.createEvent("CustomEvent");
evt.initCustomEvent("socialChatActivity", true, true, {});
chat2.contentDocument.documentElement.dispatchEvent(evt);
ok(second.hasAttribute("activity"), "second chat still has activity after unloading sub-iframe");
closeAllChats();
port.close();
next();
})
})
subiframe.setAttribute("src", "data:text/plain:new location for iframe");
});
},
testOnlyOneCallback: function(next) {
let chats = document.getElementById("pinnedchats");
let port = SocialSidebar.provider.getWorkerPort();
@ -413,7 +232,7 @@ var tests = {
case "pong":
executeSoon(function() {
is(numOpened, 1, "only got one open message");
chats.removeAll();
chats.selectedChat.close();
port.close();
next();
});
@ -422,86 +241,6 @@ var tests = {
port.postMessage({topic: "test-init", data: { id: 1 }});
},
testSecondTopLevelWindow: function(next) {
// Bug 817782 - check chats work in new top-level windows.
const chatUrl = SocialSidebar.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
let port = SocialSidebar.provider.getWorkerPort();
let secondWindow;
port.onmessage = function(e) {
if (e.data.topic == "test-init-done") {
secondWindow = OpenBrowserWindow();
secondWindow.addEventListener("load", function loadListener() {
secondWindow.removeEventListener("load", loadListener);
port.postMessage({topic: "test-worker-chat", data: chatUrl});
});
} else if (e.data.topic == "got-chatbox-message") {
// the chat was created - let's make sure it was created in the second window.
is(secondWindow.SocialChatBar.chatbar.childElementCount, 1);
secondWindow.close();
next();
}
}
port.postMessage({topic: "test-init"});
},
testChatWindowChooser: function(next) {
// Tests that when a worker creates a chat, it is opened in the correct
// window.
// open a chat (it will open in the main window)
ok(!window.SocialChatBar.hasChats, "first window should start with no chats");
openChat(SocialSidebar.provider, function() {
ok(window.SocialChatBar.hasChats, "first window has the chat");
// create a second window - this will be the "most recent" and will
// therefore be the window that hosts the new chat (see bug 835111)
let secondWindow = OpenBrowserWindow();
secondWindow.addEventListener("load", function loadListener() {
secondWindow.removeEventListener("load", loadListener);
ok(!secondWindow.SocialChatBar.hasChats, "second window has no chats");
openChat(SocialSidebar.provider, function() {
ok(secondWindow.SocialChatBar.hasChats, "second window now has chats");
is(window.SocialChatBar.chatbar.childElementCount, 1, "first window still has 1 chat");
window.SocialChatBar.chatbar.removeAll();
// now open another chat - it should still open in the second.
openChat(SocialSidebar.provider, function() {
ok(!window.SocialChatBar.hasChats, "first window has no chats");
ok(secondWindow.SocialChatBar.hasChats, "second window has a chat");
// focus the first window, and open yet another chat - it
// should open in the first window.
waitForFocus(function() {
openChat(SocialSidebar.provider, function() {
ok(window.SocialChatBar.hasChats, "first window has chats");
window.SocialChatBar.chatbar.removeAll();
ok(!window.SocialChatBar.hasChats, "first window has no chats");
let privateWindow = OpenBrowserWindow({private: true});
privateWindow.addEventListener("load", function loadListener() {
privateWindow.removeEventListener("load", loadListener);
// open a last chat - the focused window can't accept
// chats (it's a private window), so the chat should open
// in the window that was selected before. This is known
// to be broken on Linux.
openChat(SocialSidebar.provider, function() {
let os = Services.appinfo.OS;
const BROKEN_WM_Z_ORDER = os != "WINNT" && os != "Darwin";
let fn = BROKEN_WM_Z_ORDER ? todo : ok;
fn(window.SocialChatBar.hasChats, "first window has a chat");
window.SocialChatBar.chatbar.removeAll();
privateWindow.close();
secondWindow.close();
next();
});
});
});
});
window.focus();
});
});
})
});
},
testMultipleProviderChat: function(next) {
// test incomming chats from all providers
openChat(Social.providers[0], function() {
@ -517,7 +256,7 @@ var tests = {
port.postMessage({topic: "test-logout"});
waitForCondition(function() chats.children.length == Social.providers.length - 1,
function() {
chats.removeAll();
closeAllChats();
waitForCondition(function() chats.children.length == 0,
function() {
ok(!chats.selectedChat, "multiprovider chats are all closed");

View File

@ -9,7 +9,7 @@ function isTabFocused() {
}
function isChatFocused(chat) {
return SocialChatBar.chatbar._isChatFocused(chat);
return getChatBar()._isChatFocused(chat);
}
function openChatViaUser() {
@ -32,7 +32,7 @@ function openChatViaSidebarMessage(port, data, callback) {
function openChatViaWorkerMessage(port, data, callback) {
// sadly there is no message coming back to tell us when the chat has
// been opened, so we wait until one appears.
let chatbar = SocialChatBar.chatbar;
let chatbar = getChatBar();
let numExpected = chatbar.childElementCount + 1;
port.postMessage({topic: "test-worker-chat", data: data});
waitForCondition(function() chatbar.childElementCount == numExpected,
@ -40,12 +40,13 @@ function openChatViaWorkerMessage(port, data, callback) {
// so the child has been added, but we don't know if it
// has been intialized - re-request it and the callback
// means it's done. Minimized, same as the worker.
SocialChatBar.openChat(SocialSidebar.provider,
data,
function() {
callback();
},
"minimized");
chatbar.openChat(SocialSidebar.provider.origin,
SocialSidebar.provider.name,
data,
"minimized",
function() {
callback();
});
},
"No new chat appeared");
}
@ -109,7 +110,7 @@ function test() {
waitForCondition(function() isTabFocused(), cb, "tab should have focus");
}
let postSubTest = function(cb) {
window.SocialChatBar.chatbar.removeAll();
closeAllChats();
cb();
}
// and run the tests.
@ -132,21 +133,22 @@ var tests = {
// Then we do it again - should still not be focused.
// Then we perform a user-initiated request - it should get focus.
testNoFocusWhenViaWorker: function(next) {
let chatbar = getChatBar();
startTestAndWaitForSidebar(function(port) {
openChatViaSidebarMessage(port, {stealFocus: 1}, function() {
ok(true, "got chatbox message");
is(SocialChatBar.chatbar.childElementCount, 1, "exactly 1 chat open");
is(chatbar.childElementCount, 1, "exactly 1 chat open");
ok(isTabFocused(), "tab should still be focused");
// re-request the same chat via a message.
openChatViaSidebarMessage(port, {stealFocus: 1}, function() {
is(SocialChatBar.chatbar.childElementCount, 1, "still exactly 1 chat open");
is(chatbar.childElementCount, 1, "still exactly 1 chat open");
ok(isTabFocused(), "tab should still be focused");
// re-request the same chat via user event.
openChatViaUser();
waitForCondition(function() isChatFocused(SocialChatBar.chatbar.selectedChat),
waitForCondition(function() isChatFocused(chatbar.selectedChat),
function() {
is(SocialChatBar.chatbar.childElementCount, 1, "still exactly 1 chat open");
is(SocialChatBar.chatbar.selectedChat, SocialChatBar.chatbar.firstElementChild, "chat should be selected");
is(chatbar.childElementCount, 1, "still exactly 1 chat open");
is(chatbar.selectedChat, chatbar.firstElementChild, "chat should be selected");
next();
}, "chat should be focused");
});
@ -158,204 +160,14 @@ var tests = {
// click. This should cause the new chat to be opened and focused.
testFocusWhenViaUser: function(next) {
startTestAndWaitForSidebar(function(port) {
let chatbar = getChatBar();
openChatViaUser();
ok(SocialChatBar.chatbar.firstElementChild, "chat opened");
waitForCondition(function() isChatFocused(SocialChatBar.chatbar.selectedChat),
ok(chatbar.firstElementChild, "chat opened");
waitForCondition(function() isChatFocused(chatbar.selectedChat),
function() {
is(SocialChatBar.chatbar.selectedChat, SocialChatBar.chatbar.firstElementChild, "chat is selected");
is(chatbar.selectedChat, chatbar.firstElementChild, "chat is selected");
next();
}, "chat should be focused");
});
},
// Open a chat via the worker - it will open and not have focus.
// Then open the same chat via a sidebar message - it will be restored but
// should still not have grabbed focus.
testNoFocusOnAutoRestore: function(next) {
const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html?id=1";
let chatbar = SocialChatBar.chatbar;
startTestAndWaitForSidebar(function(port) {
openChatViaWorkerMessage(port, chatUrl, function() {
is(chatbar.childElementCount, 1, "exactly 1 chat open");
// bug 865086 opening minimized still sets the window as selected
todo(chatbar.selectedChat != chatbar.firstElementChild, "chat is not selected");
ok(isTabFocused(), "tab should be focused");
openChatViaSidebarMessage(port, {stealFocus: 1, id: 1}, function() {
is(chatbar.childElementCount, 1, "still 1 chat open");
ok(!chatbar.firstElementChild.minimized, "chat no longer minimized");
// bug 865086 because we marked it selected on open, it still is
todo(chatbar.selectedChat != chatbar.firstElementChild, "chat is not selected");
ok(isTabFocused(), "tab should still be focused");
next();
});
});
});
},
// Here we open a chat, which will not be focused. Then we minimize it and
// restore it via a titlebar clock - it should get focus at that point.
testFocusOnExplicitRestore: function(next) {
startTestAndWaitForSidebar(function(port) {
openChatViaSidebarMessage(port, {stealFocus: 1}, function() {
ok(true, "got chatbox message");
ok(isTabFocused(), "tab should still be focused");
let chatbox = SocialChatBar.chatbar.firstElementChild;
ok(chatbox, "chat opened");
chatbox.minimized = true;
ok(isTabFocused(), "tab should still be focused");
// pretend we clicked on the titlebar
chatbox.onTitlebarClick({button: 0});
waitForCondition(function() isChatFocused(SocialChatBar.chatbar.selectedChat),
function() {
ok(!chatbox.minimized, "chat should have been restored");
ok(isChatFocused(chatbox), "chat should be focused");
is(chatbox, SocialChatBar.chatbar.selectedChat, "chat is marked selected");
next();
}, "chat should have focus");
});
});
},
// Open 2 chats and give 1 focus. Minimize the focused one - the second
// should get focus.
testMinimizeFocused: function(next) {
let chatbar = SocialChatBar.chatbar;
startTestAndWaitForSidebar(function(port) {
openChatViaSidebarMessage(port, {stealFocus: 1, id: 1}, function() {
let chat1 = chatbar.firstElementChild;
openChatViaSidebarMessage(port, {stealFocus: 1, id: 2}, function() {
is(chatbar.childElementCount, 2, "exactly 2 chats open");
let chat2 = chat1.nextElementSibling || chat1.previousElementSibling;
chatbar.selectedChat = chat1;
chatbar.focus();
waitForCondition(function() isChatFocused(chat1),
function() {
is(chat1, SocialChatBar.chatbar.selectedChat, "chat1 is marked selected");
isnot(chat2, SocialChatBar.chatbar.selectedChat, "chat2 is not marked selected");
chat1.minimized = true;
waitForCondition(function() isChatFocused(chat2),
function() {
// minimizing the chat with focus should give it to another.
isnot(chat1, SocialChatBar.chatbar.selectedChat, "chat1 is not marked selected");
is(chat2, SocialChatBar.chatbar.selectedChat, "chat2 is marked selected");
next();
}, "chat2 should have focus");
}, "chat1 should have focus");
});
});
});
},
// Open 2 chats, select (but not focus) one, then re-request it be
// opened via a message. Focus should not move.
testReopenNonFocused: function(next) {
let chatbar = SocialChatBar.chatbar;
startTestAndWaitForSidebar(function(port) {
openChatViaSidebarMessage(port, {id: 1}, function() {
let chat1 = chatbar.firstElementChild;
openChatViaSidebarMessage(port, {id: 2}, function() {
let chat2 = chat1.nextElementSibling || chat1.previousElementSibling;
chatbar.selectedChat = chat2;
// tab still has focus
ok(isTabFocused(), "tab should still be focused");
// re-request the first.
openChatViaSidebarMessage(port, {id: 1}, function() {
is(chatbar.selectedChat, chat1, "chat1 now selected");
ok(isTabFocused(), "tab should still be focused");
next();
});
});
});
});
},
// Open 2 chats, select and focus the second. Pressing the TAB key should
// cause focus to move between all elements in our chat window before moving
// to the next chat window.
testTab: function(next) {
function sendTabAndWaitForFocus(chat, eltid, callback) {
// ideally we would use the 'focus' event here, but that doesn't work
// as expected for the iframe - the iframe itself never gets the focus
// event (apparently the sub-document etc does.)
// So just poll for the correct element getting focus...
let doc = chat.contentDocument;
EventUtils.sendKey("tab");
waitForCondition(function() {
let elt = eltid ? doc.getElementById(eltid) : doc.documentElement;
return doc.activeElement == elt;
}, callback, "element " + eltid + " never got focus");
}
let chatbar = SocialChatBar.chatbar;
startTestAndWaitForSidebar(function(port) {
openChatViaSidebarMessage(port, {id: 1}, function() {
let chat1 = chatbar.firstElementChild;
openChatViaSidebarMessage(port, {id: 2}, function() {
let chat2 = chat1.nextElementSibling || chat1.previousElementSibling;
chatbar.selectedChat = chat2;
chatbar.focus();
waitForCondition(function() isChatFocused(chatbar.selectedChat),
function() {
// Our chats have 3 focusable elements, so it takes 4 TABs to move
// to the new chat.
sendTabAndWaitForFocus(chat2, "input1", function() {
is(chat2.contentDocument.activeElement.getAttribute("id"), "input1",
"first input field has focus");
ok(isChatFocused(chat2), "new chat still focused after first tab");
sendTabAndWaitForFocus(chat2, "input2", function() {
ok(isChatFocused(chat2), "new chat still focused after tab");
is(chat2.contentDocument.activeElement.getAttribute("id"), "input2",
"second input field has focus");
sendTabAndWaitForFocus(chat2, "iframe", function() {
ok(isChatFocused(chat2), "new chat still focused after tab");
is(chat2.contentDocument.activeElement.getAttribute("id"), "iframe",
"iframe has focus");
// this tab now should move to the next chat, but focus the
// document element itself (hence the null eltid)
sendTabAndWaitForFocus(chat1, null, function() {
ok(isChatFocused(chat1), "first chat is focused");
next();
});
});
});
});
}, "chat should have focus");
});
});
});
},
// Open a chat and focus an element other than the first. Move focus to some
// other item (the tab itself in this case), then focus the chatbar - the
// same element that was previously focused should still have focus.
testFocusedElement: function(next) {
let chatbar = SocialChatBar.chatbar;
startTestAndWaitForSidebar(function(port) {
openChatViaUser();
let chat = chatbar.firstElementChild;
// need to wait for the content to load before we can focus it.
chat.addEventListener("DOMContentLoaded", function DOMContentLoaded() {
chat.removeEventListener("DOMContentLoaded", DOMContentLoaded);
chat.contentDocument.getElementById("input2").focus();
waitForCondition(function() isChatFocused(chat),
function() {
is(chat.contentDocument.activeElement.getAttribute("id"), "input2",
"correct input field has focus");
// set focus to the tab.
let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab);
Services.focus.moveFocus(tabb.contentWindow, null, Services.focus.MOVEFOCUS_ROOT, 0);
waitForCondition(function() isTabFocused(),
function() {
chatbar.focus();
waitForCondition(function() isChatFocused(chat),
function() {
is(chat.contentDocument.activeElement.getAttribute("id"), "input2",
"correct input field still has focus");
next();
}, "chat took focus");
}, "tab has focus");
}, "chat took focus");
});
});
},
};

View File

@ -9,6 +9,8 @@ function gc() {
wu.garbageCollect();
}
let openChatWindow = Cu.import("resource://gre/modules/MozSocialAPI.jsm", {}).openChatWindow;
// Support for going on and offline.
// (via browser/base/content/test/browser_bookmark_titles.js)
let origProxyType = Services.prefs.getIntPref('network.proxy.type');
@ -42,9 +44,10 @@ function openPanel(url, panelCallback, loadCallback) {
function openChat(url, panelCallback, loadCallback) {
// open a chat window
SocialChatBar.openChat(SocialSidebar.provider, url, panelCallback);
SocialChatBar.chatbar.firstChild.addEventListener("DOMContentLoaded", function panelLoad() {
SocialChatBar.chatbar.firstChild.removeEventListener("DOMContentLoaded", panelLoad, true);
let chatbar = getChatBar();
openChatWindow(null, SocialSidebar.provider, url, panelCallback);
chatbar.firstChild.addEventListener("DOMContentLoaded", function panelLoad() {
chatbar.firstChild.removeEventListener("DOMContentLoaded", panelLoad, true);
loadCallback();
}, true);
}
@ -154,7 +157,7 @@ var tests = {
testChatWindow: function(next) {
let panelCallbackCount = 0;
// go offline and open a flyout.
// go offline and open a chat.
goOffline();
openChat(
"https://example.com/browser/browser/base/content/test/social/social_chat.html",
@ -164,7 +167,7 @@ var tests = {
function() { // the "load" callback.
executeSoon(function() {
todo_is(panelCallbackCount, 0, "Bug 833207 - should be no callback when error page loads.");
let chat = SocialChatBar.chatbar.selectedChat;
let chat = getChatBar().selectedChat;
waitForCondition(function() chat.contentDocument.location.href.indexOf("about:socialerror?")==0,
function() {
chat.close();
@ -174,5 +177,36 @@ var tests = {
});
}
);
},
testChatWindowAfterTearOff: function(next) {
// Ensure that the error listener survives the chat window being detached.
let url = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
let panelCallbackCount = 0;
// open a chat while we are still online.
openChat(
url,
null,
function() { // the "load" callback.
executeSoon(function() {
let chat = getChatBar().selectedChat;
is(chat.contentDocument.location.href, url, "correct url loaded");
// toggle to a detached window.
chat.swapWindows().then(
chat => {
// now go offline and reload the chat - about:socialerror should be loaded.
goOffline();
chat.contentDocument.location.reload();
waitForCondition(function() chat.contentDocument.location.href.indexOf("about:socialerror?")==0,
function() {
chat.close();
next();
},
"error page didn't appear");
}
);
});
}
);
}
}

View File

@ -237,8 +237,6 @@ function checkSocialUI(win) {
_is(!!a, !!b, msg);
}
isbool(win.SocialSidebar.canShow, sidebarEnabled, "social sidebar active?");
isbool(win.SocialChatBar.isAvailable, enabled, "chatbar available?");
isbool(!win.SocialChatBar.chatbar.hidden, enabled, "chatbar visible?");
let contextMenus = [
{
@ -279,8 +277,6 @@ function checkSocialUI(win) {
// and for good measure, check all the social commands.
isbool(!doc.getElementById("Social:ToggleSidebar").hidden, sidebarEnabled, "Social:ToggleSidebar visible?");
isbool(!doc.getElementById("Social:ToggleNotifications").hidden, enabled, "Social:ToggleNotifications visible?");
isbool(!doc.getElementById("Social:FocusChat").hidden, enabled, "Social:FocusChat visible?");
isbool(doc.getElementById("Social:FocusChat").getAttribute("disabled"), enabled ? "false" : "true", "Social:FocusChat disabled?");
// and report on overall success of failure of the various checks here.
is(numGoodTests, numTests, "The Social UI tests succeeded.")
@ -403,7 +399,7 @@ function get3ChatsForCollapsing(mode, cb) {
// To make our life easier we don't go via the worker and ports so we get
// more control over creation *and* to make the code much simpler. We
// assume the worker/port stuff is individually tested above.
let chatbar = window.SocialChatBar.chatbar;
let chatbar = getChatBar();
let chatWidth = undefined;
let num = 0;
is(chatbar.childNodes.length, 0, "chatbar starting empty");
@ -447,23 +443,21 @@ function makeChat(mode, uniqueid, cb) {
info("making a chat window '" + uniqueid +"'");
let provider = SocialSidebar.provider;
const chatUrl = provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
let isOpened = window.SocialChatBar.openChat(provider, chatUrl + "?id=" + uniqueid, function(chat) {
// Note that we use promiseChatLoaded instead of the callback to ensure the
// content has started loading.
let chatbox = getChatBar().openChat(provider.origin, provider.name,
chatUrl + "?id=" + uniqueid, mode);
chatbox.promiseChatLoaded.then(
() => {
info("chat window has opened");
// we can't callback immediately or we might close the chat during
// this event which upsets the implementation - it is only 1/2 way through
// handling the load event.
chat.document.title = uniqueid;
executeSoon(cb);
}, mode);
if (!isOpened) {
ok(false, "unable to open chat window, no provider? more failures to come");
executeSoon(cb);
}
chatbox.contentDocument.title = uniqueid;
cb();
});
}
function checkPopup() {
// popup only showing if any collapsed popup children.
let chatbar = window.SocialChatBar.chatbar;
let chatbar = getChatBar();
let numCollapsed = 0;
for (let chat of chatbar.childNodes) {
if (chat.collapsed) {
@ -482,7 +476,7 @@ function checkPopup() {
// Does a callback passing |true| if the window is now big enough or false
// if we couldn't resize large enough to satisfy the test requirement.
function resizeWindowToChatAreaWidth(desired, cb, count = 0) {
let current = window.SocialChatBar.chatbar.getBoundingClientRect().width;
let current = getChatBar().getBoundingClientRect().width;
let delta = desired - current;
info(count + ": resizing window so chat area is " + desired + " wide, currently it is "
+ current + ". Screen avail is " + window.screen.availWidth
@ -515,7 +509,7 @@ function resizeWindowToChatAreaWidth(desired, cb, count = 0) {
}
function resize_handler(event) {
// we did resize - but did we get far enough to be able to continue?
let newSize = window.SocialChatBar.chatbar.getBoundingClientRect().width;
let newSize = getChatBar().getBoundingClientRect().width;
let sizedOk = widthDeltaCloseEnough(newSize - desired);
if (!sizedOk)
return;
@ -563,8 +557,13 @@ function resizeAndCheckWidths(first, second, third, checks, cb) {
}, count);
}
function getChatBar() {
return document.getElementById("pinnedchats");
}
function getPopupWidth() {
let popup = window.SocialChatBar.chatbar.menupopup;
let chatbar = getChatBar();
let popup = chatbar.menupopup;
ok(!popup.parentNode.collapsed, "asking for popup width when it is visible");
let cs = document.defaultView.getComputedStyle(popup.parentNode);
let margins = parseInt(cs.marginLeft) + parseInt(cs.marginRight);
@ -572,6 +571,8 @@ function getPopupWidth() {
}
function closeAllChats() {
let chatbar = window.SocialChatBar.chatbar;
chatbar.removeAll();
let chatbar = getChatBar();
while (chatbar.selectedChat) {
chatbar.selectedChat.close();
}
}

View File

@ -13,6 +13,7 @@ MOCHITEST_CHROME_MANIFESTS += [
]
BROWSER_CHROME_MANIFESTS += [
'content/test/chat/browser.ini',
'content/test/general/browser.ini',
'content/test/newtab/browser.ini',
'content/test/plugins/browser.ini',

View File

@ -758,7 +758,7 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
* The corresponding item.
*/
_onBreakpointRemoved: function(aItem) {
dumpn("Finalizing breakpoint item: " + aItem);
dumpn("Finalizing breakpoint item: " + aItem.stringify());
// Destroy the context menu for the breakpoint.
let contextMenu = aItem.attachment.popup;
@ -2215,6 +2215,7 @@ WatchExpressionsView.prototype = Heritage.extend(WidgetMethods, {
_createItemView: function(aExpression) {
let container = document.createElement("hbox");
container.className = "list-widget-item dbg-expression";
container.setAttribute("align", "center");
let arrowNode = document.createElement("hbox");
arrowNode.className = "dbg-expression-arrow";

View File

@ -491,7 +491,7 @@ StackFramesView.prototype = Heritage.extend(WidgetMethods, {
* The corresponding item.
*/
_onStackframeRemoved: function(aItem) {
dumpn("Finalizing stackframe item: " + aItem);
dumpn("Finalizing stackframe item: " + aItem.stringify());
// Remove the mirrored item in the classic list.
let depth = aItem.attachment.depth;

View File

@ -574,10 +574,16 @@ Item.prototype = {
/**
* Returns a string representing the object.
* Avoid using `toString` to avoid accidental JSONification.
* @return string
*/
toString: function() {
return this._value + " :: " + this._target + " :: " + this.attachment;
stringify: function() {
return JSON.stringify({
value: this._value,
target: this._target + "",
prebuiltNode: this._prebuiltNode + "",
attachment: this.attachment
}, null, 2);
},
_value: "",

View File

@ -9,5 +9,5 @@
<!ENTITY newtab.undo.restoreButton "Restore All.">
<!ENTITY newtab.undo.closeTooltip "Hide">
<!ENTITY newtab.sponsored.release.message "This Sponsor site was suggested because we hoped youd find it interesting and because it supports Mozillas mission.">
<!ENTITY newtab.sponsored.trial.message "This site was suggested because we hoped youd find it interesting and because it supports Mozillas mission.">
<!ENTITY newtab.sponsored.trial.message2 "This site was suggested because we hoped youd find it interesting and because it supports Mozillas mission.">
<!ENTITY newtab.panel.link.text "Learn more…">

View File

@ -160,8 +160,6 @@ var BrowserUI = {
Util.dumpLn("* delay load started...");
window.removeEventListener("UIReadyDelayed", delayedInit, false);
// Login Manager and Form History initialization
Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
messageManager.addMessageListener("Browser:MozApplicationManifest", OfflineApps);
try {

191
browser/modules/Chat.jsm Normal file
View File

@ -0,0 +1,191 @@
/* 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";
// A module for working with chat windows.
this.EXPORTED_SYMBOLS = ["Chat"];
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
// A couple of internal helper function.
function isWindowChromeless(win) {
// XXX - stolen from browser-social.js, but there's no obvious place to
// put this so it can be shared.
// Is this a popup window that doesn't want chrome shown?
let docElem = win.document.documentElement;
// extrachrome is not restored during session restore, so we need
// to check for the toolbar as well.
let chromeless = docElem.getAttribute("chromehidden").contains("extrachrome") ||
docElem.getAttribute('chromehidden').contains("toolbar");
return chromeless;
}
function isWindowGoodForChats(win) {
return !win.closed &&
!!win.document.getElementById("pinnedchats") &&
!isWindowChromeless(win) &&
!PrivateBrowsingUtils.isWindowPrivate(win);
}
function getChromeWindow(contentWin) {
return contentWin.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
}
/*
* The exported Chat object
*/
let Chat = {
/**
* Open a new chatbox.
*
* @param contentWindow [optional]
* The content window that requested this chat. May be null.
* @param origin
* The origin for the chat. This is primarily used as an identifier
* to help identify all chats from the same provider.
* @param title
* The title to be used if a new chat window is created.
* @param url
* The URL for the that. Should be under the origin. If an existing
* chatbox exists with the same URL, it will be reused and returned.
* @param mode [optional]
* May be undefined or 'minimized'
* @param focus [optional]
* Indicates if the chatbox should be focused. If undefined the chat
* will be focused if the window is currently handling user input (ie,
* if the chat is being opened as a direct result of user input)
* @return A chatbox binding. This binding has a number of promises which
* can be used to determine when the chatbox is being created and
* has loaded. Will return null if no chat can be created (Which
* should only happen in edge-cases)
*/
open: function(contentWindow, origin, title, url, mode, focus, callback) {
let chromeWindow = this.findChromeWindowForChats(contentWindow);
if (!chromeWindow) {
Cu.reportError("Failed to open a chat window - no host window could be found.");
return null;
}
let chatbar = chromeWindow.document.getElementById("pinnedchats");
chatbar.hidden = false;
let chatbox = chatbar.openChat(origin, title, url, mode, callback);
// getAttention is ignored if the target window is already foreground, so
// we can call it unconditionally.
chromeWindow.getAttention();
// If focus is undefined we want automatic focus handling, and only focus
// if a direct result of user action.
if (focus === undefined) {
let dwu = chromeWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
focus = dwu.isHandlingUserInput;
}
if (focus) {
chatbar.focus();
}
return chatbox;
},
/**
* Close all chats from the specified origin.
*
* @param origin
* The origin from which all chats should be closed.
*/
closeAll: function(origin) {
// close all attached chat windows
let winEnum = Services.wm.getEnumerator("navigator:browser");
while (winEnum.hasMoreElements()) {
let win = winEnum.getNext();
let chatbar = win.document.getElementById("pinnedchats");
if (!chatbar)
continue;
let chats = [c for (c of chatbar.children) if (c.content.getAttribute("origin") == origin)];
[c.close() for (c of chats)];
}
// close all standalone chat windows
winEnum = Services.wm.getEnumerator("Social:Chat");
while (winEnum.hasMoreElements()) {
let win = winEnum.getNext();
if (win.closed)
continue;
let chatOrigin = win.document.getElementById("chatter").content.getAttribute("origin");
if (origin == chatOrigin)
win.close();
}
},
/**
* Focus the chatbar associated with a window
*
* @param window
*/
focus: function(win) {
let chatbar = win.document.getElementById("pinnedchats");
if (chatbar && !chatbar.hidden) {
chatbar.focus();
}
},
// This is exported as socialchat.xml needs to find a window when a chat
// is re-docked.
findChromeWindowForChats: function(preferredWindow) {
if (preferredWindow) {
preferredWindow = getChromeWindow(preferredWindow);
if (isWindowGoodForChats(preferredWindow)) {
return preferredWindow;
}
}
// no good - we just use the "most recent" browser window which can host
// chats (we used to try and "group" all chats in the same browser window,
// but that didn't work out so well - see bug 835111
// Try first the most recent window as getMostRecentWindow works
// even on platforms where getZOrderDOMWindowEnumerator is broken
// (ie. Linux). This will handle most cases, but won't work if the
// foreground window is a popup.
let mostRecent = Services.wm.getMostRecentWindow("navigator:browser");
if (isWindowGoodForChats(mostRecent))
return mostRecent;
let topMost, enumerator;
// *sigh* - getZOrderDOMWindowEnumerator is broken except on Mac and
// Windows. We use BROKEN_WM_Z_ORDER as that is what some other code uses
// and a few bugs recommend searching mxr for this symbol to identify the
// workarounds - we want this code to be hit in such searches.
let os = Services.appinfo.OS;
const BROKEN_WM_Z_ORDER = os != "WINNT" && os != "Darwin";
if (BROKEN_WM_Z_ORDER) {
// this is oldest to newest and no way to change the order.
enumerator = Services.wm.getEnumerator("navigator:browser");
} else {
// here we explicitly ask for bottom-to-top so we can use the same logic
// where BROKEN_WM_Z_ORDER is true.
enumerator = Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", false);
}
while (enumerator.hasMoreElements()) {
let win = enumerator.getNext();
if (!win.closed && isWindowGoodForChats(win))
topMost = win;
}
return topMost;
},
}

View File

@ -9,6 +9,7 @@ TEST_DIRS += ['test']
EXTRA_JS_MODULES += [
'BrowserNewTabPreloader.jsm',
'BrowserUITelemetry.jsm',
'Chat.jsm',
'ContentClick.jsm',
'ContentLinkHandler.jsm',
'ContentSearch.jsm',

View File

@ -280,10 +280,11 @@
}
.dbg-expression-arrow {
background: url(commandline-icon.png);
background-position: 16px;
width: 16px;
height: auto;
height: 16px;
margin: 2px;
background: -moz-image-rect(url(commandline-icon.png), 0, 32, 16, 16);
}
.dbg-expression-input {
@ -294,8 +295,8 @@
-moz-appearance: none;
border: none;
background: none;
cursor: pointer;
text-decoration: underline;
cursor: pointer;
}
.theme-dark .dbg-expression-button {

View File

View File

@ -48,7 +48,23 @@ function exitprintpreview() {
.getInterface(Components.interfaces.nsIWebBrowserPrint).exitPrintPreview();
}
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
let webProgress = Components.classes["@mozilla.org/docloaderservice;1"]
.getService(Components.interfaces.nsIWebProgress);
let webProgressListener = {
QueryInterface : XPCOMUtils.generateQI(
[Components.interfaces.nsIWebProgressListener,
Components.interfaces.nsISupportsWeakReference]),
onStateChange : function() { /* NOP */ },
onProgressChange : function() { throw "Unexpected onProgressChange"; },
onLocationChange : function() { throw "Unexpected onLocationChange"; },
onStatusChange : function() { throw "Unexpected onStatusChange"; },
onSecurityChange : function() { throw "Unexpected onSecurityChange"; }
};
function finish() {
webProgress.removeProgressListener(webProgressListener);
SimpleTest.finish();
window.close();
}
@ -56,6 +72,12 @@ function finish() {
function run()
{
/** Test for Bug 396024 **/
// Gross hacky workaround for bug 881996. The WPL does nothing.
webProgress.addProgressListener(
webProgressListener,
Components.interfaces.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
var printService = Components.classes["@mozilla.org/gfx/printsettings-service;1"]
.getService(Components.interfaces.nsIPrintSettingsService);

View File

@ -50,6 +50,9 @@ public final class GeckoSharedPrefs {
// Name for app-scoped prefs
public static final String APP_PREFS_NAME = "GeckoApp";
// Used when fetching profile-scoped prefs.
public static final String PROFILE_PREFS_NAME_PREFIX = "GeckoProfile-";
// The prefs key that holds the current migration
private static final String PREFS_VERSION_KEY = "gecko_shared_prefs_migration";
@ -73,9 +76,6 @@ public final class GeckoSharedPrefs {
DISABLE_MIGRATIONS
}
// Used when fetching profile-scoped prefs.
private static final String PROFILE_PREFS_NAME_PREFIX = "GeckoProfile-";
public static SharedPreferences forApp(Context context) {
return forApp(context, EnumSet.noneOf(Flags.class));
}

View File

@ -7,13 +7,13 @@ package org.mozilla.gecko;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.util.GeckoEventListener;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import java.util.Map;
@ -27,6 +27,28 @@ public final class SharedPreferencesHelper
{
public static final String LOGTAG = "GeckoAndSharedPrefs";
private enum Scope {
APP("app"),
PROFILE("profile"),
GLOBAL("global");
public final String key;
private Scope(String key) {
this.key = key;
}
public static Scope forKey(String key) {
for (Scope scope : values()) {
if (scope.key.equals(key)) {
return scope;
}
}
throw new IllegalStateException("SharedPreferences scope must be valid.");
}
}
protected final Context mContext;
// mListeners is not synchronized because it is only updated in
@ -61,12 +83,45 @@ public final class SharedPreferencesHelper
"SharedPreferences:Observe");
}
private SharedPreferences getSharedPreferences(String branch) {
if (branch == null) {
return GeckoSharedPrefs.forApp(mContext);
} else {
return mContext.getSharedPreferences(branch, Context.MODE_PRIVATE);
private SharedPreferences getSharedPreferences(JSONObject message) throws JSONException {
final Scope scope = Scope.forKey(message.getString("scope"));
switch (scope) {
case APP:
return GeckoSharedPrefs.forApp(mContext);
case PROFILE:
final String profileName = message.optString("profileName", null);
if (profileName == null) {
return GeckoSharedPrefs.forProfile(mContext);
} else {
return GeckoSharedPrefs.forProfileName(mContext, profileName);
}
case GLOBAL:
final String branch = message.optString("branch", null);
if (branch == null) {
return PreferenceManager.getDefaultSharedPreferences(mContext);
} else {
return mContext.getSharedPreferences(branch, Context.MODE_PRIVATE);
}
}
return null;
}
private String getBranch(Scope scope, String profileName, String branch) {
switch (scope) {
case APP:
return GeckoSharedPrefs.APP_PREFS_NAME;
case PROFILE:
if (profileName == null) {
profileName = GeckoProfile.get(mContext).getName();
}
return GeckoSharedPrefs.PROFILE_PREFS_NAME_PREFIX + profileName;
case GLOBAL:
return branch;
}
return null;
}
/**
@ -79,13 +134,7 @@ public final class SharedPreferencesHelper
* and an Object value.
*/
private void handleSet(JSONObject message) throws JSONException {
if (!message.has("branch")) {
Log.e(LOGTAG, "No branch specified for SharedPreference:Set; aborting.");
return;
}
String branch = message.isNull("branch") ? null : message.getString("branch");
SharedPreferences.Editor editor = getSharedPreferences(branch).edit();
SharedPreferences.Editor editor = getSharedPreferences(message).edit();
JSONArray jsonPrefs = message.getJSONArray("preferences");
@ -116,13 +165,7 @@ public final class SharedPreferencesHelper
* "string"].
*/
private JSONArray handleGet(JSONObject message) throws JSONException {
if (!message.has("branch")) {
Log.e(LOGTAG, "No branch specified for SharedPreference:Get; aborting.");
return null;
}
String branch = message.isNull("branch") ? null : message.getString("branch");
SharedPreferences prefs = getSharedPreferences(branch);
SharedPreferences prefs = getSharedPreferences(message);
JSONArray jsonPrefs = message.getJSONArray("preferences");
JSONArray jsonValues = new JSONArray();
@ -159,10 +202,14 @@ public final class SharedPreferencesHelper
private static class ChangeListener
implements SharedPreferences.OnSharedPreferenceChangeListener {
public final Scope scope;
public final String branch;
public final String profileName;
public ChangeListener(final String branch) {
public ChangeListener(final Scope scope, final String branch, final String profileName) {
this.scope = scope;
this.branch = branch;
this.profileName = profileName;
}
@Override
@ -172,7 +219,9 @@ public final class SharedPreferencesHelper
}
try {
final JSONObject msg = new JSONObject();
msg.put("scope", this.scope.key);
msg.put("branch", this.branch);
msg.put("profileName", this.profileName);
msg.put("key", key);
// Truly, this is awful, but the API impedence is strong: there
@ -197,24 +246,29 @@ public final class SharedPreferencesHelper
* disable listening.
*/
private void handleObserve(JSONObject message) throws JSONException {
if (!message.has("branch")) {
final SharedPreferences prefs = getSharedPreferences(message);
final boolean enable = message.getBoolean("enable");
final Scope scope = Scope.forKey(message.getString("scope"));
final String profileName = message.optString("profileName", null);
final String branch = getBranch(scope, profileName, message.optString("branch", null));
if (branch == null) {
Log.e(LOGTAG, "No branch specified for SharedPreference:Observe; aborting.");
return;
}
String branch = message.isNull("branch") ? null : message.getString("branch");
SharedPreferences prefs = getSharedPreferences(branch);
boolean enable = message.getBoolean("enable");
// mListeners is only modified in this one observer, which is called
// from Gecko serially.
if (enable && !this.mListeners.containsKey(branch)) {
SharedPreferences.OnSharedPreferenceChangeListener listener = new ChangeListener(branch);
SharedPreferences.OnSharedPreferenceChangeListener listener
= new ChangeListener(scope, branch, profileName);
this.mListeners.put(branch, listener);
prefs.registerOnSharedPreferenceChangeListener(listener);
}
if (!enable && this.mListeners.containsKey(branch)) {
SharedPreferences.OnSharedPreferenceChangeListener listener = this.mListeners.remove(branch);
SharedPreferences.OnSharedPreferenceChangeListener listener
= this.mListeners.remove(branch);
prefs.unregisterOnSharedPreferenceChangeListener(listener);
}
}

View File

@ -513,9 +513,11 @@ sync_java_files = [
'background/fxa/PasswordStretcher.java',
'background/fxa/QuickPasswordStretcher.java',
'background/fxa/SkewHandler.java',
'background/healthreport/AndroidConfigurationProvider.java',
'background/healthreport/Environment.java',
'background/healthreport/EnvironmentBuilder.java',
'background/healthreport/EnvironmentV1.java',
'background/healthreport/EnvironmentV2.java',
'background/healthreport/HealthReportBroadcastReceiver.java',
'background/healthreport/HealthReportBroadcastService.java',
'background/healthreport/HealthReportDatabases.java',

View File

@ -0,0 +1,77 @@
/* 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 org.mozilla.gecko.background.healthreport;
import org.mozilla.gecko.background.healthreport.Environment.UIType;
import org.mozilla.gecko.background.healthreport.EnvironmentBuilder.ConfigurationProvider;
import org.mozilla.gecko.sync.jpake.stage.GetRequestStage.GetStepTimerTask;
import org.mozilla.gecko.util.HardwareUtils;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.util.DisplayMetrics;
public class AndroidConfigurationProvider implements ConfigurationProvider {
private static final float MILLIMETERS_PER_INCH = 25.4f;
private final Configuration configuration;
private final DisplayMetrics displayMetrics;
public AndroidConfigurationProvider(final Context context) {
final Resources resources = context.getResources();
this.configuration = resources.getConfiguration();
this.displayMetrics = resources.getDisplayMetrics();
HardwareUtils.init(context);
}
@Override
public boolean hasHardwareKeyboard() {
return configuration.keyboard != Configuration.KEYBOARD_NOKEYS;
}
@Override
public UIType getUIType() {
if (HardwareUtils.isLargeTablet()) {
return UIType.LARGE_TABLET;
}
if (HardwareUtils.isSmallTablet()) {
return UIType.SMALL_TABLET;
}
return UIType.DEFAULT;
}
@Override
public int getUIModeType() {
return configuration.uiMode & Configuration.UI_MODE_TYPE_MASK;
}
@Override
public int getScreenLayoutSize() {
return configuration.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
}
/**
* Calculate screen horizontal width, in millimeters.
* This is approximate, will be wrong on some devices, and
* most likely doesn't include screen area that the app doesn't own.
* http://stackoverflow.com/questions/2193457/is-there-a-way-to-determine-android-physical-screen-height-in-cm-or-inches
*/
@Override
public int getScreenXInMM() {
return Math.round((displayMetrics.widthPixels / displayMetrics.xdpi) * MILLIMETERS_PER_INCH);
}
/**
* @see #getScreenXInMM() for caveats.
*/
@Override
public int getScreenYInMM() {
return Math.round((displayMetrics.heightPixels / displayMetrics.ydpi) * MILLIMETERS_PER_INCH);
}
}

View File

@ -18,14 +18,61 @@ package org.mozilla.gecko.background.healthreport;
* registered an <code>Environment</code>, don't do so again; start from scratch.
*
*/
public abstract class Environment extends EnvironmentV1 {
public abstract class Environment extends EnvironmentV2 {
// Version 2 adds osLocale, appLocale, acceptLangSet, and distribution.
public static final int CURRENT_VERSION = 2;
// Version 3 adds device characteristics.
public static final int CURRENT_VERSION = 3;
public String osLocale; // The Android OS "Locale" value.
public String appLocale;
public int acceptLangSet;
public String distribution; // ID + version. Typically empty.
public static enum UIType {
// Corresponds to the typical phone interface.
DEFAULT("default"),
// Corresponds to a device for which Fennec is displaying the large tablet UI.
LARGE_TABLET("largetablet"),
// Corresponds to a device for which Fennec is displaying the small tablet UI.
SMALL_TABLET("smalltablet");
private final String label;
private UIType(final String label) {
this.label = label;
}
public String toString() {
return this.label;
}
public static UIType fromLabel(final String label) {
for (UIType type : UIType.values()) {
if (type.label.equals(label)) {
return type;
}
}
throw new IllegalArgumentException("Bad enum value: " + label);
}
}
public UIType uiType = UIType.DEFAULT;
/**
* Mask of Configuration#uiMode. E.g., UI_MODE_TYPE_CAR.
*/
public int uiMode = 0; // UI_MODE_TYPE_UNDEFINED = 0
/**
* Computed physical dimensions in millimeters.
*/
public int screenXInMM;
public int screenYInMM;
/**
* One of the Configuration#SCREENLAYOUT_SIZE_* constants.
*/
public int screenLayout = 0; // SCREENLAYOUT_SIZE_UNDEFINED = 0
public boolean hasHardwareKeyboard;
public Environment() {
this(Environment.HashAppender.class);
@ -40,10 +87,12 @@ public abstract class Environment extends EnvironmentV1 {
protected void appendHash(EnvironmentAppender appender) {
super.appendHash(appender);
// v2.
appender.append(osLocale);
appender.append(appLocale);
appender.append(acceptLangSet);
appender.append(distribution);
// v3.
appender.append(hasHardwareKeyboard ? 1 : 0);
appender.append(uiType.toString());
appender.append(uiMode);
appender.append(screenLayout);
appender.append(screenXInMM);
appender.append(screenYInMM);
}
}

View File

@ -11,6 +11,7 @@ import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.SysInfo;
import org.mozilla.gecko.background.common.GlobalConstants;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.healthreport.Environment.UIType;
import android.content.ContentProvider;
import android.content.ContentProviderClient;
@ -68,8 +69,20 @@ public class EnvironmentBuilder {
public JSONObject getAddonsJSON();
}
public static interface ConfigurationProvider {
public boolean hasHardwareKeyboard();
public UIType getUIType();
public int getUIModeType();
public int getScreenLayoutSize();
public int getScreenXInMM();
public int getScreenYInMM();
}
protected static void populateEnvironment(Environment e,
ProfileInformationProvider info) {
ProfileInformationProvider info,
ConfigurationProvider config) {
e.cpuCount = SysInfo.getCPUCount();
e.memoryMB = SysInfo.getMemSize();
@ -100,31 +113,29 @@ public class EnvironmentBuilder {
e.themeCount = 0;
JSONObject addons = info.getAddonsJSON();
if (addons == null) {
return;
}
@SuppressWarnings("unchecked")
Iterator<String> it = addons.keys();
while (it.hasNext()) {
String key = it.next();
try {
JSONObject addon = addons.getJSONObject(key);
String type = addon.optString("type");
Logger.pii(LOG_TAG, "Add-on " + key + " is a " + type);
if ("extension".equals(type)) {
++e.extensionCount;
} else if ("plugin".equals(type)) {
++e.pluginCount;
} else if ("theme".equals(type)) {
++e.themeCount;
} else if ("service".equals(type)) {
// Later.
} else {
Logger.debug(LOG_TAG, "Unknown add-on type: " + type);
if (addons != null) {
@SuppressWarnings("unchecked")
Iterator<String> it = addons.keys();
while (it.hasNext()) {
String key = it.next();
try {
JSONObject addon = addons.getJSONObject(key);
String type = addon.optString("type");
Logger.pii(LOG_TAG, "Add-on " + key + " is a " + type);
if ("extension".equals(type)) {
++e.extensionCount;
} else if ("plugin".equals(type)) {
++e.pluginCount;
} else if ("theme".equals(type)) {
++e.themeCount;
} else if ("service".equals(type)) {
// Later.
} else {
Logger.debug(LOG_TAG, "Unknown add-on type: " + type);
}
} catch (Exception ex) {
Logger.warn(LOG_TAG, "Failed to process add-on " + key, ex);
}
} catch (Exception ex) {
Logger.warn(LOG_TAG, "Failed to process add-on " + key, ex);
}
}
@ -135,6 +146,14 @@ public class EnvironmentBuilder {
e.osLocale = info.getOSLocale();
e.appLocale = info.getAppLocale();
e.acceptLangSet = info.isAcceptLangUserSet() ? 1 : 0;
// v3 environment fields.
e.hasHardwareKeyboard = config.hasHardwareKeyboard();
e.uiType = config.getUIType();
e.uiMode = config.getUIModeType();
e.screenLayout = config.getScreenLayoutSize();
e.screenXInMM = config.getScreenXInMM();
e.screenYInMM = config.getScreenYInMM();
}
/**
@ -144,14 +163,14 @@ public class EnvironmentBuilder {
* @param info a source of profile data
* @return the new {@link Environment}
*/
public static Environment getCurrentEnvironment(ProfileInformationProvider info) {
public static Environment getCurrentEnvironment(ProfileInformationProvider info, ConfigurationProvider config) {
Environment e = new Environment() {
@Override
public int register() {
return 0;
}
};
populateEnvironment(e, info);
populateEnvironment(e, info, config);
return e;
}
@ -159,9 +178,10 @@ public class EnvironmentBuilder {
* @return the current environment's ID in the provided storage layer
*/
public static int registerCurrentEnvironment(final HealthReportStorage storage,
final ProfileInformationProvider info) {
final ProfileInformationProvider info,
final ConfigurationProvider config) {
Environment e = storage.getEnvironment();
populateEnvironment(e, info);
populateEnvironment(e, info, config);
e.register();
Logger.debug(LOG_TAG, "Registering current environment: " + e.getHash() + " = " + e.id);
return e.id;

View File

@ -0,0 +1,30 @@
/* 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 org.mozilla.gecko.background.healthreport;
public abstract class EnvironmentV2 extends EnvironmentV1 {
private static final int VERSION = 2;
public String osLocale;
public String appLocale;
public int acceptLangSet;
public String distribution;
public EnvironmentV2(Class<? extends EnvironmentAppender> appenderClass) {
super(appenderClass);
version = VERSION;
}
@Override
protected void appendHash(EnvironmentAppender appender) {
super.appendHash(appender);
// v2.
appender.append(osLocale);
appender.append(appLocale);
appender.append(acceptLangSet);
appender.append(distribution);
}
}

View File

@ -141,7 +141,12 @@ public class HealthReportDatabaseStorage implements HealthReportStorage {
"distribution", "osLocale", "appLocale", "acceptLangSet",
// Joined to the add-ons table.
"addonsBody"
"addonsBody",
// v3.
"hasHardwareKeyboard",
"uiMode", "uiType",
"screenLayout", "screenXInMM", "screenYInMM"
};
public static final String[] COLUMNS_MEASUREMENT_DETAILS = new String[] {"id", "name", "version"};
@ -190,7 +195,7 @@ public class HealthReportDatabaseStorage implements HealthReportStorage {
protected final HealthReportSQLiteOpenHelper helper;
public static class HealthReportSQLiteOpenHelper extends SQLiteOpenHelper {
public static final int CURRENT_VERSION = 6;
public static final int CURRENT_VERSION = 7;
public static final String LOG_TAG = "HealthReportSQL";
/**
@ -287,6 +292,14 @@ public class HealthReportDatabaseStorage implements HealthReportStorage {
" acceptLangSet INTEGER, " +
" addonsID INTEGER, " +
" hasHardwareKeyboard INTEGER, " +
" uiMode INTEGER, " +
" uiType TEXT, " +
" screenLayout INTEGER, " +
" screenXInMM INTEGER, " +
" screenYInMM INTEGER, " +
" FOREIGN KEY (addonsID) REFERENCES addons(id) ON DELETE RESTRICT, " +
" UNIQUE (hash) " +
")");
@ -395,7 +408,15 @@ public class HealthReportDatabaseStorage implements HealthReportStorage {
" e.osLocale AS osLocale, " +
" e.appLocale AS appLocale, " +
" e.acceptLangSet AS acceptLangSet, " +
" addons.body AS addonsBody " +
" addons.body AS addonsBody, " +
" e.hasHardwareKeyboard AS hasHardwareKeyboard, " +
" e.uiMode AS uiMode, " +
" e.uiType AS uiType, " +
" e.screenLayout AS screenLayout, " +
" e.screenXInMM AS screenXInMM, " +
" e.screenYInMM AS screenYInMM " +
"FROM environments AS e, addons " +
"WHERE e.addonsID = addons.id");
}
@ -408,7 +429,7 @@ public class HealthReportDatabaseStorage implements HealthReportStorage {
db.execSQL("ALTER TABLE environments ADD COLUMN addonsID INTEGER REFERENCES addons(id) ON DELETE RESTRICT");
createAddonsEnvironmentsView(db);
// No need to create view: we're just going to upgrade again.
}
private void upgradeDatabaseFrom3To4(SQLiteDatabase db) {
@ -434,7 +455,7 @@ public class HealthReportDatabaseStorage implements HealthReportStorage {
}
private void upgradeDatabaseFrom5to6(SQLiteDatabase db) {
db.execSQL("DROP VIEW environments_with_addons");
db.execSQL("DROP VIEW IF EXISTS environments_with_addons");
// Add version to environment (default to 1).
db.execSQL("ALTER TABLE environments ADD COLUMN version INTEGER DEFAULT 1");
@ -445,6 +466,20 @@ public class HealthReportDatabaseStorage implements HealthReportStorage {
db.execSQL("ALTER TABLE environments ADD COLUMN appLocale TEXT DEFAULT ''");
db.execSQL("ALTER TABLE environments ADD COLUMN acceptLangSet INTEGER DEFAULT 0");
// No need to recreate view -- we're just going to upgrade to v7 next.
}
private void upgradeDatabaseFrom6to7(SQLiteDatabase db) {
db.execSQL("DROP VIEW IF EXISTS environments_with_addons");
// Add fields to environment (default to empty string and 0).
db.execSQL("ALTER TABLE environments ADD COLUMN hasHardwareKeyboard INTEGER DEFAULT 0");
db.execSQL("ALTER TABLE environments ADD COLUMN uiMode INTEGER DEFAULT 0");
db.execSQL("ALTER TABLE environments ADD COLUMN uiType TEXT DEFAULT ''");
db.execSQL("ALTER TABLE environments ADD COLUMN screenLayout INTEGER DEFAULT 0");
db.execSQL("ALTER TABLE environments ADD COLUMN screenXInMM INTEGER DEFAULT 0");
db.execSQL("ALTER TABLE environments ADD COLUMN screenYInMM INTEGER DEFAULT 0");
// Recreate view.
createAddonsEnvironmentsView(db);
}
@ -466,6 +501,8 @@ public class HealthReportDatabaseStorage implements HealthReportStorage {
upgradeDatabaseFrom4to5(db);
case 5:
upgradeDatabaseFrom5to6(db);
case 6:
upgradeDatabaseFrom6to7(db);
}
} catch (Exception e) {
Logger.error(LOG_TAG, "Failure in onUpgrade.", e);
@ -599,6 +636,12 @@ public class HealthReportDatabaseStorage implements HealthReportStorage {
v.put("osLocale", osLocale);
v.put("appLocale", appLocale);
v.put("acceptLangSet", acceptLangSet);
v.put("hasHardwareKeyboard", hasHardwareKeyboard ? 1 : 0);
v.put("uiMode", uiMode);
v.put("uiType", uiType.toString());
v.put("screenLayout", screenLayout);
v.put("screenXInMM", screenXInMM);
v.put("screenYInMM", screenYInMM);
final SQLiteDatabase db = storage.helper.getWritableDatabase();
@ -685,6 +728,9 @@ public class HealthReportDatabaseStorage implements HealthReportStorage {
public void init(ContentValues v) {
version = v.containsKey("version") ? v.getAsInteger("version") : Environment.CURRENT_VERSION;
Logger.debug(LOG_TAG, "Initializing environment with version " + version);
profileCreation = v.getAsInteger("profileCreation");
cpuCount = v.getAsInteger("cpuCount");
memoryMB = v.getAsInteger("memoryMB");
@ -720,6 +766,15 @@ public class HealthReportDatabaseStorage implements HealthReportStorage {
// Nothing we can do.
}
if (version >= 3) {
hasHardwareKeyboard = v.getAsInteger("hasHardwareKeyboard") != 0;
uiMode = v.getAsInteger("uiMode");
uiType = UIType.fromLabel(v.getAsString("uiType"));
screenLayout = v.getAsInteger("screenLayout");
screenXInMM = v.getAsInteger("screenXInMM");
screenYInMM = v.getAsInteger("screenYInMM");
}
this.hash = null;
this.id = -1;
}
@ -771,6 +826,15 @@ public class HealthReportDatabaseStorage implements HealthReportStorage {
// Nothing we can do.
}
if (this.version >= 3) {
hasHardwareKeyboard = cursor.getInt(i++) != 0;
uiMode = cursor.getInt(i++);
uiType = UIType.fromLabel(cursor.getString(i++));
screenLayout = cursor.getInt(i++);
screenXInMM = cursor.getInt(i++);
screenYInMM = cursor.getInt(i++);
}
return cursor.moveToNext();
}

View File

@ -12,6 +12,7 @@ import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.background.common.DateUtils.DateFormatter;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.healthreport.EnvironmentBuilder.ConfigurationProvider;
import org.mozilla.gecko.background.healthreport.HealthReportStorage.Field;
import android.database.Cursor;
@ -42,7 +43,7 @@ public class HealthReportGenerator {
* @return null if no environment could be computed, or else the resulting document.
* @throws JSONException if there was an error adding environment data to the resulting document.
*/
public JSONObject generateDocument(long since, long lastPingTime, String profilePath) throws JSONException {
public JSONObject generateDocument(long since, long lastPingTime, String profilePath, ConfigurationProvider config) throws JSONException {
Logger.info(LOG_TAG, "Generating FHR document from " + since + "; last ping " + lastPingTime);
Logger.pii(LOG_TAG, "Generating for profile " + profilePath);
@ -51,7 +52,8 @@ public class HealthReportGenerator {
Logger.warn(LOG_TAG, "Not enough profile information to compute current environment.");
return null;
}
Environment current = EnvironmentBuilder.getCurrentEnvironment(cache);
Environment current = EnvironmentBuilder.getCurrentEnvironment(cache, config);
return generateDocument(since, lastPingTime, current);
}
@ -275,6 +277,7 @@ public class HealthReportGenerator {
JSONObject gecko = getGeckoInfo(e, current);
JSONObject appinfo = getAppInfo(e, current);
JSONObject counts = getAddonCounts(e, current);
JSONObject config = getDeviceConfig(e, current);
JSONObject out = new JSONObject();
if (age != null)
@ -292,12 +295,65 @@ public class HealthReportGenerator {
if (active != null)
out.put("org.mozilla.addons.active", active);
if (config != null)
out.put("org.mozilla.device.config", config);
if (current == null) {
out.put("hash", e.getHash());
}
return out;
}
// v3 environment fields.
private static JSONObject getDeviceConfig(Environment e, Environment current) throws JSONException {
JSONObject config = new JSONObject();
int changes = 0;
if (e.version < 3) {
return null;
}
if (current != null && current.version < 3) {
return getDeviceConfig(e, null);
}
if (current == null || current.hasHardwareKeyboard != e.hasHardwareKeyboard) {
config.put("hasHardwareKeyboard", e.hasHardwareKeyboard);
changes++;
}
if (current == null || current.screenLayout != e.screenLayout) {
config.put("screenLayout", e.screenLayout);
changes++;
}
if (current == null || current.screenXInMM != e.screenXInMM) {
config.put("screenXInMM", e.screenXInMM);
changes++;
}
if (current == null || current.screenYInMM != e.screenYInMM) {
config.put("screenYInMM", e.screenYInMM);
changes++;
}
if (current == null || current.uiType != e.uiType) {
config.put("uiType", e.uiType.toString());
changes++;
}
if (current == null || current.uiMode != e.uiMode) {
config.put("uiMode", e.uiMode);
changes++;
}
if (current != null && changes == 0) {
return null;
}
config.put("_v", 1);
return config;
}
private static JSONObject getProfileAge(Environment e, Environment current) throws JSONException {
JSONObject age = new JSONObject();
int changes = 0;
@ -421,6 +477,7 @@ public class HealthReportGenerator {
switch (e.version) {
// There's a straightforward correspondence between environment versions
// and appinfo versions.
case 3:
case 2:
appinfo.put("_v", 3);
break;
@ -433,6 +490,7 @@ public class HealthReportGenerator {
}
switch (e.version) {
case 3:
case 2:
if (populateAppInfoV2(appinfo, e, current, outdated)) {
changed = true;

View File

@ -5,8 +5,10 @@
package org.mozilla.gecko.background.healthreport.prune;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.healthreport.AndroidConfigurationProvider;
import org.mozilla.gecko.background.healthreport.Environment;
import org.mozilla.gecko.background.healthreport.EnvironmentBuilder;
import org.mozilla.gecko.background.healthreport.EnvironmentBuilder.ConfigurationProvider;
import org.mozilla.gecko.background.healthreport.HealthReportDatabaseStorage;
import org.mozilla.gecko.background.healthreport.ProfileInformationCache;
@ -24,6 +26,7 @@ public class PrunePolicyDatabaseStorage implements PrunePolicyStorage {
private final Context context;
private final String profilePath;
private final ConfigurationProvider config;
private ContentProviderClient client;
private HealthReportDatabaseStorage storage;
@ -33,6 +36,7 @@ public class PrunePolicyDatabaseStorage implements PrunePolicyStorage {
public PrunePolicyDatabaseStorage(final Context context, final String profilePath) {
this.context = context;
this.profilePath = profilePath;
this.config = new AndroidConfigurationProvider(context);
this.currentEnvironmentID = -1;
}
@ -128,7 +132,7 @@ public class PrunePolicyDatabaseStorage implements PrunePolicyStorage {
if (!cache.restoreUnlessInitialized()) {
throw new IllegalStateException("Current environment unknown.");
}
final Environment env = EnvironmentBuilder.getCurrentEnvironment(cache);
final Environment env = EnvironmentBuilder.getCurrentEnvironment(cache, config);
currentEnvironmentID = env.register();
}
return currentEnvironmentID;

View File

@ -17,8 +17,10 @@ import org.mozilla.gecko.background.bagheera.BagheeraClient;
import org.mozilla.gecko.background.bagheera.BagheeraRequestDelegate;
import org.mozilla.gecko.background.common.GlobalConstants;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.healthreport.AndroidConfigurationProvider;
import org.mozilla.gecko.background.healthreport.Environment;
import org.mozilla.gecko.background.healthreport.EnvironmentBuilder;
import org.mozilla.gecko.background.healthreport.EnvironmentBuilder.ConfigurationProvider;
import org.mozilla.gecko.background.healthreport.HealthReportConstants;
import org.mozilla.gecko.background.healthreport.HealthReportDatabaseStorage;
import org.mozilla.gecko.background.healthreport.HealthReportGenerator;
@ -42,11 +44,17 @@ public class AndroidSubmissionClient implements SubmissionClient {
protected final Context context;
protected final SharedPreferences sharedPreferences;
protected final String profilePath;
protected final ConfigurationProvider config;
public AndroidSubmissionClient(Context context, SharedPreferences sharedPreferences, String profilePath) {
this(context, sharedPreferences, profilePath, new AndroidConfigurationProvider(context));
}
public AndroidSubmissionClient(Context context, SharedPreferences sharedPreferences, String profilePath, ConfigurationProvider config) {
this.context = context;
this.sharedPreferences = sharedPreferences;
this.profilePath = profilePath;
this.config = config;
}
public SharedPreferences getSharedPreferences() {
@ -88,7 +96,7 @@ public class AndroidSubmissionClient implements SubmissionClient {
final SubmissionsTracker tracker) throws JSONException {
final long since = localTime - GlobalConstants.MILLISECONDS_PER_SIX_MONTHS;
final HealthReportGenerator generator = tracker.getGenerator();
return generator.generateDocument(since, last, profilePath);
return generator.generateDocument(since, last, profilePath, config);
}
protected void uploadPayload(String id, String payload, Collection<String> oldIds, BagheeraRequestDelegate uploadDelegate) {
@ -348,7 +356,7 @@ public class AndroidSubmissionClient implements SubmissionClient {
}
protected int registerCurrentEnvironment() {
return EnvironmentBuilder.registerCurrentEnvironment(storage, profileCache);
return EnvironmentBuilder.registerCurrentEnvironment(storage, profileCache, config);
}
protected void incrementFirstUploadAttemptCount() {
@ -399,7 +407,7 @@ public class AndroidSubmissionClient implements SubmissionClient {
@Override
public JSONObject generateDocument(long since, long lastPingTime,
String generationProfilePath) throws JSONException {
String generationProfilePath, ConfigurationProvider providedConfig) throws JSONException {
// Let's make sure we have an accurate locale.
BrowserLocaleManager.getInstance().getAndApplyPersistedLocale(context);
@ -410,7 +418,7 @@ public class AndroidSubmissionClient implements SubmissionClient {
final Environment environment = getCurrentEnvironment();
document = super.generateDocument(since, lastPingTime, environment);
} else {
document = super.generateDocument(since, lastPingTime, generationProfilePath);
document = super.generateDocument(since, lastPingTime, generationProfilePath, providedConfig);
}
if (document == null) {
@ -420,7 +428,7 @@ public class AndroidSubmissionClient implements SubmissionClient {
}
protected Environment getCurrentEnvironment() {
return EnvironmentBuilder.getCurrentEnvironment(profileCache);
return EnvironmentBuilder.getCurrentEnvironment(profileCache, config);
}
}

View File

@ -27,7 +27,9 @@ import org.mozilla.gecko.Distribution.DistributionDescriptor;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.background.healthreport.AndroidConfigurationProvider;
import org.mozilla.gecko.background.healthreport.EnvironmentBuilder;
import org.mozilla.gecko.background.healthreport.EnvironmentBuilder.ConfigurationProvider;
import org.mozilla.gecko.background.healthreport.HealthReportDatabaseStorage;
import org.mozilla.gecko.background.healthreport.HealthReportStorage.Field;
import org.mozilla.gecko.background.healthreport.HealthReportStorage.MeasurementFields;
@ -89,6 +91,7 @@ public class BrowserHealthRecorder implements HealthRecorder, GeckoEventListener
private ContentProviderClient client;
private volatile HealthReportDatabaseStorage storage;
private final ProfileInformationCache profileCache;
private final ConfigurationProvider configProvider;
private final EventDispatcher dispatcher;
private final SharedPreferences prefs;
@ -155,6 +158,8 @@ public class BrowserHealthRecorder implements HealthRecorder, GeckoEventListener
Log.e(LOG_TAG, "Exception initializing.", e);
}
this.configProvider = new AndroidConfigurationProvider(context);
this.prefs = appPrefs;
}
@ -290,7 +295,8 @@ public class BrowserHealthRecorder implements HealthRecorder, GeckoEventListener
return -1;
}
return this.env = EnvironmentBuilder.registerCurrentEnvironment(this.storage,
this.profileCache);
this.profileCache,
this.configProvider);
}
private static final String getTimesPath(final String profilePath) {

View File

@ -5,26 +5,25 @@
package org.mozilla.gecko.health;
import android.content.ContentProviderClient;
import android.content.Context;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.background.healthreport.EnvironmentBuilder;
import org.mozilla.gecko.background.common.GlobalConstants;
import org.mozilla.gecko.background.healthreport.AndroidConfigurationProvider;
import org.mozilla.gecko.background.healthreport.EnvironmentBuilder;
import org.mozilla.gecko.background.healthreport.EnvironmentBuilder.ConfigurationProvider;
import org.mozilla.gecko.background.healthreport.HealthReportConstants;
import org.mozilla.gecko.background.healthreport.HealthReportDatabaseStorage;
import org.mozilla.gecko.background.healthreport.HealthReportGenerator;
import org.mozilla.gecko.util.GeckoEventListener;
import org.mozilla.gecko.util.ThreadUtils;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.ContentProviderClient;
import android.content.Context;
import android.util.Log;
/**
* BrowserHealthReporter is the browser's interface to the Firefox Health
@ -89,7 +88,8 @@ public class BrowserHealthReporter implements GeckoEventListener {
}
HealthReportGenerator generator = new HealthReportGenerator(storage);
JSONObject report = generator.generateDocument(since, lastPingTime, profilePath);
ConfigurationProvider configProvider = new AndroidConfigurationProvider(context);
JSONObject report = generator.generateDocument(since, lastPingTime, profilePath, configProvider);
if (report == null) {
throw new IllegalStateException("Not enough profile information to generate report.");
}

View File

@ -14,6 +14,7 @@ import org.mozilla.gecko.FennecTalosAssert;
import android.app.Activity;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
public abstract class BaseRobocopTest extends ActivityInstrumentationTestCase2<Activity> {
public enum Type {
@ -21,6 +22,8 @@ public abstract class BaseRobocopTest extends ActivityInstrumentationTestCase2<A
TALOS
}
private static final String DEFAULT_ROOT_PATH = "/mnt/sdcard/tests";
protected Assert mAsserter;
protected String mLogFile;
@ -66,6 +69,10 @@ public abstract class BaseRobocopTest extends ActivityInstrumentationTestCase2<A
protected void setUp() throws Exception {
// Load config file from root path (set up by Python script).
mRootPath = FennecInstrumentationTestRunner.getFennecArguments().getString("deviceroot");
if (mRootPath == null) {
Log.w("Robocop", "Did not find deviceroot in arguments; falling back to: " + DEFAULT_ROOT_PATH);
mRootPath = DEFAULT_ROOT_PATH;
}
String configFile = FennecNativeDriver.getFile(mRootPath + "/robotium.config");
mConfig = FennecNativeDriver.convertTextToTable(configFile);
mLogFile = (String) mConfig.get("logfile");

View File

@ -64,7 +64,8 @@ skip-if = android_version == "10"
[testMailToContextMenu]
[testMasterPassword]
# disabled on 2.3; bug 979603
skip-if = android_version == "10"
# disabled on 4.0; bug 1006242
skip-if = android_version == "10" || android_version == "15"
[testNewTab]
# disabled on 2.3; bug 979621
skip-if = android_version == "10"

View File

@ -28,7 +28,7 @@ function makeObserver() {
};
add_task(function test_get_set() {
let branch = new SharedPreferences("test");
let branch = SharedPreferences.forAndroid("test");
branch.setBoolPref("boolKey", true);
branch.setCharPref("charKey", "string value");
@ -52,7 +52,7 @@ add_task(function test_get_set() {
});
add_task(function test_default() {
let branch = new SharedPreferences();
let branch = SharedPreferences.forAndroid();
branch.setBoolPref("boolKey", true);
branch.setCharPref("charKey", "string value");
@ -76,8 +76,8 @@ add_task(function test_default() {
});
add_task(function test_multiple_branches() {
let branch1 = new SharedPreferences("test1");
let branch2 = new SharedPreferences("test2");
let branch1 = SharedPreferences.forAndroid("test1");
let branch2 = SharedPreferences.forAndroid("test2");
branch1.setBoolPref("boolKey", true);
branch2.setBoolPref("boolKey", false);
@ -93,7 +93,7 @@ add_task(function test_multiple_branches() {
});
add_task(function test_add_remove_observer() {
let branch = new SharedPreferences("test");
let branch = SharedPreferences.forAndroid("test");
branch.setBoolPref("boolKey", false);
do_check_eq(branch.getBoolPref("boolKey"), false);
@ -145,7 +145,7 @@ add_task(function test_add_remove_observer() {
});
add_task(function test_observer_ignores() {
let branch = new SharedPreferences("test");
let branch = SharedPreferences.forAndroid("test");
branch.setCharPref("charKey", "first value");
do_check_eq(branch.getCharPref("charKey"), "first value");
@ -176,7 +176,7 @@ add_task(function test_observer_ignores() {
});
add_task(function test_observer_ignores_branches() {
let branch = new SharedPreferences("test");
let branch = SharedPreferences.forAndroid("test");
branch.setCharPref("charKey", "first value");
do_check_eq(branch.getCharPref("charKey"), "first value");
@ -186,9 +186,9 @@ add_task(function test_observer_ignores_branches() {
try {
// These should all be ignored.
let branch2 = new SharedPreferences("test2");
let branch2 = SharedPreferences.forAndroid("test2");
branch2.setCharPref("charKey", "a wrong value");
let branch3 = new SharedPreferences("test.2");
let branch3 = SharedPreferences.forAndroid("test.2");
branch3.setCharPref("charKey", "a different wrong value");
// This should not be ignored.
@ -208,4 +208,24 @@ add_task(function test_observer_ignores_branches() {
}
});
add_task(function test_scopes() {
let forApp = SharedPreferences.forApp();
let forProfile = SharedPreferences.forProfile();
let forProfileName = SharedPreferences.forProfileName("testProfile");
let forAndroidDefault = SharedPreferences.forAndroid();
let forAndroidBranch = SharedPreferences.forAndroid("testBranch");
forApp.setCharPref("charKey", "forApp");
forProfile.setCharPref("charKey", "forProfile");
forProfileName.setCharPref("charKey", "forProfileName");
forAndroidDefault.setCharPref("charKey", "forAndroidDefault");
forAndroidBranch.setCharPref("charKey", "forAndroidBranch");
do_check_eq(forApp.getCharPref("charKey"), "forApp");
do_check_eq(forProfile.getCharPref("charKey"), "forProfile");
do_check_eq(forProfileName.getCharPref("charKey"), "forProfileName");
do_check_eq(forAndroidDefault.getCharPref("charKey"), "forAndroidDefault");
do_check_eq(forAndroidBranch.getCharPref("charKey"), "forAndroidBranch");
});
run_next_test();

View File

@ -27,7 +27,7 @@ const EVENT_HEALTH_RESPONSE = "HealthReport:Response";
// about:healthreport prefs are stored in Firefox's default Android
// SharedPreferences.
let sharedPrefs = new SharedPreferences();
let sharedPrefs = SharedPreferences.forApp();
let healthReportWrapper = {
init: function () {
@ -190,4 +190,4 @@ let healthReportWrapper = {
};
window.addEventListener("load", healthReportWrapper.init.bind(healthReportWrapper), false);
window.addEventListener("unload", healthReportWrapper.uninit.bind(healthReportWrapper), false);
window.addEventListener("unload", healthReportWrapper.uninit.bind(healthReportWrapper), false);

View File

@ -457,7 +457,7 @@ let HomePanels = (function () {
_assertPanelExists(id);
let authKey = PREFS_PANEL_AUTH_PREFIX + id;
let sharedPrefs = new SharedPreferences();
let sharedPrefs = SharedPreferences.forProfile();
sharedPrefs.setBoolPref(authKey, isAuthenticated);
}
});

Some files were not shown because too many files have changed in this diff Show More