mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to inbound on a CLOSED TREE.
This commit is contained in:
commit
e9d4f36f17
@ -65,8 +65,12 @@ exports.install = function install(xpiPath) {
|
||||
|
||||
// Order AddonManager to install the addon
|
||||
AddonManager.getInstallForFile(file, function(install) {
|
||||
install.addListener(listener);
|
||||
install.install();
|
||||
if (install.error != null) {
|
||||
install.addListener(listener);
|
||||
install.install();
|
||||
} else {
|
||||
reject(install.error);
|
||||
}
|
||||
});
|
||||
|
||||
return promise;
|
||||
|
@ -286,17 +286,6 @@ const ContentWorker = Object.freeze({
|
||||
value: self
|
||||
});
|
||||
|
||||
// Deprecated use of on/postMessage from globals
|
||||
exports.postMessage = function deprecatedPostMessage() {
|
||||
console.error("DEPRECATED: The global `postMessage()` function in " +
|
||||
"content scripts is deprecated in favor of the " +
|
||||
"`self.postMessage()` function, which works the same. " +
|
||||
"Replace calls to `postMessage()` with calls to " +
|
||||
"`self.postMessage()`." +
|
||||
"For more info on `self.on`, see " +
|
||||
"<https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/addon-development/web-content.html>.");
|
||||
return self.postMessage.apply(null, arguments);
|
||||
};
|
||||
exports.on = function deprecatedOn() {
|
||||
console.error("DEPRECATED: The global `on()` function in content " +
|
||||
"scripts is deprecated in favor of the `self.on()` " +
|
||||
|
@ -46,9 +46,11 @@ function onDocumentReady2Translate(event) {
|
||||
|
||||
try {
|
||||
// Finally display document when we finished replacing all text content
|
||||
let winUtils = document.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
winUtils.removeSheet(hideSheetUri, winUtils.USER_SHEET);
|
||||
if (document.defaultView) {
|
||||
let winUtils = document.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
winUtils.removeSheet(hideSheetUri, winUtils.USER_SHEET);
|
||||
}
|
||||
}
|
||||
catch(e) {
|
||||
console.exception(e);
|
||||
|
@ -223,7 +223,6 @@ exports.show = show
|
||||
function setupPanelFrame(frame) {
|
||||
frame.setAttribute("flex", 1);
|
||||
frame.setAttribute("transparent", "transparent");
|
||||
frame.setAttribute("showcaret", true);
|
||||
frame.setAttribute("autocompleteenabled", true);
|
||||
if (platform === "darwin") {
|
||||
frame.style.borderRadius = "6px";
|
||||
|
@ -191,9 +191,6 @@ exports["test postMessage"] = createProxyTest(html, function (helper, assert) {
|
||||
|
||||
helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
assert(postMessage === postMessage,
|
||||
"verify that we doesn't generate multiple functions for the same method");
|
||||
|
||||
var json = JSON.stringify({foo : "bar\n \"escaped\"."});
|
||||
|
||||
document.getElementById("iframe").contentWindow.postMessage(json, "*");
|
||||
|
@ -26,7 +26,13 @@ const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings";
|
||||
|
||||
const DEFAULT_CONTENT_URL = "data:text/html;charset=utf-8,foo";
|
||||
|
||||
function makeWindow(contentURL) {
|
||||
const WINDOW_SCRIPT_URL = "data:text/html;charset=utf-8," +
|
||||
"<script>window.addEventListener('message', function (e) {" +
|
||||
" if (e.data === 'from -> content-script')" +
|
||||
" window.postMessage('from -> window', '*');" +
|
||||
"});</script>";
|
||||
|
||||
function makeWindow() {
|
||||
let content =
|
||||
"<?xml version=\"1.0\"?>" +
|
||||
"<window " +
|
||||
@ -782,50 +788,6 @@ exports["test:check worker API with page history"] = WorkerTest(
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:global postMessage"] = WorkerTest(
|
||||
DEFAULT_CONTENT_URL,
|
||||
function(assert, browser, done) {
|
||||
let { loader } = LoaderWithHookedConsole(module, onMessage);
|
||||
setPref(DEPRECATE_PREF, true);
|
||||
|
||||
// Intercept all console method calls
|
||||
let seenMessages = 0;
|
||||
function onMessage(type, message) {
|
||||
seenMessages++;
|
||||
assert.equal(type, "error", "Should be an error");
|
||||
assert.equal(message, "DEPRECATED: The global `postMessage()` function in " +
|
||||
"content scripts is deprecated in favor of the " +
|
||||
"`self.postMessage()` function, which works the same. " +
|
||||
"Replace calls to `postMessage()` with calls to " +
|
||||
"`self.postMessage()`." +
|
||||
"For more info on `self.on`, see " +
|
||||
"<https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/addon-development/web-content.html>.",
|
||||
"Should have seen the deprecation message")
|
||||
}
|
||||
|
||||
assert.notEqual(browser.contentWindow.location.href, "about:blank",
|
||||
"window is now on the right document");
|
||||
|
||||
let window = browser.contentWindow
|
||||
let worker = loader.require("sdk/content/worker").Worker({
|
||||
window: window,
|
||||
contentScript: "new " + function WorkerScope() {
|
||||
postMessage("success");
|
||||
},
|
||||
contentScriptWhen: "ready",
|
||||
onMessage: function(msg) {
|
||||
assert.equal("success", msg, "Should have seen the right postMessage call");
|
||||
assert.equal(1, seenMessages, "Should have seen the deprecation message");
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(worker.url, window.location.href,
|
||||
"worker.url works");
|
||||
worker.postMessage("hi!");
|
||||
}
|
||||
);
|
||||
|
||||
exports['test:conentScriptFile as URL instance'] = WorkerTest(
|
||||
DEFAULT_CONTENT_URL,
|
||||
function(assert, browser, done) {
|
||||
@ -889,8 +851,8 @@ exports["test:onDetach in contentScript on destroy"] = WorkerTest(
|
||||
})
|
||||
},
|
||||
});
|
||||
browser.contentWindow.addEventListener('hashchange', _ => {
|
||||
assert.equal(browser.contentWindow.location.hash, '#detach!',
|
||||
browser.contentWindow.addEventListener('hashchange', _ => {
|
||||
assert.equal(browser.contentWindow.location.hash, '#detach!',
|
||||
"location.href is as expected");
|
||||
done();
|
||||
})
|
||||
@ -910,8 +872,8 @@ exports["test:onDetach in contentScript on unload"] = WorkerTest(
|
||||
})
|
||||
},
|
||||
});
|
||||
browser.contentWindow.addEventListener('hashchange', _ => {
|
||||
assert.equal(browser.contentWindow.location.hash, '#detach!shutdown',
|
||||
browser.contentWindow.addEventListener('hashchange', _ => {
|
||||
assert.equal(browser.contentWindow.location.hash, '#detach!shutdown',
|
||||
"location.href is as expected");
|
||||
done();
|
||||
})
|
||||
@ -954,4 +916,25 @@ exports["test:console method log functions properly"] = WorkerTest(
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:global postMessage"] = WorkerTest(
|
||||
WINDOW_SCRIPT_URL,
|
||||
function(assert, browser, done) {
|
||||
let contentScript = "window.addEventListener('message', function (e) {" +
|
||||
" if (e.data === 'from -> window')" +
|
||||
" self.port.emit('response', e.data, e.origin);" +
|
||||
"});" +
|
||||
"postMessage('from -> content-script', '*');";
|
||||
let { loader } = LoaderWithHookedConsole(module);
|
||||
let worker = loader.require("sdk/content/worker").Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScriptWhen: "ready",
|
||||
contentScript: contentScript
|
||||
});
|
||||
|
||||
worker.port.on("response", (data, origin) => {
|
||||
assert.equal(data, "from -> window", "Communication from content-script to window completed");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
require("test").run(exports);
|
||||
|
@ -1,12 +1,10 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { Loader } = require('sdk/test/loader');
|
||||
const Pages = require("sdk/page-worker");
|
||||
const Page = Pages.Page;
|
||||
const { Page } = require("sdk/page-worker");
|
||||
const { URL } = require("sdk/url");
|
||||
const fixtures = require("./fixtures");
|
||||
const testURI = fixtures.url("test.html");
|
||||
@ -93,17 +91,17 @@ exports.testPageProperties = function(assert) {
|
||||
|
||||
exports.testConstructorAndDestructor = function(assert, done) {
|
||||
let loader = Loader(module);
|
||||
let Pages = loader.require("sdk/page-worker");
|
||||
let { Page } = loader.require("sdk/page-worker");
|
||||
let global = loader.sandbox("sdk/page-worker");
|
||||
|
||||
let pagesReady = 0;
|
||||
|
||||
let page1 = Pages.Page({
|
||||
let page1 = Page({
|
||||
contentScript: "self.postMessage('')",
|
||||
contentScriptWhen: "end",
|
||||
onMessage: pageReady
|
||||
});
|
||||
let page2 = Pages.Page({
|
||||
let page2 = Page({
|
||||
contentScript: "self.postMessage('')",
|
||||
contentScriptWhen: "end",
|
||||
onMessage: pageReady
|
||||
@ -128,9 +126,9 @@ exports.testConstructorAndDestructor = function(assert, done) {
|
||||
|
||||
exports.testAutoDestructor = function(assert, done) {
|
||||
let loader = Loader(module);
|
||||
let Pages = loader.require("sdk/page-worker");
|
||||
let { Page } = loader.require("sdk/page-worker");
|
||||
|
||||
let page = Pages.Page({
|
||||
let page = Page({
|
||||
contentScript: "self.postMessage('')",
|
||||
contentScriptWhen: "end",
|
||||
onMessage: function() {
|
||||
@ -316,12 +314,12 @@ exports.testPingPong = function(assert, done) {
|
||||
exports.testRedirect = function (assert, done) {
|
||||
let page = Page({
|
||||
contentURL: 'data:text/html;charset=utf-8,first-page',
|
||||
contentScript: '(function () {' +
|
||||
contentScriptWhen: "end",
|
||||
contentScript: '' +
|
||||
'if (/first-page/.test(document.location.href)) ' +
|
||||
' document.location.href = "data:text/html;charset=utf-8,redirect";' +
|
||||
'else ' +
|
||||
' self.port.emit("redirect", document.location.href);' +
|
||||
'})();'
|
||||
' self.port.emit("redirect", document.location.href);'
|
||||
});
|
||||
|
||||
page.port.on('redirect', function (url) {
|
||||
|
@ -1167,9 +1167,9 @@ let RemoteDebugger = {
|
||||
};
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
DebuggerServer.onConnectionChange = function(what) {
|
||||
DebuggerServer.on("connectionchange", function() {
|
||||
AdbController.updateState();
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,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="dadf0e60a6421f5b57ee9fc536c6617212805c19"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="375d87bfef8a5d7135f139da8c17f237b990d3f5"/>
|
||||
<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="55bcc2d7e44dc805c24b57d1e783fc26e8a2ee86"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</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="dadf0e60a6421f5b57ee9fc536c6617212805c19"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="375d87bfef8a5d7135f139da8c17f237b990d3f5"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
|
||||
@ -128,7 +128,7 @@
|
||||
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
|
||||
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="09485b73629856b21b2ed6073e327ab0e69a1189"/>
|
||||
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="f8e49f3837230cfbb2d6fbcf239d1f25b57b39a7"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="8afce6d5d48b71b6e7cb917179fb6147fb747521"/>
|
||||
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="72e3a520e3c700839f07ba0113fd527b923c3330"/>
|
||||
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="6a7ada569fd37c09ed4bbee6eb78cea5b467b229"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="52c909ccead537f8f9dbf634f3e6639078a8b0bd">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="dadf0e60a6421f5b57ee9fc536c6617212805c19"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="375d87bfef8a5d7135f139da8c17f237b990d3f5"/>
|
||||
<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="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
|
@ -19,7 +19,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="dadf0e60a6421f5b57ee9fc536c6617212805c19"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="375d87bfef8a5d7135f139da8c17f237b990d3f5"/>
|
||||
<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="55bcc2d7e44dc805c24b57d1e783fc26e8a2ee86"/>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="dadf0e60a6421f5b57ee9fc536c6617212805c19"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="375d87bfef8a5d7135f139da8c17f237b990d3f5"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
|
||||
@ -118,11 +118,11 @@
|
||||
<default remote="caf" revision="jb_3.2" sync-j="4"/>
|
||||
<!-- Flame specific things -->
|
||||
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="e8a318f7690092e639ba88891606f4183e846d3f"/>
|
||||
<project name="device/qcom/common" path="device/qcom/common" revision="c2c115bec6f5e9bc1daf7bb74bb4d14861c00b9c"/>
|
||||
<project name="device/qcom/common" path="device/qcom/common" revision="234ed34543345f58c0d4dcb1aa012de68802b9dc"/>
|
||||
<project name="device-flame" path="device/t2m/flame" remote="b2g" revision="92f9b79e3a5ecf24cb0f66e20d5292b300f8cac9"/>
|
||||
<project name="kernel/msm" path="kernel" revision="39ca56deb6c4419c0220a762f6e340091032390d"/>
|
||||
<project name="kernel/msm" path="kernel" revision="b3092c54430df89636fb0670d32058bc63474017"/>
|
||||
<project name="platform/bootable/recovery" path="bootable/recovery" revision="f2914eacee9120680a41463708bb6ee8291749fc"/>
|
||||
<project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="c53977638eb8a7091f752d870e8e0f00f7a23d95"/>
|
||||
<project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="fa892235a9bd8983f8b591129fc1a9398f64e514"/>
|
||||
<project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="f0689ac1914cdbc59e53bdc9edd9013dc157c299"/>
|
||||
<project name="platform/external/bluetooth/glib" path="external/bluetooth/glib" revision="dd925f76e4f149c3d5571b80e12f7e24bbe89c59"/>
|
||||
<project name="platform/external/dbus" path="external/dbus" revision="ea87119c843116340f5df1d94eaf8275e1055ae8"/>
|
||||
@ -139,7 +139,7 @@
|
||||
<project name="platform/hardware/qcom/wlan" path="hardware/qcom/wlan" revision="2208fa3537ace873b8f9ec2355055761c79dfd5f"/>
|
||||
<project name="platform/hardware/ril" path="hardware/ril" revision="c4e2ac95907a5519a0e09f01a0d8e27fec101af0"/>
|
||||
<project name="platform/system/bluetooth" path="system/bluetooth" revision="e1eb226fa3ad3874ea7b63c56a9dc7012d7ff3c2"/>
|
||||
<project name="platform/system/core" path="system/core" revision="8b7736c56fa9a3fd8b4341eb243e12eec847efed"/>
|
||||
<project name="platform/system/core" path="system/core" revision="e284280277c1312017a9450956a9676584441cdf"/>
|
||||
<project name="platform/system/qcom" path="system/qcom" revision="1cdab258b15258b7f9657da70e6f06ebd5a2fc25"/>
|
||||
<project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="b3001d5f1686f89995b7bf70963cf69c8faebd83"/>
|
||||
</manifest>
|
||||
|
@ -4,6 +4,6 @@
|
||||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "35bd8bfb0ee9dce0eb9dcfdbb43587517b655121",
|
||||
"revision": "d253e50da6b54f14a1ffa6d8a8a287337f0e3f34",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -17,7 +17,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="dadf0e60a6421f5b57ee9fc536c6617212805c19"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="375d87bfef8a5d7135f139da8c17f237b990d3f5"/>
|
||||
<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"/>
|
||||
|
@ -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="dadf0e60a6421f5b57ee9fc536c6617212805c19"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="375d87bfef8a5d7135f139da8c17f237b990d3f5"/>
|
||||
<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"/>
|
||||
|
@ -19,7 +19,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="dadf0e60a6421f5b57ee9fc536c6617212805c19"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="375d87bfef8a5d7135f139da8c17f237b990d3f5"/>
|
||||
<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"/>
|
||||
|
@ -17,7 +17,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="dadf0e60a6421f5b57ee9fc536c6617212805c19"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="375d87bfef8a5d7135f139da8c17f237b990d3f5"/>
|
||||
<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"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</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="dadf0e60a6421f5b57ee9fc536c6617212805c19"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="375d87bfef8a5d7135f139da8c17f237b990d3f5"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
|
||||
|
@ -17,7 +17,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="dadf0e60a6421f5b57ee9fc536c6617212805c19"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="375d87bfef8a5d7135f139da8c17f237b990d3f5"/>
|
||||
<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"/>
|
||||
|
@ -7,6 +7,7 @@ XPCOMUtils.defineLazyGetter(this, "FxAccountsCommon", function () {
|
||||
});
|
||||
|
||||
const PREF_SYNC_START_DOORHANGER = "services.sync.ui.showSyncStartDoorhanger";
|
||||
const DOORHANGER_ACTIVATE_DELAY_MS = 5000;
|
||||
|
||||
let gFxAccounts = {
|
||||
|
||||
@ -33,16 +34,6 @@ let gFxAccounts = {
|
||||
];
|
||||
},
|
||||
|
||||
// The set of topics that only the active window should handle.
|
||||
get activeWindowTopics() {
|
||||
// Do all this dance to lazy-load FxAccountsCommon.
|
||||
delete this.activeWindowTopics;
|
||||
return this.activeWindowTopics = new Set([
|
||||
"weave:service:sync:start",
|
||||
FxAccountsCommon.ONVERIFIED_NOTIFICATION
|
||||
]);
|
||||
},
|
||||
|
||||
get button() {
|
||||
delete this.button;
|
||||
return this.button = document.getElementById("PanelUI-fxa-status");
|
||||
@ -64,9 +55,8 @@ let gFxAccounts = {
|
||||
},
|
||||
|
||||
get isActiveWindow() {
|
||||
let mostRecentNonPopupWindow =
|
||||
RecentWindow.getMostRecentBrowserWindow({allowPopups: false});
|
||||
return window == mostRecentNonPopupWindow;
|
||||
let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
|
||||
return fm.activeWindow == window;
|
||||
},
|
||||
|
||||
init: function () {
|
||||
@ -79,6 +69,7 @@ let gFxAccounts = {
|
||||
Services.obs.addObserver(this, topic, false);
|
||||
}
|
||||
|
||||
addEventListener("activate", this);
|
||||
gNavToolbox.addEventListener("customizationstarting", this);
|
||||
gNavToolbox.addEventListener("customizationending", this);
|
||||
|
||||
@ -100,11 +91,6 @@ let gFxAccounts = {
|
||||
},
|
||||
|
||||
observe: function (subject, topic) {
|
||||
// Ignore certain topics if we're not the active window.
|
||||
if (this.activeWindowTopics.has(topic) && !this.isActiveWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (topic) {
|
||||
case FxAccountsCommon.ONVERIFIED_NOTIFICATION:
|
||||
Services.prefs.setBoolPref(PREF_SYNC_START_DOORHANGER, true);
|
||||
@ -119,6 +105,10 @@ let gFxAccounts = {
|
||||
},
|
||||
|
||||
onSyncStart: function () {
|
||||
if (!this.isActiveWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
let showDoorhanger = false;
|
||||
|
||||
try {
|
||||
@ -132,8 +122,17 @@ let gFxAccounts = {
|
||||
},
|
||||
|
||||
handleEvent: function (event) {
|
||||
this._inCustomizationMode = event.type == "customizationstarting";
|
||||
this.updateUI();
|
||||
if (event.type == "activate") {
|
||||
// Our window might have been in the background while we received the
|
||||
// sync:start notification. If still needed, show the doorhanger after
|
||||
// a short delay. Without this delay the doorhanger would not show up
|
||||
// or with a too small delay show up while we're still animating the
|
||||
// window.
|
||||
setTimeout(() => this.onSyncStart(), DOORHANGER_ACTIVATE_DELAY_MS);
|
||||
} else {
|
||||
this._inCustomizationMode = event.type == "customizationstarting";
|
||||
this.updateUI();
|
||||
}
|
||||
},
|
||||
|
||||
showDoorhanger: function (id) {
|
||||
|
@ -6999,10 +6999,6 @@ var TabContextMenu = {
|
||||
aPopupMenu.triggerNode : gBrowser.selectedTab;
|
||||
let disabled = gBrowser.tabs.length == 1;
|
||||
|
||||
// Enable the "Close Tab" menuitem when the window doesn't close with the last tab.
|
||||
document.getElementById("context_closeTab").disabled =
|
||||
disabled && gBrowser.tabContainer._closeWindowWithLastTab;
|
||||
|
||||
var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple");
|
||||
for (let menuItem of menuItems)
|
||||
menuItem.disabled = disabled;
|
||||
|
@ -1,3 +1,7 @@
|
||||
/* 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/. */
|
||||
|
||||
const kObservedTopics = [
|
||||
"getUserMedia:response:allow",
|
||||
"getUserMedia:revoke",
|
||||
@ -736,6 +740,50 @@ let gTests = [
|
||||
info("request video, stop sharing resets video only");
|
||||
yield stopAndCheckPerm(false, true);
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "'Always Allow' ignored and not shown on http pages",
|
||||
run: function checkNoAlwaysOnHttp() {
|
||||
// Load an http page instead of the https version.
|
||||
let deferred = Promise.defer();
|
||||
let browser = gBrowser.selectedTab.linkedBrowser;
|
||||
browser.addEventListener("load", function onload() {
|
||||
browser.removeEventListener("load", onload, true);
|
||||
deferred.resolve();
|
||||
}, true);
|
||||
content.location = content.location.href.replace("https://", "http://");
|
||||
yield deferred.promise;
|
||||
|
||||
// Initially set both permissions to 'allow'.
|
||||
let Perms = Services.perms;
|
||||
let uri = content.document.documentURIObject;
|
||||
Perms.add(uri, "microphone", Perms.ALLOW_ACTION);
|
||||
Perms.add(uri, "camera", Perms.ALLOW_ACTION);
|
||||
|
||||
// Request devices and expect a prompt despite the saved 'Allow' permission,
|
||||
// because the connection isn't secure.
|
||||
yield promisePopupNotificationShown("webRTC-shareDevices", () => {
|
||||
content.wrappedJSObject.requestDevice(true, true);
|
||||
});
|
||||
expectObserverCalled("getUserMedia:request");
|
||||
|
||||
// Ensure that the 'Always Allow' action isn't shown.
|
||||
let alwaysLabel = gNavigatorBundle.getString("getUserMedia.always.label");
|
||||
ok(!!alwaysLabel, "found the 'Always Allow' localized label");
|
||||
let labels = [];
|
||||
let notification = PopupNotifications.panel.firstChild;
|
||||
for (let node of notification.childNodes) {
|
||||
if (node.localName == "menuitem")
|
||||
labels.push(node.getAttribute("label"));
|
||||
}
|
||||
is(labels.indexOf(alwaysLabel), -1, "The 'Always Allow' item isn't shown");
|
||||
|
||||
// Cleanup.
|
||||
yield closeStream(true);
|
||||
Perms.remove(uri.host, "camera");
|
||||
Perms.remove(uri.host, "microphone");
|
||||
}
|
||||
}
|
||||
|
||||
];
|
||||
|
@ -163,8 +163,8 @@ let gSyncPane = {
|
||||
checkbox.checked = false;
|
||||
}
|
||||
|
||||
checkbox.disabled = !allowPasswordsEngine;
|
||||
help.hidden = allowPasswordsEngine;
|
||||
checkbox.disabled = !allowPasswordsEngine || enginesListDisabled;
|
||||
help.hidden = allowPasswordsEngine || enginesListDisabled;
|
||||
});
|
||||
// If fxAccountEnabled is false and we are in a "not configured" state,
|
||||
// then fxAccounts is probably fully disabled rather than just unconfigured,
|
||||
|
@ -163,8 +163,8 @@ let gSyncPane = {
|
||||
checkbox.checked = false;
|
||||
}
|
||||
|
||||
checkbox.disabled = !allowPasswordsEngine;
|
||||
help.hidden = allowPasswordsEngine;
|
||||
checkbox.disabled = !allowPasswordsEngine || enginesListDisabled;
|
||||
help.hidden = allowPasswordsEngine || enginesListDisabled;
|
||||
});
|
||||
// If fxAccountEnabled is false and we are in a "not configured" state,
|
||||
// then fxAccounts is probably fully disabled rather than just unconfigured,
|
||||
|
@ -7,7 +7,7 @@ function test() {
|
||||
let originalTab = gBrowser.tabs[0];
|
||||
|
||||
popup(originalTab);
|
||||
ok(document.getElementById("context_closeTab").disabled, "The 'Close tab' menu item is disabled");
|
||||
ok(!document.getElementById("context_closeTab").disabled, "The 'Close tab' menu item is enabled");
|
||||
ok(document.getElementById("context_openTabInWindow").disabled, "The 'Move to New Window' menu item is disabled");
|
||||
|
||||
let newTabOne = gBrowser.addTab("about:blank", {skipAnimation: true});
|
||||
|
@ -16,9 +16,13 @@ Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
|
||||
Cu.import("resource://gre/modules/devtools/Loader.jsm");
|
||||
let require = devtools.require;
|
||||
let Telemetry = require("devtools/shared/telemetry");
|
||||
let EventEmitter = require("devtools/toolkit/event-emitter");
|
||||
const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["BrowserToolboxProcess"];
|
||||
|
||||
let processes = Set();
|
||||
|
||||
/**
|
||||
* Constructor for creating a process that will hold a chrome toolbox.
|
||||
*
|
||||
@ -30,15 +34,33 @@ this.EXPORTED_SYMBOLS = ["BrowserToolboxProcess"];
|
||||
* An object with properties for configuring BrowserToolboxProcess.
|
||||
*/
|
||||
this.BrowserToolboxProcess = function BrowserToolboxProcess(aOnClose, aOnRun, aOptions) {
|
||||
let emitter = new EventEmitter();
|
||||
this.on = emitter.on.bind(emitter);
|
||||
this.off = emitter.off.bind(emitter);
|
||||
this.once = emitter.once.bind(emitter);
|
||||
// Forward any events to the shared emitter.
|
||||
this.emit = function(...args) {
|
||||
emitter.emit(...args);
|
||||
BrowserToolboxProcess.emit(...args);
|
||||
}
|
||||
|
||||
// If first argument is an object, use those properties instead of
|
||||
// all three arguments
|
||||
if (typeof aOnClose === "object") {
|
||||
this._closeCallback = aOnClose.onClose;
|
||||
this._runCallback = aOnClose.onRun;
|
||||
if (aOnClose.onClose) {
|
||||
this.on("close", aOnClose.onClose);
|
||||
}
|
||||
if (aOnClose.onRun) {
|
||||
this.on("run", aOnClose.onRun);
|
||||
}
|
||||
this._options = aOnClose;
|
||||
} else {
|
||||
this._closeCallback = aOnClose;
|
||||
this._runCallback = aOnRun;
|
||||
if (aOnClose) {
|
||||
this.on("close", aOnClose);
|
||||
}
|
||||
if (aOnRun) {
|
||||
this.on("run", aOnRun);
|
||||
}
|
||||
this._options = aOptions || {};
|
||||
}
|
||||
|
||||
@ -49,8 +71,12 @@ this.BrowserToolboxProcess = function BrowserToolboxProcess(aOnClose, aOnRun, aO
|
||||
this._initServer();
|
||||
this._initProfile();
|
||||
this._create();
|
||||
|
||||
processes.add(this);
|
||||
};
|
||||
|
||||
EventEmitter.decorate(BrowserToolboxProcess);
|
||||
|
||||
/**
|
||||
* Initializes and starts a chrome toolbox process.
|
||||
* @return object
|
||||
@ -59,6 +85,25 @@ BrowserToolboxProcess.init = function(aOnClose, aOnRun, aOptions) {
|
||||
return new BrowserToolboxProcess(aOnClose, aOnRun, aOptions);
|
||||
};
|
||||
|
||||
/**
|
||||
* Passes a set of options to the BrowserAddonActors for the given ID.
|
||||
*
|
||||
* @param aId string
|
||||
* The ID of the add-on to pass the options to
|
||||
* @param aOptions object
|
||||
* The options.
|
||||
* @return a promise that will be resolved when complete.
|
||||
*/
|
||||
BrowserToolboxProcess.setAddonOptions = function DSC_setAddonOptions(aId, aOptions) {
|
||||
let promises = [];
|
||||
|
||||
for (let process of processes.values()) {
|
||||
promises.push(process.debuggerServer.setAddonOptions(aId, aOptions));
|
||||
}
|
||||
|
||||
return promise.all(promises);
|
||||
};
|
||||
|
||||
BrowserToolboxProcess.prototype = {
|
||||
/**
|
||||
* Initializes the debugger server.
|
||||
@ -77,6 +122,9 @@ BrowserToolboxProcess.prototype = {
|
||||
this.loader.main("devtools/server/main");
|
||||
this.debuggerServer = this.loader.DebuggerServer;
|
||||
dumpn("Created a separate loader instance for the DebuggerServer.");
|
||||
|
||||
// Forward interesting events.
|
||||
this.debuggerServer.on("connectionchange", this.emit.bind(this));
|
||||
}
|
||||
|
||||
if (!this.debuggerServer.initialized) {
|
||||
@ -169,9 +217,7 @@ BrowserToolboxProcess.prototype = {
|
||||
this._telemetry.toolOpened("jsbrowserdebugger");
|
||||
|
||||
dumpn("Chrome toolbox is now running...");
|
||||
if (typeof this._runCallback == "function") {
|
||||
this._runCallback.call({}, this);
|
||||
}
|
||||
this.emit("run", this);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -196,9 +242,8 @@ BrowserToolboxProcess.prototype = {
|
||||
|
||||
dumpn("Chrome toolbox is now closed...");
|
||||
this.closed = true;
|
||||
if (typeof this._closeCallback == "function") {
|
||||
this._closeCallback.call({}, this);
|
||||
}
|
||||
this.emit("close", this);
|
||||
processes.delete(this);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -9,9 +9,11 @@
|
||||
const Cu = Components.utils;
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
|
||||
let {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
|
||||
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
|
||||
let gClient;
|
||||
let gConnectionTimeout;
|
||||
@ -75,59 +77,97 @@ function submit() {
|
||||
/**
|
||||
* Connection is ready. List actors and build buttons.
|
||||
*/
|
||||
function onConnectionReady(aType, aTraits) {
|
||||
let onConnectionReady = Task.async(function*(aType, aTraits) {
|
||||
clearTimeout(gConnectionTimeout);
|
||||
gClient.listTabs(function(aResponse) {
|
||||
document.body.classList.remove("connecting");
|
||||
document.body.classList.add("actors-mode");
|
||||
|
||||
let parent = document.getElementById("tabActors");
|
||||
|
||||
// Add Global Process debugging...
|
||||
let globals = JSON.parse(JSON.stringify(aResponse));
|
||||
delete globals.tabs;
|
||||
delete globals.selected;
|
||||
// ...only if there are appropriate actors (a 'from' property will always
|
||||
// be there).
|
||||
|
||||
// Add one entry for each open tab.
|
||||
for (let i = 0; i < aResponse.tabs.length; i++) {
|
||||
buildLink(aResponse.tabs[i], parent, i == aResponse.selected);
|
||||
}
|
||||
|
||||
let gParent = document.getElementById("globalActors");
|
||||
|
||||
// Build the Remote Process button
|
||||
if (Object.keys(globals).length > 1) {
|
||||
let a = document.createElement("a");
|
||||
a.onclick = function() {
|
||||
openToolbox(globals, true);
|
||||
let deferred = promise.defer();
|
||||
gClient.listAddons(deferred.resolve);
|
||||
let response = yield deferred.promise;
|
||||
|
||||
let parent = document.getElementById("addonActors")
|
||||
if (!response.error && response.addons.length > 0) {
|
||||
// Add one entry for each add-on.
|
||||
for (let addon of response.addons) {
|
||||
if (!addon.debuggable) {
|
||||
continue;
|
||||
}
|
||||
a.title = a.textContent = window.l10n.GetStringFromName("mainProcess");
|
||||
a.className = "remote-process";
|
||||
a.href = "#";
|
||||
gParent.appendChild(a);
|
||||
}
|
||||
// Move the selected tab on top
|
||||
let selectedLink = parent.querySelector("a.selected");
|
||||
if (selectedLink) {
|
||||
parent.insertBefore(selectedLink, parent.firstChild);
|
||||
buildAddonLink(addon, parent);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Hide the section when there are no add-ons
|
||||
parent.previousElementSibling.remove();
|
||||
parent.remove();
|
||||
}
|
||||
|
||||
// Ensure the first link is focused
|
||||
let firstLink = parent.querySelector("a:first-of-type");
|
||||
if (firstLink) {
|
||||
firstLink.focus();
|
||||
}
|
||||
deferred = promise.defer();
|
||||
gClient.listTabs(deferred.resolve);
|
||||
response = yield deferred.promise;
|
||||
|
||||
});
|
||||
parent = document.getElementById("tabActors");
|
||||
|
||||
// Add Global Process debugging...
|
||||
let globals = JSON.parse(JSON.stringify(response));
|
||||
delete globals.tabs;
|
||||
delete globals.selected;
|
||||
// ...only if there are appropriate actors (a 'from' property will always
|
||||
// be there).
|
||||
|
||||
// Add one entry for each open tab.
|
||||
for (let i = 0; i < response.tabs.length; i++) {
|
||||
buildTabLink(response.tabs[i], parent, i == response.selected);
|
||||
}
|
||||
|
||||
let gParent = document.getElementById("globalActors");
|
||||
|
||||
// Build the Remote Process button
|
||||
if (Object.keys(globals).length > 1) {
|
||||
let a = document.createElement("a");
|
||||
a.onclick = function() {
|
||||
openToolbox(globals, true);
|
||||
|
||||
}
|
||||
a.title = a.textContent = window.l10n.GetStringFromName("mainProcess");
|
||||
a.className = "remote-process";
|
||||
a.href = "#";
|
||||
gParent.appendChild(a);
|
||||
}
|
||||
// Move the selected tab on top
|
||||
let selectedLink = parent.querySelector("a.selected");
|
||||
if (selectedLink) {
|
||||
parent.insertBefore(selectedLink, parent.firstChild);
|
||||
}
|
||||
|
||||
document.body.classList.remove("connecting");
|
||||
document.body.classList.add("actors-mode");
|
||||
|
||||
// Ensure the first link is focused
|
||||
let firstLink = parent.querySelector("a:first-of-type");
|
||||
if (firstLink) {
|
||||
firstLink.focus();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Build one button for an add-on actor.
|
||||
*/
|
||||
function buildAddonLink(addon, parent) {
|
||||
let a = document.createElement("a");
|
||||
a.onclick = function() {
|
||||
openToolbox({ addonActor: addon.actor, title: addon.name }, true, "jsdebugger");
|
||||
}
|
||||
|
||||
a.textContent = addon.name;
|
||||
a.title = addon.id;
|
||||
a.href = "#";
|
||||
|
||||
parent.appendChild(a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build one button for an actor.
|
||||
* Build one button for a tab actor.
|
||||
*/
|
||||
function buildLink(tab, parent, selected) {
|
||||
function buildTabLink(tab, parent, selected) {
|
||||
let a = document.createElement("a");
|
||||
a.onclick = function() {
|
||||
openToolbox(tab);
|
||||
@ -173,7 +213,7 @@ function handleConnectionTimeout() {
|
||||
* The user clicked on one of the buttons.
|
||||
* Opens the toolbox.
|
||||
*/
|
||||
function openToolbox(form, chrome=false) {
|
||||
function openToolbox(form, chrome=false, tool="webconsole") {
|
||||
let options = {
|
||||
form: form,
|
||||
client: gClient,
|
||||
@ -181,7 +221,7 @@ function openToolbox(form, chrome=false) {
|
||||
};
|
||||
devtools.TargetFactory.forRemoteTab(options).then((target) => {
|
||||
let hostType = devtools.Toolbox.HostType.WINDOW;
|
||||
gDevTools.showToolbox(target, "webconsole", hostType).then((toolbox) => {
|
||||
gDevTools.showToolbox(target, tool, hostType).then((toolbox) => {
|
||||
toolbox.once("destroyed", function() {
|
||||
gClient.close();
|
||||
});
|
||||
|
@ -39,6 +39,8 @@
|
||||
<section id="actors-list">
|
||||
<p>&availableTabs;</p>
|
||||
<ul class="actors" id="tabActors"></ul>
|
||||
<p>&availableAddons;</p>
|
||||
<ul class="actors" id="addonActors"></ul>
|
||||
<p>&availableProcesses;</p>
|
||||
<ul class="actors" id="globalActors"></ul>
|
||||
</section>
|
||||
|
@ -822,11 +822,17 @@ MarkupView.prototype = {
|
||||
|
||||
/**
|
||||
* Mark the given node expanded.
|
||||
* @param aNode The NodeFront to mark as expanded.
|
||||
* @param {NodeFront} aNode The NodeFront to mark as expanded.
|
||||
* @param {Boolean} aExpanded Whether the expand or collapse.
|
||||
* @param {Boolean} aExpandDescendants Whether to expand all descendants too
|
||||
*/
|
||||
setNodeExpanded: function(aNode, aExpanded) {
|
||||
setNodeExpanded: function(aNode, aExpanded, aExpandDescendants) {
|
||||
if (aExpanded) {
|
||||
this.expandNode(aNode);
|
||||
if (aExpandDescendants) {
|
||||
this.expandAll(aNode);
|
||||
} else {
|
||||
this.expandNode(aNode);
|
||||
}
|
||||
} else {
|
||||
this.collapseNode(aNode);
|
||||
}
|
||||
@ -1413,7 +1419,7 @@ MarkupContainer.prototype = {
|
||||
_onToggle: function(event) {
|
||||
this.markup.navigate(this);
|
||||
if(this.hasChildren) {
|
||||
this.markup.setNodeExpanded(this.node, !this.expanded);
|
||||
this.markup.setNodeExpanded(this.node, !this.expanded, event.altKey);
|
||||
}
|
||||
event.stopPropagation();
|
||||
},
|
||||
|
@ -9,6 +9,7 @@ support-files =
|
||||
doc_markup_pagesize_01.html
|
||||
doc_markup_pagesize_02.html
|
||||
doc_markup_search.html
|
||||
doc_markup_toggle.html
|
||||
doc_markup_tooltip.png
|
||||
head.js
|
||||
helper_attributes_test_runner.js
|
||||
@ -37,3 +38,6 @@ support-files =
|
||||
[browser_markupview_tag_edit_07.js]
|
||||
[browser_markupview_tag_edit_08.js]
|
||||
[browser_markupview_textcontent_edit_01.js]
|
||||
[browser_markupview_toggle_01.js]
|
||||
[browser_markupview_toggle_02.js]
|
||||
[browser_markupview_toggle_03.js]
|
||||
|
@ -113,14 +113,6 @@ function pressKey(key) {
|
||||
}
|
||||
}
|
||||
|
||||
function waitForChildrenUpdated(inspector) {
|
||||
let def = promise.defer();
|
||||
inspector.markup._waitForChildren().then(() => {
|
||||
executeSoon(def.resolve);
|
||||
});
|
||||
return def.promise;
|
||||
}
|
||||
|
||||
function checkSelectedNode(key, className, inspector) {
|
||||
let node = inspector.selection.node;
|
||||
|
||||
|
@ -0,0 +1,45 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test toggling (expand/collapse) elements by clicking on twisties
|
||||
|
||||
const TEST_URL = TEST_URL_ROOT + "doc_markup_toggle.html";
|
||||
|
||||
let test = asyncTest(function*() {
|
||||
let {inspector} = yield addTab(TEST_URL).then(openInspector);
|
||||
|
||||
info("Getting the container for the UL parent element");
|
||||
let container = getContainerForRawNode("ul", inspector);
|
||||
|
||||
info("Clicking on the UL parent expander, and waiting for children");
|
||||
let onChildren = waitForChildrenUpdated(inspector);
|
||||
let onUpdated = inspector.once("inspector-updated");
|
||||
EventUtils.synthesizeMouseAtCenter(container.expander, {},
|
||||
inspector.markup.doc.defaultView);
|
||||
yield onChildren;
|
||||
yield onUpdated;
|
||||
|
||||
info("Checking that child LI elements have been created");
|
||||
for (let li of content.document.querySelectorAll("li")) {
|
||||
ok(getContainerForRawNode(li, inspector),
|
||||
"A container for the child LI element was created");
|
||||
}
|
||||
ok(container.expanded, "Parent UL container is expanded");
|
||||
|
||||
info("Clicking again on the UL expander");
|
||||
// No need to wait, this is a local, synchronous operation where nodes are
|
||||
// only hidden from the view, not destroyed
|
||||
EventUtils.synthesizeMouseAtCenter(container.expander, {},
|
||||
inspector.markup.doc.defaultView);
|
||||
|
||||
info("Checking that child LI elements have been hidden");
|
||||
for (let li of content.document.querySelectorAll("li")) {
|
||||
let liContainer = getContainerForRawNode(li, inspector);
|
||||
is(liContainer.elt.getClientRects().length, 0,
|
||||
"The container for the child LI element was hidden");
|
||||
}
|
||||
ok(!container.expanded, "Parent UL container is collapsed");
|
||||
});
|
@ -0,0 +1,45 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test toggling (expand/collapse) elements by dbl-clicking on tag lines
|
||||
|
||||
const TEST_URL = TEST_URL_ROOT + "doc_markup_toggle.html";
|
||||
|
||||
let test = asyncTest(function*() {
|
||||
let {inspector} = yield addTab(TEST_URL).then(openInspector);
|
||||
|
||||
info("Getting the container for the UL parent element");
|
||||
let container = getContainerForRawNode("ul", inspector);
|
||||
|
||||
info("Dbl-clicking on the UL parent expander, and waiting for children");
|
||||
let onChildren = waitForChildrenUpdated(inspector);
|
||||
let onUpdated = inspector.once("inspector-updated");
|
||||
EventUtils.synthesizeMouseAtCenter(container.tagLine, {clickCount: 2},
|
||||
inspector.markup.doc.defaultView);
|
||||
yield onChildren;
|
||||
yield onUpdated;
|
||||
|
||||
info("Checking that child LI elements have been created");
|
||||
for (let li of content.document.querySelectorAll("li")) {
|
||||
ok(getContainerForRawNode(li, inspector),
|
||||
"A container for the child LI element was created");
|
||||
}
|
||||
ok(container.expanded, "Parent UL container is expanded");
|
||||
|
||||
info("Dbl-clicking again on the UL expander");
|
||||
// No need to wait, this is a local, synchronous operation where nodes are
|
||||
// only hidden from the view, not destroyed
|
||||
EventUtils.synthesizeMouseAtCenter(container.tagLine, {clickCount: 2},
|
||||
inspector.markup.doc.defaultView);
|
||||
|
||||
info("Checking that child LI elements have been hidden");
|
||||
for (let li of content.document.querySelectorAll("li")) {
|
||||
let liContainer = getContainerForRawNode(li, inspector);
|
||||
is(liContainer.elt.getClientRects().length, 0,
|
||||
"The container for the child LI element was hidden");
|
||||
}
|
||||
ok(!container.expanded, "Parent UL container is collapsed");
|
||||
});
|
@ -0,0 +1,44 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test toggling (expand/collapse) elements by alt-clicking on twisties, which
|
||||
// should expand all the descendants
|
||||
|
||||
const TEST_URL = TEST_URL_ROOT + "doc_markup_toggle.html";
|
||||
|
||||
let test = asyncTest(function*() {
|
||||
let {inspector} = yield addTab(TEST_URL).then(openInspector);
|
||||
|
||||
info("Getting the container for the UL parent element");
|
||||
let container = getContainerForRawNode("ul", inspector);
|
||||
|
||||
info("Alt-clicking on the UL parent expander, and waiting for children");
|
||||
let onUpdated = inspector.once("inspector-updated");
|
||||
EventUtils.synthesizeMouseAtCenter(container.expander, {altKey: true},
|
||||
inspector.markup.doc.defaultView);
|
||||
yield onUpdated;
|
||||
yield waitForMultipleChildrenUpdates(inspector);
|
||||
|
||||
info("Checking that all nodes exist and are expanded");
|
||||
for (let node of content.document.querySelectorAll("ul, li, span, em")) {
|
||||
let nodeContainer = getContainerForRawNode(node, inspector);
|
||||
ok(nodeContainer, "Container for node " + node.tagName + " exists");
|
||||
ok(nodeContainer.expanded,
|
||||
"Container for node " + node.tagName + " is expanded");
|
||||
}
|
||||
});
|
||||
|
||||
// The expand all operation of the markup-view calls itself recursively and
|
||||
// there's not one event we can wait for to know when it's done
|
||||
function* waitForMultipleChildrenUpdates(inspector) {
|
||||
// As long as child updates are queued up while we wait for an update already
|
||||
// wait again
|
||||
if (inspector.markup._queuedChildUpdates &&
|
||||
inspector.markup._queuedChildUpdates.size) {
|
||||
yield waitForChildrenUpdated(inspector);
|
||||
return yield waitForMultipleChildrenUpdates(inspector);
|
||||
}
|
||||
}
|
28
browser/devtools/markupview/test/doc_markup_toggle.html
Normal file
28
browser/devtools/markupview/test/doc_markup_toggle.html
Normal file
@ -0,0 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Expanding and collapsing markup-view containers</title>
|
||||
</head>
|
||||
<body>
|
||||
<ul>
|
||||
<li>
|
||||
<span>list <em>item</em></span>
|
||||
</li>
|
||||
<li>
|
||||
<span>list <em>item</em></span>
|
||||
</li>
|
||||
<li>
|
||||
<span>list <em>item</em></span>
|
||||
</li>
|
||||
<li>
|
||||
<span>list <em>item</em></span>
|
||||
</li>
|
||||
<li>
|
||||
<span>list <em>item</em></span>
|
||||
</li>
|
||||
<li>
|
||||
<span>list <em>item</em></span>
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
@ -166,6 +166,23 @@ function getContainerForRawNode(nodeOrSelector, {markup}) {
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Using the markupview's _waitForChildren function, wait for all queued
|
||||
* children updates to be handled.
|
||||
* @param {InspectorPanel} inspector The instance of InspectorPanel currently
|
||||
* loaded in the toolbox
|
||||
* @return a promise that resolves when all queued children updates have been
|
||||
* handled
|
||||
*/
|
||||
function waitForChildrenUpdated({markup}) {
|
||||
info("Waiting for queued children updates to be handled");
|
||||
let def = promise.defer();
|
||||
markup._waitForChildren().then(() => {
|
||||
executeSoon(def.resolve);
|
||||
});
|
||||
return def.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate a mouse-over on the markup-container (a line in the markup-view)
|
||||
* that corresponds to the node or selector passed.
|
||||
@ -352,3 +369,15 @@ function searchUsingSelectorSearch(selector, inspector) {
|
||||
field.value = selector;
|
||||
EventUtils.sendKey("return", inspector.panelWin);
|
||||
}
|
||||
|
||||
/**
|
||||
* This shouldn't be used in the tests, but is useful when writing new tests or
|
||||
* debugging existing tests in order to introduce delays in the test steps
|
||||
* @param {Number} ms The time to wait
|
||||
* @return A promise that resolves when the time is passed
|
||||
*/
|
||||
function wait(ms) {
|
||||
let def = promise.defer();
|
||||
content.setTimeout(def.resolve, ms);
|
||||
return def.promise;
|
||||
}
|
||||
|
@ -42,11 +42,9 @@ XPCOMUtils.defineLazyGetter(this, "CertUtils",
|
||||
return mod;
|
||||
});
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter",
|
||||
"@mozilla.org/xre/app-info;1",
|
||||
"nsICrashReporter");
|
||||
#endif
|
||||
|
||||
const FILE_CACHE = "experiments.json";
|
||||
const OBSERVER_TOPIC = "experiments-changed";
|
||||
@ -76,20 +74,29 @@ const TELEMETRY_LOG = {
|
||||
// log(key, [kind, experimentId, details])
|
||||
ACTIVATION_KEY: "EXPERIMENT_ACTIVATION",
|
||||
ACTIVATION: {
|
||||
ACTIVATED: "ACTIVATED", // successfully activated
|
||||
INSTALL_FAILURE: "INSTALL_FAILURE", // failed to install the extension
|
||||
REJECTED: "REJECTED", // experiment was rejected because of it's conditions,
|
||||
// provides details on which
|
||||
// Successfully activated.
|
||||
ACTIVATED: "ACTIVATED",
|
||||
// Failed to install the add-on.
|
||||
INSTALL_FAILURE: "INSTALL_FAILURE",
|
||||
// Experiment does not meet activation requirements. Details will
|
||||
// be provided.
|
||||
REJECTED: "REJECTED",
|
||||
},
|
||||
|
||||
// log(key, [kind, experimentId, optionalDetails...])
|
||||
TERMINATION_KEY: "EXPERIMENT_TERMINATION",
|
||||
TERMINATION: {
|
||||
USERDISABLED: "USERDISABLED", // the user disabled this experiment
|
||||
FROM_API: "FROM_API", // the experiment disabled itself
|
||||
EXPIRED: "EXPIRED", // experiment expired e.g. by exceeding the end-date
|
||||
RECHECK: "RECHECK", // disabled after re-evaluating conditions,
|
||||
// provides details on which
|
||||
// The Experiments service was disabled.
|
||||
SERVICE_DISABLED: "SERVICE_DISABLED",
|
||||
// Add-on uninstalled.
|
||||
ADDON_UNINSTALLED: "ADDON_UNINSTALLED",
|
||||
// The experiment disabled itself.
|
||||
FROM_API: "FROM_API",
|
||||
// The experiment expired (e.g. by exceeding the end date).
|
||||
EXPIRED: "EXPIRED",
|
||||
// Disabled after re-evaluating conditions. If this is specified,
|
||||
// details will be provided.
|
||||
RECHECK: "RECHECK",
|
||||
},
|
||||
};
|
||||
|
||||
@ -106,6 +113,10 @@ let gExperimentEntryCounter = 0;
|
||||
// installs.
|
||||
let gActiveInstallURLs = new Set();
|
||||
|
||||
// Tracks add-on IDs that are being uninstalled by us. This allows us
|
||||
// to differentiate between expected uninstalled and user-driven uninstalls.
|
||||
let gActiveUninstallAddonIDs = new Set();
|
||||
|
||||
let gLogger;
|
||||
let gLogDumping = false;
|
||||
|
||||
@ -316,6 +327,14 @@ Experiments.Policy.prototype = {
|
||||
},
|
||||
};
|
||||
|
||||
function AlreadyShutdownError(message="already shut down") {
|
||||
this.name = "AlreadyShutdownError";
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
AlreadyShutdownError.prototype = new Error();
|
||||
AlreadyShutdownError.prototype.constructor = AlreadyShutdownError;
|
||||
|
||||
/**
|
||||
* Manages the experiments and provides an interface to control them.
|
||||
*/
|
||||
@ -340,9 +359,6 @@ Experiments.Experiments = function (policy=new Experiments.Policy()) {
|
||||
// Loading the cache happens once asynchronously on startup
|
||||
this._loadTask = null;
|
||||
|
||||
// Ignore addon-manager notifications for addons that we are uninstalling ourself
|
||||
this._pendingUninstall = null;
|
||||
|
||||
// The _main task handles all other actions:
|
||||
// * refreshing the manifest off the network (if _refresh)
|
||||
// * disabling/enabling experiments
|
||||
@ -426,7 +442,11 @@ Experiments.Experiments.prototype = {
|
||||
|
||||
this._shutdown = true;
|
||||
if (this._mainTask) {
|
||||
yield this._mainTask;
|
||||
try {
|
||||
yield this._mainTask;
|
||||
} catch (e if e instanceof AlreadyShutdownError) {
|
||||
// We error out of tasks after shutdown via that exception.
|
||||
}
|
||||
}
|
||||
|
||||
this._log.info("Completed uninitialization.");
|
||||
@ -449,7 +469,7 @@ Experiments.Experiments.prototype = {
|
||||
*/
|
||||
_checkForShutdown: function() {
|
||||
if (this._shutdown) {
|
||||
throw Error("uninit() already called");
|
||||
throw new AlreadyShutdownError("uninit() already called");
|
||||
}
|
||||
},
|
||||
|
||||
@ -468,7 +488,7 @@ Experiments.Experiments.prototype = {
|
||||
gPrefs.set(PREF_ENABLED, enabled);
|
||||
},
|
||||
|
||||
_toggleExperimentsEnabled: function (enabled) {
|
||||
_toggleExperimentsEnabled: Task.async(function* (enabled) {
|
||||
this._log.trace("_toggleExperimentsEnabled(" + enabled + ")");
|
||||
let wasEnabled = gExperimentsEnabled;
|
||||
gExperimentsEnabled = enabled && telemetryEnabled();
|
||||
@ -478,14 +498,14 @@ Experiments.Experiments.prototype = {
|
||||
}
|
||||
|
||||
if (gExperimentsEnabled) {
|
||||
this.updateManifest();
|
||||
yield this.updateManifest();
|
||||
} else {
|
||||
this.disableExperiment();
|
||||
yield this.disableExperiment(TELEMETRY_LOG.TERMINATION.SERVICE_DISABLED);
|
||||
if (this._timer) {
|
||||
this._timer.clear();
|
||||
}
|
||||
}
|
||||
},
|
||||
}),
|
||||
|
||||
_telemetryStatusChanged: function () {
|
||||
this._toggleExperimentsEnabled(gExperimentsEnabled);
|
||||
@ -667,21 +687,9 @@ Experiments.Experiments.prototype = {
|
||||
|
||||
// START OF ADD-ON LISTENERS
|
||||
|
||||
onDisabled: function (addon) {
|
||||
this._log.trace("onDisabled() - addon id: " + addon.id);
|
||||
if (addon.id == this._pendingUninstall) {
|
||||
return;
|
||||
}
|
||||
let activeExperiment = this._getActiveExperiment();
|
||||
if (!activeExperiment || activeExperiment._addonId != addon.id) {
|
||||
return;
|
||||
}
|
||||
this.disableExperiment();
|
||||
},
|
||||
|
||||
onUninstalled: function (addon) {
|
||||
this._log.trace("onUninstalled() - addon id: " + addon.id);
|
||||
if (addon.id == this._pendingUninstall) {
|
||||
if (gActiveUninstallAddonIDs.has(addon.id)) {
|
||||
this._log.trace("matches pending uninstall");
|
||||
return;
|
||||
}
|
||||
@ -689,7 +697,8 @@ Experiments.Experiments.prototype = {
|
||||
if (!activeExperiment || activeExperiment._addonId != addon.id) {
|
||||
return;
|
||||
}
|
||||
this.disableExperiment();
|
||||
|
||||
this.disableExperiment(TELEMETRY_LOG.TERMINATION.ADDON_UNINSTALLED);
|
||||
},
|
||||
|
||||
onInstallStarted: function (install) {
|
||||
@ -927,15 +936,17 @@ Experiments.Experiments.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable an experiment by id.
|
||||
* @param experimentId The id of the experiment.
|
||||
* @param userDisabled (optional) Whether this is disabled as a result of a user action.
|
||||
* Disables all active experiments.
|
||||
*
|
||||
* @return Promise<> Promise that will get resolved once the task is done or failed.
|
||||
*/
|
||||
disableExperiment: function (userDisabled=true) {
|
||||
this._log.trace("disableExperiment()");
|
||||
disableExperiment: function (reason) {
|
||||
if (!reason) {
|
||||
throw new Error("Must specify a termination reason.");
|
||||
}
|
||||
|
||||
this._terminateReason = userDisabled ? TELEMETRY_LOG.TERMINATION.USERDISABLED : TELEMETRY_LOG.TERMINATION.FROM_API;
|
||||
this._log.trace("disableExperiment()");
|
||||
this._terminateReason = reason;
|
||||
return this._run();
|
||||
},
|
||||
|
||||
@ -992,44 +1003,43 @@ Experiments.Experiments.prototype = {
|
||||
gPrefs.set(PREF_ACTIVE_EXPERIMENT, false);
|
||||
}
|
||||
|
||||
// Ensure the active experiment is in the proper state. This may install,
|
||||
// uninstall, upgrade, or enable the experiment add-on. What exactly is
|
||||
// abstracted away from us by design.
|
||||
if (activeExperiment) {
|
||||
this._pendingUninstall = activeExperiment._addonId;
|
||||
try {
|
||||
let wasStopped;
|
||||
if (this._terminateReason) {
|
||||
yield activeExperiment.stop(this._terminateReason);
|
||||
wasStopped = true;
|
||||
let changes;
|
||||
let shouldStopResult = yield activeExperiment.shouldStop();
|
||||
if (shouldStopResult.shouldStop) {
|
||||
let expireReasons = ["endTime", "maxActiveSeconds"];
|
||||
let kind, reason;
|
||||
|
||||
if (expireReasons.indexOf(shouldStopResult.reason[0]) != -1) {
|
||||
kind = TELEMETRY_LOG.TERMINATION.EXPIRED;
|
||||
reason = null;
|
||||
} else {
|
||||
wasStopped = yield activeExperiment.maybeStop();
|
||||
kind = TELEMETRY_LOG.TERMINATION.RECHECK;
|
||||
reason = shouldStopResult.reason;
|
||||
}
|
||||
if (wasStopped) {
|
||||
this._dirty = true;
|
||||
this._log.debug("evaluateExperiments() - stopped experiment "
|
||||
+ activeExperiment.id);
|
||||
activeExperiment = null;
|
||||
activeChanged = true;
|
||||
} else if (!gExperimentsEnabled) {
|
||||
// No further actions if the feature is disabled.
|
||||
} else if (activeExperiment.needsUpdate) {
|
||||
this._log.debug("evaluateExperiments() - updating experiment "
|
||||
+ activeExperiment.id);
|
||||
try {
|
||||
yield activeExperiment.stop();
|
||||
yield activeExperiment.start();
|
||||
} catch (e) {
|
||||
this._log.error(e);
|
||||
// On failure try the next experiment.
|
||||
activeExperiment = null;
|
||||
}
|
||||
this._dirty = true;
|
||||
activeChanged = true;
|
||||
} else {
|
||||
yield activeExperiment.ensureActive();
|
||||
}
|
||||
} finally {
|
||||
this._pendingUninstall = null;
|
||||
changes = yield activeExperiment.stop(kind, reason);
|
||||
}
|
||||
else if (this._terminateReason) {
|
||||
changes = yield activeExperiment.stop(this._terminateReason);
|
||||
}
|
||||
else {
|
||||
changes = yield activeExperiment.reconcileAddonState();
|
||||
}
|
||||
|
||||
if (changes) {
|
||||
this._dirty = true;
|
||||
activeChanged = true;
|
||||
}
|
||||
|
||||
if (!activeExperiment._enabled) {
|
||||
activeExperiment = null;
|
||||
activeChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
this._terminateReason = null;
|
||||
|
||||
if (!activeExperiment && gExperimentsEnabled) {
|
||||
@ -1052,30 +1062,35 @@ Experiments.Experiments.prototype = {
|
||||
TelemetryLog.log(TELEMETRY_LOG.ACTIVATION_KEY, data);
|
||||
}
|
||||
|
||||
if (applicable) {
|
||||
this._log.debug("evaluateExperiments() - activating experiment " + id);
|
||||
try {
|
||||
yield experiment.start();
|
||||
activeChanged = true;
|
||||
activeExperiment = experiment;
|
||||
this._dirty = true;
|
||||
break;
|
||||
} catch (e) {
|
||||
// On failure try the next experiment.
|
||||
}
|
||||
if (!applicable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this._log.debug("evaluateExperiments() - activating experiment " + id);
|
||||
try {
|
||||
yield experiment.start();
|
||||
activeChanged = true;
|
||||
activeExperiment = experiment;
|
||||
this._dirty = true;
|
||||
break;
|
||||
} catch (e) {
|
||||
// On failure, clean up the best we can and try the next experiment.
|
||||
this._log.error("evaluateExperiments() - Unable to start experiment: " + e.message);
|
||||
experiment._enabled = false;
|
||||
yield experiment.reconcileAddonState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gPrefs.set(PREF_ACTIVE_EXPERIMENT, activeExperiment != null);
|
||||
|
||||
if (activeChanged) {
|
||||
Services.obs.notifyObservers(null, OBSERVER_TOPIC, null);
|
||||
}
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
if (activeExperiment) {
|
||||
if ("@mozilla.org/toolkit/crash-reporter;1" in Cc && activeExperiment) {
|
||||
gCrashReporter.annotateCrashReport("ActiveExperiment", activeExperiment.id);
|
||||
}
|
||||
#endif
|
||||
},
|
||||
|
||||
/*
|
||||
@ -1127,7 +1142,7 @@ Experiments.ExperimentEntry = function (policy) {
|
||||
"Browser.Experiments.Experiments",
|
||||
"ExperimentEntry #" + gExperimentEntryCounter++ + "::");
|
||||
|
||||
// Is this experiment running?
|
||||
// Is the experiment supposed to be running.
|
||||
this._enabled = false;
|
||||
// When this experiment was started, if ever.
|
||||
this._startDate = null;
|
||||
@ -1198,6 +1213,11 @@ Experiments.ExperimentEntry.prototype = {
|
||||
"_endDate",
|
||||
]),
|
||||
|
||||
ADDON_CHANGE_NONE: 0,
|
||||
ADDON_CHANGE_INSTALL: 1,
|
||||
ADDON_CHANGE_UNINSTALL: 2,
|
||||
ADDON_CHANGE_ENABLE: 4,
|
||||
|
||||
/*
|
||||
* Initialize entry from the manifest.
|
||||
* @param data The experiment data from the manifest.
|
||||
@ -1487,26 +1507,18 @@ Experiments.ExperimentEntry.prototype = {
|
||||
|
||||
/*
|
||||
* Start running the experiment.
|
||||
*
|
||||
* @return Promise<> Resolved when the operation is complete.
|
||||
*/
|
||||
start: function () {
|
||||
start: Task.async(function* () {
|
||||
this._log.trace("start() for " + this.id);
|
||||
|
||||
return Task.spawn(function* ExperimentEntry_start_task() {
|
||||
let addons = yield installedExperimentAddons();
|
||||
if (addons.length > 0) {
|
||||
this._log.error("start() - there are already "
|
||||
+ addons.length + " experiment addons installed");
|
||||
yield uninstallAddons(addons);
|
||||
}
|
||||
|
||||
yield this._installAddon();
|
||||
gPrefs.set(PREF_ACTIVE_EXPERIMENT, true);
|
||||
}.bind(this));
|
||||
},
|
||||
this._enabled = true;
|
||||
return yield this.reconcileAddonState();
|
||||
}),
|
||||
|
||||
// Async install of the addon for this experiment, part of the start task above.
|
||||
_installAddon: function* () {
|
||||
_installAddon: Task.async(function* () {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let hash = this._policy.ignoreHashes ? null : this._manifestData.xpiHash;
|
||||
@ -1605,75 +1617,105 @@ Experiments.ExperimentEntry.prototype = {
|
||||
install.addListener(listener);
|
||||
install.install();
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
return yield deferred.promise;
|
||||
}),
|
||||
|
||||
/*
|
||||
/**
|
||||
* Stop running the experiment if it is active.
|
||||
* @param terminationKind (optional) The termination kind, e.g. USERDISABLED or EXPIRED.
|
||||
* @param terminationReason (optional) The termination reason details for
|
||||
* termination kind RECHECK.
|
||||
*
|
||||
* @param terminationKind (optional)
|
||||
* The termination kind, e.g. ADDON_UNINSTALLED or EXPIRED.
|
||||
* @param terminationReason (optional)
|
||||
* The termination reason details for termination kind RECHECK.
|
||||
* @return Promise<> Resolved when the operation is complete.
|
||||
*/
|
||||
stop: function (terminationKind, terminationReason) {
|
||||
stop: Task.async(function* (terminationKind, terminationReason) {
|
||||
this._log.trace("stop() - id=" + this.id + ", terminationKind=" + terminationKind);
|
||||
if (!this._enabled) {
|
||||
this._log.warning("stop() - experiment not enabled: " + id);
|
||||
return Promise.reject();
|
||||
throw new Error("Must not call stop() on an inactive experiment.");
|
||||
}
|
||||
|
||||
this._enabled = false;
|
||||
gPrefs.set(PREF_ACTIVE_EXPERIMENT, false);
|
||||
|
||||
let deferred = Promise.defer();
|
||||
let updateDates = () => {
|
||||
let now = this._policy.now();
|
||||
this._lastChangedDate = now;
|
||||
this._endDate = now;
|
||||
};
|
||||
let changes = yield this.reconcileAddonState();
|
||||
let now = this._policy.now();
|
||||
this._lastChangedDate = now;
|
||||
this._endDate = now;
|
||||
this._logTermination(terminationKind, terminationReason);
|
||||
|
||||
this._getAddon().then((addon) => {
|
||||
if (!addon) {
|
||||
let message = "could not get Addon for " + this.id;
|
||||
this._log.warn("stop() - " + message);
|
||||
updateDates();
|
||||
deferred.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
updateDates();
|
||||
this._logTermination(terminationKind, terminationReason);
|
||||
deferred.resolve(uninstallAddons([addon]));
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
return changes;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Try to ensure this experiment is active.
|
||||
* Reconcile the state of the add-on against what it's supposed to be.
|
||||
*
|
||||
* The returned promise will be resolved if the experiment is active
|
||||
* in the Addon Manager or rejected if it isn't.
|
||||
* If we are active, ensure the add-on is enabled and up to date.
|
||||
*
|
||||
* @return Promise<>
|
||||
* If we are inactive, ensure the add-on is not installed.
|
||||
*/
|
||||
ensureActive: Task.async(function* () {
|
||||
this._log.trace("ensureActive() for " + this.id);
|
||||
reconcileAddonState: Task.async(function* () {
|
||||
this._log.trace("reconcileAddonState()");
|
||||
|
||||
if (!this._enabled) {
|
||||
if (!this._addonId) {
|
||||
this._log.trace("reconcileAddonState() - Experiment is not enabled and " +
|
||||
"has no add-on. Doing nothing.");
|
||||
return this.ADDON_CHANGE_NONE;
|
||||
}
|
||||
|
||||
let addon = yield this._getAddon();
|
||||
if (!addon) {
|
||||
this._log.trace("reconcileAddonState() - Inactive experiment has no " +
|
||||
"add-on. Doing nothing.");
|
||||
return this.ADDON_CHANGE_NONE;
|
||||
}
|
||||
|
||||
this._log.info("reconcileAddonState() - Uninstalling add-on for inactive " +
|
||||
"experiment: " + addon.id);
|
||||
gActiveUninstallAddonIDs.add(addon.id);
|
||||
yield uninstallAddons([addon]);
|
||||
gActiveUninstallAddonIDs.delete(addon.id);
|
||||
return this.ADDON_CHANGE_UNINSTALL;
|
||||
}
|
||||
|
||||
// If we get here, we're supposed to be active.
|
||||
|
||||
let changes = 0;
|
||||
|
||||
// That requires an add-on.
|
||||
let currentAddon = yield this._getAddon();
|
||||
|
||||
// If we have an add-on but it isn't up to date, uninstall it
|
||||
// (to prepare for reinstall).
|
||||
if (currentAddon && this._needsUpdate) {
|
||||
this._log.info("reconcileAddonState() - Uninstalling add-on because update " +
|
||||
"needed: " + currentAddon.id);
|
||||
gActiveUninstallAddonIDs.add(currentAddon.id);
|
||||
yield uninstallAddons([currentAddon]);
|
||||
gActiveUninstallAddonIDs.delete(currentAddon.id);
|
||||
changes |= this.ADDON_CHANGE_UNINSTALL;
|
||||
}
|
||||
|
||||
if (!currentAddon || this._needsUpdate) {
|
||||
this._log.info("reconcileAddonState() - Installing add-on.");
|
||||
yield this._installAddon();
|
||||
changes |= this.ADDON_CHANGE_INSTALL;
|
||||
}
|
||||
|
||||
let addon = yield this._getAddon();
|
||||
if (!addon) {
|
||||
this._log.warn("Experiment is not installed: " + this._addonId);
|
||||
throw new Error("Experiment is not installed: " + this._addonId);
|
||||
throw new Error("Could not obtain add-on for experiment that should be " +
|
||||
"enabled.");
|
||||
}
|
||||
|
||||
// User disabled likely means the experiment is disabled at startup,
|
||||
// since the permissions don't allow it to be disabled by the user.
|
||||
// If we have the add-on and it is enabled, we are done.
|
||||
if (!addon.userDisabled) {
|
||||
return;
|
||||
return changes;
|
||||
}
|
||||
|
||||
let deferred = Promise.defer();
|
||||
|
||||
// Else we need to enable it.
|
||||
let listener = {
|
||||
onEnabled: enabledAddon => {
|
||||
if (enabledAddon.id != addon.id) {
|
||||
@ -1689,7 +1731,11 @@ Experiments.ExperimentEntry.prototype = {
|
||||
AddonManager.addAddonListener(listener);
|
||||
addon.userDisabled = false;
|
||||
yield deferred.promise;
|
||||
}),
|
||||
changes |= this.ADDON_CHANGE_ENABLE;
|
||||
|
||||
this._log.info("Add-on has been enabled: " + addon.id);
|
||||
return changes;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Obtain the underlying Addon from the Addon Manager.
|
||||
@ -1697,6 +1743,10 @@ Experiments.ExperimentEntry.prototype = {
|
||||
* @return Promise<Addon|null>
|
||||
*/
|
||||
_getAddon: function () {
|
||||
if (!this._addonId) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
let deferred = Promise.defer();
|
||||
|
||||
AddonManager.getAddonByID(this._addonId, deferred.resolve);
|
||||
@ -1722,43 +1772,18 @@ Experiments.ExperimentEntry.prototype = {
|
||||
TelemetryLog.log(TELEMETRY_LOG.TERMINATION_KEY, data);
|
||||
},
|
||||
|
||||
/*
|
||||
* Stop if experiment stop criteria are met.
|
||||
* @return Promise<boolean> Resolved when done stopping or checking,
|
||||
* the value indicates whether it was stopped.
|
||||
/**
|
||||
* Determine whether an active experiment should be stopped.
|
||||
*/
|
||||
maybeStop: function () {
|
||||
this._log.trace("maybeStop()");
|
||||
return Task.spawn(function* ExperimentEntry_maybeStop_task() {
|
||||
if (!gExperimentsEnabled) {
|
||||
this._log.warn("maybeStop() - should not get here");
|
||||
yield this.stop(TELEMETRY_LOG.TERMINATION.FROM_API);
|
||||
return true;
|
||||
}
|
||||
shouldStop: function () {
|
||||
if (!this._enabled) {
|
||||
throw new Error("shouldStop must not be called on disabled experiments.");
|
||||
}
|
||||
|
||||
let result = yield this._shouldStop();
|
||||
if (result.shouldStop) {
|
||||
let expireReasons = ["endTime", "maxActiveSeconds"];
|
||||
if (expireReasons.indexOf(result.reason[0]) != -1) {
|
||||
yield this.stop(TELEMETRY_LOG.TERMINATION.EXPIRED);
|
||||
} else {
|
||||
yield this.stop(TELEMETRY_LOG.TERMINATION.RECHECK, result.reason);
|
||||
}
|
||||
}
|
||||
|
||||
return result.shouldStop;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
_shouldStop: function () {
|
||||
let data = this._manifestData;
|
||||
let now = this._policy.now() / 1000; // The manifest times are in seconds.
|
||||
let maxActiveSec = data.maxActiveSeconds || 0;
|
||||
|
||||
if (!this._enabled) {
|
||||
return Promise.resolve({shouldStop: false});
|
||||
}
|
||||
|
||||
let deferred = Promise.defer();
|
||||
this.isApplicable().then(
|
||||
() => deferred.resolve({shouldStop: false}),
|
||||
|
@ -9,7 +9,7 @@ EXTRA_COMPONENTS += [
|
||||
|
||||
JS_MODULES_PATH = 'modules/experiments'
|
||||
|
||||
EXTRA_PP_JS_MODULES += [
|
||||
EXTRA_JS_MODULES += [
|
||||
'Experiments.jsm',
|
||||
]
|
||||
|
||||
|
@ -102,7 +102,8 @@ add_task(function* test_startStop() {
|
||||
Assert.equal(result.applicable, true, "Experiment should now be applicable.");
|
||||
Assert.equal(experiment.enabled, false, "Experiment should not be enabled.");
|
||||
|
||||
yield experiment.start();
|
||||
let changes = yield experiment.start();
|
||||
Assert.equal(changes, experiment.ADDON_CHANGE_INSTALL, "Add-on was installed.");
|
||||
addons = yield getExperimentAddons();
|
||||
Assert.equal(experiment.enabled, true, "Experiment should now be enabled.");
|
||||
Assert.equal(addons.length, 1, "1 experiment add-on is installed.");
|
||||
@ -110,12 +111,14 @@ add_task(function* test_startStop() {
|
||||
Assert.equal(addons[0].userDisabled, false, "The add-on is not userDisabled.");
|
||||
Assert.ok(addons[0].isActive, "The add-on is active.");
|
||||
|
||||
yield experiment.stop();
|
||||
changes = yield experiment.stop();
|
||||
Assert.equal(changes, experiment.ADDON_CHANGE_UNINSTALL, "Add-on was uninstalled.");
|
||||
addons = yield getExperimentAddons();
|
||||
Assert.equal(experiment.enabled, false, "Experiment should not be enabled.");
|
||||
Assert.equal(addons.length, 0, "Experiment should be uninstalled from the Addon Manager.");
|
||||
|
||||
yield experiment.start();
|
||||
changes = yield experiment.start();
|
||||
Assert.equal(changes, experiment.ADDON_CHANGE_INSTALL, "Add-on was installed.");
|
||||
addons = yield getExperimentAddons();
|
||||
Assert.equal(experiment.enabled, true, "Experiment should now be enabled.");
|
||||
Assert.equal(addons.length, 1, "1 experiment add-on is installed.");
|
||||
@ -123,20 +126,18 @@ add_task(function* test_startStop() {
|
||||
Assert.equal(addons[0].userDisabled, false, "The add-on is not userDisabled.");
|
||||
Assert.ok(addons[0].isActive, "The add-on is active.");
|
||||
|
||||
let result = yield experiment._shouldStop();
|
||||
let result = yield experiment.shouldStop();
|
||||
Assert.equal(result.shouldStop, false, "shouldStop should be false.");
|
||||
let maybeStop = yield experiment.maybeStop();
|
||||
Assert.equal(maybeStop, false, "Experiment should not have been stopped.");
|
||||
Assert.equal(experiment.enabled, true, "Experiment should be enabled.");
|
||||
addons = yield getExperimentAddons();
|
||||
Assert.equal(addons.length, 1, "Experiment still in add-ons manager.");
|
||||
Assert.ok(addons[0].isActive, "The add-on is still active.");
|
||||
|
||||
defineNow(gPolicy, futureDate(endDate, MS_IN_ONE_DAY));
|
||||
result = yield experiment._shouldStop();
|
||||
result = yield experiment.shouldStop();
|
||||
Assert.equal(result.shouldStop, true, "shouldStop should now be true.");
|
||||
maybeStop = yield experiment.maybeStop();
|
||||
Assert.equal(maybeStop, true, "Experiment should have been stopped.");
|
||||
changes = yield experiment.stop();
|
||||
Assert.equal(changes, experiment.ADDON_CHANGE_UNINSTALL, "Add-on should be uninstalled.");
|
||||
Assert.equal(experiment.enabled, false, "Experiment should be disabled.");
|
||||
addons = yield getExperimentAddons();
|
||||
Assert.equal(addons.length, 0, "Experiment add-on is uninstalled.");
|
||||
|
@ -424,7 +424,7 @@ add_task(function* test_disableExperiment() {
|
||||
|
||||
now = futureDate(now, 1 * MS_IN_ONE_DAY);
|
||||
defineNow(gPolicy, now);
|
||||
yield experiments.disableExperiment();
|
||||
yield experiments.disableExperiment("foo");
|
||||
|
||||
list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
|
||||
@ -749,7 +749,7 @@ add_task(function* test_userDisabledAndUpdated() {
|
||||
|
||||
now = futureDate(now, 20 * MS_IN_ONE_DAY);
|
||||
defineNow(gPolicy, now);
|
||||
yield experiments.disableExperiment();
|
||||
yield experiments.disableExperiment("foo");
|
||||
Assert.equal(observerFireCount, ++expectedObserverFireCount,
|
||||
"Experiments observer should have been called.");
|
||||
|
||||
@ -1405,7 +1405,7 @@ add_task(function* testForeignExperimentInstall() {
|
||||
let addons = yield getExperimentAddons();
|
||||
Assert.equal(addons.length, 0, "Precondition: No experiment add-ons present.");
|
||||
|
||||
let failed;
|
||||
let failed = false;
|
||||
try {
|
||||
yield AddonTestUtils.installXPIFromURL(gDataRoot + EXPERIMENT1_XPI_NAME, EXPERIMENT1_XPI_SHA1);
|
||||
} catch (ex) {
|
||||
|
@ -243,7 +243,7 @@ add_task(function* test_cache() {
|
||||
|
||||
// Cleanup.
|
||||
|
||||
yield experiments.disableExperiment();
|
||||
yield experiments._toggleExperimentsEnabled(false);
|
||||
yield experiments.uninit();
|
||||
yield removeCacheFile();
|
||||
});
|
||||
|
@ -6,7 +6,7 @@
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
Cu.import("resource://gre/modules/TelemetryLog.jsm");
|
||||
Cu.import("resource://gre/modules/TelemetryPing.jsm");
|
||||
Cu.import("resource:///modules/experiments/Experiments.jsm");
|
||||
let bsp = Cu.import("resource:///modules/experiments/Experiments.jsm");
|
||||
|
||||
|
||||
const FILE_MANIFEST = "experiments.manifest";
|
||||
@ -25,25 +25,7 @@ let gPolicy = null;
|
||||
let gManifestObject = null;
|
||||
let gManifestHandlerURI = null;
|
||||
|
||||
const TLOG = {
|
||||
// log(key, [kind, experimentId, details])
|
||||
ACTIVATION_KEY: "EXPERIMENT_ACTIVATION",
|
||||
ACTIVATION: {
|
||||
ACTIVATED: "ACTIVATED",
|
||||
INSTALL_FAILURE: "INSTALL_FAILURE",
|
||||
REJECTED: "REJECTED",
|
||||
},
|
||||
|
||||
// log(key, [kind, experimentId, optionalDetails...])
|
||||
TERMINATION_KEY: "EXPERIMENT_TERMINATION",
|
||||
TERMINATION: {
|
||||
USERDISABLED: "USERDISABLED",
|
||||
FROM_API: "FROM_API",
|
||||
EXPIRED: "EXPIRED",
|
||||
RECHECK: "RECHECK",
|
||||
},
|
||||
};
|
||||
|
||||
const TLOG = bsp.TELEMETRY_LOG;
|
||||
|
||||
let gGlobalScope = this;
|
||||
function loadAddonManager() {
|
||||
@ -263,12 +245,12 @@ add_task(function* test_telemetryBasics() {
|
||||
checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY,
|
||||
[TLOG.ACTIVATION.ACTIVATED, EXPERIMENT2_ID]);
|
||||
|
||||
// Fake user-disable of an experiment.
|
||||
// Fake user uninstall of experiment via add-on manager.
|
||||
|
||||
now = futureDate(now, MS_IN_ONE_DAY);
|
||||
defineNow(gPolicy, now);
|
||||
|
||||
yield experiments.disableExperiment();
|
||||
yield experiments.disableExperiment(TLOG.TERMINATION.ADDON_UNINSTALLED);
|
||||
list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 2, "Experiment list should have 2 entries.");
|
||||
|
||||
@ -276,7 +258,7 @@ add_task(function* test_telemetryBasics() {
|
||||
log = TelemetryPing.getPayload().log;
|
||||
Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
|
||||
checkEvent(log[log.length-1], TLOG.TERMINATION_KEY,
|
||||
[TLOG.TERMINATION.USERDISABLED, EXPERIMENT2_ID]);
|
||||
[TLOG.TERMINATION.ADDON_UNINSTALLED, EXPERIMENT2_ID]);
|
||||
|
||||
// Trigger update with experiment 1a ready to start.
|
||||
|
||||
@ -295,12 +277,12 @@ add_task(function* test_telemetryBasics() {
|
||||
checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY,
|
||||
[TLOG.ACTIVATION.ACTIVATED, EXPERIMENT3_ID]);
|
||||
|
||||
// Trigger non-user-disable of an experiment via the API
|
||||
// Trigger disable of an experiment via the API.
|
||||
|
||||
now = futureDate(now, MS_IN_ONE_DAY);
|
||||
defineNow(gPolicy, now);
|
||||
|
||||
yield experiments.disableExperiment(false);
|
||||
yield experiments.disableExperiment(TLOG.TERMINATION.FROM_API);
|
||||
list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 3, "Experiment list should have 3 entries.");
|
||||
|
||||
|
@ -230,6 +230,9 @@ Var ControlRightPX
|
||||
|
||||
!include "defines.nsi"
|
||||
|
||||
; Must be included after defines.nsi
|
||||
!include "locale-fonts.nsh"
|
||||
|
||||
; The OFFICIAL define is a workaround to support different urls for Release and
|
||||
; Beta since they share the same branding when building with other branches that
|
||||
; set the update channel to beta.
|
||||
@ -416,9 +419,27 @@ Function .onInit
|
||||
!endif
|
||||
StrCpy $WasOptionsButtonClicked "0"
|
||||
|
||||
CreateFont $FontBlurb "$(^Font)" "12" "500"
|
||||
CreateFont $FontNormal "$(^Font)" "11" "500"
|
||||
CreateFont $FontItalic "$(^Font)" "11" "500" /ITALIC
|
||||
StrCpy $0 ""
|
||||
!ifdef FONT_FILE1
|
||||
${If} ${FileExists} "$FONTS\${FONT_FILE1}"
|
||||
StrCpy $0 "${FONT_NAME1}"
|
||||
${EndIf}
|
||||
!endif
|
||||
|
||||
!ifdef FONT_FILE2
|
||||
${If} $0 == ""
|
||||
${AndIf} ${FileExists} "$FONTS\${FONT_FILE2}"
|
||||
StrCpy $0 "${FONT_NAME2}"
|
||||
${EndIf}
|
||||
!endif
|
||||
|
||||
${If} $0 == ""
|
||||
StrCpy $0 "$(^Font)"
|
||||
${EndIf}
|
||||
|
||||
CreateFont $FontBlurb "$0" "12" "500"
|
||||
CreateFont $FontNormal "$0" "11" "500"
|
||||
CreateFont $FontItalic "$0" "11" "500" /ITALIC
|
||||
|
||||
InitPluginsDir
|
||||
File /oname=$PLUGINSDIR\bgintro.bmp "bgintro.bmp"
|
||||
|
@ -13,6 +13,7 @@
|
||||
<!ENTITY port "Port:">
|
||||
<!ENTITY connect "Connect">
|
||||
<!ENTITY connecting "Connecting…">
|
||||
<!ENTITY availableAddons "Available remote add-ons:">
|
||||
<!ENTITY availableTabs "Available remote tabs:">
|
||||
<!ENTITY availableProcesses "Available remote processes:">
|
||||
<!ENTITY connectionError "Error:">
|
||||
|
@ -140,14 +140,6 @@ function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevi
|
||||
};
|
||||
|
||||
let secondaryActions = [
|
||||
{
|
||||
label: stringBundle.getString("getUserMedia.always.label"),
|
||||
accessKey: stringBundle.getString("getUserMedia.always.accesskey"),
|
||||
callback: function () {
|
||||
// don't save unless secure load!
|
||||
mainAction.callback(aSecure);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: stringBundle.getString("getUserMedia.denyRequest.label"),
|
||||
accessKey: stringBundle.getString("getUserMedia.denyRequest.accesskey"),
|
||||
@ -160,8 +152,8 @@ function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevi
|
||||
accessKey: stringBundle.getString("getUserMedia.never.accesskey"),
|
||||
callback: function () {
|
||||
denyRequest(aCallID);
|
||||
// Let someone save "Never" for http sites so that they can be stopped from
|
||||
// bothering you with doorhangers
|
||||
// Let someone save "Never" for http sites so that they can be stopped from
|
||||
// bothering you with doorhangers.
|
||||
let perms = Services.perms;
|
||||
if (audioDevices.length)
|
||||
perms.add(uri, "microphone", perms.DENY_ACTION);
|
||||
@ -171,6 +163,17 @@ function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevi
|
||||
}
|
||||
];
|
||||
|
||||
if (aSecure) {
|
||||
// Don't show the 'Always' action if the connection isn't secure.
|
||||
secondaryActions.unshift({
|
||||
label: stringBundle.getString("getUserMedia.always.label"),
|
||||
accessKey: stringBundle.getString("getUserMedia.always.accesskey"),
|
||||
callback: function () {
|
||||
mainAction.callback(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let options = {
|
||||
eventCallback: function(aTopic, aNewBrowser) {
|
||||
if (aTopic == "swapping")
|
||||
|
@ -35,6 +35,11 @@
|
||||
moveY 3.4s linear 0s infinite alternate;
|
||||
}
|
||||
|
||||
#PanelUI-popup #PanelUI-contents:-moz-locale-dir(rtl):empty::before {
|
||||
animation: moveXRTL 3.05s linear 0s infinite alternate,
|
||||
moveY 3.4s linear 0s infinite alternate;
|
||||
}
|
||||
|
||||
#PanelUI-popup #PanelUI-contents:empty:hover::before {
|
||||
background-image: url(chrome://browser/skin/customizableui/whimsy.png);
|
||||
}
|
||||
@ -53,6 +58,12 @@
|
||||
/* These values are adjusted for the padding on the panel. */
|
||||
from { margin-left: -15px; } to { margin-left: calc(100% - 49px); }
|
||||
}
|
||||
|
||||
@keyframes moveXRTL {
|
||||
/* These values are adjusted for the padding on the panel. */
|
||||
from { margin-right: -15px; } to { margin-right: calc(100% - 49px); }
|
||||
}
|
||||
|
||||
@keyframes moveY {
|
||||
/* These values are adjusted for the padding and height of the panel. */
|
||||
from { margin-top: -.5em; } to { margin-top: calc(64px - .5em); }
|
||||
|
@ -293,12 +293,21 @@ case "$target" in
|
||||
if test ! -d "$android_platform_tools" ; then
|
||||
android_platform_tools="$android_sdk"/tools # SDK Tools < r8
|
||||
fi
|
||||
# The build tools got moved around to different directories in
|
||||
# SDK Tools r22. Try to locate them.
|
||||
|
||||
dnl The build tools got moved around to different directories in SDK
|
||||
dnl Tools r22. Try to locate them. This is awful, but, from
|
||||
dnl http://stackoverflow.com/a/4495368, the following sorts versions
|
||||
dnl of the form x.y.z.a.b from newest to oldest:
|
||||
dnl sort -t. -k 1,1nr -k 2,2nr -k 3,3nr -k 4,4nr -k 5,5nr
|
||||
dnl We want to favour the newer versions that start with 'android-';
|
||||
dnl that's what the sed is about.
|
||||
dnl We might iterate over directories that aren't build-tools at all;
|
||||
dnl we use the presence of aapt as a marker.
|
||||
AC_MSG_CHECKING([for android build-tools directory])
|
||||
android_build_tools=""
|
||||
for suffix in android-4.4 android-4.3 android-4.2.2 19.0.3 19.0.2 19.0.0 18.1.0 18.0.1 18.0.0 17.0.0; do
|
||||
tools_directory="$android_sdk_root/build-tools/$suffix"
|
||||
if test -d "$tools_directory" ; then
|
||||
for suffix in `ls "$android_sdk_root/build-tools" | sed -e "s,android-,999.," | sort -t. -k 1,1nr -k 2,2nr -k 3,3nr -k 4,4nr -k 5,5nr`; do
|
||||
tools_directory=`echo "$android_sdk_root/build-tools/$suffix" | sed -e "s,999.,android-,"`
|
||||
if test -d "$tools_directory" -a -f "$tools_directory/aapt"; then
|
||||
android_build_tools="$tools_directory"
|
||||
break
|
||||
fi
|
||||
@ -306,6 +315,13 @@ case "$target" in
|
||||
if test -z "$android_build_tools" ; then
|
||||
android_build_tools="$android_platform_tools" # SDK Tools < r22
|
||||
fi
|
||||
|
||||
if test -d "$android_build_tools" -a -f "$android_build_tools/aapt"; then
|
||||
AC_MSG_RESULT([$android_build_tools])
|
||||
else
|
||||
AC_MSG_ERROR([not found. Please check your SDK for the subdirectory of build-tools. With the current configuration, it should be in $android_sdk_root/build_tools])
|
||||
fi
|
||||
|
||||
ANDROID_SDK="${android_sdk}"
|
||||
ANDROID_SDK_ROOT="${android_sdk_root}"
|
||||
if test -e "${ANDROID_SDK_ROOT}/extras/android/compatibility/v4/android-support-v4.jar" ; then
|
||||
|
@ -288,6 +288,10 @@ DOMInterfaces = {
|
||||
'nativeType': 'nsDOMDataChannel',
|
||||
},
|
||||
|
||||
'DataStoreCursor': {
|
||||
'wrapperCache': False,
|
||||
},
|
||||
|
||||
'DedicatedWorkerGlobalScope': {
|
||||
'headerFile': 'mozilla/dom/WorkerScope.h',
|
||||
'workers': True,
|
||||
|
@ -603,40 +603,39 @@ BluetoothHfpManager::ProcessAtChld(bthf_chld_type_t aChld)
|
||||
message.AppendInt((int)aChld);
|
||||
BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER,
|
||||
NS_ConvertUTF8toUTF16(message));
|
||||
SendResponse(BTHF_AT_RESPONSE_OK);
|
||||
}
|
||||
|
||||
void BluetoothHfpManager::ProcessDialCall(char *aNumber)
|
||||
{
|
||||
nsAutoCString message(aNumber);
|
||||
|
||||
// There are three cases based on aNumber,
|
||||
// 1) Empty value: Redial, BLDN
|
||||
// 2) >xxx: Memory dial, ATD>xxx
|
||||
// 3) xxx: Normal dial, ATDxxx
|
||||
// We need to respond OK/Error for dial requests for every case listed above,
|
||||
// 1) and 2): Respond in either RespondToBLDNTask or
|
||||
// HandleCallStateChanged()
|
||||
// 3): Respond here
|
||||
if (message.IsEmpty()) {
|
||||
// Redial: BLDN
|
||||
mDialingRequestProcessed = false;
|
||||
BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER,
|
||||
NS_LITERAL_STRING("BLDN"));
|
||||
} else {
|
||||
BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::POST_TASK_RESPOND_TO_BLDN);
|
||||
} else if (message[0] == '>') {
|
||||
mDialingRequestProcessed = false;
|
||||
nsAutoCString newMsg("ATD");
|
||||
|
||||
if (message[0] == '>') {
|
||||
// Memory dial: ATD>xxx
|
||||
mDialingRequestProcessed = false;
|
||||
newMsg += message;
|
||||
} else {
|
||||
// Dial number: ATDxxx
|
||||
int end = message.FindChar(';');
|
||||
if (end < 0) {
|
||||
BT_WARNING("Couldn't get the number to dial");
|
||||
SendResponse(BTHF_AT_RESPONSE_OK);
|
||||
return;
|
||||
}
|
||||
newMsg += nsDependentCSubstring(message, 0, end);
|
||||
}
|
||||
|
||||
newMsg += StringHead(message, message.Length() - 1);
|
||||
BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER,
|
||||
NS_ConvertUTF8toUTF16(newMsg));
|
||||
}
|
||||
|
||||
if (!mDialingRequestProcessed) {
|
||||
BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::POST_TASK_RESPOND_TO_BLDN);
|
||||
} else {
|
||||
nsAutoCString newMsg("ATD");
|
||||
newMsg += StringHead(message, message.Length() - 1);
|
||||
BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER,
|
||||
NS_ConvertUTF8toUTF16(newMsg));
|
||||
SendResponse(BTHF_AT_RESPONSE_OK);
|
||||
}
|
||||
}
|
||||
|
||||
@ -699,6 +698,8 @@ BluetoothHfpManager::ProcessAtClcc()
|
||||
|
||||
SendCLCC(mCdmaSecondCall, 2);
|
||||
}
|
||||
|
||||
SendResponse(BTHF_AT_RESPONSE_OK);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1098,6 +1099,13 @@ BluetoothHfpManager::HandleCallStateChanged(uint32_t aCallIndex,
|
||||
return;
|
||||
}
|
||||
|
||||
// aCallIndex can be UINT32_MAX for the pending outgoing call state update.
|
||||
// aCallIndex will be updated again after real call state changes. See Bug
|
||||
// 990467.
|
||||
if (aCallIndex == UINT32_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (aCallIndex >= mCurrentCallArray.Length()) {
|
||||
Call call;
|
||||
mCurrentCallArray.AppendElement(call);
|
||||
|
@ -1154,8 +1154,8 @@ AppendDeviceName(BluetoothSignal& aSignal)
|
||||
|
||||
bool success = sDBusConnection->SendWithReply(
|
||||
AppendDeviceNameReplyHandler::Callback, handler.get(), 1000,
|
||||
NS_ConvertUTF16toUTF8(devicePath).get(), DBUS_DEVICE_IFACE,
|
||||
"GetProperties", DBUS_TYPE_INVALID);
|
||||
BLUEZ_DBUS_BASE_IFC, NS_ConvertUTF16toUTF8(devicePath).get(),
|
||||
DBUS_DEVICE_IFACE, "GetProperties", DBUS_TYPE_INVALID);
|
||||
|
||||
NS_ENSURE_TRUE_VOID(success);
|
||||
|
||||
@ -1488,6 +1488,7 @@ private:
|
||||
|
||||
bool success = sDBusConnection->SendWithReply(
|
||||
RegisterAgentReplyHandler::Callback, handler.get(), -1,
|
||||
BLUEZ_DBUS_BASE_IFC,
|
||||
NS_ConvertUTF16toUTF8(sAdapterPath).get(),
|
||||
DBUS_ADAPTER_IFACE, "RegisterAgent",
|
||||
DBUS_TYPE_OBJECT_PATH, &agentPath,
|
||||
@ -1527,6 +1528,7 @@ public:
|
||||
|
||||
bool success = sDBusConnection->SendWithReply(
|
||||
DBusReplyHandler::Callback, handler.get(), -1,
|
||||
BLUEZ_DBUS_BASE_IFC,
|
||||
NS_ConvertUTF16toUTF8(sAdapterPath).get(),
|
||||
DBUS_ADAPTER_IFACE, "AddReservedServiceRecords",
|
||||
DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
|
||||
@ -1939,7 +1941,7 @@ public:
|
||||
*/
|
||||
if (sAdapterPath.IsEmpty()) {
|
||||
bool success = sDBusConnection->SendWithReply(OnDefaultAdapterReply, nullptr,
|
||||
1000, "/",
|
||||
1000, BLUEZ_DBUS_BASE_IFC, "/",
|
||||
DBUS_MANAGER_IFACE,
|
||||
"DefaultAdapter",
|
||||
DBUS_TYPE_INVALID);
|
||||
@ -2211,6 +2213,7 @@ protected:
|
||||
|
||||
bool success = sDBusConnection->SendWithReply(
|
||||
DefaultAdapterPathReplyHandler::Callback, handler.get(), 1000,
|
||||
BLUEZ_DBUS_BASE_IFC,
|
||||
NS_ConvertUTF16toUTF8(mAdapterPath).get(),
|
||||
DBUS_ADAPTER_IFACE, "GetProperties", DBUS_TYPE_INVALID);
|
||||
|
||||
@ -2274,7 +2277,7 @@ public:
|
||||
|
||||
bool success = sDBusConnection->SendWithReply(
|
||||
DefaultAdapterPathReplyHandler::Callback,
|
||||
handler.get(), 1000,
|
||||
handler.get(), 1000, BLUEZ_DBUS_BASE_IFC,
|
||||
"/", DBUS_MANAGER_IFACE, "DefaultAdapter",
|
||||
DBUS_TYPE_INVALID);
|
||||
NS_ENSURE_TRUE_VOID(success);
|
||||
@ -2342,6 +2345,7 @@ public:
|
||||
bool success = sDBusConnection->SendWithReply(
|
||||
OnSendDiscoveryMessageReply,
|
||||
static_cast<void*>(mRunnable.get()), -1,
|
||||
BLUEZ_DBUS_BASE_IFC,
|
||||
NS_ConvertUTF16toUTF8(sAdapterPath).get(),
|
||||
DBUS_ADAPTER_IFACE, mMessageName.get(),
|
||||
DBUS_TYPE_INVALID);
|
||||
@ -2420,8 +2424,8 @@ public:
|
||||
|
||||
bool success = sDBusConnection->SendWithReply(
|
||||
mCallback, static_cast<void*>(mServiceClass), -1,
|
||||
mObjectPath.get(), mInterface.get(), mMessage.get(),
|
||||
DBUS_TYPE_INVALID);
|
||||
BLUEZ_DBUS_BASE_IFC, mObjectPath.get(), mInterface.get(),
|
||||
mMessage.get(), DBUS_TYPE_INVALID);
|
||||
NS_ENSURE_TRUE_VOID(success);
|
||||
|
||||
mServiceClass.forget();
|
||||
@ -2607,7 +2611,7 @@ protected:
|
||||
|
||||
bool success = sDBusConnection->SendWithReply(
|
||||
BluetoothArrayOfDevicePropertiesReplyHandler::Callback,
|
||||
handler.get(), 1000,
|
||||
handler.get(), 1000, BLUEZ_DBUS_BASE_IFC,
|
||||
NS_ConvertUTF16toUTF8(mObjectPath).get(),
|
||||
DBUS_DEVICE_IFACE, "GetProperties",
|
||||
DBUS_TYPE_INVALID);
|
||||
@ -2735,7 +2739,7 @@ public:
|
||||
MOZ_ASSERT(!sAdapterPath.IsEmpty());
|
||||
|
||||
DBusMessage* msg =
|
||||
dbus_message_new_method_call("org.bluez",
|
||||
dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
|
||||
NS_ConvertUTF16toUTF8(sAdapterPath).get(),
|
||||
sBluetoothDBusIfaces[mType],
|
||||
"SetProperty");
|
||||
@ -2905,6 +2909,7 @@ public:
|
||||
// unregister it after pairing process is over
|
||||
bool success = sDBusConnection->SendWithReply(
|
||||
GetObjectPathCallback, static_cast<void*>(mRunnable), mTimeout,
|
||||
BLUEZ_DBUS_BASE_IFC,
|
||||
NS_ConvertUTF16toUTF8(sAdapterPath).get(),
|
||||
DBUS_ADAPTER_IFACE,
|
||||
"CreatePairedDevice",
|
||||
@ -2976,6 +2981,7 @@ public:
|
||||
|
||||
bool success = sDBusConnection->SendWithReply(
|
||||
OnRemoveDeviceReply, static_cast<void*>(mRunnable.get()), -1,
|
||||
BLUEZ_DBUS_BASE_IFC,
|
||||
NS_ConvertUTF16toUTF8(sAdapterPath).get(),
|
||||
DBUS_ADAPTER_IFACE, "RemoveDevice",
|
||||
DBUS_TYPE_OBJECT_PATH, &cstrDeviceObjectPath,
|
||||
@ -3498,6 +3504,7 @@ public:
|
||||
|
||||
bool success = sDBusConnection->SendWithReply(
|
||||
OnGetServiceChannelReplyHandler::Callback, handler, -1,
|
||||
BLUEZ_DBUS_BASE_IFC,
|
||||
NS_ConvertUTF16toUTF8(objectPath).get(),
|
||||
DBUS_DEVICE_IFACE, "GetServiceAttributeValue",
|
||||
DBUS_TYPE_STRING, &cstrServiceUUID,
|
||||
@ -3578,6 +3585,7 @@ public:
|
||||
|
||||
sDBusConnection->SendWithReply(DiscoverServicesCallback,
|
||||
(void*)callbackRunnable, -1,
|
||||
BLUEZ_DBUS_BASE_IFC,
|
||||
NS_ConvertUTF16toUTF8(objectPath).get(),
|
||||
DBUS_DEVICE_IFACE,
|
||||
"DiscoverServices",
|
||||
@ -3796,6 +3804,7 @@ public:
|
||||
|
||||
bool success = sDBusConnection->SendWithReply(
|
||||
GetVoidCallback, static_cast<void*>(mRunnable.get()), -1,
|
||||
BLUEZ_DBUS_BASE_IFC,
|
||||
objectPath.get(),
|
||||
DBUS_CTL_IFACE, "UpdateMetaData",
|
||||
DBUS_TYPE_STRING, &title,
|
||||
@ -3932,6 +3941,7 @@ public:
|
||||
|
||||
bool success = sDBusConnection->SendWithReply(
|
||||
GetVoidCallback, static_cast<void*>(mRunnable.get()), -1,
|
||||
BLUEZ_DBUS_BASE_IFC,
|
||||
objectPath.get(),
|
||||
DBUS_CTL_IFACE, "UpdatePlayStatus",
|
||||
DBUS_TYPE_UINT32, &mDuration,
|
||||
@ -4055,6 +4065,7 @@ public:
|
||||
|
||||
bool success = sDBusConnection->SendWithReply(
|
||||
ControlCallback, nullptr, -1,
|
||||
BLUEZ_DBUS_BASE_IFC,
|
||||
objectPath.get(),
|
||||
DBUS_CTL_IFACE, "UpdatePlayStatus",
|
||||
DBUS_TYPE_UINT32, &mDuration,
|
||||
@ -4121,6 +4132,7 @@ public:
|
||||
|
||||
bool success = sDBusConnection->SendWithReply(
|
||||
ControlCallback, nullptr, -1,
|
||||
BLUEZ_DBUS_BASE_IFC,
|
||||
objectPath.get(),
|
||||
DBUS_CTL_IFACE, "UpdateNotification",
|
||||
DBUS_TYPE_UINT16, &eventId,
|
||||
|
188
dom/datastore/DataStore.cpp
Normal file
188
dom/datastore/DataStore.cpp
Normal file
@ -0,0 +1,188 @@
|
||||
/* 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/. */
|
||||
|
||||
#include "mozilla/dom/DataStore.h"
|
||||
#include "mozilla/dom/DataStoreCursor.h"
|
||||
#include "mozilla/dom/DataStoreBinding.h"
|
||||
#include "mozilla/dom/DataStoreImplBinding.h"
|
||||
#include "mozilla/dom/Navigator.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "AccessCheck.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(DataStore, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(DataStore, DOMEventTargetHelper)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DataStore)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_1(DataStore, mStore)
|
||||
|
||||
DataStore::DataStore(nsPIDOMWindow* aWindow)
|
||||
: DOMEventTargetHelper(aWindow)
|
||||
{
|
||||
}
|
||||
|
||||
already_AddRefed<DataStore>
|
||||
DataStore::Constructor(GlobalObject& aGlobal, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<DataStore> store = new DataStore(window);
|
||||
return store.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
DataStore::WrapObject(JSContext* aCx)
|
||||
{
|
||||
return DataStoreBinding::Wrap(aCx, this);
|
||||
}
|
||||
|
||||
/*static*/ bool
|
||||
DataStore::EnabledForScope(JSContext* aCx, JS::Handle<JSObject*> aObj)
|
||||
{
|
||||
// Only expose the interface when it is:
|
||||
// 1. enabled by the preference and
|
||||
// 2. accessed by the chrome codes in Gecko.
|
||||
return (Navigator::HasDataStoreSupport(aCx, aObj) &&
|
||||
nsContentUtils::ThreadsafeIsCallerChrome());
|
||||
}
|
||||
|
||||
void
|
||||
DataStore::GetName(nsAString& aName, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mStore);
|
||||
|
||||
nsAutoString name;
|
||||
mStore->GetName(name, aRv);
|
||||
aName.Assign(name);
|
||||
}
|
||||
|
||||
void
|
||||
DataStore::GetOwner(nsAString& aOwner, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mStore);
|
||||
|
||||
nsAutoString owner;
|
||||
mStore->GetOwner(owner, aRv);
|
||||
aOwner.Assign(owner);
|
||||
}
|
||||
|
||||
bool
|
||||
DataStore::GetReadOnly(ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mStore);
|
||||
|
||||
return mStore->GetReadOnly(aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
DataStore::Get(const Sequence<OwningStringOrUnsignedLong>& aId,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mStore);
|
||||
|
||||
return mStore->Get(aId, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
DataStore::Put(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aObj,
|
||||
const StringOrUnsignedLong& aId,
|
||||
const nsAString& aRevisionId,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mStore);
|
||||
|
||||
return mStore->Put(aObj, aId, aRevisionId, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
DataStore::Add(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aObj,
|
||||
const Optional<StringOrUnsignedLong>& aId,
|
||||
const nsAString& aRevisionId,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mStore);
|
||||
|
||||
return mStore->Add(aObj, aId, aRevisionId, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
DataStore::Remove(const StringOrUnsignedLong& aId,
|
||||
const nsAString& aRevisionId,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mStore);
|
||||
|
||||
return mStore->Remove(aId, aRevisionId, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
DataStore::Clear(const nsAString& aRevisionId, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mStore);
|
||||
|
||||
return mStore->Clear(aRevisionId, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
DataStore::GetRevisionId(nsAString& aRevisionId, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mStore);
|
||||
|
||||
nsAutoString revisionId;
|
||||
mStore->GetRevisionId(revisionId, aRv);
|
||||
aRevisionId.Assign(revisionId);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
DataStore::GetLength(ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mStore);
|
||||
|
||||
return mStore->GetLength(aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<DataStoreCursor>
|
||||
DataStore::Sync(const nsAString& aRevisionId, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mStore);
|
||||
|
||||
return mStore->Sync(aRevisionId, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
DataStore::SetDataStoreImpl(DataStoreImpl& aStore, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!mStore);
|
||||
|
||||
mStore = &aStore;
|
||||
mStore->SetEventTarget(*this, aRv);
|
||||
}
|
||||
|
||||
} //namespace dom
|
||||
} //namespace mozilla
|
95
dom/datastore/DataStore.h
Normal file
95
dom/datastore/DataStore.h
Normal file
@ -0,0 +1,95 @@
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_DataStore_h
|
||||
#define mozilla_dom_DataStore_h
|
||||
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class Promise;
|
||||
class DataStoreCursor;
|
||||
class DataStoreImpl;
|
||||
class StringOrUnsignedLong;
|
||||
class OwningStringOrUnsignedLong;
|
||||
|
||||
class DataStore MOZ_FINAL : public DOMEventTargetHelper
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DataStore,
|
||||
DOMEventTargetHelper)
|
||||
|
||||
explicit DataStore(nsPIDOMWindow* aWindow);
|
||||
|
||||
// WebIDL (internal functions)
|
||||
|
||||
static already_AddRefed<DataStore> Constructor(GlobalObject& aGlobal,
|
||||
ErrorResult& aRv);
|
||||
|
||||
virtual JSObject* WrapObject(JSContext *aCx) MOZ_OVERRIDE;
|
||||
|
||||
static bool EnabledForScope(JSContext* aCx, JS::Handle<JSObject*> aObj);
|
||||
|
||||
// WebIDL (public APIs)
|
||||
|
||||
void GetName(nsAString& aName, ErrorResult& aRv);
|
||||
|
||||
void GetOwner(nsAString& aOwner, ErrorResult& aRv);
|
||||
|
||||
bool GetReadOnly(ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise> Get(const Sequence<OwningStringOrUnsignedLong>& aId,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise> Put(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aObj,
|
||||
const StringOrUnsignedLong& aId,
|
||||
const nsAString& aRevisionId,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise> Add(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aObj,
|
||||
const Optional<StringOrUnsignedLong>& aId,
|
||||
const nsAString& aRevisionId,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise> Remove(const StringOrUnsignedLong& aId,
|
||||
const nsAString& aRevisionId,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise> Clear(const nsAString& aRevisionId,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void GetRevisionId(nsAString& aRevisionId, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise> GetLength(ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<DataStoreCursor> Sync(const nsAString& aRevisionId,
|
||||
ErrorResult& aRv);
|
||||
|
||||
IMPL_EVENT_HANDLER(change)
|
||||
|
||||
// This internal function (ChromeOnly) is aimed to make the DataStore keep a
|
||||
// reference to the DataStoreImpl which really implements the API's logic in
|
||||
// JS. We also need to let the DataStoreImpl implementation keep the event
|
||||
// target of DataStore, so that it can know where to fire the events.
|
||||
void SetDataStoreImpl(DataStoreImpl& aStore, ErrorResult& aRv);
|
||||
|
||||
protected:
|
||||
virtual ~DataStore() {}
|
||||
|
||||
private:
|
||||
nsRefPtr<DataStoreImpl> mStore;
|
||||
};
|
||||
|
||||
} //namespace dom
|
||||
} //namespace mozilla
|
||||
|
||||
#endif
|
71
dom/datastore/DataStoreCursor.cpp
Normal file
71
dom/datastore/DataStoreCursor.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
/* 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/. */
|
||||
|
||||
#include "mozilla/dom/DataStore.h"
|
||||
#include "mozilla/dom/DataStoreCursor.h"
|
||||
#include "mozilla/dom/DataStoreBinding.h"
|
||||
#include "mozilla/dom/DataStoreImplBinding.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DataStoreCursor, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DataStoreCursor, Release)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_1(DataStoreCursor, mCursor)
|
||||
|
||||
already_AddRefed<DataStoreCursor>
|
||||
DataStoreCursor::Constructor(GlobalObject& aGlobal, ErrorResult& aRv)
|
||||
{
|
||||
nsRefPtr<DataStoreCursor> cursor = new DataStoreCursor();
|
||||
return cursor.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
DataStoreCursor::WrapObject(JSContext* aCx)
|
||||
{
|
||||
return DataStoreCursorBinding::Wrap(aCx, this);
|
||||
}
|
||||
|
||||
already_AddRefed<DataStore>
|
||||
DataStoreCursor::GetStore(ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mCursor);
|
||||
|
||||
return mCursor->GetStore(aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
DataStoreCursor::Next(ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mCursor);
|
||||
|
||||
return mCursor->Next(aRv);
|
||||
}
|
||||
|
||||
void
|
||||
DataStoreCursor::Close(ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mCursor);
|
||||
|
||||
mCursor->Close(aRv);
|
||||
}
|
||||
|
||||
void
|
||||
DataStoreCursor::SetDataStoreCursorImpl(DataStoreCursorImpl& aCursor)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!mCursor);
|
||||
|
||||
mCursor = &aCursor;
|
||||
}
|
||||
|
||||
} //namespace dom
|
||||
} //namespace mozilla
|
61
dom/datastore/DataStoreCursor.h
Normal file
61
dom/datastore/DataStoreCursor.h
Normal file
@ -0,0 +1,61 @@
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_DataStoreCursor_h
|
||||
#define mozilla_dom_DataStoreCursor_h
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class Promise;
|
||||
class DataStore;
|
||||
class GlobalObject;
|
||||
class DataStoreCursorImpl;
|
||||
|
||||
class DataStoreCursor MOZ_FINAL
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(DataStoreCursor)
|
||||
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(DataStoreCursor)
|
||||
|
||||
// WebIDL (internal functions)
|
||||
|
||||
static already_AddRefed<DataStoreCursor> Constructor(GlobalObject& aGlobal,
|
||||
ErrorResult& aRv);
|
||||
|
||||
JSObject* WrapObject(JSContext *aCx);
|
||||
|
||||
// WebIDL (public APIs)
|
||||
|
||||
already_AddRefed<DataStore> GetStore(ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise> Next(ErrorResult& aRv);
|
||||
|
||||
void Close(ErrorResult& aRv);
|
||||
|
||||
// This internal function (ChromeOnly) is aimed to make the DataStoreCursor
|
||||
// keep a reference to the DataStoreCursorImpl which really implements the
|
||||
// API's logic in JS.
|
||||
void SetDataStoreCursorImpl(DataStoreCursorImpl& aCursor);
|
||||
|
||||
protected:
|
||||
virtual ~DataStoreCursor() {}
|
||||
|
||||
private:
|
||||
nsRefPtr<DataStoreCursorImpl> mCursor;
|
||||
};
|
||||
|
||||
} //namespace dom
|
||||
} //namespace mozilla
|
||||
|
||||
#endif
|
@ -77,13 +77,14 @@ function createDOMError(aWindow, aEvent) {
|
||||
|
||||
/* DataStoreCursor object */
|
||||
this.DataStoreCursor = function(aWindow, aDataStore, aRevisionId) {
|
||||
debug("DataStoreCursor created");
|
||||
this.init(aWindow, aDataStore, aRevisionId);
|
||||
}
|
||||
|
||||
this.DataStoreCursor.prototype = {
|
||||
classDescription: 'DataStoreCursor XPCOM Component',
|
||||
classID: Components.ID('{b6d14349-1eab-46b8-8513-584a7328a26b}'),
|
||||
contractID: '@mozilla.org/dom/datastore-cursor;1',
|
||||
contractID: '@mozilla.org/dom/datastore-cursor-impl;1',
|
||||
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsISupports]),
|
||||
|
||||
_window: null,
|
@ -23,7 +23,7 @@ const REVISION_VOID = "void";
|
||||
// and yet we don't know if it's too low or too high.
|
||||
const MAX_REQUESTS = 25;
|
||||
|
||||
Cu.import("resource://gre/modules/DataStoreCursor.jsm");
|
||||
Cu.import("resource://gre/modules/DataStoreCursorImpl.jsm");
|
||||
Cu.import("resource://gre/modules/DataStoreDB.jsm");
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
@ -67,7 +67,7 @@ this.DataStore = function(aWindow, aName, aOwner, aReadOnly) {
|
||||
this.DataStore.prototype = {
|
||||
classDescription: "DataStore XPCOM Component",
|
||||
classID: Components.ID("{db5c9602-030f-4bff-a3de-881a8de370f2}"),
|
||||
contractID: "@mozilla.org/dom/datastore;1",
|
||||
contractID: "@mozilla.org/dom/datastore-impl;1",
|
||||
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsISupports,
|
||||
Components.interfaces.nsIObserver]),
|
||||
|
||||
@ -81,6 +81,7 @@ this.DataStore.prototype = {
|
||||
_exposedObject: null,
|
||||
_cursor: null,
|
||||
_shuttingdown: false,
|
||||
_eventTarget: null,
|
||||
|
||||
init: function(aWindow, aName, aOwner, aReadOnly) {
|
||||
debug("DataStore init");
|
||||
@ -116,6 +117,10 @@ this.DataStore.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
setEventTarget: function(aEventTarget) {
|
||||
this._eventTarget = aEventTarget;
|
||||
},
|
||||
|
||||
newDBPromise: function(aTxnType, aFunction) {
|
||||
let self = this;
|
||||
return new this._window.Promise(function(aResolve, aReject) {
|
||||
@ -365,7 +370,7 @@ this.DataStore.prototype = {
|
||||
|
||||
let event = new self._window.DataStoreChangeEvent('change',
|
||||
aMessage.data.message);
|
||||
self.__DOM_IMPL__.dispatchEvent(event);
|
||||
self._eventTarget.dispatchEvent(event);
|
||||
}
|
||||
);
|
||||
},
|
||||
@ -519,19 +524,15 @@ this.DataStore.prototype = {
|
||||
);
|
||||
},
|
||||
|
||||
set onchange(aCallback) {
|
||||
debug("Set OnChange");
|
||||
this.__DOM_IMPL__.setEventHandler("onchange", aCallback);
|
||||
},
|
||||
|
||||
get onchange() {
|
||||
debug("Get OnChange");
|
||||
return this.__DOM_IMPL__.getEventHandler("onchange");
|
||||
},
|
||||
|
||||
sync: function(aRevisionId) {
|
||||
debug("Sync");
|
||||
this._cursor = new DataStoreCursor(this._window, this, aRevisionId);
|
||||
return this._window.DataStoreCursor._create(this._window, this._cursor);
|
||||
|
||||
let cursorImpl = this._window.DataStoreCursorImpl.
|
||||
_create(this._window, this._cursor);
|
||||
|
||||
let exposedCursor = new this._window.DataStoreCursor();
|
||||
exposedCursor.setDataStoreCursorImpl(cursorImpl);
|
||||
return exposedCursor;
|
||||
}
|
||||
};
|
@ -16,7 +16,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
Cu.import('resource://gre/modules/DataStore.jsm');
|
||||
Cu.import('resource://gre/modules/DataStoreImpl.jsm');
|
||||
Cu.import("resource://gre/modules/DataStoreDB.jsm");
|
||||
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
|
||||
|
||||
@ -352,10 +352,15 @@ DataStoreService.prototype = {
|
||||
for (let i = 0; i < aStores.length; ++i) {
|
||||
let obj = new DataStore(aWindow, aStores[i].name,
|
||||
aStores[i].owner, aStores[i].readOnly);
|
||||
let exposedObj = aWindow.DataStore._create(aWindow, obj);
|
||||
obj.exposedObject = exposedObj;
|
||||
|
||||
results.push(exposedObj);
|
||||
let storeImpl = aWindow.DataStoreImpl._create(aWindow, obj);
|
||||
|
||||
let exposedStore = new aWindow.DataStore();
|
||||
exposedStore.setDataStoreImpl(storeImpl);
|
||||
|
||||
obj.exposedObject = exposedStore;
|
||||
|
||||
results.push(exposedStore);
|
||||
|
||||
obj.retrieveRevisionId(
|
||||
function() {
|
||||
|
@ -10,17 +10,33 @@ XPIDL_SOURCES += [
|
||||
|
||||
XPIDL_MODULE = 'dom_datastore'
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'DataStore.h',
|
||||
'DataStoreCursor.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'DataStore.cpp',
|
||||
'DataStoreCursor.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/js/xpconnect/wrappers',
|
||||
]
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'DataStore.manifest',
|
||||
'DataStoreService.js',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'DataStore.jsm',
|
||||
'DataStoreChangeNotifier.jsm',
|
||||
'DataStoreCursor.jsm',
|
||||
'DataStoreCursorImpl.jsm',
|
||||
'DataStoreDB.jsm',
|
||||
'DataStoreImpl.jsm',
|
||||
'DataStoreServiceInternal.jsm',
|
||||
]
|
||||
|
||||
MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
@ -4,6 +4,8 @@
|
||||
# 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/.
|
||||
|
||||
TEST_DIRS += ['test']
|
||||
|
||||
PARALLEL_DIRS += ['interfaces']
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
|
5
dom/messages/test/mochitest.ini
Normal file
5
dom/messages/test/mochitest.ini
Normal file
@ -0,0 +1,5 @@
|
||||
[DEFAULT]
|
||||
skip-if = e10s
|
||||
|
||||
[test_bug_993732.html]
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
|
8
dom/messages/test/moz.build
Normal file
8
dom/messages/test/moz.build
Normal file
@ -0,0 +1,8 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
MOCHITEST_MANIFESTS += ['mochitest.ini']
|
||||
|
70
dom/messages/test/test_bug_993732.html
Normal file
70
dom/messages/test/test_bug_993732.html
Normal file
@ -0,0 +1,70 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test Bug 993732</title>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
"use strict";
|
||||
|
||||
// The syndrome of Bug 993732 is that the running app (either foreground or background)
|
||||
// is not able to receive system messages. Even worse, the app will be killed when the
|
||||
// listening system message is broadcast. So this test case uses the alarm message
|
||||
// to test if a running app can receive the system message.
|
||||
|
||||
function testAlarm(aMillisecondsFromNow) {
|
||||
var at = new Date();
|
||||
at.setTime(at.getTime() + aMillisecondsFromNow);
|
||||
|
||||
navigator.mozSetMessageHandler('alarm', function(message) {
|
||||
ok(true, "We got alarm message!");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
var domRequest;
|
||||
try {
|
||||
domRequest = navigator.mozAlarms.add(at, "honorTimezone", {});
|
||||
} catch (e) {
|
||||
ok(false,
|
||||
"Unexpected exception while adding alarm " + aMillisecondsFromNow + " ms from now.");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
domRequest.onsuccess = function(e) {
|
||||
// Waiting for alarm message.
|
||||
};
|
||||
domRequest.onerror = function(e) {
|
||||
ok(false, "Unable to add alarm for tomorrow`.");
|
||||
SimpleTest.finish();
|
||||
};
|
||||
}
|
||||
|
||||
function startTests() {
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.mozAlarms.enabled", true]]}, function() {
|
||||
// Currently applicable only on FxOS
|
||||
if (navigator.userAgent.indexOf("Mobile") != -1 &&
|
||||
navigator.appVersion.indexOf("Android") == -1)
|
||||
{
|
||||
testAlarm(10000);
|
||||
} else {
|
||||
ok(true, "mozAlarms on Firefox OS only.");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
SimpleTest.expectAssertions(0, 9);
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPermissions([{'type': 'alarms', 'allow': true, 'context': document}], startTests);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -6,58 +6,91 @@
|
||||
|
||||
typedef (DOMString or unsigned long) DataStoreKey;
|
||||
|
||||
// TODO Bug 957086 - The constructor and the setDataStoreImpl(...) will be
|
||||
// removed once the DataStore API is fully rewritten in C++,
|
||||
// which currently plays a role of C++ proxy directing to the
|
||||
// JS codes implemented by the DataStoreImpl WebIDL.
|
||||
|
||||
[Func="Navigator::HasDataStoreSupport",
|
||||
JSImplementation="@mozilla.org/dom/datastore;1"]
|
||||
ChromeConstructor]
|
||||
interface DataStore : EventTarget {
|
||||
// Returns the label of the DataSource.
|
||||
[GetterThrows]
|
||||
readonly attribute DOMString name;
|
||||
|
||||
// Returns the origin of the DataSource (e.g., 'facebook.com').
|
||||
// This value is the manifest URL of the owner app.
|
||||
[GetterThrows]
|
||||
readonly attribute DOMString owner;
|
||||
|
||||
// is readOnly a F(current_app, datastore) function? yes
|
||||
[GetterThrows]
|
||||
readonly attribute boolean readOnly;
|
||||
|
||||
// Promise<any>
|
||||
[Throws]
|
||||
Promise get(DataStoreKey... id);
|
||||
|
||||
// Promise<void>
|
||||
[Throws]
|
||||
Promise put(any obj, DataStoreKey id, optional DOMString revisionId = "");
|
||||
|
||||
// Promise<DataStoreKey>
|
||||
[Throws]
|
||||
Promise add(any obj, optional DataStoreKey id,
|
||||
optional DOMString revisionId = "");
|
||||
|
||||
// Promise<boolean>
|
||||
[Throws]
|
||||
Promise remove(DataStoreKey id, optional DOMString revisionId = "");
|
||||
|
||||
// Promise<void>
|
||||
[Throws]
|
||||
Promise clear(optional DOMString revisionId = "");
|
||||
|
||||
[GetterThrows]
|
||||
readonly attribute DOMString revisionId;
|
||||
|
||||
attribute EventHandler onchange;
|
||||
|
||||
// Promise<unsigned long>
|
||||
[Throws]
|
||||
Promise getLength();
|
||||
|
||||
[NewObject, Throws]
|
||||
DataStoreCursor sync(optional DOMString revisionId = "");
|
||||
};
|
||||
|
||||
[Pref="dom.datastore.enabled",
|
||||
JSImplementation="@mozilla.org/dom/datastore-cursor;1"]
|
||||
interface DataStoreCursor {
|
||||
partial interface DataStore {
|
||||
[ChromeOnly, Throws]
|
||||
void setDataStoreImpl(DataStoreImpl store);
|
||||
};
|
||||
|
||||
// TODO Bug 957086 - The constructor and the setDataStoreCursorImpl(...) will be
|
||||
// removed once the DataStore API is fully rewritten in C++,
|
||||
// which currently plays a role of C++ proxy directing to the
|
||||
// JS codes implemented by the DataStoreCursorImpl WebIDL.
|
||||
|
||||
[Pref="dom.datastore.enabled",
|
||||
ChromeConstructor]
|
||||
interface DataStoreCursor {
|
||||
// the DataStore
|
||||
[GetterThrows]
|
||||
readonly attribute DataStore store;
|
||||
|
||||
// Promise<DataStoreTask>
|
||||
[Throws]
|
||||
Promise next();
|
||||
|
||||
[Throws]
|
||||
void close();
|
||||
};
|
||||
|
||||
partial interface DataStoreCursor {
|
||||
[ChromeOnly]
|
||||
void setDataStoreCursorImpl(DataStoreCursorImpl cursor);
|
||||
};
|
||||
|
||||
enum DataStoreOperation {
|
||||
"add",
|
||||
"update",
|
||||
|
68
dom/webidl/DataStoreImpl.webidl
Normal file
68
dom/webidl/DataStoreImpl.webidl
Normal file
@ -0,0 +1,68 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*/
|
||||
|
||||
// TODO Bug 957086 - The DataStoreImpl WebIDL will be removed once the
|
||||
// DataStore API is fully rewritten in C++ (i.e. should be
|
||||
// directly implemented by the DataStore WebIDL).
|
||||
|
||||
[HeaderFile="mozilla/dom/DataStore.h",
|
||||
Func="mozilla::dom::DataStore::EnabledForScope",
|
||||
JSImplementation="@mozilla.org/dom/datastore-impl;1"]
|
||||
interface DataStoreImpl {
|
||||
void setEventTarget(EventTarget eventTarget);
|
||||
|
||||
// Returns the label of the DataSource.
|
||||
readonly attribute DOMString name;
|
||||
|
||||
// Returns the origin of the DataSource (e.g., 'facebook.com').
|
||||
// This value is the manifest URL of the owner app.
|
||||
readonly attribute DOMString owner;
|
||||
|
||||
// is readOnly a F(current_app, datastore) function? yes
|
||||
readonly attribute boolean readOnly;
|
||||
|
||||
// Promise<any>
|
||||
Promise get(DataStoreKey... id);
|
||||
|
||||
// Promise<void>
|
||||
Promise put(any obj, DataStoreKey id, optional DOMString revisionId = "");
|
||||
|
||||
// Promise<DataStoreKey>
|
||||
Promise add(any obj, optional DataStoreKey id,
|
||||
optional DOMString revisionId = "");
|
||||
|
||||
// Promise<boolean>
|
||||
Promise remove(DataStoreKey id, optional DOMString revisionId = "");
|
||||
|
||||
// Promise<void>
|
||||
Promise clear(optional DOMString revisionId = "");
|
||||
|
||||
readonly attribute DOMString revisionId;
|
||||
|
||||
// Promise<unsigned long>
|
||||
Promise getLength();
|
||||
|
||||
[NewObject]
|
||||
DataStoreCursor sync(optional DOMString revisionId = "");
|
||||
};
|
||||
|
||||
|
||||
// TODO Bug 957086 - The DataStoreCursorImpl WebIDL will be removed once the
|
||||
// DataStore API is fully rewritten in C++ (i.e. should be
|
||||
// directly implemented by the DataStoreCursor WebIDL).
|
||||
|
||||
[HeaderFile="mozilla/dom/DataStore.h",
|
||||
Func="mozilla::dom::DataStore::EnabledForScope",
|
||||
JSImplementation="@mozilla.org/dom/datastore-cursor-impl;1"]
|
||||
interface DataStoreCursorImpl {
|
||||
// the DataStore
|
||||
readonly attribute DataStore store;
|
||||
|
||||
// Promise<DataStoreTask>
|
||||
Promise next();
|
||||
|
||||
void close();
|
||||
};
|
@ -68,6 +68,7 @@ WEBIDL_FILES = [
|
||||
'CSSValueList.webidl',
|
||||
'DataContainerEvent.webidl',
|
||||
'DataStore.webidl',
|
||||
'DataStoreImpl.webidl',
|
||||
'DataTransfer.webidl',
|
||||
'DedicatedWorkerGlobalScope.webidl',
|
||||
'DelayNode.webidl',
|
||||
|
@ -20,9 +20,6 @@
|
||||
#define CHROMIUM_LOG(args...) printf(args);
|
||||
#endif
|
||||
|
||||
/* TODO: Remove BlueZ constant */
|
||||
#define BLUEZ_DBUS_BASE_IFC "org.bluez"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
@ -334,6 +331,7 @@ bool RawDBusConnection::SendWithReply(DBusReplyCallback aCallback,
|
||||
bool RawDBusConnection::SendWithReply(DBusReplyCallback aCallback,
|
||||
void* aData,
|
||||
int aTimeout,
|
||||
const char* aDestination,
|
||||
const char* aPath,
|
||||
const char* aIntf,
|
||||
const char* aFunc,
|
||||
@ -344,7 +342,7 @@ bool RawDBusConnection::SendWithReply(DBusReplyCallback aCallback,
|
||||
va_list args;
|
||||
|
||||
va_start(args, aFirstArgType);
|
||||
DBusMessage* msg = BuildDBusMessage(aPath, aIntf, aFunc,
|
||||
DBusMessage* msg = BuildDBusMessage(aDestination, aPath, aIntf, aFunc,
|
||||
aFirstArgType, args);
|
||||
va_end(args);
|
||||
|
||||
@ -355,14 +353,16 @@ bool RawDBusConnection::SendWithReply(DBusReplyCallback aCallback,
|
||||
return SendWithReply(aCallback, aData, aTimeout, msg);
|
||||
}
|
||||
|
||||
DBusMessage* RawDBusConnection::BuildDBusMessage(const char* aPath,
|
||||
DBusMessage* RawDBusConnection::BuildDBusMessage(const char* aDestination,
|
||||
const char* aPath,
|
||||
const char* aIntf,
|
||||
const char* aFunc,
|
||||
int aFirstArgType,
|
||||
va_list aArgs)
|
||||
{
|
||||
DBusMessage* msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
|
||||
aPath, aIntf, aFunc);
|
||||
DBusMessage* msg = dbus_message_new_method_call(aDestination,
|
||||
aPath, aIntf,
|
||||
aFunc);
|
||||
if (!msg) {
|
||||
CHROMIUM_LOG("Could not allocate D-Bus message object!");
|
||||
return nullptr;
|
||||
|
@ -44,11 +44,14 @@ public:
|
||||
int aTimeout, DBusMessage* aMessage);
|
||||
|
||||
bool SendWithReply(DBusReplyCallback aCallback, void* aData,
|
||||
int aTimeout, const char* aPath, const char* aIntf,
|
||||
int aTimeout,
|
||||
const char* aDestination,
|
||||
const char* aPath, const char* aIntf,
|
||||
const char *aFunc, int aFirstArgType, ...);
|
||||
|
||||
protected:
|
||||
DBusMessage* BuildDBusMessage(const char* aPath, const char* aIntf,
|
||||
DBusMessage* BuildDBusMessage(const char* aDestination,
|
||||
const char* aPath, const char* aIntf,
|
||||
const char* aFunc, int aFirstArgType,
|
||||
va_list args);
|
||||
|
||||
|
@ -154,7 +154,6 @@ public abstract class GeckoApp
|
||||
public static final String EXTRA_STATE_BUNDLE = "stateBundle";
|
||||
|
||||
public static final String PREFS_ALLOW_STATE_BUNDLE = "allowStateBundle";
|
||||
public static final String PREFS_CRASHED = "crashed";
|
||||
public static final String PREFS_OOM_EXCEPTION = "OOMException";
|
||||
public static final String PREFS_VERSION_CODE = "versionCode";
|
||||
public static final String PREFS_WAS_STOPPED = "wasStopped";
|
||||
@ -1772,9 +1771,12 @@ public abstract class GeckoApp
|
||||
});
|
||||
|
||||
shouldRestore = true;
|
||||
} else if (savedInstanceState != null || getSessionRestorePreference().equals("always") || getRestartFromIntent()) {
|
||||
} else if (savedInstanceState != null ||
|
||||
getSessionRestorePreference().equals("always") ||
|
||||
getRestartFromIntent() ||
|
||||
prefs.getBoolean(GeckoApp.PREFS_WAS_STOPPED, false)) {
|
||||
// We're coming back from a background kill by the OS, the user
|
||||
// has chosen to always restore, or we just restarted.
|
||||
// has chosen to always restore, we restarted, or we crashed.
|
||||
shouldRestore = true;
|
||||
}
|
||||
|
||||
|
@ -690,7 +690,6 @@ sync_java_files = [
|
||||
'sync/NullClusterURLException.java',
|
||||
'sync/PersistedMetaGlobal.java',
|
||||
'sync/PrefsBackoffHandler.java',
|
||||
'sync/PrefsSource.java',
|
||||
'sync/receivers/SyncAccountDeletedReceiver.java',
|
||||
'sync/receivers/SyncAccountDeletedService.java',
|
||||
'sync/receivers/UpgradeReceiver.java',
|
||||
|
@ -176,32 +176,35 @@ public class FxAccountClient10 {
|
||||
|
||||
protected final byte[] tokenId;
|
||||
protected final byte[] reqHMACKey;
|
||||
protected final boolean payload;
|
||||
protected final SkewHandler skewHandler;
|
||||
|
||||
/**
|
||||
* Create a delegate for an un-authenticated resource.
|
||||
*/
|
||||
public ResourceDelegate(final Resource resource, final RequestDelegate<T> delegate) {
|
||||
this(resource, delegate, null, null, false);
|
||||
this(resource, delegate, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a delegate for a Hawk-authenticated resource.
|
||||
* <p>
|
||||
* Every Hawk request that encloses an entity (PATCH, POST, and PUT) will
|
||||
* include the payload verification hash.
|
||||
*/
|
||||
public ResourceDelegate(final Resource resource, final RequestDelegate<T> delegate, final byte[] tokenId, final byte[] reqHMACKey, final boolean authenticatePayload) {
|
||||
public ResourceDelegate(final Resource resource, final RequestDelegate<T> delegate, final byte[] tokenId, final byte[] reqHMACKey) {
|
||||
super(resource);
|
||||
this.delegate = delegate;
|
||||
this.reqHMACKey = reqHMACKey;
|
||||
this.tokenId = tokenId;
|
||||
this.payload = authenticatePayload;
|
||||
this.skewHandler = SkewHandler.getSkewHandlerForResource(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthHeaderProvider getAuthHeaderProvider() {
|
||||
if (tokenId != null && reqHMACKey != null) {
|
||||
return new HawkAuthHeaderProvider(Utils.byte2Hex(tokenId), reqHMACKey, payload, skewHandler.getSkewInSeconds());
|
||||
// We always include the payload verification hash for FxA Hawk-authenticated requests.
|
||||
final boolean includePayloadVerificationHash = true;
|
||||
return new HawkAuthHeaderProvider(Utils.byte2Hex(tokenId), reqHMACKey, includePayloadVerificationHash, skewHandler.getSkewInSeconds());
|
||||
}
|
||||
return super.getAuthHeaderProvider();
|
||||
}
|
||||
@ -483,7 +486,7 @@ public class FxAccountClient10 {
|
||||
return;
|
||||
}
|
||||
|
||||
resource.delegate = new ResourceDelegate<TwoTokens>(resource, delegate, tokenId, reqHMACKey, false) {
|
||||
resource.delegate = new ResourceDelegate<TwoTokens>(resource, delegate, tokenId, reqHMACKey) {
|
||||
@Override
|
||||
public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) {
|
||||
try {
|
||||
@ -519,7 +522,7 @@ public class FxAccountClient10 {
|
||||
return;
|
||||
}
|
||||
|
||||
resource.delegate = new ResourceDelegate<Void>(resource, delegate, tokenId, reqHMACKey, false) {
|
||||
resource.delegate = new ResourceDelegate<Void>(resource, delegate, tokenId, reqHMACKey) {
|
||||
@Override
|
||||
public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) {
|
||||
delegate.handleSuccess(null);
|
||||
@ -608,7 +611,7 @@ public class FxAccountClient10 {
|
||||
return;
|
||||
}
|
||||
|
||||
resource.delegate = new ResourceDelegate<TwoKeys>(resource, delegate, tokenId, reqHMACKey, false) {
|
||||
resource.delegate = new ResourceDelegate<TwoKeys>(resource, delegate, tokenId, reqHMACKey) {
|
||||
@Override
|
||||
public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) {
|
||||
try {
|
||||
@ -670,7 +673,7 @@ public class FxAccountClient10 {
|
||||
return;
|
||||
}
|
||||
|
||||
resource.delegate = new ResourceDelegate<StatusResponse>(resource, delegate, tokenId, reqHMACKey, false) {
|
||||
resource.delegate = new ResourceDelegate<StatusResponse>(resource, delegate, tokenId, reqHMACKey) {
|
||||
@Override
|
||||
public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) {
|
||||
try {
|
||||
@ -712,7 +715,7 @@ public class FxAccountClient10 {
|
||||
return;
|
||||
}
|
||||
|
||||
resource.delegate = new ResourceDelegate<String>(resource, delegate, tokenId, reqHMACKey, true) {
|
||||
resource.delegate = new ResourceDelegate<String>(resource, delegate, tokenId, reqHMACKey) {
|
||||
@Override
|
||||
public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) {
|
||||
String cert = body.getString("cert");
|
||||
@ -753,7 +756,7 @@ public class FxAccountClient10 {
|
||||
return;
|
||||
}
|
||||
|
||||
resource.delegate = new ResourceDelegate<Void>(resource, delegate, tokenId, reqHMACKey, false) {
|
||||
resource.delegate = new ResourceDelegate<Void>(resource, delegate, tokenId, reqHMACKey) {
|
||||
@Override
|
||||
public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) {
|
||||
try {
|
||||
|
@ -5,7 +5,10 @@
|
||||
package org.mozilla.gecko.fxa.activities;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
@ -24,8 +27,11 @@ import org.mozilla.gecko.sync.SyncConfiguration;
|
||||
import org.mozilla.gecko.sync.setup.Constants;
|
||||
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.text.method.PasswordTransformationMethod;
|
||||
@ -35,6 +41,8 @@ import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnFocusChangeListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ProgressBar;
|
||||
@ -54,7 +62,7 @@ abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractAc
|
||||
|
||||
protected int minimumPasswordLength = 8;
|
||||
|
||||
protected EditText emailEdit;
|
||||
protected AutoCompleteTextView emailEdit;
|
||||
protected EditText passwordEdit;
|
||||
protected Button showPasswordButton;
|
||||
protected TextView remoteErrorTextView;
|
||||
@ -312,4 +320,54 @@ abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractAc
|
||||
protected PasswordStretcher makePasswordStretcher(String password) {
|
||||
return new QuickPasswordStretcher(password);
|
||||
}
|
||||
|
||||
protected abstract static class GetAccountsAsyncTask extends AsyncTask<Void, Void, Account[]> {
|
||||
protected final Context context;
|
||||
|
||||
public GetAccountsAsyncTask(Context context) {
|
||||
super();
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Account[] doInBackground(Void... params) {
|
||||
return AccountManager.get(context).getAccounts();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This updates UI, so needs to be done on the foreground thread.
|
||||
*/
|
||||
protected void populateEmailAddressAutocomplete(Account[] accounts) {
|
||||
// First a set, since we don't want repeats.
|
||||
final Set<String> emails = new HashSet<String>();
|
||||
for (Account account : accounts) {
|
||||
if (!Patterns.EMAIL_ADDRESS.matcher(account.name).matches()) {
|
||||
continue;
|
||||
}
|
||||
emails.add(account.name);
|
||||
}
|
||||
|
||||
// And then sorted in alphabetical order.
|
||||
final String[] sortedEmails = emails.toArray(new String[0]);
|
||||
Arrays.sort(sortedEmails);
|
||||
|
||||
final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, sortedEmails);
|
||||
emailEdit.setAdapter(adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// Getting Accounts accesses databases on disk, so needs to be done on a
|
||||
// background thread.
|
||||
final GetAccountsAsyncTask task = new GetAccountsAsyncTask(this) {
|
||||
@Override
|
||||
public void onPostExecute(Account[] accounts) {
|
||||
populateEmailAddressAutocomplete(accounts);
|
||||
}
|
||||
};
|
||||
task.execute();
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
@ -66,7 +67,7 @@ public class FxAccountCreateAccountActivity extends FxAccountAbstractSetupActivi
|
||||
super.onCreate(icicle);
|
||||
setContentView(R.layout.fxaccount_create_account);
|
||||
|
||||
emailEdit = (EditText) ensureFindViewById(null, R.id.email, "email edit");
|
||||
emailEdit = (AutoCompleteTextView) ensureFindViewById(null, R.id.email, "email edit");
|
||||
passwordEdit = (EditText) ensureFindViewById(null, R.id.password, "password edit");
|
||||
showPasswordButton = (Button) ensureFindViewById(null, R.id.show_password, "show password button");
|
||||
yearEdit = (EditText) ensureFindViewById(null, R.id.year_edit, "year edit");
|
||||
|
@ -1,191 +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/. */
|
||||
|
||||
package org.mozilla.gecko.fxa.activities;
|
||||
|
||||
import android.support.v4.app.Fragment;
|
||||
|
||||
public class FxAccountCreateAccountFragment extends Fragment { // implements OnClickListener {
|
||||
protected static final String LOG_TAG = FxAccountCreateAccountFragment.class.getSimpleName();
|
||||
//
|
||||
// protected FxAccountSetupActivity activity;
|
||||
//
|
||||
// protected EditText emailEdit;
|
||||
// protected EditText passwordEdit;
|
||||
// protected EditText password2Edit;
|
||||
// protected Button button;
|
||||
//
|
||||
// protected TextView emailError;
|
||||
// protected TextView passwordError;
|
||||
//
|
||||
// protected TextChangedListener textChangedListener;
|
||||
// protected EditorActionListener editorActionListener;
|
||||
// protected OnFocusChangeListener focusChangeListener;
|
||||
// @Override
|
||||
// public void onCreate(Bundle savedInstanceState) {
|
||||
// super.onCreate(savedInstanceState);
|
||||
// // Retain this fragment across configuration changes. See, for example,
|
||||
// // http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html
|
||||
// // This fragment will own AsyncTask instances which should not be
|
||||
// // interrupted by configuration changes (and activity changes).
|
||||
// setRetainInstance(true);
|
||||
// }
|
||||
|
||||
// @Override
|
||||
// public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
// Bundle savedInstanceState) {
|
||||
// View v = inflater.inflate(R.layout.fxaccount_create_account_fragment, container, false);
|
||||
//
|
||||
// FxAccountSetupActivity.linkifyTextViews(v, new int[] { R.id.description, R.id.policy });
|
||||
//
|
||||
// emailEdit = (EditText) ensureFindViewById(v, R.id.email, "email");
|
||||
// passwordEdit = (EditText) ensureFindViewById(v, R.id.password, "password");
|
||||
// // Second password can be null.
|
||||
// password2Edit = (EditText) v.findViewById(R.id.password2);
|
||||
//
|
||||
// emailError = (TextView) ensureFindViewById(v, R.id.email_error, "email error");
|
||||
// passwordError = (TextView) ensureFindViewById(v, R.id.password_error, "password error");
|
||||
//
|
||||
// textChangedListener = new TextChangedListener();
|
||||
// editorActionListener = new EditorActionListener();
|
||||
// focusChangeListener = new FocusChangeListener();
|
||||
//
|
||||
// addListeners(emailEdit);
|
||||
// addListeners(passwordEdit);
|
||||
// if (password2Edit != null) {
|
||||
// addListeners(password2Edit);
|
||||
// }
|
||||
//
|
||||
// button = (Button) ensureFindViewById(v, R.id.create_account_button, "button");
|
||||
// button.setOnClickListener(this);
|
||||
// return v;
|
||||
// }
|
||||
|
||||
// protected void onCreateAccount(View button) {
|
||||
// Logger.debug(LOG_TAG, "onCreateAccount: Asking for username/password for new account.");
|
||||
// String email = emailEdit.getText().toString();
|
||||
// String password = passwordEdit.getText().toString();
|
||||
// activity.signUp(email, password);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onClick(View v) {
|
||||
// switch (v.getId()) {
|
||||
// case R.id.create_account_button:
|
||||
// if (!validate(false)) {
|
||||
// return;
|
||||
// }
|
||||
// onCreateAccount(v);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// protected void addListeners(EditText editText) {
|
||||
// editText.addTextChangedListener(textChangedListener);
|
||||
// editText.setOnEditorActionListener(editorActionListener);
|
||||
// editText.setOnFocusChangeListener(focusChangeListener);
|
||||
// }
|
||||
//
|
||||
// protected class FocusChangeListener implements OnFocusChangeListener {
|
||||
// @Override
|
||||
// public void onFocusChange(View v, boolean hasFocus) {
|
||||
// if (hasFocus) {
|
||||
// return;
|
||||
// }
|
||||
// validate(false);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// protected class EditorActionListener implements OnEditorActionListener {
|
||||
// @Override
|
||||
// public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
// validate(false);
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// protected class TextChangedListener implements TextWatcher {
|
||||
// @Override
|
||||
// public void afterTextChanged(Editable s) {
|
||||
// validate(true);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
// // Do nothing.
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
// // Do nothing.
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Show or hide error messaging.
|
||||
// *
|
||||
// * @param removeOnly
|
||||
// * if true, possibly remove existing error messages but do not set an
|
||||
// * error message if one was not present.
|
||||
// * @param errorResourceId
|
||||
// * of error string, or -1 to hide.
|
||||
// * @param errorView
|
||||
// * <code>TextView</code> instance to display error message in.
|
||||
// * @param edits
|
||||
// * <code>EditText</code> instances to style.
|
||||
// */
|
||||
// protected void setError(boolean removeOnly, int errorResourceId, TextView errorView, EditText... edits) {
|
||||
// if (removeOnly && errorResourceId != -1) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// int res = errorResourceId == -1 ? R.drawable.fxaccount_textfield_background : R.drawable.fxaccount_textfield_error_background;
|
||||
// for (EditText edit : edits) {
|
||||
// if (edit == null) {
|
||||
// continue;
|
||||
// }
|
||||
// edit.setBackgroundResource(res);
|
||||
// }
|
||||
// if (errorResourceId == -1) {
|
||||
// errorView.setVisibility(View.GONE);
|
||||
// errorView.setText(null);
|
||||
// } else {
|
||||
// errorView.setText(errorResourceId);
|
||||
// errorView.setVisibility(View.VISIBLE);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// protected boolean validate(boolean removeOnly) {
|
||||
// boolean enabled = true;
|
||||
// final String email = emailEdit.getText().toString();
|
||||
// final String password = passwordEdit.getText().toString();
|
||||
//
|
||||
// if (email.length() == 0 || Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
|
||||
// setError(removeOnly, -1, emailError, emailEdit);
|
||||
// } else {
|
||||
// enabled = false;
|
||||
// setError(removeOnly, R.string.fxaccount_bad_email, emailError, emailEdit);
|
||||
// }
|
||||
//
|
||||
// if (password2Edit != null) {
|
||||
// final String password2 = password2Edit.getText().toString();
|
||||
// enabled = enabled && password2.length() > 0;
|
||||
//
|
||||
// boolean passwordsMatch = password.equals(password2);
|
||||
// if (passwordsMatch) {
|
||||
// setError(removeOnly, -1, passwordError, passwordEdit, password2Edit);
|
||||
// } else {
|
||||
// enabled = false;
|
||||
// setError(removeOnly, R.string.fxaccount_bad_passwords, passwordError, passwordEdit, password2Edit);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (enabled != button.isEnabled()) {
|
||||
// Logger.debug(LOG_TAG, (enabled ? "En" : "Dis") + "abling button.");
|
||||
// button.setEnabled(enabled);
|
||||
// }
|
||||
//
|
||||
// return enabled;
|
||||
// }
|
||||
}
|
@ -23,6 +23,7 @@ import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ProgressBar;
|
||||
@ -46,7 +47,7 @@ public class FxAccountSignInActivity extends FxAccountAbstractSetupActivity {
|
||||
super.onCreate(icicle);
|
||||
setContentView(R.layout.fxaccount_sign_in);
|
||||
|
||||
emailEdit = (EditText) ensureFindViewById(null, R.id.email, "email edit");
|
||||
emailEdit = (AutoCompleteTextView) ensureFindViewById(null, R.id.email, "email edit");
|
||||
passwordEdit = (EditText) ensureFindViewById(null, R.id.password, "password edit");
|
||||
showPasswordButton = (Button) ensureFindViewById(null, R.id.show_password, "show password button");
|
||||
remoteErrorTextView = (TextView) ensureFindViewById(null, R.id.remote_error, "remote error text view");
|
||||
|
@ -28,6 +28,7 @@ import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ProgressBar;
|
||||
@ -59,7 +60,7 @@ public class FxAccountUpdateCredentialsActivity extends FxAccountAbstractSetupAc
|
||||
super.onCreate(icicle);
|
||||
setContentView(R.layout.fxaccount_update_credentials);
|
||||
|
||||
emailEdit = (EditText) ensureFindViewById(null, R.id.email, "email edit");
|
||||
emailEdit = (AutoCompleteTextView) ensureFindViewById(null, R.id.email, "email edit");
|
||||
passwordEdit = (EditText) ensureFindViewById(null, R.id.password, "password edit");
|
||||
showPasswordButton = (Button) ensureFindViewById(null, R.id.show_password, "show password button");
|
||||
remoteErrorTextView = (TextView) ensureFindViewById(null, R.id.remote_error, "remote error text view");
|
||||
|
@ -5,13 +5,11 @@
|
||||
package org.mozilla.gecko.fxa.sync;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.json.simple.parser.ParseException;
|
||||
import org.mozilla.gecko.fxa.FxAccountConstants;
|
||||
import org.mozilla.gecko.sync.GlobalSession;
|
||||
import org.mozilla.gecko.sync.NonObjectJSONException;
|
||||
import org.mozilla.gecko.sync.SyncConfiguration;
|
||||
@ -25,16 +23,12 @@ import org.mozilla.gecko.sync.stage.GlobalSyncStage.Stage;
|
||||
import android.content.Context;
|
||||
|
||||
public class FxAccountGlobalSession extends GlobalSession {
|
||||
private static final String LOG_TAG = FxAccountGlobalSession.class.getSimpleName();
|
||||
|
||||
public FxAccountGlobalSession(String storageEndpoint, SyncConfiguration config, BaseGlobalSessionCallback callback,
|
||||
Context context, ClientsDataDelegate clientsDelegate)
|
||||
throws SyncConfigurationException, IllegalArgumentException, IOException,
|
||||
ParseException, NonObjectJSONException, URISyntaxException {
|
||||
public FxAccountGlobalSession(SyncConfiguration config,
|
||||
BaseGlobalSessionCallback callback,
|
||||
Context context,
|
||||
ClientsDataDelegate clientsDelegate)
|
||||
throws SyncConfigurationException, IllegalArgumentException, IOException, ParseException, NonObjectJSONException, URISyntaxException {
|
||||
super(config, callback, context, clientsDelegate, null);
|
||||
URI storageURI = new URI(storageEndpoint);
|
||||
this.config.setClusterURL(storageURI);
|
||||
FxAccountConstants.pii(LOG_TAG, "clusterURL is " + config.getClusterURLString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -20,8 +20,6 @@ import org.mozilla.gecko.background.fxa.FxAccountClient20;
|
||||
import org.mozilla.gecko.background.fxa.SkewHandler;
|
||||
import org.mozilla.gecko.browserid.BrowserIDKeyPair;
|
||||
import org.mozilla.gecko.browserid.JSONWebTokenUtils;
|
||||
import org.mozilla.gecko.browserid.verifier.BrowserIDRemoteVerifierClient;
|
||||
import org.mozilla.gecko.browserid.verifier.BrowserIDVerifierDelegate;
|
||||
import org.mozilla.gecko.fxa.FirefoxAccounts;
|
||||
import org.mozilla.gecko.fxa.FxAccountConstants;
|
||||
import org.mozilla.gecko.fxa.authenticator.AccountPickler;
|
||||
@ -35,7 +33,6 @@ import org.mozilla.gecko.fxa.login.State;
|
||||
import org.mozilla.gecko.fxa.login.State.StateLabel;
|
||||
import org.mozilla.gecko.fxa.login.StateFactory;
|
||||
import org.mozilla.gecko.sync.BackoffHandler;
|
||||
import org.mozilla.gecko.sync.ExtendedJSONObject;
|
||||
import org.mozilla.gecko.sync.GlobalSession;
|
||||
import org.mozilla.gecko.sync.PrefsBackoffHandler;
|
||||
import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate;
|
||||
@ -368,15 +365,21 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
// global session.
|
||||
final SkewHandler storageServerSkewHandler = SkewHandler.getSkewHandlerForHostname(storageHostname);
|
||||
final long storageServerSkew = storageServerSkewHandler.getSkewInSeconds();
|
||||
final AuthHeaderProvider authHeaderProvider = new HawkAuthHeaderProvider(token.id, token.key.getBytes("UTF-8"), false, storageServerSkew);
|
||||
// We expect Sync to upload large sets of records. Calculating the
|
||||
// payload verification hash for these record sets could be expensive,
|
||||
// so we explicitly do not send payload verification hashes to the
|
||||
// Sync storage endpoint.
|
||||
final boolean includePayloadVerificationHash = false;
|
||||
final AuthHeaderProvider authHeaderProvider = new HawkAuthHeaderProvider(token.id, token.key.getBytes("UTF-8"), includePayloadVerificationHash, storageServerSkew);
|
||||
|
||||
final Context context = getContext();
|
||||
final SyncConfiguration syncConfig = new SyncConfiguration(token.uid, authHeaderProvider, sharedPrefs, syncKeyBundle);
|
||||
|
||||
Collection<String> knownStageNames = SyncConfiguration.validEngineNames();
|
||||
syncConfig.stagesToSync = Utils.getStagesToSyncFromBundle(knownStageNames, extras);
|
||||
syncConfig.setClusterURL(storageServerURI);
|
||||
|
||||
globalSession = new FxAccountGlobalSession(token.endpoint, syncConfig, callback, context, clientsDataDelegate);
|
||||
globalSession = new FxAccountGlobalSession(syncConfig, callback, context, clientsDataDelegate);
|
||||
globalSession.start();
|
||||
} catch (Exception e) {
|
||||
callback.handleError(globalSession, e);
|
||||
@ -386,7 +389,6 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
|
||||
@Override
|
||||
public void handleFailure(TokenServerException e) {
|
||||
debugAssertion(audience, assertion);
|
||||
handleError(e);
|
||||
}
|
||||
|
||||
@ -611,34 +613,4 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
Logger.info(LOG_TAG, "Syncing done.");
|
||||
lastSyncRealtimeMillis = SystemClock.elapsedRealtime();
|
||||
}
|
||||
|
||||
protected void debugAssertion(String audience, String assertion) {
|
||||
final CountDownLatch verifierLatch = new CountDownLatch(1);
|
||||
BrowserIDRemoteVerifierClient client = new BrowserIDRemoteVerifierClient(URI.create(BrowserIDRemoteVerifierClient.DEFAULT_VERIFIER_URL));
|
||||
client.verify(audience, assertion, new BrowserIDVerifierDelegate() {
|
||||
@Override
|
||||
public void handleSuccess(ExtendedJSONObject response) {
|
||||
Logger.info(LOG_TAG, "Remote verifier returned success: " + response.toJSONString());
|
||||
verifierLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFailure(ExtendedJSONObject response) {
|
||||
Logger.warn(LOG_TAG, "Remote verifier returned failure: " + response.toJSONString());
|
||||
verifierLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleError(Exception e) {
|
||||
Logger.error(LOG_TAG, "Remote verifier returned error.", e);
|
||||
verifierLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
verifierLatch.await();
|
||||
} catch (InterruptedException e) {
|
||||
Logger.error(LOG_TAG, "Got error.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,6 +54,7 @@
|
||||
<!ENTITY back "Back">
|
||||
<!ENTITY stop "Stop">
|
||||
<!ENTITY site_security "Site Security">
|
||||
<!ENTITY edit_mode_cancel "Cancel">
|
||||
|
||||
<!ENTITY close_tab "Close Tab">
|
||||
<!ENTITY one_tab "1 tab">
|
||||
|
@ -61,7 +61,9 @@ public class MenuItemActionView extends LinearLayout
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
View parent = (View) getParent();
|
||||
if ((right - left) < parent.getMeasuredWidth() || mActionButtons.size() != 0) {
|
||||
final int padding = getPaddingLeft() + getPaddingRight();
|
||||
final int parentPadding = parent.getPaddingLeft() + parent.getPaddingRight();
|
||||
if ((right - left - padding) < (parent.getMeasuredWidth() - parentPadding) || mActionButtons.size() != 0) {
|
||||
// Use the icon.
|
||||
mMenuItem.setVisibility(View.GONE);
|
||||
mMenuButton.setVisibility(View.VISIBLE);
|
||||
|
@ -439,6 +439,7 @@ gbjar.generated_sources += [
|
||||
'org/mozilla/gecko/widget/ThemedRelativeLayout.java',
|
||||
'org/mozilla/gecko/widget/ThemedTextSwitcher.java',
|
||||
'org/mozilla/gecko/widget/ThemedTextView.java',
|
||||
'org/mozilla/gecko/widget/ThemedView.java',
|
||||
]
|
||||
if CONFIG['MOZ_CRASHREPORTER']:
|
||||
gbjar.sources += [ 'CrashReporter.java' ]
|
||||
|
BIN
mobile/android/base/resources/drawable-hdpi/close_edit_mode.png
Normal file
BIN
mobile/android/base/resources/drawable-hdpi/close_edit_mode.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 301 B |
BIN
mobile/android/base/resources/drawable-mdpi/close_edit_mode.png
Normal file
BIN
mobile/android/base/resources/drawable-mdpi/close_edit_mode.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 205 B |
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid
|
||||
android:color="@android:color/white" />
|
||||
<stroke
|
||||
android:width="@dimen/fxaccount_stroke_width"
|
||||
android:color="@color/fxaccount_input_borderActive" />
|
||||
<corners
|
||||
android:radius="@dimen/fxaccount_corner_radius"
|
||||
android:topLeftRadius="@dimen/fxaccount_corner_radius"
|
||||
android:topRightRadius="0dp"
|
||||
android:bottomLeftRadius="@dimen/fxaccount_corner_radius"
|
||||
android:bottomRightRadius="0dp" />
|
||||
</shape>
|
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid
|
||||
android:color="@color/fxaccount_password_hide_backgroundcolor" />
|
||||
<stroke
|
||||
android:width="@dimen/fxaccount_stroke_width"
|
||||
android:color="@color/fxaccount_input_borderActive" />
|
||||
<corners
|
||||
android:radius="@dimen/fxaccount_corner_radius"
|
||||
android:topLeftRadius="0dp"
|
||||
android:topRightRadius="@dimen/fxaccount_corner_radius"
|
||||
android:bottomLeftRadius="0dp"
|
||||
android:bottomRightRadius="@dimen/fxaccount_corner_radius" />
|
||||
</shape>
|
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid
|
||||
android:color="@color/fxaccount_password_show_backgroundcolor" />
|
||||
<stroke
|
||||
android:width="@dimen/fxaccount_stroke_width"
|
||||
android:color="@color/fxaccount_input_borderActive" />
|
||||
<corners
|
||||
android:radius="@dimen/fxaccount_corner_radius"
|
||||
android:topLeftRadius="0dp"
|
||||
android:topRightRadius="@dimen/fxaccount_corner_radius"
|
||||
android:bottomLeftRadius="0dp"
|
||||
android:bottomRightRadius="@dimen/fxaccount_corner_radius" />
|
||||
</shape>
|
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid
|
||||
android:color="@android:color/white" />
|
||||
<stroke
|
||||
android:width="@dimen/fxaccount_stroke_width"
|
||||
android:color="@color/fxaccount_input_borderInactive" />
|
||||
<corners
|
||||
android:radius="@dimen/fxaccount_corner_radius"
|
||||
android:topLeftRadius="@dimen/fxaccount_corner_radius"
|
||||
android:topRightRadius="0dp"
|
||||
android:bottomLeftRadius="@dimen/fxaccount_corner_radius"
|
||||
android:bottomRightRadius="0dp" />
|
||||
</shape>
|
BIN
mobile/android/base/resources/drawable-xhdpi/close_edit_mode.png
Normal file
BIN
mobile/android/base/resources/drawable-xhdpi/close_edit_mode.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 370 B |
@ -10,10 +10,13 @@
|
||||
<stroke
|
||||
android:width="@dimen/fxaccount_stroke_width"
|
||||
android:color="@color/fxaccount_input_borderActive" />
|
||||
<!-- On Android pre v12/3.0/Gingerbread, bottom left and bottom
|
||||
right are swapped. These values correct this bug; the resources
|
||||
that don't need correction are in res/drawable-v12. -->
|
||||
<corners
|
||||
android:radius="@dimen/fxaccount_corner_radius"
|
||||
android:topLeftRadius="@dimen/fxaccount_corner_radius"
|
||||
android:topRightRadius="0dp"
|
||||
android:bottomLeftRadius="@dimen/fxaccount_corner_radius"
|
||||
android:bottomRightRadius="0dp" />
|
||||
android:bottomLeftRadius="0dp"
|
||||
android:bottomRightRadius="@dimen/fxaccount_corner_radius" />
|
||||
</shape>
|
||||
|
@ -10,10 +10,13 @@
|
||||
<stroke
|
||||
android:width="@dimen/fxaccount_stroke_width"
|
||||
android:color="@color/fxaccount_input_borderActive" />
|
||||
<!-- On Android pre v12/3.0/Gingerbread, bottom left and bottom
|
||||
right are swapped. These values correct this bug; the resources
|
||||
that don't need correction are in res/drawable-v12. -->
|
||||
<corners
|
||||
android:radius="@dimen/fxaccount_corner_radius"
|
||||
android:topLeftRadius="0dp"
|
||||
android:topRightRadius="@dimen/fxaccount_corner_radius"
|
||||
android:bottomLeftRadius="0dp"
|
||||
android:bottomRightRadius="@dimen/fxaccount_corner_radius" />
|
||||
android:bottomRightRadius="0dp"
|
||||
android:bottomLeftRadius="@dimen/fxaccount_corner_radius" />
|
||||
</shape>
|
||||
|
@ -10,10 +10,13 @@
|
||||
<stroke
|
||||
android:width="@dimen/fxaccount_stroke_width"
|
||||
android:color="@color/fxaccount_input_borderActive" />
|
||||
<!-- On Android pre v12/3.0/Gingerbread, bottom left and bottom
|
||||
right are swapped. These values correct this bug; the resources
|
||||
that don't need correction are in res/drawable-v12. -->
|
||||
<corners
|
||||
android:radius="@dimen/fxaccount_corner_radius"
|
||||
android:topLeftRadius="0dp"
|
||||
android:topRightRadius="@dimen/fxaccount_corner_radius"
|
||||
android:bottomLeftRadius="0dp"
|
||||
android:bottomRightRadius="@dimen/fxaccount_corner_radius" />
|
||||
android:bottomRightRadius="0dp"
|
||||
android:bottomLeftRadius="@dimen/fxaccount_corner_radius" />
|
||||
</shape>
|
||||
|
@ -10,10 +10,13 @@
|
||||
<stroke
|
||||
android:width="@dimen/fxaccount_stroke_width"
|
||||
android:color="@color/fxaccount_input_borderInactive" />
|
||||
<!-- On Android pre v12/3.0/Gingerbread, bottom left and bottom
|
||||
right are swapped. These values correct this bug; the resources
|
||||
that don't need correction are in res/drawable-v12. -->
|
||||
<corners
|
||||
android:radius="@dimen/fxaccount_corner_radius"
|
||||
android:topLeftRadius="@dimen/fxaccount_corner_radius"
|
||||
android:topRightRadius="0dp"
|
||||
android:bottomLeftRadius="@dimen/fxaccount_corner_radius"
|
||||
android:bottomRightRadius="0dp" />
|
||||
android:bottomLeftRadius="0dp"
|
||||
android:bottomRightRadius="@dimen/fxaccount_corner_radius" />
|
||||
</shape>
|
||||
|
12
mobile/android/base/resources/drawable/toolbar_separator.xml
Normal file
12
mobile/android/base/resources/drawable/toolbar_separator.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="#a6aeb4"/>
|
||||
<size android:width="1dp" />
|
||||
|
||||
</shape>
|
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="#222"/>
|
||||
<size android:width="1dp" />
|
||||
|
||||
</shape>
|
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!-- Note that android:color="@color/toolbar_separator" (which uses a selector)
|
||||
directly in the <shape> does not appear to work, so instead we select
|
||||
between two shapes with different colors. -->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:gecko="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item gecko:state_private="true" android:drawable="@drawable/toolbar_separator_pb"/>
|
||||
<item android:drawable="@drawable/toolbar_separator"/>
|
||||
|
||||
</selector>
|
@ -12,6 +12,13 @@
|
||||
<ImageButton android:id="@+id/forward"
|
||||
style="@style/UrlBar.ImageButton.Unused"/>
|
||||
|
||||
<!-- Note: any layout parameters setting the right edge of
|
||||
this View should be matched in the url_bar_translating_edge.
|
||||
|
||||
Note 2: On devices where the editing mode cancel items are
|
||||
wider than the tabs and similar buttons (e.g. hardware menu
|
||||
button), the url bar will shrink, in which case the LayoutParams
|
||||
of this View are changed dynamically. -->
|
||||
<ImageView android:id="@+id/url_bar_entry"
|
||||
style="@style/UrlBar.Button"
|
||||
android:layout_marginLeft="4dp"
|
||||
@ -27,18 +34,22 @@
|
||||
android:src="@drawable/url_bar_entry"
|
||||
android:scaleType="fitXY"/>
|
||||
|
||||
<ImageView android:id="@+id/url_bar_right_edge"
|
||||
<!-- A View that clips with url_bar_entry and translates
|
||||
around it to animate shrinking or growing the url bar,
|
||||
which occurs in the display/editing mode transitions. -->
|
||||
<ImageView android:id="@+id/url_bar_translating_edge"
|
||||
style="@style/UrlBar.Button"
|
||||
android:layout_alignLeft="@id/url_bar_entry"
|
||||
android:layout_alignRight="@id/url_bar_entry"
|
||||
android:layout_toLeftOf="@+id/tabs"
|
||||
android:layout_alignTop="@id/url_bar_entry"
|
||||
android:layout_alignBottom="@id/url_bar_entry"
|
||||
android:layout_marginRight="-19dp"
|
||||
android:paddingRight="4dp"
|
||||
android:duplicateParentState="true"
|
||||
android:clickable="false"
|
||||
android:focusable="false"
|
||||
android:visibility="invisible"
|
||||
android:src="@drawable/url_bar_right_edge"
|
||||
android:src="@drawable/url_bar_translating_edge"
|
||||
android:scaleType="fitXY"/>
|
||||
|
||||
<LinearLayout android:id="@+id/menu_items"
|
||||
@ -86,8 +97,29 @@
|
||||
android:layout_marginTop="12dip"
|
||||
android:layout_alignRight="@id/tabs"/>
|
||||
|
||||
<!-- Note that the edit components are invisible so that the views
|
||||
depending on their location can properly layout. -->
|
||||
<ImageView android:id="@+id/edit_cancel"
|
||||
style="@style/UrlBar.ImageButton.Icon"
|
||||
android:layout_alignParentRight="true"
|
||||
android:src="@drawable/close_edit_mode"
|
||||
android:contentDescription="@string/edit_mode_cancel"
|
||||
android:visibility="invisible"/>
|
||||
|
||||
<org.mozilla.gecko.widget.ThemedView android:id="@+id/edit_separator"
|
||||
android:layout_toLeftOf="@id/edit_cancel"
|
||||
android:layout_width="1dip"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="2dp"
|
||||
android:background="@drawable/toolbar_separator_selector"
|
||||
android:visibility="invisible"/>
|
||||
|
||||
<org.mozilla.gecko.toolbar.ToolbarEditLayout android:id="@+id/edit_layout"
|
||||
style="@style/UrlBar.Button"
|
||||
android:layout_toLeftOf="@id/edit_separator"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:paddingLeft="8dp"
|
||||
|
@ -12,16 +12,17 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<EditText
|
||||
<AutoCompleteTextView
|
||||
android:id="@+id/email"
|
||||
style="@style/FxAccountEditItem"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:completionThreshold="2"
|
||||
android:ems="10"
|
||||
android:hint="@string/fxaccount_email_hint"
|
||||
android:inputType="textEmailAddress" >
|
||||
|
||||
<requestFocus />
|
||||
</EditText>
|
||||
</AutoCompleteTextView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
@ -39,20 +40,46 @@
|
||||
android:hint="@string/fxaccount_password_hint"
|
||||
android:inputType="textPassword" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/show_password"
|
||||
style="@android:style/Widget.Button"
|
||||
<!-- For the following, I beg forgiveness. The show/hide button is a
|
||||
toggle button; its text depends on its state. The text for each
|
||||
state could be a different length. We want to maintain the
|
||||
button's width regardless of its state. To achieve this, we
|
||||
size the actual button to its container, and include two
|
||||
invisible (but present for layout purposes) buttons, one of
|
||||
each state. The container wraps the larger of the two dummy
|
||||
buttons; the actual button sizes to the container; and we're
|
||||
happy. Be thankful there are not three buttons! -->
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="0"
|
||||
android:background="@drawable/fxaccount_password_button_show_background"
|
||||
android:minHeight="0dp"
|
||||
android:padding="0dp"
|
||||
android:text="@string/fxaccount_password_show"
|
||||
android:textColor="@color/fxaccount_input_textColor"
|
||||
android:textSize="20sp" >
|
||||
</Button>
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<Button
|
||||
android:id="@+id/show_password"
|
||||
style="@style/FxAccountShowHidePasswordButton"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="@string/fxaccount_password_show" >
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
style="@style/FxAccountShowHidePasswordButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="@string/fxaccount_password_show"
|
||||
android:visibility="invisible" >
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
style="@style/FxAccountShowHidePasswordButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="@string/fxaccount_password_hide"
|
||||
android:visibility="invisible" >
|
||||
</Button>
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</merge>
|
||||
</merge>
|
||||
|
@ -13,11 +13,20 @@
|
||||
<item name="android:minWidth">500dp</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_weight">1</item>
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<!-- layout_gravity controls where the middle container goes
|
||||
in its parent, and gravity controls where the container
|
||||
places its internal content. Usually, the middle
|
||||
container is a LinearLayout and the container is a
|
||||
ScrollView. In this case, layout_gravity="center"
|
||||
interacts badly with vertical scrolling. What is needed
|
||||
is layout_gravity="center_horizontal" and
|
||||
gravity="center". -->
|
||||
<item name="android:gravity">center</item>
|
||||
<item name="android:layout_gravity">center_horizontal</item>
|
||||
<item name="android:paddingTop">25dp</item>
|
||||
<item name="android:paddingLeft">12dp</item>
|
||||
<item name="android:paddingRight">12dp</item>
|
||||
<item name="android:paddingBottom">15dp</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
</resources>
|
||||
|
@ -14,6 +14,11 @@
|
||||
fields. -->
|
||||
<dimen name="fxaccount_input_padding_vertical">10dp</dimen>
|
||||
|
||||
<!-- The amount of horizontal padding that appears around the show/hide
|
||||
password button. Vertical padding is provided by the adjacent input
|
||||
field. -->
|
||||
<dimen name="fxaccount_show_password_padding_horizontal">8dp</dimen>
|
||||
|
||||
<!-- Preference fragment padding, bottom -->
|
||||
<dimen name="preference_fragment_padding_bottom">0dp</dimen>
|
||||
<!-- Preference fragment padding, sides -->
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user