mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to inbound
This commit is contained in:
commit
84e41cc206
@ -671,6 +671,44 @@ SettingsListener.observe("accessibility.screenreader", false, function(value) {
|
||||
});
|
||||
})();
|
||||
|
||||
// =================== Low-precision buffer ======================
|
||||
(function setupLowPrecisionSettings() {
|
||||
// The gaia setting layers.low-precision maps to two gecko prefs
|
||||
SettingsListener.observe('layers.low-precision', null, function(value) {
|
||||
if (value !== null) {
|
||||
// Update gecko from the new Gaia setting
|
||||
Services.prefs.setBoolPref('layers.low-precision-buffer', value);
|
||||
Services.prefs.setBoolPref('layers.progressive-paint', value);
|
||||
} else {
|
||||
// Update gaia setting from gecko value
|
||||
try {
|
||||
let prefValue = Services.prefs.getBoolPref('layers.low-precision-buffer');
|
||||
let setting = { 'layers.low-precision': prefValue };
|
||||
window.navigator.mozSettings.createLock().set(setting);
|
||||
} catch (e) {
|
||||
console.log('Unable to read pref layers.low-precision-buffer: ' + e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// The gaia setting layers.low-opacity maps to a string gecko pref (0.5/1.0)
|
||||
SettingsListener.observe('layers.low-opacity', null, function(value) {
|
||||
if (value !== null) {
|
||||
// Update gecko from the new Gaia setting
|
||||
Services.prefs.setCharPref('layers.low-precision-opacity', value ? '0.5' : '1.0');
|
||||
} else {
|
||||
// Update gaia setting from gecko value
|
||||
try {
|
||||
let prefValue = Services.prefs.getCharPref('layers.low-precision-opacity');
|
||||
let setting = { 'layers.low-opacity': (prefValue == '0.5') };
|
||||
window.navigator.mozSettings.createLock().set(setting);
|
||||
} catch (e) {
|
||||
console.log('Unable to read pref layers.low-precision-opacity: ' + e);
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
// =================== Various simple mapping ======================
|
||||
let settingsToObserve = {
|
||||
'app.update.channel': {
|
||||
|
@ -19,8 +19,8 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="449d632c69b1a4bd5101d07d18f76799d3fd5f38"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="227354333a185180b85471f2cc6abfb029e44718"/>
|
||||
|
@ -17,8 +17,8 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="449d632c69b1a4bd5101d07d18f76799d3fd5f38"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
|
@ -15,9 +15,9 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="999e945b85c578c503ad445c2285940f16aacdae">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="449d632c69b1a4bd5101d07d18f76799d3fd5f38"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
|
@ -19,8 +19,8 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="449d632c69b1a4bd5101d07d18f76799d3fd5f38"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="227354333a185180b85471f2cc6abfb029e44718"/>
|
||||
|
@ -17,8 +17,8 @@
|
||||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="449d632c69b1a4bd5101d07d18f76799d3fd5f38"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
|
@ -4,6 +4,6 @@
|
||||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "d7d92199ea7e8f850ca0c6f0514bf596f178a16f",
|
||||
"revision": "4024e28dbc44b11d3297378484c2474dcee425fa",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -17,8 +17,8 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="449d632c69b1a4bd5101d07d18f76799d3fd5f38"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
|
@ -15,8 +15,8 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="449d632c69b1a4bd5101d07d18f76799d3fd5f38"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
|
@ -17,8 +17,8 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="449d632c69b1a4bd5101d07d18f76799d3fd5f38"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
|
@ -17,8 +17,8 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="449d632c69b1a4bd5101d07d18f76799d3fd5f38"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
|
@ -256,6 +256,7 @@ pref("browser.uitour.themeOrigin", "https://addons.mozilla.org/%LOCALE%/firefox/
|
||||
pref("browser.uitour.pinnedTabUrl", "https://support.mozilla.org/%LOCALE%/kb/pinned-tabs-keep-favorite-websites-open");
|
||||
pref("browser.uitour.url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/tour/");
|
||||
pref("browser.uitour.whitelist.add.260", "www.mozilla.org,support.mozilla.org");
|
||||
pref("browser.uitour.whitelist.add.340", "about:home");
|
||||
|
||||
pref("browser.customizemode.tip0.shown", false);
|
||||
pref("browser.customizemode.tip0.learnMoreUrl", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/customize");
|
||||
|
@ -14,7 +14,8 @@ const TESTCASE_URI_SCSS = TEST_BASE + "sourcemap-sass/sourcemaps.scss";
|
||||
const TESTCASE_URI_MAP = TEST_BASE + "sourcemap-css/sourcemaps.css.map";
|
||||
const TESTCASE_SCSS_NAME = "sourcemaps.scss";
|
||||
|
||||
const PREF = "devtools.styleeditor.source-maps-enabled";
|
||||
const SOURCE_MAP_PREF = "devtools.styleeditor.source-maps-enabled";
|
||||
const TRANSITIONS_PREF = "devtools.styleeditor.transitions";
|
||||
|
||||
const CSS_TEXT = "* { color: blue }";
|
||||
|
||||
@ -31,7 +32,8 @@ function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
Services.prefs.setBoolPref(PREF, true);
|
||||
Services.prefs.setBoolPref(SOURCE_MAP_PREF, true);
|
||||
Services.prefs.setBoolPref(TRANSITIONS_PREF, false);
|
||||
|
||||
Task.spawn(function() {
|
||||
// copy all our files over so we don't screw them up for other tests
|
||||
@ -126,7 +128,8 @@ function pauseForTimeChange() {
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
Services.prefs.clearUserPref(PREF);
|
||||
Services.prefs.clearUserPref(SOURCE_MAP_PREF);
|
||||
Services.prefs.clearUserPref(TRANSITIONS_PREF);
|
||||
finish();
|
||||
}
|
||||
|
||||
|
@ -50,11 +50,17 @@ let test = asyncTest(function*() {
|
||||
is(specificity, expected,
|
||||
'Selector "' + selectorText + '" has a specificity of ' + expected);
|
||||
}
|
||||
|
||||
info("Testing specificity of element.style");
|
||||
let colorProp = cssLogic.getPropertyInfo("background");
|
||||
is(colorProp.matchedSelectors[0].specificity, 0x01000000,
|
||||
"Element styles have specificity of 0x01000000 (16777216).");
|
||||
});
|
||||
|
||||
function createDocument() {
|
||||
let doc = content.document;
|
||||
doc.body.innerHTML = getStylesheetText();
|
||||
doc.body.style.background = "blue";
|
||||
doc.title = "Computed view specificity test";
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ window.addEventListener("load", function onLoad() {
|
||||
|
||||
// Buttons
|
||||
document.querySelector("#close").onclick = CloseUI;
|
||||
document.querySelector("#restoreButton").onclick = RestoreDefaults;
|
||||
document.querySelector("#restore").onclick = RestoreDefaults;
|
||||
document.querySelector("#manageSimulators").onclick = ShowAddons;
|
||||
|
||||
// Initialize the controls
|
||||
|
@ -19,6 +19,8 @@
|
||||
<body>
|
||||
|
||||
<div id="controls">
|
||||
<a id="restore">&prefs_restore;</a>
|
||||
<a id="manageSimulators">&prefs_simulators;</a>
|
||||
<a id="close">&deck_close;</a>
|
||||
</div>
|
||||
|
||||
@ -27,6 +29,12 @@
|
||||
<h2>&prefs_general_title;</h2>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<label title="&prefs_options_templatesurl_tooltip;">
|
||||
<span>&prefs_options_templatesurl;</span>
|
||||
<input data-pref="devtools.webide.templatesURL"/>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_enablelocalruntime_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.webide.enableLocalRuntime"/>
|
||||
@ -40,9 +48,9 @@
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_templatesurl_tooltip;">
|
||||
<span>&prefs_options_templatesurl;</span>
|
||||
<input data-pref="devtools.webide.templatesURL"/>
|
||||
<label title="&prefs_options_showeditor_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.webide.showProjectEditor"/>
|
||||
<span>&prefs_options_showeditor;</span>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
@ -50,36 +58,6 @@
|
||||
<h2>&prefs_editor_title;</h2>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<label title="&prefs_options_showeditor_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.webide.showProjectEditor"/>
|
||||
<span>&prefs_options_showeditor;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_autoclosebrackets_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.editor.autoclosebrackets"/>
|
||||
<span>&prefs_options_autoclosebrackets;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_autocomplete_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.editor.autocomplete"/>
|
||||
<span>&prefs_options_autocomplete;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_detectindentation_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.editor.detectindentation"/>
|
||||
<span>&prefs_options_detectindentation;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_expandtab_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.editor.expandtab"/>
|
||||
<span>&prefs_options_expandtab;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label><span>&prefs_options_tabsize;</span>
|
||||
<select data-pref="devtools.editor.tabsize">
|
||||
@ -89,10 +67,31 @@
|
||||
</select>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_expandtab_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.editor.expandtab"/>
|
||||
<span>&prefs_options_expandtab;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_detectindentation_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.editor.detectindentation"/>
|
||||
<span>&prefs_options_detectindentation;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_autocomplete_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.editor.autocomplete"/>
|
||||
<span>&prefs_options_autocomplete;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_autoclosebrackets_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.editor.autoclosebrackets"/>
|
||||
<span>&prefs_options_autoclosebrackets;</span>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<button id="manageSimulators">&prefs_simulators;</button>
|
||||
<button id="restoreButton">&prefs_restore;</button>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -108,15 +108,15 @@
|
||||
<!ENTITY prefs_options_templatesurl_tooltip "Index of available templates">
|
||||
<!ENTITY prefs_options_showeditor "Show editor">
|
||||
<!ENTITY prefs_options_showeditor_tooltip "Show internal editor">
|
||||
<!ENTITY prefs_options_detectindentation "Detect indentation">
|
||||
<!ENTITY prefs_options_tabsize "Tab size">
|
||||
<!ENTITY prefs_options_expandtab "Soft tabs">
|
||||
<!ENTITY prefs_options_expandtab_tooltip "Use spaces instead of the tab character">
|
||||
<!ENTITY prefs_options_detectindentation "Autoindent">
|
||||
<!ENTITY prefs_options_detectindentation_tooltip "Guess indentation based on source content">
|
||||
<!ENTITY prefs_options_autocomplete "Autocomplete">
|
||||
<!ENTITY prefs_options_autocomplete_tooltip "Enable code autocompletion">
|
||||
<!ENTITY prefs_options_autoclosebrackets "Autoclose brackets">
|
||||
<!ENTITY prefs_options_autoclosebrackets_tooltip "Automatically insert closing brackets">
|
||||
<!ENTITY prefs_options_expandtab "Indent using spaces">
|
||||
<!ENTITY prefs_options_expandtab_tooltip "Use spaces instead of the tab character">
|
||||
<!ENTITY prefs_options_autocomplete "Autocompletion">
|
||||
<!ENTITY prefs_options_autocomplete_tooltip "Enable code autocompletion">
|
||||
<!ENTITY prefs_options_tabsize "Tab size">
|
||||
|
||||
<!-- Permissions Table -->
|
||||
<!ENTITY permissionstable_title "Permissions Table">
|
||||
|
@ -37,9 +37,6 @@ h1 {
|
||||
font-size: small;
|
||||
cursor: pointer;
|
||||
border-bottom: 1px dotted;
|
||||
}
|
||||
|
||||
#close {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
|
@ -619,7 +619,7 @@ this.UITour = {
|
||||
},
|
||||
|
||||
isSafeScheme: function(aURI) {
|
||||
let allowedSchemes = new Set(["https"]);
|
||||
let allowedSchemes = new Set(["https", "about"]);
|
||||
if (!Services.prefs.getBoolPref("browser.uitour.requireSecure"))
|
||||
allowedSchemes.add("http");
|
||||
|
||||
|
@ -8,10 +8,10 @@ const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
// This module exposes a subset of the functionnalities of the parent DOM
|
||||
// Registry to content processes, to be be used from the AppsService component.
|
||||
// This module exposes a subset of the functionalities of the parent DOM
|
||||
// Registry to content processes, to be used from the AppsService component.
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["DOMApplicationRegistry"];
|
||||
this.EXPORTED_SYMBOLS = ["DOMApplicationRegistry", "WrappedManifestCache"];
|
||||
|
||||
Cu.import("resource://gre/modules/AppsUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
@ -20,54 +20,324 @@ function debug(s) {
|
||||
//dump("-*- AppsServiceChild.jsm: " + s + "\n");
|
||||
}
|
||||
|
||||
const APPS_IPC_MSG_NAMES = [
|
||||
"Webapps:AddApp",
|
||||
"Webapps:RemoveApp",
|
||||
"Webapps:UpdateApp",
|
||||
"Webapps:CheckForUpdate:Return:KO",
|
||||
"Webapps:FireEvent",
|
||||
"Webapps:UpdateState"
|
||||
];
|
||||
|
||||
// A simple cache for the wrapped manifests.
|
||||
this.WrappedManifestCache = {
|
||||
_cache: { },
|
||||
|
||||
// Gets an entry from the cache, and populates the cache if needed.
|
||||
get: function mcache_get(aManifestURL, aManifest, aWindow, aInnerWindowID) {
|
||||
if (!aManifest) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(aManifestURL in this._cache)) {
|
||||
this._cache[aManifestURL] = { };
|
||||
}
|
||||
|
||||
let winObjs = this._cache[aManifestURL];
|
||||
if (!(aInnerWindowID in winObjs)) {
|
||||
winObjs[aInnerWindowID] = Cu.cloneInto(aManifest, aWindow);
|
||||
}
|
||||
|
||||
return winObjs[aInnerWindowID];
|
||||
},
|
||||
|
||||
// Invalidates an entry in the cache.
|
||||
evict: function mcache_evict(aManifestURL, aInnerWindowID) {
|
||||
debug("Evicting manifest " + aManifestURL + " window ID " +
|
||||
aInnerWindowID);
|
||||
if (aManifestURL in this._cache) {
|
||||
let winObjs = this._cache[aManifestURL];
|
||||
if (aInnerWindowID in winObjs) {
|
||||
delete winObjs[aInnerWindowID];
|
||||
}
|
||||
|
||||
if (Object.keys(winObjs).length == 0) {
|
||||
delete this._cache[aManifestURL];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
// Clear the cache on memory pressure.
|
||||
this._cache = { };
|
||||
Cu.forceGC();
|
||||
},
|
||||
|
||||
init: function() {
|
||||
Services.obs.addObserver(this, "memory-pressure", false);
|
||||
}
|
||||
};
|
||||
|
||||
this.WrappedManifestCache.init();
|
||||
|
||||
|
||||
// DOMApplicationRegistry keeps a cache containing a list of apps in the device.
|
||||
// This information is updated with the data received from the main process and
|
||||
// it is queried by the DOM objects to set their state.
|
||||
// This module handle all the messages broadcasted from the parent process,
|
||||
// including DOM events, which are dispatched to the corresponding DOM objects.
|
||||
|
||||
this.DOMApplicationRegistry = {
|
||||
// DOMApps will hold a list of arrays of weak references to
|
||||
// mozIDOMApplication objects indexed by manifest URL.
|
||||
DOMApps: {},
|
||||
|
||||
ready: false,
|
||||
webapps: null,
|
||||
|
||||
init: function init() {
|
||||
debug("init");
|
||||
this.cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
|
||||
.getService(Ci.nsISyncMessageSender);
|
||||
|
||||
["Webapps:AddApp", "Webapps:RemoveApp"].forEach((function(aMsgName) {
|
||||
APPS_IPC_MSG_NAMES.forEach((function(aMsgName) {
|
||||
this.cpmm.addMessageListener(aMsgName, this);
|
||||
}).bind(this));
|
||||
|
||||
// We need to prime the cache with the list of apps.
|
||||
// XXX shoud we do this async and block callers if it's not yet there?
|
||||
this.webapps = this.cpmm.sendSyncMessage("Webapps:GetList", { })[0];
|
||||
this.cpmm.sendAsyncMessage("Webapps:RegisterForMessages", {
|
||||
messages: APPS_IPC_MSG_NAMES
|
||||
});
|
||||
|
||||
// We need to prime the cache with the list of apps.
|
||||
let list = this.cpmm.sendSyncMessage("Webapps:GetList", { })[0];
|
||||
this.webapps = list.webapps;
|
||||
// We need a fast mapping from localId -> app, so we add an index.
|
||||
// We also add the manifest to the app object.
|
||||
this.localIdIndex = { };
|
||||
for (let id in this.webapps) {
|
||||
let app = this.webapps[id];
|
||||
this.localIdIndex[app.localId] = app;
|
||||
app.manifest = list.manifests[id];
|
||||
}
|
||||
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
// cpmm.addMessageListener causes the DOMApplicationRegistry object to live
|
||||
// forever if we don't clean up properly.
|
||||
// cpmm.addMessageListener causes the DOMApplicationRegistry object to
|
||||
// live forever if we don't clean up properly.
|
||||
this.webapps = null;
|
||||
["Webapps:AddApp", "Webapps:RemoveApp"].forEach((function(aMsgName) {
|
||||
this.DOMApps = null;
|
||||
|
||||
APPS_IPC_MSG_NAMES.forEach((aMsgName) => {
|
||||
this.cpmm.removeMessageListener(aMsgName, this);
|
||||
}).bind(this));
|
||||
});
|
||||
|
||||
this.cpmm.sendAsyncMessage("Webapps:UnregisterForMessages",
|
||||
APPS_IPC_MSG_NAMES)
|
||||
},
|
||||
|
||||
receiveMessage: function receiveMessage(aMessage) {
|
||||
debug("Received " + aMessage.name + " message.");
|
||||
let msg = aMessage.json;
|
||||
let msg = aMessage.data;
|
||||
switch (aMessage.name) {
|
||||
case "Webapps:AddApp":
|
||||
this.webapps[msg.id] = msg.app;
|
||||
this.localIdIndex[msg.app.localId] = msg.app;
|
||||
if (msg.manifest) {
|
||||
this.webapps[msg.id].manifest = msg.manifest;
|
||||
}
|
||||
break;
|
||||
case "Webapps:RemoveApp":
|
||||
delete this.DOMApps[this.webapps[msg.id].manifestURL];
|
||||
delete this.localIdIndex[this.webapps[msg.id].localId];
|
||||
delete this.webapps[msg.id];
|
||||
break;
|
||||
case "Webapps:UpdateApp":
|
||||
let app = this.webapps[msg.oldId];
|
||||
if (!app) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.app) {
|
||||
for (let prop in msg.app) {
|
||||
app[prop] = msg.app[prop];
|
||||
}
|
||||
}
|
||||
|
||||
this.webapps[msg.newId] = app;
|
||||
this.localIdIndex[app.localId] = app;
|
||||
delete this.webapps[msg.oldId];
|
||||
|
||||
let apps = this.DOMApps[msg.app.manifestURL];
|
||||
if (!apps) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < apps.length; i++) {
|
||||
let domApp = apps[i].get();
|
||||
if (!domApp || domApp._window === null) {
|
||||
apps.splice(i, 1);
|
||||
continue;
|
||||
}
|
||||
domApp._proxy = new Proxy(domApp, {
|
||||
get: function(target, prop) {
|
||||
if (!DOMApplicationRegistry.webapps[msg.newId]) {
|
||||
return;
|
||||
}
|
||||
return DOMApplicationRegistry.webapps[msg.newId][prop];
|
||||
},
|
||||
set: function(target, prop, val) {
|
||||
if (!DOMApplicationRegistry.webapps[msg.newId]) {
|
||||
return;
|
||||
}
|
||||
DOMApplicationRegistry.webapps[msg.newId][prop] = val;
|
||||
return;
|
||||
},
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "Webapps:FireEvent":
|
||||
this._fireEvent(aMessage);
|
||||
break;
|
||||
case "Webapps:UpdateState":
|
||||
this._updateState(msg);
|
||||
break;
|
||||
case "Webapps:CheckForUpdate:Return:KO":
|
||||
let DOMApps = this.DOMApps[msg.manifestURL];
|
||||
if (!DOMApps || !msg.requestID) {
|
||||
return;
|
||||
}
|
||||
DOMApps.forEach((DOMApp) => {
|
||||
let domApp = DOMApp.get();
|
||||
if (domApp && msg.requestID) {
|
||||
domApp._fireRequestResult(aMessage, true /* aIsError */);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* mozIDOMApplication management
|
||||
*/
|
||||
|
||||
// Every time a DOM app is created, we save a weak reference to it that will
|
||||
// be used to dispatch events and fire request results.
|
||||
addDOMApp: function(aApp, aManifestURL, aId) {
|
||||
let weakRef = Cu.getWeakReference(aApp);
|
||||
|
||||
if (!this.DOMApps[aManifestURL]) {
|
||||
this.DOMApps[aManifestURL] = [];
|
||||
}
|
||||
|
||||
let apps = this.DOMApps[aManifestURL];
|
||||
|
||||
// Get rid of dead weak references.
|
||||
for (let i = 0; i < apps.length; i++) {
|
||||
let app = apps[i].get();
|
||||
if (!app || app._window === null) {
|
||||
apps.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
apps.push(weakRef);
|
||||
|
||||
// Each DOM app contains a proxy object used to build their state. We
|
||||
// return the handler for this proxy object with traps to get and set
|
||||
// app properties kept in the DOMApplicationRegistry app cache.
|
||||
return {
|
||||
get: function(target, prop) {
|
||||
if (!DOMApplicationRegistry.webapps[aId]) {
|
||||
return;
|
||||
}
|
||||
return DOMApplicationRegistry.webapps[aId][prop];
|
||||
},
|
||||
set: function(target, prop, val) {
|
||||
if (!DOMApplicationRegistry.webapps[aId]) {
|
||||
return;
|
||||
}
|
||||
DOMApplicationRegistry.webapps[aId][prop] = val;
|
||||
return;
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
_fireEvent: function(aMessage) {
|
||||
let msg = aMessage.data;
|
||||
debug("_fireEvent " + JSON.stringify(msg));
|
||||
if (!this.DOMApps || !msg.manifestURL || !msg.eventType) {
|
||||
return;
|
||||
}
|
||||
|
||||
let DOMApps = this.DOMApps[msg.manifestURL];
|
||||
if (!DOMApps) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The parent might ask childs to trigger more than one event in one
|
||||
// shot, so in order to avoid needless IPC we allow an array for the
|
||||
// 'eventType' IPC message field.
|
||||
if (!Array.isArray(msg.eventType)) {
|
||||
msg.eventType = [msg.eventType];
|
||||
}
|
||||
|
||||
DOMApps.forEach((DOMApp) => {
|
||||
let domApp = DOMApp.get();
|
||||
if (!domApp) {
|
||||
return;
|
||||
}
|
||||
msg.eventType.forEach((aEventType) => {
|
||||
if ('on' + aEventType in domApp) {
|
||||
domApp._fireEvent(aEventType);
|
||||
}
|
||||
});
|
||||
|
||||
if (msg.requestID) {
|
||||
aMessage.data.result = msg.manifestURL;
|
||||
domApp._fireRequestResult(aMessage);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_updateState: function(aMessage) {
|
||||
if (!this.DOMApps || !aMessage.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let app = this.webapps[aMessage.id];
|
||||
if (!app) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aMessage.app) {
|
||||
for (let prop in aMessage.app) {
|
||||
app[prop] = aMessage.app[prop];
|
||||
}
|
||||
}
|
||||
|
||||
if ("error" in aMessage) {
|
||||
app.downloadError = aMessage.error;
|
||||
}
|
||||
|
||||
if (aMessage.manifest) {
|
||||
app.manifest = aMessage.manifest;
|
||||
// Evict the wrapped manifest cache for all the affected DOM objects.
|
||||
let DOMApps = this.DOMApps[app.manifestURL];
|
||||
if (!DOMApps) {
|
||||
return;
|
||||
}
|
||||
DOMApps.forEach((DOMApp) => {
|
||||
let domApp = DOMApp.get();
|
||||
if (!domApp) {
|
||||
return;
|
||||
}
|
||||
WrappedManifestCache.evict(app.manifestURL, domApp.innerWindowID);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* nsIAppsService API
|
||||
*/
|
||||
getAppByManifestURL: function getAppByManifestURL(aManifestURL) {
|
||||
debug("getAppByManifestURL " + aManifestURL);
|
||||
return AppsUtils.getAppByManifestURL(this.webapps, aManifestURL);
|
||||
@ -89,7 +359,7 @@ this.DOMApplicationRegistry = {
|
||||
},
|
||||
|
||||
getAppByLocalId: function getAppByLocalId(aLocalId) {
|
||||
debug("getAppByLocalId " + aLocalId);
|
||||
debug("getAppByLocalId " + aLocalId + " - ready: " + this.ready);
|
||||
let app = this.localIdIndex[aLocalId];
|
||||
if (!app) {
|
||||
debug("Ouch, No app!");
|
||||
|
@ -12,6 +12,7 @@ Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
|
||||
Cu.import("resource://gre/modules/AppsUtils.jsm");
|
||||
Cu.import("resource://gre/modules/BrowserElementPromptService.jsm");
|
||||
Cu.import("resource://gre/modules/AppsServiceChild.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
@ -278,50 +279,9 @@ WebappsRegistry.prototype = {
|
||||
* mozIDOMApplication object
|
||||
*/
|
||||
|
||||
// A simple cache for the wrapped manifests.
|
||||
let manifestCache = {
|
||||
_cache: { },
|
||||
|
||||
// Gets an entry from the cache, and populates the cache if needed.
|
||||
get: function mcache_get(aManifestURL, aManifest, aWindow, aInnerWindowID) {
|
||||
if (!(aManifestURL in this._cache)) {
|
||||
this._cache[aManifestURL] = { };
|
||||
}
|
||||
|
||||
let winObjs = this._cache[aManifestURL];
|
||||
if (!(aInnerWindowID in winObjs)) {
|
||||
winObjs[aInnerWindowID] = Cu.cloneInto(aManifest, aWindow);
|
||||
}
|
||||
|
||||
return winObjs[aInnerWindowID];
|
||||
},
|
||||
|
||||
// Invalidates an entry in the cache.
|
||||
evict: function mcache_evict(aManifestURL, aInnerWindowID) {
|
||||
if (aManifestURL in this._cache) {
|
||||
let winObjs = this._cache[aManifestURL];
|
||||
if (aInnerWindowID in winObjs) {
|
||||
delete winObjs[aInnerWindowID];
|
||||
}
|
||||
|
||||
if (Object.keys(winObjs).length == 0) {
|
||||
delete this._cache[aManifestURL];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
// Clear the cache on memory pressure.
|
||||
this._cache = { };
|
||||
},
|
||||
|
||||
init: function() {
|
||||
Services.obs.addObserver(this, "memory-pressure", false);
|
||||
}
|
||||
};
|
||||
|
||||
function createApplicationObject(aWindow, aApp) {
|
||||
let app = Cc["@mozilla.org/webapps/application;1"].createInstance(Ci.mozIDOMApplication);
|
||||
let app = Cc["@mozilla.org/webapps/application;1"]
|
||||
.createInstance(Ci.mozIDOMApplication);
|
||||
app.wrappedJSObject.init(aWindow, aApp);
|
||||
return app;
|
||||
}
|
||||
@ -334,27 +294,12 @@ WebappsApplication.prototype = {
|
||||
__proto__: DOMRequestIpcHelper.prototype,
|
||||
|
||||
init: function(aWindow, aApp) {
|
||||
let proxyHandler = DOMApplicationRegistry.addDOMApp(this,
|
||||
aApp.manifestURL,
|
||||
aApp.id);
|
||||
this._proxy = new Proxy(this, proxyHandler);
|
||||
|
||||
this._window = aWindow;
|
||||
let principal = this._window.document.nodePrincipal;
|
||||
this._appStatus = principal.appStatus;
|
||||
this.origin = aApp.origin;
|
||||
this._manifest = aApp.manifest;
|
||||
this._updateManifest = aApp.updateManifest;
|
||||
this.manifestURL = aApp.manifestURL;
|
||||
this.receipts = aApp.receipts;
|
||||
this.installOrigin = aApp.installOrigin;
|
||||
this.installTime = aApp.installTime;
|
||||
this.installState = aApp.installState || "installed";
|
||||
this.removable = aApp.removable;
|
||||
this.lastUpdateCheck = aApp.lastUpdateCheck ? aApp.lastUpdateCheck
|
||||
: Date.now();
|
||||
this.updateTime = aApp.updateTime ? aApp.updateTime
|
||||
: aApp.installTime;
|
||||
this.progress = NaN;
|
||||
this.downloadAvailable = aApp.downloadAvailable;
|
||||
this.downloading = aApp.downloading;
|
||||
this.readyToApplyDownload = aApp.readyToApplyDownload;
|
||||
this.downloadSize = aApp.downloadSize || 0;
|
||||
|
||||
this._onprogress = null;
|
||||
this._ondownloadsuccess = null;
|
||||
@ -362,40 +307,83 @@ WebappsApplication.prototype = {
|
||||
this._ondownloadavailable = null;
|
||||
this._ondownloadapplied = null;
|
||||
|
||||
this._downloadError = null;
|
||||
this.initDOMRequestHelper(aWindow);
|
||||
},
|
||||
|
||||
this.initDOMRequestHelper(aWindow, [
|
||||
{ name: "Webapps:CheckForUpdate:Return:KO", weakRef: true },
|
||||
{ name: "Webapps:Connect:Return:OK", weakRef: true },
|
||||
{ name: "Webapps:Connect:Return:KO", weakRef: true },
|
||||
{ name: "Webapps:FireEvent", weakRef: true },
|
||||
{ name: "Webapps:GetConnections:Return:OK", weakRef: true },
|
||||
{ name: "Webapps:UpdateState", weakRef: true }
|
||||
]);
|
||||
get _appStatus() {
|
||||
return this._proxy.appStatus;
|
||||
},
|
||||
|
||||
cpmm.sendAsyncMessage("Webapps:RegisterForMessages", {
|
||||
messages: ["Webapps:FireEvent",
|
||||
"Webapps:UpdateState"],
|
||||
app: {
|
||||
id: this.id,
|
||||
manifestURL: this.manifestURL,
|
||||
installState: this.installState,
|
||||
downloading: this.downloading
|
||||
}
|
||||
});
|
||||
get downloadAvailable() {
|
||||
return this._proxy.downloadAvailable;
|
||||
},
|
||||
|
||||
get downloading() {
|
||||
return this._proxy.downloading;
|
||||
},
|
||||
|
||||
get downloadSize() {
|
||||
return this._proxy.downloadSize;
|
||||
},
|
||||
|
||||
get installOrigin() {
|
||||
return this._proxy.installOrigin;
|
||||
},
|
||||
|
||||
get installState() {
|
||||
return this._proxy.installState;
|
||||
},
|
||||
|
||||
get installTime() {
|
||||
return this._proxy.installTime;
|
||||
},
|
||||
|
||||
get lastUpdateCheck() {
|
||||
return this._proxy.lastUpdateCheck;
|
||||
},
|
||||
|
||||
get manifestURL() {
|
||||
return this._proxy.manifestURL;
|
||||
},
|
||||
|
||||
get origin() {
|
||||
return this._proxy.origin;
|
||||
},
|
||||
|
||||
get progress() {
|
||||
return this._proxy.progress;
|
||||
},
|
||||
|
||||
get readyToApplyDownload() {
|
||||
return this._proxy.readyToApplyDownload;
|
||||
},
|
||||
|
||||
get receipts() {
|
||||
return this._proxy.receipts;
|
||||
},
|
||||
|
||||
set receipts(aReceipts) {
|
||||
this._proxy.receipts = aReceipts;
|
||||
},
|
||||
|
||||
get removable() {
|
||||
return this._proxy.removable;
|
||||
},
|
||||
|
||||
get updateTime() {
|
||||
return this._proxy.updateTime;
|
||||
},
|
||||
|
||||
get manifest() {
|
||||
return manifestCache.get(this.manifestURL,
|
||||
this._manifest,
|
||||
this._window,
|
||||
this.innerWindowID);
|
||||
return WrappedManifestCache.get(this.manifestURL,
|
||||
this._proxy.manifest,
|
||||
this._window,
|
||||
this.innerWindowID);
|
||||
},
|
||||
|
||||
get updateManifest() {
|
||||
return this.updateManifest =
|
||||
this._updateManifest ? Cu.cloneInto(this._updateManifest, this._window)
|
||||
: null;
|
||||
return this._proxy.updateManifest ?
|
||||
Cu.cloneInto(this._proxy.updateManifest, this._window) : null;
|
||||
},
|
||||
|
||||
set onprogress(aCallback) {
|
||||
@ -440,10 +428,10 @@ WebappsApplication.prototype = {
|
||||
|
||||
get downloadError() {
|
||||
// Only return DOMError when we have an error.
|
||||
if (!this._downloadError) {
|
||||
if (!this._proxy.downloadError) {
|
||||
return null;
|
||||
}
|
||||
return new this._window.DOMError(this._downloadError);
|
||||
return new this._window.DOMError(this._proxy.downloadError);
|
||||
},
|
||||
|
||||
download: function() {
|
||||
@ -485,12 +473,11 @@ WebappsApplication.prototype = {
|
||||
BrowserElementPromptService.getBrowserElementChildForWindow(this._window);
|
||||
if (browserChild) {
|
||||
this.addMessageListeners("Webapps:ClearBrowserData:Return");
|
||||
browserChild.messageManager.sendAsyncMessage(
|
||||
"Webapps:ClearBrowserData",
|
||||
{ manifestURL: this.manifestURL,
|
||||
oid: this._id,
|
||||
requestID: this.getRequestId(request) }
|
||||
);
|
||||
browserChild.messageManager.sendAsyncMessage("Webapps:ClearBrowserData", {
|
||||
manifestURL: this.manifestURL,
|
||||
oid: this._id,
|
||||
requestID: this.getRequestId(request)
|
||||
});
|
||||
} else {
|
||||
Services.DOMRequest.fireErrorAsync(request, "NO_CLEARABLE_BROWSER");
|
||||
}
|
||||
@ -498,28 +485,33 @@ WebappsApplication.prototype = {
|
||||
},
|
||||
|
||||
connect: function(aKeyword, aRules) {
|
||||
this.addMessageListeners(["Webapps:Connect:Return:OK",
|
||||
"Webapps:Connect:Return:KO"]);
|
||||
return this.createPromise(function (aResolve, aReject) {
|
||||
cpmm.sendAsyncMessage("Webapps:Connect",
|
||||
{ keyword: aKeyword,
|
||||
rules: aRules,
|
||||
manifestURL: this.manifestURL,
|
||||
outerWindowID: this._id,
|
||||
requestID: this.getPromiseResolverId({
|
||||
resolve: aResolve,
|
||||
reject: aReject
|
||||
})});
|
||||
cpmm.sendAsyncMessage("Webapps:Connect", {
|
||||
keyword: aKeyword,
|
||||
rules: aRules,
|
||||
manifestURL: this.manifestURL,
|
||||
outerWindowID: this._id,
|
||||
requestID: this.getPromiseResolverId({
|
||||
resolve: aResolve,
|
||||
reject: aReject
|
||||
})
|
||||
});
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
getConnections: function() {
|
||||
this.addMessageListeners("Webapps:GetConnections:Return:OK");
|
||||
return this.createPromise(function (aResolve, aReject) {
|
||||
cpmm.sendAsyncMessage("Webapps:GetConnections",
|
||||
{ manifestURL: this.manifestURL,
|
||||
outerWindowID: this._id,
|
||||
requestID: this.getPromiseResolverId({
|
||||
resolve: aResolve,
|
||||
reject: aReject
|
||||
})});
|
||||
cpmm.sendAsyncMessage("Webapps:GetConnections", {
|
||||
manifestURL: this.manifestURL,
|
||||
outerWindowID: this._id,
|
||||
requestID: this.getPromiseResolverId({
|
||||
resolve: aResolve,
|
||||
reject: aReject
|
||||
})
|
||||
});
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
@ -568,12 +560,7 @@ WebappsApplication.prototype = {
|
||||
|
||||
uninit: function() {
|
||||
this._onprogress = null;
|
||||
cpmm.sendAsyncMessage("Webapps:UnregisterForMessages", [
|
||||
"Webapps:FireEvent",
|
||||
"Webapps:UpdateState"
|
||||
]);
|
||||
|
||||
manifestCache.evict(this.manifestURL, this.innerWindowID);
|
||||
WrappedManifestCache.evict(this.manifestURL, this.innerWindowID);
|
||||
},
|
||||
|
||||
_fireEvent: function(aName) {
|
||||
@ -590,22 +577,16 @@ WebappsApplication.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_updateState: function(aMsg) {
|
||||
if (aMsg.app) {
|
||||
for (let prop in aMsg.app) {
|
||||
this[prop] = aMsg.app[prop];
|
||||
}
|
||||
_fireRequestResult: function(aMessage, aIsError) {
|
||||
let req;
|
||||
let msg = aMessage.data;
|
||||
req = this.takeRequest(msg.requestID);
|
||||
if (!req) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Intentional use of 'in' so we unset the error if this is explicitly null.
|
||||
if ('error' in aMsg) {
|
||||
this._downloadError = aMsg.error;
|
||||
}
|
||||
|
||||
if (aMsg.manifest) {
|
||||
this._manifest = aMsg.manifest;
|
||||
manifestCache.evict(this.manifestURL, this.innerWindowID);
|
||||
}
|
||||
aIsError ? Services.DOMRequest.fireError(req, msg.error)
|
||||
: Services.DOMRequest.fireSuccess(req, msg.result);
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
@ -619,10 +600,7 @@ WebappsApplication.prototype = {
|
||||
req = this.takeRequest(msg.requestID);
|
||||
}
|
||||
|
||||
// ondownload* callbacks should be triggered on all app instances
|
||||
if ((msg.oid != this._id || !req) &&
|
||||
aMessage.name !== "Webapps:FireEvent" &&
|
||||
aMessage.name !== "Webapps:UpdateState") {
|
||||
if (msg.oid !== this._id || !req) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -637,51 +615,13 @@ WebappsApplication.prototype = {
|
||||
"Webapps:Launch:Return:KO"]);
|
||||
Services.DOMRequest.fireSuccess(req, null);
|
||||
break;
|
||||
case "Webapps:CheckForUpdate:Return:KO":
|
||||
Services.DOMRequest.fireError(req, msg.error);
|
||||
break;
|
||||
case "Webapps:FireEvent":
|
||||
if (msg.manifestURL != this.manifestURL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The parent might ask childs to trigger more than one event in one
|
||||
// shot, so in order to avoid needless IPC we allow an array for the
|
||||
// 'eventType' IPC message field.
|
||||
if (!Array.isArray(msg.eventType)) {
|
||||
msg.eventType = [msg.eventType];
|
||||
}
|
||||
|
||||
msg.eventType.forEach((aEventType) => {
|
||||
// If we are in a successful state clear any past errors.
|
||||
if (aEventType === 'downloadapplied' ||
|
||||
aEventType === 'downloadsuccess') {
|
||||
this._downloadError = null;
|
||||
}
|
||||
|
||||
if ("_on" + aEventType in this) {
|
||||
this._fireEvent(aEventType);
|
||||
} else {
|
||||
dump("Unsupported event type " + aEventType + "\n");
|
||||
}
|
||||
});
|
||||
|
||||
if (req) {
|
||||
Services.DOMRequest.fireSuccess(req, this.manifestURL);
|
||||
}
|
||||
break;
|
||||
case "Webapps:UpdateState":
|
||||
if (msg.manifestURL != this.manifestURL) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._updateState(msg);
|
||||
break;
|
||||
case "Webapps:ClearBrowserData:Return":
|
||||
this.removeMessageListeners(aMessage.name);
|
||||
Services.DOMRequest.fireSuccess(req, null);
|
||||
break;
|
||||
case "Webapps:Connect:Return:OK":
|
||||
this.removeMessageListeners(["Webapps:Connect:Return:OK",
|
||||
"Webapps:Connect:Return:KO"]);
|
||||
let messagePorts = [];
|
||||
msg.messagePortIDs.forEach((aPortID) => {
|
||||
let port = new this._window.MozInterAppMessagePort(aPortID);
|
||||
@ -690,9 +630,12 @@ WebappsApplication.prototype = {
|
||||
req.resolve(messagePorts);
|
||||
break;
|
||||
case "Webapps:Connect:Return:KO":
|
||||
this.removeMessageListeners(["Webapps:Connect:Return:OK",
|
||||
"Webapps:Connect:Return:KO"]);
|
||||
req.reject("No connections registered");
|
||||
break;
|
||||
case "Webapps:GetConnections:Return:OK":
|
||||
this.removeMessageListeners(aMessage.name);
|
||||
let connections = [];
|
||||
msg.connections.forEach((aConnection) => {
|
||||
let connection =
|
||||
@ -805,7 +748,6 @@ WebappsApplicationMgmt.prototype = {
|
||||
},
|
||||
|
||||
uninstall: function(aApp) {
|
||||
dump("-- webapps.js uninstall " + aApp.manifestURL + "\n");
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("Webapps:Uninstall", { origin: aApp.origin,
|
||||
manifestURL: aApp.manifestURL,
|
||||
@ -874,12 +816,8 @@ WebappsApplicationMgmt.prototype = {
|
||||
break;
|
||||
case "Webapps:Uninstall:Broadcast:Return:OK":
|
||||
if (this._onuninstall) {
|
||||
let detail = {
|
||||
manifestURL: msg.manifestURL,
|
||||
origin: msg.origin
|
||||
};
|
||||
let event = new this._window.MozApplicationEvent("applicationuninstall",
|
||||
{ application : createApplicationObject(this._window, detail) });
|
||||
{ application : createApplicationObject(this._window, msg) });
|
||||
this._onuninstall.handleEvent(event);
|
||||
}
|
||||
break;
|
||||
@ -908,7 +846,5 @@ WebappsApplicationMgmt.prototype = {
|
||||
classDescription: "Webapps Application Mgmt"})
|
||||
}
|
||||
|
||||
manifestCache.init();
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsRegistry,
|
||||
WebappsApplication]);
|
||||
|
@ -179,6 +179,7 @@ this.DOMApplicationRegistry = {
|
||||
}).bind(this));
|
||||
|
||||
cpmm.addMessageListener("Activities:Register:OK", this);
|
||||
cpmm.addMessageListener("Activities:Register:KO", this);
|
||||
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||
Services.obs.addObserver(this, "memory-pressure", false);
|
||||
@ -275,9 +276,15 @@ this.DOMApplicationRegistry = {
|
||||
return this._registryStarted.promise;
|
||||
},
|
||||
|
||||
// The registry will be safe to clone when this promise is resolved.
|
||||
_safeToClone: Promise.defer(),
|
||||
|
||||
// Notify we are done with registering apps and save a copy of the registry.
|
||||
_registryReady: Promise.defer(),
|
||||
notifyAppsRegistryReady: function notifyAppsRegistryReady() {
|
||||
// Usually this promise will be resolved earlier, but just in case,
|
||||
// resolve it here also.
|
||||
this._safeToClone.resolve();
|
||||
this._registryReady.resolve();
|
||||
Services.obs.notifyObservers(this, "webapps-registry-ready", null);
|
||||
this._saveApps();
|
||||
@ -287,6 +294,10 @@ this.DOMApplicationRegistry = {
|
||||
return this._registryReady.promise;
|
||||
},
|
||||
|
||||
get safeToClone() {
|
||||
return this._safeToClone.promise;
|
||||
},
|
||||
|
||||
// Ensure that the .to property in redirects is a relative URL.
|
||||
sanitizeRedirects: function sanitizeRedirects(aSource) {
|
||||
if (!aSource) {
|
||||
@ -962,6 +973,7 @@ this.DOMApplicationRegistry = {
|
||||
this._registerInterAppConnections(manifest, app);
|
||||
appsToRegister.push({ manifest: manifest, app: app });
|
||||
});
|
||||
this._safeToClone.resolve();
|
||||
this._registerActivitiesForApps(appsToRegister, aRunUpdate);
|
||||
});
|
||||
},
|
||||
@ -1089,88 +1101,114 @@ this.DOMApplicationRegistry = {
|
||||
let mm = aMessage.target;
|
||||
msg.mm = mm;
|
||||
|
||||
let processedImmediately = true;
|
||||
|
||||
// There are two kind of messages: the messages that only make sense once the
|
||||
// registry is ready, and those that can (or have to) be treated as soon as
|
||||
// they're received.
|
||||
switch (aMessage.name) {
|
||||
case "Webapps:Install": {
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
Services.obs.notifyObservers(mm, "webapps-runtime-install", JSON.stringify(msg));
|
||||
#else
|
||||
this.doInstall(msg, mm);
|
||||
#endif
|
||||
case "Activities:Register:KO":
|
||||
dump("Activities didn't register correctly!");
|
||||
case "Activities:Register:OK":
|
||||
// Activities:Register:OK is special because it's one way the registryReady
|
||||
// promise can be resolved.
|
||||
// XXX: What to do when the activities registration failed? At this point
|
||||
// just act as if nothing happened.
|
||||
this.notifyAppsRegistryReady();
|
||||
break;
|
||||
}
|
||||
case "Webapps:GetSelf":
|
||||
this.getSelf(msg, mm);
|
||||
case "Webapps:GetList":
|
||||
// GetList is special because it's synchronous. So far so well, it's the
|
||||
// only synchronous message, if we get more at some point they should get
|
||||
// this treatment also.
|
||||
return this.doGetList();
|
||||
case "child-process-shutdown":
|
||||
this.removeMessageListener(["Webapps:Internal:AllMessages"], mm);
|
||||
break;
|
||||
case "Webapps:Uninstall":
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
Services.obs.notifyObservers(mm, "webapps-runtime-uninstall", JSON.stringify(msg));
|
||||
#else
|
||||
this.doUninstall(msg, mm);
|
||||
#endif
|
||||
break;
|
||||
case "Webapps:Launch":
|
||||
this.doLaunch(msg, mm);
|
||||
break;
|
||||
case "Webapps:CheckInstalled":
|
||||
this.checkInstalled(msg, mm);
|
||||
break;
|
||||
case "Webapps:GetInstalled":
|
||||
this.getInstalled(msg, mm);
|
||||
break;
|
||||
case "Webapps:GetNotInstalled":
|
||||
this.getNotInstalled(msg, mm);
|
||||
break;
|
||||
case "Webapps:GetAll":
|
||||
this.doGetAll(msg, mm);
|
||||
break;
|
||||
case "Webapps:InstallPackage": {
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
Services.obs.notifyObservers(mm, "webapps-runtime-install-package", JSON.stringify(msg));
|
||||
#else
|
||||
this.doInstallPackage(msg, mm);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case "Webapps:RegisterForMessages":
|
||||
this.addMessageListener(msg.messages, msg.app, mm);
|
||||
break;
|
||||
case "Webapps:UnregisterForMessages":
|
||||
this.removeMessageListener(msg, mm);
|
||||
break;
|
||||
case "child-process-shutdown":
|
||||
this.removeMessageListener(["Webapps:Internal:AllMessages"], mm);
|
||||
break;
|
||||
case "Webapps:GetList":
|
||||
this.addMessageListener(["Webapps:AddApp", "Webapps:RemoveApp"], null, mm);
|
||||
return this.webapps;
|
||||
case "Webapps:Download":
|
||||
this.startDownload(msg.manifestURL);
|
||||
break;
|
||||
case "Webapps:CancelDownload":
|
||||
this.cancelDownload(msg.manifestURL);
|
||||
break;
|
||||
case "Webapps:CheckForUpdate":
|
||||
this.checkForUpdate(msg, mm);
|
||||
break;
|
||||
case "Webapps:ApplyDownload":
|
||||
this.applyDownload(msg.manifestURL);
|
||||
break;
|
||||
case "Activities:Register:OK":
|
||||
this.notifyAppsRegistryReady();
|
||||
break;
|
||||
case "Webapps:Install:Return:Ack":
|
||||
this.onInstallSuccessAck(msg.manifestURL);
|
||||
break;
|
||||
case "Webapps:AddReceipt":
|
||||
this.addReceipt(msg, mm);
|
||||
break;
|
||||
case "Webapps:RemoveReceipt":
|
||||
this.removeReceipt(msg, mm);
|
||||
break;
|
||||
case "Webapps:ReplaceReceipt":
|
||||
this.replaceReceipt(msg, mm);
|
||||
break;
|
||||
default:
|
||||
processedImmediately = false;
|
||||
}
|
||||
|
||||
if (processedImmediately) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For all the rest (asynchronous), we wait till the registry is ready
|
||||
// before processing the message.
|
||||
this.registryReady.then( () => {
|
||||
switch (aMessage.name) {
|
||||
case "Webapps:Install": {
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
Services.obs.notifyObservers(mm, "webapps-runtime-install", JSON.stringify(msg));
|
||||
#else
|
||||
this.doInstall(msg, mm);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case "Webapps:GetSelf":
|
||||
this.getSelf(msg, mm);
|
||||
break;
|
||||
case "Webapps:Uninstall":
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
Services.obs.notifyObservers(mm, "webapps-runtime-uninstall", JSON.stringify(msg));
|
||||
#else
|
||||
this.doUninstall(msg, mm);
|
||||
#endif
|
||||
break;
|
||||
case "Webapps:Launch":
|
||||
this.doLaunch(msg, mm);
|
||||
break;
|
||||
case "Webapps:CheckInstalled":
|
||||
this.checkInstalled(msg, mm);
|
||||
break;
|
||||
case "Webapps:GetInstalled":
|
||||
this.getInstalled(msg, mm);
|
||||
break;
|
||||
case "Webapps:GetNotInstalled":
|
||||
this.getNotInstalled(msg, mm);
|
||||
break;
|
||||
case "Webapps:GetAll":
|
||||
this.doGetAll(msg, mm);
|
||||
break;
|
||||
case "Webapps:InstallPackage": {
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
Services.obs.notifyObservers(mm, "webapps-runtime-install-package", JSON.stringify(msg));
|
||||
#else
|
||||
this.doInstallPackage(msg, mm);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case "Webapps:Download":
|
||||
this.startDownload(msg.manifestURL);
|
||||
break;
|
||||
case "Webapps:CancelDownload":
|
||||
this.cancelDownload(msg.manifestURL);
|
||||
break;
|
||||
case "Webapps:CheckForUpdate":
|
||||
this.checkForUpdate(msg, mm);
|
||||
break;
|
||||
case "Webapps:ApplyDownload":
|
||||
this.applyDownload(msg.manifestURL);
|
||||
break;
|
||||
case "Webapps:Install:Return:Ack":
|
||||
this.onInstallSuccessAck(msg.manifestURL);
|
||||
break;
|
||||
case "Webapps:AddReceipt":
|
||||
this.addReceipt(msg, mm);
|
||||
break;
|
||||
case "Webapps:RemoveReceipt":
|
||||
this.removeReceipt(msg, mm);
|
||||
break;
|
||||
case "Webapps:ReplaceReceipt":
|
||||
this.replaceReceipt(msg, mm);
|
||||
break;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getAppInfo: function getAppInfo(aAppId) {
|
||||
@ -1245,6 +1283,38 @@ this.DOMApplicationRegistry = {
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the full list of apps and manifests.
|
||||
*/
|
||||
doGetList: function() {
|
||||
let tmp = [];
|
||||
|
||||
let res = {};
|
||||
let done = false;
|
||||
|
||||
// We allow cloning the registry when the local processing has been done.
|
||||
this.safeToClone.then( () => {
|
||||
for (let id in this.webapps) {
|
||||
tmp.push({ id: id });
|
||||
}
|
||||
this._readManifests(tmp).then(
|
||||
function(manifests) {
|
||||
manifests.forEach((item) => {
|
||||
res[item.id] = item.manifest;
|
||||
});
|
||||
done = true;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
let thread = Services.tm.currentThread;
|
||||
while (!done) {
|
||||
thread.processNextEvent(/* mayWait */ true);
|
||||
}
|
||||
return { webapps: this.webapps, manifests: res };
|
||||
},
|
||||
|
||||
|
||||
doLaunch: function (aData, aMm) {
|
||||
this.launch(
|
||||
aData.manifestURL,
|
||||
@ -1330,7 +1400,7 @@ this.DOMApplicationRegistry = {
|
||||
downloading: false
|
||||
},
|
||||
error: error,
|
||||
manifestURL: app.manifestURL,
|
||||
id: app.id
|
||||
})
|
||||
this.broadcastMessage("Webapps:FireEvent", {
|
||||
eventType: "downloaderror",
|
||||
@ -1361,7 +1431,7 @@ this.DOMApplicationRegistry = {
|
||||
if (!app.downloadAvailable) {
|
||||
this.broadcastMessage("Webapps:UpdateState", {
|
||||
error: "NO_DOWNLOAD_AVAILABLE",
|
||||
manifestURL: app.manifestURL
|
||||
id: app.id
|
||||
});
|
||||
this.broadcastMessage("Webapps:FireEvent", {
|
||||
eventType: "downloaderror",
|
||||
@ -1409,7 +1479,7 @@ this.DOMApplicationRegistry = {
|
||||
this.broadcastMessage("Webapps:UpdateState", {
|
||||
app: app,
|
||||
manifest: jsonManifest,
|
||||
manifestURL: aManifestURL
|
||||
id: app.id
|
||||
});
|
||||
this.broadcastMessage("Webapps:FireEvent", {
|
||||
eventType: "downloadsuccess",
|
||||
@ -1463,7 +1533,7 @@ this.DOMApplicationRegistry = {
|
||||
|
||||
this.broadcastMessage("Webapps:UpdateState", {
|
||||
app: app,
|
||||
manifestURL: aManifestURL
|
||||
id: app.id
|
||||
});
|
||||
this.broadcastMessage("Webapps:FireEvent", {
|
||||
eventType: "downloadsuccess",
|
||||
@ -1565,7 +1635,7 @@ this.DOMApplicationRegistry = {
|
||||
this.broadcastMessage("Webapps:UpdateState", {
|
||||
app: app,
|
||||
manifest: newManifest,
|
||||
manifestURL: app.manifestURL
|
||||
id: app.id
|
||||
});
|
||||
this.broadcastMessage("Webapps:FireEvent", {
|
||||
eventType: "downloadapplied",
|
||||
@ -1604,7 +1674,7 @@ this.DOMApplicationRegistry = {
|
||||
installState: aApp.installState,
|
||||
progress: 0
|
||||
},
|
||||
manifestURL: aApp.manifestURL
|
||||
id: aApp.id
|
||||
});
|
||||
let cacheUpdate = updateSvc.scheduleAppUpdate(
|
||||
appcacheURI, docURI, aApp.localId, false, aProfileDir);
|
||||
@ -1654,6 +1724,7 @@ this.DOMApplicationRegistry = {
|
||||
debug("checkForUpdate for " + aData.manifestURL);
|
||||
|
||||
function sendError(aError) {
|
||||
debug("checkForUpdate error " + aError);
|
||||
aData.error = aError;
|
||||
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
|
||||
}
|
||||
@ -1683,8 +1754,7 @@ this.DOMApplicationRegistry = {
|
||||
// then we can't have an update.
|
||||
if (app.origin.startsWith("app://") &&
|
||||
app.manifestURL.startsWith("app://")) {
|
||||
aData.error = "NOT_UPDATABLE";
|
||||
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
|
||||
sendError("NOT_UPDATABLE");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1701,8 +1771,7 @@ this.DOMApplicationRegistry = {
|
||||
if (onlyCheckAppCache) {
|
||||
// Bail out for packaged apps.
|
||||
if (app.origin.startsWith("app://")) {
|
||||
aData.error = "NOT_UPDATABLE";
|
||||
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
|
||||
sendError("NOT_UPDATABLE");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1710,8 +1779,7 @@ this.DOMApplicationRegistry = {
|
||||
this._readManifests([{ id: id }]).then((aResult) => {
|
||||
let manifest = aResult[0].manifest;
|
||||
if (!manifest.appcache_path) {
|
||||
aData.error = "NOT_UPDATABLE";
|
||||
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
|
||||
sendError("NOT_UPDATABLE");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1727,7 +1795,7 @@ this.DOMApplicationRegistry = {
|
||||
this._saveApps().then(() => {
|
||||
this.broadcastMessage("Webapps:UpdateState", {
|
||||
app: app,
|
||||
manifestURL: app.manifestURL
|
||||
id: app.id
|
||||
});
|
||||
this.broadcastMessage("Webapps:FireEvent", {
|
||||
eventType: "downloadavailable",
|
||||
@ -1736,8 +1804,7 @@ this.DOMApplicationRegistry = {
|
||||
});
|
||||
});
|
||||
} else {
|
||||
aData.error = "NOT_UPDATABLE";
|
||||
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
|
||||
sendError("NOT_UPDATABLE");
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1797,7 +1864,7 @@ this.DOMApplicationRegistry = {
|
||||
: "downloadapplied";
|
||||
aMm.sendAsyncMessage("Webapps:UpdateState", {
|
||||
app: app,
|
||||
manifestURL: app.manifestURL
|
||||
id: app.id
|
||||
});
|
||||
aMm.sendAsyncMessage("Webapps:FireEvent", {
|
||||
eventType: eventType,
|
||||
@ -1824,7 +1891,7 @@ this.DOMApplicationRegistry = {
|
||||
: "downloadapplied";
|
||||
aMm.sendAsyncMessage("Webapps:UpdateState", {
|
||||
app: app,
|
||||
manifestURL: app.manifestURL
|
||||
id: app.id
|
||||
});
|
||||
aMm.sendAsyncMessage("Webapps:FireEvent", {
|
||||
eventType: eventType,
|
||||
@ -1933,7 +2000,7 @@ this.DOMApplicationRegistry = {
|
||||
|
||||
this.broadcastMessage("Webapps:UpdateState", {
|
||||
app: aApp,
|
||||
manifestURL: aApp.manifestURL
|
||||
id: aApp.id
|
||||
});
|
||||
this.broadcastMessage("Webapps:FireEvent", {
|
||||
eventType: "downloadavailable",
|
||||
@ -1999,7 +2066,7 @@ this.DOMApplicationRegistry = {
|
||||
this.broadcastMessage("Webapps:UpdateState", {
|
||||
app: aApp,
|
||||
manifest: aApp.manifest,
|
||||
manifestURL: aApp.manifestURL
|
||||
id: aApp.id
|
||||
});
|
||||
this.broadcastMessage("Webapps:FireEvent", {
|
||||
eventType: "downloadapplied",
|
||||
@ -2033,7 +2100,7 @@ this.DOMApplicationRegistry = {
|
||||
this.broadcastMessage("Webapps:UpdateState", {
|
||||
app: aApp,
|
||||
manifest: aApp.manifest,
|
||||
manifestURL: aApp.manifestURL
|
||||
id: aApp.id
|
||||
});
|
||||
this.broadcastMessage("Webapps:FireEvent", {
|
||||
eventType: eventType,
|
||||
@ -2464,7 +2531,8 @@ this.DOMApplicationRegistry = {
|
||||
}
|
||||
|
||||
this._saveApps().then(() => {
|
||||
this.broadcastMessage("Webapps:AddApp", { id: app.id, app: app });
|
||||
this.broadcastMessage("Webapps:AddApp",
|
||||
{ id: app.id, app: app, manifest: aManifest });
|
||||
});
|
||||
}),
|
||||
|
||||
@ -2564,6 +2632,8 @@ this.DOMApplicationRegistry = {
|
||||
// saved in the registry.
|
||||
yield this._saveApps();
|
||||
|
||||
aData.isPackage ? appObject.updateManifest = jsonManifest :
|
||||
appObject.manifest = jsonManifest;
|
||||
this.broadcastMessage("Webapps:AddApp", { id: id, app: appObject });
|
||||
|
||||
if (!aData.isPackage) {
|
||||
@ -2646,7 +2716,8 @@ this.DOMApplicationRegistry = {
|
||||
delete this._manifestCache[aId];
|
||||
}
|
||||
|
||||
this.broadcastMessage("Webapps:AddApp", { id: aId, app: aNewApp });
|
||||
this.broadcastMessage("Webapps:AddApp",
|
||||
{ id: aId, app: aNewApp, manifest: aManifest });
|
||||
Services.obs.notifyObservers(null, "webapps-installed",
|
||||
JSON.stringify({ manifestURL: aNewApp.manifestURL }));
|
||||
|
||||
@ -2806,7 +2877,7 @@ this.DOMApplicationRegistry = {
|
||||
// Clear any previous download errors.
|
||||
error: null,
|
||||
app: aOldApp,
|
||||
manifestURL: aNewApp.manifestURL
|
||||
id: aId
|
||||
});
|
||||
|
||||
let zipFile = yield this._getPackage(requestChannel, aId, aOldApp, aNewApp);
|
||||
@ -2821,7 +2892,7 @@ this.DOMApplicationRegistry = {
|
||||
// We send an "applied" event right away so code awaiting that event
|
||||
// can proceed to access the app. We also throw an error to alert
|
||||
// the caller that the package wasn't downloaded.
|
||||
this._sendAppliedEvent(aNewApp, aOldApp, aId);
|
||||
this._sendAppliedEvent(aOldApp);
|
||||
throw new Error("PACKAGE_UNCHANGED");
|
||||
}
|
||||
|
||||
@ -2957,7 +3028,7 @@ this.DOMApplicationRegistry = {
|
||||
app: {
|
||||
progress: aProgress
|
||||
},
|
||||
manifestURL: aNewApp.manifestURL
|
||||
id: aNewApp.id
|
||||
});
|
||||
this.broadcastMessage("Webapps:FireEvent", {
|
||||
eventType: "progress",
|
||||
@ -3074,27 +3145,24 @@ this.DOMApplicationRegistry = {
|
||||
* something similar after updating the app, and we could refactor both cases
|
||||
* to use the same code to send the "applied" event.
|
||||
*
|
||||
* @param aNewApp {Object} the new app data
|
||||
* @param aOldApp {Object} the currently stored app data
|
||||
* @param aId {String} the unique id of the app
|
||||
* @param aApp {Object} app data
|
||||
*/
|
||||
_sendAppliedEvent: function(aNewApp, aOldApp, aId) {
|
||||
aOldApp.downloading = false;
|
||||
aOldApp.downloadAvailable = false;
|
||||
aOldApp.downloadSize = 0;
|
||||
aOldApp.installState = "installed";
|
||||
aOldApp.readyToApplyDownload = false;
|
||||
if (aOldApp.staged && aOldApp.staged.manifestHash) {
|
||||
_sendAppliedEvent: function(aApp) {
|
||||
aApp.downloading = false;
|
||||
aApp.downloadAvailable = false;
|
||||
aApp.downloadSize = 0;
|
||||
aApp.installState = "installed";
|
||||
aApp.readyToApplyDownload = false;
|
||||
if (aApp.staged && aApp.staged.manifestHash) {
|
||||
// If we're here then the manifest has changed but the package
|
||||
// hasn't. Let's clear this, so we don't keep offering
|
||||
// a bogus update to the user
|
||||
aOldApp.manifestHash = aOldApp.staged.manifestHash;
|
||||
aOldApp.etag = aOldApp.staged.etag || aOldApp.etag;
|
||||
aOldApp.staged = {};
|
||||
|
||||
// Move the staged update manifest to a non staged one.
|
||||
aApp.manifestHash = aApp.staged.manifestHash;
|
||||
aApp.etag = aApp.staged.etag || aApp.etag;
|
||||
aApp.staged = {};
|
||||
// Move the staged update manifest to a non staged one.
|
||||
try {
|
||||
let staged = this._getAppDir(aId);
|
||||
let staged = this._getAppDir(aApp.id);
|
||||
staged.append("staged-update.webapp");
|
||||
staged.moveTo(staged.parent, "update.webapp");
|
||||
} catch (ex) {
|
||||
@ -3105,15 +3173,15 @@ this.DOMApplicationRegistry = {
|
||||
// Save the updated registry, and cleanup the tmp directory.
|
||||
this._saveApps().then(() => {
|
||||
this.broadcastMessage("Webapps:UpdateState", {
|
||||
app: aOldApp,
|
||||
manifestURL: aNewApp.manifestURL
|
||||
app: aApp,
|
||||
id: aApp.id
|
||||
});
|
||||
this.broadcastMessage("Webapps:FireEvent", {
|
||||
manifestURL: aNewApp.manifestURL,
|
||||
manifestURL: aApp.manifestURL,
|
||||
eventType: ["downloadsuccess", "downloadapplied"]
|
||||
});
|
||||
});
|
||||
let file = FileUtils.getFile("TmpD", ["webapps", aId], false);
|
||||
let file = FileUtils.getFile("TmpD", ["webapps", aApp.id], false);
|
||||
if (file && file.exists()) {
|
||||
file.remove(true);
|
||||
}
|
||||
@ -3432,9 +3500,10 @@ this.DOMApplicationRegistry = {
|
||||
dir.moveTo(parent, newId);
|
||||
});
|
||||
// Signals that we need to swap the old id with the new app.
|
||||
this.broadcastMessage("Webapps:RemoveApp", { id: oldId });
|
||||
this.broadcastMessage("Webapps:AddApp", { id: newId,
|
||||
app: aOldApp });
|
||||
this.broadcastMessage("Webapps:UpdateApp", { oldId: oldId,
|
||||
newId: newId,
|
||||
app: aOldApp });
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -3537,7 +3606,7 @@ this.DOMApplicationRegistry = {
|
||||
this.broadcastMessage("Webapps:UpdateState", {
|
||||
app: aOldApp,
|
||||
error: aError,
|
||||
manifestURL: aNewApp.manifestURL
|
||||
id: aNewApp.id
|
||||
});
|
||||
this.broadcastMessage("Webapps:FireEvent", {
|
||||
eventType: "downloaderror",
|
||||
@ -3717,9 +3786,13 @@ this.DOMApplicationRegistry = {
|
||||
},
|
||||
|
||||
doGetAll: function(aData, aMm) {
|
||||
this.getAll(function (apps) {
|
||||
aData.apps = apps;
|
||||
aMm.sendAsyncMessage("Webapps:GetAll:Return:OK", aData);
|
||||
// We can't do this until the registry is ready.
|
||||
debug("doGetAll");
|
||||
this.registryReady.then(() => {
|
||||
this.getAll(function (apps) {
|
||||
aData.apps = apps;
|
||||
aMm.sendAsyncMessage("Webapps:GetAll:Return:OK", aData);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
@ -4087,7 +4160,7 @@ AppcacheObserver.prototype = {
|
||||
let app = this.app;
|
||||
DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
|
||||
app: app,
|
||||
manifestURL: app.manifestURL
|
||||
id: app.id
|
||||
});
|
||||
DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", {
|
||||
eventType: "progress",
|
||||
@ -4119,7 +4192,7 @@ AppcacheObserver.prototype = {
|
||||
app.downloadAvailable = false;
|
||||
DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
|
||||
app: app,
|
||||
manifestURL: app.manifestURL
|
||||
id: app.id
|
||||
});
|
||||
DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", {
|
||||
eventType: ["downloadsuccess", "downloadapplied"],
|
||||
@ -4142,7 +4215,7 @@ AppcacheObserver.prototype = {
|
||||
DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
|
||||
app: app,
|
||||
error: aError,
|
||||
manifestURL: app.manifestURL
|
||||
id: app.id
|
||||
});
|
||||
DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", {
|
||||
eventType: "downloaderror",
|
||||
|
@ -102,6 +102,7 @@ var PackagedTestHelper = (function PackagedTestHelper() {
|
||||
var aApp = evt.application;
|
||||
aApp.ondownloaderror = function(evt) {
|
||||
var error = aApp.downloadError.name;
|
||||
ok(true, "Got downloaderror " + error);
|
||||
if (error == aExpectedError) {
|
||||
ok(true, "Got expected " + aExpectedError);
|
||||
var expected = {
|
||||
|
@ -79,15 +79,15 @@ function updateApp(aExpectedReady, aPreviousVersion, aNextVersion) {
|
||||
checkLastAppState.bind(PackagedTestHelper, miniManifestURL, false, false,
|
||||
aNextVersion, PackagedTestHelper.next);
|
||||
|
||||
var ondownloadsuccesshandler =
|
||||
checkLastAppState.bind(undefined, miniManifestURL,
|
||||
aExpectedReady, false, aPreviousVersion,
|
||||
function() {
|
||||
navigator.mozApps.mgmt.applyDownload(lApp);
|
||||
});
|
||||
var ondownloadsuccesshandler =
|
||||
checkLastAppState.bind(undefined, miniManifestURL,
|
||||
aExpectedReady, false, aPreviousVersion,
|
||||
function() {
|
||||
navigator.mozApps.mgmt.applyDownload(lApp);
|
||||
});
|
||||
|
||||
checkForUpdate(true, ondownloadsuccesshandler, ondownloadappliedhandler, null,
|
||||
true);
|
||||
checkForUpdate(true, ondownloadsuccesshandler, ondownloadappliedhandler,
|
||||
null, true);
|
||||
|
||||
}
|
||||
|
||||
@ -254,7 +254,7 @@ var steps = [
|
||||
"&appName=arandomname" +
|
||||
"&appToFail1";
|
||||
PackagedTestHelper.checkAppDownloadError(miniManifestURL,
|
||||
"MANIFEST_MISMATCH", 2, false, true,
|
||||
"MANIFEST_MISMATCH", 1, false, true,
|
||||
"arandomname",
|
||||
function () {
|
||||
checkForUpdate(false, null, null, null, false,
|
||||
|
@ -243,4 +243,4 @@ addLoadEvent(go);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
@ -586,14 +586,18 @@ AudioChannelService::SendAudioChannelChangedNotification(uint64_t aChildID)
|
||||
kMozAudioChannelAttributeTable[index].value > higher &&
|
||||
kMozAudioChannelAttributeTable[index].value > (int16_t)AudioChannel::Normal;
|
||||
--index) {
|
||||
if (kMozAudioChannelAttributeTable[index].value == (int16_t)AudioChannel::Content &&
|
||||
mPlayableHiddenContentChildID != CONTENT_PROCESS_ID_UNKNOWN) {
|
||||
higher = kMozAudioChannelAttributeTable[index].value;
|
||||
}
|
||||
|
||||
// Each channel type will be split to fg and bg for recording the state,
|
||||
// so here need to do a translation.
|
||||
if (!mChannelCounters[index * 2 + 1].IsEmpty()) {
|
||||
if (mChannelCounters[index * 2 + 1].IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (kMozAudioChannelAttributeTable[index].value == (int16_t)AudioChannel::Content) {
|
||||
if (mPlayableHiddenContentChildID != CONTENT_PROCESS_ID_UNKNOWN) {
|
||||
higher = kMozAudioChannelAttributeTable[index].value;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
higher = kMozAudioChannelAttributeTable[index].value;
|
||||
break;
|
||||
}
|
||||
|
@ -166,20 +166,24 @@ DOMInterfaces = {
|
||||
'nativeType': 'mozilla::dom::bluetooth::BluetoothAdapter',
|
||||
},
|
||||
|
||||
'BluetoothDevice': {
|
||||
'nativeType': 'mozilla::dom::bluetooth::BluetoothDevice',
|
||||
'BluetoothClassOfDevice': {
|
||||
'nativeType': 'mozilla::dom::bluetooth::BluetoothClassOfDevice',
|
||||
},
|
||||
|
||||
'BluetoothManager': {
|
||||
'nativeType': 'mozilla::dom::bluetooth::BluetoothManager',
|
||||
'BluetoothDevice': {
|
||||
'nativeType': 'mozilla::dom::bluetooth::BluetoothDevice',
|
||||
},
|
||||
|
||||
'BluetoothDiscoveryHandle': {
|
||||
'nativeType': 'mozilla::dom::bluetooth::BluetoothDiscoveryHandle',
|
||||
},
|
||||
|
||||
'BluetoothClassOfDevice': {
|
||||
'nativeType': 'mozilla::dom::bluetooth::BluetoothClassOfDevice',
|
||||
'BluetoothManager': {
|
||||
'nativeType': 'mozilla::dom::bluetooth::BluetoothManager',
|
||||
},
|
||||
|
||||
'BluetoothPairingHandle': {
|
||||
'nativeType': 'mozilla::dom::bluetooth::BluetoothPairingHandle',
|
||||
},
|
||||
|
||||
'CameraCapabilities': {
|
||||
|
@ -148,7 +148,7 @@ BluetoothHfpManager::Init()
|
||||
|
||||
// static
|
||||
void
|
||||
BluetoothHfpManager::InitHfpInterface()
|
||||
BluetoothHfpManager::InitHfpInterface(BluetoothProfileResultHandler* aRes)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
@ -157,11 +157,15 @@ BluetoothHfpManager::InitHfpInterface()
|
||||
* Implement InitHfpInterface() for applications that want to create SCO
|
||||
* link without a HFP connection (e.g., VoIP).
|
||||
*/
|
||||
|
||||
if (aRes) {
|
||||
aRes->Init();
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
BluetoothHfpManager::DeinitHfpInterface()
|
||||
BluetoothHfpManager::DeinitHfpInterface(BluetoothProfileResultHandler* aRes)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
@ -170,6 +174,10 @@ BluetoothHfpManager::DeinitHfpInterface()
|
||||
* Implement DeinitHfpInterface() for applications that want to create SCO
|
||||
* link without a HFP connection (e.g., VoIP).
|
||||
*/
|
||||
|
||||
if (aRes) {
|
||||
aRes->Deinit();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -30,8 +30,8 @@ public:
|
||||
|
||||
static BluetoothHfpManager* Get();
|
||||
virtual ~BluetoothHfpManager() { }
|
||||
static void InitHfpInterface();
|
||||
static void DeinitHfpInterface();
|
||||
static void InitHfpInterface(BluetoothProfileResultHandler* aRes);
|
||||
static void DeinitHfpInterface(BluetoothProfileResultHandler* aRes);
|
||||
|
||||
bool ConnectSco();
|
||||
bool DisconnectSco();
|
||||
|
@ -407,10 +407,9 @@ BluetoothAdapter::StartDiscovery(ErrorResult& aRv)
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
|
||||
|
||||
/**
|
||||
* Ensure
|
||||
@ -451,10 +450,9 @@ BluetoothAdapter::StopDiscovery(ErrorResult& aRv)
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
|
||||
|
||||
/**
|
||||
* Ensure
|
||||
@ -484,14 +482,13 @@ already_AddRefed<Promise>
|
||||
BluetoothAdapter::SetName(const nsAString& aName, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
|
||||
if(!global) {
|
||||
if (!global) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
|
||||
|
||||
/**
|
||||
* Ensure
|
||||
@ -525,14 +522,13 @@ already_AddRefed<Promise>
|
||||
BluetoothAdapter::SetDiscoverable(bool aDiscoverable, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
|
||||
if(!global) {
|
||||
if (!global) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
|
||||
|
||||
/**
|
||||
* Ensure
|
||||
@ -646,104 +642,17 @@ BluetoothAdapter::Unpair(const nsAString& aDeviceAddress, ErrorResult& aRv)
|
||||
return PairUnpair(false, aDeviceAddress, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
BluetoothAdapter::SetPinCode(const nsAString& aDeviceAddress,
|
||||
const nsAString& aPinCode, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetOwner();
|
||||
if (!win) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<DOMRequest> request = new DOMRequest(win);
|
||||
nsRefPtr<BluetoothVoidReplyRunnable> results =
|
||||
new BluetoothVoidReplyRunnable(request);
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
if (!bs) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
if (!bs->SetPinCodeInternal(aDeviceAddress, aPinCode, results)) {
|
||||
BT_WARNING("SetPinCode failed!");
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return request.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
BluetoothAdapter::SetPasskey(const nsAString& aDeviceAddress, uint32_t aPasskey,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetOwner();
|
||||
if (!win) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<DOMRequest> request = new DOMRequest(win);
|
||||
nsRefPtr<BluetoothVoidReplyRunnable> results =
|
||||
new BluetoothVoidReplyRunnable(request);
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
if (!bs) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
if (bs->SetPasskeyInternal(aDeviceAddress, aPasskey, results)) {
|
||||
BT_WARNING("SetPasskeyInternal failed!");
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return request.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
BluetoothAdapter::SetPairingConfirmation(const nsAString& aDeviceAddress,
|
||||
bool aConfirmation, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetOwner();
|
||||
if (!win) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<DOMRequest> request = new DOMRequest(win);
|
||||
nsRefPtr<BluetoothVoidReplyRunnable> results =
|
||||
new BluetoothVoidReplyRunnable(request);
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
if (!bs) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
if (!bs->SetPairingConfirmationInternal(aDeviceAddress,
|
||||
aConfirmation,
|
||||
results)) {
|
||||
BT_WARNING("SetPairingConfirmation failed!");
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return request.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
BluetoothAdapter::EnableDisable(bool aEnable, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
|
||||
if(!global) {
|
||||
if (!global) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
|
||||
|
||||
// Ensure BluetoothService is available before modifying adapter state
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
|
@ -99,18 +99,6 @@ public:
|
||||
Unpair(const nsAString& aDeviceAddress, ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest>
|
||||
GetPairedDevices(ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest>
|
||||
SetPinCode(const nsAString& aDeviceAddress, const nsAString& aPinCode,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest>
|
||||
SetPasskey(const nsAString& aDeviceAddress, uint32_t aPasskey,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest>
|
||||
SetPairingConfirmation(const nsAString& aDeviceAddress, bool aConfirmation,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest>
|
||||
SetAuthorization(const nsAString& aDeviceAddress, bool aAllow,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise> EnableDisable(bool aEnable, ErrorResult& aRv);
|
||||
already_AddRefed<Promise> Enable(ErrorResult& aRv);
|
||||
|
@ -148,6 +148,15 @@ extern bool gBluetoothDebugFlag;
|
||||
#define HFP_STATUS_CHANGED_ID "hfpstatuschanged"
|
||||
#define SCO_STATUS_CHANGED_ID "scostatuschanged"
|
||||
|
||||
/**
|
||||
* Types of pairing requests for constructing BluetoothPairingEvent and
|
||||
* BluetoothPairingHandle.
|
||||
*/
|
||||
#define PAIRING_REQ_TYPE_DISPLAYPASSKEY "displaypasskeyreq"
|
||||
#define PAIRING_REQ_TYPE_ENTERPINCODE "enterpincodereq"
|
||||
#define PAIRING_REQ_TYPE_CONFIRMATION "pairingconfirmationreq"
|
||||
#define PAIRING_REQ_TYPE_CONSENT "pairingconsentreq"
|
||||
|
||||
/**
|
||||
* When the pair status of a Bluetooth device is changed, we'll dispatch an
|
||||
* event.
|
||||
|
@ -144,9 +144,7 @@ BluetoothDevice::FetchUuids(ErrorResult& aRv)
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
|
||||
|
129
dom/bluetooth2/BluetoothPairingHandle.cpp
Normal file
129
dom/bluetooth2/BluetoothPairingHandle.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 "BluetoothCommon.h"
|
||||
#include "BluetoothDevice.h"
|
||||
#include "BluetoothPairingHandle.h"
|
||||
#include "BluetoothReplyRunnable.h"
|
||||
#include "BluetoothService.h"
|
||||
|
||||
#include "mozilla/dom/BluetoothPairingHandleBinding.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace dom;
|
||||
|
||||
USING_BLUETOOTH_NAMESPACE
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(BluetoothPairingHandle, mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(BluetoothPairingHandle)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(BluetoothPairingHandle)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BluetoothPairingHandle)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
BluetoothPairingHandle::BluetoothPairingHandle(nsPIDOMWindow* aOwner,
|
||||
const nsAString& aDeviceAddress,
|
||||
const nsAString& aType,
|
||||
const nsAString& aPasskey)
|
||||
: mOwner(aOwner)
|
||||
, mDeviceAddress(aDeviceAddress)
|
||||
, mType(aType)
|
||||
, mPasskey(aPasskey)
|
||||
{
|
||||
MOZ_ASSERT(aOwner && !aDeviceAddress.IsEmpty() && !aType.IsEmpty());
|
||||
|
||||
if (aType.EqualsLiteral(PAIRING_REQ_TYPE_DISPLAYPASSKEY) ||
|
||||
aType.EqualsLiteral(PAIRING_REQ_TYPE_CONFIRMATION)) {
|
||||
MOZ_ASSERT(!aPasskey.IsEmpty());
|
||||
} else {
|
||||
MOZ_ASSERT(aPasskey.IsEmpty());
|
||||
}
|
||||
|
||||
SetIsDOMBinding();
|
||||
}
|
||||
|
||||
BluetoothPairingHandle::~BluetoothPairingHandle()
|
||||
{
|
||||
}
|
||||
|
||||
already_AddRefed<BluetoothPairingHandle>
|
||||
BluetoothPairingHandle::Create(nsPIDOMWindow* aOwner,
|
||||
const nsAString& aDeviceAddress,
|
||||
const nsAString& aType,
|
||||
const nsAString& aPasskey)
|
||||
{
|
||||
MOZ_ASSERT(aOwner && !aDeviceAddress.IsEmpty() && !aType.IsEmpty());
|
||||
|
||||
nsRefPtr<BluetoothPairingHandle> handle =
|
||||
new BluetoothPairingHandle(aOwner, aDeviceAddress, aType, aPasskey);
|
||||
|
||||
return handle.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
BluetoothPairingHandle::SetPinCode(const nsAString& aPinCode, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
|
||||
if (!global) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
|
||||
|
||||
BT_ENSURE_TRUE_REJECT(mType.EqualsLiteral("enterpincodereq"),
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
nsRefPtr<BluetoothReplyRunnable> result =
|
||||
new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
|
||||
promise,
|
||||
NS_LITERAL_STRING("SetPinCode"));
|
||||
bs->SetPinCodeInternal(mDeviceAddress, aPinCode, result);
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
BluetoothPairingHandle::SetPairingConfirmation(bool aConfirm, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
|
||||
if (!global) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
|
||||
|
||||
BT_ENSURE_TRUE_REJECT(mType.EqualsLiteral("pairingconfirmationreq"),
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
nsRefPtr<BluetoothReplyRunnable> result =
|
||||
new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
|
||||
promise,
|
||||
NS_LITERAL_STRING(
|
||||
"SetPairingConfirmation"));
|
||||
|
||||
bs->SetPairingConfirmationInternal(mDeviceAddress,
|
||||
aConfirm,
|
||||
result);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
BluetoothPairingHandle::WrapObject(JSContext* aCx)
|
||||
{
|
||||
return BluetoothPairingHandleBinding::Wrap(aCx, this);
|
||||
}
|
69
dom/bluetooth2/BluetoothPairingHandle.h
Normal file
69
dom/bluetooth2/BluetoothPairingHandle.h
Normal file
@ -0,0 +1,69 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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_bluetooth_bluetoothpairinghandle_h
|
||||
#define mozilla_dom_bluetooth_bluetoothpairinghandle_h
|
||||
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
class ErrorResult;
|
||||
namespace dom {
|
||||
class Promise;
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN_BLUETOOTH_NAMESPACE
|
||||
|
||||
class BluetoothDevice;
|
||||
|
||||
class BluetoothPairingHandle MOZ_FINAL : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(BluetoothPairingHandle)
|
||||
|
||||
static already_AddRefed<BluetoothPairingHandle>
|
||||
Create(nsPIDOMWindow* aOwner,
|
||||
const nsAString& aDeviceAddress,
|
||||
const nsAString& aType,
|
||||
const nsAString& aPasskey);
|
||||
|
||||
nsPIDOMWindow* GetParentObject() const
|
||||
{
|
||||
return mOwner;
|
||||
}
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
|
||||
|
||||
void GetPasskey(nsString& aPasskey) const
|
||||
{
|
||||
aPasskey = mPasskey;
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
SetPinCode(const nsAString& aPinCode, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
SetPairingConfirmation(bool aConfirm, ErrorResult& aRv);
|
||||
|
||||
private:
|
||||
BluetoothPairingHandle(nsPIDOMWindow* aOwner,
|
||||
const nsAString& aDeviceAddress,
|
||||
const nsAString& aType,
|
||||
const nsAString& aPasskey);
|
||||
~BluetoothPairingHandle();
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> mOwner;
|
||||
nsString mDeviceAddress;
|
||||
nsString mType;
|
||||
nsString mPasskey;
|
||||
};
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
||||
#endif // mozilla_dom_bluetooth_bluetoothpairinghandle_h
|
@ -219,15 +219,15 @@ public:
|
||||
UpdateSdpRecords(const nsAString& aDeviceAddress,
|
||||
BluetoothProfileManagerBase* aManager) = 0;
|
||||
|
||||
virtual bool
|
||||
virtual void
|
||||
SetPinCodeInternal(const nsAString& aDeviceAddress, const nsAString& aPinCode,
|
||||
BluetoothReplyRunnable* aRunnable) = 0;
|
||||
|
||||
virtual bool
|
||||
virtual void
|
||||
SetPasskeyInternal(const nsAString& aDeviceAddress, uint32_t aPasskey,
|
||||
BluetoothReplyRunnable* aRunnable) = 0;
|
||||
|
||||
virtual bool
|
||||
virtual void
|
||||
SetPairingConfirmationInternal(const nsAString& aDeviceAddress, bool aConfirm,
|
||||
BluetoothReplyRunnable* aRunnable) = 0;
|
||||
|
||||
|
@ -45,6 +45,15 @@
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define ENSURE_BLUETOOTH_IS_READY_VOID(runnable) \
|
||||
do { \
|
||||
if (!sBtInterface || !IsEnabled()) { \
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth is not ready"); \
|
||||
DispatchBluetoothReply(runnable, BluetoothValue(), errorStr); \
|
||||
return; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::ipc;
|
||||
USING_BLUETOOTH_NAMESPACE
|
||||
@ -1492,14 +1501,14 @@ private:
|
||||
BluetoothReplyRunnable* mRunnable;
|
||||
};
|
||||
|
||||
bool
|
||||
void
|
||||
BluetoothServiceBluedroid::SetPinCodeInternal(
|
||||
const nsAString& aDeviceAddress, const nsAString& aPinCode,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
ENSURE_BLUETOOTH_IS_READY(aRunnable, false);
|
||||
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
|
||||
|
||||
bt_bdaddr_t remoteAddress;
|
||||
StringToBdAddressType(aDeviceAddress, &remoteAddress);
|
||||
@ -1508,16 +1517,14 @@ BluetoothServiceBluedroid::SetPinCodeInternal(
|
||||
&remoteAddress, true, aPinCode.Length(),
|
||||
(bt_pin_code_t*)NS_ConvertUTF16toUTF8(aPinCode).get(),
|
||||
new PinReplyResultHandler(aRunnable));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
void
|
||||
BluetoothServiceBluedroid::SetPasskeyInternal(
|
||||
const nsAString& aDeviceAddress, uint32_t aPasskey,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
class SspReplyResultHandler MOZ_FINAL : public BluetoothResultHandler
|
||||
@ -1542,21 +1549,23 @@ private:
|
||||
BluetoothReplyRunnable* mRunnable;
|
||||
};
|
||||
|
||||
bool
|
||||
void
|
||||
BluetoothServiceBluedroid::SetPairingConfirmationInternal(
|
||||
const nsAString& aDeviceAddress, bool aConfirm,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
ENSURE_BLUETOOTH_IS_READY(aRunnable, false);
|
||||
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
|
||||
|
||||
bt_bdaddr_t remoteAddress;
|
||||
StringToBdAddressType(aDeviceAddress, &remoteAddress);
|
||||
|
||||
sBtInterface->SspReply(&remoteAddress, (bt_ssp_variant_t)0, aConfirm, 0,
|
||||
sBtInterface->SspReply(&remoteAddress,
|
||||
BT_SSP_VARIANT_PASSKEY_CONFIRMATION,
|
||||
aConfirm,
|
||||
0, /* aPasskey */
|
||||
new SspReplyResultHandler(aRunnable));
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -66,15 +66,15 @@ public:
|
||||
RemoveDeviceInternal(const nsAString& aDeviceObjectPath,
|
||||
BluetoothReplyRunnable* aRunnable);
|
||||
|
||||
virtual bool
|
||||
virtual void
|
||||
SetPinCodeInternal(const nsAString& aDeviceAddress, const nsAString& aPinCode,
|
||||
BluetoothReplyRunnable* aRunnable);
|
||||
|
||||
virtual bool
|
||||
virtual void
|
||||
SetPasskeyInternal(const nsAString& aDeviceAddress, uint32_t aPasskey,
|
||||
BluetoothReplyRunnable* aRunnable);
|
||||
|
||||
virtual bool
|
||||
virtual void
|
||||
SetPairingConfirmationInternal(const nsAString& aDeviceAddress, bool aConfirm,
|
||||
BluetoothReplyRunnable* aRunnable);
|
||||
|
||||
|
@ -148,7 +148,7 @@ BluetoothHfpManager::Init()
|
||||
|
||||
// static
|
||||
void
|
||||
BluetoothHfpManager::InitHfpInterface()
|
||||
BluetoothHfpManager::InitHfpInterface(BluetoothProfileResultHandler* aRes)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
@ -157,11 +157,15 @@ BluetoothHfpManager::InitHfpInterface()
|
||||
* Implement InitHfpInterface() for applications that want to create SCO
|
||||
* link without a HFP connection (e.g., VoIP).
|
||||
*/
|
||||
|
||||
if (aRes) {
|
||||
aRes->Init();
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
BluetoothHfpManager::DeinitHfpInterface()
|
||||
BluetoothHfpManager::DeinitHfpInterface(BluetoothProfileResultHandler* aRes)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
@ -170,6 +174,10 @@ BluetoothHfpManager::DeinitHfpInterface()
|
||||
* Implement DeinitHfpInterface() for applications that want to create SCO
|
||||
* link without a HFP connection (e.g., VoIP).
|
||||
*/
|
||||
|
||||
if (aRes) {
|
||||
aRes->Deinit();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -30,8 +30,8 @@ public:
|
||||
|
||||
static BluetoothHfpManager* Get();
|
||||
virtual ~BluetoothHfpManager() { }
|
||||
static void InitHfpInterface();
|
||||
static void DeinitHfpInterface();
|
||||
static void InitHfpInterface(BluetoothProfileResultHandler* aRes);
|
||||
static void DeinitHfpInterface(BluetoothProfileResultHandler* aRes);
|
||||
|
||||
bool ConnectSco();
|
||||
bool DisconnectSco();
|
||||
|
@ -3195,7 +3195,7 @@ private:
|
||||
nsRefPtr<BluetoothReplyRunnable> mRunnable;
|
||||
};
|
||||
|
||||
bool
|
||||
void
|
||||
BluetoothDBusService::SetPinCodeInternal(const nsAString& aDeviceAddress,
|
||||
const nsAString& aPinCode,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
@ -3204,8 +3204,6 @@ BluetoothDBusService::SetPinCodeInternal(const nsAString& aDeviceAddress,
|
||||
NS_ConvertUTF16toUTF8(aPinCode),
|
||||
aRunnable);
|
||||
DispatchToDBusThread(task);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class SetPasskeyTask : public Task
|
||||
@ -3271,7 +3269,7 @@ private:
|
||||
nsRefPtr<BluetoothReplyRunnable> mRunnable;
|
||||
};
|
||||
|
||||
bool
|
||||
void
|
||||
BluetoothDBusService::SetPasskeyInternal(const nsAString& aDeviceAddress,
|
||||
uint32_t aPasskey,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
@ -3280,12 +3278,10 @@ BluetoothDBusService::SetPasskeyInternal(const nsAString& aDeviceAddress,
|
||||
aPasskey,
|
||||
aRunnable);
|
||||
DispatchToDBusThread(task);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
void
|
||||
BluetoothDBusService::SetPairingConfirmationInternal(
|
||||
const nsAString& aDeviceAddress,
|
||||
bool aConfirm,
|
||||
@ -3297,8 +3293,6 @@ BluetoothDBusService::SetPairingConfirmationInternal(
|
||||
aConfirm,
|
||||
aRunnable);
|
||||
DispatchToDBusThread(task);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -93,15 +93,15 @@ public:
|
||||
RemoveDeviceInternal(const nsAString& aDeviceObjectPath,
|
||||
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
virtual nsresult
|
||||
SetPinCodeInternal(const nsAString& aDeviceAddress, const nsAString& aPinCode,
|
||||
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
virtual nsresult
|
||||
SetPasskeyInternal(const nsAString& aDeviceAddress, uint32_t aPasskey,
|
||||
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
virtual nsresult
|
||||
SetPairingConfirmationInternal(const nsAString& aDeviceAddress, bool aConfirm,
|
||||
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
|
@ -466,12 +466,9 @@ BluetoothRequestParent::DoRequest(const SetPinCodeRequest& aRequest)
|
||||
MOZ_ASSERT(mService);
|
||||
MOZ_ASSERT(mRequestType == Request::TSetPinCodeRequest);
|
||||
|
||||
bool result =
|
||||
mService->SetPinCodeInternal(aRequest.path(),
|
||||
aRequest.pincode(),
|
||||
mReplyRunnable.get());
|
||||
|
||||
NS_ENSURE_TRUE(result, false);
|
||||
mService->SetPinCodeInternal(aRequest.path(),
|
||||
aRequest.pincode(),
|
||||
mReplyRunnable.get());
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -482,12 +479,9 @@ BluetoothRequestParent::DoRequest(const SetPasskeyRequest& aRequest)
|
||||
MOZ_ASSERT(mService);
|
||||
MOZ_ASSERT(mRequestType == Request::TSetPasskeyRequest);
|
||||
|
||||
bool result =
|
||||
mService->SetPasskeyInternal(aRequest.path(),
|
||||
aRequest.passkey(),
|
||||
mReplyRunnable.get());
|
||||
|
||||
NS_ENSURE_TRUE(result, false);
|
||||
mService->SetPasskeyInternal(aRequest.path(),
|
||||
aRequest.passkey(),
|
||||
mReplyRunnable.get());
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -499,12 +493,9 @@ BluetoothRequestParent::DoRequest(const ConfirmPairingConfirmationRequest&
|
||||
MOZ_ASSERT(mService);
|
||||
MOZ_ASSERT(mRequestType == Request::TConfirmPairingConfirmationRequest);
|
||||
|
||||
bool result =
|
||||
mService->SetPairingConfirmationInternal(aRequest.path(),
|
||||
true,
|
||||
mReplyRunnable.get());
|
||||
|
||||
NS_ENSURE_TRUE(result, false);
|
||||
mService->SetPairingConfirmationInternal(aRequest.path(),
|
||||
true,
|
||||
mReplyRunnable.get());
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -516,12 +507,9 @@ BluetoothRequestParent::DoRequest(const DenyPairingConfirmationRequest&
|
||||
MOZ_ASSERT(mService);
|
||||
MOZ_ASSERT(mRequestType == Request::TDenyPairingConfirmationRequest);
|
||||
|
||||
bool result =
|
||||
mService->SetPairingConfirmationInternal(aRequest.path(),
|
||||
false,
|
||||
mReplyRunnable.get());
|
||||
|
||||
NS_ENSURE_TRUE(result, false);
|
||||
mService->SetPairingConfirmationInternal(aRequest.path(),
|
||||
false,
|
||||
mReplyRunnable.get());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ BluetoothServiceChildProcess::UpdateSdpRecords(const nsAString& aDeviceAddress,
|
||||
MOZ_CRASH("This should never be called!");
|
||||
}
|
||||
|
||||
bool
|
||||
void
|
||||
BluetoothServiceChildProcess::SetPinCodeInternal(
|
||||
const nsAString& aDeviceAddress,
|
||||
const nsAString& aPinCode,
|
||||
@ -215,10 +215,9 @@ BluetoothServiceChildProcess::SetPinCodeInternal(
|
||||
{
|
||||
SendRequest(aRunnable,
|
||||
SetPinCodeRequest(nsString(aDeviceAddress), nsString(aPinCode)));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
void
|
||||
BluetoothServiceChildProcess::SetPasskeyInternal(
|
||||
const nsAString& aDeviceAddress,
|
||||
uint32_t aPasskey,
|
||||
@ -226,10 +225,9 @@ BluetoothServiceChildProcess::SetPasskeyInternal(
|
||||
{
|
||||
SendRequest(aRunnable,
|
||||
SetPasskeyRequest(nsString(aDeviceAddress), aPasskey));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
void
|
||||
BluetoothServiceChildProcess::SetPairingConfirmationInternal(
|
||||
const nsAString& aDeviceAddress,
|
||||
bool aConfirm,
|
||||
@ -242,7 +240,6 @@ BluetoothServiceChildProcess::SetPairingConfirmationInternal(
|
||||
SendRequest(aRunnable,
|
||||
DenyPairingConfirmationRequest(nsString(aDeviceAddress)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -94,17 +94,17 @@ public:
|
||||
UpdateSdpRecords(const nsAString& aDeviceAddress,
|
||||
BluetoothProfileManagerBase* aManager) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
virtual void
|
||||
SetPinCodeInternal(const nsAString& aDeviceAddress,
|
||||
const nsAString& aPinCode,
|
||||
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
virtual void
|
||||
SetPasskeyInternal(const nsAString& aDeviceAddress,
|
||||
uint32_t aPasskey,
|
||||
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
virtual void
|
||||
SetPairingConfirmationInternal(const nsAString& aDeviceAddress,
|
||||
bool aConfirm,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
|
@ -12,6 +12,7 @@ if CONFIG['MOZ_B2G_BT']:
|
||||
'BluetoothDiscoveryHandle.cpp',
|
||||
'BluetoothHidManager.cpp',
|
||||
'BluetoothManager.cpp',
|
||||
'BluetoothPairingHandle.cpp',
|
||||
'BluetoothProfileController.cpp',
|
||||
'BluetoothReplyRunnable.cpp',
|
||||
'BluetoothService.cpp',
|
||||
@ -99,6 +100,7 @@ EXPORTS.mozilla.dom.bluetooth += [
|
||||
'BluetoothDevice.h',
|
||||
'BluetoothDiscoveryHandle.h',
|
||||
'BluetoothManager.h',
|
||||
'BluetoothPairingHandle.h',
|
||||
]
|
||||
|
||||
IPDL_SOURCES += [
|
||||
|
@ -242,6 +242,7 @@ BrowserElementChild.prototype = {
|
||||
"go-forward": this._recvGoForward,
|
||||
"reload": this._recvReload,
|
||||
"stop": this._recvStop,
|
||||
"zoom": this._recvZoom,
|
||||
"unblock-modal-prompt": this._recvStopWaiting,
|
||||
"fire-ctx-callback": this._recvFireCtxCallback,
|
||||
"owner-visibility-change": this._recvOwnerVisibilityChange,
|
||||
@ -1097,6 +1098,10 @@ BrowserElementChild.prototype = {
|
||||
webNav.stop(webNav.STOP_NETWORK);
|
||||
},
|
||||
|
||||
_recvZoom: function(data) {
|
||||
docShell.contentViewer.fullZoom = data.json.zoom;
|
||||
},
|
||||
|
||||
_recvDoCommand: function(data) {
|
||||
if (this._isCommandEnabled(data.json.command)) {
|
||||
docShell.doCommand(COMMAND_MAP[data.json.command]);
|
||||
|
@ -126,6 +126,7 @@ function BrowserElementParent(frameLoader, hasRemoteFrame, isPendingFrame) {
|
||||
defineNoReturnMethod('goForward', this._goForward);
|
||||
defineNoReturnMethod('reload', this._reload);
|
||||
defineNoReturnMethod('stop', this._stop);
|
||||
defineNoReturnMethod('zoom', this._zoom);
|
||||
defineMethod('download', this._download);
|
||||
defineDOMRequestMethod('purgeHistory', 'purge-history');
|
||||
defineMethod('getScreenshot', this._getScreenshot);
|
||||
@ -608,6 +609,16 @@ BrowserElementParent.prototype = {
|
||||
this._sendAsyncMsg('stop');
|
||||
},
|
||||
|
||||
/*
|
||||
* The valid range of zoom scale is defined in preference "zoom.maxPercent" and "zoom.minPercent".
|
||||
*/
|
||||
_zoom: function(zoom) {
|
||||
zoom *= 100;
|
||||
zoom = Math.min(getIntPref("zoom.maxPercent", 300), zoom);
|
||||
zoom = Math.max(getIntPref("zoom.minPercent", 50), zoom);
|
||||
this._sendAsyncMsg('zoom', {zoom: zoom / 100.0});
|
||||
},
|
||||
|
||||
_download: function(_url, _options) {
|
||||
let ioService =
|
||||
Cc['@mozilla.org/network/io-service;1'].getService(Ci.nsIIOService);
|
||||
|
@ -60,6 +60,10 @@ const kEventConstructors = {
|
||||
return new BluetoothDiscoveryStateChangedEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
BluetoothPairingEvent: { create: function (aName, aProps) {
|
||||
return new BluetoothPairingEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
BluetoothStatusChangedEvent: { create: function (aName, aProps) {
|
||||
return new BluetoothStatusChangedEvent(aName, aProps);
|
||||
},
|
||||
|
@ -198,6 +198,18 @@ let PaymentManager = {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
let appsService = Cc["@mozilla.org/AppsService;1"]
|
||||
.getService(Ci.nsIAppsService);
|
||||
let systemAppId = Ci.nsIScriptSecurityManager.NO_APP_ID;
|
||||
|
||||
try {
|
||||
let manifestURL = Services.prefs.getCharPref("b2g.system_manifest_url");
|
||||
systemAppId = appsService.getAppLocalIdByManifestURL(manifestURL);
|
||||
this.LOG("System app id=" + systemAppId);
|
||||
} catch(e) {}
|
||||
#endif
|
||||
|
||||
// Now register the payment providers.
|
||||
for (let i in nums) {
|
||||
let branch = prefService
|
||||
@ -211,12 +223,28 @@ let PaymentManager = {
|
||||
if (type in this.registeredProviders) {
|
||||
continue;
|
||||
}
|
||||
this.registeredProviders[type] = {
|
||||
let provider = this.registeredProviders[type] = {
|
||||
name: branch.getCharPref("name"),
|
||||
uri: branch.getCharPref("uri"),
|
||||
description: branch.getCharPref("description"),
|
||||
requestMethod: branch.getCharPref("requestMethod")
|
||||
};
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
// Let this payment provider access the firefox-accounts API when
|
||||
// it's loaded in the trusted UI.
|
||||
if (systemAppId != Ci.nsIScriptSecurityManager.NO_APP_ID) {
|
||||
this.LOG("Granting firefox-accounts permission to " + provider.uri);
|
||||
let uri = Services.io.newURI(provider.uri, null, null);
|
||||
let principal = Services.scriptSecurityManager
|
||||
.getAppCodebasePrincipal(uri, systemAppId, true);
|
||||
|
||||
Services.perms.addFromPrincipal(principal, "firefox-accounts",
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION,
|
||||
Ci.nsIPermissionManager.EXPIRE_SESSION);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (this._debug) {
|
||||
this.LOG("Registered Payment Providers: " +
|
||||
JSON.stringify(this.registeredProviders[type]));
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
DIRS += ['interfaces']
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
EXTRA_PP_JS_MODULES += [
|
||||
'Payment.jsm',
|
||||
]
|
||||
|
||||
|
@ -113,12 +113,6 @@ interface BluetoothAdapter : EventTarget {
|
||||
DOMRequest getPairedDevices();
|
||||
[NewObject, Throws]
|
||||
DOMRequest getConnectedDevices(unsigned short serviceUuid);
|
||||
[NewObject, Throws]
|
||||
DOMRequest setPinCode(DOMString deviceAddress, DOMString pinCode);
|
||||
[NewObject, Throws]
|
||||
DOMRequest setPasskey(DOMString deviceAddress, unsigned long passkey);
|
||||
[NewObject, Throws]
|
||||
DOMRequest setPairingConfirmation(DOMString deviceAddress, boolean confirmation);
|
||||
|
||||
/**
|
||||
* Connect/Disconnect to a specific service of a target remote device.
|
||||
|
20
dom/webidl/BluetoothPairingEvent.webidl
Normal file
20
dom/webidl/BluetoothPairingEvent.webidl
Normal file
@ -0,0 +1,20 @@
|
||||
/* -*- 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/.
|
||||
*/
|
||||
|
||||
[CheckPermissions="bluetooth",
|
||||
Constructor(DOMString type,
|
||||
optional BluetoothPairingEventInit eventInitDict)]
|
||||
interface BluetoothPairingEvent : Event
|
||||
{
|
||||
readonly attribute BluetoothDevice? device;
|
||||
readonly attribute BluetoothPairingHandle? handle;
|
||||
};
|
||||
|
||||
dictionary BluetoothPairingEventInit : EventInit
|
||||
{
|
||||
BluetoothDevice? device = null;
|
||||
BluetoothPairingHandle? handle = null;
|
||||
};
|
22
dom/webidl/BluetoothPairingHandle.webidl
Normal file
22
dom/webidl/BluetoothPairingHandle.webidl
Normal file
@ -0,0 +1,22 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
[CheckPermissions="bluetooth"]
|
||||
interface BluetoothPairingHandle
|
||||
{
|
||||
/**
|
||||
* A 6-digit string ranging from decimal 000000 to 999999.
|
||||
* This attribute is an empty string for enterpincodereq and
|
||||
* pairingconsentreq.
|
||||
*/
|
||||
readonly attribute DOMString passkey;
|
||||
|
||||
// Promise<void>
|
||||
[NewObject, Throws]
|
||||
Promise setPinCode(DOMString aPinCode);
|
||||
// Promise<void>
|
||||
[NewObject, Throws]
|
||||
Promise setPairingConfirmation(boolean aConfirm);
|
||||
};
|
@ -571,6 +571,7 @@ if CONFIG['MOZ_B2G_BT']:
|
||||
'BluetoothDevice2.webidl',
|
||||
'BluetoothDiscoveryHandle.webidl',
|
||||
'BluetoothManager2.webidl',
|
||||
'BluetoothPairingHandle.webidl',
|
||||
]
|
||||
else:
|
||||
WEBIDL_FILES += [
|
||||
@ -696,6 +697,7 @@ if CONFIG['MOZ_B2G_BT']:
|
||||
GENERATED_EVENTS_WEBIDL_FILES += [
|
||||
'BluetoothAdapterEvent.webidl',
|
||||
'BluetoothAttributeEvent.webidl',
|
||||
'BluetoothPairingEvent.webidl',
|
||||
]
|
||||
else:
|
||||
GENERATED_EVENTS_WEBIDL_FILES += [
|
||||
|
@ -3642,6 +3642,14 @@ nsTextPaintStyle::InitSelectionColorsAndShadow()
|
||||
eCSSProperty_color;
|
||||
nscoord frameColor = mFrame->GetVisitedDependentColor(property);
|
||||
mSelectionTextColor = EnsureDifferentColors(frameColor, mSelectionBGColor);
|
||||
} else if (mSelectionTextColor == NS_CHANGE_COLOR_IF_SAME_AS_BG) {
|
||||
nsCSSProperty property = mFrame->IsSVGText() ? eCSSProperty_fill :
|
||||
eCSSProperty_color;
|
||||
nscolor frameColor = mFrame->GetVisitedDependentColor(property);
|
||||
if (frameColor == mSelectionBGColor) {
|
||||
mSelectionTextColor =
|
||||
LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForegroundCustom);
|
||||
}
|
||||
} else {
|
||||
EnsureSufficientContrast(&mSelectionTextColor, &mSelectionBGColor);
|
||||
}
|
||||
|
@ -26,6 +26,15 @@ function setPermissions() {
|
||||
perms.add(uri, "allowXULXBL", Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
}
|
||||
|
||||
let cm = Cc["@mozilla.org/categorymanager;1"]
|
||||
.getService(Components.interfaces.nsICategoryManager);
|
||||
|
||||
// Disable update timers that cause b2g failures.
|
||||
if (cm) {
|
||||
cm.deleteCategoryEntry("update-timer", "WebappsUpdateTimer", false);
|
||||
cm.deleteCategoryEntry("update-timer", "nsUpdateService", false);
|
||||
}
|
||||
|
||||
// Load into any existing windows
|
||||
let wm = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator);
|
||||
|
@ -15,7 +15,6 @@
|
||||
#endif
|
||||
android:targetSdkVersion="@ANDROID_TARGET_SDK@"/>
|
||||
|
||||
#include ../services/manifests/AnnouncementsAndroidManifest_permissions.xml.in
|
||||
#include ../services/manifests/FxAccountAndroidManifest_permissions.xml.in
|
||||
#include ../services/manifests/HealthReportAndroidManifest_permissions.xml.in
|
||||
#include ../services/manifests/SyncAndroidManifest_permissions.xml.in
|
||||
@ -330,7 +329,6 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
#include ../services/manifests/AnnouncementsAndroidManifest_activities.xml.in
|
||||
#include ../services/manifests/FxAccountAndroidManifest_activities.xml.in
|
||||
#include ../services/manifests/HealthReportAndroidManifest_activities.xml.in
|
||||
#include ../services/manifests/SyncAndroidManifest_activities.xml.in
|
||||
@ -428,7 +426,6 @@
|
||||
</service>
|
||||
|
||||
|
||||
#include ../services/manifests/AnnouncementsAndroidManifest_services.xml.in
|
||||
#include ../services/manifests/FxAccountAndroidManifest_services.xml.in
|
||||
#include ../services/manifests/HealthReportAndroidManifest_services.xml.in
|
||||
#include ../services/manifests/SyncAndroidManifest_services.xml.in
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||
* 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/. */
|
||||
|
||||
@ -10,6 +11,8 @@ import org.mozilla.gecko.util.EventCallback;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONException;
|
||||
|
||||
import com.google.android.gms.cast.Cast.MessageReceivedCallback;
|
||||
import com.google.android.gms.cast.ApplicationMetadata;
|
||||
import com.google.android.gms.cast.Cast;
|
||||
import com.google.android.gms.cast.Cast.ApplicationConnectionResult;
|
||||
import com.google.android.gms.cast.CastDevice;
|
||||
@ -33,10 +36,13 @@ import android.util.Log;
|
||||
class ChromeCast implements GeckoMediaPlayer {
|
||||
private static final boolean SHOW_DEBUG = false;
|
||||
|
||||
static final String MIRROR_RECIEVER_APP_ID = "D40D28D6";
|
||||
|
||||
private final Context context;
|
||||
private final RouteInfo route;
|
||||
private GoogleApiClient apiClient;
|
||||
private RemoteMediaPlayer remoteMediaPlayer;
|
||||
private boolean canMirror;
|
||||
|
||||
// Callback to start playback of a url on a remote device
|
||||
private class VideoPlayCallback implements ResultCallback<ApplicationConnectionResult>,
|
||||
@ -133,6 +139,7 @@ class ChromeCast implements GeckoMediaPlayer {
|
||||
public ChromeCast(Context context, RouteInfo route) {
|
||||
this.context = context;
|
||||
this.route = route;
|
||||
this.canMirror = route.supportsControlCategory(CastMediaControlIntent.categoryForCast(MIRROR_RECIEVER_APP_ID));
|
||||
}
|
||||
|
||||
// This dumps everything we can find about the device into JSON. This will hopefully make it
|
||||
@ -146,6 +153,7 @@ class ChromeCast implements GeckoMediaPlayer {
|
||||
obj.put("friendlyName", device.getFriendlyName());
|
||||
obj.put("location", device.getIpAddress().toString());
|
||||
obj.put("modelName", device.getModelName());
|
||||
obj.put("mirror", canMirror);
|
||||
// For now we just assume all of these are Google devices
|
||||
obj.put("manufacturer", "Google Inc.");
|
||||
} catch(JSONException ex) {
|
||||
@ -263,6 +271,127 @@ class ChromeCast implements GeckoMediaPlayer {
|
||||
});
|
||||
}
|
||||
|
||||
private String mSessionId;
|
||||
MirrorChannel mMirrorChannel;
|
||||
boolean mApplicationStarted = false;
|
||||
|
||||
class MirrorChannel implements MessageReceivedCallback {
|
||||
|
||||
/**
|
||||
* @return custom namespace
|
||||
*/
|
||||
public String getNamespace() {
|
||||
return "urn:x-cast:org.mozilla.mirror";
|
||||
}
|
||||
|
||||
/*
|
||||
* Receive message from the receiver app
|
||||
*/
|
||||
@Override
|
||||
public void onMessageReceived(CastDevice castDevice, String namespace,
|
||||
String message) {
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("MediaPlayer:Response", message));
|
||||
}
|
||||
|
||||
public void sendMessage(String message) {
|
||||
if (apiClient != null && mMirrorChannel != null) {
|
||||
try {
|
||||
Cast.CastApi.sendMessage(apiClient, mMirrorChannel.getNamespace(), message)
|
||||
.setResultCallback(
|
||||
new ResultCallback<Status>() {
|
||||
@Override
|
||||
public void onResult(Status result) {
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Log.e(LOGTAG, "Exception while sending message", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private class MirrorCallback implements ResultCallback<ApplicationConnectionResult> {
|
||||
|
||||
final EventCallback callback;
|
||||
MirrorCallback(final EventCallback callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
|
||||
public void onResult(ApplicationConnectionResult result) {
|
||||
Status status = result.getStatus();
|
||||
if (status.isSuccess()) {
|
||||
ApplicationMetadata applicationMetadata = result.getApplicationMetadata();
|
||||
mSessionId = result.getSessionId();
|
||||
String applicationStatus = result.getApplicationStatus();
|
||||
boolean wasLaunched = result.getWasLaunched();
|
||||
mApplicationStarted = true;
|
||||
|
||||
// Create the custom message
|
||||
// channel
|
||||
mMirrorChannel = new MirrorChannel();
|
||||
try {
|
||||
Cast.CastApi.setMessageReceivedCallbacks(apiClient,
|
||||
mMirrorChannel
|
||||
.getNamespace(),
|
||||
mMirrorChannel);
|
||||
callback.sendSuccess(null);
|
||||
} catch (IOException e) {
|
||||
Log.e(LOGTAG, "Exception while creating channel", e);
|
||||
}
|
||||
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Casting:Mirror", route.getId()));
|
||||
} else {
|
||||
callback.sendError(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void message(String msg, final EventCallback callback) {
|
||||
if (mMirrorChannel != null) {
|
||||
mMirrorChannel.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public void mirror(final EventCallback callback) {
|
||||
final CastDevice device = CastDevice.getFromBundle(route.getExtras());
|
||||
Cast.CastOptions.Builder apiOptionsBuilder = Cast.CastOptions.builder(device, new Cast.Listener() {
|
||||
@Override
|
||||
public void onApplicationStatusChanged() { }
|
||||
|
||||
@Override
|
||||
public void onVolumeChanged() { }
|
||||
|
||||
@Override
|
||||
public void onApplicationDisconnected(int errorCode) { }
|
||||
});
|
||||
|
||||
apiClient = new GoogleApiClient.Builder(context)
|
||||
.addApi(Cast.API, apiOptionsBuilder.build())
|
||||
.addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
|
||||
@Override
|
||||
public void onConnected(Bundle connectionHint) {
|
||||
if (!apiClient.isConnected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Launch the media player app and launch this url once its loaded
|
||||
try {
|
||||
Cast.CastApi.launchApplication(apiClient, MIRROR_RECIEVER_APP_ID, true)
|
||||
.setResultCallback(new MirrorCallback(callback));
|
||||
} catch (Exception e) {
|
||||
debug("Failed to launch application", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionSuspended(int cause) {
|
||||
debug("suspended");
|
||||
}
|
||||
}).build();
|
||||
|
||||
apiClient.connect();
|
||||
}
|
||||
|
||||
private static final String LOGTAG = "GeckoChromeCast";
|
||||
private void debug(String msg, Exception e) {
|
||||
if (SHOW_DEBUG) {
|
||||
|
@ -1546,7 +1546,7 @@ public class ContactService implements GeckoEventListener {
|
||||
}
|
||||
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
|
||||
builder.setTitle(mActivity.getResources().getString(R.string.contacts_account_chooser_dialog_title))
|
||||
builder.setTitle(mActivity.getResources().getString(R.string.contacts_account_chooser_dialog_title2))
|
||||
.setSingleChoiceItems(accountNames, 0, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int position) {
|
||||
|
@ -27,7 +27,6 @@ import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
|
||||
import org.mozilla.gecko.background.announcements.AnnouncementsBroadcastService;
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
import org.mozilla.gecko.favicons.Favicons;
|
||||
import org.mozilla.gecko.gfx.BitmapUtils;
|
||||
@ -1562,16 +1561,10 @@ public abstract class GeckoApp
|
||||
rec.recordJavaStartupTime(javaDuration);
|
||||
}
|
||||
|
||||
// Record our launch time for the announcements service
|
||||
// to use in assessing inactivity.
|
||||
final Context context = GeckoApp.this;
|
||||
AnnouncementsBroadcastService.recordLastLaunch(context);
|
||||
|
||||
// Kick off our background services. We do this by invoking the broadcast
|
||||
// receiver, which uses the system alarm infrastructure to perform tasks at
|
||||
// intervals.
|
||||
GeckoPreferences.broadcastAnnouncementsPref(context);
|
||||
GeckoPreferences.broadcastHealthReportUploadPref(context);
|
||||
GeckoPreferences.broadcastHealthReportUploadPref(GeckoApp.this);
|
||||
if (!GeckoThread.checkLaunchState(GeckoThread.LaunchState.Launched)) {
|
||||
return;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||
* 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/. */
|
||||
|
||||
@ -19,6 +20,8 @@ import android.support.v7.media.MediaRouter;
|
||||
import android.support.v7.media.MediaRouter.RouteInfo;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.cast.CastMediaControlIntent;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/* Wraper for different MediaRouter types supproted by Android. i.e. Chromecast, Miracast, etc. */
|
||||
@ -30,6 +33,8 @@ interface GeckoMediaPlayer {
|
||||
public void stop(EventCallback callback);
|
||||
public void start(EventCallback callback);
|
||||
public void end(EventCallback callback);
|
||||
public void mirror(EventCallback callback);
|
||||
public void message(String message, EventCallback callback);
|
||||
}
|
||||
|
||||
/* Manages a list of GeckoMediaPlayers methods (i.e. Chromecast/Miracast). Routes messages
|
||||
@ -74,14 +79,17 @@ class MediaPlayerManager implements NativeEventListener,
|
||||
app.addAppStateListener(this);
|
||||
}
|
||||
|
||||
mediaRouter = MediaRouter.getInstance(context);
|
||||
EventDispatcher.getInstance().registerGeckoThreadListener(this, "MediaPlayer:Load",
|
||||
"MediaPlayer:Start",
|
||||
"MediaPlayer:Stop",
|
||||
"MediaPlayer:Play",
|
||||
"MediaPlayer:Pause",
|
||||
"MediaPlayer:Get",
|
||||
"MediaPlayer:End");
|
||||
mediaRouter = MediaRouter.getInstance(context);
|
||||
EventDispatcher.getInstance().registerGeckoThreadListener(this,
|
||||
"MediaPlayer:Load",
|
||||
"MediaPlayer:Start",
|
||||
"MediaPlayer:Stop",
|
||||
"MediaPlayer:Play",
|
||||
"MediaPlayer:Pause",
|
||||
"MediaPlayer:Get",
|
||||
"MediaPlayer:End",
|
||||
"MediaPlayer:Mirror",
|
||||
"MediaPlayer:Message");
|
||||
}
|
||||
|
||||
public static void onDestroy() {
|
||||
@ -89,13 +97,16 @@ class MediaPlayerManager implements NativeEventListener,
|
||||
return;
|
||||
}
|
||||
|
||||
EventDispatcher.getInstance().unregisterGeckoThreadListener(instance, "MediaPlayer:Load",
|
||||
"MediaPlayer:Start",
|
||||
"MediaPlayer:Stop",
|
||||
"MediaPlayer:Play",
|
||||
"MediaPlayer:Pause",
|
||||
"MediaPlayer:Get",
|
||||
"MediaPlayer:End");
|
||||
EventDispatcher.getInstance().unregisterGeckoThreadListener(instance,
|
||||
"MediaPlayer:Load",
|
||||
"MediaPlayer:Start",
|
||||
"MediaPlayer:Stop",
|
||||
"MediaPlayer:Play",
|
||||
"MediaPlayer:Pause",
|
||||
"MediaPlayer:Get",
|
||||
"MediaPlayer:End",
|
||||
"MediaPlayer:Mirror",
|
||||
"MediaPlayer:Message");
|
||||
if (instance.context instanceof GeckoApp) {
|
||||
GeckoApp app = (GeckoApp) instance.context;
|
||||
app.removeAppStateListener(instance);
|
||||
@ -132,8 +143,10 @@ class MediaPlayerManager implements NativeEventListener,
|
||||
|
||||
final GeckoMediaPlayer display = displays.get(message.getString("id"));
|
||||
if (display == null) {
|
||||
Log.e(LOGTAG, "Couldn't find a display for this id");
|
||||
callback.sendError(null);
|
||||
Log.e(LOGTAG, "Couldn't find a display for this id: " + message.getString("id") + " for message: " + event);
|
||||
if (callback != null) {
|
||||
callback.sendError(null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -147,6 +160,10 @@ class MediaPlayerManager implements NativeEventListener,
|
||||
display.pause(callback);
|
||||
} else if ("MediaPlayer:End".equals(event)) {
|
||||
display.end(callback);
|
||||
} else if ("MediaPlayer:Mirror".equals(event)) {
|
||||
display.mirror(callback);
|
||||
} else if ("MediaPlayer:Message".equals(event) && message.has("data")) {
|
||||
display.message(message.getString("data"), callback);
|
||||
} else if ("MediaPlayer:Load".equals(event)) {
|
||||
final String url = message.optString("source", "");
|
||||
final String type = message.optString("type", "video/mp4");
|
||||
@ -155,48 +172,49 @@ class MediaPlayerManager implements NativeEventListener,
|
||||
}
|
||||
}
|
||||
|
||||
private final MediaRouter.Callback callback = new MediaRouter.Callback() {
|
||||
@Override
|
||||
public void onRouteRemoved(MediaRouter router, RouteInfo route) {
|
||||
debug("onRouteRemoved: route=" + route);
|
||||
displays.remove(route.getId());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void onRouteSelected(MediaRouter router, int type, MediaRouter.RouteInfo route) {
|
||||
}
|
||||
|
||||
// These methods aren't used by the support version Media Router
|
||||
@SuppressWarnings("unused")
|
||||
public void onRouteUnselected(MediaRouter router, int type, RouteInfo route) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo route) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo route) {
|
||||
debug("onRouteAdded: route=" + route);
|
||||
GeckoMediaPlayer display = getMediaPlayerForRoute(route);
|
||||
if (display != null) {
|
||||
displays.put(route.getId(), display);
|
||||
private final MediaRouter.Callback callback =
|
||||
new MediaRouter.Callback() {
|
||||
@Override
|
||||
public void onRouteRemoved(MediaRouter router, RouteInfo route) {
|
||||
debug("onRouteRemoved: route=" + route);
|
||||
displays.remove(route.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo route) {
|
||||
debug("onRouteChanged: route=" + route);
|
||||
GeckoMediaPlayer display = displays.get(route.getId());
|
||||
if (display != null) {
|
||||
displays.put(route.getId(), display);
|
||||
@SuppressWarnings("unused")
|
||||
public void onRouteSelected(MediaRouter router, int type, MediaRouter.RouteInfo route) {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// These methods aren't used by the support version Media Router
|
||||
@SuppressWarnings("unused")
|
||||
public void onRouteUnselected(MediaRouter router, int type, RouteInfo route) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo route) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo route) {
|
||||
debug("onRouteAdded: route=" + route);
|
||||
GeckoMediaPlayer display = getMediaPlayerForRoute(route);
|
||||
if (display != null) {
|
||||
displays.put(route.getId(), display);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo route) {
|
||||
debug("onRouteChanged: route=" + route);
|
||||
GeckoMediaPlayer display = displays.get(route.getId());
|
||||
if (display != null) {
|
||||
displays.put(route.getId(), display);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private GeckoMediaPlayer getMediaPlayerForRoute(MediaRouter.RouteInfo route) {
|
||||
try {
|
||||
@ -221,6 +239,7 @@ class MediaPlayerManager implements NativeEventListener,
|
||||
MediaRouteSelector selectorBuilder = new MediaRouteSelector.Builder()
|
||||
.addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)
|
||||
.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
|
||||
.addControlCategory(CastMediaControlIntent.categoryForCast(ChromeCast.MIRROR_RECIEVER_APP_ID))
|
||||
.build();
|
||||
mediaRouter.addCallback(selectorBuilder, callback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
|
||||
}
|
||||
|
@ -178,7 +178,6 @@ size. -->
|
||||
<!ENTITY pref_font_size_preview_text "The quick orange fox jumps over your expectations with more speed, more flexibility and more security. As a non-profit, we\'re free to innovate on your behalf without any pressure to compromise. That means a better experience for you and a brighter future for the Web.">
|
||||
|
||||
<!ENTITY pref_use_master_password "Use master password">
|
||||
<!ENTITY pref_show_product_announcements "Show product announcements">
|
||||
<!ENTITY pref_sync "Sync">
|
||||
<!ENTITY pref_search_suggestions "Show search suggestions">
|
||||
<!ENTITY pref_import_android "Import from Android">
|
||||
|
@ -29,7 +29,6 @@ import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.Telemetry;
|
||||
import org.mozilla.gecko.TelemetryContract;
|
||||
import org.mozilla.gecko.TelemetryContract.Method;
|
||||
import org.mozilla.gecko.background.announcements.AnnouncementsConstants;
|
||||
import org.mozilla.gecko.background.common.GlobalConstants;
|
||||
import org.mozilla.gecko.background.healthreport.HealthReportConstants;
|
||||
import org.mozilla.gecko.db.BrowserContract.SuggestedSites;
|
||||
@ -106,7 +105,6 @@ OnSharedPreferenceChangeListener
|
||||
// These match keys in resources/xml*/preferences*.xml
|
||||
private static final String PREFS_SEARCH_RESTORE_DEFAULTS = NON_PREF_PREFIX + "search.restore_defaults";
|
||||
private static final String PREFS_HOME_ADD_PANEL = NON_PREF_PREFIX + "home.add_panel";
|
||||
private static final String PREFS_ANNOUNCEMENTS_ENABLED = NON_PREF_PREFIX + "privacy.announcements.enabled";
|
||||
private static final String PREFS_DATA_REPORTING_PREFERENCES = NON_PREF_PREFIX + "datareporting.preferences";
|
||||
private static final String PREFS_TELEMETRY_ENABLED = "toolkit.telemetry.enabled";
|
||||
private static final String PREFS_CRASHREPORTER_ENABLED = "datareporting.crashreporter.submitEnabled";
|
||||
@ -838,26 +836,6 @@ OnSharedPreferenceChangeListener
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast the provided value as the value of the
|
||||
* <code>PREFS_ANNOUNCEMENTS_ENABLED</code> pref.
|
||||
*/
|
||||
public static void broadcastAnnouncementsPref(final Context context, final boolean value) {
|
||||
broadcastPrefAction(context,
|
||||
AnnouncementsConstants.ACTION_ANNOUNCEMENTS_PREF,
|
||||
PREFS_ANNOUNCEMENTS_ENABLED,
|
||||
value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast the current value of the
|
||||
* <code>PREFS_ANNOUNCEMENTS_ENABLED</code> pref.
|
||||
*/
|
||||
public static void broadcastAnnouncementsPref(final Context context) {
|
||||
final boolean value = getBooleanPref(context, PREFS_ANNOUNCEMENTS_ENABLED, true);
|
||||
broadcastAnnouncementsPref(context, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast the provided value as the value of the
|
||||
* <code>PREFS_HEALTHREPORT_UPLOAD_ENABLED</code> pref.
|
||||
@ -1031,10 +1009,6 @@ OnSharedPreferenceChangeListener
|
||||
|
||||
if (PREFS_MENU_CHAR_ENCODING.equals(prefName)) {
|
||||
setCharEncodingState(((String) newValue).equals("true"));
|
||||
} else if (PREFS_ANNOUNCEMENTS_ENABLED.equals(prefName)) {
|
||||
// Send a broadcast intent to the product announcements service, either to start or
|
||||
// to stop the repeated background checks.
|
||||
broadcastAnnouncementsPref(this, ((Boolean) newValue).booleanValue());
|
||||
} else if (PREFS_UPDATER_AUTODOWNLOAD.equals(prefName)) {
|
||||
org.mozilla.gecko.updater.UpdateServiceHelper.registerForUpdates(this, (String) newValue);
|
||||
} else if (PREFS_HEALTHREPORT_UPLOAD_ENABLED.equals(prefName)) {
|
||||
|
@ -95,6 +95,8 @@
|
||||
<item name="floatingHintEditTextStyle">@style/FloatingHintEditText</item>
|
||||
</style>
|
||||
|
||||
<style name="Gecko.Preferences" parent="GeckoPreferencesBase"/>
|
||||
<style name="Gecko.Preferences" parent="GeckoPreferencesBase">
|
||||
<item name="floatingHintEditTextStyle">@style/FloatingHintEditText</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
@ -17,11 +17,6 @@
|
||||
<org.mozilla.gecko.preferences.LinkPreference android:title="@string/pref_vendor_feedback"
|
||||
url="about:feedback" />
|
||||
|
||||
<CheckBoxPreference android:key="android.not_a_preference.privacy.announcements.enabled"
|
||||
android:title="@string/pref_show_product_announcements"
|
||||
android:defaultValue="true"
|
||||
android:persistent="true" />
|
||||
|
||||
<PreferenceCategory android:key="android.not_a_preference.datareporting.preferences"
|
||||
android:title="@string/pref_category_datareporting">
|
||||
|
||||
|
@ -188,7 +188,6 @@
|
||||
<string name="pref_restore">&pref_restore;</string>
|
||||
<string name="pref_restore_always">&pref_restore_always;</string>
|
||||
<string name="pref_restore_quit">&pref_restore_quit;</string>
|
||||
<string name="pref_show_product_announcements">&pref_show_product_announcements;</string>
|
||||
<string name="pref_sync">&pref_sync;</string>
|
||||
<string name="pref_search_suggestions">&pref_search_suggestions;</string>
|
||||
<string name="pref_private_data_history2">&pref_private_data_history2;</string>
|
||||
@ -417,7 +416,7 @@
|
||||
<string name="set_image_chooser_title">&set_image_chooser_title;</string>
|
||||
|
||||
<!-- Contacts API -->
|
||||
<string name="contacts_account_chooser_dialog_title">Share contacts from...</string>
|
||||
<string name="contacts_account_chooser_dialog_title2">Share contacts from…</string>
|
||||
|
||||
|
||||
<!-- Guest mode -->
|
||||
|
@ -131,7 +131,6 @@ public class StringHelper {
|
||||
public static final String ABOUT_LABEL = "About (Fennec|Nightly|Aurora|Firefox Beta|Firefox)";
|
||||
public static final String FAQS_LABEL = "FAQs";
|
||||
public static final String FEEDBACK_LABEL = "Give feedback";
|
||||
public static final String PRODUCT_ANNOUNCEMENTS_LABEL = "Show product announcements";
|
||||
public static final String LOCATION_SERVICES_LABEL = "Mozilla location services";
|
||||
public static final String HELTH_REPORT_LABEL = "(Fennec|Nightly|Aurora|Firefox Beta|Firefox) Health Report";
|
||||
public static final String MY_HEALTH_REPORT_LABEL = "View my Health Report";
|
||||
|
@ -71,7 +71,6 @@ public class testSettingsMenuItems extends PixelTest {
|
||||
{ "About " + BRAND_NAME },
|
||||
{ StringHelper.FAQS_LABEL },
|
||||
{ StringHelper.FEEDBACK_LABEL },
|
||||
{ StringHelper.PRODUCT_ANNOUNCEMENTS_LABEL },
|
||||
{ "Data choices" },
|
||||
{ BRAND_NAME + " Health Report", "Shares data with Mozilla about your browser health and helps you understand your browser performance" },
|
||||
{ StringHelper.MY_HEALTH_REPORT_LABEL },
|
||||
|
@ -65,11 +65,30 @@ var CastingApps = {
|
||||
Services.obs.addObserver(this, "Casting:Play", false);
|
||||
Services.obs.addObserver(this, "Casting:Pause", false);
|
||||
Services.obs.addObserver(this, "Casting:Stop", false);
|
||||
Services.obs.addObserver(this, "Casting:Mirror", false);
|
||||
|
||||
BrowserApp.deck.addEventListener("TabSelect", this, true);
|
||||
BrowserApp.deck.addEventListener("pageshow", this, true);
|
||||
BrowserApp.deck.addEventListener("playing", this, true);
|
||||
BrowserApp.deck.addEventListener("ended", this, true);
|
||||
|
||||
NativeWindow.menu.add(
|
||||
Strings.browser.GetStringFromName("casting.mirrorTab"),
|
||||
"drawable://casting",
|
||||
function() {
|
||||
function callbackFunc(aService) {
|
||||
let app = SimpleServiceDiscovery.findAppForService(aService);
|
||||
if (app)
|
||||
app.mirror(function() {
|
||||
});
|
||||
}
|
||||
|
||||
function filterFunc(aService) {
|
||||
Cu.reportError("testing: " + aService);
|
||||
return aService.mirror == true;
|
||||
}
|
||||
this.prompt(callbackFunc, filterFunc);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
uninit: function ca_uninit() {
|
||||
@ -81,6 +100,7 @@ var CastingApps = {
|
||||
Services.obs.removeObserver(this, "Casting:Play");
|
||||
Services.obs.removeObserver(this, "Casting:Pause");
|
||||
Services.obs.removeObserver(this, "Casting:Stop");
|
||||
Services.obs.removeObserver(this, "Casting:Mirror");
|
||||
|
||||
NativeWindow.contextmenus.remove(this._castMenuId);
|
||||
},
|
||||
@ -106,6 +126,12 @@ var CastingApps = {
|
||||
this.closeExternal();
|
||||
}
|
||||
break;
|
||||
case "Casting:Mirror":
|
||||
{
|
||||
Cu.import("resource://gre/modules/TabMirror.jsm");
|
||||
this.tabMirror = new TabMirror(aData, window);
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -3084,6 +3084,39 @@ function Tab(aURL, aParams) {
|
||||
this.create(aURL, aParams);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sanity limit for URIs passed to UI code.
|
||||
*
|
||||
* 2000 is the typical industry limit, largely due to older IE versions.
|
||||
*
|
||||
* We use 25000, so we'll allow almost any value through.
|
||||
*
|
||||
* Still, this truncation doesn't affect history, so this is only a practical
|
||||
* concern in two ways: the truncated value is used when editing URIs, and as
|
||||
* the key for favicon fetches.
|
||||
*/
|
||||
const MAX_URI_LENGTH = 25000;
|
||||
|
||||
/*
|
||||
* Similar restriction for titles. This is only a display concern.
|
||||
*/
|
||||
const MAX_TITLE_LENGTH = 255;
|
||||
|
||||
/**
|
||||
* Ensure that a string is of a sane length.
|
||||
*/
|
||||
function truncate(text, max) {
|
||||
if (!text || !max) {
|
||||
return text;
|
||||
}
|
||||
|
||||
if (text.length <= max) {
|
||||
return text;
|
||||
}
|
||||
|
||||
return text.slice(0, max) + "…";
|
||||
}
|
||||
|
||||
Tab.prototype = {
|
||||
create: function(aURL, aParams) {
|
||||
if (this.browser)
|
||||
@ -3154,12 +3187,12 @@ Tab.prototype = {
|
||||
let message = {
|
||||
type: "Tab:Added",
|
||||
tabID: this.id,
|
||||
uri: uri,
|
||||
uri: truncate(uri, MAX_URI_LENGTH),
|
||||
parentId: ("parentId" in aParams) ? aParams.parentId : -1,
|
||||
tabIndex: ("tabIndex" in aParams) ? aParams.tabIndex : -1,
|
||||
external: ("external" in aParams) ? aParams.external : false,
|
||||
selected: ("selected" in aParams) ? aParams.selected : true,
|
||||
title: title,
|
||||
title: truncate(title, MAX_TITLE_LENGTH),
|
||||
delayLoad: aParams.delayLoad || false,
|
||||
desktopMode: this.desktopMode,
|
||||
isPrivate: isPrivate,
|
||||
@ -3208,7 +3241,7 @@ Tab.prototype = {
|
||||
this.browser.__SS_data = {
|
||||
entries: [{
|
||||
url: aURL,
|
||||
title: title
|
||||
title: truncate(title, MAX_TITLE_LENGTH)
|
||||
}],
|
||||
index: 1
|
||||
};
|
||||
@ -3947,7 +3980,7 @@ Tab.prototype = {
|
||||
sendMessageToJava({
|
||||
type: "DOMTitleChanged",
|
||||
tabID: this.id,
|
||||
title: aEvent.target.title.substring(0, 255)
|
||||
title: truncate(aEvent.target.title, MAX_TITLE_LENGTH)
|
||||
});
|
||||
break;
|
||||
}
|
||||
@ -4136,7 +4169,7 @@ Tab.prototype = {
|
||||
let message = {
|
||||
type: "Content:StateChange",
|
||||
tabID: this.id,
|
||||
uri: uri,
|
||||
uri: truncate(uri, MAX_URI_LENGTH),
|
||||
state: aStateFlags,
|
||||
restoring: restoring,
|
||||
success: success
|
||||
@ -4202,7 +4235,7 @@ Tab.prototype = {
|
||||
let message = {
|
||||
type: "Content:LocationChange",
|
||||
tabID: this.id,
|
||||
uri: fixedURI.spec,
|
||||
uri: truncate(fixedURI.spec, MAX_URI_LENGTH),
|
||||
userSearch: this.userSearch || "",
|
||||
baseDomain: baseDomain,
|
||||
contentType: (contentType ? contentType : ""),
|
||||
@ -7405,8 +7438,8 @@ let Reader = {
|
||||
sendMessageToJava({
|
||||
type: "Reader:Added",
|
||||
result: result,
|
||||
title: article.title,
|
||||
url: url,
|
||||
title: truncate(article.title, MAX_TITLE_LENGTH),
|
||||
url: truncate(url, MAX_URI_LENGTH),
|
||||
length: article.length,
|
||||
excerpt: article.excerpt
|
||||
});
|
||||
|
@ -176,6 +176,7 @@ selectionHelper.textCopied=Text copied to clipboard
|
||||
|
||||
# Casting
|
||||
casting.prompt=Cast to Device
|
||||
casting.mirrorTab=Mirror Tab
|
||||
|
||||
# Context menu
|
||||
contextmenu.openInNewTab=Open Link in New Tab
|
||||
|
@ -13,7 +13,7 @@ Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Messaging.jsm");
|
||||
let log = Cu.import("resource://gre/modules/AndroidLog.jsm", {}).AndroidLog.d.bind(null, "MediaPlayerApp");
|
||||
|
||||
// Helper function for sending commands to Java.
|
||||
// Helper function for sending commands to Java.
|
||||
function send(type, data, callback) {
|
||||
let msg = {
|
||||
type: type
|
||||
@ -52,6 +52,12 @@ MediaPlayerApp.prototype = {
|
||||
callback(new RemoteMedia(this.id, listener));
|
||||
}
|
||||
},
|
||||
|
||||
mirror: function mirror(callback) {
|
||||
send("MediaPlayer:Mirror", { id: this.id }, (result) => {
|
||||
if (callback) callback(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* RemoteMedia provides a proxy to a native media player session.
|
||||
|
@ -187,7 +187,8 @@ var SimpleServiceDiscovery = {
|
||||
friendlyName: display.friendlyName,
|
||||
uuid: display.uuid,
|
||||
manufacturer: display.manufacturer,
|
||||
modelName: display.modelName
|
||||
modelName: display.modelName,
|
||||
mirror: display.mirror
|
||||
};
|
||||
|
||||
this._addService(service);
|
||||
|
229
mobile/android/modules/TabMirror.jsm
Normal file
229
mobile/android/modules/TabMirror.jsm
Normal file
@ -0,0 +1,229 @@
|
||||
/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 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/. */
|
||||
"use strict";
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
let TabMirror = function(deviceId, window) {
|
||||
let out_queue = [];
|
||||
let in_queue = [];
|
||||
let DEBUG = false;
|
||||
|
||||
let log = Cu.import("resource://gre/modules/AndroidLog.jsm",
|
||||
{}).AndroidLog.d.bind(null, "TabMirror");
|
||||
|
||||
let RTCPeerConnection = window.mozRTCPeerConnection;
|
||||
let RTCSessionDescription = window.mozRTCSessionDescription;
|
||||
let RTCIceCandidate = window.mozRTCIceCandidate;
|
||||
let getUserMedia = window.navigator.mozGetUserMedia.bind(window.navigator);
|
||||
|
||||
Services.obs.addObserver(function observer(aSubject, aTopic, aData) {
|
||||
in_queue.push(aData);
|
||||
}, "MediaPlayer:Response", false);
|
||||
|
||||
let poll_timeout = 1000; // ms
|
||||
|
||||
let audio_stream = undefined;
|
||||
let video_stream = undefined;
|
||||
let pc = undefined;
|
||||
|
||||
let poll_success = function(msg) {
|
||||
if (!msg) {
|
||||
poll_error();
|
||||
return;
|
||||
}
|
||||
|
||||
let js;
|
||||
try {
|
||||
js = JSON.parse(msg);
|
||||
} catch(ex) {
|
||||
log("ex: " + ex);
|
||||
}
|
||||
|
||||
let sdp = js.body;
|
||||
|
||||
if (sdp) {
|
||||
if (sdp.sdp) {
|
||||
if (sdp.type === "offer") {
|
||||
process_offer(sdp);
|
||||
} else if (sdp.type === "answer") {
|
||||
process_answer(sdp);
|
||||
}
|
||||
} else {
|
||||
process_ice_candidate(sdp);
|
||||
}
|
||||
}
|
||||
|
||||
window.setTimeout(poll, poll_timeout);
|
||||
};
|
||||
|
||||
let poll_error = function (msg) {
|
||||
window.setTimeout(poll, poll_timeout);
|
||||
};
|
||||
|
||||
let poll = function () {
|
||||
if (in_queue) {
|
||||
poll_success(in_queue.pop());
|
||||
}
|
||||
};
|
||||
|
||||
let failure = function(x) {
|
||||
log("ERROR: " + JSON.stringify(x));
|
||||
};
|
||||
|
||||
|
||||
// Signaling methods
|
||||
let send_sdpdescription= function(sdp) {
|
||||
let msg = {
|
||||
body: sdp
|
||||
};
|
||||
|
||||
sendMessage(JSON.stringify(msg));
|
||||
};
|
||||
|
||||
|
||||
let deobjify = function(x) {
|
||||
return JSON.parse(JSON.stringify(x));
|
||||
};
|
||||
|
||||
|
||||
let process_offer = function(sdp) {
|
||||
pc.setRemoteDescription(new RTCSessionDescription(sdp),
|
||||
set_remote_offer_success, failure);
|
||||
};
|
||||
|
||||
let process_answer = function(sdp) {
|
||||
pc.setRemoteDescription(new RTCSessionDescription(sdp),
|
||||
set_remote_answer_success, failure);
|
||||
};
|
||||
|
||||
let process_ice_candidate = function(msg) {
|
||||
pc.addIceCandidate(new RTCIceCandidate(msg));
|
||||
};
|
||||
|
||||
let set_remote_offer_success = function() {
|
||||
pc.createAnswer(create_answer_success, failure);
|
||||
};
|
||||
|
||||
let set_remote_answer_success= function() {
|
||||
};
|
||||
|
||||
let set_local_success_offer = function(sdp) {
|
||||
send_sdpdescription(sdp);
|
||||
};
|
||||
|
||||
let set_local_success_answer = function(sdp) {
|
||||
send_sdpdescription(sdp);
|
||||
};
|
||||
|
||||
let filter_nonrelay_candidates = function(sdp) {
|
||||
let lines = sdp.sdp.split("\r\n");
|
||||
let lines2 = lines.filter(function(x) {
|
||||
if (!/candidate/.exec(x))
|
||||
return true;
|
||||
if (/relay/.exec(x))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
sdp.sdp = lines2.join("\r\n");
|
||||
};
|
||||
|
||||
let create_offer_success = function(sdp) {
|
||||
pc.setLocalDescription(sdp,
|
||||
function() {
|
||||
set_local_success_offer(sdp);
|
||||
},
|
||||
failure);
|
||||
};
|
||||
|
||||
let create_answer_success = function(sdp) {
|
||||
pc.setLocalDescription(sdp,
|
||||
function() {
|
||||
set_local_success_answer(sdp);
|
||||
},
|
||||
failure);
|
||||
};
|
||||
|
||||
let on_ice_candidate = function (candidate) {
|
||||
send_sdpdescription(candidate.candidate);
|
||||
};
|
||||
|
||||
let ready = function() {
|
||||
pc.createOffer(create_offer_success, failure);
|
||||
poll();
|
||||
};
|
||||
|
||||
|
||||
let config = {
|
||||
iceServers: [{ "url": "stun:stun.services.mozilla.com" }]
|
||||
};
|
||||
|
||||
let pc = new RTCPeerConnection(config, {});
|
||||
|
||||
if (!pc) {
|
||||
log("Failure creating Webrtc object");
|
||||
return;
|
||||
}
|
||||
|
||||
pc.onicecandidate = on_ice_candidate;
|
||||
|
||||
let windowId = window.BrowserApp.selectedBrowser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
|
||||
let viewport = window.BrowserApp.selectedTab.getViewport();
|
||||
let maxWidth = Math.max(viewport.cssWidth, viewport.width);
|
||||
let maxHeight = Math.max(viewport.cssHeight, viewport.height);
|
||||
let minRatio = Math.sqrt((maxWidth * maxHeight) / (640 * 480));
|
||||
|
||||
let screenWidth = 640;
|
||||
let screenHeight = 480;
|
||||
let videoWidth = 0;
|
||||
let videoHeight = 0;
|
||||
if (screenWidth/screenHeight > maxWidth / maxHeight) {
|
||||
videoWidth = screenWidth;
|
||||
videoHeight = Math.ceil(videoWidth * maxHeight / maxWidth);
|
||||
} else {
|
||||
videoHeight = screenHeight;
|
||||
videoWidth = Math.ceil(videoHeight * maxWidth / maxHeight);
|
||||
}
|
||||
|
||||
let constraints = {
|
||||
video: {
|
||||
mediaSource: "browser",
|
||||
advanced: [
|
||||
{ width: { min: videoWidth, max: videoWidth },
|
||||
height: { min: videoHeight, max: videoHeight }
|
||||
},
|
||||
{ aspectRatio: maxWidth / maxHeight }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
let gUM_success = function(stream){
|
||||
pc.addStream(stream);
|
||||
ready();
|
||||
};
|
||||
|
||||
let gUM_failure = function() {
|
||||
log("Could not get video stream");
|
||||
};
|
||||
|
||||
getUserMedia( constraints, gUM_success, gUM_failure);
|
||||
|
||||
function sendMessage(msg) {
|
||||
let obj = {
|
||||
type: "MediaPlayer:Message",
|
||||
id: deviceId,
|
||||
data: msg
|
||||
};
|
||||
|
||||
if (deviceId) {
|
||||
Services.androidBridge.handleGeckoMessage(obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["TabMirror"];
|
@ -24,6 +24,7 @@ EXTRA_JS_MODULES += [
|
||||
'SharedPreferences.jsm',
|
||||
'SimpleServiceDiscovery.jsm',
|
||||
'SSLExceptions.jsm',
|
||||
'TabMirror.jsm',
|
||||
'WebappManagerWorker.js',
|
||||
]
|
||||
|
||||
|
@ -1245,6 +1245,55 @@ the attempt, even if the result occurred on a different day from the attempt.
|
||||
Therefore, the sum of the result counts should equal the result of the attempt
|
||||
counts.
|
||||
|
||||
org.mozilla.hotfix.update
|
||||
-------------------------
|
||||
|
||||
This measurement contains results from the Firefox update hotfix.
|
||||
|
||||
The Firefox update hotfix bypasses the built-in application update mechanism
|
||||
and installs a modern Firefox.
|
||||
|
||||
Version 1
|
||||
^^^^^^^^^
|
||||
|
||||
The fields in this measurement are dynamically created based on which
|
||||
versions of the update hotfix state file are found on disk.
|
||||
|
||||
The general format of the fields is ``<version>.<thing>`` where ``version``
|
||||
is a hotfix version like ``v20140527`` and ``thing`` is a key from the
|
||||
hotfix state file, e.g. ``upgradedFrom``. Here are some of the ``things``
|
||||
that can be defined.
|
||||
|
||||
upgradedFrom
|
||||
String identifying the Firefox version that the hotfix upgraded from.
|
||||
e.g. ``16.0`` or ``17.0.1``.
|
||||
|
||||
uninstallReason
|
||||
String with enumerated values identifying why the hotfix was uninstalled.
|
||||
Value will be ``STILL_INSTALLED`` if the hotfix is still installed.
|
||||
|
||||
downloadAttempts
|
||||
Integer number of times the hotfix started downloading an installer.
|
||||
Download resumes are part of this count.
|
||||
|
||||
downloadFailures
|
||||
Integer count of times a download supposedly completed but couldn't
|
||||
be validated. This likely represents something wrong with the network
|
||||
connection. The ratio of this to ``downloadAttempts`` should be low.
|
||||
|
||||
installAttempts
|
||||
Integer count of times the hotfix attempted to run the installer.
|
||||
This should ideally be 1. It should only be greater than 1 if UAC
|
||||
elevation was cancelled or not allowed.
|
||||
|
||||
installFailures
|
||||
Integer count of total installation failures this client experienced.
|
||||
Can be 0. ``installAttempts - installFailures`` implies install successes.
|
||||
|
||||
notificationsShown
|
||||
Integer count of times a notification was displayed to the user that
|
||||
they are running an older Firefox.
|
||||
|
||||
org.mozilla.places.places
|
||||
-------------------------
|
||||
|
||||
|
@ -23,6 +23,7 @@ this.EXPORTED_SYMBOLS = [
|
||||
"CrashesProvider",
|
||||
#endif
|
||||
"HealthReportProvider",
|
||||
"HotfixProvider",
|
||||
"PlacesProvider",
|
||||
"SearchesProvider",
|
||||
"SessionsProvider",
|
||||
@ -1127,6 +1128,157 @@ CrashesProvider.prototype = Object.freeze({
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Records data from update hotfixes.
|
||||
*
|
||||
* This measurement has dynamic fields. Field names are of the form
|
||||
* <version>.<thing> where <version> is the hotfix version that produced
|
||||
* the data. e.g. "v20140527". The sub-version of the hotfix is omitted
|
||||
* because hotfixes can go through multiple minor versions during development
|
||||
* and we don't want to introduce more fields than necessary. Furthermore,
|
||||
* the subsequent dots make parsing field names slightly harder. By stripping,
|
||||
* we can just split on the first dot.
|
||||
*/
|
||||
function UpdateHotfixMeasurement1() {
|
||||
Metrics.Measurement.call(this);
|
||||
}
|
||||
|
||||
UpdateHotfixMeasurement1.prototype = Object.freeze({
|
||||
__proto__: Metrics.Measurement.prototype,
|
||||
|
||||
name: "update",
|
||||
version: 1,
|
||||
|
||||
hotfixFieldTypes: {
|
||||
"upgradedFrom": Metrics.Storage.FIELD_LAST_TEXT,
|
||||
"uninstallReason": Metrics.Storage.FIELD_LAST_TEXT,
|
||||
"downloadAttempts": Metrics.Storage.FIELD_LAST_NUMERIC,
|
||||
"downloadFailures": Metrics.Storage.FIELD_LAST_NUMERIC,
|
||||
"installAttempts": Metrics.Storage.FIELD_LAST_NUMERIC,
|
||||
"installFailures": Metrics.Storage.FIELD_LAST_NUMERIC,
|
||||
"notificationsShown": Metrics.Storage.FIELD_LAST_NUMERIC,
|
||||
},
|
||||
|
||||
fields: { },
|
||||
|
||||
// Our fields have dynamic names from the hotfix version that supplied them.
|
||||
// We need to override the default behavior to deal with unknown fields.
|
||||
shouldIncludeField: function (name) {
|
||||
return name.contains(".");
|
||||
},
|
||||
|
||||
fieldType: function (name) {
|
||||
for (let known in this.hotfixFieldTypes) {
|
||||
if (name.endsWith(known)) {
|
||||
return this.hotfixFieldTypes[known];
|
||||
}
|
||||
}
|
||||
|
||||
return Metrics.Measurement.prototype.fieldType.call(this, name);
|
||||
},
|
||||
});
|
||||
|
||||
this.HotfixProvider = function () {
|
||||
Metrics.Provider.call(this);
|
||||
};
|
||||
|
||||
HotfixProvider.prototype = Object.freeze({
|
||||
__proto__: Metrics.Provider.prototype,
|
||||
|
||||
name: "org.mozilla.hotfix",
|
||||
measurementTypes: [
|
||||
UpdateHotfixMeasurement1,
|
||||
],
|
||||
|
||||
pullOnly: true,
|
||||
|
||||
collectDailyData: function () {
|
||||
return this.storage.enqueueTransaction(this._populateHotfixData.bind(this));
|
||||
},
|
||||
|
||||
_populateHotfixData: function* () {
|
||||
let m = this.getMeasurement("update", 1);
|
||||
|
||||
// The update hotfix retains its JSON state file after uninstall.
|
||||
// The initial update hotfix had a hard-coded filename. We treat it
|
||||
// specially. Subsequent update hotfixes named their files in a
|
||||
// recognizeable pattern so we don't need to update this probe code to
|
||||
// know about them.
|
||||
let files = [
|
||||
["v20140527", OS.Path.join(OS.Constants.Path.profileDir,
|
||||
"hotfix.v20140527.01.json")],
|
||||
];
|
||||
|
||||
let it = new OS.File.DirectoryIterator(OS.Constants.Path.profileDir);
|
||||
try {
|
||||
yield it.forEach((e, index, it) => {
|
||||
let m = e.name.match(/^updateHotfix\.([a-zA-Z0-9]+)\.json$/);
|
||||
if (m) {
|
||||
files.push([m[1], e.path]);
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
it.close();
|
||||
}
|
||||
|
||||
let decoder = new TextDecoder();
|
||||
for (let e of files) {
|
||||
let [version, path] = e;
|
||||
let p;
|
||||
try {
|
||||
let data = yield OS.File.read(path);
|
||||
p = JSON.parse(decoder.decode(data));
|
||||
} catch (ex if ex instanceof OS.File.Error && ex.becauseNoSuchFile) {
|
||||
continue;
|
||||
} catch (ex) {
|
||||
this._log.warn("Error loading update hotfix payload: " + ex.message);
|
||||
}
|
||||
|
||||
// Wrap just in case.
|
||||
try {
|
||||
for (let k in m.hotfixFieldTypes) {
|
||||
if (!(k in p)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let value = p[k];
|
||||
if (value === null && k == "uninstallReason") {
|
||||
value = "STILL_INSTALLED";
|
||||
}
|
||||
|
||||
let field = version + "." + k;
|
||||
let fieldType;
|
||||
let storageOp;
|
||||
switch (typeof(value)) {
|
||||
case "string":
|
||||
fieldType = this.storage.FIELD_LAST_TEXT;
|
||||
storageOp = "setLastTextFromFieldID";
|
||||
break;
|
||||
case "number":
|
||||
fieldType = this.storage.FIELD_LAST_NUMERIC;
|
||||
storageOp = "setLastNumericFromFieldID";
|
||||
break;
|
||||
default:
|
||||
this._log.warn("Unknown value in hotfix state: " + k + "=" + value);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.storage.hasFieldFromMeasurement(m.id, field, fieldType)) {
|
||||
let fieldID = this.storage.fieldIDFromMeasurement(m.id, field);
|
||||
yield this.storage[storageOp](fieldID, value);
|
||||
} else {
|
||||
let fieldID = yield this.storage.registerField(m.id, field,
|
||||
fieldType);
|
||||
yield this.storage[storageOp](fieldID, value);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (ex) {
|
||||
this._log.warn("Error processing update hotfix data: " + ex);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Holds basic statistics about the Places database.
|
||||
|
179
services/healthreport/tests/xpcshell/test_provider_hotfix.js
Normal file
179
services/healthreport/tests/xpcshell/test_provider_hotfix.js
Normal file
@ -0,0 +1,179 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
Cu.import("resource://gre/modules/Metrics.jsm");
|
||||
Cu.import("resource://gre/modules/services/healthreport/providers.jsm");
|
||||
|
||||
const EXAMPLE_2014052701 = {
|
||||
"upgradedFrom":"13.0.1",
|
||||
"uninstallReason":"SUCCESSFUL_UPGRADE",
|
||||
"_entityID":null,
|
||||
"forensicsID":"29525548-b653-49db-bfb8-a160cdfbeb4a",
|
||||
"_installInProgress":false,
|
||||
"_everCompatible":true,
|
||||
"reportedWindowsVersion":[6,1,1],
|
||||
"actualWindowsVersion":[6,1,1],
|
||||
"firstNotifyDay":0,
|
||||
"lastNotifyDay":0,
|
||||
"downloadAttempts":1,
|
||||
"downloadFailures":0,
|
||||
"installAttempts":1,
|
||||
"installSuccesses":1,
|
||||
"installLauncherFailures":0,
|
||||
"installFailures":0,
|
||||
"notificationsShown":0,
|
||||
"notificationsClicked":0,
|
||||
"notificationsDismissed":0,
|
||||
"notificationsRemoved":0,
|
||||
"launcherExitCodes":{"0":1}
|
||||
};
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* init() {
|
||||
do_get_profile();
|
||||
});
|
||||
|
||||
add_task(function test_constructor() {
|
||||
new HotfixProvider();
|
||||
});
|
||||
|
||||
add_task(function* test_init() {
|
||||
let storage = yield Metrics.Storage("init");
|
||||
let provider = new HotfixProvider();
|
||||
yield provider.init(storage);
|
||||
yield provider.shutdown();
|
||||
|
||||
yield storage.close();
|
||||
});
|
||||
|
||||
add_task(function* test_collect_empty() {
|
||||
let storage = yield Metrics.Storage("collect_empty");
|
||||
let provider = new HotfixProvider();
|
||||
yield provider.init(storage);
|
||||
|
||||
yield provider.collectDailyData();
|
||||
|
||||
let m = provider.getMeasurement("update", 1);
|
||||
let data = yield m.getValues();
|
||||
Assert.equal(data.singular.size, 0);
|
||||
Assert.equal(data.days.size, 0);
|
||||
|
||||
yield storage.close();
|
||||
});
|
||||
|
||||
add_task(function* test_collect_20140527() {
|
||||
let storage = yield Metrics.Storage("collect_20140527");
|
||||
let provider = new HotfixProvider();
|
||||
yield provider.init(storage);
|
||||
|
||||
let path = OS.Path.join(OS.Constants.Path.profileDir,
|
||||
"hotfix.v20140527.01.json");
|
||||
let encoder = new TextEncoder();
|
||||
yield OS.File.writeAtomic(path,
|
||||
encoder.encode(JSON.stringify(EXAMPLE_2014052701)));
|
||||
|
||||
yield provider.collectDailyData();
|
||||
|
||||
let m = provider.getMeasurement("update", 1);
|
||||
let data = yield m.getValues();
|
||||
let s = data.singular;
|
||||
Assert.equal(s.size, 7);
|
||||
Assert.equal(s.get("v20140527.upgradedFrom")[1], "13.0.1");
|
||||
Assert.equal(s.get("v20140527.uninstallReason")[1], "SUCCESSFUL_UPGRADE");
|
||||
Assert.equal(s.get("v20140527.downloadAttempts")[1], 1);
|
||||
Assert.equal(s.get("v20140527.downloadFailures")[1], 0);
|
||||
Assert.equal(s.get("v20140527.installAttempts")[1], 1);
|
||||
Assert.equal(s.get("v20140527.installFailures")[1], 0);
|
||||
Assert.equal(s.get("v20140527.notificationsShown")[1], 0);
|
||||
|
||||
// Ensure the dynamic fields get serialized.
|
||||
let serializer = m.serializer(m.SERIALIZE_JSON);
|
||||
let d = serializer.singular(s);
|
||||
|
||||
Assert.deepEqual(d, {
|
||||
"_v": 1,
|
||||
"v20140527.upgradedFrom": "13.0.1",
|
||||
"v20140527.uninstallReason": "SUCCESSFUL_UPGRADE",
|
||||
"v20140527.downloadAttempts": 1,
|
||||
"v20140527.downloadFailures": 0,
|
||||
"v20140527.installAttempts": 1,
|
||||
"v20140527.installFailures": 0,
|
||||
"v20140527.notificationsShown": 0,
|
||||
});
|
||||
|
||||
// Don't interfere with next test.
|
||||
yield OS.File.remove(path);
|
||||
|
||||
yield storage.close();
|
||||
});
|
||||
|
||||
add_task(function* test_collect_multiple_versions() {
|
||||
let storage = yield Metrics.Storage("collect_multiple_versions");
|
||||
let provider = new HotfixProvider();
|
||||
yield provider.init(storage);
|
||||
|
||||
let p1 = {
|
||||
upgradedFrom: "12.0",
|
||||
uninstallReason: "SUCCESSFUL_UPGRADE",
|
||||
downloadAttempts: 3,
|
||||
downloadFailures: 1,
|
||||
installAttempts: 1,
|
||||
installFailures: 1,
|
||||
notificationsShown: 2,
|
||||
};
|
||||
|
||||
let p2 = {
|
||||
downloadAttempts: 5,
|
||||
downloadFailures: 3,
|
||||
installAttempts: 2,
|
||||
installFailures: 2,
|
||||
uninstallReason: null,
|
||||
notificationsShown: 1,
|
||||
};
|
||||
|
||||
let path1 = OS.Path.join(OS.Constants.Path.profileDir, "updateHotfix.v20140601.json");
|
||||
let path2 = OS.Path.join(OS.Constants.Path.profileDir, "updateHotfix.v20140701.json");
|
||||
|
||||
let encoder = new TextEncoder();
|
||||
yield OS.File.writeAtomic(path1, encoder.encode(JSON.stringify(p1)));
|
||||
yield OS.File.writeAtomic(path2, encoder.encode(JSON.stringify(p2)));
|
||||
|
||||
yield provider.collectDailyData();
|
||||
|
||||
let m = provider.getMeasurement("update", 1);
|
||||
let data = yield m.getValues();
|
||||
|
||||
let serializer = m.serializer(m.SERIALIZE_JSON);
|
||||
let d = serializer.singular(data.singular);
|
||||
|
||||
Assert.deepEqual(d, {
|
||||
"_v": 1,
|
||||
"v20140601.upgradedFrom": "12.0",
|
||||
"v20140601.uninstallReason": "SUCCESSFUL_UPGRADE",
|
||||
"v20140601.downloadAttempts": 3,
|
||||
"v20140601.downloadFailures": 1,
|
||||
"v20140601.installAttempts": 1,
|
||||
"v20140601.installFailures": 1,
|
||||
"v20140601.notificationsShown": 2,
|
||||
"v20140701.uninstallReason": "STILL_INSTALLED",
|
||||
"v20140701.downloadAttempts": 5,
|
||||
"v20140701.downloadFailures": 3,
|
||||
"v20140701.installAttempts": 2,
|
||||
"v20140701.installFailures": 2,
|
||||
"v20140701.notificationsShown": 1,
|
||||
});
|
||||
|
||||
// Don't interfere with next test.
|
||||
yield OS.File.remove(path1);
|
||||
yield OS.File.remove(path2);
|
||||
|
||||
yield storage.close();
|
||||
});
|
@ -10,6 +10,7 @@ skip-if = buildapp == 'mulet'
|
||||
[test_provider_appinfo.js]
|
||||
[test_provider_crashes.js]
|
||||
run-if = crashreporter
|
||||
[test_provider_hotfix.js]
|
||||
[test_provider_places.js]
|
||||
[test_provider_searches.js]
|
||||
[test_provider_sysinfo.js]
|
||||
|
@ -54,10 +54,13 @@ this.MobileIdentityClient.prototype = {
|
||||
return this._request(REGISTER, "POST", null, {});
|
||||
},
|
||||
|
||||
smsMtVerify: function(aSessionToken, aMsisdn, aWantShortCode = false) {
|
||||
smsMtVerify: function(aSessionToken, aMsisdn, aMcc, aMnc,
|
||||
aWantShortCode = false) {
|
||||
let credentials = this._deriveHawkCredentials(aSessionToken);
|
||||
return this._request(SMS_MT_VERIFY, "POST", credentials, {
|
||||
msisdn: aMsisdn,
|
||||
mcc: aMcc,
|
||||
mnc: aMnc,
|
||||
shortVerificationCode: aWantShortCode
|
||||
});
|
||||
},
|
||||
|
@ -450,30 +450,31 @@ this.MobileIdentityManager = {
|
||||
aToVerify.msisdn &&
|
||||
aToVerify.verificationDetails &&
|
||||
aToVerify.verificationDetails.mtSender) {
|
||||
this.activeVerificationFlow = new MobileIdentitySmsMtVerificationFlow(
|
||||
aOrigin,
|
||||
aToVerify.msisdn,
|
||||
aToVerify.iccId,
|
||||
aToVerify.serviceId === undefined, // external: the phone number does
|
||||
// not seem to belong to any of the
|
||||
// device SIM cards.
|
||||
aToVerify.verificationDetails.mtSender,
|
||||
this.activeVerificationFlow = new MobileIdentitySmsMtVerificationFlow({
|
||||
origin: aOrigin,
|
||||
msisdn: aToVerify.msisdn,
|
||||
mcc: aToVerify.mcc,
|
||||
mnc: aToVerify.mnc,
|
||||
iccId: aToVerify.iccId,
|
||||
external: aToVerify.serviceId === undefined,
|
||||
mtSender: aToVerify.verificationDetails.mtSender
|
||||
},
|
||||
this.ui,
|
||||
this.client
|
||||
);
|
||||
#ifdef MOZ_B2G_RIL
|
||||
} else if (aToVerify.verificationMethod.indexOf(SMS_MO_MT) != -1 &&
|
||||
aToVerify.serviceId &&
|
||||
aToVerify.verificationDetails &&
|
||||
aToVerify.verificationDetails.moVerifier &&
|
||||
aToVerify.verificationDetails.mtSender) {
|
||||
|
||||
this.activeVerificationFlow = new MobileIdentitySmsMoMtVerificationFlow(
|
||||
aOrigin,
|
||||
aToVerify.serviceId,
|
||||
aToVerify.iccId,
|
||||
aToVerify.verificationDetails.mtSender,
|
||||
aToVerify.verificationDetails.moVerifier,
|
||||
aToVerify.serviceId &&
|
||||
aToVerify.verificationDetails &&
|
||||
aToVerify.verificationDetails.moVerifier &&
|
||||
aToVerify.verificationDetails.mtSender) {
|
||||
this.activeVerificationFlow = new MobileIdentitySmsMoMtVerificationFlow({
|
||||
origin: aOrigin,
|
||||
serviceId: aToVerify.serviceId,
|
||||
iccId: aToVerify.iccId,
|
||||
mtSender: aToVerify.verificationDetails.mtSender,
|
||||
moVerifier: aToVerify.verificationDetails.moVerifier
|
||||
},
|
||||
this.ui,
|
||||
this.client
|
||||
);
|
||||
@ -517,6 +518,8 @@ this.MobileIdentityManager = {
|
||||
toVerify.serviceId = serviceId;
|
||||
toVerify.iccId = this.iccInfo[serviceId].iccId;
|
||||
toVerify.msisdn = this.iccInfo[serviceId].msisdn;
|
||||
toVerify.mcc = this.iccInfo[serviceId].mcc;
|
||||
toVerify.mnc = this.iccInfo[serviceId].mnc;
|
||||
toVerify.verificationMethod =
|
||||
this.iccInfo[serviceId].verificationMethods[0];
|
||||
toVerify.verificationDetails =
|
||||
@ -524,6 +527,7 @@ this.MobileIdentityManager = {
|
||||
return this._verificationFlow(toVerify, aOrigin);
|
||||
} else {
|
||||
toVerify.msisdn = aUserSelection.msisdn;
|
||||
toVerify.mcc = aUserSelection.mcc;
|
||||
return this.client.discover(aUserSelection.msisdn,
|
||||
aUserSelection.mcc)
|
||||
.then(
|
||||
|
@ -39,24 +39,14 @@ SilentSmsRequest.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
this.MobileIdentitySmsMoMtVerificationFlow = function(aOrigin,
|
||||
aServiceId,
|
||||
aIccId,
|
||||
aMtSender,
|
||||
aMoVerifier,
|
||||
this.MobileIdentitySmsMoMtVerificationFlow = function(aVerificationOptions,
|
||||
aUI,
|
||||
aClient) {
|
||||
|
||||
log.debug("MobileIdentitySmsMoMtVerificationFlow");
|
||||
log.debug("MobileIdentitySmsMoMtVerificationFlow ${}", aVerificationOptions);
|
||||
|
||||
MobileIdentitySmsVerificationFlow.call(this,
|
||||
aOrigin,
|
||||
null, //msisdn
|
||||
aIccId,
|
||||
aServiceId,
|
||||
false, // external
|
||||
aMtSender,
|
||||
aMoVerifier,
|
||||
aVerificationOptions,
|
||||
aUI,
|
||||
aClient,
|
||||
this.smsVerifyStrategy);
|
||||
|
@ -13,25 +13,14 @@ Cu.import("resource://gre/modules/MobileIdentitySmsVerificationFlow.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
this.MobileIdentitySmsMtVerificationFlow = function(aOrigin,
|
||||
aMsisdn,
|
||||
aIccId,
|
||||
aExternal,
|
||||
aMtSender,
|
||||
this.MobileIdentitySmsMtVerificationFlow = function(aVerificationOptions,
|
||||
aUI,
|
||||
aClient) {
|
||||
|
||||
log.debug("MobileIdentitySmsVerificationFlow " + aMsisdn + ", external: " +
|
||||
aExternal);
|
||||
log.debug("MobileIdentitySmsVerificationFlow ${}", aVerificationOptions);
|
||||
|
||||
MobileIdentitySmsVerificationFlow.call(this,
|
||||
aOrigin,
|
||||
aMsisdn,
|
||||
aIccId,
|
||||
null, // service ID
|
||||
aExternal,
|
||||
aMtSender,
|
||||
null, // moVerifier
|
||||
aVerificationOptions,
|
||||
aUI,
|
||||
aClient,
|
||||
this.smsVerifyStrategy);
|
||||
@ -44,6 +33,8 @@ this.MobileIdentitySmsMtVerificationFlow.prototype = {
|
||||
smsVerifyStrategy: function() {
|
||||
return this.client.smsMtVerify(this.sessionToken,
|
||||
this.verificationOptions.msisdn,
|
||||
this.verificationOptions.mcc,
|
||||
this.verificationOptions.mnc,
|
||||
this.verificationOptions.external);
|
||||
}
|
||||
};
|
||||
|
@ -19,13 +19,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "smsService",
|
||||
"nsISmsService");
|
||||
#endif
|
||||
|
||||
this.MobileIdentitySmsVerificationFlow = function(aOrigin,
|
||||
aMsisdn,
|
||||
aIccId,
|
||||
aServiceId,
|
||||
aExternal,
|
||||
aMtSender,
|
||||
aMoVerifier,
|
||||
this.MobileIdentitySmsVerificationFlow = function(aVerificationOptions,
|
||||
aUI,
|
||||
aClient,
|
||||
aVerifyStrategy) {
|
||||
@ -33,15 +27,9 @@ this.MobileIdentitySmsVerificationFlow = function(aOrigin,
|
||||
// SMS MT or SMS MO+MT specific verify strategy.
|
||||
this.smsVerifyStrategy = aVerifyStrategy;
|
||||
|
||||
MobileIdentityVerificationFlow.call(this, {
|
||||
origin: aOrigin,
|
||||
msisdn: aMsisdn,
|
||||
iccId: aIccId,
|
||||
serviceId: aServiceId,
|
||||
external: aExternal,
|
||||
mtSender: aMtSender,
|
||||
moVerifier: aMoVerifier
|
||||
}, aUI, aClient, this._verifyStrategy, this._cleanupStrategy);
|
||||
log.debug("aVerificationOptions ${}", aVerificationOptions);
|
||||
MobileIdentityVerificationFlow.call(this, aVerificationOptions, aUI, aClient,
|
||||
this._verifyStrategy, this._cleanupStrategy);
|
||||
};
|
||||
|
||||
this.MobileIdentitySmsVerificationFlow.prototype = {
|
||||
|
@ -138,7 +138,8 @@ MockUi.prototype = {
|
||||
__proto__: Mock.prototype,
|
||||
|
||||
_startFlowResult: {
|
||||
phoneNumber: PHONE_NUMBER
|
||||
phoneNumber: PHONE_NUMBER,
|
||||
mcc: MNC
|
||||
},
|
||||
|
||||
_verifyCodePromptResult: {
|
||||
@ -434,7 +435,9 @@ add_test(function() {
|
||||
client._("smsMtVerify").callsLength(1);
|
||||
client._("smsMtVerify").call(1).arg(1, SESSION_TOKEN);
|
||||
client._("smsMtVerify").call(1).arg(2, PHONE_NUMBER);
|
||||
client._("smsMtVerify").call(1).arg(3, true);
|
||||
client._("smsMtVerify").call(1).arg(3, MNC);
|
||||
client._("smsMtVerify").call(1).arg(4, undefined);
|
||||
client._("smsMtVerify").call(1).arg(5, true);
|
||||
client._("verifyCode").callsLength(1);
|
||||
client._("verifyCode").call(1).arg(1, SESSION_TOKEN);
|
||||
client._("verifyCode").call(1).arg(2, {
|
||||
@ -670,7 +673,9 @@ add_test(function() {
|
||||
client._("smsMtVerify").callsLength(1);
|
||||
client._("smsMtVerify").call(1).arg(1, _sessionToken);
|
||||
client._("smsMtVerify").call(1).arg(2, PHONE_NUMBER);
|
||||
client._("smsMtVerify").call(1).arg(3, true);
|
||||
client._("smsMtVerify").call(1).arg(3, MNC);
|
||||
client._("smsMtVerify").call(1).arg(4, undefined);
|
||||
client._("smsMtVerify").call(1).arg(5, true);
|
||||
client._("verifyCode").callsLength(1);
|
||||
client._("verifyCode").call(1).arg(1, _sessionToken);
|
||||
client._("verifyCode").call(1).arg(2, {
|
||||
|
@ -112,7 +112,7 @@ EventEmitter.prototype = {
|
||||
|
||||
/**
|
||||
* Emit an event. All arguments to this method will
|
||||
* be sent to listner functions.
|
||||
* be sent to listener functions.
|
||||
*/
|
||||
emit: function EventEmitter_emit(aEvent) {
|
||||
this.logEvent(aEvent, arguments);
|
||||
|
@ -8,3 +8,4 @@ libs::
|
||||
$(INSTALL) $(IFLAGS1) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/devtools
|
||||
$(INSTALL) $(IFLAGS1) $(srcdir)/*.js $(FINAL_TARGET)/modules/devtools/server
|
||||
$(INSTALL) $(IFLAGS1) $(srcdir)/actors/*.js $(FINAL_TARGET)/modules/devtools/server/actors
|
||||
$(INSTALL) $(IFLAGS1) $(srcdir)/actors/utils/*.js $(FINAL_TARGET)/modules/devtools/server/actors/utils
|
||||
|
@ -119,15 +119,6 @@ loader.lazyGetter(this, "DOMParser", function() {
|
||||
return Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
|
||||
});
|
||||
|
||||
loader.lazyGetter(this, "Debugger", function() {
|
||||
let JsDebugger = require("resource://gre/modules/jsdebugger.jsm");
|
||||
|
||||
let global = Cu.getGlobalForObject({});
|
||||
JsDebugger.addDebuggerToGlobal(global);
|
||||
|
||||
return global.Debugger;
|
||||
});
|
||||
|
||||
loader.lazyGetter(this, "eventListenerService", function() {
|
||||
return Cc["@mozilla.org/eventlistenerservice;1"]
|
||||
.getService(Ci.nsIEventListenerService);
|
||||
@ -358,7 +349,8 @@ var NodeActor = exports.NodeActor = protocol.ActorClass({
|
||||
* }
|
||||
*/
|
||||
getEventListeners: function(node) {
|
||||
let dbg = new Debugger();
|
||||
let dbg = this.parent().tabActor.makeDebugger();
|
||||
|
||||
let handlers = eventListenerService.getListenerInfoFor(node);
|
||||
let events = [];
|
||||
|
||||
@ -371,7 +363,7 @@ var NodeActor = exports.NodeActor = protocol.ActorClass({
|
||||
}
|
||||
|
||||
let global = Cu.getGlobalForObject(listener);
|
||||
let globalDO = dbg.addDebuggee(global);
|
||||
let globalDO = dbg.makeGlobalObjectReference(global);
|
||||
let listenerDO = globalDO.makeDebuggeeValue(listener);
|
||||
|
||||
// If the listener is an object with a 'handleEvent' method, use that.
|
||||
@ -452,8 +444,6 @@ var NodeActor = exports.NodeActor = protocol.ActorClass({
|
||||
origin: origin,
|
||||
searchString: searchString
|
||||
});
|
||||
|
||||
dbg.removeDebuggee(globalDO);
|
||||
}
|
||||
return events;
|
||||
},
|
||||
|
@ -63,7 +63,7 @@ let MemoryActor = protocol.ActorClass({
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
let url = this.tabActor.url;
|
||||
console.error("Error getting size of "+url);
|
||||
console.error("Error getting size of " + url);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -11,6 +11,7 @@ const Services = require("Services");
|
||||
const { ActorPool, appendExtraActors, createExtraActors } = require("devtools/server/actors/common");
|
||||
const { DebuggerServer } = require("devtools/server/main");
|
||||
const { dumpProtocolSpec } = require("devtools/server/protocol");
|
||||
const makeDebugger = require("./utils/make-debugger");
|
||||
|
||||
/* Root actor for the remote debugging protocol. */
|
||||
|
||||
@ -92,6 +93,12 @@ function RootActor(aConnection, aParameters) {
|
||||
this._onTabListChanged = this.onTabListChanged.bind(this);
|
||||
this._onAddonListChanged = this.onAddonListChanged.bind(this);
|
||||
this._extraActors = {};
|
||||
|
||||
// This creates a Debugger instance for chrome debugging all globals.
|
||||
this.makeDebugger = makeDebugger.bind(null, {
|
||||
findDebuggees: dbg => dbg.findAllGlobals(),
|
||||
shouldAddNewGlobalAsDebuggee: () => true
|
||||
});
|
||||
}
|
||||
|
||||
RootActor.prototype = {
|
||||
|
@ -16,6 +16,7 @@ const { SourceMapConsumer, SourceMapGenerator } = require("source-map");
|
||||
const promise = require("promise");
|
||||
const Debugger = require("Debugger");
|
||||
const xpcInspector = require("xpcInspector");
|
||||
const mapURIToAddonID = require("./utils/map-uri-to-addon-id");
|
||||
|
||||
const { defer, resolve, reject, all } = require("devtools/toolkit/deprecated-sync-thenables");
|
||||
const { CssLogic } = require("devtools/styleinspector/css-logic");
|
||||
@ -24,8 +25,6 @@ DevToolsUtils.defineLazyGetter(this, "NetUtil", () => {
|
||||
return Cu.import("resource://gre/modules/NetUtil.jsm", {}).NetUtil;
|
||||
});
|
||||
|
||||
let B2G_ID = "{3c2e2abc-06d4-11e1-ac3b-374f68613e61}";
|
||||
|
||||
let TYPED_ARRAY_CLASSES = ["Uint8Array", "Uint8ClampedArray", "Uint16Array",
|
||||
"Uint32Array", "Int8Array", "Int16Array", "Int32Array", "Float32Array",
|
||||
"Float64Array"];
|
||||
@ -34,34 +33,6 @@ let TYPED_ARRAY_CLASSES = ["Uint8Array", "Uint8ClampedArray", "Uint16Array",
|
||||
// collections, etc.
|
||||
let OBJECT_PREVIEW_MAX_ITEMS = 10;
|
||||
|
||||
let addonManager = null;
|
||||
|
||||
/**
|
||||
* This is a wrapper around amIAddonManager.mapURIToAddonID which always returns
|
||||
* false on B2G to avoid loading the add-on manager there and reports any
|
||||
* exceptions rather than throwing so that the caller doesn't have to worry
|
||||
* about them.
|
||||
*/
|
||||
function mapURIToAddonID(uri, id) {
|
||||
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT ||
|
||||
(Services.appinfo.ID || undefined) == B2G_ID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!addonManager) {
|
||||
addonManager = Cc["@mozilla.org/addons/integration;1"].
|
||||
getService(Ci.amIAddonManager);
|
||||
}
|
||||
|
||||
try {
|
||||
return addonManager.mapURIToAddonID(uri, id);
|
||||
}
|
||||
catch (e) {
|
||||
DevToolsUtils.reportException("mapURIToAddonID", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* BreakpointStore objects keep track of all breakpoints that get set so that we
|
||||
* can reset them when the same script is introduced to the thread again (such
|
||||
@ -481,41 +452,53 @@ EventLoop.prototype = {
|
||||
/**
|
||||
* JSD2 actors.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a ThreadActor.
|
||||
*
|
||||
* ThreadActors manage a JSInspector object and manage execution/inspection
|
||||
* of debuggees.
|
||||
*
|
||||
* @param aHooks object
|
||||
* An object with preNest and postNest methods for calling when entering
|
||||
* and exiting a nested event loop.
|
||||
* @param aParent object
|
||||
* This |ThreadActor|'s parent actor. It must implement the following
|
||||
* properties:
|
||||
* - url: The URL string of the debuggee.
|
||||
* - window: The global window object.
|
||||
* - preNest: Function called before entering a nested event loop.
|
||||
* - postNest: Function called after exiting a nested event loop.
|
||||
* - makeDebugger: A function that takes no arguments and instantiates
|
||||
* a Debugger that manages its globals on its own.
|
||||
* @param aGlobal object [optional]
|
||||
* An optional (for content debugging only) reference to the content
|
||||
* window.
|
||||
*/
|
||||
function ThreadActor(aHooks, aGlobal)
|
||||
function ThreadActor(aParent, aGlobal)
|
||||
{
|
||||
this._state = "detached";
|
||||
this._frameActors = [];
|
||||
this._hooks = aHooks;
|
||||
this.global = aGlobal;
|
||||
// A map of actorID -> actor for breakpoints created and managed by the server.
|
||||
this._hiddenBreakpoints = new Map();
|
||||
|
||||
this.findGlobals = this.globalManager.findGlobals.bind(this);
|
||||
this.onNewGlobal = this.globalManager.onNewGlobal.bind(this);
|
||||
this.onNewSource = this.onNewSource.bind(this);
|
||||
this._allEventsListener = this._allEventsListener.bind(this);
|
||||
this._parent = aParent;
|
||||
this._dbg = null;
|
||||
this._gripDepth = 0;
|
||||
this._threadLifetimePool = null;
|
||||
this._tabClosed = false;
|
||||
|
||||
this._options = {
|
||||
useSourceMaps: false,
|
||||
autoBlackBox: false
|
||||
};
|
||||
|
||||
this._gripDepth = 0;
|
||||
this._threadLifetimePool = null;
|
||||
this._tabClosed = false;
|
||||
// A map of actorID -> actor for breakpoints created and managed by the
|
||||
// server.
|
||||
this._hiddenBreakpoints = new Map();
|
||||
|
||||
this.global = aGlobal;
|
||||
|
||||
this._allEventsListener = this._allEventsListener.bind(this);
|
||||
this.onNewGlobal = this.onNewGlobal.bind(this);
|
||||
this.onNewSource = this.onNewSource.bind(this);
|
||||
this.uncaughtExceptionHook = this.uncaughtExceptionHook.bind(this);
|
||||
this.onDebuggerStatement = this.onDebuggerStatement.bind(this);
|
||||
this.onNewScript = this.onNewScript.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -530,6 +513,23 @@ ThreadActor.prototype = {
|
||||
|
||||
actorPrefix: "context",
|
||||
|
||||
get dbg() {
|
||||
if (!this._dbg) {
|
||||
this._dbg = this._parent.makeDebugger();
|
||||
this._dbg.uncaughtExceptionHook = this.uncaughtExceptionHook;
|
||||
this._dbg.onDebuggerStatement = this.onDebuggerStatement;
|
||||
this._dbg.onNewScript = this.onNewScript;
|
||||
this._dbg.on("newGlobal", this.onNewGlobal);
|
||||
// Keep the debugger disabled until a client attaches.
|
||||
this._dbg.enabled = this._state != "detached";
|
||||
}
|
||||
return this._dbg;
|
||||
},
|
||||
|
||||
get globalDebugObject() {
|
||||
return this.dbg.makeGlobalObjectReference(this._parent.window);
|
||||
},
|
||||
|
||||
get state() { return this._state; },
|
||||
get attached() this.state == "attached" ||
|
||||
this.state == "running" ||
|
||||
@ -618,126 +618,23 @@ ThreadActor.prototype = {
|
||||
* Remove all debuggees and clear out the thread's sources.
|
||||
*/
|
||||
clearDebuggees: function () {
|
||||
if (this.dbg) {
|
||||
if (this._dbg) {
|
||||
this.dbg.removeAllDebuggees();
|
||||
}
|
||||
this._sources = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a debuggee global to the Debugger object.
|
||||
*
|
||||
* @returns the Debugger.Object that corresponds to the global.
|
||||
* Listener for our |Debugger|'s "newGlobal" event.
|
||||
*/
|
||||
addDebuggee: function (aGlobal) {
|
||||
let globalDebugObject;
|
||||
try {
|
||||
globalDebugObject = this.dbg.addDebuggee(aGlobal);
|
||||
} catch (e) {
|
||||
// Ignore attempts to add the debugger's compartment as a debuggee.
|
||||
dumpn("Ignoring request to add the debugger's compartment as a debuggee");
|
||||
}
|
||||
return globalDebugObject;
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize the Debugger.
|
||||
*/
|
||||
_initDebugger: function () {
|
||||
this.dbg = new Debugger();
|
||||
this.dbg.uncaughtExceptionHook = this.uncaughtExceptionHook.bind(this);
|
||||
this.dbg.onDebuggerStatement = this.onDebuggerStatement.bind(this);
|
||||
this.dbg.onNewScript = this.onNewScript.bind(this);
|
||||
this.dbg.onNewGlobalObject = this.globalManager.onNewGlobal.bind(this);
|
||||
// Keep the debugger disabled until a client attaches.
|
||||
this.dbg.enabled = this._state != "detached";
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a debuggee global from the JSInspector.
|
||||
*/
|
||||
removeDebugee: function (aGlobal) {
|
||||
try {
|
||||
this.dbg.removeDebuggee(aGlobal);
|
||||
} catch(ex) {
|
||||
// XXX: This debuggee has code currently executing on the stack,
|
||||
// we need to save this for later.
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Add the provided window and all windows in its frame tree as debuggees.
|
||||
*
|
||||
* @returns the Debugger.Object that corresponds to the window.
|
||||
*/
|
||||
_addDebuggees: function (aWindow) {
|
||||
let globalDebugObject = this.addDebuggee(aWindow);
|
||||
let frames = aWindow.frames;
|
||||
if (frames) {
|
||||
for (let i = 0; i < frames.length; i++) {
|
||||
this._addDebuggees(frames[i]);
|
||||
}
|
||||
}
|
||||
return globalDebugObject;
|
||||
},
|
||||
|
||||
/**
|
||||
* An object that will be used by ThreadActors to tailor their behavior
|
||||
* depending on the debugging context being required (chrome or content).
|
||||
*/
|
||||
globalManager: {
|
||||
findGlobals: function () {
|
||||
const { getContentGlobals } = require("devtools/server/content-globals");
|
||||
|
||||
this.globalDebugObject = this._addDebuggees(this.global);
|
||||
|
||||
// global may not be a window
|
||||
try {
|
||||
getContentGlobals({
|
||||
'inner-window-id': getInnerId(this.global)
|
||||
}).forEach(this.addDebuggee.bind(this));
|
||||
}
|
||||
catch(e) {}
|
||||
},
|
||||
|
||||
/**
|
||||
* A function that the engine calls when a new global object
|
||||
* (for example a sandbox) has been created.
|
||||
*
|
||||
* @param aGlobal Debugger.Object
|
||||
* The new global object that was created.
|
||||
*/
|
||||
onNewGlobal: function (aGlobal) {
|
||||
let useGlobal = (aGlobal.hostAnnotations &&
|
||||
aGlobal.hostAnnotations.type == "document" &&
|
||||
aGlobal.hostAnnotations.element === this.global);
|
||||
|
||||
// check if the global is a sdk page-mod sandbox
|
||||
if (!useGlobal) {
|
||||
let metadata = {};
|
||||
let id = "";
|
||||
try {
|
||||
id = getInnerId(this.global);
|
||||
metadata = Cu.getSandboxMetadata(aGlobal.unsafeDereference());
|
||||
}
|
||||
catch (e) {}
|
||||
|
||||
useGlobal = (metadata['inner-window-id'] && metadata['inner-window-id'] == id);
|
||||
}
|
||||
|
||||
// Content debugging only cares about new globals in the contant window,
|
||||
// like iframe children.
|
||||
if (useGlobal) {
|
||||
this.addDebuggee(aGlobal);
|
||||
// Notify the client.
|
||||
this.conn.send({
|
||||
from: this.actorID,
|
||||
type: "newGlobal",
|
||||
// TODO: after bug 801084 lands see if we need to JSONify this.
|
||||
hostAnnotations: aGlobal.hostAnnotations
|
||||
});
|
||||
}
|
||||
}
|
||||
onNewGlobal: function (aGlobal) {
|
||||
// Notify the client.
|
||||
this.conn.send({
|
||||
from: this.actorID,
|
||||
type: "newGlobal",
|
||||
// TODO: after bug 801084 lands see if we need to JSONify this.
|
||||
hostAnnotations: aGlobal.hostAnnotations
|
||||
});
|
||||
},
|
||||
|
||||
disconnect: function () {
|
||||
@ -759,11 +656,11 @@ ThreadActor.prototype = {
|
||||
this._prettyPrintWorker = null;
|
||||
}
|
||||
|
||||
if (!this.dbg) {
|
||||
if (!this._dbg) {
|
||||
return;
|
||||
}
|
||||
this.dbg.enabled = false;
|
||||
this.dbg = null;
|
||||
this._dbg.enabled = false;
|
||||
this._dbg = null;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -792,15 +689,12 @@ ThreadActor.prototype = {
|
||||
// Initialize an event loop stack. This can't be done in the constructor,
|
||||
// because this.conn is not yet initialized by the actor pool at that time.
|
||||
this._nestedEventLoops = new EventLoopStack({
|
||||
hooks: this._hooks,
|
||||
hooks: this._parent,
|
||||
connection: this.conn,
|
||||
thread: this
|
||||
});
|
||||
|
||||
if (!this.dbg) {
|
||||
this._initDebugger();
|
||||
}
|
||||
this.findGlobals();
|
||||
this.dbg.addDebuggees();
|
||||
this.dbg.enabled = true;
|
||||
try {
|
||||
// Put ourselves in the paused state.
|
||||
@ -1132,8 +1026,8 @@ ThreadActor.prototype = {
|
||||
// different tabs or multiple debugger clients connected to the same tab)
|
||||
// only allow resumption in a LIFO order.
|
||||
if (this._nestedEventLoops.size && this._nestedEventLoops.lastPausedUrl
|
||||
&& (this._nestedEventLoops.lastPausedUrl !== this._hooks.url
|
||||
|| this._nestedEventLoops.lastConnection !== this.conn)) {
|
||||
&& (this._nestedEventLoops.lastPausedUrl !== this._parent.url
|
||||
|| this._nestedEventLoops.lastConnection !== this.conn)) {
|
||||
return {
|
||||
error: "wrongOrder",
|
||||
message: "trying to resume in the wrong order.",
|
||||
@ -1925,6 +1819,7 @@ ThreadActor.prototype = {
|
||||
aFrame.onStep = undefined;
|
||||
aFrame.onPop = undefined;
|
||||
}
|
||||
|
||||
// Clear DOM event breakpoints.
|
||||
// XPCShell tests don't use actual DOM windows for globals and cause
|
||||
// removeListenerForAllEvents to throw.
|
||||
@ -4836,13 +4731,13 @@ Object.defineProperty(Debugger.Frame.prototype, "line", {
|
||||
* is associated. (Currently unused, but required to make this
|
||||
* constructor usable with addGlobalActor.)
|
||||
*
|
||||
* @param aHooks object
|
||||
* An object with preNest and postNest methods for calling when entering
|
||||
* and exiting a nested event loop.
|
||||
* @param aParent object
|
||||
* This actor's parent actor. See ThreadActor for a list of expected
|
||||
* properties.
|
||||
*/
|
||||
function ChromeDebuggerActor(aConnection, aHooks)
|
||||
function ChromeDebuggerActor(aConnection, aParent)
|
||||
{
|
||||
ThreadActor.call(this, aHooks);
|
||||
ThreadActor.call(this, aParent);
|
||||
}
|
||||
|
||||
ChromeDebuggerActor.prototype = Object.create(ThreadActor.prototype);
|
||||
@ -4857,38 +4752,7 @@ update(ChromeDebuggerActor.prototype, {
|
||||
* Override the eligibility check for scripts and sources to make sure every
|
||||
* script and source with a URL is stored when debugging chrome.
|
||||
*/
|
||||
_allowSource: function(aSourceURL) !!aSourceURL,
|
||||
|
||||
/**
|
||||
* An object that will be used by ThreadActors to tailor their behavior
|
||||
* depending on the debugging context being required (chrome or content).
|
||||
* The methods that this object provides must be bound to the ThreadActor
|
||||
* before use.
|
||||
*/
|
||||
globalManager: {
|
||||
findGlobals: function () {
|
||||
// Add every global known to the debugger as debuggee.
|
||||
this.dbg.addAllGlobalsAsDebuggees();
|
||||
},
|
||||
|
||||
/**
|
||||
* A function that the engine calls when a new global object has been
|
||||
* created.
|
||||
*
|
||||
* @param aGlobal Debugger.Object
|
||||
* The new global object that was created.
|
||||
*/
|
||||
onNewGlobal: function (aGlobal) {
|
||||
this.addDebuggee(aGlobal);
|
||||
// Notify the client.
|
||||
this.conn.send({
|
||||
from: this.actorID,
|
||||
type: "newGlobal",
|
||||
// TODO: after bug 801084 lands see if we need to JSONify this.
|
||||
hostAnnotations: aGlobal.hostAnnotations
|
||||
});
|
||||
}
|
||||
}
|
||||
_allowSource: aSourceURL => !!aSourceURL
|
||||
});
|
||||
|
||||
exports.ChromeDebuggerActor = ChromeDebuggerActor;
|
||||
@ -4902,18 +4766,12 @@ exports.ChromeDebuggerActor = ChromeDebuggerActor;
|
||||
* is associated. (Currently unused, but required to make this
|
||||
* constructor usable with addGlobalActor.)
|
||||
*
|
||||
* @param aHooks object
|
||||
* An object with preNest and postNest methods for calling
|
||||
* when entering and exiting a nested event loops.
|
||||
*
|
||||
* @param aAddonID string
|
||||
* ID of the add-on this actor will debug. It will be used to
|
||||
* filter out globals marked for debugging.
|
||||
* @param aParent object
|
||||
* This actor's parent actor. See ThreadActor for a list of expected
|
||||
* properties.
|
||||
*/
|
||||
|
||||
function AddonThreadActor(aConnect, aHooks, aAddonID) {
|
||||
this.addonID = aAddonID;
|
||||
ThreadActor.call(this, aHooks);
|
||||
function AddonThreadActor(aConnect, aParent) {
|
||||
ThreadActor.call(this, aParent);
|
||||
}
|
||||
|
||||
AddonThreadActor.prototype = Object.create(ThreadActor.prototype);
|
||||
@ -4935,7 +4793,7 @@ update(AddonThreadActor.prototype, {
|
||||
return false;
|
||||
}
|
||||
|
||||
// XPIProvider.jsm evals some code in every add-on's bootstrap.js. Hide it
|
||||
// XPIProvider.jsm evals some code in every add-on's bootstrap.js. Hide it.
|
||||
if (aSourceURL == "resource://gre/modules/addons/XPIProvider.jsm") {
|
||||
return false;
|
||||
}
|
||||
@ -4943,97 +4801,6 @@ update(AddonThreadActor.prototype, {
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* An object that will be used by ThreadActors to tailor their
|
||||
* behaviour depending on the debugging context being required (chrome,
|
||||
* addon or content). The methods that this object provides must
|
||||
* be bound to the ThreadActor before use.
|
||||
*/
|
||||
globalManager: {
|
||||
findGlobals: function ADA_findGlobals() {
|
||||
for (let global of this.dbg.findAllGlobals()) {
|
||||
if (this._checkGlobal(global)) {
|
||||
this.dbg.addDebuggee(global);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* A function that the engine calls when a new global object
|
||||
* has been created.
|
||||
*
|
||||
* @param aGlobal Debugger.Object
|
||||
* The new global object that was created.
|
||||
*/
|
||||
onNewGlobal: function ADA_onNewGlobal(aGlobal) {
|
||||
if (this._checkGlobal(aGlobal)) {
|
||||
this.addDebuggee(aGlobal);
|
||||
// Notify the client.
|
||||
this.conn.send({
|
||||
from: this.actorID,
|
||||
type: "newGlobal",
|
||||
// TODO: after bug 801084 lands see if we need to JSONify this.
|
||||
hostAnnotations: aGlobal.hostAnnotations
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the provided global belongs to the debugged add-on.
|
||||
*
|
||||
* @param aGlobal Debugger.Object
|
||||
*/
|
||||
_checkGlobal: function ADA_checkGlobal(aGlobal) {
|
||||
let obj = null;
|
||||
try {
|
||||
obj = aGlobal.unsafeDereference();
|
||||
}
|
||||
catch (e) {
|
||||
// Because of bug 991399 we sometimes get bad objects here. If we can't
|
||||
// dereference them then they won't be useful to us
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// This will fail for non-Sandbox objects, hence the try-catch block.
|
||||
let metadata = Cu.getSandboxMetadata(obj);
|
||||
if (metadata) {
|
||||
return metadata.addonID === this.addonID;
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
if (obj instanceof Ci.nsIDOMWindow) {
|
||||
let id = {};
|
||||
if (mapURIToAddonID(obj.document.documentURIObject, id)) {
|
||||
return id.value === this.addonID;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the global for a __URI__ property and then try to map that to an
|
||||
// add-on
|
||||
let uridescriptor = aGlobal.getOwnPropertyDescriptor("__URI__");
|
||||
if (uridescriptor && "value" in uridescriptor && uridescriptor.value) {
|
||||
let uri;
|
||||
try {
|
||||
uri = Services.io.newURI(uridescriptor.value, null, null);
|
||||
}
|
||||
catch (e) {
|
||||
DevToolsUtils.reportException("AddonThreadActor.prototype._checkGlobal",
|
||||
new Error("Invalid URI: " + uridescriptor.value));
|
||||
return false;
|
||||
}
|
||||
|
||||
let id = {};
|
||||
if (mapURIToAddonID(uri, id)) {
|
||||
return id.value === this.addonID;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
exports.AddonThreadActor = AddonThreadActor;
|
||||
|
@ -67,15 +67,20 @@ const TRACE_TYPES = new Set([
|
||||
]);
|
||||
|
||||
/**
|
||||
* Creates a TraceActor. TraceActor provides a stream of function
|
||||
* Creates a TracerActor. TracerActor provides a stream of function
|
||||
* call/return packets to a remote client gathering a full trace.
|
||||
*/
|
||||
function TraceActor(aConn, aParentActor)
|
||||
function TracerActor(aConn, aParent)
|
||||
{
|
||||
this._dbg = null;
|
||||
this._parent = aParent;
|
||||
this._attached = false;
|
||||
this._activeTraces = new MapStack();
|
||||
this._totalTraces = 0;
|
||||
this._startTime = 0;
|
||||
this._sequence = 0;
|
||||
this._bufferSendTimer = null;
|
||||
this._buffer = [];
|
||||
|
||||
// Keep track of how many different trace requests have requested what kind of
|
||||
// tracing info. This way we can minimize the amount of data we are collecting
|
||||
@ -85,27 +90,25 @@ function TraceActor(aConn, aParentActor)
|
||||
this._requestsForTraceType[type] = 0;
|
||||
}
|
||||
|
||||
this._sequence = 0;
|
||||
this._bufferSendTimer = null;
|
||||
this._buffer = [];
|
||||
this.onEnterFrame = this.onEnterFrame.bind(this);
|
||||
this.onExitFrame = this.onExitFrame.bind(this);
|
||||
|
||||
// aParentActor.window might be an Xray for a window, but it might also be a
|
||||
// double-wrapper for a Sandbox. We want to unwrap the latter but not the
|
||||
// former.
|
||||
this.global = aParentActor.window;
|
||||
if (!Cu.isXrayWrapper(this.global)) {
|
||||
this.global = this.global.wrappedJSObject;
|
||||
}
|
||||
}
|
||||
|
||||
TraceActor.prototype = {
|
||||
TracerActor.prototype = {
|
||||
actorPrefix: "trace",
|
||||
|
||||
get attached() { return this._attached; },
|
||||
get idle() { return this._attached && this._activeTraces.size === 0; },
|
||||
get tracing() { return this._attached && this._activeTraces.size > 0; },
|
||||
|
||||
get dbg() {
|
||||
if (!this._dbg) {
|
||||
this._dbg = this._parent.makeDebugger();
|
||||
this._dbg.onEnterFrame = this.onEnterFrame;
|
||||
}
|
||||
return this._dbg;
|
||||
},
|
||||
|
||||
/**
|
||||
* Buffer traces and only send them every BUFFER_SEND_DELAY milliseconds.
|
||||
*/
|
||||
@ -123,74 +126,6 @@ TraceActor.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes a Debugger instance and adds listeners to it.
|
||||
*/
|
||||
_initDebugger: function() {
|
||||
this.dbg = new Debugger();
|
||||
this.dbg.onEnterFrame = this.onEnterFrame.bind(this);
|
||||
this.dbg.onNewGlobalObject = this.globalManager.onNewGlobal.bind(this);
|
||||
this.dbg.enabled = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a debuggee global to the Debugger object.
|
||||
*/
|
||||
_addDebuggee: function(aGlobal) {
|
||||
try {
|
||||
this.dbg.addDebuggee(aGlobal);
|
||||
} catch (e) {
|
||||
// Ignore attempts to add the debugger's compartment as a debuggee.
|
||||
DevToolsUtils.reportException("TraceActor",
|
||||
new Error("Ignoring request to add the debugger's "
|
||||
+ "compartment as a debuggee"));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Add the provided window and all windows in its frame tree as debuggees.
|
||||
*/
|
||||
_addDebuggees: function(aWindow) {
|
||||
this._addDebuggee(aWindow);
|
||||
let frames = aWindow.frames;
|
||||
if (frames) {
|
||||
for (let i = 0; i < frames.length; i++) {
|
||||
this._addDebuggees(frames[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* An object used by TraceActors to tailor their behavior depending
|
||||
* on the debugging context required (chrome or content).
|
||||
*/
|
||||
globalManager: {
|
||||
/**
|
||||
* Adds all globals in the global object as debuggees.
|
||||
*/
|
||||
findGlobals: function() {
|
||||
this._addDebuggees(this.global);
|
||||
},
|
||||
|
||||
/**
|
||||
* A function that the engine calls when a new global object has been
|
||||
* created. Adds the global object as a debuggee if it is in the content
|
||||
* window.
|
||||
*
|
||||
* @param aGlobal Debugger.Object
|
||||
* The new global object that was created.
|
||||
*/
|
||||
onNewGlobal: function(aGlobal) {
|
||||
// Content debugging only cares about new globals in the content
|
||||
// window, like iframe children.
|
||||
if (aGlobal.hostAnnotations &&
|
||||
aGlobal.hostAnnotations.type == "document" &&
|
||||
aGlobal.hostAnnotations.element === this.global) {
|
||||
this._addDebuggee(aGlobal);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a protocol request to attach to the trace actor.
|
||||
*
|
||||
@ -205,11 +140,7 @@ TraceActor.prototype = {
|
||||
};
|
||||
}
|
||||
|
||||
if (!this.dbg) {
|
||||
this._initDebugger();
|
||||
this.globalManager.findGlobals.call(this);
|
||||
}
|
||||
|
||||
this.dbg.addDebuggees();
|
||||
this._attached = true;
|
||||
|
||||
return {
|
||||
@ -230,10 +161,12 @@ TraceActor.prototype = {
|
||||
this.onStopTrace();
|
||||
}
|
||||
|
||||
this.dbg = null;
|
||||
|
||||
this._dbg = null;
|
||||
this._attached = false;
|
||||
return { type: "detached" };
|
||||
|
||||
return {
|
||||
type: "detached"
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
@ -307,7 +240,11 @@ TraceActor.prototype = {
|
||||
this.dbg.enabled = false;
|
||||
}
|
||||
|
||||
return { type: "stoppedTrace", why: "requested", name: name };
|
||||
return {
|
||||
type: "stoppedTrace",
|
||||
why: "requested",
|
||||
name
|
||||
};
|
||||
},
|
||||
|
||||
// JS Debugger API hooks.
|
||||
@ -443,19 +380,19 @@ TraceActor.prototype = {
|
||||
/**
|
||||
* The request types this actor can handle.
|
||||
*/
|
||||
TraceActor.prototype.requestTypes = {
|
||||
"attach": TraceActor.prototype.onAttach,
|
||||
"detach": TraceActor.prototype.onDetach,
|
||||
"startTrace": TraceActor.prototype.onStartTrace,
|
||||
"stopTrace": TraceActor.prototype.onStopTrace
|
||||
TracerActor.prototype.requestTypes = {
|
||||
"attach": TracerActor.prototype.onAttach,
|
||||
"detach": TracerActor.prototype.onDetach,
|
||||
"startTrace": TracerActor.prototype.onStartTrace,
|
||||
"stopTrace": TracerActor.prototype.onStopTrace
|
||||
};
|
||||
|
||||
exports.register = function(handle) {
|
||||
handle.addTabActor(TraceActor, "traceActor");
|
||||
handle.addTabActor(TracerActor, "traceActor");
|
||||
};
|
||||
|
||||
exports.unregister = function(handle) {
|
||||
handle.removeTabActor(TraceActor, "traceActor");
|
||||
handle.removeTabActor(TracerActor, "traceActor");
|
||||
};
|
||||
|
||||
|
||||
@ -610,7 +547,7 @@ function createValueSnapshot(aValue, aDetailed=false) {
|
||||
? detailedObjectSnapshot(aValue)
|
||||
: objectSnapshot(aValue);
|
||||
default:
|
||||
DevToolsUtils.reportException("TraceActor",
|
||||
DevToolsUtils.reportException("TracerActor",
|
||||
new Error("Failed to provide a grip for: " + aValue));
|
||||
return null;
|
||||
}
|
||||
|
100
toolkit/devtools/server/actors/utils/make-debugger.js
Normal file
100
toolkit/devtools/server/actors/utils/make-debugger.js
Normal file
@ -0,0 +1,100 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* 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 EventEmitter = require("devtools/toolkit/event-emitter");
|
||||
const Debugger = require("Debugger");
|
||||
|
||||
const { reportException } = require("devtools/toolkit/DevToolsUtils");
|
||||
|
||||
/**
|
||||
* Multiple actors that use a |Debugger| instance come in a few versions, each
|
||||
* with a different set of debuggees. One version for content tabs (globals
|
||||
* within a tab), one version for chrome debugging (all globals), and sometimes
|
||||
* a third version for addon debugging (chrome globals the addon is loaded in
|
||||
* and content globals the addon injects scripts into). The |makeDebugger|
|
||||
* function helps us avoid repeating the logic for finding and maintaining the
|
||||
* correct set of globals for a given |Debugger| instance across each version of
|
||||
* all of our actors.
|
||||
*
|
||||
* The |makeDebugger| function expects a single object parameter with the
|
||||
* following properties:
|
||||
*
|
||||
* @param Function findDebuggees
|
||||
* Called with one argument: a |Debugger| instance. This function should
|
||||
* return an iterable of globals to be added to the |Debugger|
|
||||
* instance. The globals may be wrapped in a |Debugger.Object|, or
|
||||
* unwrapped.
|
||||
*
|
||||
* @param Function shouldAddNewGlobalAsDebuggee
|
||||
* Called with one argument: a |Debugger.Object| wrapping a global
|
||||
* object. This function must return |true| if the global object should
|
||||
* be added as debuggee, and |false| otherwise.
|
||||
*
|
||||
* @returns Debugger
|
||||
* Returns a |Debugger| instance that can manage its set of debuggee
|
||||
* globals itself and is decorated with the |EventEmitter| class.
|
||||
*
|
||||
* Events emitted by the returned |Debugger| instance:
|
||||
*
|
||||
* - "newGlobal": Emitted when a new global has been added as a
|
||||
* debuggee. Passes the |Debugger.Object| wrapping the new
|
||||
* debuggee global to listeners.
|
||||
*
|
||||
* Existing |Debugger| properties set on the returned |Debugger|
|
||||
* instance:
|
||||
*
|
||||
* - onNewGlobalObject: The |Debugger| will automatically add new
|
||||
* globals as debuggees if calling |shouldAddNewGlobalAsDebuggee|
|
||||
* with the global returns true.
|
||||
*
|
||||
* - uncaughtExceptionHook: The |Debugger| already has an error
|
||||
* reporter attached to |uncaughtExceptionHook|, so if any
|
||||
* |Debugger| hooks fail, the error will be reported.
|
||||
*
|
||||
* New properties set on the returned |Debugger| instance:
|
||||
*
|
||||
* - addDebuggees: A function which takes no arguments. It adds all
|
||||
* current globals that should be debuggees (as determined by
|
||||
* |findDebuggees|) to the |Debugger| instance.
|
||||
*/
|
||||
module.exports = function makeDebugger({ findDebuggees, shouldAddNewGlobalAsDebuggee }) {
|
||||
const dbg = new Debugger();
|
||||
EventEmitter.decorate(dbg);
|
||||
|
||||
dbg.uncaughtExceptionHook = reportDebuggerHookException;
|
||||
|
||||
dbg.onNewGlobalObject = global => {
|
||||
if (shouldAddNewGlobalAsDebuggee(global)) {
|
||||
safeAddDebuggee(dbg, global);
|
||||
}
|
||||
};
|
||||
|
||||
dbg.addDebuggees = () => {
|
||||
for (let global of findDebuggees(dbg)) {
|
||||
safeAddDebuggee(dbg, global);
|
||||
}
|
||||
};
|
||||
|
||||
return dbg;
|
||||
};
|
||||
|
||||
const reportDebuggerHookException = e => reportException("Debugger Hook", e);
|
||||
|
||||
/**
|
||||
* Add |global| as a debuggee to |dbg|, handling error cases.
|
||||
*/
|
||||
function safeAddDebuggee(dbg, global) {
|
||||
try {
|
||||
let wrappedGlobal = dbg.addDebuggee(global);
|
||||
if (wrappedGlobal) {
|
||||
dbg.emit("newGlobal", wrappedGlobal);
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignoring attempt to add the debugger's compartment as a debuggee.
|
||||
}
|
||||
}
|
47
toolkit/devtools/server/actors/utils/map-uri-to-addon-id.js
Normal file
47
toolkit/devtools/server/actors/utils/map-uri-to-addon-id.js
Normal file
@ -0,0 +1,47 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* 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 DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
const Services = require("Services");
|
||||
const { Cc, Ci } = require("chrome");
|
||||
|
||||
Object.defineProperty(this, "addonManager", {
|
||||
get: (function () {
|
||||
let cached;
|
||||
return () => cached
|
||||
? cached
|
||||
: (cached = Cc["@mozilla.org/addons/integration;1"]
|
||||
.getService(Ci.amIAddonManager))
|
||||
}())
|
||||
});
|
||||
|
||||
const B2G_ID = "{3c2e2abc-06d4-11e1-ac3b-374f68613e61}";
|
||||
|
||||
/**
|
||||
* This is a wrapper around amIAddonManager.mapURIToAddonID which always returns
|
||||
* false on B2G to avoid loading the add-on manager there and reports any
|
||||
* exceptions rather than throwing so that the caller doesn't have to worry
|
||||
* about them.
|
||||
*/
|
||||
module.exports = function mapURIToAddonID(uri, id) {
|
||||
if (!Services.appinfo
|
||||
|| Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT
|
||||
|| Services.appinfo.ID == B2G_ID
|
||||
|| !uri
|
||||
|| !addonManager) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return addonManager.mapURIToAddonID(uri, id);
|
||||
}
|
||||
catch (e) {
|
||||
DevToolsUtils.reportException("mapURIToAddonID", e);
|
||||
return false;
|
||||
}
|
||||
};
|
@ -262,7 +262,7 @@ WebappsActor.prototype = {
|
||||
reg.broadcastMessage("Webapps:UpdateState", {
|
||||
app: aApp,
|
||||
manifest: manifest,
|
||||
manifestURL: aApp.manifestURL
|
||||
id: aApp.id
|
||||
});
|
||||
reg.broadcastMessage("Webapps:FireEvent", {
|
||||
eventType: ["downloadsuccess", "downloadapplied"],
|
||||
|
@ -14,6 +14,8 @@ let { AddonThreadActor, ThreadActor } = require("devtools/server/actors/script")
|
||||
let { DebuggerServer } = require("devtools/server/main");
|
||||
let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
let { dbg_assert } = DevToolsUtils;
|
||||
let makeDebugger = require("./utils/make-debugger");
|
||||
let mapURIToAddonID = require("./utils/map-uri-to-addon-id");
|
||||
|
||||
let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
@ -71,6 +73,29 @@ function sendShutdownEvent() {
|
||||
|
||||
exports.sendShutdownEvent = sendShutdownEvent;
|
||||
|
||||
/**
|
||||
* Unwrap a global that is wrapped in a |Debugger.Object|, or if the global has
|
||||
* become a dead object, return |undefined|.
|
||||
*
|
||||
* @param Debugger.Object wrappedGlobal
|
||||
* The |Debugger.Object| which wraps a global.
|
||||
*
|
||||
* @returns {Object|undefined}
|
||||
* Returns the unwrapped global object or |undefined| if unwrapping
|
||||
* failed.
|
||||
*/
|
||||
const unwrapDebuggerObjectGlobal = wrappedGlobal => {
|
||||
let global;
|
||||
try {
|
||||
global = wrappedGlobal.unsafeDereference();
|
||||
}
|
||||
catch (e) {
|
||||
// Because of bug 991399 we sometimes get bad objects here. If we
|
||||
// can't dereference them then they won't be useful to us.
|
||||
}
|
||||
return global;
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct a root actor appropriate for use in a server running in a
|
||||
* browser. The returned root actor:
|
||||
@ -520,6 +545,13 @@ function TabActor(aConnection)
|
||||
this._extraActors = {};
|
||||
this._exited = false;
|
||||
|
||||
this._shouldAddNewGlobalAsDebuggee = this._shouldAddNewGlobalAsDebuggee.bind(this);
|
||||
|
||||
this.makeDebugger = makeDebugger.bind(null, {
|
||||
findDebuggees: () => this.windows,
|
||||
shouldAddNewGlobalAsDebuggee: this._shouldAddNewGlobalAsDebuggee
|
||||
});
|
||||
|
||||
this.traits = { reconfigure: true };
|
||||
}
|
||||
|
||||
@ -715,6 +747,37 @@ TabActor.prototype = {
|
||||
this._exited = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return true if the given global is associated with this tab and should be
|
||||
* added as a debuggee, false otherwise.
|
||||
*/
|
||||
_shouldAddNewGlobalAsDebuggee: function (wrappedGlobal) {
|
||||
if (wrappedGlobal.hostAnnotations &&
|
||||
wrappedGlobal.hostAnnotations.type == "document" &&
|
||||
wrappedGlobal.hostAnnotations.element === this.window) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let global = unwrapDebuggerObjectGlobal(wrappedGlobal);
|
||||
if (!global) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the global is a sdk page-mod sandbox.
|
||||
let metadata = {};
|
||||
let id = "";
|
||||
try {
|
||||
id = getInnerId(this.window);
|
||||
metadata = Cu.getSandboxMetadata(global);
|
||||
}
|
||||
catch (e) {}
|
||||
if (metadata["inner-window-id"] && metadata["inner-window-id"] == id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/* Support for DebuggerServer.addTabActor. */
|
||||
_createExtraActors: createExtraActors,
|
||||
_appendExtraActors: appendExtraActors,
|
||||
@ -999,7 +1062,7 @@ TabActor.prototype = {
|
||||
// Refresh the debuggee list when a new window object appears (top window or
|
||||
// iframe).
|
||||
if (threadActor.attached) {
|
||||
threadActor.findGlobals();
|
||||
threadActor.dbg.addDebuggees();
|
||||
}
|
||||
},
|
||||
|
||||
@ -1287,6 +1350,14 @@ function BrowserAddonActor(aConnection, aAddon) {
|
||||
this.conn.addActorPool(this._contextPool);
|
||||
this._threadActor = null;
|
||||
this._global = null;
|
||||
|
||||
this._shouldAddNewGlobalAsDebuggee = this._shouldAddNewGlobalAsDebuggee.bind(this);
|
||||
|
||||
this.makeDebugger = makeDebugger.bind(null, {
|
||||
findDebuggees: this._findDebuggees.bind(this),
|
||||
shouldAddNewGlobalAsDebuggee: this._shouldAddNewGlobalAsDebuggee
|
||||
});
|
||||
|
||||
AddonManager.addAddonListener(this);
|
||||
}
|
||||
|
||||
@ -1373,8 +1444,7 @@ BrowserAddonActor.prototype = {
|
||||
}
|
||||
|
||||
if (!this.attached) {
|
||||
this._threadActor = new AddonThreadActor(this.conn, this,
|
||||
this._addon.id);
|
||||
this._threadActor = new AddonThreadActor(this.conn, this);
|
||||
this._contextPool.addActor(this._threadActor);
|
||||
}
|
||||
|
||||
@ -1413,6 +1483,61 @@ BrowserAddonActor.prototype = {
|
||||
windowUtils.resumeTimeouts();
|
||||
windowUtils.suppressEventHandling(false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Return true if the given global is associated with this addon and should be
|
||||
* added as a debuggee, false otherwise.
|
||||
*/
|
||||
_shouldAddNewGlobalAsDebuggee: function (aGlobal) {
|
||||
const global = unwrapDebuggerObjectGlobal(aGlobal);
|
||||
try {
|
||||
// This will fail for non-Sandbox objects, hence the try-catch block.
|
||||
let metadata = Cu.getSandboxMetadata(global);
|
||||
if (metadata) {
|
||||
return metadata.addonID === this.id;
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
if (global instanceof Ci.nsIDOMWindow) {
|
||||
let id = {};
|
||||
if (mapURIToAddonID(global.document.documentURIObject, id)) {
|
||||
return id.value === this.id;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the global for a __URI__ property and then try to map that to an
|
||||
// add-on
|
||||
let uridescriptor = aGlobal.getOwnPropertyDescriptor("__URI__");
|
||||
if (uridescriptor && "value" in uridescriptor && uridescriptor.value) {
|
||||
let uri;
|
||||
try {
|
||||
uri = Services.io.newURI(uridescriptor.value, null, null);
|
||||
}
|
||||
catch (e) {
|
||||
DevToolsUtils.reportException(
|
||||
"BrowserAddonActor.prototype._shouldAddNewGlobalAsDebuggee",
|
||||
new Error("Invalid URI: " + uridescriptor.value)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
let id = {};
|
||||
if (mapURIToAddonID(uri, id)) {
|
||||
return id.value === this.id;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Yield the current set of globals associated with this addon that should be
|
||||
* added as debuggees.
|
||||
*/
|
||||
_findDebuggees: function (dbg) {
|
||||
return dbg.findAllGlobals().filter(this._shouldAddNewGlobalAsDebuggee);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -10,7 +10,6 @@ const { Cc, Ci, Cu } = require("chrome");
|
||||
const { DebuggerServer, ActorPool } = require("devtools/server/main");
|
||||
const { EnvironmentActor, LongStringActor, ObjectActor, ThreadActor } = require("devtools/server/actors/script");
|
||||
const { update } = require("devtools/toolkit/DevToolsUtils");
|
||||
const Debugger = require("Debugger");
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
@ -67,7 +66,7 @@ function WebConsoleActor(aConnection, aParentActor)
|
||||
|
||||
this._prefs = {};
|
||||
|
||||
this.dbg = new Debugger();
|
||||
this.dbg = this.parentActor.makeDebugger();
|
||||
|
||||
this._netEvents = new Map();
|
||||
this._gripDepth = 0;
|
||||
@ -1011,7 +1010,6 @@ WebConsoleActor.prototype =
|
||||
// If we have an object to bind to |_self|, create a Debugger.Object
|
||||
// referring to that object, belonging to dbg.
|
||||
let bindSelf = null;
|
||||
let dbgWindow = dbg.makeGlobalObjectReference(this.evalWindow);
|
||||
if (aOptions.bindObjectActor) {
|
||||
let objActor = this.getActorByID(aOptions.bindObjectActor);
|
||||
if (objActor) {
|
||||
|
@ -99,7 +99,8 @@ let listener = {
|
||||
}
|
||||
|
||||
// Make sure we exit all nested event loops so that the test can finish.
|
||||
while (DebuggerServer.xpcInspector.eventLoopNestLevel > 0) {
|
||||
while (DebuggerServer.xpcInspector
|
||||
&& DebuggerServer.xpcInspector.eventLoopNestLevel > 0) {
|
||||
DebuggerServer.xpcInspector.exitNestedEventLoop();
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,9 @@ function test_enter_exit_frame()
|
||||
do_check_eq(traceNames[4], "foo",
|
||||
'Should have entered "foo" frame in fifth packet');
|
||||
finishClient(gClient);
|
||||
});
|
||||
})
|
||||
.then(null, e => DevToolsUtils.reportException("test_trace_actor-04.js",
|
||||
e));
|
||||
}
|
||||
|
||||
function start_trace()
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user