Merge m-c to inbound. a=merge

This commit is contained in:
Ryan VanderMeulen 2014-08-25 16:28:25 -04:00
commit 04c90e50f6
108 changed files with 1422 additions and 354 deletions

View File

@ -707,6 +707,8 @@ var CustomEventManager = {
switch(detail.type) {
case 'webapps-install-granted':
case 'webapps-install-denied':
case 'webapps-uninstall-granted':
case 'webapps-uninstall-denied':
WebappsHelper.handleEvent(detail);
break;
case 'select-choicechange':
@ -749,6 +751,7 @@ var WebappsHelper = {
init: function webapps_init() {
Services.obs.addObserver(this, "webapps-launch", false);
Services.obs.addObserver(this, "webapps-ask-install", false);
Services.obs.addObserver(this, "webapps-ask-uninstall", false);
Services.obs.addObserver(this, "webapps-close", false);
},
@ -771,6 +774,12 @@ var WebappsHelper = {
case "webapps-install-denied":
DOMApplicationRegistry.denyInstall(installer);
break;
case "webapps-uninstall-granted":
DOMApplicationRegistry.confirmUninstall(installer);
break;
case "webapps-uninstall-denied":
DOMApplicationRegistry.denyUninstall(installer);
break;
}
},
@ -778,6 +787,8 @@ var WebappsHelper = {
let json = JSON.parse(data);
json.mm = subject;
let id;
switch(topic) {
case "webapps-launch":
DOMApplicationRegistry.getManifestFor(json.manifestURL).then((aManifest) => {
@ -795,13 +806,21 @@ var WebappsHelper = {
});
break;
case "webapps-ask-install":
let id = this.registerInstaller(json);
id = this.registerInstaller(json);
shell.sendChromeEvent({
type: "webapps-ask-install",
id: id,
app: json.app
});
break;
case "webapps-ask-uninstall":
id = this.registerInstaller(json);
shell.sendChromeEvent({
type: "webapps-ask-uninstall",
id: id,
app: json.app
});
break;
case "webapps-close":
shell.sendCustomEvent("webapps-close", {
"manifestURL": json.manifestURL

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="53a59364ce4f14068034c8d6fe01f4f6b9f78f23">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="a25ae14dbd2fe3e25144a7064efc0cc4e31042b8"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1934a2297ffc0d90424cd9cd3294c4a8c74a7333"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
@ -133,9 +133,9 @@
<project name="platform/hardware/akm" path="hardware/akm" revision="6d3be412647b0eab0adff8a2768736cf4eb68039"/>
<project groups="invensense" name="platform/hardware/invensense" path="hardware/invensense" revision="e6d9ab28b4f4e7684f6c07874ee819c9ea0002a2"/>
<project name="platform/hardware/ril" path="hardware/ril" revision="865ce3b4a2ba0b3a31421ca671f4d6c5595f8690"/>
<project name="kernel/common" path="kernel" revision="388015e5f2f34a8156500226bf37be76a68154ce"/>
<project name="kernel/common" path="kernel" revision="f3a717dd8dbb08e558c807c5ede071d83d454207"/>
<project name="platform/system/core" path="system/core" revision="b5de04ae22343b6bdaa3455aee291bdf9a872738"/>
<project name="u-boot" path="u-boot" revision="3ab28edd4be2673ac979a2338be8d9f0f25b8314"/>
<project name="u-boot" path="u-boot" revision="d18f67c122a69f5ff7596a604cf484de5e824f9b"/>
<project name="vendor/sprd/gps" path="vendor/sprd/gps" revision="7feb3df0e150053e0143ef525f6e082bda320aea"/>
<project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="8010ea42ca4963d610c88279dbe35dbaa2f6daf6"/>
<project name="vendor/sprd/partner" path="vendor/sprd/partner" revision="8649c7145972251af11b0639997edfecabfc7c2e"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a25ae14dbd2fe3e25144a7064efc0cc4e31042b8"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1934a2297ffc0d90424cd9cd3294c4a8c74a7333"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="a25ae14dbd2fe3e25144a7064efc0cc4e31042b8"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1934a2297ffc0d90424cd9cd3294c4a8c74a7333"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d59843c1d66bd8d93c6012efda47fa447b99813c"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="53a59364ce4f14068034c8d6fe01f4f6b9f78f23">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="a25ae14dbd2fe3e25144a7064efc0cc4e31042b8"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1934a2297ffc0d90424cd9cd3294c4a8c74a7333"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a25ae14dbd2fe3e25144a7064efc0cc4e31042b8"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1934a2297ffc0d90424cd9cd3294c4a8c74a7333"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>

View File

@ -17,7 +17,7 @@
</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="a25ae14dbd2fe3e25144a7064efc0cc4e31042b8"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1934a2297ffc0d90424cd9cd3294c4a8c74a7333"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d59843c1d66bd8d93c6012efda47fa447b99813c"/>

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "75d8e419442b5810145a888944ab0ab4f75bbcc9",
"revision": "5efa211f0198ab15798b456445f163d57e735cdb",
"repo_path": "/integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a25ae14dbd2fe3e25144a7064efc0cc4e31042b8"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1934a2297ffc0d90424cd9cd3294c4a8c74a7333"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a25ae14dbd2fe3e25144a7064efc0cc4e31042b8"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1934a2297ffc0d90424cd9cd3294c4a8c74a7333"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="a25ae14dbd2fe3e25144a7064efc0cc4e31042b8"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1934a2297ffc0d90424cd9cd3294c4a8c74a7333"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d59843c1d66bd8d93c6012efda47fa447b99813c"/>

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a25ae14dbd2fe3e25144a7064efc0cc4e31042b8"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1934a2297ffc0d90424cd9cd3294c4a8c74a7333"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -233,8 +233,10 @@ Tools.canvasDebugger = {
label: l10n("ToolboxCanvasDebugger.label", canvasDebuggerStrings),
panelLabel: l10n("ToolboxCanvasDebugger.panelLabel", canvasDebuggerStrings),
tooltip: l10n("ToolboxCanvasDebugger.tooltip", canvasDebuggerStrings),
// Hide the Canvas Debugger in the Add-on Debugger and Browser Toolbox
// (bug 1047520).
isTargetSupported: function(target) {
return !target.isAddon;
return !target.isAddon && !target.chrome;
},
build: function (iframeWindow, toolbox) {
let panel = new CanvasDebuggerPanel(iframeWindow, toolbox);

View File

@ -500,8 +500,7 @@ StyleEditorUI.prototype = {
this._selectEditor(editor);
}
if (this._styleSheetToSelect
&& this._styleSheetToSelect.stylesheet == editor.styleSheet.href) {
if (this._isEditorToSelect(editor)) {
this.switchToSelectedSheet();
}
@ -566,25 +565,42 @@ StyleEditorUI.prototype = {
* Promise that will resolve when the editor is selected.
*/
switchToSelectedSheet: function() {
let sheet = this._styleSheetToSelect;
let isHref = sheet.stylesheet === null || typeof sheet.stylesheet == "string";
let toSelect = this._styleSheetToSelect;
for (let editor of this.editors) {
if ((isHref && editor.styleSheet.href == sheet.stylesheet) ||
sheet.stylesheet == editor.styleSheet) {
if (this._isEditorToSelect(editor)) {
// The _styleSheetBoundToSelect will always hold the latest pending
// requested style sheet (with line and column) which is not yet
// selected by the source editor. Only after we select that particular
// editor and go the required line and column, it will become null.
this._styleSheetBoundToSelect = this._styleSheetToSelect;
this._styleSheetToSelect = null;
return this._selectEditor(editor, sheet.line, sheet.col);
return this._selectEditor(editor, toSelect.line, toSelect.col);
}
}
return promise.resolve();
},
/**
* Returns whether a given editor is the current editor to be selected. Tests
* based on href or underlying stylesheet.
*
* @param {StyleSheetEditor} editor
* The editor to test.
*/
_isEditorToSelect: function(editor) {
let toSelect = this._styleSheetToSelect;
if (!toSelect) {
return false;
}
let isHref = toSelect.stylesheet === null ||
typeof toSelect.stylesheet == "string";
return (isHref && editor.styleSheet.href == toSelect.stylesheet) ||
(toSelect.stylesheet == editor.styleSheet);
},
/**
* Select an editor in the UI.
*

View File

@ -42,6 +42,7 @@ function test() {
function checkCache() {
checkDiskCacheFor(TEST_HOST, function() {
gUI.off("editor-added", onEditorAdded);
win.close();
win = null;
gUI = null;

View File

@ -1424,19 +1424,16 @@ SelectorView.prototype = {
return;
}
let location = promise.resolve({
href: rule.href,
line: rule.line
});
if (rule.href && Services.prefs.getBoolPref(PREF_ORIG_SOURCES)) {
let location = promise.resolve(rule.location);
if (Services.prefs.getBoolPref(PREF_ORIG_SOURCES)) {
location = rule.getOriginalLocation();
}
location.then(({href, line}) => {
location.then(({source, href, line, column}) => {
let target = inspector.target;
if (ToolDefinitions.styleEditor.isTargetSupported(target)) {
gDevTools.showToolbox(target, "styleeditor").then(function(toolbox) {
toolbox.getCurrentPanel().selectStyleSheet(href, line);
let sheet = source || href;
toolbox.getCurrentPanel().selectStyleSheet(sheet, line, column);
});
}
});

View File

@ -61,11 +61,12 @@ function RuleViewTool(aInspector, aWindow, aIFrame)
if (Services.prefs.getBoolPref(PREF_ORIG_SOURCES)) {
location = rule.getOriginalLocation();
}
location.then(({ href, line, column }) => {
location.then(({ source, href, line, column }) => {
let target = this.inspector.target;
if (ToolDefinitions.styleEditor.isTargetSupported(target)) {
gDevTools.showToolbox(target, "styleeditor").then(function(toolbox) {
toolbox.getCurrentPanel().selectStyleSheet(href, line, column);
let sheet = source || href;
toolbox.getCurrentPanel().selectStyleSheet(sheet, line, column);
});
}
return;

View File

@ -21,6 +21,9 @@ const DOCUMENT_URL = "data:text/html;charset=utf-8,"+encodeURIComponent(
'.nomatches {color: #ff0000;}</style> <div id="first" style="margin: 10em; ',
'font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA">',
'</style>',
'<style>',
'div { color: #f06; }',
'</style>',
'<link rel="stylesheet" type="text/css" href="'+STYLESHEET_URL+'">',
'</head>',
'<body>',
@ -50,7 +53,8 @@ let test = asyncTest(function*() {
yield selectNode("span", inspector);
yield testInlineStyle(view, inspector);
yield testInlineStyleSheet(view, toolbox);
yield testFirstInlineStyleSheet(view, toolbox);
yield testSecondInlineStyleSheet(view, toolbox);
yield testExternalStyleSheet(view, toolbox);
});
@ -61,8 +65,7 @@ function* testInlineStyle(view, inspector) {
let onWindow = waitForWindow();
info("Clicking on the first rule-link in the computed-view");
let link = getComputedViewLinkByIndex(view, 0);
link.click();
clickLinkByIndex(view, 0);
let win = yield onWindow;
@ -72,15 +75,14 @@ function* testInlineStyle(view, inspector) {
win.close();
}
function* testInlineStyleSheet(view, toolbox) {
function* testFirstInlineStyleSheet(view, toolbox) {
info("Testing inline stylesheet");
info("Listening for toolbox switch to the styleeditor");
let onSwitch = waitForStyleEditor(toolbox);
info("Clicking an inline stylesheet");
let link = getComputedViewLinkByIndex(view, 2);
link.click();
clickLinkByIndex(view, 2);
let editor = yield onSwitch;
ok(true, "Switched to the style-editor panel in the toolbox");
@ -88,6 +90,24 @@ function* testInlineStyleSheet(view, toolbox) {
validateStyleEditorSheet(editor, 0);
}
function* testSecondInlineStyleSheet(view, toolbox) {
info("Testing second inline stylesheet");
info("Waiting for the stylesheet editor to be selected");
let panel = toolbox.getCurrentPanel();
let onSelected = panel.UI.once("editor-selected");
info("Switching back to the inspector panel in the toolbox");
yield toolbox.selectTool("inspector");
info("Clicking on second inline stylesheet link");
clickLinkByIndex(view, 4);
let editor = yield onSelected;
is(toolbox.currentToolId, "styleeditor", "The style editor is selected again");
validateStyleEditorSheet(editor, 1);
}
function* testExternalStyleSheet(view, toolbox) {
info("Testing external stylesheet");
@ -99,12 +119,11 @@ function* testExternalStyleSheet(view, toolbox) {
yield toolbox.selectTool("inspector");
info("Clicking on an external stylesheet link");
let link = getComputedViewLinkByIndex(view, 1);
link.click();
clickLinkByIndex(view, 1);
let editor = yield onSelected;
is(toolbox.currentToolId, "styleeditor", "The style editor is selected again");
validateStyleEditorSheet(editor, 1);
validateStyleEditorSheet(editor, 2);
}
function validateStyleEditorSheet(editor, expectedSheetIndex) {
@ -112,3 +131,9 @@ function validateStyleEditorSheet(editor, expectedSheetIndex) {
let sheet = content.document.styleSheets[expectedSheetIndex];
is(editor.styleSheet.href, sheet.href, "loaded stylesheet matches document stylesheet");
}
function clickLinkByIndex(view, index) {
let link = getComputedViewLinkByIndex(view, index);
link.scrollIntoView();
link.click();
}

View File

@ -24,6 +24,9 @@ const DOCUMENT_URL = "data:text/html;charset=utf-8,"+encodeURIComponent(
'.nomatches {color: #ff0000;}</style> <div id="first" style="margin: 10em; ',
'font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA">',
'</style>',
'<style>',
'div { font-weight: bold; }',
'</style>',
'<link rel="stylesheet" type="text/css" href="'+STYLESHEET_URL+'">',
'<link rel="stylesheet" type="text/css" href="'+EXTERNAL_STYLESHEET_URL+'">',
'</head>',
@ -52,7 +55,8 @@ let test = asyncTest(function*() {
yield selectNode("div", inspector);
yield testInlineStyle(view, inspector);
yield testInlineStyleSheet(view, toolbox);
yield testFirstInlineStyleSheet(view, toolbox);
yield testSecondInlineStyleSheet(view, toolbox);
yield testExternalStyleSheet(view, toolbox);
});
@ -61,9 +65,7 @@ function* testInlineStyle(view, inspector) {
let onWindow = waitForWindow();
info("Clicking on the first link in the rule-view");
let link = getRuleViewLinkByIndex(view, 0);
link.scrollIntoView();
link.click();
clickLinkByIndex(view, 0);
let win = yield onWindow;
@ -73,16 +75,14 @@ function* testInlineStyle(view, inspector) {
win.close();
}
function* testInlineStyleSheet(view, toolbox) {
function* testFirstInlineStyleSheet(view, toolbox) {
info("Testing inline stylesheet");
info("Listening for toolbox switch to the styleeditor");
let onSwitch = waitForStyleEditor(toolbox);
info("Clicking an inline stylesheet");
let link = getRuleViewLinkByIndex(view, 4);
link.scrollIntoView();
link.click();
clickLinkByIndex(view, 4);
let editor = yield onSwitch;
ok(true, "Switched to the style-editor panel in the toolbox");
@ -90,6 +90,25 @@ function* testInlineStyleSheet(view, toolbox) {
validateStyleEditorSheet(editor, 0);
}
function* testSecondInlineStyleSheet(view, toolbox) {
info("Testing second inline stylesheet");
info("Waiting for the stylesheet editor to be selected");
let panel = toolbox.getCurrentPanel();
let onSelected = panel.UI.once("editor-selected");
info("Switching back to the inspector panel in the toolbox");
yield toolbox.selectTool("inspector");
info("Clicking on second inline stylesheet link");
testRuleViewLinkLabel(view);
clickLinkByIndex(view, 3);
let editor = yield onSelected;
is(toolbox.currentToolId, "styleeditor", "The style editor is selected again");
validateStyleEditorSheet(editor, 1);
}
function* testExternalStyleSheet(view, toolbox) {
info("Testing external stylesheet");
@ -102,19 +121,20 @@ function* testExternalStyleSheet(view, toolbox) {
info("Clicking on an external stylesheet link");
testRuleViewLinkLabel(view);
let link = getRuleViewLinkByIndex(view, 1);
link.scrollIntoView();
link.click();
clickLinkByIndex(view, 1);
let editor = yield onSelected;
is(toolbox.currentToolId, "styleeditor", "The style editor is selected again");
validateStyleEditorSheet(editor, 1);
validateStyleEditorSheet(editor, 2);
}
function validateStyleEditorSheet(editor, expectedSheetIndex) {
info("validating style editor stylesheet");
is(editor.styleSheet.styleSheetIndex, expectedSheetIndex,
"loaded stylesheet index matches document stylesheet");
let sheet = content.document.styleSheets[expectedSheetIndex];
is(editor.styleSheet.href, sheet.href, "loaded stylesheet matches document stylesheet");
is(editor.styleSheet.href, sheet.href, "loaded stylesheet href matches document stylesheet");
}
function testRuleViewLinkLabel(view) {
@ -128,3 +148,9 @@ function testRuleViewLinkLabel(view) {
is(tooltipText, EXTERNAL_STYLESHEET_URL,
"rule view stylesheet tooltip text matches the full URI path");
}
function clickLinkByIndex(view, index) {
let link = getRuleViewLinkByIndex(view, index);
link.scrollIntoView();
link.click();
}

View File

@ -449,6 +449,12 @@ webapps.install.accesskey = I
webapps.requestInstall = Do you want to install "%1$S" from this site (%2$S)?
webapps.install.success = Application Installed
webapps.install.inprogress = Installation in progress
webapps.uninstall = Uninstall
webapps.uninstall.accesskey = U
webapps.doNotUninstall = Don't Uninstall
webapps.doNotUninstall.accesskey = D
#LOCALIZATION NOTE (webapps.requestUninstall) %1$S is the web app name
webapps.requestUninstall = Do you want to uninstall "%1$S"?
# LOCALIZATION NOTE (fullscreen.entered): displayed when we enter HTML5 fullscreen mode, %S is the domain name of the focused website (e.g. mozilla.com).
fullscreen.entered=%S is now fullscreen.

View File

@ -31,6 +31,7 @@ this.WebappManager = {
init: function() {
Services.obs.addObserver(this, "webapps-ask-install", false);
Services.obs.addObserver(this, "webapps-ask-uninstall", false);
Services.obs.addObserver(this, "webapps-launch", false);
Services.obs.addObserver(this, "webapps-uninstall", false);
cpmm.addMessageListener("Webapps:Install:Return:OK", this);
@ -40,6 +41,7 @@ this.WebappManager = {
uninit: function() {
Services.obs.removeObserver(this, "webapps-ask-install");
Services.obs.removeObserver(this, "webapps-ask-uninstall");
Services.obs.removeObserver(this, "webapps-launch");
Services.obs.removeObserver(this, "webapps-uninstall");
cpmm.removeMessageListener("Webapps:Install:Return:OK", this);
@ -81,13 +83,20 @@ this.WebappManager = {
let data = JSON.parse(aData);
data.mm = aSubject;
let win;
switch(aTopic) {
case "webapps-ask-install":
let win = this._getWindowForId(data.oid);
win = this._getWindowForId(data.oid);
if (win && win.location.href == data.from) {
this.doInstall(data, win);
}
break;
case "webapps-ask-uninstall":
win = this._getWindowForId(data.windowId);
if (win && win.location.href == data.from) {
this.doUninstall(data, win);
}
break;
case "webapps-launch":
WebappOSUtils.launch(data);
break;
@ -193,6 +202,49 @@ this.WebappManager = {
"webapps-notification-icon",
mainAction);
},
doUninstall: function(aData, aWindow) {
let browser = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
.chromeEventHandler;
let chromeDoc = browser.ownerDocument;
let chromeWin = chromeDoc.defaultView;
let bundle = chromeWin.gNavigatorBundle;
let jsonManifest = aData.app.manifest;
let notification;
let mainAction = {
label: bundle.getString("webapps.uninstall"),
accessKey: bundle.getString("webapps.uninstall.accesskey"),
callback: () => {
notification.remove();
DOMApplicationRegistry.confirmUninstall(aData);
}
};
let secondaryAction = {
label: bundle.getString("webapps.doNotUninstall"),
accessKey: bundle.getString("webapps.doNotUninstall.accesskey"),
callback: () => {
notification.remove();
DOMApplicationRegistry.denyUninstall(aData, "USER_DECLINED");
}
};
let manifest = new ManifestHelper(jsonManifest, aData.app.origin,
aData.app.manifestURL);
let message = bundle.getFormattedString("webapps.requestUninstall",
[manifest.name]);
notification = chromeWin.PopupNotifications.show(
browser, "webapps-uninstall", message,
"webapps-notification-icon",
mainAction, [secondaryAction]);
}
}

View File

@ -405,9 +405,10 @@ interface nsIFrameScriptLoader : nsISupports
jsval getDelayedFrameScripts();
};
[scriptable, builtinclass, uuid(ad57800b-ff21-4e2f-91d3-e68615ae8afe)]
[scriptable, builtinclass, uuid(637e8538-4f8f-4a3d-8510-e74386233e19)]
interface nsIProcessChecker : nsISupports
{
bool killChild();
/**
* Return true if the "remote" process has |aPermission|. This is

View File

@ -803,6 +803,18 @@ nsFrameMessageManager::Atob(const nsAString& aAsciiString,
// nsIProcessChecker
NS_IMETHODIMP
nsFrameMessageManager::KillChild(bool *aValid)
{
if (!mCallback) {
*aValid = false;
return NS_ERROR_NOT_AVAILABLE;
}
*aValid = mCallback->KillChild();
return NS_OK;
}
nsresult
nsFrameMessageManager::AssertProcessInternal(ProcessCheckerType aType,
const nsAString& aCapability,

View File

@ -98,6 +98,12 @@ public:
return false;
}
virtual bool KillChild()
{
// By default, does nothing.
return false;
}
protected:
bool BuildClonedMessageDataForParent(nsIContentParent* aParent,
const StructuredCloneData& aData,

View File

@ -54,10 +54,12 @@
}
function cleanup() {
let req = navigator.mozApps.mgmt.uninstall(app);
req.onsuccess = function () {
SimpleTest.finish();
};
SpecialPowers.autoConfirmAppUninstall(function () {
let req = navigator.mozApps.mgmt.uninstall(app);
req.onsuccess = function () {
SimpleTest.finish();
};
});
}
setupTest();

View File

@ -69,11 +69,19 @@ GetPluginFile(const std::string& aPluginPath,
}
#endif
nsAutoString leafName;
if (NS_FAILED(aLibFile->GetLeafName(leafName))) {
nsCOMPtr<nsIFile> parent;
rv = aLibFile->GetParent(getter_AddRefs(parent));
if (NS_FAILED(rv)) {
return false;
}
nsAutoString baseName(Substring(leafName, 4, leafName.Length() - 1));
nsAutoString parentLeafName;
rv = parent->GetLeafName(parentLeafName);
if (NS_FAILED(rv)) {
return false;
}
nsAutoString baseName(Substring(parentLeafName, 4, parentLeafName.Length() - 1));
#if defined(XP_MACOSX)
nsAutoString binaryName = NS_LITERAL_STRING("lib") + baseName + NS_LITERAL_STRING(".dylib");

View File

@ -90,16 +90,23 @@ GMPParent::Init(GeckoMediaPluginService *aService, nsIFile* aPluginDir)
mService = aService;
mDirectory = aPluginDir;
nsAutoString leafname;
nsresult rv = aPluginDir->GetLeafName(leafname);
// aPluginDir is <profile-dir>/<gmp-plugin-id>/<version>
// where <gmp-plugin-id> should be gmp-gmpopenh264
nsCOMPtr<nsIFile> parent;
nsresult rv = aPluginDir->GetParent(getter_AddRefs(parent));
if (NS_FAILED(rv)) {
return rv;
}
nsAutoString parentLeafName;
rv = parent->GetLeafName(parentLeafName);
if (NS_FAILED(rv)) {
return rv;
}
LOGD(("%s::%s: %p for %s", __CLASS__, __FUNCTION__, this,
NS_LossyConvertUTF16toASCII(leafname).get()));
NS_LossyConvertUTF16toASCII(parentLeafName).get()));
MOZ_ASSERT(leafname.Length() > 4);
mName = Substring(leafname, 4);
MOZ_ASSERT(parentLeafName.Length() > 4);
mName = Substring(parentLeafName, 4);
return ReadGMPMetaData();
}

View File

@ -215,6 +215,7 @@ WebappsRegistry.prototype = {
let mgmt = Cc["@mozilla.org/webapps/manager;1"]
.createInstance(Ci.nsISupports);
mgmt.wrappedJSObject.init(this._window);
mgmt.wrappedJSObject._windowId = this._id;
this._mgmt = mgmt.__DOM_IMPL__
? mgmt.__DOM_IMPL__
: this._window.DOMApplicationsManager._create(this._window, mgmt.wrappedJSObject);
@ -721,10 +722,15 @@ WebappsApplicationMgmt.prototype = {
uninstall: function(aApp) {
let request = this.createRequest();
cpmm.sendAsyncMessage("Webapps:Uninstall", { origin: aApp.origin,
manifestURL: aApp.manifestURL,
oid: this._id,
requestID: this.getRequestId(request) });
cpmm.sendAsyncMessage("Webapps:Uninstall", {
origin: aApp.origin,
manifestURL: aApp.manifestURL,
oid: this._id,
from: this._window.location.href,
windowId: this._windowId,
requestID: this.getRequestId(request)
});
return request;
},

View File

@ -162,6 +162,7 @@ this.DOMApplicationRegistry = {
children: [ ],
allAppsLaunchable: false,
_updateHandlers: [ ],
_pendingUninstalls: {},
init: function() {
this.messages = ["Webapps:Install", "Webapps:Uninstall",
@ -547,7 +548,7 @@ this.DOMApplicationRegistry = {
if (this.webapps[id].manifestURL === httpsManifestURL) {
debug("Found a http/https match: " + app.manifestURL + " / " +
this.webapps[id].manifestURL);
this.uninstall(app.manifestURL, function() {}, function() {});
this.uninstall(app.manifestURL);
return;
}
}
@ -2638,7 +2639,7 @@ this.DOMApplicationRegistry = {
manifest: jsonManifest
},
isReinstall,
this.uninstall.bind(this, aData, aData.mm)
this.doUninstall.bind(this, aData, aData.mm)
);
}
@ -3675,39 +3676,50 @@ this.DOMApplicationRegistry = {
AppDownloadManager.remove(aNewApp.manifestURL);
},
doUninstall: function(aData, aMm) {
this.uninstall(aData.manifestURL,
function onsuccess() {
aMm.sendAsyncMessage("Webapps:Uninstall:Return:OK", aData);
},
function onfailure() {
// Fall-through, fails to uninstall the desired app because:
// - we cannot find the app to be uninstalled.
// - the app to be uninstalled is not removable.
aMm.sendAsyncMessage("Webapps:Uninstall:Return:KO", aData);
doUninstall: Task.async(function*(aData, aMm) {
// The yields here could get stuck forever, so we only hold
// a weak reference to the message manager while yielding, to avoid
// leaking the whole page associationed with the message manager.
aMm = Cu.getWeakReference(aMm);
let response = "Webapps:Uninstall:Return:OK";
try {
aData.app = yield this._getAppWithManifest(aData.manifestURL);
let prefName = "dom.mozApps.auto_confirm_uninstall";
if (Services.prefs.prefHasUserValue(prefName) &&
Services.prefs.getBoolPref(prefName)) {
yield this._uninstallApp(aData.app);
} else {
yield this._promptForUninstall(aData);
}
);
} catch (error) {
aData.error = error;
response = "Webapps:Uninstall:Return:KO";
}
if (aMm = aMm.get()) {
aMm.sendAsyncMessage(response, aData);
}
}),
uninstall: function(aManifestURL) {
return this._getAppWithManifest(aManifestURL)
.then(this._uninstallApp.bind(this));
},
uninstall: function(aManifestURL, aOnSuccess, aOnFailure) {
debug("uninstall " + aManifestURL);
let app = this.getAppByManifestURL(aManifestURL);
if (!app) {
aOnFailure("NO_SUCH_APP");
return;
}
let id = app.id;
if (!app.removable) {
_uninstallApp: Task.async(function*(aApp) {
if (!aApp.removable) {
debug("Error: cannot uninstall a non-removable app.");
aOnFailure("NON_REMOVABLE_APP");
return;
throw new Error("NON_REMOVABLE_APP");
}
let id = aApp.id;
// Check if we are downloading something for this app, and cancel the
// download if needed.
this.cancelDownload(app.manifestURL);
this.cancelDownload(aApp.manifestURL);
// Clean up the deprecated manifest cache if needed.
if (id in this._manifestCache) {
@ -3715,18 +3727,13 @@ this.DOMApplicationRegistry = {
}
// Clear private data first.
this._clearPrivateData(app.localId, false);
this._clearPrivateData(aApp.localId, false);
// Then notify observers.
// We have to clone the app object as nsIDOMApplication objects are
// stringified as an empty object. (see bug 830376)
let appClone = AppsUtils.cloneAppObject(app);
Services.obs.notifyObservers(null, "webapps-uninstall", JSON.stringify(appClone));
Services.obs.notifyObservers(null, "webapps-uninstall", JSON.stringify(aApp));
if (supportSystemMessages()) {
this._readManifests([{ id: id }]).then((aResult) => {
this._unregisterActivities(aResult[0].manifest, app);
});
this._unregisterActivities(aApp.manifest, aApp);
}
let dir = this._getAppDir(id);
@ -3736,18 +3743,47 @@ this.DOMApplicationRegistry = {
delete this.webapps[id];
this._saveApps().then(() => {
this.broadcastMessage("Webapps:Uninstall:Broadcast:Return:OK", appClone);
this.broadcastMessage("Webapps:RemoveApp", { id: id });
try {
if (aOnSuccess) {
aOnSuccess();
}
} catch(ex) {
Cu.reportError("DOMApplicationRegistry: Exception on app uninstall: " +
ex + "\n" + ex.stack);
}
});
yield this._saveApps();
this.broadcastMessage("Webapps:Uninstall:Broadcast:Return:OK", aApp);
this.broadcastMessage("Webapps:RemoveApp", { id: id });
return aApp;
}),
_promptForUninstall: function(aData) {
let deferred = Promise.defer();
this._pendingUninstalls[aData.requestID] = deferred;
Services.obs.notifyObservers(null, "webapps-ask-uninstall",
JSON.stringify(aData));
return deferred.promise;
},
confirmUninstall: function(aData) {
let pending = this._pendingUninstalls[aData.requestID];
if (pending) {
delete this._pendingUninstalls[aData.requestID];
return this._uninstallApp(aData.app).then(() => {
pending.resolve();
return aData.app;
});
}
return Promise.reject(new Error("PENDING_UNINSTALL_NOT_FOUND"));
},
denyUninstall: function(aData, aReason = "ERROR_UNKNOWN_FAILURE") {
// Fails to uninstall the desired app because:
// - we cannot find the app to be uninstalled.
// - the app to be uninstalled is not removable.
// - the user declined the confirmation
debug("Failed to uninstall app: " + aReason);
let pending = this._pendingUninstalls[aData.requestID];
if (pending) {
delete this._pendingUninstalls[aData.requestID];
pending.reject(new Error(aReason));
return Promise.resolve();
}
return Promise.reject(new Error("PENDING_UNINSTALL_NOT_FOUND"));
},
getSelf: function(aData, aMm) {
@ -4067,6 +4103,17 @@ this.DOMApplicationRegistry = {
return AppsUtils.getAppByManifestURL(this.webapps, aManifestURL);
},
_getAppWithManifest: Task.async(function*(aManifestURL) {
let app = this.getAppByManifestURL(aManifestURL);
if (!app) {
throw new Error("NO_SUCH_APP");
}
app.manifest = ( yield this._readManifests([{ id: app.id }]) )[0].manifest;
return app;
}),
getCSPByLocalId: function(aLocalId) {
debug("getCSPByLocalId:" + aLocalId);
return AppsUtils.getCSPByLocalId(this.webapps, aLocalId);

View File

@ -72,6 +72,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=826058
yield undefined;
SpecialPowers.autoConfirmAppInstall(continueTest);
yield undefined;
SpecialPowers.autoConfirmAppUninstall(continueTest);
yield undefined;
// Load the app, uninstalled.
checkAppState(null, false, 1, continueTest);

View File

@ -44,6 +44,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=795164
SpecialPowers.autoConfirmAppInstall(continueTest);
yield undefined;
SpecialPowers.autoConfirmAppUninstall(continueTest);
yield undefined;
// Keeping track of the number of times `mozApps.mgmt.onuninstall` gets triggered
let uninstallCount = 0;

View File

@ -71,6 +71,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=945152
SpecialPowers.autoConfirmAppInstall(continueTest);
yield undefined;
SpecialPowers.autoConfirmAppUninstall(continueTest);
yield undefined;
// Create app on server side.
createApp(continueTest);
yield undefined;

View File

@ -60,6 +60,9 @@ function runTest() {
SpecialPowers.autoConfirmAppInstall(continueTest);
yield undefined;
SpecialPowers.autoConfirmAppUninstall(continueTest);
yield undefined;
request = navigator.mozApps.mgmt.getAll();
request.onerror = cbError;
request.onsuccess = continueTest;

View File

@ -57,6 +57,9 @@ function runTest() {
SpecialPowers.autoConfirmAppInstall(continueTest);
yield undefined;
SpecialPowers.autoConfirmAppUninstall(continueTest);
yield undefined;
// Test install with three valid receipts
let valid_receipt1 = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJwcm9kdWN0IjogeyJ1cmwiOiAiaHR0cHM6Ly93d3cubW96aWxsYS5vcmciLCAic3RvcmVkYXRhIjogIjUxNjkzMTQzNTYifSwgInJlaXNzdWUiOiAiaHR0cDovL21vY2hpLnRlc3Q6ODg4OC9yZWlzc3VlLzUxNjkzMTQzNTYiLCAidXNlciI6IHsidHlwZSI6ICJkaXJlY3RlZC1pZGVudGlmaWVyIiwgInZhbHVlIjogIjRmYjM1MTUxLTJiOWItNGJhMi04MjgzLWM0OWQzODE2NDBiZCJ9LCAidmVyaWZ5IjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgvdmVyaWZ5LzUxNjkzMTQzNTYiLCAiaXNzIjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgiLCAiaWF0IjogMTMxMzYwMTg4LCAidHlwIjogInB1cmNoYXNlLXJlY2VpcHQiLCAibmJmIjogMTMxMzYwMTg1LCAiZGV0YWlsIjogImh0dHA6Ly9tb2NoaS50ZXN0Ojg4ODgvcmVjZWlwdC81MTY5MzE0MzU2In0.eZpTEnCLUR3iP3rm9WyJOqx1k66mQaAxqcrvX11r5E0';

View File

@ -131,8 +131,10 @@ PackagedTestHelper.setSteps([
SpecialPowers.addPermission("webapps-manage", true, document);
SpecialPowers.addPermission("browser", true, document);
SpecialPowers.autoConfirmAppInstall(() =>
SpecialPowers.pushPrefEnv({set: [["dom.mozBrowserFramesEnabled", true]]},
PackagedTestHelper.next));
SpecialPowers.autoConfirmAppUninstall(() =>
SpecialPowers.pushPrefEnv({set: [["dom.mozBrowserFramesEnabled",
true]]},
PackagedTestHelper.next)));
},
function() {
info("== TEST == Marketplace packaged app from https://marketplace.firefox.com/");

View File

@ -140,6 +140,9 @@ function runTest() {
SpecialPowers.autoConfirmAppInstall(continueTest);
yield undefined;
SpecialPowers.autoConfirmAppUninstall(continueTest);
yield undefined;
setState("setVersion", 1, continueTest);
yield undefined;

View File

@ -90,6 +90,10 @@ var steps = [
info("autoConfirmAppInstall");
SpecialPowers.autoConfirmAppInstall(PackagedTestHelper.next);
},
function() {
ok(true, "autoConfirmAppUninstall");
SpecialPowers.autoConfirmAppUninstall(PackagedTestHelper.next);
},
function() {
PackagedTestHelper.setAppVersion(0, PackagedTestHelper.next);
},

View File

@ -57,6 +57,9 @@ function runTest() {
SpecialPowers.autoConfirmAppInstall(continueTest);
yield undefined;
SpecialPowers.autoConfirmAppUninstall(continueTest);
yield undefined;
var request = navigator.mozApps.install(gManifestURL);
request.onerror = cbError;
request.onsuccess = continueTest;

View File

@ -106,6 +106,10 @@ var steps = [
info("autoConfirmAppInstall");
SpecialPowers.autoConfirmAppInstall(PackagedTestHelper.next);
},
function() {
info("autoConfirmAppUninstall");
SpecialPowers.autoConfirmAppUninstall(PackagedTestHelper.next);
},
function() {
info("== TEST == Install packaged app");
var miniManifestURL = gSJS + "?" + "app=valid&" + "version=1";

View File

@ -44,6 +44,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=830258
SpecialPowers.autoConfirmAppInstall(continueTest);
yield undefined;
SpecialPowers.autoConfirmAppUninstall(continueTest);
yield undefined;
let request = navigator.mozApps.install(url1);
request.onerror = mozAppsError;
request.onsuccess = continueTest;

View File

@ -190,7 +190,9 @@
// No confirmation needed when an app is installed
function() {
SpecialPowers.autoConfirmAppInstall(runTest);
SpecialPowers.autoConfirmAppInstall(() => {
SpecialPowers.autoConfirmAppUninstall(runTest);
});
},
// Installing the app

View File

@ -2328,23 +2328,23 @@ BluetoothHandsfreeInterface::Init(
BluetoothHandsfreeResultHandler* aRes)
{
static bthf_callbacks_t sCallbacks = {
.size = sizeof(sCallbacks),
.connection_state_cb = BluetoothHandsfreeCallback::ConnectionState,
.audio_state_cb = BluetoothHandsfreeCallback::AudioState,
.vr_cmd_cb = BluetoothHandsfreeCallback::VoiceRecognition,
.answer_call_cmd_cb = BluetoothHandsfreeCallback::AnswerCall,
.hangup_call_cmd_cb = BluetoothHandsfreeCallback::HangupCall,
.volume_cmd_cb = BluetoothHandsfreeCallback::Volume,
.dial_call_cmd_cb = BluetoothHandsfreeCallback::DialCall,
.dtmf_cmd_cb = BluetoothHandsfreeCallback::Dtmf,
.nrec_cmd_cb = BluetoothHandsfreeCallback::NoiseReductionEchoCancellation,
.chld_cmd_cb = BluetoothHandsfreeCallback::CallHold,
.cnum_cmd_cb = BluetoothHandsfreeCallback::Cnum,
.cind_cmd_cb = BluetoothHandsfreeCallback::Cind,
.cops_cmd_cb = BluetoothHandsfreeCallback::Cops,
.clcc_cmd_cb = BluetoothHandsfreeCallback::Clcc,
.unknown_at_cmd_cb = BluetoothHandsfreeCallback::UnknownAt,
.key_pressed_cmd_cb = BluetoothHandsfreeCallback::KeyPressed
sizeof(sCallbacks),
BluetoothHandsfreeCallback::ConnectionState,
BluetoothHandsfreeCallback::AudioState,
BluetoothHandsfreeCallback::VoiceRecognition,
BluetoothHandsfreeCallback::AnswerCall,
BluetoothHandsfreeCallback::HangupCall,
BluetoothHandsfreeCallback::Volume,
BluetoothHandsfreeCallback::DialCall,
BluetoothHandsfreeCallback::Dtmf,
BluetoothHandsfreeCallback::NoiseReductionEchoCancellation,
BluetoothHandsfreeCallback::CallHold,
BluetoothHandsfreeCallback::Cnum,
BluetoothHandsfreeCallback::Cind,
BluetoothHandsfreeCallback::Cops,
BluetoothHandsfreeCallback::Clcc,
BluetoothHandsfreeCallback::UnknownAt,
BluetoothHandsfreeCallback::KeyPressed
};
sHandsfreeNotificationHandler = aNotificationHandler;

View File

@ -26,10 +26,10 @@ using namespace mozilla::dom;
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsDOMCameraManager, mWindow)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCameraManager)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCameraManager)
@ -116,7 +116,16 @@ nsDOMCameraManager::CreateInstance(nsPIDOMWindow* aWindow)
new nsDOMCameraManager(aWindow);
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
obs->AddObserver(cameraManager, "xpcom-shutdown", true);
if (!obs) {
DOM_CAMERA_LOGE("Camera manager failed to get observer service\n");
return nullptr;
}
nsresult rv = obs->AddObserver(cameraManager, "xpcom-shutdown", true);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("Camera manager failed to add 'xpcom-shutdown' observer (0x%x)\n", rv);
return nullptr;
}
return cameraManager.forget();
}

View File

@ -54,6 +54,9 @@
SpecialPowers.autoConfirmAppInstall(continueTest);
yield undefined;
SpecialPowers.autoConfirmAppUninstall(continueTest);
yield undefined;
var request = navigator.mozApps.install(gHostedManifestURL);
request.onerror = cbError;
request.onsuccess = continueTest;

View File

@ -95,7 +95,8 @@
// No confirmation needed when an app is installed
function() {
SpecialPowers.autoConfirmAppInstall(runTest);
SpecialPowers.autoConfirmAppInstall(() =>
SpecialPowers.autoConfirmAppUninstall(runTest));
},
// Installing the app

View File

@ -95,7 +95,8 @@
// No confirmation needed when an app is installed
function() {
SpecialPowers.autoConfirmAppInstall(runTest);
SpecialPowers.autoConfirmAppInstall(() =>
SpecialPowers.autoConfirmAppUninstall(runTest));
},
// Installing the app

View File

@ -95,7 +95,8 @@
// No confirmation needed when an app is installed
function() {
SpecialPowers.autoConfirmAppInstall(runTest);
SpecialPowers.autoConfirmAppInstall(() =>
SpecialPowers.autoConfirmAppUninstall(runTest));
},
// Installing the app

View File

@ -96,7 +96,8 @@
// No confirmation needed when an app is installed
function() {
SpecialPowers.autoConfirmAppInstall(runTest);
SpecialPowers.autoConfirmAppInstall(() =>
SpecialPowers.autoConfirmAppUninstall(runTest));
},
// Installing the app

View File

@ -95,7 +95,8 @@
// No confirmation needed when an app is installed
function() {
SpecialPowers.autoConfirmAppInstall(runTest);
SpecialPowers.autoConfirmAppInstall(() =>
SpecialPowers.autoConfirmAppUninstall(runTest));
},
// Installing the app

View File

@ -112,6 +112,11 @@
// Run tests in app
testApp,
// No confirmation needed when an app is installed
function() {
SpecialPowers.autoConfirmAppUninstall(runTest);
},
// Uninstall the app
uninstallApp
];

View File

@ -100,7 +100,8 @@
// No confirmation needed when an app is installed
function() {
SpecialPowers.autoConfirmAppInstall(runTest);
SpecialPowers.autoConfirmAppInstall(() =>
SpecialPowers.autoConfirmAppUninstall(runTest));
},
// Installing the app1

View File

@ -92,7 +92,8 @@
// No confirmation needed when an app is installed
function() {
SpecialPowers.autoConfirmAppInstall(runTest);
SpecialPowers.autoConfirmAppInstall(() =>
SpecialPowers.autoConfirmAppUninstall(runTest));
},
// Installing the app1

View File

@ -102,7 +102,8 @@
// No confirmation needed when an app is installed
function() {
SpecialPowers.autoConfirmAppInstall(runTest);
SpecialPowers.autoConfirmAppInstall(() =>
SpecialPowers.autoConfirmAppUninstall(runTest));
},
// Installing the app

View File

@ -138,7 +138,8 @@
// No confirmation needed when an app is installed
function() {
SpecialPowers.autoConfirmAppInstall(runTest);
SpecialPowers.autoConfirmAppInstall(() =>
SpecialPowers.autoConfirmAppUninstall(runTest));
},
// Installing the app1

View File

@ -95,7 +95,8 @@
// No confirmation needed when an app is installed
function() {
SpecialPowers.autoConfirmAppInstall(runTest);
SpecialPowers.autoConfirmAppInstall(() =>
SpecialPowers.autoConfirmAppUninstall(runTest));
},
// Installing the app

View File

@ -95,7 +95,8 @@
// No confirmation needed when an app is installed
function() {
SpecialPowers.autoConfirmAppInstall(runTest);
SpecialPowers.autoConfirmAppInstall(() =>
SpecialPowers.autoConfirmAppUninstall(runTest));
},
// Installing the app

View File

@ -89,7 +89,9 @@
function() {
SpecialPowers.setAllAppsLaunchable(true);
// No confirmation needed when an app is installed.
SpecialPowers.autoConfirmAppInstall(runTest);
SpecialPowers.autoConfirmAppInstall(() => {
SpecialPowers.autoConfirmAppUninstall(runTest);
});
},
function() {

View File

@ -96,7 +96,8 @@
// No confirmation needed when an app is installed
function() {
SpecialPowers.autoConfirmAppInstall(runTest);
SpecialPowers.autoConfirmAppInstall(() =>
SpecialPowers.autoConfirmAppUninstall(runTest));
},
// Installing the app

View File

@ -124,7 +124,8 @@
// No confirmation needed when an app is installed
function() {
SpecialPowers.autoConfirmAppInstall(runTest);
SpecialPowers.autoConfirmAppInstall(() =>
SpecialPowers.autoConfirmAppUninstall(runTest));
},
// Installing the apps

View File

@ -40,6 +40,9 @@
SpecialPowers.autoConfirmAppInstall(continueTest);
yield undefined;
SpecialPowers.autoConfirmAppUninstall(continueTest);
yield undefined;
var request = navigator.mozApps.install(gHostedManifestURL);
request.onerror = cbError;
request.onsuccess = continueTest;

View File

@ -94,7 +94,8 @@
// No confirmation needed when an app is installed
function() {
SpecialPowers.autoConfirmAppInstall(runTest);
SpecialPowers.autoConfirmAppInstall(() =>
SpecialPowers.autoConfirmAppUninstall(runTest));
},
// Installing the app

View File

@ -94,7 +94,8 @@
// No confirmation needed when an app is installed
function() {
SpecialPowers.autoConfirmAppInstall(runTest);
SpecialPowers.autoConfirmAppInstall(() =>
SpecialPowers.autoConfirmAppUninstall(runTest));
},
// Installing the app

View File

@ -95,7 +95,8 @@
// No confirmation needed when an app is installed
function() {
SpecialPowers.autoConfirmAppInstall(runTest);
SpecialPowers.autoConfirmAppInstall(() =>
SpecialPowers.autoConfirmAppUninstall(runTest));
},
// Installing the app

View File

@ -3746,6 +3746,13 @@ ContentParent::CheckAppHasStatus(unsigned short aStatus)
return AssertAppHasStatus(this, aStatus);
}
bool
ContentParent::KillChild()
{
KillHard();
return true;
}
PBlobParent*
ContentParent::SendPBlobConstructor(PBlobParent* aActor,
const BlobConstructorParams& aParams)

View File

@ -161,6 +161,7 @@ public:
virtual bool CheckManifestURL(const nsAString& aManifestURL) MOZ_OVERRIDE;
virtual bool CheckAppHasPermission(const nsAString& aPermission) MOZ_OVERRIDE;
virtual bool CheckAppHasStatus(unsigned short aStatus) MOZ_OVERRIDE;
virtual bool KillChild() MOZ_OVERRIDE;
/** Notify that a tab is beginning its destruction sequence. */
void NotifyTabDestroying(PBrowserParent* aTab);

View File

@ -4,7 +4,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
INSTALL_TARGETS += FAKE_GMP_PLUGIN
FAKE_GMP_PLUGIN_DEST = $(DEPTH)/dist/bin/gmp-fake
FAKE_GMP_PLUGIN_DEST = $(DEPTH)/dist/bin/gmp-fake/1.0
FAKE_GMP_PLUGIN_FILES = \
$(SHARED_LIBRARY) \
$(srcdir)/fake.info

View File

@ -4292,7 +4292,7 @@ RadioInterface.prototype = {
if (!response.errorMsg) {
request.notifyGetSmscAddress(response.smscAddress);
} else {
request.notifyGetSmscAddressFailed(response.errorMsg);
request.notifyGetSmscAddressFailed(Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR);
}
}).bind(this));
},

View File

@ -183,7 +183,9 @@ function browserLoadEvent() {
addLoadEvent(() =>
SpecialPowers.pushPrefEnv({set: [['dom.mozBrowserFramesEnabled', true]]}, () =>
SpecialPowers.autoConfirmAppInstall(runTest)
SpecialPowers.autoConfirmAppInstall(() =>
SpecialPowers.autoConfirmAppUninstall(runTest)
)
)
);

View File

@ -197,7 +197,9 @@ function checks() {
addLoadEvent(() =>
SpecialPowers.pushPrefEnv({set: [['dom.mozBrowserFramesEnabled', true]]}, () =>
SpecialPowers.autoConfirmAppInstall(runTest)
SpecialPowers.autoConfirmAppInstall(() =>
SpecialPowers.autoConfirmAppUninstall(runTest)
)
)
);

View File

@ -19,7 +19,7 @@
var parent = SpecialPowers.wrap(window).parent;
confirmNextInstall();
confirmNextPopup();
navigator.mozApps.install(parent.url2, null).onsuccess = function onInstall() {
// Give the test page a reference to the installed app, so it can uninstall it
// after it finishes the tests.

View File

@ -34,19 +34,22 @@ function runAll(steps) {
next();
}
function confirmNextInstall() {
function confirmNextPopup() {
var Ci = SpecialPowers.Ci;
var popupPanel = SpecialPowers.wrap(window).top.
QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIWebNavigation).
QueryInterface(Ci.nsIDocShell).
chromeEventHandler.ownerDocument.defaultView.
PopupNotifications.panel;
var popupNotifications = SpecialPowers.wrap(window).top.
QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIWebNavigation).
QueryInterface(Ci.nsIDocShell).
chromeEventHandler.ownerDocument.defaultView.
PopupNotifications;
var popupPanel = popupNotifications.panel;
function onPopupShown() {
popupPanel.removeEventListener("popupshown", onPopupShown, false);
SpecialPowers.wrap(this).childNodes[0].button.doCommand();
popupNotifications._dismiss();
}
popupPanel.addEventListener("popupshown", onPopupShown, false);
}

View File

@ -43,7 +43,7 @@ ppmm.addMessageListener("Webapps:Install", mmListener);
// We call this here, even though the app is installed by the helper page,
// because the helper page redirect would cause its install listener to unload
// before it can confirm the install.
confirmNextInstall();
confirmNextPopup();
</script>

View File

@ -43,7 +43,7 @@ runAll(steps);
* the app itself is from a different origin (http://test1.example.com).
*/
function installAppFromOwnOrigin(next) {
confirmNextInstall();
confirmNextPopup();
navigator.mozApps.install(url1, null).onsuccess = function onInstall() {
app1 = this.result;
next();
@ -93,7 +93,9 @@ function getAll(next) {
}
function uninstall(next) {
confirmNextPopup();
navigator.mozApps.mgmt.uninstall(app1).onsuccess = function onUninstallApp1() {
confirmNextPopup();
navigator.mozApps.mgmt.uninstall(app2).onsuccess = function onUninstallApp2() {
next();
};

View File

@ -76,11 +76,11 @@ function getNotInstalled(next) {
// Add an app to the appregistry
function installApp(next) {
confirmNextInstall();
confirmNextPopup();
navigator.mozApps.install(url, null).onsuccess = function onInstall() {
app = this.result;
next();
}
};
}
// Call navigator.mozApps.mgmt.getNotInstalled and make sure there is one more.
@ -128,6 +128,7 @@ function unmonkeyPatchDOMApplicationRegistry(next) {
// Remove the app from the appregistry
function uninstallApp(next) {
confirmNextPopup();
window.navigator.mozApps.mgmt.uninstall(app).onsuccess = function onUninstall() {
app = null;
next();

View File

@ -45,7 +45,7 @@ function install(next) {
var beforehand = Date.now();
const fuzzySpan = 250;
confirmNextInstall();
confirmNextPopup();
navigator.mozApps.install(url, null).onsuccess = function onInstall() {
app = this.result;
@ -84,6 +84,7 @@ function getSelf(next) {
}
function uninstall(next) {
confirmNextPopup();
navigator.mozApps.mgmt.uninstall(app).onsuccess = function onUninstall() {
// Try to retrieve the app we just uninstalled, to make sure it no longer
// exists in the registry.

View File

@ -69,7 +69,7 @@ function invalidManifest(next) {
function permissionDenied(next) {
var url = "http://test/chrome/dom/tests/mochitest/webapps/apps/no_delegated_install.webapp";
confirmNextInstall();
confirmNextPopup();
var request = navigator.mozApps.install(url, null);
request.onerror = function onInstallError() {
@ -79,6 +79,7 @@ function permissionDenied(next) {
request.onsuccess = function onInstall() {
todo(false, "manifest without installs_allowed_from fails");
confirmNextPopup();
navigator.mozApps.mgmt.uninstall(this.result).onsuccess = function onUninstall() {
next();
};
@ -97,6 +98,7 @@ function invalidContent(next) {
request.onsuccess = function onInstall() {
ok(false, "manifest with bad content type should fail");
confirmNextPopup();
navigator.mozApps.mgmt.uninstall(this.result).onsuccess = function onUninstall() {
next();
};
@ -206,6 +208,7 @@ function originNotAllowed(next) {
request.onsuccess = function onInstall() {
ok(false, "test should fail because of installs_allowed_from");
confirmNextPopup();
navigator.mozApps.mgmt.uninstall(this.result).onsuccess = function onUninstall() {
next();
};
@ -215,7 +218,7 @@ function originNotAllowed(next) {
function originAllowed(next) {
var url = "http://test/chrome/dom/tests/mochitest/webapps/apps/installs_allowed_from_chrome_mochitests.webapp";
confirmNextInstall();
confirmNextPopup();
var request = navigator.mozApps.install(url, null);
request.onerror = function onInstallError() {
@ -225,6 +228,7 @@ function originAllowed(next) {
request.onsuccess = function onInstall() {
ok(true, "test origin is in installs_allowed_from");
confirmNextPopup();
navigator.mozApps.mgmt.uninstall(this.result).onsuccess = function onUninstall() {
next();
};

View File

@ -22,12 +22,13 @@ SimpleTest.waitForExplicitFinish();
var url = "http://test/chrome/dom/tests/mochitest/webapps/apps/utf8.webapp";
confirmNextInstall();
confirmNextPopup();
navigator.mozApps.install(url, null).onsuccess = function onInstall() {
is(this.result.manifest.name, "TheBOM ゲゴケ゚セニツ゚ヅヂチ", "manifest.name");
is(this.result.manifest.description, "This App is THE BOM, yo. ヅヂチ",
"manifest.description");
confirmNextPopup();
navigator.mozApps.mgmt.uninstall(this.result).onsuccess = function onUninstall() {
SimpleTest.finish();
}

View File

@ -45,7 +45,7 @@ function install(next) {
var beforehand = Date.now();
const fuzzySpan = 250;
confirmNextInstall();
confirmNextPopup();
navigator.mozApps.install(url, null).onsuccess = function onInstall() {
app = this.result;
@ -84,6 +84,7 @@ function getSelf(next) {
}
function uninstall(next) {
confirmNextPopup();
navigator.mozApps.mgmt.uninstall(app).onsuccess = function onUninstall() {
// Try to retrieve the app we just uninstalled, to make sure it no longer
// exists in the registry.

View File

@ -0,0 +1,643 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const TYPE_WIFI = "wifi";
const TYPE_BLUETOOTH = "bt";
const TYPE_USB = "usb";
/**
* General tethering setting.
*/
const TETHERING_SETTING_IP = "192.168.1.1";
const TETHERING_SETTNG_PREFIX = "24";
const TETHERING_SETTING_START_IP = "192.168.1.10";
const TETHERING_SETTING_END_IP = "192.168.1.30";
const TETHERING_SETTING_DNS1 = "8.8.8.8";
const TETHERING_SETTING_DNS2 = "8.8.4.4";
/**
* Wifi tethering setting.
*/
const TETHERING_SETTING_SSID = "FirefoxHotSpot";
const TETHERING_SETTING_SECURITY = "open";
const TETHERING_SETTING_KEY = "1234567890";
const SETTINGS_RIL_DATA_ENABLED = 'ril.data.enabled';
let Promise =
SpecialPowers.Cu.import("resource://gre/modules/Promise.jsm").Promise;
let gTestSuite = (function() {
let suite = {};
let tetheringManager;
let pendingEmulatorShellCount = 0;
/**
* A wrapper function of "is".
*
* Calls the marionette function "is" as well as throws an exception
* if the givens values are not equal.
*
* @param value1
* Any type of value to compare.
*
* @param value2
* Any type of value to compare.
*
* @param message
* Debug message for this check.
*
*/
function isOrThrow(value1, value2, message) {
is(value1, value2, message);
if (value1 !== value2) {
throw message;
}
}
/**
* Send emulator shell command with safe guard.
*
* We should only call |finish()| after all emulator command transactions
* end, so here comes with the pending counter. Resolve when the emulator
* gives positive response, and reject otherwise.
*
* Fulfill params:
* result -- an array of emulator response lines.
* Reject params:
* result -- an array of emulator response lines.
*
* @param aCommand
* A string command to be passed to emulator through its telnet console.
*
* @return A deferred promise.
*/
function runEmulatorShellSafe(aCommand) {
let deferred = Promise.defer();
++pendingEmulatorShellCount;
runEmulatorShell(aCommand, function(aResult) {
--pendingEmulatorShellCount;
ok(true, "Emulator shell response: " + JSON.stringify(aResult));
if (Array.isArray(aResult)) {
deferred.resolve(aResult);
} else {
deferred.reject(aResult);
}
});
return deferred.promise;
}
/**
* Wait for timeout.
*
* Resolve when the given duration elapsed. Never reject.
*
* Fulfill params: (none)
*
* @param aTimeoutMs
* The duration after which the timeout event should occurs.
*
* @return A deferred promise.
*/
function waitForTimeout(aTimeoutMs) {
let deferred = Promise.defer();
setTimeout(function() {
deferred.resolve();
}, aTimeoutMs);
return deferred.promise;
}
/**
* Get mozSettings value specified by @aKey.
*
* Resolve if that mozSettings value is retrieved successfully, reject
* otherwise.
*
* Fulfill params:
* The corresponding mozSettings value of the key.
* Reject params: (none)
*
* @param aKey
* A string.
*
* @return A deferred promise.
*/
function getSettings(aKey) {
let request = navigator.mozSettings.createLock().get(aKey);
return wrapDomRequestAsPromise(request)
.then(function resolve(aEvent) {
ok(true, "getSettings(" + aKey + ") - success");
return aEvent.target.result[aKey];
}, function reject(aEvent) {
ok(false, "getSettings(" + aKey + ") - error");
throw aEvent.target.error;
});
}
/**
* Set mozSettings values.
*
* Resolve if that mozSettings value is set successfully, reject otherwise.
*
* Fulfill params: (none)
* Reject params: (none)
*
* @param aSettings
* An object of format |{key1: value1, key2: value2, ...}|.
* @return A deferred promise.
*/
function setSettings(aSettings) {
let request = navigator.mozSettings.createLock().set(aSettings);
return wrapDomRequestAsPromise(request)
.then(function resolve() {
ok(true, "setSettings(" + JSON.stringify(aSettings) + ")");
}, function reject(aEvent) {
ok(false, "setSettings(" + JSON.stringify(aSettings) + ")");
throw aEvent.target.error;
});
}
/**
* Set mozSettings value with only one key.
*
* Resolve if that mozSettings value is set successfully, reject otherwise.
*
* Fulfill params: (none)
* Reject params: (none)
*
* @param aKey
* A string key.
* @param aValue
* An object value.
* @param aAllowError [optional]
* A boolean value. If set to true, an error response won't be treated
* as test failure. Default: false.
*
* @return A deferred promise.
*/
function setSettings1(aKey, aValue, aAllowError) {
let settings = {};
settings[aKey] = aValue;
return setSettings(settings, aAllowError);
}
/**
* Wrap DOMRequest onsuccess/onerror events to Promise resolve/reject.
*
* Fulfill params: A DOMEvent.
* Reject params: A DOMEvent.
*
* @param aRequest
* A DOMRequest instance.
*
* @return A deferred promise.
*/
function wrapDomRequestAsPromise(aRequest) {
let deffered = Promise.defer();
ok(aRequest instanceof DOMRequest,
"aRequest is instanceof" + aRequest.constructor);
aRequest.onsuccess = function(aEvent) {
deffered.resolve(aEvent);
};
aRequest.onerror = function(aEvent) {
deffered.reject(aEvent);
};
return deffered.promise;
}
/**
* Wait for one named MozMobileConnection event.
*
* Resolve if that named event occurs. Never reject.
*
* Fulfill params: the DOMEvent passed.
*
* @param aEventName
* A string event name.
*
* @return A deferred promise.
*/
function waitForMobileConnectionEventOnce(aEventName, aServiceId) {
aServiceId = aServiceId || 0;
let deferred = Promise.defer();
let mobileconnection = navigator.mozMobileConnections[aServiceId];
mobileconnection.addEventListener(aEventName, function onevent(aEvent) {
mobileconnection.removeEventListener(aEventName, onevent);
ok(true, "Mobile connection event '" + aEventName + "' got.");
deferred.resolve(aEvent);
});
return deferred.promise;
}
/**
* Wait for RIL data being connected.
*
* This function will check |MozMobileConnection.data.connected| on
* every 'datachange' event. Resolve when |MozMobileConnection.data.connected|
* becomes the expected state. Never reject.
*
* Fulfill params: (none)
* Reject params: (none)
*
* @param aConnected
* Boolean that indicates the desired data state.
*
* @param aServiceId [optional]
* A numeric DSDS service id. Default: 0.
*
* @return A deferred promise.
*/
function waitForRilDataConnected(aConnected, aServiceId) {
aServiceId = aServiceId || 0;
return waitForMobileConnectionEventOnce('datachange', aServiceId)
.then(function () {
let mobileconnection = navigator.mozMobileConnections[aServiceId];
if (mobileconnection.data.connected !== aConnected) {
return waitForRilDataConnected(aConnected, aServiceId);
}
});
}
/**
* Verify everything about routing when the wifi tethering is either on or off.
*
* We use two unix commands to verify the routing: 'netcfg' and 'ip route'.
* For now the following two things will be checked:
* 1) The default route interface should be 'rmnet0'.
* 2) wlan0 is up and its ip is set to a private subnet.
*
* We also verify iptables output as netd's NatController will execute
* $ iptables -t nat -A POSTROUTING -o rmnet0 -j MASQUERADE
*
* Resolve when the verification is successful and reject otherwise.
*
* Fulfill params: (none)
* Reject params: String that indicates the reason of rejection.
*
* @return A deferred promise.
*/
function verifyTetheringRouting(aEnabled) {
let netcfgResult = {};
let ipRouteResult = {};
// Execute 'netcfg' and parse to |netcfgResult|, each key of which is the
// interface name and value is { ip(string) }.
function exeAndParseNetcfg() {
return runEmulatorShellSafe(['netcfg'])
.then(function (aLines) {
// Sample output:
//
// lo UP 127.0.0.1/8 0x00000049 00:00:00:00:00:00
// eth0 UP 10.0.2.15/24 0x00001043 52:54:00:12:34:56
// rmnet1 DOWN 0.0.0.0/0 0x00001002 52:54:00:12:34:58
// rmnet2 DOWN 0.0.0.0/0 0x00001002 52:54:00:12:34:59
// rmnet3 DOWN 0.0.0.0/0 0x00001002 52:54:00:12:34:5a
// wlan0 UP 192.168.1.1/24 0x00001043 52:54:00:12:34:5b
// sit0 DOWN 0.0.0.0/0 0x00000080 00:00:00:00:00:00
// rmnet0 UP 10.0.2.100/24 0x00001043 52:54:00:12:34:57
//
aLines.forEach(function (aLine) {
let tokens = aLine.split(/\s+/);
if (tokens.length < 5) {
return;
}
let ifname = tokens[0];
let ip = (tokens[2].split('/'))[0];
netcfgResult[ifname] = { ip: ip };
});
});
}
// Execute 'ip route' and parse to |ipRouteResult|, each key of which is the
// interface name and value is { src(string), default(boolean) }.
function exeAndParseIpRoute() {
return runEmulatorShellSafe(['ip', 'route'])
.then(function (aLines) {
// Sample output:
//
// 10.0.2.4 via 10.0.2.2 dev rmnet0
// 10.0.2.3 via 10.0.2.2 dev rmnet0
// 192.168.1.0/24 dev wlan0 proto kernel scope link src 192.168.1.1
// 10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15
// 10.0.2.0/24 dev rmnet0 proto kernel scope link src 10.0.2.100
// default via 10.0.2.2 dev rmnet0
// default via 10.0.2.2 dev eth0 metric 2
//
// Parse source ip for each interface.
aLines.forEach(function (aLine) {
let tokens = aLine.trim().split(/\s+/);
let srcIndex = tokens.indexOf('src');
if (srcIndex < 0 || srcIndex + 1 >= tokens.length) {
return;
}
let ifname = tokens[2];
let src = tokens[srcIndex + 1];
ipRouteResult[ifname] = { src: src, default: false };
});
// Parse default interfaces.
aLines.forEach(function (aLine) {
let tokens = aLine.split(/\s+/);
if (tokens.length < 2) {
return;
}
if ('default' === tokens[0]) {
let ifnameIndex = tokens.indexOf('dev');
if (ifnameIndex < 0 || ifnameIndex + 1 >= tokens.length) {
return;
}
let ifname = tokens[ifnameIndex + 1];
if (ipRouteResult[ifname]) {
ipRouteResult[ifname].default = true;
}
return;
}
});
});
}
// Find MASQUERADE in POSTROUTING section. 'MASQUERADE' should be found
// when tethering is enabled. 'MASQUERADE' shouldn't be found when tethering
// is disabled.
function verifyIptables() {
return runEmulatorShellSafe(['iptables', '-t', 'nat', '-L', 'POSTROUTING'])
.then(function(aLines) {
// $ iptables -t nat -L POSTROUTING
//
// Sample output (tethering on):
//
// Chain POSTROUTING (policy ACCEPT)
// target prot opt source destination
// MASQUERADE all -- anywhere anywhere
//
let found = (function find_MASQUERADE() {
// Skip first two lines.
for (let i = 2; i < aLines.length; i++) {
if (-1 !== aLines[i].indexOf('MASQUERADE')) {
return true;
}
}
return false;
})();
if ((aEnabled && !found) || (!aEnabled && found)) {
throw 'MASQUERADE' + (found ? '' : ' not') + ' found while tethering is ' +
(aEnabled ? 'enabled' : 'disabled');
}
});
}
function verifyDefaultRouteAndIp(aExpectedWifiTetheringIp) {
log(JSON.stringify(ipRouteResult));
log(JSON.stringify(netcfgResult));
if (aEnabled) {
isOrThrow(ipRouteResult['rmnet0'].src, netcfgResult['rmnet0'].ip, 'rmnet0.ip');
isOrThrow(ipRouteResult['rmnet0'].default, true, 'rmnet0.default');
isOrThrow(ipRouteResult['wlan0'].src, netcfgResult['wlan0'].ip, 'wlan0.ip');
isOrThrow(ipRouteResult['wlan0'].src, aExpectedWifiTetheringIp, 'expected ip');
isOrThrow(ipRouteResult['wlan0'].default, false, 'wlan0.default');
}
}
return verifyIptables()
.then(exeAndParseNetcfg)
.then(exeAndParseIpRoute)
.then(() => verifyDefaultRouteAndIp(TETHERING_SETTING_IP));
}
/**
* Request to enable/disable wifi tethering.
*
* Enable/disable wifi tethering by using setTetheringEnabled API
* Resolve when the routing is verified to set up successfully in 20 seconds. The polling
* period is 1 second.
*
* Fulfill params: (none)
* Reject params: The error message.
*
* @param aEnabled
* Boolean that indicates to enable or disable wifi tethering.
*
* @return A deferred promise.
*/
function setWifiTetheringEnabled(aEnabled) {
let RETRY_INTERVAL_MS = 1000;
let retryCnt = 20;
let config = {
"ip" : TETHERING_SETTING_IP,
"prefix" : TETHERING_SETTNG_PREFIX,
"startIp" : TETHERING_SETTING_START_IP,
"endIp" : TETHERING_SETTING_END_IP,
"dns1" : TETHERING_SETTING_DNS1,
"dns2" : TETHERING_SETTING_DNS2,
"wifiConfig": {
"ssid" : TETHERING_SETTING_SSID,
"security" : TETHERING_SETTING_SECURITY
}
};
return tetheringManager.setTetheringEnabled(aEnabled, TYPE_WIFI, config)
.then(function waitForRoutingVerified() {
return verifyTetheringRouting(aEnabled)
.then(null, function onreject(aReason) {
log('verifyTetheringRouting rejected due to ' + aReason +
' (' + retryCnt + ')');
if (!retryCnt--) {
throw aReason;
}
return waitForTimeout(RETRY_INTERVAL_MS).then(waitForRoutingVerified);
});
});
}
/**
* Ensure wifi is enabled/disabled.
*
* Issue a wifi enable/disable request if wifi is not in the desired state;
* return a resolved promise otherwise.
*
* Fulfill params: (none)
* Reject params: (none)
*
* @return a resolved promise or deferred promise.
*/
function ensureWifiEnabled(aEnabled) {
let wifiManager = window.navigator.mozWifiManager;
if (wifiManager.enabled === aEnabled) {
return Promise.resolve();
}
let request = wifiManager.setWifiEnabled(aEnabled);
return wrapDomRequestAsPromise(request)
}
/**
* Ensure tethering manager exists.
*
* Check navigator property |mozTetheringManager| to ensure we could access
* tethering related function.
*
* Fulfill params: (none)
* Reject params: (none)
*
* @return A deferred promise.
*/
function ensureTetheringManager() {
let deferred = Promise.defer();
tetheringManager = window.navigator.mozTetheringManager;
if (tetheringManager instanceof MozTetheringManager) {
deferred.resolve();
} else {
log("navigator.mozTetheringManager is unavailable");
deferred.reject();
}
return deferred.promise;
}
/**
* Add required permissions for tethering. Never reject.
*
* The permissions required for wifi testing are 'wifi-manage' and 'settings-write'.
* Never reject.
*
* Fulfill params: (none)
*
* @return A deferred promise.
*/
function acquirePermission() {
let deferred = Promise.defer();
let permissions = [{ 'type': 'wifi-manage', 'allow': 1, 'context': window.document },
{ 'type': 'settings-write', 'allow': 1, 'context': window.document },
{ 'type': 'settings-read', 'allow': 1, 'context': window.document },
{ 'type': 'mobileconnection', 'allow': 1, 'context': window.document }];
SpecialPowers.pushPermissions(permissions, function() {
deferred.resolve();
});
return deferred.promise;
}
/**
* Common test routine.
*
* Start a test with the given test case chain. The test environment will be
* settled down before the test. After the test, all the affected things will
* be restored.
*
* Fulfill params: (none)
* Reject params: (none)
*
* @param aTestCaseChain
* The test case entry point, which can be a function or a promise.
*
* @return A deferred promise.
*/
suite.startTest = function(aTestCaseChain) {
function setUp() {
return ensureTetheringManager()
.then(acquirePermission);
}
function tearDown() {
waitFor(finish, function() {
return pendingEmulatorShellCount === 0;
});
}
return setUp()
.then(aTestCaseChain)
.then(function onresolve() {
tearDown();
}, function onreject(aReason) {
ok(false, 'Promise rejects during test' + (aReason ? '(' + aReason + ')' : ''));
tearDown();
});
};
//---------------------------------------------------
// Public test suite functions
//---------------------------------------------------
suite.ensureWifiEnabled = ensureWifiEnabled;
suite.setWifiTetheringEnabled = setWifiTetheringEnabled;
/**
* The common test routine for wifi tethering.
*
* Set 'ril.data.enabled' to true
* before testing and restore it afterward. It will also verify 'ril.data.enabled'
* and 'tethering.wifi.enabled' to be false in the beginning. Note that this routine
* will NOT change the state of 'tethering.wifi.enabled' so the user should enable
* than disable on his/her own. This routine will only check if tethering is turned
* off after testing.
*
* Fulfill params: (none)
* Reject params: (none)
*
* @param aTestCaseChain
* The test case entry point, which can be a function or a promise.
*
* @return A deferred promise.
*/
suite.startTetheringTest = function(aTestCaseChain) {
let oriDataEnabled;
function verifyInitialState() {
return getSettings(SETTINGS_RIL_DATA_ENABLED)
.then(enabled => initTetheringTestEnvironment(enabled));
}
function initTetheringTestEnvironment(aEnabled) {
oriDataEnabled = aEnabled;
if (aEnabled) {
return Promise.resolve();
} else {
return Promise.all([waitForRilDataConnected(true),
setSettings1(SETTINGS_RIL_DATA_ENABLED, true)]);
}
}
function restoreToInitialState() {
return setSettings1(SETTINGS_RIL_DATA_ENABLED, oriDataEnabled);
}
return suite.startTest(function() {
return verifyInitialState()
.then(aTestCaseChain)
.then(restoreToInitialState, function onreject(aReason) {
return restoreToInitialState()
.then(() => { throw aReason; }); // Re-throw the orignal reject reason.
});
});
};
return suite;
})();

View File

@ -0,0 +1,6 @@
[DEFAULT]
b2g = true
browser = false
qemu = true
[test_wifi_tethering_enabled.js]

View File

@ -0,0 +1,11 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
gTestSuite.startTetheringTest(function() {
return gTestSuite.ensureWifiEnabled(false)
.then(() => gTestSuite.setWifiTetheringEnabled(true));
});

View File

@ -172,7 +172,9 @@ function checkCookie() {
SpecialPowers.pushPrefEnv({set: [['network.cookie.cookieBehavior', 0]]}, () =>
SpecialPowers.autoConfirmAppInstall(() =>
navigator.mozApps.install(gManifestURL, null).onsuccess = onInstall
SpecialPowers.autoConfirmAppUninstall(() =>
navigator.mozApps.install(gManifestURL, null).onsuccess = onInstall
)
)
);
</script>

View File

@ -103,7 +103,9 @@ function onInstall() {
}
SpecialPowers.autoConfirmAppInstall(() =>
navigator.mozApps.install(gManifestURL, null).onsuccess = onInstall
SpecialPowers.autoConfirmAppUninstall(() =>
navigator.mozApps.install(gManifestURL, null).onsuccess = onInstall
)
);
</script>

View File

@ -353,6 +353,7 @@ var BrowserApp = {
Services.obs.addObserver(this, "webapps-runtime-install", false);
Services.obs.addObserver(this, "webapps-runtime-install-package", false);
Services.obs.addObserver(this, "webapps-ask-install", false);
Services.obs.addObserver(this, "webapps-ask-uninstall", false);
Services.obs.addObserver(this, "webapps-launch", false);
Services.obs.addObserver(this, "webapps-runtime-uninstall", false);
Services.obs.addObserver(this, "Webapps:AutoInstall", false);
@ -1707,6 +1708,10 @@ var BrowserApp = {
WebappManager.askInstall(JSON.parse(aData));
break;
case "webapps-ask-uninstall":
WebappManager.askUninstall(JSON.parse(aData));
break;
case "webapps-launch": {
WebappManager.launch(JSON.parse(aData));
break;

View File

@ -194,6 +194,12 @@ this.WebappManager = {
});
},
askUninstall: function(aData) {
// Android does not currently support automatic uninstalling of apps.
// See bug 1019054.
DOMApplicationRegistry.denyUninstall(aData, "NOT_SUPPORTED");
},
launch: function({ apkPackageName }) {
debug("launch: " + apkPackageName);
@ -633,8 +639,7 @@ this.WebappManager = {
let app = DOMApplicationRegistry.webapps[id];
if (aData.apkPackageNames.indexOf(app.apkPackageName) > -1) {
debug("attempting to uninstall " + app.name);
DOMApplicationRegistry.uninstall(
app.manifestURL,
DOMApplicationRegistry.uninstall(app.manifestURL).then(
function() {
debug("success uninstalling " + app.name);
},

View File

@ -28,6 +28,7 @@ skip = false
[include:../../../../../dom/events/test/marionette/manifest.ini]
[include:../../../../../dom/wifi/test/marionette/manifest.ini]
[include:../../../../../dom/cellbroadcast/tests/marionette/manifest.ini]
[include:../../../../../dom/tethering/tests/marionette/manifest.ini]
; layout tests
[include:../../../../../layout/base/tests/marionette/manifest.ini]

View File

@ -176,7 +176,7 @@ $(_DEST_DIR):
$(NSINSTALL) -D $@
stage-package:
$(NSINSTALL) -D $(PKG_STAGE)/mochitest && $(NSINSTALL) -D $(PKG_STAGE)/bin/plugins && $(NSINSTALL) -D $(DIST)/plugins && $(NSINSTALL) -D $(PKG_STAGE)/bin/plugins/gmp-fake
$(NSINSTALL) -D $(PKG_STAGE)/mochitest && $(NSINSTALL) -D $(PKG_STAGE)/bin/plugins && $(NSINSTALL) -D $(DIST)/plugins && $(NSINSTALL) -D $(PKG_STAGE)/bin/plugins/gmp-fake/1.0
cp $(DEPTH)/mozinfo.json $(PKG_STAGE)/mochitest
(cd $(DEPTH)/_tests/testing && tar $(TAR_CREATE_FLAGS) - mochitest) | (cd $(PKG_STAGE) && tar -xf -)
@cp $(DEPTH)/mozinfo.json $(PKG_STAGE)/mochitest
@ -184,4 +184,4 @@ stage-package:
@(cd $(DIST_BIN)/components && tar $(TAR_CREATE_FLAGS) - $(TEST_HARNESS_COMPONENTS)) | (cd $(PKG_STAGE)/bin/components && tar -xf -)
(cd $(topsrcdir)/build/pgo && tar $(TAR_CREATE_FLAGS) - certs) | (cd $(PKG_STAGE) && tar -xf -)
@(cd $(DIST)/plugins && tar $(TAR_CREATE_FLAGS) - $(TEST_HARNESS_PLUGINS)) | (cd $(PKG_STAGE)/bin/plugins && tar -xf -)
$(foreach x,$(GMP_TEST_FILES),cp $(x) $(PKG_STAGE)/bin/plugins/gmp-fake;)
$(foreach x,$(GMP_TEST_FILES),cp $(x) $(PKG_STAGE)/bin/plugins/gmp-fake/1.0;)

View File

@ -363,7 +363,7 @@ class MochitestRunner(MozbuildObject):
if options.gmp_path is None:
# Need to fix the location of gmp_fake which might not be shipped in the binary
bin_path = self.get_binary_path()
options.gmp_path = os.path.join(os.path.dirname(bin_path), 'gmp-fake')
options.gmp_path = os.path.join(os.path.dirname(bin_path), 'gmp-fake', '1.0')
logger_options = {key: value for key, value in vars(options).iteritems() if key.startswith('log')}

View File

@ -1166,12 +1166,12 @@ class Mochitest(MochitestUtilsMixin):
return options.gmp_path
# For local builds, gmp-fake will be under dist/bin.
gmp_path = os.path.join(options.xrePath, 'gmp-fake')
gmp_path = os.path.join(options.xrePath, 'gmp-fake', '1.0')
if os.path.isdir(gmp_path):
return gmp_path
# For packaged builds, gmp-fake will get copied under $profile/plugins.
gmp_path = os.path.join(self.profile.profile, 'plugins', 'gmp-fake')
gmp_path = os.path.join(self.profile.profile, 'plugins', 'gmp-fake', '1.0')
if os.path.isdir(gmp_path):
return gmp_path
# This is fatal for desktop environments.

View File

@ -1077,6 +1077,10 @@ SpecialPowersAPI.prototype = {
this.pushPrefEnv({set: [['dom.mozApps.auto_confirm_install', true]]}, cb);
},
autoConfirmAppUninstall: function(cb) {
this.pushPrefEnv({set: [['dom.mozApps.auto_confirm_uninstall', true]]}, cb);
},
// Allow tests to disable the per platform app validity checks so we can
// test higher level WebApp functionality without full platform support.
setAllAppsLaunchable: function(launchable) {

View File

@ -12,6 +12,11 @@ const { ActorPool, appendExtraActors, createExtraActors } = require("devtools/se
const { DebuggerServer } = require("devtools/server/main");
const { dumpProtocolSpec } = require("devtools/server/protocol");
const makeDebugger = require("./utils/make-debugger");
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
DevToolsUtils.defineLazyGetter(this, "StyleSheetActor", () => {
return require("devtools/server/actors/stylesheets").StyleSheetActor;
});
/* Root actor for the remote debugging protocol. */
@ -94,6 +99,9 @@ function RootActor(aConnection, aParameters) {
this._onAddonListChanged = this.onAddonListChanged.bind(this);
this._extraActors = {};
// Map of DOM stylesheets to StyleSheetActors
this._styleSheetActors = new Map();
// This creates a Debugger instance for chrome debugging all globals.
this.makeDebugger = makeDebugger.bind(null, {
findDebuggees: dbg => dbg.findAllGlobals(),
@ -226,6 +234,8 @@ RootActor.prototype = {
this._parameters.onShutdown();
}
this._extraActors = null;
this._styleSheetActors.clear();
this._styleSheetActors = null;
},
/* The 'listTabs' request and the 'tabListChanged' notification. */
@ -387,6 +397,28 @@ RootActor.prototype = {
windowUtils.resumeTimeouts();
windowUtils.suppressEventHandling(false);
}
},
/**
* Create or return the StyleSheetActor for a style sheet. This method
* is here because the Style Editor and Inspector share style sheet actors.
*
* @param DOMStyleSheet styleSheet
* The style sheet to creat an actor for.
* @return StyleSheetActor actor
* The actor for this style sheet.
*
*/
createStyleSheetActor: function(styleSheet) {
if (this._styleSheetActors.has(styleSheet)) {
return this._styleSheetActors.get(styleSheet);
}
let actor = new StyleSheetActor(styleSheet, this);
this._styleSheetActors.set(styleSheet, actor);
this._globalActorPool.addActor(actor);
return actor;
}
};

View File

@ -12,7 +12,9 @@ const {Arg, Option, method, RetVal, types} = protocol;
const events = require("sdk/event/core");
const object = require("sdk/util/object");
const { Class } = require("sdk/core/heritage");
const { StyleSheetActor } = require("devtools/server/actors/stylesheets");
// This will add the "stylesheet" actor type for protocol.js to recognize
require("devtools/server/actors/stylesheets");
loader.lazyGetter(this, "CssLogic", () => require("devtools/styleinspector/css-logic").CssLogic);
loader.lazyGetter(this, "DOMUtils", () => Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils));
@ -112,17 +114,15 @@ var PageStyleActor = protocol.ActorClass({
},
/**
* Return or create a StyleSheetActor for the given
* nsIDOMCSSStyleSheet
* Return or create a StyleSheetActor for the given nsIDOMCSSStyleSheet.
* @param {DOMStyleSheet} sheet
* The style sheet to create an actor for.
* @return {StyleSheetActor}
* The actor for this style sheet
*/
_sheetRef: function(sheet) {
if (this.refMap.has(sheet)) {
return this.refMap.get(sheet);
}
let actor = new StyleSheetActor(sheet, this, this.walker.rootWin);
this.manage(actor);
this.refMap.set(sheet, actor);
let tabActor = this.inspector.tabActor;
let actor = tabActor.createStyleSheetActor(sheet);
return actor;
},
@ -975,6 +975,7 @@ var StyleRuleFront = protocol.FrontClass(StyleRuleActor, {
get location()
{
return {
source: this.parentStyleSheet,
href: this.href,
line: this.line,
column: this.column
@ -992,12 +993,15 @@ var StyleRuleFront = protocol.FrontClass(StyleRuleActor, {
return promise.resolve(this.location);
}
return parentSheet.getOriginalLocation(this.line, this.column)
.then(({ source, line, column }) => {
.then(({ fromSourceMap, source, line, column }) => {
let location = {
href: source,
line: line,
column: column
}
if (fromSourceMap === false) {
location.source = this.parentStyleSheet;
}
if (!source) {
location.href = this.href;
}

View File

@ -74,17 +74,6 @@ let StyleSheetsActor = protocol.ActorClass({
protocol.Actor.prototype.initialize.call(this, null);
this.parentActor = tabActor;
// keep a map of sheets-to-actors so we don't create two actors for one sheet
this._sheets = new Map();
},
/**
* Destroy the current StyleSheetsActor instance.
*/
destroy: function()
{
this._sheets.clear();
},
/**
@ -156,7 +145,7 @@ let StyleSheetsActor = protocol.ActorClass({
return Task.spawn(function() {
let actors = [];
for (let i = 0; i < styleSheets.length; i++) {
let actor = this._createStyleSheetActor(styleSheets[i]);
let actor = this.parentActor.createStyleSheetActor(styleSheets[i]);
actors.push(actor);
// Get all sheets, including imported ones
@ -188,7 +177,7 @@ let StyleSheetsActor = protocol.ActorClass({
if (!rule.styleSheet) {
continue;
}
let actor = this._createStyleSheetActor(rule.styleSheet);
let actor = this.parentActor.createStyleSheetActor(rule.styleSheet);
imported.push(actor);
// recurse imports in this stylesheet as well
@ -205,36 +194,6 @@ let StyleSheetsActor = protocol.ActorClass({
}.bind(this));
},
/**
* Create a new actor for a style sheet, if it hasn't already been created.
*
* @param {DOMStyleSheet} styleSheet
* The style sheet to create an actor for.
* @return {StyleSheetActor}
* The actor for this style sheet
*/
_createStyleSheetActor: function(styleSheet)
{
if (this._sheets.has(styleSheet)) {
return this._sheets.get(styleSheet);
}
let actor = new StyleSheetActor(styleSheet, this);
this.manage(actor);
this._sheets.set(styleSheet, actor);
return actor;
},
/**
* Clear all the current stylesheet actors in map.
*/
_clearStyleSheetActors: function() {
for (let actor in this._sheets) {
this.unmanage(this._sheets[actor]);
}
this._sheets.clear();
},
/**
* Create a new style sheet in the document with the given text.
@ -255,7 +214,7 @@ let StyleSheetsActor = protocol.ActorClass({
}
parent.appendChild(style);
let actor = this._createStyleSheetActor(style.sheet);
let actor = this.parentActor.createStyleSheetActor(style.sheet);
return actor;
}, {
request: { text: Arg(0, "string") },
@ -323,6 +282,8 @@ let MediaRuleActor = protocol.ActorClass({
if (this.mql) {
this.mql.removeListener(this._matchesChange);
}
protocol.Actor.prototype.destroy.call(this);
},
form: function(detail) {
@ -420,16 +381,6 @@ let StyleSheetActor = protocol.ActorClass({
*/
get document() this.window.document,
/**
* Browser for the target.
*/
get browser() {
if (this.parentActor.parentActor) {
return this.parentActor.parentActor.browser;
}
return null;
},
get ownerNode() this.rawSheet.ownerNode,
/**
@ -768,6 +719,7 @@ let StyleSheetActor = protocol.ActorClass({
return sourceMap.originalPositionFor({ line: line, column: column });
}
return {
fromSourceMap: false,
source: this.href,
line: line,
column: column
@ -998,7 +950,6 @@ var StyleSheetFront = protocol.FrontClass(StyleSheetActor, {
destroy: function() {
events.off(this, "property-change", this._onPropertyChange);
protocol.Front.prototype.destroy.call(this);
},

View File

@ -705,23 +705,11 @@ WebappsActor.prototype = {
let manifestURL = aRequest.manifestURL;
if (!manifestURL) {
return { error: "missingParameter",
message: "missing parameter manifestURL" };
return Promise.resolve({ error: "missingParameter",
message: "missing parameter manifestURL" });
}
let deferred = promise.defer();
let reg = DOMApplicationRegistry;
reg.uninstall(
manifestURL,
function onsuccess() {
deferred.resolve({});
},
function onfailure(reason) {
deferred.resolve({ error: reason });
}
);
return deferred.promise;
return DOMApplicationRegistry.uninstall(manifestURL);
},
_findManifestByURL: function wa__findManifestByURL(aManifestURL) {

View File

@ -29,6 +29,10 @@ XPCOMUtils.defineLazyGetter(this, "events", () => {
return require("sdk/event/core");
});
XPCOMUtils.defineLazyGetter(this, "StyleSheetActor", () => {
return require("devtools/server/actors/stylesheets").StyleSheetActor;
});
// Also depends on following symbols, shared by common scope with main.js:
// DebuggerServer, CommonCreateExtraActors, CommonAppendExtraActors, ActorPool,
// ThreadActor
@ -545,6 +549,9 @@ function TabActor(aConnection)
this._extraActors = {};
this._exited = false;
// Map of DOM stylesheets to StyleSheetActors
this._styleSheetActors = new Map();
this._shouldAddNewGlobalAsDebuggee = this._shouldAddNewGlobalAsDebuggee.bind(this);
this.makeDebugger = makeDebugger.bind(null, {
@ -722,6 +729,7 @@ TabActor.prototype = {
disconnect: function BTA_disconnect() {
this._detach();
this._extraActors = null;
this._styleSheetActors.clear();
this._exited = true;
},
@ -1059,6 +1067,12 @@ TabActor.prototype = {
}
}
for (let sheetActor of this._styleSheetActors.values()) {
this._tabPool.removeActor(sheetActor);
}
this._styleSheetActors.clear();
// Refresh the debuggee list when a new window object appears (top window or
// iframe).
if (threadActor.attached) {
@ -1175,6 +1189,28 @@ TabActor.prototype = {
}
catch (ex) { }
return isNative;
},
/**
* Create or return the StyleSheetActor for a style sheet. This method
* is here because the Style Editor and Inspector share style sheet actors.
*
* @param DOMStyleSheet styleSheet
* The style sheet to creat an actor for.
* @return StyleSheetActor actor
* The actor for this style sheet.
*
*/
createStyleSheetActor: function BTA_createStyleSheetActor(styleSheet) {
if (this._styleSheetActors.has(styleSheet)) {
return this._styleSheetActors.get(styleSheet);
}
let actor = new StyleSheetActor(styleSheet, this);
this._styleSheetActors.set(styleSheet, actor);
this._tabPool.addActor(actor);
return actor;
}
};

View File

@ -107,7 +107,6 @@ let GMPPrefs = {
*/
KEY_LOG_ENABLED: "media.gmp-manager.log",
KEY_ADDON_LAST_UPDATE: "media.{0}.lastUpdate",
KEY_ADDON_PATH: "media.{0}.path",
KEY_ADDON_VERSION: "media.{0}.version",
KEY_ADDON_AUTOUPDATE: "media.{0}.autoupdate",
KEY_URL: "media.gmp-manager.url",
@ -889,7 +888,9 @@ GMPDownloader.prototype = {
let gmpAddon = this._gmpAddon;
let installToDirPath = Cc["@mozilla.org/file/local;1"].
createInstance(Ci.nsIFile);
let path = OS.Path.join(OS.Constants.Path.profileDir, gmpAddon.id);
let path = OS.Path.join(OS.Constants.Path.profileDir,
gmpAddon.id,
gmpAddon.version);
installToDirPath.initWithPath(path);
log.info("install to directory path: " + installToDirPath.path);
let gmpInstaller = new GMPExtractor(zipPath, installToDirPath.path);
@ -898,12 +899,10 @@ GMPDownloader.prototype = {
// Success, set the prefs
let now = Math.round(Date.now() / 1000);
GMPPrefs.set(GMPPrefs.KEY_ADDON_LAST_UPDATE, now, gmpAddon.id);
// Setting the path pref signals installation completion to consumers,
// so set the version and potential other information they use first.
// Setting the version pref signals installation completion to consumers,
// if you need to set other prefs etc. do it before this.
GMPPrefs.set(GMPPrefs.KEY_ADDON_VERSION, gmpAddon.version,
gmpAddon.id);
GMPPrefs.set(GMPPrefs.KEY_ADDON_PATH,
installToDirPath.path, gmpAddon.id);
this._deferred.resolve(extractedPaths);
}, err => {
this._deferred.reject(err);

View File

@ -29,11 +29,9 @@ add_test(function test_prefs() {
GMPPrefs.set(GMPPrefs.KEY_URL, "http://not-really-used");
GMPPrefs.set(GMPPrefs.KEY_URL_OVERRIDE, "http://not-really-used-2");
GMPPrefs.set(GMPPrefs.KEY_ADDON_LAST_UPDATE, "1", addon1);
GMPPrefs.set(GMPPrefs.KEY_ADDON_PATH, "2", addon1);
GMPPrefs.set(GMPPrefs.KEY_ADDON_VERSION, "3", addon1);
GMPPrefs.set(GMPPrefs.KEY_ADDON_LAST_UPDATE, "4", addon2);
GMPPrefs.set(GMPPrefs.KEY_ADDON_PATH, "5", addon2);
GMPPrefs.set(GMPPrefs.KEY_ADDON_VERSION, "6", addon2);
GMPPrefs.set(GMPPrefs.KEY_ADDON_VERSION, "2", addon1);
GMPPrefs.set(GMPPrefs.KEY_ADDON_LAST_UPDATE, "3", addon2);
GMPPrefs.set(GMPPrefs.KEY_ADDON_VERSION, "4", addon2);
GMPPrefs.set(GMPPrefs.KEY_ADDON_AUTOUPDATE, false, addon2);
GMPPrefs.set(GMPPrefs.KEY_CERT_CHECKATTRS, true);
@ -41,11 +39,9 @@ add_test(function test_prefs() {
do_check_eq(GMPPrefs.get(GMPPrefs.KEY_URL), "http://not-really-used");
do_check_eq(GMPPrefs.get(GMPPrefs.KEY_URL_OVERRIDE), "http://not-really-used-2");
do_check_eq(GMPPrefs.get(GMPPrefs.KEY_ADDON_LAST_UPDATE, addon1), "1");
do_check_eq(GMPPrefs.get(GMPPrefs.KEY_ADDON_PATH, addon1), "2");
do_check_eq(GMPPrefs.get(GMPPrefs.KEY_ADDON_VERSION, addon1), "3");
do_check_eq(GMPPrefs.get(GMPPrefs.KEY_ADDON_LAST_UPDATE, addon2), "4");
do_check_eq(GMPPrefs.get(GMPPrefs.KEY_ADDON_PATH, addon2), "5");
do_check_eq(GMPPrefs.get(GMPPrefs.KEY_ADDON_VERSION, addon2), "6");
do_check_eq(GMPPrefs.get(GMPPrefs.KEY_ADDON_VERSION, addon1), "2");
do_check_eq(GMPPrefs.get(GMPPrefs.KEY_ADDON_LAST_UPDATE, addon2), "3");
do_check_eq(GMPPrefs.get(GMPPrefs.KEY_ADDON_VERSION, addon2), "4");
do_check_eq(GMPPrefs.get(GMPPrefs.KEY_ADDON_AUTOUPDATE, addon2), false);
do_check_true(GMPPrefs.get(GMPPrefs.KEY_CERT_CHECKATTRS));
GMPPrefs.set(GMPPrefs.KEY_ADDON_AUTOUPDATE, true, addon2);
@ -435,7 +431,7 @@ function test_checkForAddons_installAddon(id, includeSize,wantInstallReject) {
let readData = readStringFromFile(extractedFile);
do_check_eq(readData, data);
// Check that the downloaded zip mathces the offered zip exactly
// Check that the downloaded zip matches the offered zip exactly
let downloadedGMPFile = FileUtils.getFile("TmpD",
[gmpAddon.id + ".zip"]);
do_check_true(downloadedGMPFile.exists());
@ -446,8 +442,6 @@ function test_checkForAddons_installAddon(id, includeSize,wantInstallReject) {
// Make sure the prefs are set correctly
do_check_true(!!GMPPrefs.get(GMPPrefs.KEY_ADDON_LAST_UPDATE,
gmpAddon.id, ""));
do_check_eq(GMPPrefs.get(GMPPrefs.KEY_ADDON_PATH, gmpAddon.id, ""),
extractedFile.parent.path);
do_check_eq(GMPPrefs.get(GMPPrefs.KEY_ADDON_VERSION, gmpAddon.id, ""),
"1.1");
// Make sure it reports as being installed

View File

@ -29,7 +29,6 @@ const SEC_IN_A_DAY = 24 * 60 * 60;
const OPENH264_PLUGIN_ID = "gmp-gmpopenh264";
const OPENH264_PREF_BRANCH = "media." + OPENH264_PLUGIN_ID + ".";
const OPENH264_PREF_ENABLED = "enabled";
const OPENH264_PREF_PATH = "path";
const OPENH264_PREF_VERSION = "version";
const OPENH264_PREF_LASTUPDATE = "lastUpdate";
const OPENH264_PREF_AUTOUPDATE = "autoupdate";
@ -94,12 +93,7 @@ let OpenH264Wrapper = {
get description() { return pluginsBundle.GetStringFromName("openH264_description"); },
get version() {
if (this.isInstalled) {
return prefs.get(OPENH264_PREF_VERSION, "");
}
return "";
},
get version() { return prefs.get(OPENH264_PREF_VERSION, ""); },
get isActive() { return !this.userDisabled; },
get appDisabled() { return false; },
@ -223,17 +217,24 @@ let OpenH264Wrapper = {
get pluginMimeTypes() { return []; },
get pluginLibraries() {
let path = prefs.get(OPENH264_PREF_PATH, null);
return path && path.length ? [OS.Path.basename(path)] : [];
if (this.isInstalled) {
let path = this.version;
return [path];
}
return [];
},
get pluginFullpath() {
let path = prefs.get(OPENH264_PREF_PATH, null);
return path && path.length ? [path] : [];
if (this.isInstalled) {
let path = OS.Path.join(OS.Constants.Path.profileDir,
OPENH264_PLUGIN_ID,
this.version);
return [path];
}
return [];
},
get isInstalled() {
let path = prefs.get(OPENH264_PREF_PATH, "");
return path.length > 0;
return this.version.length > 0;
},
};
@ -244,14 +245,19 @@ let OpenH264Provider = {
"OpenH264Provider" + "::");
OpenH264Wrapper._log = Log.repository.getLoggerWithMessagePrefix("Toolkit.OpenH264Provider",
"OpenH264Wrapper" + "::");
this.gmpPath = prefs.get(OPENH264_PREF_PATH, null);
this.gmpPath = null;
if (OpenH264Wrapper.isInstalled) {
this.gmpPath = OS.Path.join(OS.Constants.Path.profileDir,
OPENH264_PLUGIN_ID,
prefs.get(OPENH264_PREF_VERSION, null));
}
let enabled = prefs.get(OPENH264_PREF_ENABLED, true);
this._log.trace("startup() - enabled=" + enabled + ", gmpPath="+this.gmpPath);
Services.obs.addObserver(this, AddonManager.OPTIONS_NOTIFICATION_DISPLAYED, false);
prefs.observe(OPENH264_PREF_ENABLED, this.onPrefEnabledChanged, this);
prefs.observe(OPENH264_PREF_PATH, this.onPrefPathChanged, this);
prefs.observe(OPENH264_PREF_VERSION, this.onPrefVersionChanged, this);
prefs.observe(OPENH264_PREF_LOGGING, configureLogging);
if (this.gmpPath && enabled) {
@ -264,7 +270,7 @@ let OpenH264Provider = {
this._log.trace("shutdown()");
Services.obs.removeObserver(this, AddonManager.OPTIONS_NOTIFICATION_DISPLAYED);
prefs.ignore(OPENH264_PREF_ENABLED, this.onPrefEnabledChanged, this);
prefs.ignore(OPENH264_PREF_PATH, this.onPrefPathChanged, this);
prefs.ignore(OPENH264_PREF_VERSION, this.onPrefVersionChanged, this);
prefs.ignore(OPENH264_PREF_LOGGING, configureLogging);
return OpenH264Wrapper._updateTask;
@ -290,20 +296,25 @@ let OpenH264Provider = {
wrapper);
},
onPrefPathChanged: function() {
onPrefVersionChanged: function() {
let wrapper = OpenH264Wrapper;
AddonManagerPrivate.callAddonListeners("onUninstalling", wrapper, false);
if (this.gmpPath) {
this._log.info("onPrefPathChanged() - removing gmp directory " + this.gmpPath);
this._log.info("onPrefVersionChanged() - unregistering gmp directory " + this.gmpPath);
gmpService.removePluginDirectory(this.gmpPath);
}
AddonManagerPrivate.callAddonListeners("onUninstalled", wrapper);
AddonManagerPrivate.callInstallListeners("onExternalInstall", null, wrapper, null, false);
this.gmpPath = prefs.get(OPENH264_PREF_PATH, null);
this.gmpPath = null;
if (OpenH264Wrapper.isInstalled) {
this.gmpPath = OS.Path.join(OS.Constants.Path.profileDir,
OPENH264_PLUGIN_ID,
prefs.get(OPENH264_PREF_VERSION, null));
}
if (this.gmpPath && wrapper.isActive) {
this._log.info("onPrefPathChanged() - adding gmp directory " + this.gmpPath);
this._log.info("onPrefVersionChanged() - registering gmp directory " + this.gmpPath);
gmpService.addPluginDirectory(this.gmpPath);
}
AddonManagerPrivate.callAddonListeners("onInstalled", wrapper);

View File

@ -11,7 +11,6 @@ let OpenH264Scope = Cu.import("resource://gre/modules/addons/OpenH264Provider.js
const OPENH264_PLUGIN_ID = "gmp-gmpopenh264";
const OPENH264_PREF_BRANCH = "media." + OPENH264_PLUGIN_ID + ".";
const OPENH264_PREF_ENABLED = OPENH264_PREF_BRANCH + "enabled";
const OPENH264_PREF_PATH = OPENH264_PREF_BRANCH + "path";
const OPENH264_PREF_VERSION = OPENH264_PREF_BRANCH + "version";
const OPENH264_PREF_LASTUPDATE = OPENH264_PREF_BRANCH + "lastUpdate";
const OPENH264_PREF_AUTOUPDATE = OPENH264_PREF_BRANCH + "autoupdate";
@ -99,7 +98,6 @@ add_task(function* initializeState() {
Services.obs.removeObserver(gOptionsObserver, AddonManager.OPTIONS_NOTIFICATION_DISPLAYED);
Services.prefs.clearUserPref(OPENH264_PREF_ENABLED);
Services.prefs.clearUserPref(OPENH264_PREF_PATH);
Services.prefs.clearUserPref(OPENH264_PREF_VERSION);
Services.prefs.clearUserPref(OPENH264_PREF_LASTUPDATE);
Services.prefs.clearUserPref(OPENH264_PREF_AUTOUPDATE);
@ -116,10 +114,9 @@ add_task(function* initializeState() {
// Start out with OpenH264 not being installed, disabled and automatic updates disabled.
Services.prefs.setBoolPref(OPENH264_PREF_ENABLED, false);
Services.prefs.setCharPref(OPENH264_PREF_VERSION, "");
Services.prefs.setCharPref(OPENH264_PREF_LASTUPDATE, "");
Services.prefs.setIntPref (OPENH264_PREF_LASTUPDATE, 0);
Services.prefs.setBoolPref(OPENH264_PREF_AUTOUPDATE, false);
Services.prefs.setCharPref(OPENH264_PREF_PATH, "");
Services.prefs.setCharPref(OPENH264_PREF_VERSION, "");
});
add_task(function* testNotInstalled() {
@ -162,10 +159,9 @@ add_task(function* testNotInstalledDetails() {
add_task(function* testInstalled() {
Services.prefs.setBoolPref(OPENH264_PREF_ENABLED, true);
Services.prefs.setBoolPref(OPENH264_PREF_VERSION, "1.2.3.4");
Services.prefs.setBoolPref(OPENH264_PREF_LASTUPDATE, "" + TEST_DATE.getTime());
Services.prefs.setIntPref (OPENH264_PREF_LASTUPDATE, TEST_DATE.getTime());
Services.prefs.setBoolPref(OPENH264_PREF_AUTOUPDATE, false);
Services.prefs.setCharPref(OPENH264_PREF_PATH, "foo/bar");
Services.prefs.setCharPref(OPENH264_PREF_VERSION, "1.2.3.4");
yield gCategoryUtilities.openType("plugin");
@ -204,20 +200,17 @@ add_task(function* testInstalledDetails() {
});
add_task(function* testPreferencesButton() {
let file = Services.dirsvc.get("ProfD", Ci.nsIFile);
file.append("openh264");
file.append("testDir");
let prefValues = [
{ enabled: false, path: "" },
{ enabled: false, path: file.path },
{ enabled: true, path: "" },
{ enabled: true, path: file.path },
{ enabled: false, version: "" },
{ enabled: false, version: "1.2.3.4" },
{ enabled: true, version: "" },
{ enabled: true, version: "1.2.3.4" },
];
for (let prefs of prefValues) {
dump("Testing preferences button with pref settings: " + JSON.stringify(prefs) + "\n");
Services.prefs.setCharPref(OPENH264_PREF_PATH, prefs.path);
Services.prefs.setCharPref(OPENH264_PREF_VERSION, prefs.version);
Services.prefs.setBoolPref(OPENH264_PREF_ENABLED, prefs.enabled);
yield gCategoryUtilities.openType("plugin");

View File

@ -8,7 +8,6 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
const OPENH264_PLUGIN_ID = "gmp-gmpopenh264";
const OPENH264_PREF_BRANCH = "media." + OPENH264_PLUGIN_ID + ".";
const OPENH264_PREF_ENABLED = OPENH264_PREF_BRANCH + "enabled";
const OPENH264_PREF_PATH = OPENH264_PREF_BRANCH + "path";
const OPENH264_PREF_VERSION = OPENH264_PREF_BRANCH + "version";
const OPENH264_PREF_LASTUPDATE = OPENH264_PREF_BRANCH + "lastUpdate";
const OPENH264_PREF_AUTOUPDATE = OPENH264_PREF_BRANCH + "autoupdate";
@ -55,7 +54,7 @@ function run_test() {
}
add_task(function* test_notInstalled() {
Services.prefs.setCharPref(OPENH264_PREF_PATH, "");
Services.prefs.setCharPref(OPENH264_PREF_VERSION, "");
Services.prefs.setBoolPref(OPENH264_PREF_ENABLED, false);
let addons = yield promiseAddonsByIDs([OPENH264_PLUGIN_ID]);
@ -106,13 +105,12 @@ add_task(function* test_installed() {
const TEST_TIME_SEC = Math.round(TEST_DATE.getTime() / 1000);
let file = Services.dirsvc.get("ProfD", Ci.nsIFile);
file.append("openh264");
file.append("testDir");
file.append(OPENH264_PLUGIN_ID);
file.append(TEST_VERSION);
Services.prefs.setBoolPref(OPENH264_PREF_ENABLED, false);
Services.prefs.setCharPref(OPENH264_PREF_LASTUPDATE, "" + TEST_TIME_SEC);
Services.prefs.setCharPref(OPENH264_PREF_VERSION, TEST_VERSION);
Services.prefs.setCharPref(OPENH264_PREF_PATH, file.path);
let addons = yield promiseAddonsByIDs([OPENH264_PLUGIN_ID]);
Assert.equal(addons.length, 1);
@ -139,7 +137,7 @@ add_task(function* test_installed() {
let libraries = addon.pluginLibraries;
Assert.ok(libraries);
Assert.equal(libraries.length, 1);
Assert.equal(libraries[0], "testDir");
Assert.equal(libraries[0], TEST_VERSION);
let fullpath = addon.pluginFullpath;
Assert.equal(fullpath.length, 1);
Assert.equal(fullpath[0], file.path);
@ -179,9 +177,11 @@ add_task(function* test_autoUpdatePrefPersistance() {
});
add_task(function* test_pluginRegistration() {
const TEST_VERSION = "1.2.3.4";
let file = Services.dirsvc.get("ProfD", Ci.nsIFile);
file.append("openh264");
file.append("testDir");
file.append(OPENH264_PLUGIN_ID);
file.append(TEST_VERSION);
let addedPath = null
let removedPath = null;
@ -197,35 +197,37 @@ add_task(function* test_pluginRegistration() {
Services.prefs.setBoolPref(OPENH264_PREF_ENABLED, true);
// Check that the OpenH264 plugin gets registered after startup.
Services.prefs.setCharPref(OPENH264_PREF_PATH, file.path);
Services.prefs.setCharPref(OPENH264_PREF_VERSION, TEST_VERSION);
clearPaths();
yield promiseRestartManager();
Assert.equal(addedPath, file.path);
Assert.equal(removedPath, null);
// Check that clearing the path doesn't trigger registration.
// Check that clearing the version doesn't trigger registration.
clearPaths();
Services.prefs.clearUserPref(OPENH264_PREF_PATH);
Services.prefs.clearUserPref(OPENH264_PREF_VERSION);
Assert.equal(addedPath, null);
Assert.equal(removedPath, file.path);
// Restarting with no path set should not trigger registration.
// Restarting with no version set should not trigger registration.
clearPaths();
yield promiseRestartManager();
Assert.equal(addedPath, null);
Assert.equal(removedPath, null);
// Changing the pref mid-session should cause unregistration and registration.
Services.prefs.setCharPref(OPENH264_PREF_PATH, file.path);
Services.prefs.setCharPref(OPENH264_PREF_VERSION, TEST_VERSION);
clearPaths();
let file2 = file.clone();
file2.append("foo");
Services.prefs.setCharPref(OPENH264_PREF_PATH, file2.path);
const TEST_VERSION_2 = "5.6.7.8";
let file2 = Services.dirsvc.get("ProfD", Ci.nsIFile);
file2.append(OPENH264_PLUGIN_ID);
file2.append(TEST_VERSION_2);
Services.prefs.setCharPref(OPENH264_PREF_VERSION, TEST_VERSION_2);
Assert.equal(addedPath, file2.path);
Assert.equal(removedPath, file.path);
// Disabling OpenH264 should cause unregistration.
Services.prefs.setCharPref(OPENH264_PREF_PATH, file.path);
Services.prefs.setCharPref(OPENH264_PREF_VERSION, TEST_VERSION);
clearPaths();
Services.prefs.setBoolPref(OPENH264_PREF_ENABLED, false);
Assert.equal(addedPath, null);

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