mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to inbound a=merge
This commit is contained in:
commit
7904e1ccbc
@ -8,16 +8,13 @@ module.metadata = {
|
||||
"stability": "stable"
|
||||
};
|
||||
|
||||
const { Cc, Ci, Cu } = require("chrome");
|
||||
const { Cc, Ci } = require("chrome");
|
||||
const file = require("./io/file");
|
||||
const prefs = require("./preferences/service");
|
||||
const jpSelf = require("./self");
|
||||
const timer = require("./timers");
|
||||
const unload = require("./system/unload");
|
||||
const { emit, on, off } = require("./event/core");
|
||||
const { defer } = require('./core/promise');
|
||||
|
||||
const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
|
||||
const WRITE_PERIOD_PREF = "extensions.addon-sdk.simple-storage.writePeriod";
|
||||
const WRITE_PERIOD_DEFAULT = 300000; // 5 minutes
|
||||
@ -38,57 +35,6 @@ Object.defineProperties(exports, {
|
||||
}
|
||||
});
|
||||
|
||||
function getHash(data) {
|
||||
let { promise, resolve } = defer();
|
||||
|
||||
let crypto = Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
|
||||
crypto.init(crypto.MD5);
|
||||
|
||||
let listener = {
|
||||
onStartRequest: function() { },
|
||||
|
||||
onDataAvailable: function(request, context, inputStream, offset, count) {
|
||||
crypto.updateFromStream(inputStream, count);
|
||||
},
|
||||
|
||||
onStopRequest: function(request, context, status) {
|
||||
resolve(crypto.finish(false));
|
||||
}
|
||||
};
|
||||
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
|
||||
createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
let stream = converter.convertToInputStream(data);
|
||||
let pump = Cc["@mozilla.org/network/input-stream-pump;1"].
|
||||
createInstance(Ci.nsIInputStreamPump);
|
||||
pump.init(stream, -1, -1, 0, 0, true);
|
||||
pump.asyncRead(listener, null);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function writeData(filename, data) {
|
||||
let { promise, resolve, reject } = defer();
|
||||
|
||||
let stream = file.open(filename, "w");
|
||||
try {
|
||||
stream.writeAsync(data, err => {
|
||||
if (err)
|
||||
reject(err);
|
||||
else
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
// writeAsync closes the stream after it's done, so only close on error.
|
||||
stream.close();
|
||||
reject(err);
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
// A generic JSON store backed by a file on disk. This should be isolated
|
||||
// enough to move to its own module if need be...
|
||||
function JsonStore(options) {
|
||||
@ -97,9 +43,11 @@ function JsonStore(options) {
|
||||
this.writePeriod = options.writePeriod;
|
||||
this.onOverQuota = options.onOverQuota;
|
||||
this.onWrite = options.onWrite;
|
||||
this.hash = null;
|
||||
|
||||
unload.ensure(this);
|
||||
this.startTimer();
|
||||
|
||||
this.writeTimer = timer.setInterval(this.write.bind(this),
|
||||
this.writePeriod);
|
||||
}
|
||||
|
||||
JsonStore.prototype = {
|
||||
@ -133,18 +81,11 @@ JsonStore.prototype = {
|
||||
undefined;
|
||||
},
|
||||
|
||||
startTimer: function JsonStore_startTimer() {
|
||||
timer.setTimeout(() => {
|
||||
this.write().then(this.startTimer.bind(this));
|
||||
}, this.writePeriod);
|
||||
},
|
||||
|
||||
// Removes the backing file and all empty subdirectories.
|
||||
purge: function JsonStore_purge() {
|
||||
try {
|
||||
// This'll throw if the file doesn't exist.
|
||||
file.remove(this.filename);
|
||||
this.hash = null;
|
||||
let parentPath = this.filename;
|
||||
do {
|
||||
parentPath = file.dirname(parentPath);
|
||||
@ -164,25 +105,31 @@ JsonStore.prototype = {
|
||||
// errors cause tests to fail. Supporting "known" errors in the test
|
||||
// harness appears to be non-trivial. Maybe later.
|
||||
this.root = JSON.parse(str);
|
||||
let self = this;
|
||||
getHash(str).then(hash => this.hash = hash);
|
||||
}
|
||||
catch (err) {
|
||||
this.root = {};
|
||||
this.hash = null;
|
||||
}
|
||||
},
|
||||
|
||||
// If the store is under quota, writes the root to the backing file.
|
||||
// Otherwise quota observers are notified and nothing is written.
|
||||
write: function JsonStore_write() {
|
||||
if (this.quotaUsage > 1)
|
||||
this.onOverQuota(this);
|
||||
else
|
||||
this._write();
|
||||
},
|
||||
|
||||
// Cleans up on unload. If unloading because of uninstall, the store is
|
||||
// purged; otherwise it's written.
|
||||
unload: function JsonStore_unload(reason) {
|
||||
timer.clearTimeout(this.writeTimer);
|
||||
timer.clearInterval(this.writeTimer);
|
||||
this.writeTimer = null;
|
||||
|
||||
if (reason === "uninstall")
|
||||
this.purge();
|
||||
else
|
||||
this.write();
|
||||
this._write();
|
||||
},
|
||||
|
||||
// True if the root is an empty object.
|
||||
@ -201,40 +148,32 @@ JsonStore.prototype = {
|
||||
// Writes the root to the backing file, notifying write observers when
|
||||
// complete. If the store is over quota or if it's empty and the store has
|
||||
// never been written, nothing is written and write observers aren't notified.
|
||||
write: Task.async(function JsonStore_write() {
|
||||
_write: function JsonStore__write() {
|
||||
// Don't write if the root is uninitialized or if the store is empty and the
|
||||
// backing file doesn't yet exist.
|
||||
if (!this.isRootInited || (this._isEmpty && !file.exists(this.filename)))
|
||||
return;
|
||||
|
||||
let data = JSON.stringify(this.root);
|
||||
|
||||
// If the store is over quota, don't write. The current under-quota state
|
||||
// should persist.
|
||||
if ((this.quota > 0) && (data.length > this.quota)) {
|
||||
this.onOverQuota(this);
|
||||
return;
|
||||
}
|
||||
|
||||
// Hash the data to compare it to any previously written data
|
||||
let hash = yield getHash(data);
|
||||
|
||||
if (hash == this.hash)
|
||||
if (this.quotaUsage > 1)
|
||||
return;
|
||||
|
||||
// Finally, write.
|
||||
let stream = file.open(this.filename, "w");
|
||||
try {
|
||||
yield writeData(this.filename, data);
|
||||
|
||||
this.hash = hash;
|
||||
if (this.onWrite)
|
||||
this.onWrite(this);
|
||||
stream.writeAsync(JSON.stringify(this.root), function writeAsync(err) {
|
||||
if (err)
|
||||
console.error("Error writing simple storage file: " + this.filename);
|
||||
else if (this.onWrite)
|
||||
this.onWrite(this);
|
||||
}.bind(this));
|
||||
}
|
||||
catch (err) {
|
||||
console.error("Error writing simple storage file: " + this.filename);
|
||||
console.error(err);
|
||||
// writeAsync closes the stream after it's done, so only close on error.
|
||||
stream.close();
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -6,7 +6,6 @@ const file = require("sdk/io/file");
|
||||
const prefs = require("sdk/preferences/service");
|
||||
|
||||
const QUOTA_PREF = "extensions.addon-sdk.simple-storage.quota";
|
||||
const WRITE_PERIOD_PREF = "extensions.addon-sdk.simple-storage.writePeriod";
|
||||
|
||||
let {Cc,Ci} = require("chrome");
|
||||
|
||||
@ -35,13 +34,12 @@ exports.testSetGet = function (assert, done) {
|
||||
// Load the module again and make sure the value stuck.
|
||||
loader = Loader(module);
|
||||
ss = loader.require("sdk/simple-storage");
|
||||
assert.equal(ss.storage.foo, val, "Value should persist");
|
||||
manager(loader).jsonStore.onWrite = function (storage) {
|
||||
assert.fail("Nothing should be written since `storage` was not changed.");
|
||||
file.remove(storeFilename);
|
||||
done();
|
||||
};
|
||||
assert.equal(ss.storage.foo, val, "Value should persist");
|
||||
loader.unload();
|
||||
file.remove(storeFilename);
|
||||
done();
|
||||
};
|
||||
let val = "foo";
|
||||
ss.storage.foo = val;
|
||||
@ -162,11 +160,10 @@ exports.testQuotaExceededHandle = function (assert, done) {
|
||||
assert.equal(ss.storage.x, 4, "x value should be correct");
|
||||
assert.equal(ss.storage.y, 5, "y value should be correct");
|
||||
manager(loader).jsonStore.onWrite = function (storage) {
|
||||
assert.fail("Nothing should be written since `storage` was not changed.");
|
||||
prefs.reset(QUOTA_PREF);
|
||||
done();
|
||||
};
|
||||
loader.unload();
|
||||
prefs.reset(QUOTA_PREF);
|
||||
done();
|
||||
};
|
||||
loader.unload();
|
||||
});
|
||||
@ -200,9 +197,6 @@ exports.testQuotaExceededNoHandle = function (assert, done) {
|
||||
assert.equal(ss.storage, val,
|
||||
"Over-quota value should not have been written, " +
|
||||
"old value should have persisted: " + ss.storage);
|
||||
manager(loader).jsonStore.onWrite = function (storage) {
|
||||
assert.fail("Nothing should be written since `storage` was not changed.");
|
||||
};
|
||||
loader.unload();
|
||||
prefs.reset(QUOTA_PREF);
|
||||
done();
|
||||
@ -257,184 +251,6 @@ exports.testUninstall = function (assert, done) {
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
exports.testChangeInnerArray = function(assert, done) {
|
||||
prefs.set(WRITE_PERIOD_PREF, 10);
|
||||
|
||||
let expected = {
|
||||
x: [5, 7],
|
||||
y: [7, 28],
|
||||
z: [6, 2]
|
||||
};
|
||||
|
||||
// Load the module, set a value.
|
||||
let loader = Loader(module);
|
||||
let ss = loader.require("sdk/simple-storage");
|
||||
manager(loader).jsonStore.onWrite = function (storage) {
|
||||
assert.ok(file.exists(storeFilename), "Store file should exist");
|
||||
|
||||
// Load the module again and check the result
|
||||
loader = Loader(module);
|
||||
ss = loader.require("sdk/simple-storage");
|
||||
assert.equal(JSON.stringify(ss.storage),
|
||||
JSON.stringify(expected), "Should see the expected object");
|
||||
|
||||
// Add a property
|
||||
ss.storage.x.push(["bar"]);
|
||||
expected.x.push(["bar"]);
|
||||
manager(loader).jsonStore.onWrite = function (storage) {
|
||||
assert.equal(JSON.stringify(ss.storage),
|
||||
JSON.stringify(expected), "Should see the expected object");
|
||||
|
||||
// Modify a property
|
||||
ss.storage.y[0] = 42;
|
||||
expected.y[0] = 42;
|
||||
manager(loader).jsonStore.onWrite = function (storage) {
|
||||
assert.equal(JSON.stringify(ss.storage),
|
||||
JSON.stringify(expected), "Should see the expected object");
|
||||
|
||||
// Delete a property
|
||||
delete ss.storage.z[1];
|
||||
delete expected.z[1];
|
||||
manager(loader).jsonStore.onWrite = function (storage) {
|
||||
assert.equal(JSON.stringify(ss.storage),
|
||||
JSON.stringify(expected), "Should see the expected object");
|
||||
|
||||
// Modify the new inner-object
|
||||
ss.storage.x[2][0] = "baz";
|
||||
expected.x[2][0] = "baz";
|
||||
manager(loader).jsonStore.onWrite = function (storage) {
|
||||
assert.equal(JSON.stringify(ss.storage),
|
||||
JSON.stringify(expected), "Should see the expected object");
|
||||
|
||||
manager(loader).jsonStore.onWrite = function (storage) {
|
||||
assert.fail("Nothing should be written since `storage` was not changed.");
|
||||
};
|
||||
loader.unload();
|
||||
|
||||
// Load the module again and check the result
|
||||
loader = Loader(module);
|
||||
ss = loader.require("sdk/simple-storage");
|
||||
assert.equal(JSON.stringify(ss.storage),
|
||||
JSON.stringify(expected), "Should see the expected object");
|
||||
loader.unload();
|
||||
file.remove(storeFilename);
|
||||
prefs.reset(WRITE_PERIOD_PREF);
|
||||
done();
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ss.storage = {
|
||||
x: [5, 7],
|
||||
y: [7, 28],
|
||||
z: [6, 2]
|
||||
};
|
||||
assert.equal(JSON.stringify(ss.storage),
|
||||
JSON.stringify(expected), "Should see the expected object");
|
||||
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
exports.testChangeInnerObject = function(assert, done) {
|
||||
prefs.set(WRITE_PERIOD_PREF, 10);
|
||||
|
||||
let expected = {
|
||||
x: {
|
||||
a: 5,
|
||||
b: 7
|
||||
},
|
||||
y: {
|
||||
c: 7,
|
||||
d: 28
|
||||
},
|
||||
z: {
|
||||
e: 6,
|
||||
f: 2
|
||||
}
|
||||
};
|
||||
|
||||
// Load the module, set a value.
|
||||
let loader = Loader(module);
|
||||
let ss = loader.require("sdk/simple-storage");
|
||||
manager(loader).jsonStore.onWrite = function (storage) {
|
||||
assert.ok(file.exists(storeFilename), "Store file should exist");
|
||||
|
||||
// Load the module again and check the result
|
||||
loader = Loader(module);
|
||||
ss = loader.require("sdk/simple-storage");
|
||||
assert.equal(JSON.stringify(ss.storage),
|
||||
JSON.stringify(expected), "Should see the expected object");
|
||||
|
||||
// Add a property
|
||||
ss.storage.x.g = {foo: "bar"};
|
||||
expected.x.g = {foo: "bar"};
|
||||
manager(loader).jsonStore.onWrite = function (storage) {
|
||||
assert.equal(JSON.stringify(ss.storage),
|
||||
JSON.stringify(expected), "Should see the expected object");
|
||||
|
||||
// Modify a property
|
||||
ss.storage.y.c = 42;
|
||||
expected.y.c = 42;
|
||||
manager(loader).jsonStore.onWrite = function (storage) {
|
||||
assert.equal(JSON.stringify(ss.storage),
|
||||
JSON.stringify(expected), "Should see the expected object");
|
||||
|
||||
// Delete a property
|
||||
delete ss.storage.z.f;
|
||||
delete expected.z.f;
|
||||
manager(loader).jsonStore.onWrite = function (storage) {
|
||||
assert.equal(JSON.stringify(ss.storage),
|
||||
JSON.stringify(expected), "Should see the expected object");
|
||||
|
||||
// Modify the new inner-object
|
||||
ss.storage.x.g.foo = "baz";
|
||||
expected.x.g.foo = "baz";
|
||||
manager(loader).jsonStore.onWrite = function (storage) {
|
||||
assert.equal(JSON.stringify(ss.storage),
|
||||
JSON.stringify(expected), "Should see the expected object");
|
||||
|
||||
manager(loader).jsonStore.onWrite = function (storage) {
|
||||
assert.fail("Nothing should be written since `storage` was not changed.");
|
||||
};
|
||||
loader.unload();
|
||||
|
||||
// Load the module again and check the result
|
||||
loader = Loader(module);
|
||||
ss = loader.require("sdk/simple-storage");
|
||||
assert.equal(JSON.stringify(ss.storage),
|
||||
JSON.stringify(expected), "Should see the expected object");
|
||||
loader.unload();
|
||||
file.remove(storeFilename);
|
||||
prefs.reset(WRITE_PERIOD_PREF);
|
||||
done();
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ss.storage = {
|
||||
x: {
|
||||
a: 5,
|
||||
b: 7
|
||||
},
|
||||
y: {
|
||||
c: 7,
|
||||
d: 28
|
||||
},
|
||||
z: {
|
||||
e: 6,
|
||||
f: 2
|
||||
}
|
||||
};
|
||||
assert.equal(JSON.stringify(ss.storage),
|
||||
JSON.stringify(expected), "Should see the expected object");
|
||||
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
exports.testSetNoSetRead = function (assert, done) {
|
||||
// Load the module, set a value.
|
||||
let loader = Loader(module);
|
||||
@ -453,13 +269,12 @@ exports.testSetNoSetRead = function (assert, done) {
|
||||
// Load the module a third time and make sure the value stuck.
|
||||
loader = Loader(module);
|
||||
ss = loader.require("sdk/simple-storage");
|
||||
assert.equal(ss.storage.foo, val, "Value should persist");
|
||||
manager(loader).jsonStore.onWrite = function (storage) {
|
||||
assert.fail("Nothing should be written since `storage` was not changed.");
|
||||
file.remove(storeFilename);
|
||||
done();
|
||||
};
|
||||
assert.equal(ss.storage.foo, val, "Value should persist");
|
||||
loader.unload();
|
||||
file.remove(storeFilename);
|
||||
done();
|
||||
};
|
||||
let val = "foo";
|
||||
ss.storage.foo = val;
|
||||
@ -480,13 +295,12 @@ function setGetRoot(assert, done, val, compare) {
|
||||
// Load the module again and make sure the value stuck.
|
||||
loader = Loader(module);
|
||||
ss = loader.require("sdk/simple-storage");
|
||||
assert.ok(compare(ss.storage, val), "Value should persist");
|
||||
manager(loader).jsonStore.onWrite = function (storage) {
|
||||
assert.fail("Nothing should be written since `storage` was not changed.");
|
||||
manager(loader).jsonStore.onWrite = function () {
|
||||
file.remove(storeFilename);
|
||||
done();
|
||||
};
|
||||
assert.ok(compare(ss.storage, val), "Value should persist");
|
||||
loader.unload();
|
||||
file.remove(storeFilename);
|
||||
done();
|
||||
};
|
||||
ss.storage = val;
|
||||
assert.ok(compare(ss.storage, val), "Value read should be value set");
|
||||
|
@ -15,9 +15,9 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="fe92ddd450e03b38edb2d465de7897971d68ac68">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c7ef0bf06ce1c98cbe68aa52e2ecd862acb23e9c"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b81855b6b67f285d6f27a4f8c1cfe2e0387ea57c"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
|
@ -19,8 +19,8 @@
|
||||
<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="12d634c63ec8a41553a9f150c38260c992dc0020"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c7ef0bf06ce1c98cbe68aa52e2ecd862acb23e9c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b81855b6b67f285d6f27a4f8c1cfe2e0387ea57c"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
|
||||
|
@ -17,8 +17,8 @@
|
||||
</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="12d634c63ec8a41553a9f150c38260c992dc0020"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c7ef0bf06ce1c98cbe68aa52e2ecd862acb23e9c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b81855b6b67f285d6f27a4f8c1cfe2e0387ea57c"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f3e998242fb9a857cf50f5bf3a02304a530ea617"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
|
@ -15,9 +15,9 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="fe92ddd450e03b38edb2d465de7897971d68ac68">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c7ef0bf06ce1c98cbe68aa52e2ecd862acb23e9c"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b81855b6b67f285d6f27a4f8c1cfe2e0387ea57c"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
|
@ -19,8 +19,8 @@
|
||||
<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="12d634c63ec8a41553a9f150c38260c992dc0020"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c7ef0bf06ce1c98cbe68aa52e2ecd862acb23e9c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b81855b6b67f285d6f27a4f8c1cfe2e0387ea57c"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
|
||||
|
@ -15,9 +15,9 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="fe92ddd450e03b38edb2d465de7897971d68ac68">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c7ef0bf06ce1c98cbe68aa52e2ecd862acb23e9c"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b81855b6b67f285d6f27a4f8c1cfe2e0387ea57c"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
|
@ -17,8 +17,8 @@
|
||||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c7ef0bf06ce1c98cbe68aa52e2ecd862acb23e9c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b81855b6b67f285d6f27a4f8c1cfe2e0387ea57c"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f3e998242fb9a857cf50f5bf3a02304a530ea617"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
|
@ -4,6 +4,6 @@
|
||||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "02fabec9910191bf6f99cb9879a1e6603d5ed7c3",
|
||||
"revision": "b5f6c10e258311d9f04ed759291bbe6af10a772b",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -17,8 +17,8 @@
|
||||
<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="12d634c63ec8a41553a9f150c38260c992dc0020"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c7ef0bf06ce1c98cbe68aa52e2ecd862acb23e9c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b81855b6b67f285d6f27a4f8c1cfe2e0387ea57c"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
|
@ -15,8 +15,8 @@
|
||||
<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="12d634c63ec8a41553a9f150c38260c992dc0020"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c7ef0bf06ce1c98cbe68aa52e2ecd862acb23e9c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b81855b6b67f285d6f27a4f8c1cfe2e0387ea57c"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
|
@ -17,8 +17,8 @@
|
||||
</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="12d634c63ec8a41553a9f150c38260c992dc0020"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c7ef0bf06ce1c98cbe68aa52e2ecd862acb23e9c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b81855b6b67f285d6f27a4f8c1cfe2e0387ea57c"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f3e998242fb9a857cf50f5bf3a02304a530ea617"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
|
@ -17,8 +17,8 @@
|
||||
<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="12d634c63ec8a41553a9f150c38260c992dc0020"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c7ef0bf06ce1c98cbe68aa52e2ecd862acb23e9c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b81855b6b67f285d6f27a4f8c1cfe2e0387ea57c"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
|
@ -97,6 +97,15 @@ const injectObjectAPI = function(api, targetWindow) {
|
||||
return contentObj;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the two-digit hexadecimal code for a byte
|
||||
*
|
||||
* @param {byte} charCode
|
||||
*/
|
||||
const toHexString = function(charCode) {
|
||||
return ("0" + charCode.toString(16)).slice(-2);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject the loop API into the given window. The caller must be sure the
|
||||
* window is a loop content window (eg, a panel, chatwindow, or similar).
|
||||
@ -510,6 +519,42 @@ function injectLoopAPI(targetWindow) {
|
||||
Services.telemetry.getHistogramById(histogramId).add(value);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Compose a URL pointing to the location of an avatar by email address.
|
||||
* At the moment we use the Gravatar service to match email addresses with
|
||||
* avatars. This might change in the future as avatars might come from another
|
||||
* source.
|
||||
*
|
||||
* @param {String} emailAddress Users' email address
|
||||
* @param {Number} size Size of the avatar image to return in pixels.
|
||||
* Optional. Default value: 40.
|
||||
* @return the URL pointing to an avatar matching the provided email address.
|
||||
*/
|
||||
getUserAvatar: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: function(emailAddress, size = 40) {
|
||||
if (!emailAddress) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Do the MD5 dance.
|
||||
let hasher = Cc["@mozilla.org/security/hash;1"]
|
||||
.createInstance(Ci.nsICryptoHash);
|
||||
hasher.init(Ci.nsICryptoHash.MD5);
|
||||
let stringStream = Cc["@mozilla.org/io/string-input-stream;1"]
|
||||
.createInstance(Ci.nsIStringInputStream);
|
||||
stringStream.data = emailAddress.trim().toLowerCase();
|
||||
hasher.updateFromStream(stringStream, -1);
|
||||
let hash = hasher.finish(false);
|
||||
// Convert the binary hash data to a hex string.
|
||||
let md5Email = [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
|
||||
|
||||
// Compose the Gravatar URL.
|
||||
return "http://www.gravatar.com/avatar/" + md5Email + ".jpg?default=blank&s=" + size;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function onStatusChanged(aSubject, aTopic, aData) {
|
||||
|
195
browser/components/loop/content/js/contacts.js
Normal file
195
browser/components/loop/content/js/contacts.js
Normal file
@ -0,0 +1,195 @@
|
||||
/** @jsx React.DOM */
|
||||
|
||||
/* 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/. */
|
||||
|
||||
/*jshint newcap:false*/
|
||||
/*global loop:true, React */
|
||||
|
||||
var loop = loop || {};
|
||||
loop.contacts = (function(_, mozL10n) {
|
||||
"use strict";
|
||||
|
||||
// Number of contacts to add to the list at the same time.
|
||||
const CONTACTS_CHUNK_SIZE = 100;
|
||||
|
||||
const ContactDetail = React.createClass({displayName: 'ContactDetail',
|
||||
propTypes: {
|
||||
handleContactClick: React.PropTypes.func,
|
||||
contact: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
handleContactClick: function() {
|
||||
if (this.props.handleContactClick) {
|
||||
this.props.handleContactClick(this.props.key);
|
||||
}
|
||||
},
|
||||
|
||||
getContactNames: function() {
|
||||
// The model currently does not enforce a name to be present, but we're
|
||||
// going to assume it is awaiting more advanced validation of required fields
|
||||
// by the model. (See bug 1069918)
|
||||
// NOTE: this method of finding a firstname and lastname is not i18n-proof.
|
||||
let names = this.props.contact.name[0].split(" ");
|
||||
return {
|
||||
firstName: names.shift(),
|
||||
lastName: names.join(" ")
|
||||
};
|
||||
},
|
||||
|
||||
getPreferredEmail: function() {
|
||||
// The model currently does not enforce a name to be present, but we're
|
||||
// going to assume it is awaiting more advanced validation of required fields
|
||||
// by the model. (See bug 1069918)
|
||||
let email = this.props.contact.email[0];
|
||||
this.props.contact.email.some(function(address) {
|
||||
if (address.pref) {
|
||||
email = address;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return email;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
let names = this.getContactNames();
|
||||
let email = this.getPreferredEmail();
|
||||
let cx = React.addons.classSet;
|
||||
let contactCSSClass = cx({
|
||||
contact: true,
|
||||
blocked: this.props.contact.blocked
|
||||
});
|
||||
|
||||
return (
|
||||
React.DOM.li({onClick: this.handleContactClick, className: contactCSSClass},
|
||||
React.DOM.div({className: "avatar"},
|
||||
React.DOM.img({src: navigator.mozLoop.getUserAvatar(email.value)})
|
||||
),
|
||||
React.DOM.div({className: "details"},
|
||||
React.DOM.div({className: "username"}, React.DOM.strong(null, names.firstName), " ", names.lastName,
|
||||
React.DOM.i({className: cx({"icon icon-google": this.props.contact.category[0] == "google"})}),
|
||||
React.DOM.i({className: cx({"icon icon-blocked": this.props.contact.blocked})})
|
||||
),
|
||||
React.DOM.div({className: "email"}, email.value)
|
||||
),
|
||||
React.DOM.div({className: "icons"},
|
||||
React.DOM.i({className: "icon icon-video"}),
|
||||
React.DOM.i({className: "icon icon-caret-down"})
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const ContactsList = React.createClass({displayName: 'ContactsList',
|
||||
getInitialState: function() {
|
||||
return {
|
||||
contacts: {}
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
let contactsAPI = navigator.mozLoop.contacts;
|
||||
|
||||
contactsAPI.getAll((err, contacts) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Add contacts already present in the DB. We do this in timed chunks to
|
||||
// circumvent blocking the main event loop.
|
||||
let addContactsInChunks = () => {
|
||||
contacts.splice(0, CONTACTS_CHUNK_SIZE).forEach(contact => {
|
||||
this.handleContactAddOrUpdate(contact);
|
||||
});
|
||||
if (contacts.length) {
|
||||
setTimeout(addContactsInChunks, 0);
|
||||
}
|
||||
};
|
||||
|
||||
addContactsInChunks(contacts);
|
||||
|
||||
// Listen for contact changes/ updates.
|
||||
contactsAPI.on("add", (eventName, contact) => {
|
||||
this.handleContactAddOrUpdate(contact);
|
||||
});
|
||||
contactsAPI.on("remove", (eventName, contact) => {
|
||||
this.handleContactRemove(contact);
|
||||
});
|
||||
contactsAPI.on("removeAll", () => {
|
||||
this.handleContactRemoveAll();
|
||||
});
|
||||
contactsAPI.on("update", (eventName, contact) => {
|
||||
this.handleContactAddOrUpdate(contact);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
handleContactAddOrUpdate: function(contact) {
|
||||
let contacts = this.state.contacts;
|
||||
let guid = String(contact._guid);
|
||||
contacts[guid] = contact;
|
||||
this.setState({});
|
||||
},
|
||||
|
||||
handleContactRemove: function(contact) {
|
||||
let contacts = this.state.contacts;
|
||||
let guid = String(contact._guid);
|
||||
if (!contacts[guid]) {
|
||||
return;
|
||||
}
|
||||
delete contacts[guid];
|
||||
this.setState({});
|
||||
},
|
||||
|
||||
handleContactRemoveAll: function() {
|
||||
this.setState({contacts: {}});
|
||||
},
|
||||
|
||||
sortContacts: function(contact1, contact2) {
|
||||
let comp = contact1.name[0].localeCompare(contact2.name[0]);
|
||||
if (comp !== 0) {
|
||||
return comp;
|
||||
}
|
||||
// If names are equal, compare against unique ids to make sure we have
|
||||
// consistent ordering.
|
||||
return contact1._guid - contact2._guid;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
let viewForItem = item => {
|
||||
return ContactDetail({key: item._guid, contact: item})
|
||||
};
|
||||
|
||||
let shownContacts = _.groupBy(this.state.contacts, function(contact) {
|
||||
return contact.blocked ? "blocked" : "available";
|
||||
});
|
||||
|
||||
return (
|
||||
React.DOM.div({className: "listWrapper"},
|
||||
React.DOM.div({ref: "listSlider", className: "listPanels"},
|
||||
React.DOM.div({className: "faded"},
|
||||
React.DOM.ul(null,
|
||||
shownContacts.available ?
|
||||
shownContacts.available.sort(this.sortContacts).map(viewForItem) :
|
||||
null,
|
||||
shownContacts.blocked ?
|
||||
React.DOM.h3({className: "header"}, mozL10n.get("contacts_blocked_contacts")) :
|
||||
null,
|
||||
shownContacts.blocked ?
|
||||
shownContacts.blocked.sort(this.sortContacts).map(viewForItem) :
|
||||
null
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
ContactsList: ContactsList
|
||||
};
|
||||
})(_, document.mozL10n);
|
195
browser/components/loop/content/js/contacts.jsx
Normal file
195
browser/components/loop/content/js/contacts.jsx
Normal file
@ -0,0 +1,195 @@
|
||||
/** @jsx React.DOM */
|
||||
|
||||
/* 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/. */
|
||||
|
||||
/*jshint newcap:false*/
|
||||
/*global loop:true, React */
|
||||
|
||||
var loop = loop || {};
|
||||
loop.contacts = (function(_, mozL10n) {
|
||||
"use strict";
|
||||
|
||||
// Number of contacts to add to the list at the same time.
|
||||
const CONTACTS_CHUNK_SIZE = 100;
|
||||
|
||||
const ContactDetail = React.createClass({
|
||||
propTypes: {
|
||||
handleContactClick: React.PropTypes.func,
|
||||
contact: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
handleContactClick: function() {
|
||||
if (this.props.handleContactClick) {
|
||||
this.props.handleContactClick(this.props.key);
|
||||
}
|
||||
},
|
||||
|
||||
getContactNames: function() {
|
||||
// The model currently does not enforce a name to be present, but we're
|
||||
// going to assume it is awaiting more advanced validation of required fields
|
||||
// by the model. (See bug 1069918)
|
||||
// NOTE: this method of finding a firstname and lastname is not i18n-proof.
|
||||
let names = this.props.contact.name[0].split(" ");
|
||||
return {
|
||||
firstName: names.shift(),
|
||||
lastName: names.join(" ")
|
||||
};
|
||||
},
|
||||
|
||||
getPreferredEmail: function() {
|
||||
// The model currently does not enforce a name to be present, but we're
|
||||
// going to assume it is awaiting more advanced validation of required fields
|
||||
// by the model. (See bug 1069918)
|
||||
let email = this.props.contact.email[0];
|
||||
this.props.contact.email.some(function(address) {
|
||||
if (address.pref) {
|
||||
email = address;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return email;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
let names = this.getContactNames();
|
||||
let email = this.getPreferredEmail();
|
||||
let cx = React.addons.classSet;
|
||||
let contactCSSClass = cx({
|
||||
contact: true,
|
||||
blocked: this.props.contact.blocked
|
||||
});
|
||||
|
||||
return (
|
||||
<li onClick={this.handleContactClick} className={contactCSSClass}>
|
||||
<div className="avatar">
|
||||
<img src={navigator.mozLoop.getUserAvatar(email.value)} />
|
||||
</div>
|
||||
<div className="details">
|
||||
<div className="username"><strong>{names.firstName}</strong> {names.lastName}
|
||||
<i className={cx({"icon icon-google": this.props.contact.category[0] == "google"})} />
|
||||
<i className={cx({"icon icon-blocked": this.props.contact.blocked})} />
|
||||
</div>
|
||||
<div className="email">{email.value}</div>
|
||||
</div>
|
||||
<div className="icons">
|
||||
<i className="icon icon-video" />
|
||||
<i className="icon icon-caret-down" />
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const ContactsList = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
contacts: {}
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
let contactsAPI = navigator.mozLoop.contacts;
|
||||
|
||||
contactsAPI.getAll((err, contacts) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Add contacts already present in the DB. We do this in timed chunks to
|
||||
// circumvent blocking the main event loop.
|
||||
let addContactsInChunks = () => {
|
||||
contacts.splice(0, CONTACTS_CHUNK_SIZE).forEach(contact => {
|
||||
this.handleContactAddOrUpdate(contact);
|
||||
});
|
||||
if (contacts.length) {
|
||||
setTimeout(addContactsInChunks, 0);
|
||||
}
|
||||
};
|
||||
|
||||
addContactsInChunks(contacts);
|
||||
|
||||
// Listen for contact changes/ updates.
|
||||
contactsAPI.on("add", (eventName, contact) => {
|
||||
this.handleContactAddOrUpdate(contact);
|
||||
});
|
||||
contactsAPI.on("remove", (eventName, contact) => {
|
||||
this.handleContactRemove(contact);
|
||||
});
|
||||
contactsAPI.on("removeAll", () => {
|
||||
this.handleContactRemoveAll();
|
||||
});
|
||||
contactsAPI.on("update", (eventName, contact) => {
|
||||
this.handleContactAddOrUpdate(contact);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
handleContactAddOrUpdate: function(contact) {
|
||||
let contacts = this.state.contacts;
|
||||
let guid = String(contact._guid);
|
||||
contacts[guid] = contact;
|
||||
this.setState({});
|
||||
},
|
||||
|
||||
handleContactRemove: function(contact) {
|
||||
let contacts = this.state.contacts;
|
||||
let guid = String(contact._guid);
|
||||
if (!contacts[guid]) {
|
||||
return;
|
||||
}
|
||||
delete contacts[guid];
|
||||
this.setState({});
|
||||
},
|
||||
|
||||
handleContactRemoveAll: function() {
|
||||
this.setState({contacts: {}});
|
||||
},
|
||||
|
||||
sortContacts: function(contact1, contact2) {
|
||||
let comp = contact1.name[0].localeCompare(contact2.name[0]);
|
||||
if (comp !== 0) {
|
||||
return comp;
|
||||
}
|
||||
// If names are equal, compare against unique ids to make sure we have
|
||||
// consistent ordering.
|
||||
return contact1._guid - contact2._guid;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
let viewForItem = item => {
|
||||
return <ContactDetail key={item._guid} contact={item} />
|
||||
};
|
||||
|
||||
let shownContacts = _.groupBy(this.state.contacts, function(contact) {
|
||||
return contact.blocked ? "blocked" : "available";
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="listWrapper">
|
||||
<div ref="listSlider" className="listPanels">
|
||||
<div className="faded">
|
||||
<ul>
|
||||
{shownContacts.available ?
|
||||
shownContacts.available.sort(this.sortContacts).map(viewForItem) :
|
||||
null}
|
||||
{shownContacts.blocked ?
|
||||
<h3 className="header">{mozL10n.get("contacts_blocked_contacts")}</h3> :
|
||||
null}
|
||||
{shownContacts.blocked ?
|
||||
shownContacts.blocked.sort(this.sortContacts).map(viewForItem) :
|
||||
null}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
ContactsList: ContactsList
|
||||
};
|
||||
})(_, document.mozL10n);
|
@ -14,6 +14,7 @@ loop.panel = (function(_, mozL10n) {
|
||||
var sharedViews = loop.shared.views;
|
||||
var sharedModels = loop.shared.models;
|
||||
var sharedMixins = loop.shared.mixins;
|
||||
var ContactsList = loop.contacts.ContactsList;
|
||||
var __ = mozL10n.get; // aliasing translation function as __ for concision
|
||||
|
||||
/**
|
||||
@ -498,7 +499,7 @@ loop.panel = (function(_, mozL10n) {
|
||||
ToSView(null)
|
||||
),
|
||||
Tab({name: "contacts"},
|
||||
React.DOM.span(null, "contacts")
|
||||
ContactsList(null)
|
||||
)
|
||||
),
|
||||
React.DOM.div({className: "footer"},
|
||||
|
@ -14,6 +14,7 @@ loop.panel = (function(_, mozL10n) {
|
||||
var sharedViews = loop.shared.views;
|
||||
var sharedModels = loop.shared.models;
|
||||
var sharedMixins = loop.shared.mixins;
|
||||
var ContactsList = loop.contacts.ContactsList;
|
||||
var __ = mozL10n.get; // aliasing translation function as __ for concision
|
||||
|
||||
/**
|
||||
@ -498,7 +499,7 @@ loop.panel = (function(_, mozL10n) {
|
||||
<ToSView />
|
||||
</Tab>
|
||||
<Tab name="contacts">
|
||||
<span>contacts</span>
|
||||
<ContactsList />
|
||||
</Tab>
|
||||
</TabView>
|
||||
<div className="footer">
|
||||
|
@ -9,6 +9,7 @@
|
||||
<link rel="stylesheet" type="text/css" href="loop/shared/css/reset.css">
|
||||
<link rel="stylesheet" type="text/css" href="loop/shared/css/common.css">
|
||||
<link rel="stylesheet" type="text/css" href="loop/shared/css/panel.css">
|
||||
<link rel="stylesheet" type="text/css" href="loop/shared/css/contacts.css">
|
||||
</head>
|
||||
<body class="panel">
|
||||
|
||||
@ -25,6 +26,7 @@
|
||||
<script type="text/javascript" src="loop/shared/js/mixins.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/views.js"></script>
|
||||
<script type="text/javascript" src="loop/js/client.js"></script>
|
||||
<script type="text/javascript;version=1.8" src="loop/js/contacts.js"></script>
|
||||
<script type="text/javascript" src="loop/js/panel.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -395,3 +395,15 @@ p {
|
||||
background: transparent url(../img/firefox-logo.png) no-repeat center center;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 5px 10px;
|
||||
color: #888;
|
||||
margin: 0;
|
||||
border-top: 1px solid #CCC;
|
||||
background: #EEE;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
height: 24px;
|
||||
}
|
||||
|
163
browser/components/loop/content/shared/css/contacts.css
Normal file
163
browser/components/loop/content/shared/css/contacts.css
Normal file
@ -0,0 +1,163 @@
|
||||
/* 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/. */
|
||||
|
||||
.contact {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
position: relative;
|
||||
padding: 5px 10px;
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.contact:not(:first-child) {
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.contact.blocked > .details > .username {
|
||||
color: #d74345;
|
||||
}
|
||||
|
||||
.contact:hover {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
.contact.selected {
|
||||
background: #ebebeb;
|
||||
}
|
||||
|
||||
.contact:hover > .icons {
|
||||
display: block;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.contact > .avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: #ccc;
|
||||
border-radius: 50%;
|
||||
margin-right: 10px;
|
||||
overflow: hidden;
|
||||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.3);
|
||||
background-image: url("../img/audio-call-avatar.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-color: #4ba6e7;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.contact > .avatar > img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.contact > .details > .username {
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
color: #222;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.contact > .details > .username > strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.contact > .details > .username > i.icon-blocked {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 20px;
|
||||
-moz-margin-start: 3px;
|
||||
background-image: url("../img/icons-16x16.svg#block-red");
|
||||
background-position: center;
|
||||
background-size: 10px 10px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.contact > .details > .username > i.icon-google {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 35%;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
background-image: url("../img/icons-16x16.svg#google");
|
||||
background-position: center;
|
||||
background-size: 16px 16px;
|
||||
background-repeat: no-repeat;
|
||||
background-color: fff;
|
||||
}
|
||||
|
||||
.contact > .details > .email {
|
||||
color: #999;
|
||||
font-size: 11px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.listWrapper {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
/* Show six contacts and scroll for the rest */
|
||||
max-height: 305px;
|
||||
}
|
||||
|
||||
.listPanels {
|
||||
display: flex;
|
||||
width: 200%;
|
||||
flex-direction: row;
|
||||
transition: 200ms ease-in;
|
||||
transition-property: transform;
|
||||
}
|
||||
|
||||
.listPanels > div {
|
||||
flex: 0 0 50%;
|
||||
}
|
||||
|
||||
.list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.list.faded {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.list h3 {
|
||||
margin: 0;
|
||||
border-bottom: none;
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.icons {
|
||||
cursor: pointer;
|
||||
display: none;
|
||||
margin-left: auto;
|
||||
padding: 12px 10px;
|
||||
border-radius: 30px;
|
||||
background: #7ed321;
|
||||
}
|
||||
|
||||
.icons:hover {
|
||||
background: #89e029;
|
||||
}
|
||||
|
||||
.icons i {
|
||||
margin: 0 5px;
|
||||
display: inline-block;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.icons i.icon-video {
|
||||
background-image: url("../img/icons-14x14.svg#video-white");
|
||||
background-size: 14px 14px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.icons i.icon-caret-down {
|
||||
background-image: url("../img/icons-10x10.svg#dropdown-white");
|
||||
background-size: 10px 10px;
|
||||
width: 10px;
|
||||
height: 16px;
|
||||
}
|
@ -39,6 +39,7 @@
|
||||
padding: 0 10px;
|
||||
height: 16px;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 16px 16px;
|
||||
background-position: center;
|
||||
@ -82,6 +83,7 @@
|
||||
|
||||
.share {
|
||||
background: #fbfbfb;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.share .description,
|
||||
@ -313,5 +315,4 @@ body[dir=rtl] .dropdown-menu-item {
|
||||
background: #EAEAEA;
|
||||
color: #7F7F7F;
|
||||
padding: 14px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
53
browser/components/loop/content/shared/img/icons-10x10.svg
Normal file
53
browser/components/loop/content/shared/img/icons-10x10.svg
Normal file
@ -0,0 +1,53 @@
|
||||
<?xml version="1.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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px" y="0px"
|
||||
viewBox="0 0 10 10"
|
||||
enable-background="new 0 0 10 10"
|
||||
xml:space="preserve">
|
||||
<style>
|
||||
use:not(:target) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
use {
|
||||
fill: #ccc;
|
||||
}
|
||||
|
||||
use[id$="-hover"] {
|
||||
fill: #444;
|
||||
}
|
||||
|
||||
use[id$="-active"] {
|
||||
fill: #0095dd;
|
||||
}
|
||||
|
||||
use[id$="-white"] {
|
||||
fill: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
</style>
|
||||
<defs style="display:none">
|
||||
<polygon id="close-shape" fill-rule="evenodd" clip-rule="evenodd" points="10,1.717 8.336,0.049 5.024,3.369 1.663,0 0,1.668
|
||||
3.36,5.037 0.098,8.307 1.762,9.975 5.025,6.705 8.311,10 9.975,8.332 6.688,5.037"/>
|
||||
<path id="dropdown-shape" fill-rule="evenodd" clip-rule="evenodd" d="M9,3L4.984,7L1,3H9z"/>
|
||||
<polygon id="expand-shape" fill-rule="evenodd" clip-rule="evenodd" points="10,0 4.838,0 6.506,1.669 0,8.175 1.825,10 8.331,3.494
|
||||
10,5.162"/>
|
||||
<rect id="minimize-shape" y="3.6" fill-rule="evenodd" clip-rule="evenodd" width="10" height="2.8"/>
|
||||
</defs>
|
||||
<use id="close" xlink:href="#close-shape"/>
|
||||
<use id="close-active" xlink:href="#close-shape"/>
|
||||
<use id="close-disabled" xlink:href="#close-shape"/>
|
||||
<use id="dropdown" xlink:href="#dropdown-shape"/>
|
||||
<use id="dropdown-white" xlink:href="#dropdown-shape"/>
|
||||
<use id="dropdown-active" xlink:href="#dropdown-shape"/>
|
||||
<use id="dropdown-disabled" xlink:href="#dropdown-shape"/>
|
||||
<use id="expand" xlink:href="#expand-shape"/>
|
||||
<use id="expand-active" xlink:href="#expand-shape"/>
|
||||
<use id="expand-disabled" xlink:href="#expand-shape"/>
|
||||
<use id="minimize" xlink:href="#minimize-shape"/>
|
||||
<use id="minimize-active" xlink:href="#minimize-shape"/>
|
||||
<use id="minimize-disabled" xlink:href="#minimize-shape"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
134
browser/components/loop/content/shared/img/icons-14x14.svg
Normal file
134
browser/components/loop/content/shared/img/icons-14x14.svg
Normal file
@ -0,0 +1,134 @@
|
||||
<?xml version="1.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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px" y="0px"
|
||||
viewBox="0 0 14 14"
|
||||
enable-background="new 0 0 14 14"
|
||||
xml:space="preserve">
|
||||
<style>
|
||||
use:not(:target) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
use {
|
||||
fill: #ccc;
|
||||
}
|
||||
|
||||
use[id$="-hover"] {
|
||||
fill: #444;
|
||||
}
|
||||
|
||||
use[id$="-active"] {
|
||||
fill: #0095dd;
|
||||
}
|
||||
|
||||
use[id$="-white"] {
|
||||
fill: #fff;
|
||||
}
|
||||
</style>
|
||||
<defs style="display:none">
|
||||
<path id="audio-shape" fill-rule="evenodd" clip-rule="evenodd" d="M9.571,6.143v1.714c0,1.42-1.151,2.571-2.571,2.571
|
||||
c-1.42,0-2.571-1.151-2.571-2.571V6.143H3.571v1.714c0,1.597,1.093,2.935,2.571,3.316v0.97H5.714c-0.56,0-1.034,0.358-1.211,0.857
|
||||
h4.993c-0.177-0.499-0.651-0.857-1.211-0.857H7.857v-0.97c1.478-0.381,2.571-1.719,2.571-3.316V6.143H9.571z M7,10
|
||||
c1.183,0,2.143-0.959,2.143-2.143V3.143C9.143,1.959,8.183,1,7,1C5.817,1,4.857,1.959,4.857,3.143v4.714C4.857,9.041,5.817,10,7,10
|
||||
z"/>
|
||||
<g id="facemute-shape">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.174,3.551L9.568,5.856V5.847L3.39,11.49h5.066
|
||||
c0.613,0,1.111-0.533,1.111-1.19V8.526l2.606,2.304C12.4,11.071,12.71,11.142,13,11.078V3.302C12.71,3.239,12.4,3.309,12.174,3.551
|
||||
z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.395,2.617l-0.001-0.001l-0.809-0.884l-2.102,1.92
|
||||
C9.316,3.221,8.919,2.918,8.457,2.918H2.111C1.498,2.918,1,3.451,1,4.109v6.191c0,0.318,0.118,0.607,0.306,0.821l-0.288,0.263
|
||||
l0.809,0.884l0.001,0.001l0.853-0.779l6.887-6.29L12.395,2.617z"/>
|
||||
</g>
|
||||
<path id="hangup-shape" fill-rule="evenodd" clip-rule="evenodd" d="M13,11.732c-0.602,0.52-1.254,0.946-1.941,1.267
|
||||
c-1.825-0.337-4.164-1.695-6.264-3.795C2.696,7.106,1.339,4.769,1,2.945c0.321-0.688,0.748-1.341,1.268-1.944l2.528,2.855
|
||||
C4.579,4.153,4.377,4.454,4.209,4.759L4.22,4.77C3.924,5.42,4.608,6.833,5.889,8.114c1.281,1.28,2.694,1.965,3.343,1.669
|
||||
l0.011,0.011c0.305-0.168,0.606-0.37,0.904-0.587L13,11.732z"/>
|
||||
<path id="incoming-shape" fill-rule="evenodd" clip-rule="evenodd" d="M2.745,7.558l0.637,0.669c0.04,0.041,0.085,0.073,0.134,0.1
|
||||
l3.249,3.313c0.38,0.393,0.915,0.478,1.197,0.186l0.638-0.676c0.281-0.292,0.2-0.848-0.18-1.244L7.097,8.558h3.566
|
||||
c0.419,0,0.759-0.34,0.759-0.759V6.28c0-0.419-0.34-0.759-0.759-0.759H7.059l1.42-1.443c0.381-0.392,0.461-0.945,0.18-1.234
|
||||
l-0.637-0.67C7.74,1.883,7.204,1.966,6.824,2.359L3.55,5.688C3.487,5.717,3.43,5.755,3.381,5.806L2.745,6.482
|
||||
c-0.131,0.137-0.183,0.332-0.162,0.54C2.562,7.229,2.613,7.423,2.745,7.558z"/>
|
||||
<path id="link-shape" fill-rule="evenodd" clip-rule="evenodd" d="M7.359,6.107c0.757-0.757,0.757-1.995,0-2.752
|
||||
L5.573,1.568c-0.757-0.757-1.995-0.757-2.752,0L1.568,2.82c-0.757,0.757-0.757,1.995,0,2.752l1.787,1.787
|
||||
c0.757,0.757,1.995,0.757,2.752,0L6.266,7.2L6.8,7.734L6.641,7.893c-0.757,0.757-0.757,1.995,0,2.752l1.787,1.787
|
||||
c0.757,0.757,1.995,0.757,2.752,0l1.253-1.253c0.757-0.757,0.757-1.995,0-2.752l-1.787-1.787c-0.757-0.757-1.995-0.757-2.752,0
|
||||
L7.734,6.8L7.2,6.266L7.359,6.107z M9.87,7.868l1.335,1.335c0.294,0.294,0.294,0.774,0,1.068l-0.934,0.934
|
||||
c-0.294,0.294-0.774,0.294-1.068,0L7.868,9.87c-0.294-0.294-0.294-0.774,0-1.068L8.13,9.064c0.294,0.294,0.744,0.324,1.001,0.067
|
||||
C9.388,8.874,9.358,8.424,9.064,8.13L8.802,7.868C9.096,7.574,9.577,7.574,9.87,7.868z M4.13,6.132L2.795,4.797
|
||||
c-0.294-0.294-0.294-0.774,0-1.068l0.934-0.934c0.294-0.294,0.774-0.294,1.068,0L6.132,4.13c0.294,0.294,0.294,0.774,0,1.068
|
||||
L5.86,4.926C5.567,4.632,5.116,4.602,4.859,4.859C4.602,5.116,4.632,5.567,4.926,5.86l0.272,0.272
|
||||
C4.904,6.426,4.423,6.426,4.13,6.132z"/>
|
||||
<g id="mute-shape">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.186,9.492L5.49,9.188l3.822-3.822l2.354-2.354l-0.848-0.848
|
||||
L9.312,3.669V3.142C9.312,1.959,8.352,1,7.169,1C5.986,1,5.026,1.959,5.026,3.142v4.715c0,0.032,0.001,0.064,0.002,0.096
|
||||
L4.643,8.338c-0.03-0.156-0.046-0.317-0.046-0.481V6.142H3.741v1.715c0,0.414,0.073,0.81,0.208,1.176l-1.615,1.615l0.848,0.848
|
||||
l1.398-1.398v0L5.186,9.492z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.312,7.857V6.045L5.829,9.528C6.196,9.824,6.662,10,7.169,10
|
||||
C8.352,10,9.312,9.04,9.312,7.857z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.741,7.857c0,1.42-1.151,2.572-2.572,2.572
|
||||
c-0.625,0-1.199-0.223-1.645-0.595l-0.605,0.605c0.395,0.344,0.87,0.599,1.393,0.734v0.97H5.884c-0.56,0-1.034,0.359-1.212,0.858
|
||||
h4.994c-0.178-0.499-0.652-0.858-1.212-0.858H8.026v-0.97c1.478-0.38,2.572-1.718,2.572-3.316V6.142H9.741V7.857z"/>
|
||||
</g>
|
||||
<path id="pause-shape" fill-rule="evenodd" clip-rule="evenodd" d="M4.75,1h-1.5C2.836,1,2.5,1.336,2.5,1.75v10.5
|
||||
C2.5,12.664,2.836,13,3.25,13h1.5c0.414,0,0.75-0.336,0.75-0.75V1.75C5.5,1.336,5.164,1,4.75,1z M10.75,1h-1.5
|
||||
C8.836,1,8.5,1.336,8.5,1.75v10.5C8.5,12.664,8.836,13,9.25,13h1.5c0.414,0,0.75-0.336,0.75-0.75V1.75C11.5,1.336,11.164,1,10.75,1
|
||||
z"/>
|
||||
<path id="video-shape" fill-rule="evenodd" clip-rule="evenodd" d="M12.175,3.347L9.568,5.651V3.905c0-0.657-0.497-1.19-1.111-1.19
|
||||
H2.111C1.498,2.714,1,3.247,1,3.905v6.191c0,0.658,0.498,1.19,1.111,1.19h6.345c0.614,0,1.111-0.533,1.111-1.19V8.322l2.607,2.305
|
||||
C12.4,10.867,12.71,10.938,13,10.874V3.099C12.71,3.035,12.4,3.106,12.175,3.347z"/>
|
||||
<g id="volume-shape">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.513,4.404H1.896c-0.417,0-0.756,0.338-0.756,0.755v3.679
|
||||
c0,0.417,0.338,0.755,0.756,0.755H3.51l2.575,2.575c0.261,0.261,0.596,0.4,0.938,0.422V1.409C6.682,1.431,6.346,1.57,6.085,1.831
|
||||
L3.513,4.404z M8.555,5.995C8.619,6.32,8.653,6.656,8.653,7c0,0.344-0.034,0.679-0.098,1.004l0.218,0.142
|
||||
C8.852,7.777,8.895,7.393,8.895,7c0-0.394-0.043-0.777-0.123-1.147L8.555,5.995z M12.224,3.6l-0.475,0.31
|
||||
c0.359,0.962,0.557,2.003,0.557,3.09c0,1.087-0.198,2.128-0.557,3.09l0.475,0.31c0.41-1.054,0.635-2.201,0.635-3.4
|
||||
C12.859,5.8,12.634,4.654,12.224,3.6z M10.061,5.012C10.25,5.642,10.353,6.308,10.353,7c0,0.691-0.103,1.358-0.293,1.987
|
||||
l0.351,0.229C10.634,8.517,10.756,7.772,10.756,7c0-0.773-0.121-1.517-0.345-2.216L10.061,5.012z"/>
|
||||
<path d="M7.164,12.74l-0.15-0.009c-0.389-0.024-0.754-0.189-1.028-0.463L3.452,9.735H1.896
|
||||
C1.402,9.735,1,9.333,1,8.838V5.16c0-0.494,0.402-0.896,0.896-0.896h1.558l2.531-2.531C6.26,1.458,6.625,1.293,7.014,1.269
|
||||
l0.15-0.009V12.74z M1.896,4.545c-0.339,0-0.615,0.276-0.615,0.615v3.679c0,0.339,0.276,0.615,0.615,0.615h1.672l2.616,2.616
|
||||
c0.19,0.19,0.434,0.316,0.697,0.363V1.568C6.619,1.615,6.375,1.741,6.185,1.931L3.571,4.545H1.896z M12.292,10.612l-0.714-0.467
|
||||
l0.039-0.105C11.981,9.067,12.165,8.044,12.165,7c0-1.044-0.184-2.067-0.548-3.041l-0.039-0.105l0.714-0.467l0.063,0.162
|
||||
C12.783,4.649,13,5.81,13,7s-0.217,2.351-0.645,3.451L12.292,10.612z M11.92,10.033l0.234,0.153
|
||||
c0.374-1.019,0.564-2.09,0.564-3.186s-0.19-2.167-0.564-3.186L11.92,3.966C12.27,4.94,12.447,5.96,12.447,7
|
||||
C12.447,8.04,12.27,9.059,11.92,10.033z M10.489,9.435L9.895,9.047l0.031-0.101C10.116,8.315,10.212,7.66,10.212,7
|
||||
c0-0.661-0.096-1.316-0.287-1.947L9.895,4.952l0.594-0.388l0.056,0.176C10.779,5.471,10.897,6.231,10.897,7
|
||||
c0,0.769-0.118,1.529-0.351,2.259L10.489,9.435z M10.225,8.926l0.106,0.069C10.52,8.348,10.615,7.677,10.615,7
|
||||
c0-0.677-0.095-1.348-0.284-1.996l-0.106,0.07C10.403,5.699,10.494,6.347,10.494,7C10.494,7.652,10.403,8.3,10.225,8.926z
|
||||
M8.867,8.376L8.398,8.07l0.018-0.093C8.48,7.654,8.512,7.325,8.512,7S8.48,6.345,8.417,6.022L8.398,5.929l0.469-0.306l0.043,0.2
|
||||
C8.994,6.211,9.036,6.607,9.036,7c0,0.393-0.042,0.789-0.126,1.176L8.867,8.376z"/>
|
||||
</g>
|
||||
</defs>
|
||||
<use id="audio" xlink:href="#audio-shape"/>
|
||||
<use id="audio-active" xlink:href="#audio-shape"/>
|
||||
<use id="audio-disabled" xlink:href="#audio-shape"/>
|
||||
<use id="facemute" xlink:href="#facemute-shape"/>
|
||||
<use id="facemute-active" xlink:href="#facemute-shape"/>
|
||||
<use id="facemute-disabled" xlink:href="#facemute-shape"/>
|
||||
<use id="hangup" xlink:href="#hangup-shape"/>
|
||||
<use id="hangup-active" xlink:href="#hangup-shape"/>
|
||||
<use id="hangup-disabled" xlink:href="#hangup-shape"/>
|
||||
<use id="incoming" xlink:href="#incoming-shape"/>
|
||||
<use id="incoming-active" xlink:href="#incoming-shape"/>
|
||||
<use id="incoming-disabled" xlink:href="#incoming-shape"/>
|
||||
<use id="link" xlink:href="#link-shape"/>
|
||||
<use id="link-active" xlink:href="#link-shape"/>
|
||||
<use id="link-disabled" xlink:href="#link-shape"/>
|
||||
<use id="mute" xlink:href="#mute-shape"/>
|
||||
<use id="mute-active" xlink:href="#mute-shape"/>
|
||||
<use id="mute-disabled" xlink:href="#mute-shape"/>
|
||||
<use id="pause" xlink:href="#pause-shape"/>
|
||||
<use id="pause-active" xlink:href="#pause-shape"/>
|
||||
<use id="pause-disabled" xlink:href="#pause-shape"/>
|
||||
<use id="video" xlink:href="#video-shape"/>
|
||||
<use id="video-white" xlink:href="#video-shape"/>
|
||||
<use id="video-active" xlink:href="#video-shape"/>
|
||||
<use id="video-disabled" xlink:href="#video-shape"/>
|
||||
<use id="volume" xlink:href="#volume-shape"/>
|
||||
<use id="volume-active" xlink:href="#volume-shape"/>
|
||||
<use id="volume-disabled" xlink:href="#volume-shape"/>
|
||||
</svg>
|
After Width: | Height: | Size: 9.2 KiB |
@ -24,6 +24,10 @@ use[id$="-hover"] {
|
||||
use[id$="-active"] {
|
||||
fill: #0095dd;
|
||||
}
|
||||
|
||||
use[id$="-red"] {
|
||||
fill: #d74345
|
||||
}
|
||||
</style>
|
||||
<defs style="display:none">
|
||||
<path id="audio-shape" fill-rule="evenodd" clip-rule="evenodd" d="M11.429,6.857v2.286c0,1.894-1.535,3.429-3.429,3.429
|
||||
@ -88,6 +92,7 @@ use[id$="-active"] {
|
||||
<use id="audio-hover" xlink:href="#audio-shape"/>
|
||||
<use id="audio-active" xlink:href="#audio-shape"/>
|
||||
<use id="block" xlink:href="#block-shape"/>
|
||||
<use id="block-red" xlink:href="#block-shape"/>
|
||||
<use id="block-hover" xlink:href="#block-shape"/>
|
||||
<use id="block-active" xlink:href="#block-shape"/>
|
||||
<use id="contacts" xlink:href="#contacts-shape"/>
|
||||
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 8.2 KiB |
@ -16,12 +16,14 @@ browser.jar:
|
||||
content/browser/loop/js/conversation.js (content/js/conversation.js)
|
||||
content/browser/loop/js/otconfig.js (content/js/otconfig.js)
|
||||
content/browser/loop/js/panel.js (content/js/panel.js)
|
||||
content/browser/loop/js/contacts.js (content/js/contacts.js)
|
||||
|
||||
# Shared styles
|
||||
content/browser/loop/shared/css/reset.css (content/shared/css/reset.css)
|
||||
content/browser/loop/shared/css/common.css (content/shared/css/common.css)
|
||||
content/browser/loop/shared/css/panel.css (content/shared/css/panel.css)
|
||||
content/browser/loop/shared/css/conversation.css (content/shared/css/conversation.css)
|
||||
content/browser/loop/shared/css/contacts.css (content/shared/css/contacts.css)
|
||||
|
||||
# Shared images
|
||||
content/browser/loop/shared/img/happy.png (content/shared/img/happy.png)
|
||||
@ -46,6 +48,8 @@ browser.jar:
|
||||
content/browser/loop/shared/img/svg/glyph-signin-16x16.svg (content/shared/img/svg/glyph-signin-16x16.svg)
|
||||
content/browser/loop/shared/img/svg/glyph-signout-16x16.svg (content/shared/img/svg/glyph-signout-16x16.svg)
|
||||
content/browser/loop/shared/img/audio-call-avatar.svg (content/shared/img/audio-call-avatar.svg)
|
||||
content/browser/loop/shared/img/icons-10x10.svg (content/shared/img/icons-10x10.svg)
|
||||
content/browser/loop/shared/img/icons-14x14.svg (content/shared/img/icons-14x14.svg)
|
||||
content/browser/loop/shared/img/icons-16x16.svg (content/shared/img/icons-16x16.svg)
|
||||
|
||||
# Shared scripts
|
||||
|
@ -42,6 +42,7 @@
|
||||
<script src="../../content/js/client.js"></script>
|
||||
<script src="../../content/js/desktopRouter.js"></script>
|
||||
<script src="../../content/js/conversation.js"></script>
|
||||
<script type="text/javascript;version=1.8" src="../../content/js/contacts.js"></script>
|
||||
<script src="../../content/js/panel.js"></script>
|
||||
|
||||
<!-- Test scripts -->
|
||||
|
@ -42,7 +42,13 @@ describe("loop.panel", function() {
|
||||
getLoopCharPref: sandbox.stub().returns("unseen"),
|
||||
copyString: sandbox.stub(),
|
||||
noteCallUrlExpiry: sinon.spy(),
|
||||
composeEmail: sinon.spy()
|
||||
composeEmail: sinon.spy(),
|
||||
contacts: {
|
||||
getAll: function(callback) {
|
||||
callback(null, []);
|
||||
},
|
||||
on: sandbox.stub()
|
||||
}
|
||||
};
|
||||
|
||||
document.mozL10n.initialize(navigator.mozLoop);
|
||||
|
@ -85,6 +85,9 @@ new_contact_button=New Contact
|
||||
## and click the 'New Contact' button to see the fields.
|
||||
new_contact_name_placeholder=Name
|
||||
new_contact_email_placeholder=Email
|
||||
|
||||
contacts_blocked_contacts=Blocked Contacts
|
||||
|
||||
## LOCALIZATION NOTE (add_contact_button):
|
||||
## This is the button to actually add the new contact
|
||||
## See https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#contacts
|
||||
|
@ -403,7 +403,6 @@ pref("privacy.item.syncAccount", true);
|
||||
|
||||
// enable geo
|
||||
pref("geo.enabled", true);
|
||||
pref("app.geo.reportdata", 0);
|
||||
|
||||
// content sink control -- controls responsiveness during page load
|
||||
// see https://bugzilla.mozilla.org/show_bug.cgi?id=481566#c9
|
||||
|
@ -1563,12 +1563,6 @@ public abstract class GeckoApp
|
||||
}
|
||||
});
|
||||
|
||||
PrefsHelper.getPref("app.geo.reportdata", new PrefsHelper.PrefHandlerBase() {
|
||||
@Override public void prefValue(String pref, int value) {
|
||||
// Acting on this pref is Bug 1036508; for now, do nothing.
|
||||
}
|
||||
});
|
||||
|
||||
// Trigger the completion of the telemetry timer that wraps activity startup,
|
||||
// then grab the duration to give to FHR.
|
||||
mJavaUiStartupTimer.stop();
|
||||
|
@ -110,14 +110,15 @@ OnSharedPreferenceChangeListener
|
||||
private static final String PREFS_MENU_CHAR_ENCODING = "browser.menu.showCharacterEncoding";
|
||||
private static final String PREFS_MP_ENABLED = "privacy.masterpassword.enabled";
|
||||
private static final String PREFS_UPDATER_AUTODOWNLOAD = "app.update.autodownload";
|
||||
private static final String PREFS_GEO_REPORTING = "app.geo.reportdata";
|
||||
private static final String PREFS_GEO_REPORTING = NON_PREF_PREFIX + "app.geo.reportdata";
|
||||
private static final String PREFS_GEO_LEARN_MORE = NON_PREF_PREFIX + "geo.learn_more";
|
||||
private static final String PREFS_HEALTHREPORT_LINK = NON_PREF_PREFIX + "healthreport.link";
|
||||
private static final String PREFS_DEVTOOLS_REMOTE_ENABLED = "devtools.debugger.remote-enabled";
|
||||
private static final String PREFS_DISPLAY_REFLOW_ON_ZOOM = "browser.zoom.reflowOnZoom";
|
||||
private static final String PREFS_DISPLAY_TITLEBAR_MODE = "browser.chrome.titlebarMode";
|
||||
private static final String PREFS_SYNC = NON_PREF_PREFIX + "sync";
|
||||
private static final String PREFS_STUMBLER_ENABLED = AppConstants.ANDROID_PACKAGE_NAME + ".STUMBLER_PREF";
|
||||
|
||||
private static final String ACTION_STUMBLER_UPLOAD_PREF = AppConstants.ANDROID_PACKAGE_NAME + ".STUMBLER_PREF";
|
||||
|
||||
// This isn't a Gecko pref, even if it looks like one.
|
||||
private static final String PREFS_BROWSER_LOCALE = "locale";
|
||||
@ -683,10 +684,9 @@ OnSharedPreferenceChangeListener
|
||||
preferences.removePreference(pref);
|
||||
i--;
|
||||
continue;
|
||||
} else if (AppConstants.RELEASE_BUILD &&
|
||||
(PREFS_GEO_REPORTING.equals(key) ||
|
||||
PREFS_GEO_LEARN_MORE.equals(key))) {
|
||||
// We don't build wifi/cell tower collection in release builds, so hide the UI.
|
||||
} else if (!AppConstants.MOZ_STUMBLER_BUILD_TIME_ENABLED &&
|
||||
(PREFS_GEO_REPORTING.equals(key) ||
|
||||
PREFS_GEO_LEARN_MORE.equals(key))) {
|
||||
preferences.removePreference(pref);
|
||||
i--;
|
||||
continue;
|
||||
@ -869,10 +869,10 @@ OnSharedPreferenceChangeListener
|
||||
|
||||
/**
|
||||
* Broadcast the provided value as the value of the
|
||||
* <code>PREFS_STUMBLER_ENABLED</code> pref.
|
||||
* <code>PREFS_GEO_REPORTING</code> pref.
|
||||
*/
|
||||
public static void broadcastStumblerPref(final Context context, final boolean value) {
|
||||
Intent intent = new Intent(PREFS_STUMBLER_ENABLED)
|
||||
Intent intent = new Intent(ACTION_STUMBLER_UPLOAD_PREF)
|
||||
.putExtra("pref", PREFS_GEO_REPORTING)
|
||||
.putExtra("branch", GeckoSharedPrefs.APP_PREFS_NAME)
|
||||
.putExtra("enabled", value)
|
||||
@ -888,7 +888,7 @@ OnSharedPreferenceChangeListener
|
||||
|
||||
/**
|
||||
* Broadcast the current value of the
|
||||
* <code>PREFS_STUMBLER_ENABLED</code> pref.
|
||||
* <code>PREFS_GEO_REPORTING</code> pref.
|
||||
*/
|
||||
public static void broadcastStumblerPref(final Context context) {
|
||||
final boolean value = getBooleanPref(context, PREFS_GEO_REPORTING, false);
|
||||
@ -1321,22 +1321,7 @@ OnSharedPreferenceChangeListener
|
||||
@Override
|
||||
public void prefValue(String prefName, final int value) {
|
||||
final Preference pref = getField(prefName);
|
||||
final CheckBoxPrefSetter prefSetter;
|
||||
if (PREFS_GEO_REPORTING.equals(prefName)) {
|
||||
if (Versions.preICS) {
|
||||
prefSetter = new CheckBoxPrefSetter();
|
||||
} else {
|
||||
prefSetter = new TwoStatePrefSetter();
|
||||
}
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
prefSetter.setBooleanPref(pref, value == 1);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Log.w(LOGTAG, "Unhandled int value for pref [" + pref + "]");
|
||||
}
|
||||
Log.w(LOGTAG, "Unhandled int value for pref [" + pref + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -35,7 +35,7 @@
|
||||
android:summary="@string/datareporting_crashreporter_summary"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<CheckBoxPreference android:key="app.geo.reportdata"
|
||||
<CheckBoxPreference android:key="android.not_a_preference.app.geo.reportdata"
|
||||
android:title="@string/datareporting_wifi_title"
|
||||
android:summary="@string/datareporting_wifi_geolocation_summary" />
|
||||
|
||||
|
@ -1,15 +1,11 @@
|
||||
package org.mozilla.gecko.tests;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.Actions;
|
||||
import org.mozilla.gecko.tests.helpers.JavascriptBridge;
|
||||
import org.mozilla.gecko.tests.helpers.JavascriptMessageParser;
|
||||
|
||||
import android.util.Log;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.Actions;
|
||||
import org.mozilla.gecko.Assert;
|
||||
|
||||
public class JavascriptTest extends BaseTest {
|
||||
private static final String LOGTAG = "JavascriptTest";
|
||||
@ -32,8 +28,7 @@ public class JavascriptTest extends BaseTest {
|
||||
mActions.expectGeckoEvent(EVENT_TYPE);
|
||||
mAsserter.dumpLog("Registered listener for " + EVENT_TYPE);
|
||||
|
||||
final String url = getAbsoluteUrl(StringHelper.ROBOCOP_JS_HARNESS_URL +
|
||||
"?path=" + javascriptUrl);
|
||||
final String url = getAbsoluteUrl(StringHelper.getHarnessUrlForJavascript(javascriptUrl));
|
||||
mAsserter.dumpLog("Loading JavaScript test from " + url);
|
||||
loadUrl(url);
|
||||
|
||||
|
@ -104,7 +104,25 @@ public class StringHelper {
|
||||
public static final String ROBOCOP_TEXT_PAGE_URL = "/robocop/robocop_text_page.html";
|
||||
public static final String ROBOCOP_ADOBE_FLASH_URL = "/robocop/robocop_adobe_flash.html";
|
||||
public static final String ROBOCOP_INPUT_URL = "/robocop/robocop_input.html";
|
||||
public static final String ROBOCOP_JS_HARNESS_URL = "/robocop/robocop_javascript.html";
|
||||
|
||||
private static final String ROBOCOP_JS_HARNESS_URL = "/robocop/robocop_javascript.html";
|
||||
|
||||
/**
|
||||
* Build a URL for loading a Javascript file in the Robocop Javascript
|
||||
* harness.
|
||||
* <p>
|
||||
* We append a random slug to avoid caching: see
|
||||
* <a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache">https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache</a>.
|
||||
*
|
||||
* @param javascriptUrl to load.
|
||||
* @return URL with harness wrapper.
|
||||
*/
|
||||
public static String getHarnessUrlForJavascript(String javascriptUrl) {
|
||||
// We include a slug to make sure we never cache the harness.
|
||||
return ROBOCOP_JS_HARNESS_URL +
|
||||
"?slug=" + System.currentTimeMillis() +
|
||||
"&path=" + javascriptUrl;
|
||||
}
|
||||
|
||||
// Robocop page titles
|
||||
public static final String ROBOCOP_BIG_LINK_TITLE = "Big Link";
|
||||
|
@ -18,7 +18,11 @@ function _evalURI(uri, sandbox) {
|
||||
let theURI = SpecialPowers.Services.io
|
||||
.newURI(uri, window.document.characterSet, baseURI);
|
||||
|
||||
req.open('GET', theURI.spec, false);
|
||||
// We append a random slug to avoid caching: see
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache.
|
||||
req.open('GET', theURI.spec + ((/\?/).test(theURI.spec) ? "&slug=" : "?slug=") + (new Date()).getTime(), false);
|
||||
req.setRequestHeader('Cache-Control', 'no-cache');
|
||||
req.setRequestHeader('Pragma', 'no-cache');
|
||||
req.send();
|
||||
|
||||
return SpecialPowers.Cu.evalInSandbox(req.responseText, sandbox, "1.8", uri, 1);
|
||||
|
@ -59,8 +59,7 @@ public class testEventDispatcher extends UITest
|
||||
|
||||
public void testEventDispatcher() {
|
||||
GeckoHelper.blockForReady();
|
||||
NavigationHelper.enterAndLoadUrl(StringHelper.ROBOCOP_JS_HARNESS_URL +
|
||||
"?path=" + TEST_JS);
|
||||
NavigationHelper.enterAndLoadUrl(StringHelper.getHarnessUrlForJavascript(TEST_JS));
|
||||
|
||||
js.syncCall("send_test_message", GECKO_EVENT);
|
||||
js.syncCall("send_message_for_response", GECKO_RESPONSE_EVENT, "success");
|
||||
|
@ -38,7 +38,7 @@ public class testGeckoRequest extends UITest {
|
||||
|
||||
public void testGeckoRequest() {
|
||||
GeckoHelper.blockForReady();
|
||||
NavigationHelper.enterAndLoadUrl(StringHelper.ROBOCOP_JS_HARNESS_URL + "?path=" + TEST_JS);
|
||||
NavigationHelper.enterAndLoadUrl(StringHelper.getHarnessUrlForJavascript(TEST_JS));
|
||||
|
||||
// Register a listener for this request.
|
||||
js.syncCall("add_request_listener", REQUEST_EVENT);
|
||||
|
@ -32,8 +32,7 @@ public class testJavascriptBridge extends UITest {
|
||||
|
||||
public void testJavascriptBridge() {
|
||||
GeckoHelper.blockForReady();
|
||||
NavigationHelper.enterAndLoadUrl(StringHelper.ROBOCOP_JS_HARNESS_URL +
|
||||
"?path=" + TEST_JS);
|
||||
NavigationHelper.enterAndLoadUrl(StringHelper.getHarnessUrlForJavascript(TEST_JS));
|
||||
js.syncCall("check_js_int_arg", 1);
|
||||
}
|
||||
|
||||
|
@ -170,12 +170,14 @@ public class testSettingsMenuItems extends PixelTest {
|
||||
settingsMap.get(PATH_DISPLAY).remove(TITLE_BAR_LABEL_ARR);
|
||||
}
|
||||
|
||||
// Anonymous cell tower/wifi collection - only built if *not* release build
|
||||
String[] networkReportingUi = { "Mozilla Location Service", "Receives Wi-Fi and cellular location data when running in the background and shares it with Mozilla to improve our geolocation service" };
|
||||
settingsMap.get(PATH_MOZILLA).add(networkReportingUi);
|
||||
if (AppConstants.MOZ_STUMBLER_BUILD_TIME_ENABLED) {
|
||||
// Anonymous cell tower/wifi collection
|
||||
String[] networkReportingUi = { "Mozilla Location Service", "Receives Wi-Fi and cellular location data when running in the background and shares it with Mozilla to improve our geolocation service" };
|
||||
settingsMap.get(PATH_MOZILLA).add(networkReportingUi);
|
||||
|
||||
String[] learnMoreUi = { "Learn more" };
|
||||
settingsMap.get(PATH_MOZILLA).add(learnMoreUi);
|
||||
String[] learnMoreUi = { "Learn more" };
|
||||
settingsMap.get(PATH_MOZILLA).add(learnMoreUi);
|
||||
}
|
||||
}
|
||||
|
||||
// Automatic updates
|
||||
|
@ -78,7 +78,7 @@ var FindHelper = {
|
||||
}
|
||||
} else {
|
||||
// Disabled until bug 1014113 is fixed
|
||||
// ZoomHelper.zoomToRect(aData.rect);
|
||||
//ZoomHelper.zoomToRect(aData.rect, -1, false, true);
|
||||
this._viewportChanged = true;
|
||||
}
|
||||
}
|
||||
|
@ -81,16 +81,24 @@ var ZoomHelper = {
|
||||
*/
|
||||
zoomToElement: function(aElement, aClickY = -1, aCanZoomOut = true, aCanScrollHorizontally = true) {
|
||||
let rect = ElementTouchHelper.getBoundingContentRect(aElement);
|
||||
ZoomHelper.zoomToRect(rect, aClickY, aCanZoomOut, aCanScrollHorizontally, aElement);
|
||||
},
|
||||
|
||||
zoomToRect: function(aRect, aClickY = -1, aCanZoomOut = true, aCanScrollHorizontally = true, aElement) {
|
||||
const margin = 15;
|
||||
|
||||
if(!aRect.h || !aRect.w) {
|
||||
aRect.h = aRect.height;
|
||||
aRect.w = aRect.width;
|
||||
}
|
||||
|
||||
let viewport = BrowserApp.selectedTab.getViewport();
|
||||
rect = new Rect(aCanScrollHorizontally ? Math.max(viewport.cssPageLeft, rect.x - margin) : viewport.cssX,
|
||||
rect.y,
|
||||
aCanScrollHorizontally ? rect.w + 2 * margin : viewport.cssWidth,
|
||||
rect.h);
|
||||
let bRect = new Rect(aCanScrollHorizontally ? Math.max(viewport.cssPageLeft, aRect.x - margin) : viewport.cssX,
|
||||
aRect.y,
|
||||
aCanScrollHorizontally ? aRect.w + 2 * margin : viewport.cssWidth,
|
||||
aRect.h);
|
||||
// constrict the rect to the screen's right edge
|
||||
rect.width = Math.min(rect.width, viewport.cssPageRight - rect.x);
|
||||
bRect.width = Math.min(bRect.width, viewport.cssPageRight - bRect.x);
|
||||
|
||||
// if the rect is already taking up most of the visible area and is stretching the
|
||||
// width of the page, then we want to zoom out instead.
|
||||
@ -98,44 +106,37 @@ var ZoomHelper = {
|
||||
if (BrowserEventHandler.mReflozPref) {
|
||||
let zoomFactor = BrowserApp.selectedTab.getZoomToMinFontSize(aElement);
|
||||
|
||||
rect.width = zoomFactor <= 1.0 ? rect.width : gScreenWidth / zoomFactor;
|
||||
rect.height = zoomFactor <= 1.0 ? rect.height : rect.height / zoomFactor;
|
||||
if (zoomFactor == 1.0 || ZoomHelper.isRectZoomedIn(rect, viewport)) {
|
||||
bRect.width = zoomFactor <= 1.0 ? bRect.width : gScreenWidth / zoomFactor;
|
||||
bRect.height = zoomFactor <= 1.0 ? bRect.height : bRect.height / zoomFactor;
|
||||
if (zoomFactor == 1.0 || ZoomHelper.isRectZoomedIn(bRect, viewport)) {
|
||||
if (aCanZoomOut) {
|
||||
ZoomHelper.zoomOut();
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else if (ZoomHelper.isRectZoomedIn(rect, viewport)) {
|
||||
} else if (ZoomHelper.isRectZoomedIn(bRect, viewport)) {
|
||||
if (aCanZoomOut) {
|
||||
ZoomHelper.zoomOut();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ZoomHelper.zoomToRect(rect, aClickY);
|
||||
}
|
||||
},
|
||||
|
||||
/* Zoom to a specific part of the screen defined by a rect,
|
||||
* optionally keeping a particular part of it in view
|
||||
* if it is really tall.
|
||||
*/
|
||||
zoomToRect: function(aRect, aClickY = -1) {
|
||||
let rect = new Rect(aRect.x,
|
||||
aRect.y,
|
||||
aRect.width,
|
||||
Math.min(aRect.width * viewport.cssHeight / viewport.cssWidth, aRect.height));
|
||||
let rect = {};
|
||||
|
||||
rect.type = "Browser:ZoomToRect";
|
||||
rect.x = bRect.x;
|
||||
rect.y = bRect.y;
|
||||
rect.w = bRect.width;
|
||||
rect.h = Math.min(bRect.width * viewport.cssHeight / viewport.cssWidth, bRect.height);
|
||||
|
||||
if (aClickY >= 0) {
|
||||
// if the block we're zooming to is really tall, and we want to keep a particular
|
||||
// part of it in view, then adjust the y-coordinate of the target rect accordingly.
|
||||
// the 1.2 multiplier is just a little fuzz to compensate for aRect including horizontal
|
||||
// the 1.2 multiplier is just a little fuzz to compensate for bRect including horizontal
|
||||
// margins but not vertical ones.
|
||||
let cssTapY = viewport.cssY + aClickY;
|
||||
if ((aRect.height > rect.h) && (cssTapY > rect.y + (rect.h * 1.2))) {
|
||||
if ((bRect.height > rect.h) && (cssTapY > rect.y + (rect.h * 1.2))) {
|
||||
rect.y = cssTapY - (rect.h / 2);
|
||||
}
|
||||
}
|
||||
|
@ -86,5 +86,9 @@ if test ! "$RELEASE_BUILD"; then
|
||||
MOZ_ANDROID_SHARE_OVERLAY=1
|
||||
fi
|
||||
|
||||
# Don't enable the Mozilla Location Service stumbler.
|
||||
# MOZ_ANDROID_MLS_STUMBLER=1
|
||||
# Enable the Mozilla Location Service stumbler in Nightly.
|
||||
if test "$NIGHTLY_BUILD"; then
|
||||
MOZ_ANDROID_MLS_STUMBLER=1
|
||||
else
|
||||
MOZ_ANDROID_MLS_STUMBLER=
|
||||
fi
|
||||
|
@ -850,11 +850,11 @@ class TreeMetadataEmitter(LoggingMixin):
|
||||
filtered = m.active_tests(exists=False, disabled=True,
|
||||
**self.info)
|
||||
|
||||
missing = [t['name'] for t in filtered if not os.path.exists(t['path'])]
|
||||
if missing:
|
||||
raise SandboxValidationError('Test manifest (%s) lists '
|
||||
'test that does not exist: %s' % (
|
||||
path, ', '.join(missing)), context)
|
||||
missing = [t['name'] for t in filtered if not os.path.exists(t['path'])]
|
||||
if missing:
|
||||
raise SandboxValidationError('Test manifest (%s) lists '
|
||||
'test that does not exist: %s' % (
|
||||
path, ', '.join(missing)), context)
|
||||
|
||||
out_dir = mozpath.join(install_prefix, manifest_reldir)
|
||||
if 'install-to-subdir' in defaults:
|
||||
|
@ -0,0 +1,4 @@
|
||||
# Any copyright is dedicated to the Public Domain.
|
||||
# http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['xpcshell.ini']
|
@ -0,0 +1,4 @@
|
||||
[DEFAULT]
|
||||
support-files = support/**
|
||||
|
||||
[missing.js]
|
@ -501,6 +501,14 @@ class TestEmitterBasic(unittest.TestCase):
|
||||
'lists test that does not exist: test_missing.html'):
|
||||
self.read_topsrcdir(reader)
|
||||
|
||||
def test_test_manifest_missing_test_error_unfiltered(self):
|
||||
"""Missing test files should result in error, even when the test list is not filtered."""
|
||||
reader = self.reader('test-manifest-missing-test-file-unfiltered')
|
||||
|
||||
with self.assertRaisesRegexp(SandboxValidationError,
|
||||
'lists test that does not exist: missing.js'):
|
||||
self.read_topsrcdir(reader)
|
||||
|
||||
def test_ipdl_sources(self):
|
||||
reader = self.reader('ipdl_sources')
|
||||
objs = self.read_topsrcdir(reader)
|
||||
|
@ -2431,9 +2431,9 @@ this.AddonManagerPrivate = {
|
||||
// Start a timer, record a simple measure of the time interval when
|
||||
// timer.done() is called
|
||||
simpleTimer: function(aName) {
|
||||
let startTime = Date.now();
|
||||
let startTime = Cu.now();
|
||||
return {
|
||||
done: () => this.recordSimpleMeasure(aName, Date.now() - startTime)
|
||||
done: () => this.recordSimpleMeasure(aName, Math.round(Cu.now() - startTime))
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -1494,7 +1494,7 @@ XPIState.prototype = {
|
||||
*/
|
||||
getModTime(aFile, aId) {
|
||||
let changed = false;
|
||||
let scanStarted = Date.now();
|
||||
let scanStarted = Cu.now();
|
||||
// For an unknown or enabled add-on, we do a full recursive scan.
|
||||
if (!('scanTime' in this) || this.enabled) {
|
||||
logger.debug('getModTime: Recursive scan of ' + aId);
|
||||
@ -1539,7 +1539,7 @@ XPIState.prototype = {
|
||||
}
|
||||
}
|
||||
// Record duration of file-modified check
|
||||
XPIProvider.setTelemetry(aId, "scan_MS", Date.now() - scanStarted);
|
||||
XPIProvider.setTelemetry(aId, "scan_MS", Math.round(Cu.now() - scanStarted));
|
||||
|
||||
return changed;
|
||||
},
|
||||
@ -3539,10 +3539,10 @@ this.XPIProvider = {
|
||||
}
|
||||
|
||||
// Telemetry probe added around getInstallState() to check perf
|
||||
let telemetryCaptureTime = Date.now();
|
||||
let telemetryCaptureTime = Cu.now();
|
||||
let installChanged = XPIStates.getInstallState();
|
||||
let telemetry = Services.telemetry;
|
||||
telemetry.getHistogramById("CHECK_ADDONS_MODIFIED_MS").add(Date.now() - telemetryCaptureTime);
|
||||
telemetry.getHistogramById("CHECK_ADDONS_MODIFIED_MS").add(Math.round(Cu.now() - telemetryCaptureTime));
|
||||
if (installChanged) {
|
||||
updateReasons.push("directoryState");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user