Merge m-c to inbound

This commit is contained in:
Wes Kocher 2014-03-27 20:21:50 -07:00
commit 9b1e1a9f9e
159 changed files with 3906 additions and 1543 deletions

View File

@ -1,7 +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";
// Disable tests below for now.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=987348
/*
var m = require("main");
var self = require("sdk/self");
@ -22,3 +26,4 @@ exports.testID = function(test) {
test.assertEqual(self.data.url("sample.html"),
"resource://reading-data-example-at-jetpack-dot-mozillalabs-dot-com/reading-data/data/sample.html");
};
*/

View File

@ -1,7 +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";
// Disable tests below for now.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=987348
/*
var m = require("main");
var self = require("sdk/self");
@ -19,3 +23,4 @@ exports.testMain = function(test) {
exports.testData = function(test) {
test.assert(self.data.load("panel.js").length > 0);
};
*/

View File

@ -9,66 +9,124 @@ module.metadata = {
};
let { Class } = require("./heritage");
let { on, off } = require('../system/events');
let unloadSubject = require('@loader/unload');
const { Class } = require("./heritage");
const { Observer, subscribe, unsubscribe, observe } = require("./observer");
const { isWeak, WeakReference } = require("./reference");
const method = require("../../method/core");
let disposables = WeakMap();
const unloadSubject = require('@loader/unload');
const addonUnloadTopic = "sdk:loader:destroy";
function initialize(instance) {
// Create an event handler that will dispose instance on unload.
function handler(event) {
if (event.subject.wrappedJSObject === unloadSubject) {
instance.destroy();
}
}
// Form weak reference between disposable instance and an unload event
// handler associated with it. This will make sure that event handler can't
// be garbage collected as long as instance is referenced. Also note that
// system events intentionally hold weak reference to an event handler, this
// will let GC claim both instance and an unload handler before actual add-on
// unload if instance contains no other references.
disposables.set(instance, handler);
on("sdk:loader:destroy", handler);
}
exports.initialize = initialize;
function dispose(instance) {
// Disposes given instance by removing it from weak map so that handler can
// be GC-ed even if references to instance are kept. Also unregister unload
// handler.
const uninstall = method("disposable/uninstall");
exports.uninstall = uninstall;
let handler = disposables.get(instance);
if (handler) off("sdk:loader:destroy", handler);
disposables.delete(instance);
}
const shutdown = method("disposable/shutdown");
exports.shutdown = shutdown;
const disable = method("disposable/disable");
exports.disable = disable;
const upgrade = method("disposable/upgrade");
exports.upgrade = upgrade;
const downgrade = method("disposable/downgrade");
exports.downgrade = downgrade;
const unload = method("disposable/unload");
exports.unload = unload;
const dispose = method("disposable/dispose");
exports.dispose = dispose;
dispose.define(Object, object => object.dispose());
const setup = method("disposable/setup");
exports.setup = setup;
setup.define(Object, (object, ...args) => object.setup(...args));
// Set's up disposable instance.
const setupDisposable = disposable => {
subscribe(disposable, addonUnloadTopic, isWeak(disposable));
};
// Tears down disposable instance.
const disposeDisposable = disposable => {
unsubscribe(disposable, addonUnloadTopic);
};
// Base type that takes care of disposing it's instances on add-on unload.
// Also makes sure to remove unload listener if it's already being disposed.
let Disposable = Class({
initialize: function setupDisposable() {
const Disposable = Class({
implements: [Observer],
initialize: function(...args) {
// First setup instance before initializing it's disposal. If instance
// fails to initialize then there is no instance to be disposed at the
// unload.
this.setup.apply(this, arguments);
initialize(this);
setup(this, ...args);
setupDisposable(this);
},
setup: function setup() {
// Implement your initialize logic here.
},
dispose: function dispose() {
// Implement your cleanup logic here.
},
destroy: function destroy() {
destroy: function(reason) {
// Destroying disposable removes unload handler so that attempt to dispose
// won't be made at unload & delegates to dispose.
if (disposables.has(this)) {
dispose(this);
this.dispose();
}
disposeDisposable(this);
unload(this, reason);
},
setup: function() {
// Implement your initialize logic here.
},
dispose: function() {
// Implement your cleanup logic here.
}
});
exports.Disposable = Disposable;
// Disposable instances observe add-on unload notifications in
// order to trigger `unload` on them.
observe.define(Disposable, (disposable, subject, topic, data) => {
const isUnloadTopic = topic === addonUnloadTopic;
const isUnloadSubject = subject.wrappedJSObject === unloadSubject;
if (isUnloadTopic && isUnloadSubject) {
unsubscribe(disposable, topic);
unload(disposable);
}
});
const unloaders = {
destroy: dispose,
uninstall: uninstall,
shutdown: shutdown,
disable: disable,
upgrade: upgrade,
downgrade: downgrade
}
const unloaded = new WeakMap();
unload.define(Disposable, (disposable, reason) => {
if (!unloaded.get(disposable)) {
unloaded.set(disposable, true);
// Pick an unload handler associated with an unload
// reason (falling back to destroy if not found) and
// delegate unloading to it.
const unload = unloaders[reason] || unloaders.destroy;
unload(disposable);
}
});
// If add-on is disabled munally, it's being upgraded, downgraded
// or uniststalled `dispose` is invoked to undo any changes that
// has being done by it in this session.
disable.define(Disposable, dispose);
downgrade.define(Disposable, dispose);
upgrade.define(Disposable, dispose);
uninstall.define(Disposable, dispose);
// If application is shut down no dispose is invoked as undo-ing
// changes made by instance is likely to just waste of resources &
// increase shutdown time. Although specefic components may choose
// to implement shutdown handler that does something better.
shutdown.define(Disposable, disposable => {});

View File

@ -0,0 +1,87 @@
/* 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": "experimental"
};
const { Cc, Ci, Cr } = require("chrome");
const { Class } = require("./heritage");
const { isWeak } = require("./reference");
const method = require("../../method/core");
const { addObserver, removeObserver } = Cc['@mozilla.org/observer-service;1'].
getService(Ci.nsIObserverService);
// This is a method that will be invoked when notification observer
// subscribed to occurs.
const observe = method("observer/observe");
exports.observe = observe;
// Method to subscribe to the observer notification.
const subscribe = method("observe/subscribe");
exports.subscribe = subscribe;
// Method to unsubscribe from the observer notifications.
const unsubscribe = method("observer/unsubscribe");
exports.unsubscribe = unsubscribe;
// This is wrapper class that takes a `delegate` and produces
// instance of `nsIObserver` which will delegate to a given
// object when observer notification occurs.
const ObserverDelegee = Class({
initialize: function(delegate) {
this.delegate = delegate;
},
QueryInterface: function(iid) {
const isObserver = iid.equals(Ci.nsIObserver);
const isWeakReference = iid.equals(Ci.nsISupportsWeakReference);
if (!isObserver && !isWeakReference)
throw Cr.NS_ERROR_NO_INTERFACE;
return this;
},
observe: function(subject, topic, data) {
observe(this.delegate, subject, topic, data);
}
});
// Class that can be either mixed in or inherited from in
// order to subscribe / unsubscribe for observer notifications.
const Observer = Class({});
exports.Observer = Observer;
// Weak maps that associates instance of `ObserverDelegee` with
// an actual observer. It ensures that `ObserverDelegee` instance
// won't be GC-ed until given `observer` is.
const subscribers = new WeakMap();
// Implementation of `subscribe` for `Observer` type just registers
// observer for an observer service. If `isWeak(observer)` is `true`
// observer service won't hold strong reference to a given `observer`.
subscribe.define(Observer, (observer, topic) => {
if (!subscribers.has(observer)) {
const delegee = new ObserverDelegee(observer);
subscribers.set(observer, delegee);
addObserver(delegee, topic, isWeak(observer));
}
});
// Unsubscribes `observer` from observer notifications for the
// given `topic`.
unsubscribe.define(Observer, (observer, topic) => {
const delegee = subscribers.get(observer);
if (delegee) {
subscribers.delete(observer);
removeObserver(delegee, topic);
}
});

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";
module.metadata = {
"stability": "experimental"
};
const method = require("../../method/core");
const { Class } = require("./heritage");
// Object that inherit or mix WeakRefence inn will register
// weak observes for system notifications.
const WeakReference = Class({});
exports.WeakReference = WeakReference;
// If `isWeak(object)` is `true` observer installed
// for such `object` will be weak, meaning that it will
// be GC-ed if nothing else but observer is observing it.
// By default everything except `WeakReference` will return
// `false`.
const isWeak = method("reference/weak?");
exports.isWeak = isWeak;
isWeak.define(Object, _ => false);
isWeak.define(WeakReference, _ => true);

View File

@ -13,6 +13,7 @@ const { contract } = require('./util/contract');
const { getAttachEventType, WorkerHost } = require('./content/utils');
const { Class } = require('./core/heritage');
const { Disposable } = require('./core/disposable');
const { WeakReference } = require('./core/reference');
const { Worker } = require('./content/worker');
const { EventTarget } = require('./event/target');
const { on, emit, once, setListeners } = require('./event/core');
@ -97,7 +98,8 @@ const PageMod = Class({
implements: [
modContract.properties(modelFor),
EventTarget,
Disposable
Disposable,
WeakReference
],
extends: WorkerHost(workerFor),
setup: function PageMod(options) {
@ -128,7 +130,7 @@ const PageMod = Class({
applyOnExistingDocuments(mod);
},
destroy: function destroy() {
dispose: function() {
let style = styleFor(this);
if (style)
detach(style);

View File

@ -13,6 +13,7 @@ const { filter, pipe, map, merge: streamMerge, stripListeners } = require('./eve
const { detach, attach, destroy, WorkerHost } = require('./content/utils');
const { Worker } = require('./content/worker');
const { Disposable } = require('./core/disposable');
const { WeakReference } = require('./core/reference');
const { EventTarget } = require('./event/target');
const { unload } = require('./system/unload');
const { events, streamEventsFrom } = require('./content/events');
@ -84,7 +85,8 @@ function isValidURL(page, url) !page.rules || page.rules.matchesAny(url)
const Page = Class({
implements: [
EventTarget,
Disposable
Disposable,
WeakReference
],
extends: WorkerHost(workerFor),
setup: function Page(options) {

View File

@ -21,6 +21,7 @@ const { merge } = require("./util/object");
const { WorkerHost, detach, attach, destroy } = require("./content/utils");
const { Worker } = require("./content/worker");
const { Disposable } = require("./core/disposable");
const { WeakReference } = require('./core/reference');
const { contract: loaderContract } = require("./content/loader");
const { contract } = require("./util/contract");
const { on, off, emit, setListeners } = require("./event/core");
@ -111,7 +112,8 @@ const Panel = Class({
// set and return values from model on get.
panelContract.properties(modelFor),
EventTarget,
Disposable
Disposable,
WeakReference
],
extends: WorkerHost(workerFor),
setup: function setup(options) {
@ -197,7 +199,7 @@ const Panel = Class({
let model = modelFor(this);
let view = viewFor(this);
let anchorView = getNodeView(anchor || options.position);
let anchorView = getNodeView(anchor || options.position || model.position);
options = merge({
position: model.position,

View File

@ -174,10 +174,11 @@ function state(contract) {
state: function state(target, state) {
let nativeTarget = target === 'window' ? getFocusedBrowser()
: target === 'tab' ? getMostRecentTab()
: target === this ? null
: viewFor(target);
if (!nativeTarget && target !== this && !isNil(target))
throw new Error('target not allowed.');
throw new Error(ERR_INVALID_TARGET);
target = nativeTarget || target;

View File

@ -1,13 +1,12 @@
/* 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";
// The widget module currently supports only Firefox.
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=560716
module.metadata = {
"stability": "stable",
"stability": "deprecated",
"engines": {
"Firefox": "*"
}
@ -39,6 +38,7 @@ const EVENTS = {
// normal toolbarbuttons. If they're any wider than this margin, we'll
// treat them as wide widgets instead, which fill up the width of the panel:
const AUSTRALIS_PANEL_WIDE_WIDGET_CUTOFF = 70;
const AUSTRALIS_PANEL_WIDE_CLASSNAME = "panel-wide-item";
const { validateOptions } = require("./deprecated/api-utils");
const panels = require("./panel");
@ -55,6 +55,11 @@ const unload = require("./system/unload");
const { getNodeView } = require("./view/core");
const prefs = require('./preferences/service');
require("./util/deprecate").deprecateUsage(
"The widget module is deprecated. " +
"Please consider using the sdk/ui module instead."
);
// Data types definition
const valid = {
number: { is: ["null", "undefined", "number"] },
@ -230,6 +235,8 @@ function haveInserted(widgetId) {
return prefs.has(INSERTION_PREF_ROOT + widgetId);
}
const isWide = node => node.classList.contains(AUSTRALIS_PANEL_WIDE_CLASSNAME);
/**
* Main Widget class: entry point of the widget API
*
@ -626,7 +633,7 @@ BrowserWindow.prototype = {
let placement = CustomizableUI.getPlacementOfWidget(id);
if (!placement) {
if (haveInserted(id))
if (haveInserted(id) || isWide(node))
return;
placement = {area: 'nav-bar', position: undefined};
@ -703,7 +710,7 @@ WidgetChrome.prototype._createNode = function WC__createNode() {
node.setAttribute("sdkstylewidget", "true");
if (this._widget.width > AUSTRALIS_PANEL_WIDE_WIDGET_CUTOFF) {
node.classList.add("panel-wide-item");
node.classList.add(AUSTRALIS_PANEL_WIDE_CLASSNAME);
}
// TODO move into a stylesheet, configurable by consumers.

View File

@ -11,8 +11,8 @@ const app = require("sdk/system/xul-app");
// module.metadata.engines
if (app.is('Firefox')) {
merge(module.exports,
require('./tests/test-places-bookmarks'),
require('./tests/test-places-events'),
require('./tests/test-places-bookmarks'),
require('./tests/test-places-favicon'),
require('./tests/test-places-history'),
require('./tests/test-places-host'),

View File

@ -18,6 +18,13 @@ const { setTimeout } = require('sdk/timers');
const { before, after } = require('sdk/test/utils');
const bmsrv = Cc['@mozilla.org/browser/nav-bookmarks-service;1'].
getService(Ci.nsINavBookmarksService);
const { release, platform } = require('node/os');
const isOSX10_6 = (() => {
let vString = release();
return vString && /darwin/.test(platform()) && /10\.6/.test(vString);
})();
const {
search
} = require('sdk/places/history');
@ -51,6 +58,14 @@ exports['test bookmark-item-added'] = function (assert, done) {
exports['test bookmark-item-changed'] = function (assert, done) {
let id;
let complete = makeCompleted(done);
// Due to bug 969616 and bug 971964, disabling tests in 10.6 (happens only
// in debug builds) to prevent intermittent failures
if (isOSX10_6) {
assert.ok(true, 'skipping test in OSX 10.6');
return done();
}
function handler ({type, data}) {
if (type !== 'bookmark-item-changed') return;
if (data.id !== id) return;
@ -87,6 +102,13 @@ exports['test bookmark-item-moved'] = function (assert, done) {
let complete = makeCompleted(done);
let previousIndex, previousParentId;
// Due to bug 969616 and bug 971964, disabling tests in 10.6 (happens only
// in debug builds) to prevent intermittent failures
if (isOSX10_6) {
assert.ok(true, 'skipping test in OSX 10.6');
return done();
}
function handler ({type, data}) {
if (type !== 'bookmark-item-moved') return;
if (data.id !== id) return;
@ -213,7 +235,7 @@ exports['test history-start-batch, history-end-batch, history-start-clear'] = fu
off(clearEvent, 'data', clearHandler);
complete();
}
on(startEvent, 'data', startHandler);
on(clearEvent, 'data', clearHandler);

View File

@ -5,9 +5,150 @@
const { Loader } = require("sdk/test/loader");
const { Class } = require("sdk/core/heritage");
const { Disposable } = require("sdk/core/disposable");
const { Cc, Ci, Cu } = require("chrome");
const { setTimeout } = require("sdk/timers");
exports["test destroy reasons"] = assert => {
const Foo = Class({
extends: Disposable,
dispose: function() {
disposals = disposals + 1;
}
});
let disposals = 0;
const f1 = new Foo();
f1.destroy();
assert.equal(disposals, 1, "disposed on destroy");
f1.destroy();
assert.equal(disposals, 1, "second destroy is ignored");
disposals = 0;
const f2 = new Foo();
f2.destroy("uninstall");
assert.equal(disposals, 1, "uninstall invokes disposal");
f2.destroy("uninstall")
f2.destroy();
assert.equal(disposals, 1, "disposal happens just once");
disposals = 0;
const f3 = new Foo();
f3.destroy("shutdown");
assert.equal(disposals, 0, "shutdown doesn't invoke disposal");
f3.destroy();
assert.equal(disposals, 0, "shutdown already skipped disposal");
disposals = 0;
const f4 = new Foo();
f4.destroy("disable");
assert.equal(disposals, 1, "disable invokes disposal");
f4.destroy("disable")
f4.destroy();
assert.equal(disposals, 1, "destroy happens just once");
disposals = 0;
const f5 = new Foo();
f5.destroy("disable");
assert.equal(disposals, 1, "disable invokes disposal");
f5.destroy("disable")
f5.destroy();
assert.equal(disposals, 1, "destroy happens just once");
disposals = 0;
const f6 = new Foo();
f6.destroy("upgrade");
assert.equal(disposals, 1, "upgrade invokes disposal");
f6.destroy("upgrade")
f6.destroy();
assert.equal(disposals, 1, "destroy happens just once");
disposals = 0;
const f7 = new Foo();
f7.destroy("downgrade");
assert.equal(disposals, 1, "downgrade invokes disposal");
f7.destroy("downgrade")
f7.destroy();
assert.equal(disposals, 1, "destroy happens just once");
disposals = 0;
const f8 = new Foo();
f8.destroy("whatever");
assert.equal(disposals, 1, "unrecognized reason invokes disposal");
f8.destroy("meh")
f8.destroy();
assert.equal(disposals, 1, "destroy happens just once");
};
exports["test different unload hooks"] = assert => {
const { uninstall, shutdown, disable, upgrade,
downgrade, dispose } = require("sdk/core/disposable");
const UberUnload = Class({
extends: Disposable,
setup: function() {
this.log = [];
}
});
uninstall.define(UberUnload, x => x.log.push("uninstall"));
shutdown.define(UberUnload, x => x.log.push("shutdown"));
disable.define(UberUnload, x => x.log.push("disable"));
upgrade.define(UberUnload, x => x.log.push("upgrade"));
downgrade.define(UberUnload, x => x.log.push("downgrade"));
dispose.define(UberUnload, x => x.log.push("dispose"));
const u1 = new UberUnload();
u1.destroy("uninstall");
u1.destroy();
u1.destroy("shutdown");
assert.deepEqual(u1.log, ["uninstall"], "uninstall hook invoked");
const u2 = new UberUnload();
u2.destroy("shutdown");
u2.destroy();
u2.destroy("uninstall");
assert.deepEqual(u2.log, ["shutdown"], "shutdown hook invoked");
const u3 = new UberUnload();
u3.destroy("disable");
u3.destroy();
u3.destroy("uninstall");
assert.deepEqual(u3.log, ["disable"], "disable hook invoked");
const u4 = new UberUnload();
u4.destroy("upgrade");
u4.destroy();
u4.destroy("uninstall");
assert.deepEqual(u4.log, ["upgrade"], "upgrade hook invoked");
const u5 = new UberUnload();
u5.destroy("downgrade");
u5.destroy();
u5.destroy("uninstall");
assert.deepEqual(u5.log, ["downgrade"], "downgrade hook invoked");
const u6 = new UberUnload();
u6.destroy();
u6.destroy();
u6.destroy("uninstall");
assert.deepEqual(u6.log, ["dispose"], "dispose hook invoked");
const u7 = new UberUnload();
u7.destroy("whatever");
u7.destroy();
u7.destroy("uninstall");
assert.deepEqual(u7.log, ["dispose"], "dispose hook invoked");
};
exports["test disposables are desposed on unload"] = function(assert) {
let loader = Loader(module);
let { Disposable } = loader.require("sdk/core/disposable");
@ -84,6 +225,7 @@ exports["test destroyed windows dispose before unload"] = function(assert) {
exports["test disposables are GC-able"] = function(assert, done) {
let loader = Loader(module);
let { Disposable } = loader.require("sdk/core/disposable");
let { WeakReference } = loader.require("sdk/core/reference");
let arg1 = {}
let arg2 = 2
@ -91,6 +233,7 @@ exports["test disposables are GC-able"] = function(assert, done) {
let Foo = Class({
extends: Disposable,
implements: [WeakReference],
setup: function setup(a, b) {
assert.equal(a, arg1,
"arguments passed to constructur is passed to setup");
@ -106,7 +249,7 @@ exports["test disposables are GC-able"] = function(assert, done) {
this.fooed = false
disposals = disposals + 1
}
})
});
let foo1 = Foo(arg1, arg2)
let foo2 = Foo(arg1, arg2)

View File

@ -0,0 +1,123 @@
/* 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 { Loader } = require("sdk/test/loader");
const { isWeak, WeakReference } = require("sdk/core/reference");
const { subscribe, unsubscribe,
observe, Observer } = require("sdk/core/observer");
const { Class } = require("sdk/core/heritage");
const { Cc, Ci, Cu } = require("chrome");
const { notifyObservers } = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
const message = x => ({wrappedJSObject: x});
exports["test subscribe unsubscribe"] = assert => {
const topic = Date.now().toString(32);
const Subscriber = Class({
extends: Observer,
initialize: function(observe) {
this.observe = observe;
}
});
observe.define(Subscriber, (x, subject, _, data) =>
x.observe(subject.wrappedJSObject.x));
let xs = [];
const x = Subscriber((...rest) => xs.push(...rest));
let ys = [];
const y = Subscriber((...rest) => ys.push(...rest));
const publish = (topic, data) =>
notifyObservers(message(data), topic, null);
publish({x:0});
subscribe(x, topic);
publish(topic, {x:1});
subscribe(y, topic);
publish(topic, {x:2});
publish(topic + "!", {x: 2.5});
unsubscribe(x, topic);
publish(topic, {x:3});
subscribe(y, topic);
publish(topic, {x:4});
subscribe(x, topic);
publish(topic, {x:5});
unsubscribe(x, topic);
unsubscribe(y, topic);
publish(topic, {x:6});
assert.deepEqual(xs, [1, 2, 5]);
assert.deepEqual(ys, [2, 3, 4, 5]);
}
exports["test weak observers are GC-ed on unload"] = (assert, end) => {
const topic = Date.now().toString(32);
const loader = Loader(module);
const { Observer, observe,
subscribe, unsubscribe } = loader.require("sdk/core/observer");
const { isWeak, WeakReference } = loader.require("sdk/core/reference");
const MyObserver = Class({
extends: Observer,
initialize: function(observe) {
this.observe = observe;
}
});
observe.define(MyObserver, (x, ...rest) => x.observe(...rest));
const MyWeakObserver = Class({
extends: MyObserver,
implements: [WeakReference]
});
let xs = [];
let ys = [];
let x = new MyObserver((subject, topic, data) => {
xs.push(subject.wrappedJSObject, topic, data);
});
let y = new MyWeakObserver((subject, topic, data) => {
ys.push(subject.wrappedJSObject, topic, data);
});
subscribe(x, topic);
subscribe(y, topic);
notifyObservers(message({ foo: 1 }), topic, null);
x = null;
y = null;
loader.unload();
Cu.schedulePreciseGC(() => {
notifyObservers(message({ bar: 2 }), topic, ":)");
assert.deepEqual(xs, [{ foo: 1 }, topic, null,
{ bar: 2 }, topic, ":)"],
"non week observer is kept");
assert.deepEqual(ys, [{ foo: 1 }, topic, null],
"week observer was GC-ed");
end();
});
};
require("sdk/test").run(exports);

View File

@ -859,6 +859,8 @@ exports['test passing DOM node as first argument'] = function (assert, done) {
let shown = defer();
function onMessage(type, message) {
if (type != 'warn') return;
let warning = 'Passing a DOM node to Panel.show() method is an unsupported ' +
'feature that will be soon replaced. ' +
'See: https://bugzilla.mozilla.org/show_bug.cgi?id=878877';

View File

@ -0,0 +1,99 @@
/* 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 { isWeak, WeakReference } = require("sdk/core/reference");
const { Class } = require("sdk/core/heritage");
exports["test non WeakReferences"] = assert => {
assert.equal(isWeak({}), false,
"regular objects aren't weak");
assert.equal(isWeak([]), false,
"arrays aren't weak");
};
exports["test a WeakReference"] = assert => {
assert.equal(isWeak(new WeakReference()), true,
"instances of WeakReferences are weak");
};
exports["test a subclass"] = assert => {
const SubType = Class({
extends: WeakReference,
foo: function() {
}
});
const x = new SubType();
assert.equal(isWeak(x), true,
"subtypes of WeakReferences are weak");
const SubSubType = Class({
extends: SubType
});
const y = new SubSubType();
assert.equal(isWeak(y), true,
"sub subtypes of WeakReferences are weak");
};
exports["test a mixin"] = assert => {
const Mixer = Class({
implements: [WeakReference]
});
assert.equal(isWeak(new Mixer()), true,
"instances with mixed WeakReference are weak");
const Mux = Class({});
const MixMux = Class({
extends: Mux,
implements: [WeakReference]
});
assert.equal(isWeak(new MixMux()), true,
"instances with mixed WeakReference that " +
"inheret from other are weak");
const Base = Class({});
const ReMix = Class({
extends: Base,
implements: [MixMux]
});
assert.equal(isWeak(new ReMix()), true,
"subtime of mix is still weak");
};
exports["test an override"] = assert => {
const SubType = Class({ extends: WeakReference });
isWeak.define(SubType, x => false);
assert.equal(isWeak(new SubType()), false,
"behavior of isWeak can still be overrided on subtype");
const Mixer = Class({ implements: [WeakReference] });
const SubMixer = Class({ extends: Mixer });
isWeak.define(SubMixer, x => false);
assert.equal(isWeak(new SubMixer()), false,
"behavior of isWeak can still be overrided on mixed type");
};
exports["test an opt-in"] = assert => {
var BaseType = Class({});
var SubType = Class({ extends: BaseType });
isWeak.define(SubType, x => true);
assert.equal(isWeak(new SubType()), true,
"isWeak can be just implemented");
var x = {};
isWeak.implement(x, x => true);
assert.equal(isWeak(x), true,
"isWeak can be implemented per instance");
};
require("sdk/test").run(exports);

View File

@ -288,6 +288,43 @@ exports['test button global state updated'] = function(assert) {
loader.unload();
}
exports['test button global state set and get with state method'] = function(assert) {
let loader = Loader(module);
let { ActionButton } = loader.require('sdk/ui');
let button = ActionButton({
id: 'my-button-16',
label: 'my button',
icon: './icon.png'
});
// read the button's state
let state = button.state(button);
assert.equal(state.label, 'my button',
'label is correct');
assert.equal(state.icon, './icon.png',
'icon is correct');
assert.equal(state.disabled, false,
'disabled is correct');
// set the new button's state
button.state(button, {
label: 'New label',
icon: './new-icon.png',
disabled: true
});
assert.equal(button.label, 'New label',
'label is updated');
assert.equal(button.icon, './new-icon.png',
'icon is updated');
assert.equal(button.disabled, true,
'disabled is updated');
loader.unload();
}
exports['test button global state updated on multiple windows'] = function(assert, done) {
let loader = Loader(module);
let { ActionButton } = loader.require('sdk/ui');

View File

@ -298,6 +298,43 @@ exports['test button global state updated'] = function(assert) {
loader.unload();
}
exports['test button global state set and get with state method'] = function(assert) {
let loader = Loader(module);
let { ToggleButton } = loader.require('sdk/ui');
let button = ToggleButton({
id: 'my-button-16',
label: 'my button',
icon: './icon.png'
});
// read the button's state
let state = button.state(button);
assert.equal(state.label, 'my button',
'label is correct');
assert.equal(state.icon, './icon.png',
'icon is correct');
assert.equal(state.disabled, false,
'disabled is correct');
// set the new button's state
button.state(button, {
label: 'New label',
icon: './new-icon.png',
disabled: true
});
assert.equal(button.label, 'New label',
'label is updated');
assert.equal(button.icon, './new-icon.png',
'icon is updated');
assert.equal(button.disabled, true,
'disabled is updated');
loader.unload();
};
exports['test button global state updated on multiple windows'] = function(assert, done) {
let loader = Loader(module);
let { ToggleButton } = loader.require('sdk/ui');
@ -1027,35 +1064,62 @@ exports['test buttons can have anchored panels'] = function(assert, done) {
let { identify } = loader.require('sdk/ui/id');
let { getActiveView } = loader.require('sdk/view/core');
let button = ToggleButton({
let b1 = ToggleButton({
id: 'my-button-22',
label: 'my button',
icon: './icon.png',
onChange: ({checked}) => checked && panel.show({position: button})
onChange: ({checked}) => checked && panel.show()
});
let panel = Panel();
let b2 = ToggleButton({
id: 'my-button-23',
label: 'my button',
icon: './icon.png',
onChange: ({checked}) => checked && panel.show({position: b2})
});
let panel = Panel({
position: b1
});
let { document } = getMostRecentBrowserWindow();
let b1Node = document.getElementById(identify(b1));
let b2Node = document.getElementById(identify(b2));
let panelNode = getActiveView(panel);
panel.once('show', () => {
let { document } = getMostRecentBrowserWindow();
let buttonNode = document.getElementById(identify(button));
let panelNode = getActiveView(panel);
assert.ok(button.state('window').checked,
assert.ok(b1.state('window').checked,
'button is checked');
assert.equal(panelNode.getAttribute('type'), 'arrow',
'the panel is a arrow type');
assert.strictEqual(buttonNode, panelNode.anchorNode,
'the panel is anchored properly to the button');
assert.strictEqual(b1Node, panelNode.anchorNode,
'the panel is anchored properly to the button given in costructor');
loader.unload();
panel.hide();
done();
panel.once('show', () => {
assert.ok(b2.state('window').checked,
'button is checked');
assert.equal(panelNode.getAttribute('type'), 'arrow',
'the panel is a arrow type');
// test also that the button passed in `show` method, takes the precedence
// over the button set in panel's constructor.
assert.strictEqual(b2Node, panelNode.anchorNode,
'the panel is anchored properly to the button passed to show method');
loader.unload();
done();
});
b2.click();
});
button.click();
b1.click();
}
require('sdk/test').run(exports);

View File

@ -9,13 +9,11 @@ module.metadata = {
}
};
const widgets = require("sdk/widget");
const { Cc, Ci, Cu } = require("chrome");
const { Loader } = require('sdk/test/loader');
const { LoaderWithHookedConsole } = require('sdk/test/loader');
const url = require("sdk/url");
const timer = require("sdk/timers");
const self = require("sdk/self");
const windowUtils = require("sdk/deprecated/window-utils");
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
const { close, open, focus } = require("sdk/window/helpers");
const tabs = require("sdk/tabs/utils");
@ -43,8 +41,24 @@ function openNewWindowTab(url, options) {
});
}
exports.testDeprecationMessage = function(assert, done) {
let { loader } = LoaderWithHookedConsole(module, onMessage);
// Intercept all console method calls
let calls = [];
function onMessage(type, msg) {
assert.equal(type, 'error', 'the only message is an error');
assert.ok(/^DEPRECATED:/.test(msg), 'deprecated');
loader.unload();
done();
}
loader.require('sdk/widget');
}
exports.testConstructor = function(assert, done) {
let browserWindow = windowUtils.activeBrowserWindow;
let { loader: loader0 } = LoaderWithHookedConsole(module);
const widgets = loader0.require("sdk/widget");
let browserWindow = getMostRecentBrowserWindow();
let doc = browserWindow.document;
let AddonsMgrListener;
@ -78,7 +92,7 @@ exports.testConstructor = function(assert, done) {
assert.equal(widgetCount(), widgetStartCount, "panel has correct number of child elements after destroy");
// Test automatic widget destroy on unload
let loader = Loader(module);
let { loader } = LoaderWithHookedConsole(module);
let widgetsFromLoader = loader.require("sdk/widget");
let widgetStartCount = widgetCount();
let w = widgetsFromLoader.Widget({ id: "destroy-on-unload", label: "foo", content: "bar" });
@ -192,8 +206,10 @@ exports.testConstructor = function(assert, done) {
let tests = [];
function nextTest() {
assert.equal(widgetCount(), 0, "widget in last test property cleaned itself up");
if (!tests.length)
if (!tests.length) {
loader0.unload();
done();
}
else
timer.setTimeout(tests.shift(), 0);
}
@ -535,7 +551,7 @@ exports.testConstructor = function(assert, done) {
contentScript: "self.port.on('event', function () self.port.emit('event'))"
};
let widget = testSingleWidget(w1Opts);
let windows = require("sdk/windows").browserWindows;
let windows = loader0.require("sdk/windows").browserWindows;
// 2/ Retrieve a WidgetView for the initial browser window
let acceptDetach = false;
@ -627,16 +643,16 @@ exports.testConstructor = function(assert, done) {
id: "text-test-width",
label: "test widget.width",
content: "test width",
width: 200,
width: 64,
contentScript: "self.postMessage(1)",
contentScriptWhen: "ready",
onMessage: function(message) {
assert.equal(this.width, 200, 'width is 200');
assert.equal(this.width, 64, 'width is 64');
let node = widgetNode(0);
assert.equal(this.width, node.style.minWidth.replace("px", ""));
assert.equal(this.width, node.firstElementChild.style.width.replace("px", ""));
this.width = 300;
this.width = 48;
assert.equal(this.width, node.style.minWidth.replace("px", ""));
assert.equal(this.width, node.firstElementChild.style.width.replace("px", ""));
@ -677,16 +693,18 @@ exports.testConstructor = function(assert, done) {
};
exports.testWidgetWithValidPanel = function(assert, done) {
const widgets = require("sdk/widget");
let { loader } = LoaderWithHookedConsole(module);
const { Widget } = loader.require("sdk/widget");
const { Panel } = loader.require("sdk/panel");
let widget1 = widgets.Widget({
let widget1 = Widget({
id: "testWidgetWithValidPanel",
label: "panel widget 1",
content: "<div id='me'>foo</div>",
contentScript: "var evt = new MouseEvent('click', {button: 0});" +
"document.body.dispatchEvent(evt);",
contentScriptWhen: "end",
panel: require("sdk/panel").Panel({
panel: Panel({
contentURL: "data:text/html;charset=utf-8,<body>Look ma, a panel!</body>",
onShow: function() {
let { document } = getMostRecentBrowserWindow();
@ -697,6 +715,7 @@ exports.testWidgetWithValidPanel = function(assert, done) {
assert.strictEqual(panelEle.anchorNode, widgetEle, 'the panel is properly anchored to the widget');
widget1.destroy();
loader.unload();
assert.pass("panel displayed on click");
done();
}
@ -705,7 +724,9 @@ exports.testWidgetWithValidPanel = function(assert, done) {
};
exports.testWidgetWithInvalidPanel = function(assert) {
const widgets = require("sdk/widget");
let { loader } = LoaderWithHookedConsole(module);
const widgets = loader.require("sdk/widget");
assert.throws(
function() {
widgets.Widget({
@ -716,10 +737,14 @@ exports.testWidgetWithInvalidPanel = function(assert) {
},
/^The option \"panel\" must be one of the following types: null, undefined, object$/,
"widget.panel must be a Panel object");
loader.unload();
};
exports.testPanelWidget3 = function testPanelWidget3(assert, done) {
const widgets = require("sdk/widget");
let { loader } = LoaderWithHookedConsole(module);
const widgets = loader.require("sdk/widget");
const { Panel } = loader.require("sdk/panel");
let onClickCalled = false;
let widget3 = widgets.Widget({
id: "panel3",
@ -732,13 +757,14 @@ exports.testPanelWidget3 = function testPanelWidget3(assert, done) {
onClickCalled = true;
this.panel.show();
},
panel: require("sdk/panel").Panel({
panel: Panel({
contentURL: "data:text/html;charset=utf-8,<body>Look ma, a panel!</body>",
onShow: function() {
assert.ok(
onClickCalled,
"onClick called on click for widget with both panel and onClick");
widget3.destroy();
loader.unload();
done();
}
})
@ -747,7 +773,9 @@ exports.testPanelWidget3 = function testPanelWidget3(assert, done) {
exports.testWidgetWithPanelInMenuPanel = function(assert, done) {
const { CustomizableUI } = Cu.import("resource:///modules/CustomizableUI.jsm", {});
const widgets = require("sdk/widget");
let { loader } = LoaderWithHookedConsole(module);
const widgets = loader.require("sdk/widget");
const { Panel } = loader.require("sdk/panel");
let widget1 = widgets.Widget({
id: "panel1",
@ -760,7 +788,7 @@ exports.testWidgetWithPanelInMenuPanel = function(assert, done) {
});
},
contentScriptWhen: "end",
panel: require("sdk/panel").Panel({
panel: Panel({
contentURL: "data:text/html;charset=utf-8,<body>Look ma, a panel!</body>",
onShow: function() {
let { document } = getMostRecentBrowserWindow();
@ -771,6 +799,7 @@ exports.testWidgetWithPanelInMenuPanel = function(assert, done) {
'the panel is anchored to the panel menu button instead of widget');
widget1.destroy();
loader.unload();
done();
}
})
@ -797,8 +826,10 @@ exports.testWidgetWithPanelInMenuPanel = function(assert, done) {
};
exports.testWidgetMessaging = function testWidgetMessaging(assert, done) {
let { loader } = LoaderWithHookedConsole(module);
const widgets = loader.require("sdk/widget");
let origMessage = "foo";
const widgets = require("sdk/widget");
let widget = widgets.Widget({
id: "widget-messaging",
label: "foo",
@ -811,6 +842,7 @@ exports.testWidgetMessaging = function testWidgetMessaging(assert, done) {
else {
assert.equal(origMessage, message);
widget.destroy();
loader.unload();
done();
}
}
@ -818,7 +850,9 @@ exports.testWidgetMessaging = function testWidgetMessaging(assert, done) {
};
exports.testWidgetViews = function testWidgetViews(assert, done) {
const widgets = require("sdk/widget");
let { loader } = LoaderWithHookedConsole(module);
const widgets = loader.require("sdk/widget");
let widget = widgets.Widget({
id: "widget-views",
label: "foo",
@ -833,6 +867,7 @@ exports.testWidgetViews = function testWidgetViews(assert, done) {
});
view.on("detach", function () {
assert.pass("WidgetView destroyed");
loader.unload();
done();
});
}
@ -840,7 +875,10 @@ exports.testWidgetViews = function testWidgetViews(assert, done) {
};
exports.testWidgetViewsUIEvents = function testWidgetViewsUIEvents(assert, done) {
const widgets = require("sdk/widget");
let { loader } = LoaderWithHookedConsole(module);
const widgets = loader.require("sdk/widget");
const { browserWindows } = loader.require("sdk/windows");
let view = null;
let widget = widgets.Widget({
id: "widget-view-ui-events",
@ -856,17 +894,20 @@ exports.testWidgetViewsUIEvents = function testWidgetViewsUIEvents(assert, done)
onClick: function (eventView) {
assert.equal(view, eventView,
"event first argument is equal to the WidgetView");
let view2 = widget.getView(require("sdk/windows").browserWindows.activeWindow);
let view2 = widget.getView(browserWindows.activeWindow);
assert.equal(view, view2,
"widget.getView return the same WidgetView");
widget.destroy();
loader.unload();
done();
}
});
};
exports.testWidgetViewsCustomEvents = function testWidgetViewsCustomEvents(assert, done) {
const widgets = require("sdk/widget");
let { loader } = LoaderWithHookedConsole(module);
const widgets = loader.require("sdk/widget");
let widget = widgets.Widget({
id: "widget-view-custom-events",
label: "foo",
@ -883,33 +924,37 @@ exports.testWidgetViewsCustomEvents = function testWidgetViewsCustomEvents(asser
widget.port.on("event", function (data) {
assert.equal(data, "ok", "event argument is valid on Widget");
widget.destroy();
loader.unload();
done();
});
};
exports.testWidgetViewsTooltip = function testWidgetViewsTooltip(assert, done) {
const widgets = require("sdk/widget");
let { loader } = LoaderWithHookedConsole(module);
const widgets = loader.require("sdk/widget");
const { browserWindows } = loader.require("sdk/windows");
let widget = new widgets.Widget({
id: "widget-views-tooltip",
label: "foo",
content: "foo"
});
let view = widget.getView(require("sdk/windows").browserWindows.activeWindow);
let view = widget.getView(browserWindows.activeWindow);
widget.tooltip = null;
assert.equal(view.tooltip, "foo",
"view tooltip defaults to base widget label");
assert.equal(widget.tooltip, "foo",
"tooltip defaults to base widget label");
widget.destroy();
loader.unload();
done();
};
exports.testWidgetMove = function testWidgetMove(assert, done) {
let windowUtils = require("sdk/deprecated/window-utils");
let widgets = require("sdk/widget");
let { loader } = LoaderWithHookedConsole(module);
const widgets = loader.require("sdk/widget");
let browserWindow = windowUtils.activeBrowserWindow;
let browserWindow = getMostRecentBrowserWindow();
let doc = browserWindow.document;
let label = "unique-widget-label";
@ -939,6 +984,7 @@ exports.testWidgetMove = function testWidgetMove(assert, done) {
else {
assert.equal(origMessage, message, "Got message after node move");
widget.destroy();
loader.unload();
done();
}
}
@ -953,16 +999,17 @@ consider the content change a navigation change, so doesn't load
the new content.
*/
exports.testWidgetWithPound = function testWidgetWithPound(assert, done) {
let { loader } = LoaderWithHookedConsole(module);
const widgets = loader.require("sdk/widget");
function getWidgetContent(widget) {
let windowUtils = require("sdk/deprecated/window-utils");
let browserWindow = windowUtils.activeBrowserWindow;
let browserWindow = getMostRecentBrowserWindow();
let doc = browserWindow.document;
let widgetNode = doc.querySelector('toolbaritem[label="' + widget.label + '"]');
assert.ok(widgetNode, 'found widget node in the front-end');
return widgetNode.firstChild.contentDocument.body.innerHTML;
}
let widgets = require("sdk/widget");
let count = 0;
let widget = widgets.Widget({
id: "1",
@ -977,6 +1024,7 @@ exports.testWidgetWithPound = function testWidgetWithPound(assert, done) {
else {
assert.equal(getWidgetContent(widget), "foo#", "content updated to pound?");
widget.destroy();
loader.unload();
done();
}
}
@ -984,7 +1032,10 @@ exports.testWidgetWithPound = function testWidgetWithPound(assert, done) {
};
exports.testContentScriptOptionsOption = function(assert, done) {
let widget = require("sdk/widget").Widget({
let { loader } = LoaderWithHookedConsole(module);
const { Widget } = loader.require("sdk/widget");
let widget = Widget({
id: "widget-script-options",
label: "fooz",
content: "fooz",
@ -998,26 +1049,34 @@ exports.testContentScriptOptionsOption = function(assert, done) {
assert.equal( msg[1].b.join(), '1,2,3', 'array and numbers in contentScriptOptions' );
assert.equal( msg[1].c, 'string', 'string in contentScriptOptions' );
widget.destroy();
loader.unload();
done();
}
});
};
exports.testOnAttachWithoutContentScript = function(assert, done) {
let widget = require("sdk/widget").Widget({
let { loader } = LoaderWithHookedConsole(module);
const { Widget } = loader.require("sdk/widget");
let widget = Widget({
id: "onAttachNoCS",
label: "onAttachNoCS",
content: "onAttachNoCS",
onAttach: function (view) {
assert.pass("received attach event");
widget.destroy();
loader.unload();
done();
}
});
};
exports.testPostMessageOnAttach = function(assert, done) {
let widget = require("sdk/widget").Widget({
let { loader } = LoaderWithHookedConsole(module);
const { Widget } = loader.require("sdk/widget");
let widget = Widget({
id: "onAttach",
label: "onAttach",
content: "onAttach",
@ -1031,15 +1090,19 @@ exports.testPostMessageOnAttach = function(assert, done) {
onMessage: function (msg) {
assert.equal( msg, "ok", "postMessage works on `attach` event");
widget.destroy();
loader.unload();
done();
}
});
};
exports.testPostMessageOnLocationChange = function(assert, done) {
let { loader } = LoaderWithHookedConsole(module);
const { Widget } = loader.require("sdk/widget");
let attachEventCount = 0;
let messagesCount = 0;
let widget = require("sdk/widget").Widget({
let widget = Widget({
id: "onLocationChange",
label: "onLocationChange",
content: "onLocationChange",
@ -1065,6 +1128,7 @@ exports.testPostMessageOnLocationChange = function(assert, done) {
assert.equal(msg, "ok",
"We receive the message sent to the 2nd document");
widget.destroy();
loader.unload();
done();
}
}
@ -1072,10 +1136,13 @@ exports.testPostMessageOnLocationChange = function(assert, done) {
};
exports.testSVGWidget = function(assert, done) {
let { loader } = LoaderWithHookedConsole(module);
const { Widget } = loader.require("sdk/widget");
// use of capital SVG here is intended, that was failing..
let SVG_URL = fixtures.url("mofo_logo.SVG");
let widget = require("sdk/widget").Widget({
let widget = Widget({
id: "mozilla-svg-logo",
label: "moz foundation logo",
contentURL: SVG_URL,
@ -1084,16 +1151,19 @@ exports.testSVGWidget = function(assert, done) {
widget.destroy();
assert.equal(data.count, 1, 'only one image');
assert.equal(data.src, SVG_URL, 'only one image');
loader.unload();
done();
}
});
};
exports.testReinsertion = function(assert, done) {
let { loader } = LoaderWithHookedConsole(module);
const { Widget } = loader.require("sdk/widget");
const WIDGETID = "test-reinsertion";
let windowUtils = require("sdk/deprecated/window-utils");
let browserWindow = windowUtils.activeBrowserWindow;
let widget = require("sdk/widget").Widget({
let browserWindow = getMostRecentBrowserWindow();
let widget = Widget({
id: "test-reinsertion",
label: "test reinsertion",
content: "Test",
@ -1105,8 +1175,39 @@ exports.testReinsertion = function(assert, done) {
openNewWindowTab("about:blank", { inNewWindow: true, onLoad: function(e) {
assert.equal(e.target.defaultView.document.getElementById(realWidgetId), null);
close(e.target.defaultView).then(done);
close(e.target.defaultView).then(_ => {
loader.unload();
done();
});
}});
};
exports.testWideWidget = function testWideWidget(assert) {
let { loader } = LoaderWithHookedConsole(module);
const widgets = loader.require("sdk/widget");
const { document, CustomizableUI, gCustomizeMode, setTimeout } = getMostRecentBrowserWindow();
let wideWidget = widgets.Widget({
id: "my-wide-widget",
label: "wide-wdgt",
content: "foo",
width: 200
});
let widget = widgets.Widget({
id: "my-regular-widget",
label: "reg-wdgt",
content: "foo"
});
let wideWidgetNode = document.querySelector("toolbaritem[label=wide-wdgt]");
let widgetNode = document.querySelector("toolbaritem[label=reg-wdgt]");
assert.equal(wideWidgetNode, null,
"Wide Widget are not added to UI");
assert.notEqual(widgetNode, null,
"regular size widget are in the UI");
};
require("sdk/test").run(exports);

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="287195d1fed2e6c883745d7091a4c05e56c4dbb7"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8248f8da7a29757da9f7fd748a0d8d70f2bd4610"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="12408eb142739c7de87ab7ee0d0d2854d5c298f3"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="89c5816399e71bda92a8959b5b771c04d6672ea3"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>

View File

@ -16,6 +16,7 @@
"{workdir}/out/target/product/generic/*.tar.bz2",
"{workdir}/out/target/product/generic/tests/*.zip",
"{objdir}/dist/b2g-*.crashreporter-symbols.zip",
"{objdir}/dist/b2g-*.tar.gz",
"{workdir}/sources.xml"
],
"upload_platform": "emulator-jb",

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="287195d1fed2e6c883745d7091a4c05e56c4dbb7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8248f8da7a29757da9f7fd748a0d8d70f2bd4610"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="89c5816399e71bda92a8959b5b771c04d6672ea3"/>
<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

@ -16,6 +16,7 @@
"{workdir}/out/target/product/generic/*.tar.bz2",
"{workdir}/out/target/product/generic/tests/*.zip",
"{objdir}/dist/b2g-*.crashreporter-symbols.zip",
"{objdir}/dist/b2g-*.tar.gz",
"{workdir}/sources.xml"
],
"upload_platform": "emulator-kk",

View File

@ -15,14 +15,14 @@
<project name="platform_build" path="build" remote="b2g" revision="a9e08b91e9cd1f0930f16cfc49ec72f63575d5fe">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="287195d1fed2e6c883745d7091a4c05e56c4dbb7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8248f8da7a29757da9f7fd748a0d8d70f2bd4610"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
<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="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="89c5816399e71bda92a8959b5b771c04d6672ea3"/>
<!-- 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

@ -16,6 +16,7 @@
"{workdir}/out/target/product/generic/*.tar.bz2",
"{workdir}/out/target/product/generic/tests/*.zip",
"{objdir}/dist/b2g-*.crashreporter-symbols.zip",
"{objdir}/dist/b2g-*.tar.gz",
"{workdir}/sources.xml"
],
"gecko_l10n_root": "https://hg.mozilla.org/l10n-central",

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="287195d1fed2e6c883745d7091a4c05e56c4dbb7"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8248f8da7a29757da9f7fd748a0d8d70f2bd4610"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="12408eb142739c7de87ab7ee0d0d2854d5c298f3"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="89c5816399e71bda92a8959b5b771c04d6672ea3"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "57c407d3e94ba322688b4bd121eda317806ab44e",
"revision": "ec183496896f2902121bce52413c75f917bea5c9",
"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="287195d1fed2e6c883745d7091a4c05e56c4dbb7"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8248f8da7a29757da9f7fd748a0d8d70f2bd4610"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<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="cb16958e41105d7c551d9941f522db97b8312538"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="89c5816399e71bda92a8959b5b771c04d6672ea3"/>
<!-- 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="287195d1fed2e6c883745d7091a4c05e56c4dbb7"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8248f8da7a29757da9f7fd748a0d8d70f2bd4610"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<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="287195d1fed2e6c883745d7091a4c05e56c4dbb7"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8248f8da7a29757da9f7fd748a0d8d70f2bd4610"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<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="cb16958e41105d7c551d9941f522db97b8312538"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="89c5816399e71bda92a8959b5b771c04d6672ea3"/>
<!-- 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="287195d1fed2e6c883745d7091a4c05e56c4dbb7"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8248f8da7a29757da9f7fd748a0d8d70f2bd4610"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<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="cb16958e41105d7c551d9941f522db97b8312538"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="89c5816399e71bda92a8959b5b771c04d6672ea3"/>
<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="287195d1fed2e6c883745d7091a4c05e56c4dbb7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8248f8da7a29757da9f7fd748a0d8d70f2bd4610"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="89c5816399e71bda92a8959b5b771c04d6672ea3"/>
<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="287195d1fed2e6c883745d7091a4c05e56c4dbb7"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8248f8da7a29757da9f7fd748a0d8d70f2bd4610"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<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="cb16958e41105d7c551d9941f522db97b8312538"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="89c5816399e71bda92a8959b5b771c04d6672ea3"/>
<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

@ -131,7 +131,7 @@ def main(platform):
])
# Ship b2g-desktop, but prevent its gaia profile to be shipped in the xpi
add_dir_to_zip(xpi_path, os.path.join(distdir, "b2g"), "b2g", ("gaia"))
add_dir_to_zip(xpi_path, os.path.join(distdir, "b2g"), "b2g", ("gaia", "B2G.app/Contents/MacOS/gaia"))
# Then ship our own gaia profile
add_dir_to_zip(xpi_path, os.path.join(gaia_path, "profile"), "profile")

View File

@ -141,7 +141,7 @@ exports.SimulatorProcess = Class({
let bin = URL.toFilename(BIN_URL);
let executables = {
WINNT: "b2g-bin.exe",
Darwin: "Contents/MacOS/b2g-bin",
Darwin: "B2G.app/Contents/MacOS/b2g-bin",
Linux: "b2g-bin",
};

View File

@ -1182,15 +1182,14 @@
<svg:svg height="0">
#include tab-shape.inc.svg
#ifndef XP_UNIX
<svg:clipPath id="windows-keyhole-forward-clip-path" clipPathUnits="userSpaceOnUse">
#ifndef XP_MACOSX
<svg:clipPath id="keyhole-forward-clip-path" clipPathUnits="userSpaceOnUse">
<svg:path d="M 0,0 a 16 16 0 0 1 0,24 l 10000,0 l 0,-24 l -10000,0 z"/>
</svg:clipPath>
<svg:clipPath id="windows-urlbar-back-button-clip-path" clipPathUnits="userSpaceOnUse">
<svg:clipPath id="urlbar-back-button-clip-path" clipPathUnits="userSpaceOnUse">
<svg:path d="M -1,1 a 16 16 0 0 1 0,24 l 10000,0 l 0,-24 l -10000,0 z"/>
</svg:clipPath>
#endif
#ifdef XP_MACOSX
#else
<svg:clipPath id="osx-keyhole-forward-clip-path" clipPathUnits="userSpaceOnUse">
<svg:path d="M 0,0 a 16 16 0 0 1 0,24 l 10000,0 l 0,-24 l -10000,0 z"/>
</svg:clipPath>

View File

@ -529,7 +529,12 @@ var tests = [
this.notifyObj.addOptions({dismissed: true});
this.notification = showNotification(this.notifyObj);
EventUtils.synthesizeMouse(button, 1, 1, {});
// This test places a normal button in the notification area, which has
// standard GTK styling and dimensions. Due to the clip-path, this button
// gets clipped off, which makes it necessary to synthesize the mouse click
// a little bit downward. To be safe, I adjusted the x-offset with the same
// amount.
EventUtils.synthesizeMouse(button, 4, 4, {});
},
onShown: function(popup) {
checkPopup(popup, this.notifyObj);

View File

@ -49,6 +49,7 @@ const PanelUI = {
this.menuButton.addEventListener("keypress", this);
this._overlayScrollListenerBoundFn = this._overlayScrollListener.bind(this);
window.matchMedia("(-moz-overlay-scrollbars)").addListener(this._overlayScrollListenerBoundFn);
CustomizableUI.addListener(this);
this._initialized = true;
},
@ -69,10 +70,6 @@ const PanelUI = {
},
uninit: function() {
if (!this._eventListenersAdded) {
return;
}
for (let event of this.kEvents) {
this.panel.removeEventListener(event, this);
}
@ -80,6 +77,7 @@ const PanelUI = {
this.menuButton.removeEventListener("mousedown", this);
this.menuButton.removeEventListener("keypress", this);
window.matchMedia("(-moz-overlay-scrollbars)").removeListener(this._overlayScrollListenerBoundFn);
CustomizableUI.removeListener(this);
this._overlayScrollListenerBoundFn = null;
},
@ -183,6 +181,7 @@ const PanelUI = {
handleEvent: function(aEvent) {
switch (aEvent.type) {
case "popupshowing":
this._adjustLabelsForAutoHyphens();
// Fall through
case "popupshown":
// Fall through
@ -370,6 +369,26 @@ const PanelUI = {
"browser");
},
onWidgetAfterDOMChange: function(aNode, aNextNode, aContainer, aWasRemoval) {
if (aContainer != this.contents) {
return;
}
if (aWasRemoval) {
aNode.removeAttribute("auto-hyphens");
}
},
onWidgetBeforeDOMChange: function(aNode, aNextNode, aContainer, aIsRemoval) {
if (aContainer != this.contents) {
return;
}
if (!aIsRemoval &&
(this.panel.state == "open" ||
document.documentElement.hasAttribute("customizing"))) {
this._adjustLabelsForAutoHyphens(aNode);
}
},
/**
* Signal that we're about to make a lot of changes to the contents of the
* panels all at once. For performance, we ignore the mutations.
@ -389,6 +408,22 @@ const PanelUI = {
this.multiView.ignoreMutations = false;
},
_adjustLabelsForAutoHyphens: function(aNode) {
let toolbarButtons = aNode ? [aNode] :
this.contents.querySelectorAll(".toolbarbutton-1");
for (let node of toolbarButtons) {
let label = node.getAttribute("label");
if (!label) {
continue;
}
if (label.contains("\u00ad")) {
node.setAttribute("auto-hyphens", "off");
} else {
node.removeAttribute("auto-hyphens");
}
}
},
/**
* Sets the anchor node into the open or closed state, depending
* on the state of the panel.

View File

@ -68,6 +68,7 @@ skip-if = os == "linux"
[browser_948985_non_removable_defaultArea.js]
[browser_952963_areaType_getter_no_area.js]
[browser_956602_remove_special_widget.js]
[browser_962884_opt_in_disable_hyphens.js]
[browser_963639_customizing_attribute_non_customizable_toolbar.js]
[browser_967000_button_charEncoding.js]
[browser_967000_button_feeds.js]

View File

@ -0,0 +1,67 @@
/* 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";
add_task(function() {
const kNormalLabel = "Character Encoding";
CustomizableUI.addWidgetToArea("characterencoding-button", CustomizableUI.AREA_NAVBAR);
let characterEncoding = document.getElementById("characterencoding-button");
const kOriginalLabel = characterEncoding.getAttribute("label");
characterEncoding.setAttribute("label", "\u00ad" + kNormalLabel);
CustomizableUI.addWidgetToArea("characterencoding-button", CustomizableUI.AREA_PANEL);
yield PanelUI.show();
is(characterEncoding.getAttribute("auto-hyphens"), "off",
"Hyphens should be disabled if the &shy; character is present in the label");
let multilineText = document.getAnonymousElementByAttribute(characterEncoding, "class", "toolbarbutton-multiline-text");
let multilineTextCS = getComputedStyle(multilineText);
is(multilineTextCS.MozHyphens, "manual", "-moz-hyphens should be set to manual when the &shy; character is present.")
let hiddenPanelPromise = promisePanelHidden(window);
PanelUI.toggle();
yield hiddenPanelPromise;
characterEncoding.setAttribute("label", kNormalLabel);
yield PanelUI.show();
isnot(characterEncoding.getAttribute("auto-hyphens"), "off",
"Hyphens should not be disabled if the &shy; character is not present in the label");
multilineText = document.getAnonymousElementByAttribute(characterEncoding, "class", "toolbarbutton-multiline-text");
let multilineTextCS = getComputedStyle(multilineText);
is(multilineTextCS.MozHyphens, "auto", "-moz-hyphens should be set to auto by default.")
hiddenPanelPromise = promisePanelHidden(window);
PanelUI.toggle();
yield hiddenPanelPromise;
characterEncoding.setAttribute("label", "\u00ad" + kNormalLabel);
CustomizableUI.removeWidgetFromArea("characterencoding-button");
yield startCustomizing();
isnot(characterEncoding.getAttribute("auto-hyphens"), "off",
"Hyphens should not be disabled when the widget is in the palette");
gCustomizeMode.addToPanel(characterEncoding);
is(characterEncoding.getAttribute("auto-hyphens"), "off",
"Hyphens should be disabled if the &shy; character is present in the label in customization mode");
let multilineText = document.getAnonymousElementByAttribute(characterEncoding, "class", "toolbarbutton-multiline-text");
let multilineTextCS = getComputedStyle(multilineText);
is(multilineTextCS.MozHyphens, "manual", "-moz-hyphens should be set to manual when the &shy; character is present in customization mode.")
yield endCustomizing();
CustomizableUI.addWidgetToArea("characterencoding-button", CustomizableUI.AREA_NAVBAR);
ok(!characterEncoding.hasAttribute("auto-hyphens"),
"Removing the widget from the panel should remove the auto-hyphens attribute");
characterEncoding.setAttribute("label", kOriginalLabel);
});
add_task(function asyncCleanup() {
yield endCustomizing();
yield resetCustomization();
});

View File

@ -128,6 +128,15 @@
#endif
<stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/>
<hbox id="header-advanced"
class="header"
hidden="true"
data-category="paneAdvanced">
<image class="header-icon"/>
<label class="header-name"
value="&paneAdvanced.title;"/>
</hbox>
<tabbox id="advancedPrefs" flex="1"
data-category="paneAdvanced" hidden="true"
onselect="gAdvancedPane.tabSelectionChanged();">

View File

@ -55,7 +55,19 @@
<key key="&focusSearch2.key;" modifiers="accel" oncommand="gApplicationsPane.focusFilterBox();"/>
</keyset>
<vbox data-category="paneApplications" hidden="true" flex="1">
<hbox id="header-application"
class="header"
hidden="true"
data-category="paneApplications">
<image class="header-icon"/>
<label class="header-name"
value="&paneApplications.title;"/>
</hbox>
<vbox id="applicationsContent"
data-category="paneApplications"
hidden="true"
flex="1">
<hbox>
<textbox id="filter" flex="1"
type="search"

View File

@ -21,6 +21,15 @@
<script type="application/javascript"
src="chrome://browser/content/preferences/in-content/content.js"/>
<hbox id="header-content"
class="header"
hidden="true"
data-category="paneContent">
<image class="header-icon"/>
<label class="header-name"
value="&paneContent.title;"/>
</hbox>
<groupbox id="miscGroup" data-category="paneContent" hidden="true">
<caption label="&popups.label;"/>

View File

@ -85,6 +85,14 @@
#endif
</preferences>
<hbox id="header-general"
class="header"
data-category="paneGeneral">
<image class="header-icon"/>
<label class="header-name"
value="&paneGeneral.title;"/>
</hbox>
<!-- Startup -->
<groupbox id="startupGroup" data-category="paneGeneral">
<caption label="&startup.label;"/>

View File

@ -32,10 +32,8 @@ function init_all() {
let categories = document.getElementById("categories");
categories.addEventListener("select", event => gotoPref(event.target.value));
window.addEventListener("popstate", event => selectCategory(event.state));
if (history.length > 1 && history.state) {
updateCommands();
selectCategory(history.state);
} else {
history.replaceState("paneGeneral", document.title);
@ -49,38 +47,9 @@ function selectCategory(name) {
}
function gotoPref(page) {
if (history.state != page) {
window.history.pushState(page, document.title);
}
updateCommands();
window.history.replaceState(page, document.title);
search(page, "data-category");
}
function cmd_back() {
window.history.back();
}
function cmd_forward() {
window.history.forward();
}
function updateCommands() {
document.getElementById("back-btn").disabled = !canGoBack();
document.getElementById("forward-btn").disabled = !canGoForward();
}
function canGoBack() {
return window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.canGoBack;
}
function canGoForward() {
return window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.canGoForward;
}
function search(aQuery, aAttribute) {
let elements = document.getElementById("mainPrefPane").children;

View File

@ -74,15 +74,6 @@
src="chrome://browser/locale/preferences/applicationManager.properties"/>
</stringbundleset>
<hbox id="header">
<toolbarbutton id="back-btn" class="nav-button header-button"
oncommand="cmd_back()" tooltiptext="&buttonBack.tooltip;"
disabled="true"/>
<toolbarbutton id="forward-btn" class="nav-button header-button"
oncommand="cmd_forward()" tooltiptext="&buttonForward.tooltip;"
disabled="true"/>
</hbox>
<hbox flex="1">
<!-- category list -->

View File

@ -65,6 +65,15 @@
</preferences>
<hbox id="header-privacy"
class="header"
hidden="true"
data-category="panePrivacy">
<image class="header-icon"/>
<label class="header-name"
value="&panePrivacy.title;"/>
</hbox>
<!-- Tracking -->
<groupbox id="trackingGroup" data-category="panePrivacy" hidden="true" align="start">
<caption label="&tracking.label;"/>

View File

@ -30,6 +30,15 @@
</preferences>
<hbox id="header-security"
class="header"
hidden="true"
data-category="paneSecurity">
<image class="header-icon"/>
<label class="header-name"
value="&paneSecurity.title;"/>
</hbox>
<!-- addons, forgery (phishing) UI -->
<groupbox id="addonsPhishingGroup" data-category="paneSecurity" hidden="true">
<caption label="&general.label;"/>

View File

@ -28,6 +28,15 @@
<script type="application/javascript"
src="chrome://browser/content/sync/utils.js"/>
<hbox id="header-sync"
class="header"
hidden="true"
data-category="paneSync">
<image class="header-icon"/>
<label class="header-name"
value="&paneSync.title;"/>
</hbox>
<deck id="weavePrefsDeck" data-category="paneSync" hidden="true">
<vbox id="noAccount" align="center">
<spacer flex="1"/>

View File

@ -23,6 +23,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
"resource:///modules/sessionstore/SessionStore.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SessionFile",
"resource:///modules/sessionstore/SessionFile.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
// Minimal interval between two save operations (in milliseconds).
XPCOMUtils.defineLazyGetter(this, "gInterval", function () {
@ -41,14 +43,6 @@ XPCOMUtils.defineLazyGetter(this, "gInterval", function () {
return Services.prefs.getIntPref(PREF);
});
// Wrap a string as a nsISupports.
function createSupportsString(data) {
let string = Cc["@mozilla.org/supports-string;1"]
.createInstance(Ci.nsISupportsString);
string.data = data;
return string;
}
// Notify observers about a given topic with a given subject.
function notify(subject, topic) {
Services.obs.notifyObservers(subject, topic, "");
@ -192,6 +186,14 @@ let SessionSaverInternal = {
// Cancel any pending timeouts.
this.cancel();
if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
// Don't save (or even collect) anything in permanent private
// browsing mode
this.updateLastSaveTime();
return Promise.resolve();
}
stopWatchStart("COLLECT_DATA_MS", "COLLECT_DATA_LONGEST_OP_MS");
let state = SessionStore.getCurrentState(forceUpdateAllWindows);
PrivacyFilter.filterPrivateWindowsAndTabs(state);
@ -242,19 +244,13 @@ let SessionSaverInternal = {
* Write the given state object to disk.
*/
_writeState: function (state) {
// Inform observers
notify(null, "sessionstore-state-write");
stopWatchStart("SERIALIZE_DATA_MS", "SERIALIZE_DATA_LONGEST_OP_MS", "WRITE_STATE_LONGEST_OP_MS");
let data = JSON.stringify(state);
stopWatchFinish("SERIALIZE_DATA_MS", "SERIALIZE_DATA_LONGEST_OP_MS");
// Give observers a chance to modify session data.
data = this._notifyObserversBeforeStateWrite(data);
// Don't touch the file if an observer has deleted all state data.
if (!data) {
stopWatchCancel("WRITE_STATE_LONGEST_OP_MS");
return Promise.resolve();
}
// We update the time stamp before writing so that we don't write again
// too soon, if saving is requested before the write completes. Without
// this update we may save repeatedly if actions cause a runDelayed
@ -275,14 +271,4 @@ let SessionSaverInternal = {
return promise;
},
/**
* Notify sessionstore-state-write observer and give them a
* chance to modify session data before we'll write it to disk.
*/
_notifyObserversBeforeStateWrite: function (data) {
let stateString = createSupportsString(data);
notify(stateString, "sessionstore-state-write");
return stateString.data;
}
};

View File

@ -1058,16 +1058,26 @@ let SessionStoreInternal = {
// recently something was closed.
winData.closedAt = Date.now();
// Save the window if it has multiple tabs or a single saveable tab and
// it's not private.
// Save non-private windows if they have at
// least one saveable tab or are the last window.
if (!winData.isPrivate) {
// Remove any open private tabs the window may contain.
PrivacyFilter.filterPrivateTabs(winData);
let hasSingleTabToSave =
winData.tabs.length == 1 && this._shouldSaveTabState(winData.tabs[0]);
// Determine whether the window has any tabs worth saving.
let hasSaveableTabs = winData.tabs.some(this._shouldSaveTabState);
if (hasSingleTabToSave || winData.tabs.length > 1) {
// When closing windows one after the other until Firefox quits, we
// will move those closed in series back to the "open windows" bucket
// before writing to disk. If however there is only a single window
// with tabs we deem not worth saving then we might end up with a
// random closed or even a pop-up window re-opened. To prevent that
// we explicitly allow saving an "empty" window state.
let isLastWindow =
Object.keys(this._windows).length == 1 &&
!this._closedWindows.some(win => win._shouldRestore || false);
if (hasSaveableTabs || isLastWindow) {
// we don't want to save the busy state
delete winData.busy;

View File

@ -98,7 +98,6 @@ skip-if = true
[browser_394759_purge.js]
[browser_423132.js]
[browser_447951.js]
[browser_448741.js]
[browser_454908.js]
[browser_456342.js]
[browser_461634.js]

View File

@ -75,15 +75,13 @@ function test() {
// Wait for the sessionstore.js file to be written before going on.
// Note: we don't wait for the complete event, since if asyncCopy fails we
// would timeout.
Services.obs.addObserver(function (aSubject, aTopic, aData) {
Services.obs.removeObserver(arguments.callee, aTopic);
info("sessionstore.js is being written");
waitForSaveState(function(writing) {
ok(writing, "sessionstore.js is being written");
closedWindowCount = ss.getClosedWindowCount();
is(closedWindowCount, 0, "Correctly set window count");
executeSoon(aCallback);
}, "sessionstore-state-write", false);
});
// Remove the sessionstore.js file before setting the interval to 0
let profilePath = Services.dirsvc.get("ProfD", Ci.nsIFile);

View File

@ -1,62 +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() {
/** Test for Bug 448741 **/
waitForExplicitFinish();
let uniqueName = "bug 448741";
let uniqueValue = "as good as unique: " + Date.now();
// set a unique value on a new, blank tab
var tab = gBrowser.addTab();
tab.linkedBrowser.stop();
ss.setTabValue(tab, uniqueName, uniqueValue);
let valueWasCleaned = false;
// prevent our value from being written to disk
function cleaningObserver(aSubject, aTopic, aData) {
ok(aTopic == "sessionstore-state-write", "observed correct topic?");
ok(aSubject instanceof Ci.nsISupportsString, "subject is a string?");
ok(aSubject.data.indexOf(uniqueValue) > -1, "data contains our value?");
// find the data for the newly added tab and delete it
let state = JSON.parse(aSubject.data);
state.windows.forEach(function (winData) {
winData.tabs.forEach(function (tabData) {
if (tabData.extData && uniqueName in tabData.extData &&
tabData.extData[uniqueName] == uniqueValue) {
delete tabData.extData[uniqueName];
valueWasCleaned = true;
}
});
});
ok(valueWasCleaned, "found and removed the specific tab value");
aSubject.data = JSON.stringify(state);
Services.obs.removeObserver(cleaningObserver, aTopic);
}
// make sure that all later observers don't see that value any longer
function checkingObserver(aSubject, aTopic, aData) {
ok(valueWasCleaned && aSubject instanceof Ci.nsISupportsString,
"ready to check the cleaned state?");
ok(aSubject.data.indexOf(uniqueValue) == -1, "data no longer contains our value?");
// clean up
gBrowser.removeTab(tab);
Services.obs.removeObserver(checkingObserver, aTopic);
if (gPrefService.prefHasUserValue("browser.sessionstore.interval"))
gPrefService.clearUserPref("browser.sessionstore.interval");
finish();
}
// last added observers are invoked first
Services.obs.addObserver(checkingObserver, "sessionstore-state-write", false);
Services.obs.addObserver(cleaningObserver, "sessionstore-state-write", false);
// trigger an immediate save operation
gPrefService.setIntPref("browser.sessionstore.interval", 0);
}

View File

@ -20,11 +20,12 @@ function testBug600545() {
Services.prefs.clearUserPref("browser.sessionstore.interval");
});
// This tests the following use case:
// When multiple windows are open and browser.sessionstore.resume_from_crash
// preference is false, tab session data for non-active window is stripped for
// non-pinned tabs. This occurs after "sessionstore-state-write" fires which
// will only fire in this case if there is at least one pinned tab.
// This tests the following use case: When multiple windows are open
// and browser.sessionstore.resume_from_crash preference is false,
// tab session data for non-active window is stripped for non-pinned
// tabs. This occurs after "sessionstore-state-write-complete"
// fires which will only fire in this case if there is at least one
// pinned tab.
let state = { windows: [
{
tabs: [

View File

@ -1,125 +1,86 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let newWin;
let newTab;
function test() {
add_task(function* setup() {
/** Test for Bug 625016 - Restore windows closed in succession to quit (non-OSX only) **/
// We'll test this by opening a new window, waiting for the save event, then
// closing that window. We'll observe the "sessionstore-state-write" notification
// and check that the state contains no _closedWindows. We'll then add a new
// tab and make sure that the state following that was reset and the closed
// window is now in _closedWindows.
// We'll test this by opening a new window, waiting for the save
// event, then closing that window. We'll observe the
// "sessionstore-state-write-complete" notification and check that
// the state contains no _closedWindows. We'll then add a new tab
// and make sure that the state following that was reset and the
// closed window is now in _closedWindows.
waitForExplicitFinish();
requestLongerTimeout(2);
// We speed up the interval between session saves to ensure that the test
// runs quickly.
Services.prefs.setIntPref("browser.sessionstore.interval", 4000);
registerCleanupFunction(function () {
Services.prefs.clearUserPref("browser.sessionstore.interval");
});
yield forceSaveState();
waitForSaveState(setup);
}
function setup() {
// We'll clear all closed windows to make sure our state is clean
// forgetClosedWindow doesn't trigger a delayed save
while (ss.getClosedWindowCount()) {
ss.forgetClosedWindow(0);
}
is(ss.getClosedWindowCount(), 0, "starting with no closed windows");
});
// Open a new window, which should trigger a save event soon.
waitForSaveState(onSaveState);
newWin = openDialog(location, "_blank", "chrome,all,dialog=no", "about:rights");
}
function onSaveState() {
add_task(function* new_window() {
let newWin;
try {
ss.getWindowValue(newWin, "foobar");
} catch (e) {
// The window is untracked which means that the saveState() call isn't the
// one we're waiting for. It's most likely been triggered by an async
// collection running in the background.
waitForSaveState(onSaveState);
return;
newWin = yield promiseNewWindowLoaded();
let tab = newWin.gBrowser.addTab("http://example.com/browser_625016.js?" + Math.random());
yield promiseBrowserLoaded(tab.linkedBrowser);
// Double check that we have no closed windows
is(ss.getClosedWindowCount(), 0, "no closed windows on first save");
yield promiseWindowClosed(newWin);
newWin = null;
let state = JSON.parse((yield promiseSaveFileContents()));
is(state.windows.length, 2,
"observe1: 2 windows in data written to disk");
is(state._closedWindows.length, 0,
"observe1: no closed windows in data written to disk");
// The API still treats the closed window as closed, so ensure that window is there
is(ss.getClosedWindowCount(), 1,
"observe1: 1 closed window according to API");
} finally {
if (newWin) {
yield promiseWindowClosed(newWin);
}
yield forceSaveState();
}
// Double check that we have no closed windows
is(ss.getClosedWindowCount(), 0, "no closed windows on first save");
Services.obs.addObserver(observe1, "sessionstore-state-write", false);
// Now close the new window, which should trigger another save event
newWin.close();
}
function observe1(aSubject, aTopic, aData) {
info("observe1: " + aTopic);
switch (aTopic) {
case "sessionstore-state-write":
aSubject.QueryInterface(Ci.nsISupportsString);
let state = JSON.parse(aSubject.data);
is(state.windows.length, 2,
"observe1: 2 windows in data being writted to disk");
is(state._closedWindows.length, 0,
"observe1: no closed windows in data being writted to disk");
// The API still treats the closed window as closed, so ensure that window is there
is(ss.getClosedWindowCount(), 1,
"observe1: 1 closed window according to API");
Services.obs.removeObserver(observe1, "sessionstore-state-write");
Services.obs.addObserver(observe1, "sessionstore-state-write-complete", false);
break;
case "sessionstore-state-write-complete":
Services.obs.removeObserver(observe1, "sessionstore-state-write-complete");
openTab();
break;
}
}
function observe2(aSubject, aTopic, aData) {
info("observe2: " + aTopic);
switch (aTopic) {
case "sessionstore-state-write":
aSubject.QueryInterface(Ci.nsISupportsString);
let state = JSON.parse(aSubject.data);
is(state.windows.length, 1,
"observe2: 1 window in data being writted to disk");
is(state._closedWindows.length, 1,
"observe2: 1 closed window in data being writted to disk");
// The API still treats the closed window as closed, so ensure that window is there
is(ss.getClosedWindowCount(), 1,
"observe2: 1 closed window according to API");
Services.obs.removeObserver(observe2, "sessionstore-state-write");
Services.obs.addObserver(observe2, "sessionstore-state-write-complete", false);
break;
case "sessionstore-state-write-complete":
Services.obs.removeObserver(observe2, "sessionstore-state-write-complete");
done();
break;
}
}
});
// We'll open a tab, which should trigger another state save which would wipe
// the _shouldRestore attribute from the closed window
function openTab() {
Services.obs.addObserver(observe2, "sessionstore-state-write", false);
newTab = gBrowser.addTab("about:mozilla");
}
add_task(function* new_tab() {
let newTab;
try {
newTab = gBrowser.addTab("about:mozilla");
function done() {
gBrowser.removeTab(newTab);
let state = JSON.parse((yield promiseSaveFileContents()));
is(state.windows.length, 1,
"observe2: 1 window in data being written to disk");
is(state._closedWindows.length, 1,
"observe2: 1 closed window in data being written to disk");
// The API still treats the closed window as closed, so ensure that window is there
is(ss.getClosedWindowCount(), 1,
"observe2: 1 closed window according to API");
} finally {
gBrowser.removeTab(newTab);
}
});
add_task(function* done() {
// The API still represents the closed window as closed, so we can clear it
// with the API, but just to make sure...
is(ss.getClosedWindowCount(), 1, "1 closed window according to API");
ss.forgetClosedWindow(0);
executeSoon(finish);
}
// is(ss.getClosedWindowCount(), 1, "1 closed window according to API");
while (ss.getClosedWindowCount()) {
ss.forgetClosedWindow(0);
}
Services.prefs.clearUserPref("browser.sessionstore.interval");
});

View File

@ -162,15 +162,9 @@ function waitForWindowClose(aWin, aCallback) {
}
function forceWriteState(aCallback) {
Services.obs.addObserver(function observe(aSubject, aTopic, aData) {
if (aTopic == "sessionstore-state-write") {
Services.obs.removeObserver(observe, aTopic);
Services.prefs.clearUserPref("browser.sessionstore.interval");
aSubject.QueryInterface(Ci.nsISupportsString);
aCallback(JSON.parse(aSubject.data));
}
}, "sessionstore-state-write", false);
Services.prefs.setIntPref("browser.sessionstore.interval", 0);
return promiseSaveFileContents().then(function(data) {
aCallback(JSON.parse(data));
});
}
function testOnWindow(aIsPrivate, aCallback) {

View File

@ -24,43 +24,9 @@ let gDecoder = new TextDecoder();
let gSSData;
let gSSBakData;
// Wait for a state write to complete and then execute a callback.
function waitForSaveStateComplete(aSaveStateCallback) {
let topic = "sessionstore-state-write-complete";
function observer() {
Services.prefs.clearUserPref(PREF_SS_INTERVAL);
Services.obs.removeObserver(observer, topic);
executeSoon(function taskCallback() {
Task.spawn(aSaveStateCallback);
});
}
Services.obs.addObserver(observer, topic, false);
}
// Register next test callback and trigger state saving change.
function nextTest(testFunc) {
waitForSaveStateComplete(testFunc);
// We set the interval for session store state saves to be zero
// to cause a save ASAP.
Services.prefs.setIntPref(PREF_SS_INTERVAL, 0);
}
registerCleanupFunction(function() {
// Cleaning up after the test: removing the sessionstore.bak file.
Task.spawn(function cleanupTask() {
yield OS.File.remove(backupPath);
});
});
function test() {
waitForExplicitFinish();
nextTest(testAfterFirstWrite);
}
function testAfterFirstWrite() {
add_task(function* testAfterFirstWrite() {
// Ensure sessionstore.bak is not created. We start with a clean
// profile so there was nothing to move to sessionstore.bak before
// initially writing sessionstore.js
@ -78,10 +44,10 @@ function testAfterFirstWrite() {
// and a backup would not be triggered again.
yield OS.File.move(path, backupPath);
nextTest(testReadBackup);
}
yield forceSaveState();
});
function testReadBackup() {
add_task(function* testReadBackup() {
// Ensure sessionstore.bak is finally created.
let ssExists = yield OS.File.exists(path);
let ssBackupExists = yield OS.File.exists(backupPath);
@ -114,10 +80,10 @@ function testReadBackup() {
is(ssDataRead, gSSBakData,
"SessionFile.read read sessionstore.bak correctly.");
nextTest(testBackupUnchanged);
}
yield forceSaveState();
});
function testBackupUnchanged() {
add_task(function* testBackupUnchanged() {
// Ensure sessionstore.bak is backed up only once.
// Read sessionstore.bak data.
@ -125,6 +91,9 @@ function testBackupUnchanged() {
let ssBakData = gDecoder.decode(array);
// Ensure the sessionstore.bak did not change.
is(ssBakData, gSSBakData, "sessionstore.bak is unchanged.");
});
executeSoon(finish);
}
add_task(function* cleanup() {
// Cleaning up after the test: removing the sessionstore.bak file.
yield OS.File.remove(backupPath);
});

View File

@ -243,12 +243,7 @@ function waitForTopic(aTopic, aTimeout, aCallback) {
/**
* Wait until session restore has finished collecting its data and is
* getting ready to write that data ("sessionstore-state-write").
*
* This function is meant to be called immediately after the code
* that will trigger the saving.
*
* Note that this does not wait for the disk write to be complete.
* has written that data ("sessionstore-state-write-complete").
*
* @param {function} aCallback If sessionstore-state-write is sent
* within buffering interval + 100 ms, the callback is passed |true|,
@ -257,7 +252,7 @@ function waitForTopic(aTopic, aTimeout, aCallback) {
function waitForSaveState(aCallback) {
let timeout = 100 +
Services.prefs.getIntPref("browser.sessionstore.interval");
return waitForTopic("sessionstore-state-write", timeout, aCallback);
return waitForTopic("sessionstore-state-write-complete", timeout, aCallback);
}
function promiseSaveState() {
let deferred = Promise.defer();
@ -272,22 +267,30 @@ function promiseSaveState() {
function forceSaveState() {
let promise = promiseSaveState();
const PREF = "browser.sessionstore.interval";
let original = Services.prefs.getIntPref(PREF);
// Set interval to an arbitrary non-0 duration
// to ensure that setting it to 0 will notify observers
Services.prefs.setIntPref(PREF, 1000);
Services.prefs.setIntPref(PREF, 0);
return promise.then(
function onSuccess(x) {
Services.prefs.clearUserPref(PREF);
Services.prefs.setIntPref(PREF, original);
return x;
},
function onError(x) {
Services.prefs.clearUserPref(PREF);
Services.prefs.setIntPref(PREF, original);
throw x;
}
);
}
function promiseSaveFileContents() {
let promise = forceSaveState();
return promise.then(function() {
return OS.File.read(OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.js"), { encoding: "utf-8" });
});
}
function whenBrowserLoaded(aBrowser, aCallback = next, ignoreSubFrames = true) {
aBrowser.addEventListener("load", function onLoad(event) {
if (!ignoreSubFrames || event.target == aBrowser.contentDocument) {
@ -321,6 +324,11 @@ function whenWindowLoaded(aWindow, aCallback = next) {
});
}, false);
}
function promiseWindowLoaded(aWindow) {
let deferred = Promise.defer();
whenWindowLoaded(aWindow, deferred.resolve);
return deferred.promise;
}
function whenTabRestored(aTab, aCallback = next) {
aTab.addEventListener("SSTabRestored", function onRestored(aEvent) {

View File

@ -110,6 +110,7 @@ skip-if = true # Bug 922422
[browser_tabview_bug641802.js]
[browser_tabview_bug642793.js]
[browser_tabview_bug643392.js]
skip-if = os == 'linux'&&debug # bug 989083
[browser_tabview_bug644097.js]
[browser_tabview_bug648882.js]
skip-if = true # Bug 752862

View File

@ -6,13 +6,13 @@ const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionSto
let state = {
windows: [{
tabs: [{
entries: [{ url: "about:blank" }],
entries: [{ url: "about:robots" }],
hidden: true,
extData: {"tabview-tab": '{"url":"about:blank","groupID":1,"bounds":{"left":20,"top":20,"width":20,"height":20}}'}
extData: {"tabview-tab": '{"url":"about:robots","groupID":1,"bounds":{"left":20,"top":20,"width":20,"height":20}}'}
},{
entries: [{ url: "about:blank" }],
entries: [{ url: "about:robots" }],
hidden: false,
extData: {"tabview-tab": '{"url":"about:blank","groupID":2,"bounds":{"left":20,"top":20,"width":20,"height":20}}'},
extData: {"tabview-tab": '{"url":"about:robots","groupID":2,"bounds":{"left":20,"top":20,"width":20,"height":20}}'},
}],
selected: 2,
extData: {

View File

@ -4,13 +4,13 @@
let state = {
windows: [{
tabs: [{
entries: [{ url: "about:blank" }],
entries: [{ url: "about:robots" }],
hidden: true,
extData: {"tabview-tab": '{"url":"about:blank","groupID":1,"bounds":{"left":120,"top":20,"width":20,"height":20}}'}
extData: {"tabview-tab": '{"url":"about:robots","groupID":1,"bounds":{"left":120,"top":20,"width":20,"height":20}}'}
},{
entries: [{ url: "about:blank" }],
entries: [{ url: "about:robots" }],
hidden: false,
extData: {"tabview-tab": '{"url":"about:blank","groupID":2,"bounds":{"left":20,"top":20,"width":20,"height":20}}'},
extData: {"tabview-tab": '{"url":"about:robots","groupID":2,"bounds":{"left":20,"top":20,"width":20,"height":20}}'},
}],
selected: 2,
extData: {

View File

@ -8,23 +8,23 @@ function test() {
let newState = {
windows: [{
tabs: [{
entries: [{ url: "about:blank" }],
entries: [{ url: "about:robots" }],
hidden: true,
attributes: {},
extData: {
"tabview-tab":
'{"bounds":{"left":21,"top":29,"width":204,"height":153},' +
'"userSize":null,"url":"about:blank","groupID":1,' +
'"userSize":null,"url":"about:robots","groupID":1,' +
'"imageData":null,"title":null}'
}
},{
entries: [{ url: "about:blank" }],
entries: [{ url: "about:robots" }],
hidden: false,
attributes: {},
extData: {
"tabview-tab":
'{"bounds":{"left":315,"top":29,"width":111,"height":84},' +
'"userSize":null,"url":"about:blank","groupID":2,' +
'"userSize":null,"url":"about:robots","groupID":2,' +
'"imageData":null,"title":null}'
},
}],

View File

@ -3,7 +3,6 @@
ac_add_options --enable-debug
ac_add_options --enable-trace-malloc
ac_add_options --enable-signmar
ac_add_options --enable-metro
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1

View File

@ -3,7 +3,6 @@
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging
ac_add_options --with-l10n-base=../../l10n
ac_add_options --enable-metro
ac_add_options --with-windows-version=601
export MOZILLA_OFFICIAL=1

View File

@ -5,7 +5,6 @@
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging
ac_add_options --enable-jemalloc
ac_add_options --enable-metro
if [ -f /c/builds/gapi.data ]; then
_gapi_keyfile=/c/builds/gapi.data
else

View File

@ -6,7 +6,6 @@ ac_add_options --host=x86_64-pc-mingw32
ac_add_options --enable-debug
ac_add_options --enable-trace-malloc
ac_add_options --enable-signmar
ac_add_options --enable-metro
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1

View File

@ -2175,22 +2175,6 @@ Object.defineProperties(window, {
}
});
/**
* Helper method for parsing a resource URI, like
* `resource://gre/modules/commonjs/sdk/tabs.js`, and pulling out `sdk/tabs.js`
* if it's in the SDK, or `null` otherwise.
*
* @param string url
* @return string|null
*/
function getSDKModuleName(url) {
let match = (url || "").match(/^resource:\/\/gre\/modules\/commonjs\/(.*)/);
if (match) {
return match[1];
}
return null;
}
/**
* Helper method for debugging.
* @param string

View File

@ -11,6 +11,11 @@ const SAMPLE_SIZE = 50; // no of lines
const INDENT_COUNT_THRESHOLD = 5; // percentage
const CHARACTER_LIMIT = 250; // line character limit
// Maps known URLs to friendly source group names
const KNOWN_SOURCE_GROUPS = {
"Add-on SDK": "resource://gre/modules/commonjs/",
};
/**
* Functions handling the sources UI.
*/
@ -49,6 +54,15 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
showArrows: true
});
// Sort known source groups towards the end of the list
this.widget.groupSortPredicate = function(a, b) {
if ((a in KNOWN_SOURCE_GROUPS) == (b in KNOWN_SOURCE_GROUPS)) {
return a.localeCompare(b);
}
return (a in KNOWN_SOURCE_GROUPS) ? 1 : -1;
};
this.emptyText = L10N.getStr("noSourcesText");
this._blackBoxCheckboxTooltip = L10N.getStr("blackBoxCheckboxTooltip");
@ -132,12 +146,6 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
let group = SourceUtils.getSourceGroup(url);
let unicodeUrl = NetworkHelper.convertToUnicode(unescape(fullUrl));
let sdkModuleName = getSDKModuleName(url);
if (sdkModuleName) {
label = sdkModuleName;
group = "Add-on SDK";
}
let contents = document.createElement("label");
contents.className = "plain dbg-source-item";
contents.setAttribute("value", label);
@ -1560,7 +1568,17 @@ let SourceUtils = {
return cachedLabel;
}
let sourceLabel = this.trimUrl(aUrl);
let sourceLabel = null;
for (let name of Object.keys(KNOWN_SOURCE_GROUPS)) {
if (aUrl.startsWith(KNOWN_SOURCE_GROUPS[name])) {
sourceLabel = aUrl.substring(KNOWN_SOURCE_GROUPS[name].length);
}
}
if (!sourceLabel) {
sourceLabel = this.trimUrl(aUrl);
}
let unicodeLabel = NetworkHelper.convertToUnicode(unescape(sourceLabel));
this._labelsCache.set(aUrl, unicodeLabel);
return unicodeLabel;
@ -1583,14 +1601,20 @@ let SourceUtils = {
try {
// Use an nsIURL to parse all the url path parts.
let url = aUrl.split(" -> ").pop();
var uri = Services.io.newURI(url, null, null).QueryInterface(Ci.nsIURL);
var uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL);
} catch (e) {
// This doesn't look like a url, or nsIURL can't handle it.
return "";
}
let groupLabel = uri.prePath;
for (let name of Object.keys(KNOWN_SOURCE_GROUPS)) {
if (aUrl.startsWith(KNOWN_SOURCE_GROUPS[name])) {
groupLabel = name;
}
}
let unicodeLabel = NetworkHelper.convertToUnicode(unescape(groupLabel));
this._groupsCache.set(aUrl, unicodeLabel)
return unicodeLabel;

View File

@ -5,7 +5,18 @@
const ADDON4_URL = EXAMPLE_URL + "addon4.xpi";
let gAddon, gClient, gThreadClient, gDebugger, gSources;
let gAddon, gClient, gThreadClient, gDebugger, gSources, gTitle;
function onMessage(event) {
try {
let json = JSON.parse(event.data);
switch (json.name) {
case "toolbox-title":
gTitle = json.data.value;
break;
}
} catch(e) { Cu.reportError(e); }
}
function test() {
Task.spawn(function () {
@ -18,6 +29,8 @@ function test() {
let iframe = document.createElement("iframe");
document.documentElement.appendChild(iframe);
window.addEventListener("message", onMessage);
let transport = DebuggerServer.connectPipe();
gClient = new DebuggerClient(transport);
@ -43,6 +56,7 @@ function test() {
yield closeConnection();
yield debuggerPanel._toolbox.destroy();
iframe.remove();
window.removeEventListener("message", onMessage);
finish();
});
}
@ -62,7 +76,7 @@ function testSources(expectSecondModule) {
gThreadClient.getSources(({sources}) => {
ok(sources.length, "retrieved sources");
sources.forEach(source => {
for (let source of sources) {
let url = source.url.split(" -> ").pop();
let { label, group } = gSources.getItemByValue(source.url).attachment;
@ -81,12 +95,19 @@ function testSources(expectSecondModule) {
} else {
ok(false, "Saw an unexpected source: " + url);
}
});
}
ok(foundAddonModule, "found JS module for the addon in the list");
is(foundAddonModule2, expectSecondModule, "saw the second addon module");
ok(foundAddonBootstrap, "found bootstrap script for the addon in the list");
is(gTitle, "Debugger - Test add-on with JS Modules", "Saw the right toolbox title.");
let groups = gDebugger.document.querySelectorAll(".side-menu-widget-group-title .name");
is(groups[0].value, "jar:", "Add-on bootstrap should be the first group");
is(groups[1].value, "resource://browser_dbg_addon4", "Add-on code should be the second group");
is(groups.length, 2, "Should be only two groups.");
deferred.resolve();
});

View File

@ -6,7 +6,18 @@
const ADDON3_URL = EXAMPLE_URL + "addon3.xpi";
let gAddon, gClient, gThreadClient, gDebugger, gSources;
let gAddon, gClient, gThreadClient, gDebugger, gSources, gTitle;
function onMessage(event) {
try {
let json = JSON.parse(event.data);
switch (json.name) {
case "toolbox-title":
gTitle = json.data.value;
break;
}
} catch(e) { Cu.reportError(e); }
}
function test() {
Task.spawn(function () {
@ -19,6 +30,8 @@ function test() {
let iframe = document.createElement("iframe");
document.documentElement.appendChild(iframe);
window.addEventListener("message", onMessage);
let transport = DebuggerServer.connectPipe();
gClient = new DebuggerClient(transport);
@ -37,6 +50,7 @@ function test() {
yield closeConnection();
yield debuggerPanel._toolbox.destroy();
iframe.remove();
window.removeEventListener("message", onMessage);
finish();
});
}
@ -56,7 +70,7 @@ function testSources() {
gThreadClient.getSources(({sources}) => {
ok(sources.length, "retrieved sources");
sources.forEach(source => {
for (let source of sources) {
let url = source.url.split(" -> ").pop();
info(source.url + "\n\n\n" + url);
let { label, group } = gSources.getItemByValue(source.url).attachment;
@ -80,7 +94,7 @@ function testSources() {
} else {
ok(false, "Saw an unexpected source: " + url);
}
});
}
ok(foundAddonModule, "found code for the addon in the list");
ok(foundAddonBootstrap, "found bootstrap for the addon in the list");
@ -88,6 +102,14 @@ function testSources() {
// built-in browser SDK modules
ok(foundSDKModule > 10, "SDK modules are listed");
is(gTitle, "Debugger - browser_dbg_addon3", "Saw the right toolbox title.");
let groups = gDebugger.document.querySelectorAll(".side-menu-widget-group-title .name");
is(groups[0].value, "jar:", "Add-on bootstrap should be the first group");
is(groups[1].value, "resource://jid1-ami3akps3baaeg-at-jetpack", "Add-on code should be the second group");
is(groups[2].value, "Add-on SDK", "Add-on SDK should be the third group");
is(groups.length, 3, "Should be only three groups.");
deferred.resolve();
});

View File

@ -504,9 +504,9 @@ function initDebugger(aTarget, aWindow) {
function initAddonDebugger(aClient, aUrl, aFrame) {
info("Initializing an addon debugger panel.");
return getAddonActorForUrl(aClient, aUrl).then(({actor}) => {
return getAddonActorForUrl(aClient, aUrl).then((addonActor) => {
let targetOptions = {
form: { addonActor: actor },
form: { addonActor: addonActor.actor, title: addonActor.name },
client: aClient,
chrome: true
};

View File

@ -37,7 +37,7 @@ function connect() {
if (addonID) {
gClient.listAddons(({addons}) => {
let addonActor = addons.filter(addon => addon.id === addonID).pop();
openToolbox({ addonActor: addonActor.actor });
openToolbox({ addonActor: addonActor.actor, title: addonActor.name });
});
} else {
gClient.listTabs(openToolbox);

View File

@ -878,7 +878,9 @@ Toolbox.prototype = {
*/
focusConsoleInput: function() {
let hud = this.getPanel("webconsole").hud;
hud.jsterm.inputNode.focus();
if (hud && hud.jsterm) {
hud.jsterm.inputNode.focus();
}
},
/**
@ -964,7 +966,7 @@ Toolbox.prototype = {
toolName = toolboxStrings("toolbox.defaultTitle");
}
let title = toolboxStrings("toolbox.titleTemplate",
toolName, this.target.url);
toolName, this.target.url || this.target.name);
this._host.setTitle(title);
},

View File

@ -299,6 +299,9 @@ MarkupView.prototype = {
this.markNodeAsSelected(selection.nodeFront);
}
done();
}, (e) => {
console.error(e);
done();
});
} else {
this.unmarkSelectedNode();
@ -863,8 +866,10 @@ MarkupView.prototype = {
let parent = node.parentNode();
if (!container.elt.parentNode) {
let parentContainer = this._containers.get(parent);
parentContainer.childrenDirty = true;
this._updateChildren(parentContainer, {expand: node});
if (parentContainer) {
parentContainer.childrenDirty = true;
this._updateChildren(parentContainer, {expand: node});
}
}
node = parent;

View File

@ -66,10 +66,15 @@ this.SideMenuWidget = function SideMenuWidget(aNode, aOptions={}) {
SideMenuWidget.prototype = {
/**
* Specifies if groups in this container should be sorted alphabetically.
* Specifies if groups in this container should be sorted.
*/
sortedGroups: true,
/**
* The comparator used to sort groups.
*/
groupSortPredicate: function(a, b) a.localeCompare(b),
/**
* Specifies that the container viewport should be "stuck" to the
* bottom. That is, the container is automatically scrolled down to
@ -342,7 +347,7 @@ SideMenuWidget.prototype = {
});
this._groupsByName.set(aName, group);
group.insertSelfAt(this.sortedGroups ? group.findExpectedIndexForSelf() : -1);
group.insertSelfAt(this.sortedGroups ? group.findExpectedIndexForSelf(this.groupSortPredicate) : -1);
return group;
},
@ -484,14 +489,14 @@ SideMenuGroup.prototype = {
* @return number
* The expected index.
*/
findExpectedIndexForSelf: function() {
findExpectedIndexForSelf: function(sortPredicate) {
let identifier = this.identifier;
let groupsArray = this._orderedGroupElementsArray;
for (let group of groupsArray) {
let name = group.getAttribute("name");
if (name > identifier && // Insertion sort at its best :)
!name.contains(identifier)) { // Least significat group should be last.
if (sortPredicate(name, identifier) > 0 && // Insertion sort at its best :)
!name.contains(identifier)) { // Least significant group should be last.
return groupsArray.indexOf(group);
}
}

View File

@ -45,7 +45,7 @@ function testMenuFilterButton(aCategory) {
// Turn all the filters off, if they were on.
let menuItem = firstMenuItem;
while (menuItem != null) {
if (isChecked(menuItem)) {
if (menuItem.hasAttribute("prefKey") && isChecked(menuItem)) {
chooseMenuItem(menuItem);
}
menuItem = menuItem.nextSibling;
@ -89,10 +89,12 @@ function testMenuFilterButton(aCategory) {
menuItem = firstMenuItem;
while (menuItem) {
let prefKey = menuItem.getAttribute("prefKey");
ok(!isChecked(menuItem), "menu item " + prefKey + " for category " +
aCategory + " is no longer checked after clicking the button");
ok(!hud.ui.filterPrefs[prefKey], prefKey + " messages are " +
"off after clicking the button");
if (prefKey) {
ok(!isChecked(menuItem), "menu item " + prefKey + " for category " +
aCategory + " is no longer checked after clicking the button");
ok(!hud.ui.filterPrefs[prefKey], prefKey + " messages are " +
"off after clicking the button");
}
menuItem = menuItem.nextSibling;
}
@ -127,7 +129,8 @@ function testMenuFilterButton(aCategory) {
while (menuItem) {
// The csslog menu item is already unchecked at this point.
// Make sure it is not selected. See bug 971798.
if (menuItem.getAttribute("prefKey") != "csslog") {
prefKey = menuItem.getAttribute("prefKey");
if (prefKey && prefKey != "csslog") {
chooseMenuItem(menuItem);
}
menuItem = menuItem.nextSibling;
@ -164,7 +167,7 @@ function testIsolateFilterButton(aCategory) {
aCategory + " should not be checked after isolating for " + aCategory);
ok(!hud.ui.filterPrefs[prefKey], prefKey + " messages should be " +
"turned off after isolating for " + aCategory);
} else {
} else if (prefKey) {
ok(isChecked(item), "menu item " + prefKey + " for category " +
aCategory + " is checked after isolating for " + aCategory);
ok(hud.ui.filterPrefs[prefKey], prefKey + " messages are " +
@ -184,10 +187,12 @@ function testIsolateFilterButton(aCategory) {
menuItems = filterButton.querySelectorAll("menuitem");
Array.forEach(menuItems, (item) => {
let prefKey = item.getAttribute("prefKey");
ok(!isChecked(item), "menu item " + prefKey + " for category " +
aCategory + " is unchecked after isolating for " + aCategory);
ok(!hud.ui.filterPrefs[prefKey], prefKey + " messages are " +
"turned off after isolating for " + aCategory);
if (prefKey) {
ok(!isChecked(item), "menu item " + prefKey + " for category " +
aCategory + " is unchecked after isolating for " + aCategory);
ok(!hud.ui.filterPrefs[prefKey], prefKey + " messages are " +
"turned off after isolating for " + aCategory);
}
});
// Turn all the filters on again by clicking the button.

View File

@ -53,6 +53,7 @@ function runSelectionTests(aInspector) {
inspector.toolbox.once("picker-started", () => {
info("Picker mode started, now clicking on H1 to select that node");
executeSoon(() => {
h1.scrollIntoView();
EventUtils.synthesizeMouseAtCenter(h1, {}, content);
inspector.toolbox.once("picker-stopped", () => {
info("Picker mode stopped, H1 selected, now switching to the console");

View File

@ -401,6 +401,11 @@ WebConsoleFrame.prototype = {
*/
setSaveRequestAndResponseBodies:
function WCF_setSaveRequestAndResponseBodies(aValue) {
if (!this.webConsoleClient) {
// Don't continue if the webconsole disconnected.
return promise.resolve(null);
}
let deferred = promise.defer();
let newValue = !!aValue;
let toSet = {

View File

@ -104,7 +104,7 @@ function configureLogging() {
gLogger = Log.repository.getLogger("Browser.Experiments");
gLogger.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));
}
gLogger.level = gPrefs.get(PREF_LOGGING_LEVEL, 50);
gLogger.level = gPrefs.get(PREF_LOGGING_LEVEL, Log.Level.Warn);
let logDumping = gPrefs.get(PREF_LOGGING_DUMP, false);
if (logDumping != gLogDumping) {
@ -226,6 +226,11 @@ Experiments.Policy.prototype = {
return UpdateChannel.get();
},
locale: function () {
let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry);
return chrome.getSelectedLocale("global");
},
/*
* @return Promise<> Resolved with the payload data.
*/
@ -304,8 +309,10 @@ Experiments.Experiments.prototype = {
AddonManager.addAddonListener(this);
this._loadTask = Task.spawn(this._loadFromCache.bind(this)).then(
this._loadTask = Task.spawn(this._loadFromCache.bind(this));
this._loadTask.then(
() => {
gLogger.trace("Experiments::_loadTask finished ok");
this._loadTask = null;
this._run();
},
@ -386,7 +393,7 @@ Experiments.Experiments.prototype = {
},
_telemetryStatusChanged: function () {
_toggleExperimentsEnabled(gExperimentsEnabled);
this._toggleExperimentsEnabled(gExperimentsEnabled);
},
/**
@ -473,10 +480,16 @@ Experiments.Experiments.prototype = {
},
_run: function() {
gLogger.trace("Experiments::_run");
this._checkForShutdown();
if (!this._mainTask) {
this._mainTask = Task.spawn(this._main.bind(this)).then(
null,
this._mainTask = Task.spawn(this._main.bind(this));
this._mainTask.then(
() => {
gLogger.trace("Experiments::_main finished, scheduling next run");
this._mainTask = null;
this._scheduleNextRun();
},
(e) => {
gLogger.error("Experiments::_main caught error: " + e);
this._mainTask = null;
@ -488,6 +501,7 @@ Experiments.Experiments.prototype = {
_main: function*() {
do {
gLogger.trace("Experiments::_main iteration");
yield this._loadTask;
if (this._refresh) {
yield this._loadManifest();
@ -500,11 +514,10 @@ Experiments.Experiments.prototype = {
// while we were running, go again right now.
}
while (this._refresh || this._terminateReason);
this._mainTask = null;
this._scheduleNextRun();
},
_loadManifest: function*() {
gLogger.trace("Experiments::_loadManifest");
let uri = Services.urlFormatter.formatURLPref(PREF_BRANCH + PREF_MANIFEST_URI);
this._checkForShutdown();
@ -652,6 +665,7 @@ Experiments.Experiments.prototype = {
* Part of the main task to save the cache to disk, called from _main.
*/
_saveToCache: function* () {
gLogger.trace("Experiments::_saveToCache");
let path = this._cacheFilePath;
let textData = JSON.stringify({
version: CACHE_VERSION,
@ -670,6 +684,7 @@ Experiments.Experiments.prototype = {
* Task function, load the cached experiments manifest file from disk.
*/
_loadFromCache: function*() {
gLogger.trace("Experiments::_loadFromCache");
let path = this._cacheFilePath;
try {
let result = yield loadJSONAsync(path, { compression: "lz4" });
@ -706,7 +721,7 @@ Experiments.Experiments.prototype = {
* array in the manifest
*/
_updateExperiments: function (manifestObject) {
gLogger.trace("Experiments::updateExperiments() - experiments: " + JSON.stringify(manifestObject));
gLogger.trace("Experiments::_updateExperiments() - experiments: " + JSON.stringify(manifestObject));
if (manifestObject.version !== MANIFEST_VERSION) {
gLogger.warning("Experiments::updateExperiments() - unsupported version " + manifestObject.version);
@ -1149,9 +1164,8 @@ Experiments.ExperimentEntry.prototype = {
let app = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
let runtime = Cc["@mozilla.org/xre/app-info;1"]
.getService(Ci.nsIXULRuntime);
let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry);
let locale = chrome.getSelectedLocale("global");
let locale = this._policy.locale();
let channel = this._policy.updatechannel();
let data = this._manifestData;
@ -1161,6 +1175,7 @@ Experiments.ExperimentEntry.prototype = {
let startSec = (this.startDate || 0) / 1000;
gLogger.trace("ExperimentEntry::isApplicable() - now=" + now
+ ", randomValue=" + this._randomValue
+ ", data=" + JSON.stringify(this._manifestData));
// Not applicable if it already ran.
@ -1183,11 +1198,11 @@ Experiments.ExperimentEntry.prototype = {
{ name: "endTime",
condition: () => now < data.endTime },
{ name: "maxStartTime",
condition: () => !data.maxStartTime || now <= (data.maxStartTime - minActive) },
condition: () => !data.maxStartTime || now <= data.maxStartTime },
{ name: "maxActiveSeconds",
condition: () => !this._startDate || now <= (startSec + maxActive) },
{ name: "appName",
condition: () => !data.name || data.appName.indexOf(app.name) != -1 },
condition: () => !data.appName || data.appName.indexOf(app.name) != -1 },
{ name: "minBuildID",
condition: () => !data.minBuildID || app.platformBuildID >= data.minBuildID },
{ name: "maxBuildID",
@ -1201,9 +1216,9 @@ Experiments.ExperimentEntry.prototype = {
{ name: "locale",
condition: () => !data.locale || data.locale.indexOf(locale) != -1 },
{ name: "sample",
condition: () => !data.sample || this._randomValue <= data.sample },
condition: () => data.sample === undefined || this._randomValue <= data.sample },
{ name: "version",
condition: () => !data.version || data.appVersion.indexOf(app.version) != -1 },
condition: () => !data.version || data.version.indexOf(app.version) != -1 },
{ name: "minVersion",
condition: () => !data.minVersion || versionCmp.compare(app.version, data.minVersion) >= 0 },
{ name: "maxVersion",

View File

@ -3,6 +3,6 @@ contract @mozilla.org/browser/experiments-service;1 {f7800463-3b97-47f9-9341-b76
category update-timer ExperimentsService @mozilla.org/browser/experiments-service;1,getService,experiments-update-timer,experiments.manifest.fetchIntervalSeconds,86400
category profile-after-change ExperimentsService @mozilla.org/browser/experiments-service;1
category healthreport-js-provider-default ExperimentsProvider resource://gre/browser/modules/Experiments/Experiments.jsm
category healthreport-js-provider-default ExperimentsProvider resource:///modules/experiments/Experiments.jsm

View File

@ -28,6 +28,8 @@ const EXPERIMENT2_XPI_NAME = "experiment-2.xpi";
const EXPERIMENT3_ID = "test-experiment-3@tests.mozilla.org";
const EXPERIMENT4_ID = "test-experiment-4@tests.mozilla.org";
const DEFAULT_BUILDID = "2014060601";
const FAKE_EXPERIMENTS_1 = [
{
id: "id1",
@ -169,7 +171,7 @@ function createAppInfo(options) {
let platformVersion = options.platformVersion || "1.0";
let date = options.date || new Date();
let buildID = "" + date.getYear() + date.getMonth() + date.getDate() + "01";
let buildID = options.buildID || DEFAULT_BUILDID;
gAppInfo = {
// nsIXULAppInfo

View File

@ -63,6 +63,7 @@ add_task(function* test_setup() {
gReporter = yield getReporter("json_payload_simple");
yield gReporter.collectMeasurements();
let payload = yield gReporter.getJSONPayload(true);
do_register_cleanup(() => gReporter._shutdown());
patchPolicy(gPolicy, {
updatechannel: () => "nightly",
@ -131,8 +132,3 @@ add_task(function* test_startStop() {
Assert.equal(maybeStop, true, "Experiment should have been stopped.");
Assert.equal(experiment.enabled, false, "Experiment should be disabled.");
});
add_task(function* shutdown() {
yield gReporter._shutdown();
yield removeCacheFile();
});

View File

@ -75,6 +75,7 @@ add_task(function* test_setup() {
gReporter = yield getReporter("json_payload_simple");
yield gReporter.collectMeasurements();
let payload = yield gReporter.getJSONPayload(true);
do_register_cleanup(() => gReporter._shutdown());
gPolicy = new Experiments.Policy();
patchPolicy(gPolicy, {
@ -1278,9 +1279,3 @@ add_task(function* test_unexpectedUninstall() {
yield experiments.uninit();
yield removeCacheFile();
});
add_task(function* shutdown() {
yield gReporter._shutdown();
yield removeCacheFile();
});

View File

@ -76,6 +76,7 @@ add_task(function* test_setup() {
gReporter = yield getReporter("json_payload_simple");
yield gReporter.collectMeasurements();
let payload = yield gReporter.getJSONPayload(true);
do_register_cleanup(() => gReporter._shutdown());
gPolicy = new Experiments.Policy();
patchPolicy(gPolicy, {
@ -264,8 +265,3 @@ add_task(function* test_cache() {
yield experiments.uninit();
yield removeCacheFile();
});
add_task(function* shutdown() {
yield gReporter._shutdown();
yield removeCacheFile();
});

View File

@ -61,10 +61,13 @@ add_task(function* test_setup() {
gReporter = yield getReporter("json_payload_simple");
yield gReporter.collectMeasurements();
let payload = yield gReporter.getJSONPayload(true);
do_register_cleanup(() => gReporter._shutdown());
patchPolicy(gPolicy, {
updatechannel: () => "nightly",
locale: () => "en-US",
healthReportPayload: () => Promise.resolve(payload),
random: () => 0.5,
});
Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
@ -108,10 +111,81 @@ const sanityFilter = function filter(c) {
add_task(function* test_simpleFields() {
let testData = [
// "expected applicable?", failure reason or null, manifest data
// misc. environment
[false, ["appName"], {appName: []}],
[false, ["appName"], {appName: ["foo", gAppInfo.name + "-invalid"]}],
[true, null, {appName: ["not-an-app-name", gAppInfo.name]}],
[false, ["os"], {os: []}],
[false, ["os"], {os: ["42", "abcdef"]}],
[true, null, {os: [gAppInfo.OS, "plan9"]}],
[true, null, {os: [gAppInfo.OS, "plan9"]}],
[false, ["channel"], {channel: []}],
[false, ["channel"], {channel: ["foo", gPolicy.updatechannel() + "-invalid"]}],
[true, null, {channel: ["not-a-channel", gPolicy.updatechannel()]}],
[false, ["locale"], {locale: []}],
[false, ["locale"], {locale: ["foo", gPolicy.locale + "-invalid"]}],
[true, null, {locale: ["not-a-locale", gPolicy.locale()]}],
// version
[false, ["version"], {version: []}],
[false, ["version"], {version: ["-1", gAppInfo.version + "-invalid", "asdf", "0,4", "99.99", "0.1.1.1"]}],
[true, null, {version: ["99999999.999", "-1", gAppInfo.version]}],
[false, ["minVersion"], {minVersion: "1.0.1"}],
[true, null, {minVersion: "1.0b1"}],
[true, null, {minVersion: "1.0"}],
[true, null, {minVersion: "0.9"}],
[false, ["maxVersion"], {maxVersion: "0.1"}],
[false, ["maxVersion"], {maxVersion: "0.9.9"}],
[false, ["maxVersion"], {maxVersion: "1.0b1"}],
[true, ["maxVersion"], {maxVersion: "1.0"}],
[true, ["maxVersion"], {maxVersion: "1.7pre"}],
// build id
[false, ["buildIDs"], {buildIDs: []}],
[false, ["buildIDs"], {buildIDs: ["not-a-build-id", gAppInfo.platformBuildID + "-invalid"]}],
[true, null, {buildIDs: ["not-a-build-id", gAppInfo.platformBuildID]}],
[true, null, {buildIDs: ["not-a-build-id", gAppInfo.platformBuildID]}],
[true, null, {minBuildID: "2014060501"}],
[true, null, {minBuildID: "2014060601"}],
[false, ["minBuildID"], {minBuildID: "2014060701"}],
[false, ["maxBuildID"], {maxBuildID: "2014010101"}],
[true, null, {maxBuildID: "2014060601"}],
[true, null, {maxBuildID: "2014060901"}],
// sample
[false, ["sample"], {sample: -1 }],
[false, ["sample"], {sample: 0.0}],
[false, ["sample"], {sample: 0.1}],
[true, null, {sample: 0.5}],
[true, null, {sample: 0.6}],
[true, null, {sample: 1.0}],
[true, null, {sample: 0.5}],
// experiment control
[false, ["disabled"], {disabled: true}],
[true, null, {disabled: false}],
[false, ["frozen"], {frozen: true}],
[true, null, {frozen: false}],
[false, null, {frozen: true, disabled: true}],
[false, null, {frozen: true, disabled: false}],
[false, null, {frozen: false, disabled: true}],
[true, null, {frozen: false, disabled: false}],
// jsfilter
[true, null, {jsfilter: "function filter(c) { return true; }"}],
[false, ["jsfilter-false"], {jsfilter: "function filter(c) { return false; }"}],
[true, null, {jsfilter: "function filter(c) { return 123; }"}], // truthy
@ -150,18 +224,73 @@ add_task(function* test_simpleFields() {
});
add_task(function* test_times() {
let baseDate = new Date(2014, 5, 6, 12);
let baseTimeSec = baseDate.getTime() / 1000;
let now = new Date(2014, 5, 6, 12);
let nowSec = now.getTime() / 1000;
let testData = [
// "expected applicable?", rejection reason or null, fake now date, manifest data
[false, "maxStartTime", baseDate,
{maxStartTime: baseTimeSec - 10 * SEC_IN_ONE_DAY}],
[false, "endTime", baseDate,
{startTime: baseTimeSec - 10 * SEC_IN_ONE_DAY,
endTime: baseTimeSec - 5 * SEC_IN_ONE_DAY}],
[true, null, baseDate,
{startTime: baseTimeSec - 5 * SEC_IN_ONE_DAY,
endTime: baseTimeSec + 10 * SEC_IN_ONE_DAY}],
// start time
[true, null, now,
{startTime: nowSec - 5 * SEC_IN_ONE_DAY,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
[true, null, now,
{startTime: nowSec ,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
[false, "startTime", now,
{startTime: nowSec + 5 * SEC_IN_ONE_DAY,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
// end time
[false, "endTime", now,
{startTime: nowSec - 5 * SEC_IN_ONE_DAY,
endTime: nowSec - 10 * SEC_IN_ONE_DAY}],
[false, "endTime", now,
{startTime: nowSec - 5 * SEC_IN_ONE_DAY,
endTime: nowSec - 5 * SEC_IN_ONE_DAY}],
// max start time
[false, "maxStartTime", now,
{maxStartTime: nowSec - 15 * SEC_IN_ONE_DAY,
startTime: nowSec - 10 * SEC_IN_ONE_DAY,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
[false, "maxStartTime", now,
{maxStartTime: nowSec - 1 * SEC_IN_ONE_DAY,
startTime: nowSec - 10 * SEC_IN_ONE_DAY,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
[false, "maxStartTime", now,
{maxStartTime: nowSec - 10 * SEC_IN_ONE_DAY,
startTime: nowSec - 10 * SEC_IN_ONE_DAY,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
[true, null, now,
{maxStartTime: nowSec,
startTime: nowSec - 10 * SEC_IN_ONE_DAY,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
[true, null, now,
{maxStartTime: nowSec + 1 * SEC_IN_ONE_DAY,
startTime: nowSec - 10 * SEC_IN_ONE_DAY,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
// max active seconds
[true, null, now,
{maxActiveSeconds: 5 * SEC_IN_ONE_DAY,
startTime: nowSec - 10 * SEC_IN_ONE_DAY,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
[true, null, now,
{maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
startTime: nowSec - 10 * SEC_IN_ONE_DAY,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
[true, null, now,
{maxActiveSeconds: 15 * SEC_IN_ONE_DAY,
startTime: nowSec - 10 * SEC_IN_ONE_DAY,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
[true, null, now,
{maxActiveSeconds: 20 * SEC_IN_ONE_DAY,
startTime: nowSec - 10 * SEC_IN_ONE_DAY,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
];
for (let i=0; i<testData.length; ++i) {
@ -186,8 +315,3 @@ add_task(function* test_times() {
}
}
});
add_task(function* shutdown() {
yield gReporter._shutdown();
yield removeCacheFile();
});

View File

@ -77,6 +77,3 @@ add_task(function* test_fetchInvalid() {
yield ex.uninit();
});
add_task(function* shutdown() {
yield removeCacheFile();
});

View File

@ -114,6 +114,7 @@ add_task(function* test_setup() {
gReporter = yield getReporter("json_payload_simple");
yield gReporter.collectMeasurements();
let payload = yield gReporter.getJSONPayload(true);
do_register_cleanup(() => gReporter._shutdown());
gPolicy = new Experiments.Policy();
let dummyTimer = { cancel: () => {}, clear: () => {} };
@ -353,8 +354,3 @@ add_task(function* test_telemetryBasics() {
yield experiments.uninit();
yield removeCacheFile();
});
add_task(function* shutdown() {
yield gReporter._shutdown();
yield removeCacheFile();
});

View File

@ -16,7 +16,7 @@
%define forwardTransitionLength 150ms
%define conditionalForwardWithUrlbar window:not([chromehidden~="toolbar"]) #urlbar-container
%define conditionalForwardWithUrlbarWidth 40
%define conditionalForwardWithUrlbarWidth 30
#menubar-items {
-moz-box-orient: vertical; /* for flex hack */
@ -676,23 +676,98 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
-moz-margin-start: -4px;
margin-top: 3px;
margin-bottom: 3px;
}
#forward-button[disabled] {
transform: scale(0);
opacity: 0;
pointer-events: none;
#back-button {
padding-top: 3px;
padding-bottom: 3px;
-moz-padding-start: 5px;
-moz-padding-end: 0;
position: relative;
z-index: 1;
border-radius: 0 10000px 10000px 0;
}
@conditionalForwardWithUrlbar@:not([switchingtabs]) > #forward-button {
transition: @forwardTransitionLength@ ease-out;
#back-button:-moz-locale-dir(rtl) {
border-radius: 10000px 0 0 10000px;
}
#back-button > menupopup {
margin-top: -1px;
}
#back-button > .toolbarbutton-icon {
border-radius: 10000px;
background-clip: padding-box;
padding: 6px;
border: none;
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(210,54%,20%,.25),
0 1px 0 hsla(210,54%,20%,.35);
background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1));
transition-property: background-color, box-shadow;
transition-duration: 250ms;
}
#back-button:not([disabled="true"]):not([open="true"]):not(:active):hover > .toolbarbutton-icon {
background-color: hsla(210,48%,96%,.75);
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(210,54%,20%,.3),
0 1px 0 hsla(210,54%,20%,.4),
0 0 4px hsla(210,54%,20%,.2);
}
#back-button:not([disabled="true"]):hover:active > .toolbarbutton-icon,
#back-button[open="true"] > .toolbarbutton-icon {
background-color: hsla(210,54%,20%,.15);
box-shadow: 0 1px 1px hsla(210,54%,20%,.1) inset,
0 0 1px hsla(210,54%,20%,.2) inset,
0 0 0 1px hsla(210,54%,20%,.4),
0 1px 0 hsla(210,54%,20%,.2);
transition: none;
}
#main-window:not([customizing]) #back-button[disabled] > .toolbarbutton-icon {
box-shadow: 0 0 0 1px hsla(210,54%,20%,.55),
0 1px 0 hsla(210,54%,20%,.65) !important;
transition: none;
}
#back-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
#forward-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
#forward-button:-moz-locale-dir(rtl) {
transform: scaleX(-1);
}
@conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] {
opacity: 0;
}
@conditionalForwardWithUrlbar@:not([switchingtabs]) > #forward-button {
transition: opacity @forwardTransitionLength@ ease-out;
}
@conditionalForwardWithUrlbar@ > #forward-button[occluded-by-urlbar] {
visibility: hidden;
}
#forward-button {
padding: 0;
}
#forward-button > .toolbarbutton-icon {
background-clip: padding-box;
clip-path: url("chrome://browser/content/browser.xul#keyhole-forward-clip-path");
margin-left: -6px;
border-left-style: none;
border-radius: 0;
padding: 2px 3px 2px 9px;
border: 1px solid #9a9a9a;
}
/* tabview menu item */
#menu_tabview {
@ -801,9 +876,21 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
}
/* Location bar */
#urlbar,
.searchbar-textbox {
-moz-appearance: none;
padding: 1px;
border: 1px solid ThreeDShadow;
border-radius: 2px;
}
#urlbar[focused],
.searchbar-textbox[focused] {
border-color: Highlight;
}
#urlbar {
-moz-appearance: textfield;
padding: 0;
background-color: -moz-field;
}
.urlbar-textbox-container {
@ -825,26 +912,59 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
}
@conditionalForwardWithUrlbar@ > #urlbar-wrapper {
-moz-padding-start: @conditionalForwardWithUrlbarWidth@px;
padding-left: @conditionalForwardWithUrlbarWidth@px;
-moz-margin-start: -@conditionalForwardWithUrlbarWidth@px;
position: relative;
pointer-events: none;
}
@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar {
-moz-border-start: none;
margin-left: 0;
pointer-events: all;
}
@conditionalForwardWithUrlbar@:not([switchingtabs]) > #urlbar-wrapper > #urlbar {
transition: margin-left @forwardTransitionLength@ ease-out,
margin-right @forwardTransitionLength@ ease-out;
transition: margin-left @forwardTransitionLength@ ease-out;
}
@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar:-moz-locale-dir(ltr) {
@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar:-moz-locale-dir(ltr) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar:-moz-locale-dir(rtl) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper {
/* Work with margin-top to align the clip-path correctly. */
margin-top: 5px;
clip-path: url("chrome://browser/content/browser.xul#urlbar-back-button-clip-path");
}
@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar {
margin-top: -4px;
margin-left: -@conditionalForwardWithUrlbarWidth@px;
}
@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar:-moz-locale-dir(rtl) {
margin-right: -@conditionalForwardWithUrlbarWidth@px;
@conditionalForwardWithUrlbar@[forwarddisabled]:hover:not([switchingtabs]) > #urlbar-wrapper > #urlbar {
/* delay the hiding of the forward button when hovered to avoid accidental clicks on the url bar */
transition-delay: 100s;
}
@conditionalForwardWithUrlbar@[forwarddisabled][switchingtabs] + #urlbar-container > #urlbar,
@conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) > #urlbar-wrapper > #urlbar {
/* when switching tabs, or when not hovered anymore, trigger a new transition
* to hide the forward button immediately */
margin-left: -@conditionalForwardWithUrlbarWidth@.01px;
}
@conditionalForwardWithUrlbar@ > #urlbar-wrapper:-moz-locale-dir(rtl),
@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar:-moz-locale-dir(rtl) {
/* let windows-urlbar-back-button-mask clip the urlbar's right side for RTL */
transform: scaleX(-1);
}
#urlbar-icons {
@ -884,41 +1004,66 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
min-width: calc(54px + 11ch);
}
%include ../shared/identity-block.inc.css
/* identity box */
#page-proxy-favicon {
margin-top: 2px;
margin-bottom: 2px;
-moz-margin-start: 4px;
-moz-margin-end: 3px;
-moz-image-region: rect(0, 16px, 16px, 0);
}
#identity-box:hover > #page-proxy-favicon {
-moz-image-region: rect(0, 32px, 16px, 16px);
}
#identity-box:hover:active > #page-proxy-favicon,
#identity-box[open=true] > #page-proxy-favicon {
-moz-image-region: rect(0, 48px, 16px, 32px);
}
/* Identity indicator */
#identity-box {
padding: 1px;
margin: -1px;
-moz-margin-end: 0;
font-size: .9em;
}
#identity-box:-moz-locale-dir(ltr) {
border-top-left-radius: 2.5px;
border-bottom-left-radius: 2.5px;
border-top-left-radius: 1.5px;
border-bottom-left-radius: 1.5px;
}
#identity-box:-moz-locale-dir(rtl) {
border-top-right-radius: 2.5px;
border-bottom-right-radius: 2.5px;
border-top-right-radius: 1.5px;
border-bottom-right-radius: 1.5px;
}
#notification-popup-box:not([hidden]) + #identity-box {
-moz-padding-start: 10px;
border-radius: 0;
}
@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar > #identity-box {
border-radius: 0;
}
@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
padding-left: 5px;
transition: padding-left;
}
@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
padding-right: 5px;
transition: padding-right;
}
@conditionalForwardWithUrlbar@[forwarddisabled]:hover:not([switchingtabs]) > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box {
/* forward button hiding is delayed when hovered */
transition-delay: 100s;
}
@conditionalForwardWithUrlbar@[forwarddisabled][switchingtabs] + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr),
@conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
/* when not hovered anymore, trigger a new non-delayed transition to react to the forward button hiding */
padding-left: 5.01px;
}
@conditionalForwardWithUrlbar@[forwarddisabled][switchingtabs] + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl),
@conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
/* when not hovered anymore, trigger a new non-delayed transition to react to the forward button hiding */
padding-right: 5.01px;
}
#urlbar[pageproxystate="valid"] > #identity-box.chromeUI,
#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity {
-moz-margin-end: 4px;
}
#identity-box.verifiedIdentity:not(:-moz-lwtheme) {
background-color: #fff;
}
#identity-box:-moz-focusring {
@ -931,10 +1076,27 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
-moz-padding-end: 5px;
}
#urlbar[pageproxystate="valid"] > #identity-box.chromeUI,
#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity {
background-color: #fff;
-moz-margin-end: 4px;
%include ../shared/identity-block.inc.css
#page-proxy-favicon {
margin-top: 1px;
margin-bottom: 1px;
-moz-margin-start: 3px;
-moz-margin-end: 2px;
-moz-image-region: rect(0, 16px, 16px, 0);
}
@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar > #identity-box > #page-proxy-favicon {
-moz-margin-end: 1px;
}
#identity-box:hover > #page-proxy-favicon {
-moz-image-region: rect(0, 32px, 16px, 16px);
}
#identity-box:hover:active > #page-proxy-favicon,
#identity-box[open=true] > #page-proxy-favicon {
-moz-image-region: rect(0, 48px, 16px, 32px);
}
/* Identity popup icons */

View File

@ -105,6 +105,7 @@ browser.jar:
skin/classic/browser/feeds/audioFeedIcon16.png (feeds/feedIcon16.png)
skin/classic/browser/feeds/subscribe.css (feeds/subscribe.css)
skin/classic/browser/feeds/subscribe-ui.css (feeds/subscribe-ui.css)
skin/classic/browser/fonts/ClearSans-Regular.ttf (../shared/ClearSans-Regular.ttf)
skin/classic/browser/newtab/newTab.css (newtab/newTab.css)
skin/classic/browser/newtab/controls.png (newtab/controls.png)
skin/classic/browser/places/bookmarksMenu.png (places/bookmarksMenu.png)
@ -134,6 +135,12 @@ browser.jar:
#endif
* skin/classic/browser/preferences/preferences.css (preferences/preferences.css)
* skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
skin/classic/browser/preferences/in-content/check.png (preferences/in-content/check.png)
skin/classic/browser/preferences/in-content/icons.png (preferences/in-content/icons.png)
skin/classic/browser/preferences/in-content/header.png (preferences/in-content/header.png)
skin/classic/browser/preferences/in-content/dropdown.png (preferences/in-content/dropdown.png)
skin/classic/browser/preferences/in-content/sorter.png (preferences/in-content/sorter.png)
skin/classic/browser/preferences/in-content/dropdown-disabled.png (preferences/in-content/dropdown-disabled.png)
skin/classic/browser/preferences/applications.css (preferences/applications.css)
skin/classic/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
skin/classic/browser/social/services-16.png (social/services-16.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -2,174 +2,89 @@
- 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/. */
@import url("chrome://global/skin/inContentUI.css");
%include ../../../shared/in-content/preferences.css
@namespace html "http://www.w3.org/1999/xhtml";
#header {
margin-bottom: 18px;
}
caption {
font-size: 1.667rem;
}
.main-content {
max-width: 800px;
}
prefpane > .content-box {
overflow: auto;
}
/* Category List */
#categories {
button > .button-box,
menulist > .menulist-label-box {
-moz-appearance: none;
border: none;
-moz-margin-end: -1px;
background-color: transparent;
position: relative;
margin-top: 41px;
}
.category {
button[type="menu"] > .button-box > .button-menu-dropmarker {
-moz-appearance: none !important;
}
menulist:not([editable="true"]) > .menulist-dropmarker {
display: -moz-box;
margin-top: 6px;
margin-bottom: 6px;
}
checkbox {
-moz-binding: url("chrome://global/content/bindings/checkbox.xml#checkbox");
}
.checkbox-check {
max-height: 23px;
}
checkbox:hover::before,
checkbox[checked]::before {
max-height: 10px;
margin-top: 7px;
margin-bottom: 6px;
-moz-margin-end: -19px;
-moz-margin-start: 4px;
background-repeat: no-repeat;
}
radio {
-moz-binding: url("chrome://global/content/bindings/radio.xml#radio");
margin: 7px 0;
}
.radio-check {
max-height: 23px;
}
.radio-label-box {
-moz-appearance: none;
border-width: 1px;
-moz-border-end-width: 0;
border-style: solid;
border-color: transparent;
padding: 9px 4px 10px;
-moz-padding-end: 8px;
-moz-box-align: center;
overflow: hidden;
min-height: 0;
color: WindowText;
height: 52px;
}
.category:-moz-locale-dir(ltr) {
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}
.category:-moz-locale-dir(rtl) {
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
}
.category[selected] {
background-color: -moz-Field;
color: -moz-FieldText;
border-color: ThreeDShadow;
}
.category-name {
font-size: 1.5rem;
-moz-padding-end: 24px;
}
/* Maximize the size of the viewport when the window is small */
@media (max-width: 800px) {
.category-name {
display: none;
}
}
.category-icon {
width: 32px;
height: 32px;
margin: 0 6px;
radio:hover::before,
radio[selected]::before {
max-height: 11px;
margin-top: 6px;
margin-bottom: 6px;
-moz-margin-end: -17px;
-moz-margin-start: 6px;
-moz-margin-end: 5px;
list-style-image: url("chrome://browser/skin/preferences/Options.png");
}
#category-general > .category-icon {
-moz-image-region: rect(0, 32px, 32px, 0);
}
#category-content > .category-icon {
-moz-image-region: rect(0, 96px, 32px, 64px)
}
#category-application > .category-icon {
-moz-image-region: rect(0, 128px, 32px, 96px)
}
#category-privacy > .category-icon {
-moz-image-region: rect(0, 160px, 32px, 128px)
}
#category-security > .category-icon {
-moz-image-region: rect(0, 192px, 32px, 160px)
}
#category-advanced > .category-icon {
-moz-image-region: rect(0, 224px, 32px, 192px)
}
%ifdef MOZ_SERVICES_SYNC
#category-sync > .category-icon {
list-style-image: url("chrome://browser/skin/preferences/Options-sync.png");
}
%endif
/* Applications Pane Styles */
#applications-content {
padding: 15px;
}
#handlersView {
.numberbox-input-box {
-moz-appearance: none;
border: 1px solid ThreeDShadow;
overflow-y: auto;
border-width: 0;
}
/* XXX This style is for bug 740213 and should be removed once that
bug has a solution. */
description > html|a {
cursor: pointer;
spinbuttons {
-moz-appearance: none;
}
/* XXX Styles Below can be removed once bug 660726 lands */
.nav-button {
min-width: 0;
.actionsMenu {
font-family: "Clear Sans", sans-serif;
font-size: 1.25rem;
line-height: 22px;
}
#back-btn:-moz-locale-dir(ltr) {
list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=toolbar");
.actionsMenu > .menulist-label-box > .menulist-icon {
margin-top: 1px;
-moz-margin-start: 1px;
-moz-margin-end: 6px;
}
#forward-btn:-moz-locale-dir(ltr) {
list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=toolbar");
.actionsMenu > .menulist-label-box > .menulist-label {
margin-top: 2px !important;
}
#back-btn:-moz-locale-dir(rtl) {
list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=toolbar");
}
#forward-btn:-moz-locale-dir(rtl) {
list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=toolbar");
}
#back-btn[disabled="true"]:-moz-locale-dir(ltr) {
list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=toolbar&state=disabled");
}
#forward-btn[disabled="true"]:-moz-locale-dir(ltr) {
list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=toolbar&state=disabled");
}
#back-btn[disabled="true"]:-moz-locale-dir(rtl) {
list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=toolbar&state=disabled");
}
#forward-btn[disabled="true"]:-moz-locale-dir(rtl) {
list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=toolbar&state=disabled");
}
.header-button .toolbarbutton-text {
display: none;
menulist.actionsMenu > .menulist-dropmarker {
margin-top: 11px;
margin-bottom: 11px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

View File

@ -502,7 +502,7 @@ toolbar .toolbarbutton-1[type="menu-button"]:not(:-moz-any([disabled],[open],[bu
toolbar .toolbarbutton-1:not(:-moz-any([type="menu-button"],[disabled],#back-button,#forward-button)):-moz-any(:hover:active,[open],[checked]),
toolbar .toolbarbutton-1[type="menu-button"]:not(:-moz-any([disabled],[open]))[buttonover]:active > .toolbarbutton-menubutton-button,
toolbar .toolbarbutton-1[type="menu-button"]:not(:-moz-any([disabled],[open],[buttonover])):hover:active > .toolbarbutton-menubutton-dropmarker,
toolbar .toolbarbutton-1[type="menu-button"][open] > .toolbarbutton-menubutton-dropmarker {
toolbar .toolbarbutton-1[type="menu-button"][open]:not([disabled]) > .toolbarbutton-menubutton-dropmarker {
background: hsla(0,0%,0%,.02) linear-gradient(hsla(0,0%,0%,.12), hsla(0,0%,0%,0)) border-box;
border-color: hsla(0,0%,0%,.3);
box-shadow: 0 1px 0 hsla(0,0%,100%,.5),

View File

@ -168,6 +168,7 @@ browser.jar:
skin/classic/browser/feeds/videoFeedIcon16.png (feeds/feedIcon16.png)
skin/classic/browser/feeds/audioFeedIcon.png (feeds/feedIcon.png)
skin/classic/browser/feeds/audioFeedIcon16.png (feeds/feedIcon16.png)
skin/classic/browser/fonts/ClearSans-Regular.ttf (../shared/ClearSans-Regular.ttf)
skin/classic/browser/newtab/newTab.css (newtab/newTab.css)
skin/classic/browser/newtab/controls.png (newtab/controls.png)
skin/classic/browser/newtab/controls@2x.png (newtab/controls@2x.png)
@ -223,6 +224,14 @@ browser.jar:
skin/classic/browser/preferences/saveFile.png (preferences/saveFile.png)
* skin/classic/browser/preferences/preferences.css (preferences/preferences.css)
* skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
skin/classic/browser/preferences/in-content/check.png (preferences/in-content/check.png)
skin/classic/browser/preferences/in-content/check@2x.png (preferences/in-content/check@2x.png)
skin/classic/browser/preferences/in-content/icons.png (preferences/in-content/icons.png)
skin/classic/browser/preferences/in-content/icons@2x.png (preferences/in-content/icons@2x.png)
skin/classic/browser/preferences/in-content/header.png (preferences/in-content/icons@2x.png)
skin/classic/browser/preferences/in-content/sorter.png (preferences/in-content/sorter.png)
skin/classic/browser/preferences/in-content/dropdown.png (preferences/in-content/dropdown.png)
skin/classic/browser/preferences/in-content/dropdown-disabled.png (preferences/in-content/dropdown-disabled.png)
skin/classic/browser/preferences/applications.css (preferences/applications.css)
skin/classic/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
skin/classic/browser/social/services-16.png (social/services-16.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

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