Merge m-c to inbound, a=merge

This commit is contained in:
Wes Kocher 2015-09-15 17:20:16 -07:00
commit 7b3f9b8da9
79 changed files with 1666 additions and 725 deletions

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d2e5c49440bf8410ae747b15c0dd11c54053ef3e"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="994ff1537c2d7ca4d1658806c50f3ceba1053f9b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d2e5c49440bf8410ae747b15c0dd11c54053ef3e"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="994ff1537c2d7ca4d1658806c50f3ceba1053f9b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

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="d2e5c49440bf8410ae747b15c0dd11c54053ef3e"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="994ff1537c2d7ca4d1658806c50f3ceba1053f9b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="27eb2f04e149fc2c9976d881b1b5984bbe7ee089"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d2e5c49440bf8410ae747b15c0dd11c54053ef3e"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="994ff1537c2d7ca4d1658806c50f3ceba1053f9b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="fb28def18ceb2516c460c4bd5825d2dc656c7818"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d2e5c49440bf8410ae747b15c0dd11c54053ef3e"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="994ff1537c2d7ca4d1658806c50f3ceba1053f9b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d2e5c49440bf8410ae747b15c0dd11c54053ef3e"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="994ff1537c2d7ca4d1658806c50f3ceba1053f9b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

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="d2e5c49440bf8410ae747b15c0dd11c54053ef3e"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="994ff1537c2d7ca4d1658806c50f3ceba1053f9b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="27eb2f04e149fc2c9976d881b1b5984bbe7ee089"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d2e5c49440bf8410ae747b15c0dd11c54053ef3e"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="994ff1537c2d7ca4d1658806c50f3ceba1053f9b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "d2e5c49440bf8410ae747b15c0dd11c54053ef3e",
"git_revision": "994ff1537c2d7ca4d1658806c50f3ceba1053f9b",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "4b08f8a2624bc83d1f839f79b4969671417c42d6",
"revision": "9090c80639ae3689dddbefb8a76ba82c1268b63a",
"repo_path": "integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d2e5c49440bf8410ae747b15c0dd11c54053ef3e"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="994ff1537c2d7ca4d1658806c50f3ceba1053f9b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="fb28def18ceb2516c460c4bd5825d2dc656c7818"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d2e5c49440bf8410ae747b15c0dd11c54053ef3e"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="994ff1537c2d7ca4d1658806c50f3ceba1053f9b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -269,7 +269,7 @@ var gTests = [
let indicator = promiseIndicatorWindow();
yield promiseMessage("ok", () => {
PopupNotifications.panel.firstChild.button.click();
activateSecondaryAction(kActionAlways);
});
expectObserverCalled("getUserMedia:response:allow");
expectObserverCalled("recording-device-events");
@ -279,6 +279,13 @@ var gTests = [
yield indicator;
yield checkSharingUI({video: true, audio: true});
let Perms = Services.perms;
let uri = Services.io.newURI("https://example.com/", null, null);
is(Perms.testExactPermission(uri, "microphone"), Perms.ALLOW_ACTION,
"microphone persistently allowed");
is(Perms.testExactPermission(uri, "camera"), Perms.ALLOW_ACTION,
"camera persistently allowed");
yield promiseNotificationShown(PopupNotifications.getNotification("webRTC-sharingDevices"));
activateSecondaryAction(kActionDeny);
@ -296,6 +303,12 @@ var gTests = [
expectNoObserverCalled();
yield checkNotSharing();
// The persistent permissions for the frame should have been removed.
is(Perms.testExactPermission(uri, "microphone"), Perms.UNKNOWN_ACTION,
"microphone not persistently allowed");
is(Perms.testExactPermission(uri, "camera"), Perms.UNKNOWN_ACTION,
"camera not persistently allowed");
// the stream is already closed, but this will do some cleanup anyway
yield closeStream(global, true);
}

View File

@ -39,12 +39,12 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
allowevents="true"
xbl:inherits="tooltiptext=inputtooltiptext,value,type,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey"/>
</xul:hbox>
<xul:dropmarker anonid="historydropmarker"
class="autocomplete-history-dropmarker urlbar-history-dropmarker"
allowevents="true"
xbl:inherits="open,enablehistory,parentfocused=focused"/>
<children includes="hbox"/>
</xul:hbox>
<xul:dropmarker anonid="historydropmarker"
class="autocomplete-history-dropmarker urlbar-history-dropmarker"
allowevents="true"
xbl:inherits="open,enablehistory,parentfocused=focused"/>
<xul:popupset anonid="popupset"
class="autocomplete-result-popupset"/>
<children includes="toolbarbutton"/>

View File

@ -68,14 +68,11 @@
// Make sure we rebuild the popup in onpopupshowing
this._needToBuildPopup = true;
var os =
Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
os.addObserver(this, "browser-search-engine-modified", false);
Services.obs.addObserver(this, "browser-search-engine-modified", false);
this._initialized = true;
this.searchService.init((function search_init_cb(aStatus) {
Services.search.init((function search_init_cb(aStatus) {
// Bail out if the binding's been destroyed
if (!this._initialized)
return;
@ -98,9 +95,7 @@
if (this._initialized) {
this._initialized = false;
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
os.removeObserver(this, "browser-search-engine-modified");
Services.obs.removeObserver(this, "browser-search-engine-modified");
}
// Make sure to break the cycle from _textbox to us. Otherwise we leak
@ -120,7 +115,6 @@
<field name="_textboxInitialized">false</field>
<field name="_textbox">document.getAnonymousElementByAttribute(this,
"anonid", "searchbar-textbox");</field>
<field name="_ss">null</field>
<field name="_engines">null</field>
<field name="FormHistory" readonly="true">
(Components.utils.import("resource://gre/modules/FormHistory.jsm", {})).FormHistory;
@ -132,19 +126,19 @@
<property name="engines" readonly="true">
<getter><![CDATA[
if (!this._engines)
this._engines = this.searchService.getVisibleEngines();
this._engines = Services.search.getVisibleEngines();
return this._engines;
]]></getter>
</property>
<property name="currentEngine">
<setter><![CDATA[
let ss = this.searchService;
let ss = Services.search;
ss.defaultEngine = ss.currentEngine = val;
return val;
]]></setter>
<getter><![CDATA[
var currentEngine = this.searchService.currentEngine;
var currentEngine = Services.search.currentEngine;
// Return a dummy engine if there is no currentEngine
return currentEngine || {name: "", uri: null};
]]></getter>
@ -155,18 +149,6 @@
<property name="textbox" readonly="true"
onget="return this._textbox;"/>
<property name="searchService" readonly="true">
<getter><![CDATA[
if (!this._ss) {
const nsIBSS = Components.interfaces.nsIBrowserSearchService;
this._ss =
Components.classes["@mozilla.org/browser/search-service;1"]
.getService(nsIBSS);
}
return this._ss;
]]></getter>
</property>
<property name="value" onget="return this._textbox.value;"
onset="return this._textbox.value = val;"/>
@ -366,7 +348,7 @@
where = whereToOpenLink(aEvent, false, true);
}
else {
var newTabPref = textBox._prefBranch.getBoolPref("browser.search.openintab");
var newTabPref = Services.prefs.getBoolPref("browser.search.openintab");
if (((aEvent instanceof KeyboardEvent) && aEvent.altKey) ^ newTabPref)
where = "tab";
if ((aEvent instanceof MouseEvent) &&
@ -456,18 +438,15 @@
if (target.engine) {
this.currentEngine = target.engine;
} else if (target.classList.contains("addengine-item")) {
var searchService =
Components.classes["@mozilla.org/browser/search-service;1"]
.getService(Components.interfaces.nsIBrowserSearchService);
// We only detect OpenSearch files
var type = Components.interfaces.nsISearchEngine.DATA_XML;
var type = Ci.nsISearchEngine.DATA_XML;
// Select the installed engine if the installation succeeds
var installCallback = {
onSuccess: engine => this.currentEngine = engine
}
searchService.addEngine(target.getAttribute("uri"), type,
target.getAttribute("src"), false,
installCallback);
Services.search.addEngine(target.getAttribute("uri"), type,
target.getAttribute("src"), false,
installCallback);
}
else
return;
@ -559,13 +538,10 @@
// Initialize fields
this._stringBundle = document.getBindingParent(this)._stringBundle;
this._prefBranch =
Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
this._suggestEnabled =
this._prefBranch.getBoolPref("browser.search.suggest.enabled");
Services.prefs.getBoolPref("browser.search.suggest.enabled");
if (this._prefBranch.getBoolPref("browser.urlbar.clickSelectsAll"))
if (Services.prefs.getBoolPref("browser.urlbar.clickSelectsAll"))
this.setAttribute("clickSelectsAll", true);
// Add items to context menu and attach controller to handle them
@ -637,15 +613,11 @@
document.getBindingParent(this)._textboxInitialized = true;
// Add observer for suggest preference
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
prefs.addObserver("browser.search.suggest.enabled", this, false);
Services.prefs.addObserver("browser.search.suggest.enabled", this, false);
]]></constructor>
<destructor><![CDATA[
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
prefs.removeObserver("browser.search.suggest.enabled", this);
Services.prefs.removeObserver("browser.search.suggest.enabled", this);
// Because XBL and the customize toolbar code interacts poorly,
// there may not be anything to remove here
@ -655,7 +627,6 @@
]]></destructor>
<field name="_stringBundle"/>
<field name="_prefBranch"/>
<field name="_suggestMenuItem"/>
<field name="_suggestEnabled"/>
@ -698,7 +669,7 @@
}
popup.mInput = this;
popup.view = this.controller.QueryInterface(Components.interfaces.nsITreeView);
popup.view = this.controller.QueryInterface(Ci.nsITreeView);
popup.invalidate();
popup.showCommentColumn = this.showCommentColumn;
@ -730,7 +701,7 @@
<body><![CDATA[
if (aTopic == "nsPref:changed") {
this._suggestEnabled =
this._prefBranch.getBoolPref("browser.search.suggest.enabled");
Services.prefs.getBoolPref("browser.search.suggest.enabled");
this._suggestMenuItem.setAttribute("checked", this._suggestEnabled);
}
]]></body>
@ -756,7 +727,7 @@
let engine;
let oneOff = this.selectedButton;
if (oneOff) {
if (oneOff && !this.selectionFromMouseOver) {
if (!oneOff.engine) {
oneOff.doCommand();
this.mEnterEvent = null;
@ -778,6 +749,9 @@
<field name="_selectedButton"/>
<property name="selectedButton" onget="return this._selectedButton;">
<setter><![CDATA[
// Set to true from the mouseover handler right after this setter call.
this.selectionFromMouseOver = false;
if (this._selectedButton)
this._selectedButton.removeAttribute("selected");
@ -974,8 +948,8 @@
case "cmd_togglesuggest":
// The pref observer will update _suggestEnabled and the menu
// checkmark.
this._self._prefBranch.setBoolPref("browser.search.suggest.enabled",
!this._self._suggestEnabled);
Services.prefs.setBoolPref("browser.search.suggest.enabled",
!this._self._suggestEnabled);
break;
default:
// do nothing with unrecognized command
@ -1169,6 +1143,10 @@
let isOneOffSelected =
this.selectedButton &&
this.selectedButton.classList.contains("searchbar-engine-one-off-item");
// Typing de-selects the settings or opensearch buttons at the bottom
// of the search panel, as typing shows the user intends to search.
if (this.selectedButton && !isOneOffSelected)
this.selectedButton = null;
if (textbox.value) {
self.removeAttribute("showonlysettings");
groupText = headerSearchText.previousSibling.value +
@ -1232,10 +1210,6 @@
while (list.firstChild)
list.firstChild.remove();
// Avoid setting the selection based on mouse events before
// the 'popupshown' event has fired.
this._ignoreMouseEvents = true;
let Preferences =
Cu.import("resource://gre/modules/Preferences.jsm", {}).Preferences;
let pref = Preferences.get("browser.search.hiddenOneOffs");
@ -1327,10 +1301,6 @@
}
]]></handler>
<handler event="popupshown"><![CDATA[
this._ignoreMouseEvents = false;
]]></handler>
<handler event="mousedown"><![CDATA[
// Required to receive click events from the buttons on Linux.
event.preventDefault();
@ -1341,17 +1311,14 @@
if (target.localName != "button")
return;
// We ignore mouse events between the popupshowing and popupshown
// events to avoid selecting the button that happens to be under the
// mouse when the panel opens.
if (this._ignoreMouseEvents)
return;
if ((target.classList.contains("searchbar-engine-one-off-item") &&
!target.classList.contains("dummy")) ||
target.classList.contains("addengine-item") ||
target.classList.contains("search-setting-button"))
document.getElementById("searchbar").textbox.selectedButton = target;
target.classList.contains("search-setting-button")) {
let textbox = document.getElementById("searchbar").textbox;
textbox.selectedButton = target;
textbox.selectionFromMouseOver = true;
}
]]></handler>
<handler event="mouseout"><![CDATA[

View File

@ -138,4 +138,7 @@ add_task(function* test_text() {
info("Closing search panel");
EventUtils.synthesizeKey("VK_ESCAPE", {});
yield promise;
// Move the cursor out of the panel area to avoid messing with other tests.
yield synthesizeNativeMouseMove(searchbar);
});

View File

@ -152,6 +152,23 @@ add_task(function* test_arrows() {
"the textfield value should be back to initial value");
});
add_task(function* test_typing_clears_button_selection() {
is(Services.focus.focusedElement, textbox.inputField,
"the search bar should be focused"); // from the previous test.
ok(!textbox.selectedButton, "no button should be selected");
EventUtils.synthesizeKey("VK_UP", {});
is(textbox.selectedButton.getAttribute("anonid"), "search-settings",
"the settings item should be selected");
// Type a character.
EventUtils.synthesizeKey("a", {});
ok(!textbox.selectedButton, "the settings item should be de-selected");
// Remove the character.
EventUtils.synthesizeKey("VK_BACK_SPACE", {});
});
add_task(function* test_tab() {
is(Services.focus.focusedElement, textbox.inputField,
"the search bar should be focused"); // from the previous test.

View File

@ -338,9 +338,16 @@ var NetMonitorController = {
let inspector = function() {
let predicate = i => i.value === requestId;
request = NetMonitorView.RequestsMenu.getItemForPredicate(predicate);
if (!request) {
// Reset filters so that the request is visible.
NetMonitorView.RequestsMenu.filterOn("all");
request = NetMonitorView.RequestsMenu.getItemForPredicate(predicate);
}
// If the request was found, select it. Otherwise this function will be
// called again once new requests arrive.
if (request) {
window.off(EVENTS.REQUEST_ADDED, inspector);
NetMonitorView.RequestsMenu.filterOn("all");
NetMonitorView.RequestsMenu.selectedItem = request;
deferred.resolve();
}

View File

@ -328,6 +328,7 @@ skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
[browser_webconsole_live_filtering_on_search_strings.js]
[browser_webconsole_message_node_id.js]
[browser_webconsole_netlogging.js]
[browser_webconsole_netlogging_reset_filter.js]
[browser_webconsole_notifications.js]
[browser_webconsole_open-links-without-callback.js]
[browser_webconsole_promise.js]

View File

@ -0,0 +1,91 @@
/* 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/. */
// Tests that network log messages bring up the network panel and select the
// right request even if it was previously filtered off.
"use strict";
const TEST_FILE_URI = "http://example.com/browser/browser/" +
"devtools/webconsole/test/test-network.html";
const TEST_URI = "data:text/html;charset=utf8,<p>test file URI";
let hud;
let test = asyncTest(function* () {
let requests = [];
let { browser } = yield loadTab(TEST_URI);
yield pushPrefEnv();
hud = yield openConsole();
hud.jsterm.clearOutput();
HUDService.lastFinishedRequest.callback = request => requests.push(request);
let loaded = loadBrowser(browser);
content.location = TEST_FILE_URI;
yield loaded;
yield testMessages();
let htmlRequest = requests.find(e => e.request.url.endsWith("html"));
ok(htmlRequest, "htmlRequest was a html");
yield hud.ui.openNetworkPanel(htmlRequest.actor);
let toolbox = gDevTools.getToolbox(hud.target);
is(toolbox.currentToolId, "netmonitor", "Network panel was opened");
let panel = toolbox.getCurrentPanel();
let selected = panel.panelWin.NetMonitorView.RequestsMenu.selectedItem;
is(selected.attachment.method, htmlRequest.request.method,
"The correct request is selected");
is(selected.attachment.url, htmlRequest.request.url,
"The correct request is definitely selected");
// Filter out the HTML request.
panel.panelWin.NetMonitorView.RequestsMenu.filterOn("js");
yield toolbox.selectTool("webconsole");
is(toolbox.currentToolId, "webconsole", "Web console was selected");
yield hud.ui.openNetworkPanel(htmlRequest.actor);
panel.panelWin.NetMonitorView.RequestsMenu.selectedItem;
is(selected.attachment.method, htmlRequest.request.method,
"The correct request is selected");
is(selected.attachment.url, htmlRequest.request.url,
"The correct request is definitely selected");
// All tests are done. Shutdown.
HUDService.lastFinishedRequest.callback = null;
htmlRequest = browser = requests = hud = null;
});
function testMessages() {
return waitForMessages({
webconsole: hud,
messages: [{
text: "running network console logging tests",
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
},
{
text: "test-network.html",
category: CATEGORY_NETWORK,
severity: SEVERITY_LOG,
},
{
text: "testscript.js",
category: CATEGORY_NETWORK,
severity: SEVERITY_LOG,
}],
});
}
function pushPrefEnv() {
let deferred = promise.defer();
let options = {
set: [["devtools.webconsole.filter.networkinfo", true]]
};
SpecialPowers.pushPrefEnv(options, deferred.resolve);
return deferred.promise;
}

View File

@ -393,7 +393,16 @@ function prompt(aBrowser, aRequest) {
allowedDevices.push(videoDevices[0].deviceIndex);
if (audioDevices.length && micPerm == perms.ALLOW_ACTION)
allowedDevices.push(audioDevices[0].deviceIndex);
let mm = this.browser.messageManager;
// Remember on which URIs we found persistent permissions so that we
// can remove them if the user clicks 'Stop Sharing'. There's no
// other way for the stop sharing code to know the hostnames of frames
// using devices until bug 1066082 is fixed.
let browser = this.browser;
browser._devicePermissionURIs = browser._devicePermissionURIs || [];
browser._devicePermissionURIs.push(uri);
let mm = browser.messageManager;
mm.sendAsyncMessage("webrtc:Allow", {callID: aRequest.callID,
windowID: aRequest.windowID,
devices: allowedDevices});
@ -534,6 +543,13 @@ function prompt(aBrowser, aRequest) {
return;
}
if (aRemember) {
// Remember on which URIs we set persistent permissions so that we
// can remove them if the user clicks 'Stop Sharing'.
aBrowser._devicePermissionURIs = aBrowser._devicePermissionURIs || [];
aBrowser._devicePermissionURIs.push(uri);
}
let mm = notification.browser.messageManager;
mm.sendAsyncMessage("webrtc:Allow", {callID: aRequest.callID,
windowID: aRequest.windowID,
@ -862,15 +878,17 @@ function updateBrowserSpecificIndicator(aBrowser, aState) {
label: stringBundle.getString("getUserMedia.stopSharing.label"),
accessKey: stringBundle.getString("getUserMedia.stopSharing.accesskey"),
callback: function () {
let uri = Services.io.newURI(aState.documentURI, null, null);
let uris = aBrowser._devicePermissionURIs || [];
uris = uris.concat(Services.io.newURI(aState.documentURI, null, null));
let perms = Services.perms;
if (aState.camera &&
perms.testExactPermission(uri, "camera") == perms.ALLOW_ACTION)
perms.remove(uri, "camera");
if (aState.microphone &&
perms.testExactPermission(uri, "microphone") == perms.ALLOW_ACTION)
perms.remove(uri, "microphone");
for (let uri of uris) {
if (aState.camera &&
perms.testExactPermission(uri, "camera") == perms.ALLOW_ACTION)
perms.remove(uri, "camera");
if (aState.microphone &&
perms.testExactPermission(uri, "microphone") == perms.ALLOW_ACTION)
perms.remove(uri, "microphone");
}
let mm = notification.browser.messageManager;
mm.sendAsyncMessage("webrtc:StopSharing", windowId);
}
@ -902,12 +920,13 @@ function updateBrowserSpecificIndicator(aBrowser, aState) {
anchorId, mainAction, secondaryActions, options);
}
else {
removeBrowserNotification(aBrowser,"webRTC-sharingDevices");
removeBrowserNotification(aBrowser, "webRTC-sharingDevices");
aBrowser._devicePermissionURIs = null;
}
// Now handle the screen sharing indicator.
if (!aState.screen) {
removeBrowserNotification(aBrowser,"webRTC-sharingScreen");
removeBrowserNotification(aBrowser, "webRTC-sharingScreen");
return;
}

View File

@ -890,6 +890,11 @@ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-ba
.urlbar-history-dropmarker {
-moz-appearance: toolbarbutton-dropdown;
transition: opacity 0.15s ease;
}
#urlbar:not(:hover) > .urlbar-textbox-container > .urlbar-history-dropmarker {
opacity: 0;
}
#urlbar-container {

View File

@ -1699,6 +1699,11 @@ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-ba
padding: 0 3px;
list-style-image: var(--urlbar-dropmarker-url);
-moz-image-region: var(--urlbar-dropmarker-region);
transition: opacity 0.15s ease;
}
#urlbar:not(:hover) > .urlbar-textbox-container > .urlbar-history-dropmarker {
opacity: 0;
}
.urlbar-history-dropmarker[open="true"],

View File

@ -1476,6 +1476,11 @@ html|*.urlbar-input:-moz-lwtheme::-moz-placeholder,
width: auto;
list-style-image: var(--urlbar-dropmarker-url);
-moz-image-region: var(--urlbar-dropmarker-region);
transition: opacity 0.15s ease;
}
#urlbar:not(:hover) > .urlbar-textbox-container > .urlbar-history-dropmarker {
opacity: 0;
}
.urlbar-history-dropmarker:hover {

View File

@ -28,25 +28,14 @@ BluetoothDaemonA2dpModule::SetNotificationHandler(
sNotificationHandler = aNotificationHandler;
}
nsresult
BluetoothDaemonA2dpModule::Send(DaemonSocketPDU* aPDU,
BluetoothA2dpResultHandler* aRes)
{
nsRefPtr<BluetoothA2dpResultHandler> res(aRes);
nsresult rv = Send(aPDU, static_cast<void*>(res.get()));
if (NS_FAILED(rv)) {
return rv;
}
unused << res.forget(); // Keep reference for response
return NS_OK;
}
void
BluetoothDaemonA2dpModule::HandleSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU, void* aUserData)
DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aRes)
{
static void (BluetoothDaemonA2dpModule::* const HandleOp[])(
const DaemonSocketPDUHeader&, DaemonSocketPDU&, void*) = {
const DaemonSocketPDUHeader&, DaemonSocketPDU&,
DaemonSocketResultHandler*) = {
[0] = &BluetoothDaemonA2dpModule::HandleRsp,
[1] = &BluetoothDaemonA2dpModule::HandleNtf
};
@ -56,7 +45,7 @@ BluetoothDaemonA2dpModule::HandleSvc(const DaemonSocketPDUHeader& aHeader,
// negate twice to map bit to 0/1
unsigned int isNtf = !!(aHeader.mOpcode & 0x80);
(this->*(HandleOp[isNtf]))(aHeader, aPDU, aUserData);
(this->*(HandleOp[isNtf]))(aHeader, aPDU, aRes);
}
// Commands
@ -139,7 +128,7 @@ BluetoothDaemonA2dpModule::DisconnectRsp(
void
BluetoothDaemonA2dpModule::HandleRsp(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
void* aUserData)
DaemonSocketResultHandler* aRes)
{
static void (BluetoothDaemonA2dpModule::* const HandleRsp[])(
const DaemonSocketPDUHeader&,
@ -158,8 +147,7 @@ BluetoothDaemonA2dpModule::HandleRsp(
}
nsRefPtr<BluetoothA2dpResultHandler> res =
already_AddRefed<BluetoothA2dpResultHandler>(
static_cast<BluetoothA2dpResultHandler*>(aUserData));
static_cast<BluetoothA2dpResultHandler*>(aRes);
if (!res) {
return; // Return early if no result handler has been set for response
@ -315,7 +303,7 @@ BluetoothDaemonA2dpModule::AudioConfigNtf(
void
BluetoothDaemonA2dpModule::HandleNtf(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
void* aUserData)
DaemonSocketResultHandler* aRes)
{
static void (BluetoothDaemonA2dpModule::* const HandleNtf[])(
const DaemonSocketPDUHeader&, DaemonSocketPDU&) = {

View File

@ -15,6 +15,7 @@ BEGIN_BLUETOOTH_NAMESPACE
using mozilla::ipc::DaemonSocketPDU;
using mozilla::ipc::DaemonSocketPDUHeader;
using mozilla::ipc::DaemonSocketResultHandler;
class BluetoothSetupResultHandler;
@ -33,7 +34,8 @@ public:
static const int MAX_NUM_CLIENTS;
virtual nsresult Send(DaemonSocketPDU* aPDU, void* aUserData) = 0;
virtual nsresult Send(DaemonSocketPDU* aPDU,
DaemonSocketResultHandler* aRes) = 0;
virtual nsresult RegisterModule(uint8_t aId, uint8_t aMode,
uint32_t aMaxNumClients,
@ -55,11 +57,8 @@ public:
BluetoothA2dpResultHandler* aRes);
protected:
nsresult Send(DaemonSocketPDU* aPDU,
BluetoothA2dpResultHandler* aRes);
void HandleSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU, void* aUserData);
DaemonSocketPDU& aPDU, DaemonSocketResultHandler* aRes);
//
// Responses
@ -87,7 +86,7 @@ protected:
void HandleRsp(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
void* aUserData);
DaemonSocketResultHandler* aRes);
//
// Notifications
@ -125,7 +124,7 @@ protected:
void HandleNtf(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
void* aUserData);
DaemonSocketResultHandler* aRes);
static BluetoothA2dpNotificationHandler* sNotificationHandler;
};
@ -142,15 +141,15 @@ public:
void Init(
BluetoothA2dpNotificationHandler* aNotificationHandler,
BluetoothA2dpResultHandler* aRes);
void Cleanup(BluetoothA2dpResultHandler* aRes);
BluetoothA2dpResultHandler* aRes) override;
void Cleanup(BluetoothA2dpResultHandler* aRes) override;
/* Connect / Disconnect */
void Connect(const nsAString& aBdAddr,
BluetoothA2dpResultHandler* aRes);
BluetoothA2dpResultHandler* aRes) override;
void Disconnect(const nsAString& aBdAddr,
BluetoothA2dpResultHandler* aRes);
BluetoothA2dpResultHandler* aRes) override;
private:
void DispatchError(BluetoothA2dpResultHandler* aRes,

View File

@ -28,25 +28,14 @@ BluetoothDaemonAvrcpModule::SetNotificationHandler(
sNotificationHandler = aNotificationHandler;
}
nsresult
BluetoothDaemonAvrcpModule::Send(DaemonSocketPDU* aPDU,
BluetoothAvrcpResultHandler* aRes)
{
nsRefPtr<BluetoothAvrcpResultHandler> res(aRes);
nsresult rv = Send(aPDU, static_cast<void*>(res.get()));
if (NS_FAILED(rv)) {
return rv;
}
unused << res.forget(); // Keep reference for response
return NS_OK;
}
void
BluetoothDaemonAvrcpModule::HandleSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU, void* aUserData)
DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aRes)
{
static void (BluetoothDaemonAvrcpModule::* const HandleOp[])(
const DaemonSocketPDUHeader&, DaemonSocketPDU&, void*) = {
const DaemonSocketPDUHeader&, DaemonSocketPDU&,
DaemonSocketResultHandler*) = {
[0] = &BluetoothDaemonAvrcpModule::HandleRsp,
[1] = &BluetoothDaemonAvrcpModule::HandleNtf
};
@ -55,7 +44,7 @@ BluetoothDaemonAvrcpModule::HandleSvc(const DaemonSocketPDUHeader& aHeader,
unsigned int isNtf = !!(aHeader.mOpcode & 0x80);
(this->*(HandleOp[isNtf]))(aHeader, aPDU, aUserData);
(this->*(HandleOp[isNtf]))(aHeader, aPDU, aRes);
}
// Commands
@ -421,7 +410,7 @@ BluetoothDaemonAvrcpModule::SetVolumeRsp(
void
BluetoothDaemonAvrcpModule::HandleRsp(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
void* aUserData)
DaemonSocketResultHandler* aRes)
{
static void (BluetoothDaemonAvrcpModule::* const HandleRsp[])(
const DaemonSocketPDUHeader&,
@ -459,8 +448,7 @@ BluetoothDaemonAvrcpModule::HandleRsp(
}
nsRefPtr<BluetoothAvrcpResultHandler> res =
already_AddRefed<BluetoothAvrcpResultHandler>(
static_cast<BluetoothAvrcpResultHandler*>(aUserData));
static_cast<BluetoothAvrcpResultHandler*>(aRes);
if (!res) {
return; // Return early if no result handler has been set for response
@ -792,7 +780,7 @@ BluetoothDaemonAvrcpModule::PassthroughCmdNtf(
void
BluetoothDaemonAvrcpModule::HandleNtf(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
void* aUserData)
DaemonSocketResultHandler* aRes)
{
static void (BluetoothDaemonAvrcpModule::* const HandleNtf[])(
const DaemonSocketPDUHeader&, DaemonSocketPDU&) = {

View File

@ -15,6 +15,7 @@ BEGIN_BLUETOOTH_NAMESPACE
using mozilla::ipc::DaemonSocketPDU;
using mozilla::ipc::DaemonSocketPDUHeader;
using mozilla::ipc::DaemonSocketResultHandler;
class BluetoothSetupResultHandler;
@ -65,7 +66,8 @@ public:
static const int MAX_NUM_CLIENTS;
virtual nsresult Send(DaemonSocketPDU* aPDU, void* aUserData) = 0;
virtual nsresult Send(DaemonSocketPDU* aPDU,
DaemonSocketResultHandler* aRes) = 0;
virtual nsresult RegisterModule(uint8_t aId, uint8_t aMode,
uint32_t aMaxNumClients,
@ -119,11 +121,8 @@ public:
nsresult SetVolumeCmd(uint8_t aVolume, BluetoothAvrcpResultHandler* aRes);
protected:
nsresult Send(DaemonSocketPDU* aPDU,
BluetoothAvrcpResultHandler* aRes);
void HandleSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU, void* aUserData);
DaemonSocketPDU& aPDU, DaemonSocketResultHandler* aRes);
//
// Responses
@ -183,7 +182,7 @@ protected:
void HandleRsp(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
void* aUserData);
DaemonSocketResultHandler* aRes);
//
// Notifications
@ -293,7 +292,7 @@ protected:
void HandleNtf(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
void* aUserData);
DaemonSocketResultHandler* aRes);
static BluetoothAvrcpNotificationHandler* sNotificationHandler;
};

View File

@ -28,25 +28,14 @@ BluetoothDaemonGattModule::SetNotificationHandler(
sNotificationHandler = aNotificationHandler;
}
nsresult
BluetoothDaemonGattModule::Send(DaemonSocketPDU* aPDU,
BluetoothGattResultHandler* aRes)
{
nsRefPtr<BluetoothGattResultHandler> res(aRes);
nsresult rv = Send(aPDU, static_cast<void*>(res.get()));
if (NS_FAILED(rv)) {
return rv;
}
unused << res.forget(); // Keep reference for response
return NS_OK;
}
void
BluetoothDaemonGattModule::HandleSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU, void* aUserData)
DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aRes)
{
static void (BluetoothDaemonGattModule::* const HandleOp[])(
const DaemonSocketPDUHeader&, DaemonSocketPDU&, void*) = {
const DaemonSocketPDUHeader&, DaemonSocketPDU&,
DaemonSocketResultHandler*) = {
[0] = &BluetoothDaemonGattModule::HandleRsp,
[1] = &BluetoothDaemonGattModule::HandleNtf
};
@ -56,7 +45,7 @@ BluetoothDaemonGattModule::HandleSvc(const DaemonSocketPDUHeader& aHeader,
// Negate twice to map bit to 0/1
unsigned long isNtf = !!(aHeader.mOpcode & 0x80);
(this->*(HandleOp[isNtf]))(aHeader, aPDU, aUserData);
(this->*(HandleOp[isNtf]))(aHeader, aPDU, aRes);
}
// Commands
@ -1405,7 +1394,7 @@ BluetoothDaemonGattModule::ServerSendResponseRsp(
void
BluetoothDaemonGattModule::HandleRsp(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
void* aUserData)
DaemonSocketResultHandler* aRes)
{
static void (BluetoothDaemonGattModule::* const HandleRsp[])(
const DaemonSocketPDUHeader&,
@ -1493,8 +1482,7 @@ BluetoothDaemonGattModule::HandleRsp(
}
nsRefPtr<BluetoothGattResultHandler> res =
already_AddRefed<BluetoothGattResultHandler>(
static_cast<BluetoothGattResultHandler*>(aUserData));
static_cast<BluetoothGattResultHandler*>(aRes);
if (!res) {
return;
@ -2152,7 +2140,7 @@ BluetoothDaemonGattModule::ServerResponseConfirmationNtf(
void
BluetoothDaemonGattModule::HandleNtf(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
void* aUserData)
DaemonSocketResultHandler* aRes)
{
static void (BluetoothDaemonGattModule::* const HandleNtf[])(
const DaemonSocketPDUHeader&, DaemonSocketPDU&) = {

View File

@ -15,6 +15,7 @@ BEGIN_BLUETOOTH_NAMESPACE
using mozilla::ipc::DaemonSocketPDU;
using mozilla::ipc::DaemonSocketPDUHeader;
using mozilla::ipc::DaemonSocketResultHandler;
class BluetoothSetupResultHandler;
@ -67,7 +68,8 @@ public:
static const int MAX_NUM_CLIENTS;
virtual nsresult Send(DaemonSocketPDU* aPDU, void* aUserData) = 0;
virtual nsresult Send(DaemonSocketPDU* aPDU,
DaemonSocketResultHandler* aRes) = 0;
virtual nsresult RegisterModule(uint8_t aId, uint8_t aMode,
uint32_t aMaxNumClients,
@ -300,11 +302,8 @@ public:
// TODO: Add L support
protected:
nsresult Send(DaemonSocketPDU* aPDU,
BluetoothGattResultHandler* aRes);
void HandleSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU, void* aUserData);
DaemonSocketPDU& aPDU, DaemonSocketResultHandler* aRes);
//
// Responses
@ -471,7 +470,7 @@ protected:
void HandleRsp(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
void* aUserData);
DaemonSocketResultHandler* aRes);
//
// Notifications
@ -770,7 +769,7 @@ protected:
void HandleNtf(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
void* aUserData);
DaemonSocketResultHandler* aRes);
static BluetoothGattNotificationHandler* sNotificationHandler;
};
@ -786,81 +785,81 @@ public:
~BluetoothDaemonGattInterface();
void Init(BluetoothGattNotificationHandler* aNotificationHandler,
BluetoothGattResultHandler* aRes);
void Cleanup(BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
void Cleanup(BluetoothGattResultHandler* aRes) override;
/* Register / Unregister */
void RegisterClient(const BluetoothUuid& aUuid,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
void UnregisterClient(int aClientIf,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
/* Start / Stop LE Scan */
void Scan(int aClientIf, bool aStart,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
/* Connect / Disconnect */
void Connect(int aClientIf,
const nsAString& aBdAddr,
bool aIsDirect, /* auto connect */
BluetoothTransport aTransport,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
void Disconnect(int aClientIf,
const nsAString& aBdAddr,
int aConnId,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
/* Start / Stop advertisements to listen for incoming connections */
void Listen(int aClientIf,
bool aIsStart,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
/* Clear the attribute cache for a given device*/
void Refresh(int aClientIf,
const nsAString& aBdAddr,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
/* Enumerate Attributes */
void SearchService(int aConnId,
bool aSearchAll,
const BluetoothUuid& aUuid,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
void GetIncludedService(int aConnId,
const BluetoothGattServiceId& aServiceId,
bool aFirst,
const BluetoothGattServiceId& aStartServiceId,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
void GetCharacteristic(int aConnId,
const BluetoothGattServiceId& aServiceId,
bool aFirst,
const BluetoothGattId& aStartCharId,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
void GetDescriptor(int aConnId,
const BluetoothGattServiceId& aServiceId,
const BluetoothGattId& aCharId,
bool aFirst,
const BluetoothGattId& aDescriptorId,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
/* Read / Write An Attribute */
void ReadCharacteristic(int aConnId,
const BluetoothGattServiceId& aServiceId,
const BluetoothGattId& aCharId,
BluetoothGattAuthReq aAuthReq,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
void WriteCharacteristic(int aConnId,
const BluetoothGattServiceId& aServiceId,
const BluetoothGattId& aCharId,
BluetoothGattWriteType aWriteType,
BluetoothGattAuthReq aAuthReq,
const nsTArray<uint8_t>& aValue,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
void ReadDescriptor(int aConnId,
const BluetoothGattServiceId& aServiceId,
const BluetoothGattId& aCharId,
const BluetoothGattId& aDescriptorId,
BluetoothGattAuthReq aAuthReq,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
void WriteDescriptor(int aConnId,
const BluetoothGattServiceId& aServiceId,
const BluetoothGattId& aCharId,
@ -868,12 +867,12 @@ public:
BluetoothGattWriteType aWriteType,
BluetoothGattAuthReq aAuthReq,
const nsTArray<uint8_t>& aValue,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
/* Execute / Abort Prepared Write*/
void ExecuteWrite(int aConnId,
int aIsExecute,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
/* Register / Deregister Characteristic Notifications or Indications */
@ -881,19 +880,19 @@ public:
const nsAString& aBdAddr,
const BluetoothGattServiceId& aServiceId,
const BluetoothGattId& aCharId,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
void DeregisterNotification(int aClientIf,
const nsAString& aBdAddr,
const BluetoothGattServiceId& aServiceId,
const BluetoothGattId& aCharId,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
void ReadRemoteRssi(int aClientIf,
const nsAString& aBdAddr,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
void GetDeviceType(const nsAString& aBdAddr,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
/* Set advertising data or scan response data */
void SetAdvData(int aServerIf,
@ -906,61 +905,61 @@ public:
uint16_t aManufacturerLen, char* aManufacturerData,
uint16_t aServiceDataLen, char* aServiceData,
uint16_t aServiceUuidLen, char* aServiceUuid,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
void TestCommand(int aCommand,
const BluetoothGattTestParam& aTestParam,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
/* Register / Unregister */
void RegisterServer(const BluetoothUuid& aUuid,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
void UnregisterServer(int aServerIf,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
/* Connect / Disconnect */
void ConnectPeripheral(int aServerIf,
const nsAString& aBdAddr,
bool aIsDirect, /* auto connect */
BluetoothTransport aTransport,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
void DisconnectPeripheral(int aServerIf,
const nsAString& aBdAddr,
int aConnId,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
/* Add a services / a characteristic / a descriptor */
void AddService(int aServerIf,
const BluetoothGattServiceId& aServiceId,
int aNumHandles,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
void AddIncludedService(int aServerIf,
int aServiceHandle,
int aIncludedServiceHandle,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
void AddCharacteristic(int aServerIf,
int aServiceHandle,
const BluetoothUuid& aUuid,
BluetoothGattCharProp aProperties,
BluetoothGattAttrPerm aPermissions,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
void AddDescriptor(int aServerIf,
int aServiceHandle,
const BluetoothUuid& aUuid,
BluetoothGattAttrPerm aPermissions,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
/* Start / Stop / Delete a service */
void StartService(int aServerIf,
int aServiceHandle,
BluetoothTransport aTransport,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
void StopService(int aServerIf,
int aServiceHandle,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
void DeleteService(int aServerIf,
int aServiceHandle,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
/* Send an indication or a notification */
void SendIndication(
@ -969,14 +968,14 @@ public:
int aConnId,
const nsTArray<uint8_t>& aValue,
bool aConfirm, /* true: indication, false: notification */
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
/* Send a response for an incoming indication */
void SendResponse(int aConnId,
int aTransId,
BluetoothGattStatus aStatus,
const BluetoothGattResponse& aResponse,
BluetoothGattResultHandler* aRes);
BluetoothGattResultHandler* aRes) override;
private:
void DispatchError(BluetoothGattResultHandler* aRes,

View File

@ -31,25 +31,14 @@ BluetoothDaemonHandsfreeModule::SetNotificationHandler(
sNotificationHandler = aNotificationHandler;
}
nsresult
BluetoothDaemonHandsfreeModule::Send(DaemonSocketPDU* aPDU,
BluetoothHandsfreeResultHandler* aRes)
{
nsRefPtr<BluetoothHandsfreeResultHandler> res(aRes);
nsresult rv = Send(aPDU, static_cast<void*>(res.get()));
if (NS_FAILED(rv)) {
return rv;
}
unused << res.forget(); // Keep reference for response
return NS_OK;
}
void
BluetoothDaemonHandsfreeModule::HandleSvc(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU, void* aUserData)
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aRes)
{
static void (BluetoothDaemonHandsfreeModule::* const HandleOp[])(
const DaemonSocketPDUHeader&, DaemonSocketPDU&, void*) = {
const DaemonSocketPDUHeader&, DaemonSocketPDU&,
DaemonSocketResultHandler*) = {
[0] = &BluetoothDaemonHandsfreeModule::HandleRsp,
[1] = &BluetoothDaemonHandsfreeModule::HandleNtf
};
@ -59,7 +48,7 @@ BluetoothDaemonHandsfreeModule::HandleSvc(
// Negate twice to map bit to 0/1
unsigned long isNtf = !!(aHeader.mOpcode & 0x80);
(this->*(HandleOp[isNtf]))(aHeader, aPDU, aUserData);
(this->*(HandleOp[isNtf]))(aHeader, aPDU, aRes);
}
// Commands
@ -679,7 +668,7 @@ BluetoothDaemonHandsfreeModule::ConfigureWbsRsp(
void
BluetoothDaemonHandsfreeModule::HandleRsp(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
void* aUserData)
DaemonSocketResultHandler* aRes)
{
static void (BluetoothDaemonHandsfreeModule::* const HandleRsp[])(
const DaemonSocketPDUHeader&,
@ -727,8 +716,7 @@ BluetoothDaemonHandsfreeModule::HandleRsp(
}
nsRefPtr<BluetoothHandsfreeResultHandler> res =
already_AddRefed<BluetoothHandsfreeResultHandler>(
static_cast<BluetoothHandsfreeResultHandler*>(aUserData));
static_cast<BluetoothHandsfreeResultHandler*>(aRes);
if (!res) {
return; // Return early if no result handler has been set for response
@ -1463,7 +1451,7 @@ BluetoothDaemonHandsfreeModule::WbsNtf(
void
BluetoothDaemonHandsfreeModule::HandleNtf(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
void* aUserData)
DaemonSocketResultHandler* aRes)
{
static void (BluetoothDaemonHandsfreeModule::* const HandleNtf[])(
const DaemonSocketPDUHeader&, DaemonSocketPDU&) = {

View File

@ -15,6 +15,7 @@ BEGIN_BLUETOOTH_NAMESPACE
using mozilla::ipc::DaemonSocketPDU;
using mozilla::ipc::DaemonSocketPDUHeader;
using mozilla::ipc::DaemonSocketResultHandler;
class BluetoothSetupResultHandler;
@ -44,7 +45,8 @@ public:
OPCODE_CONFIGURE_WBS = 0x0f
};
virtual nsresult Send(DaemonSocketPDU* aPDU, void* aUserData) = 0;
virtual nsresult Send(DaemonSocketPDU* aPDU,
DaemonSocketResultHandler* aRes) = 0;
virtual nsresult RegisterModule(uint8_t aId, uint8_t aMode,
uint32_t aMaxNumClients,
@ -128,11 +130,9 @@ public:
BluetoothHandsfreeResultHandler* aRes);
protected:
nsresult Send(DaemonSocketPDU* aPDU,
BluetoothHandsfreeResultHandler* aRes);
void HandleSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU, void* aUserData);
DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aRes);
//
// Responses
@ -212,7 +212,7 @@ protected:
void HandleRsp(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
void* aUserData);
DaemonSocketResultHandler* aRes);
//
// Notifications
@ -369,7 +369,7 @@ protected:
void HandleNtf(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
void* aUserData);
DaemonSocketResultHandler* aRes);
static BluetoothHandsfreeNotificationHandler* sNotificationHandler;
#if ANDROID_VERSION < 21
@ -400,39 +400,39 @@ public:
void Init(
BluetoothHandsfreeNotificationHandler* aNotificationHandler,
int aMaxNumClients, BluetoothHandsfreeResultHandler* aRes);
void Cleanup(BluetoothHandsfreeResultHandler* aRes);
int aMaxNumClients, BluetoothHandsfreeResultHandler* aRes) override;
void Cleanup(BluetoothHandsfreeResultHandler* aRes) override;
/* Connect / Disconnect */
void Connect(const nsAString& aBdAddr,
BluetoothHandsfreeResultHandler* aRes);
BluetoothHandsfreeResultHandler* aRes) override;
void Disconnect(const nsAString& aBdAddr,
BluetoothHandsfreeResultHandler* aRes);
BluetoothHandsfreeResultHandler* aRes) override;
void ConnectAudio(const nsAString& aBdAddr,
BluetoothHandsfreeResultHandler* aRes);
BluetoothHandsfreeResultHandler* aRes) override;
void DisconnectAudio(const nsAString& aBdAddr,
BluetoothHandsfreeResultHandler* aRes);
BluetoothHandsfreeResultHandler* aRes) override;
/* Voice Recognition */
void StartVoiceRecognition(const nsAString& aBdAddr,
BluetoothHandsfreeResultHandler* aRes);
BluetoothHandsfreeResultHandler* aRes) override;
void StopVoiceRecognition(const nsAString& aBdAddr,
BluetoothHandsfreeResultHandler* aRes);
BluetoothHandsfreeResultHandler* aRes) override;
/* Volume */
void VolumeControl(BluetoothHandsfreeVolumeType aType, int aVolume,
const nsAString& aBdAddr,
BluetoothHandsfreeResultHandler* aRes);
BluetoothHandsfreeResultHandler* aRes) override;
/* Device status */
void DeviceStatusNotification(BluetoothHandsfreeNetworkState aNtkState,
BluetoothHandsfreeServiceType aSvcType,
int aSignal, int aBattChg,
BluetoothHandsfreeResultHandler* aRes);
BluetoothHandsfreeResultHandler* aRes) override;
/* Responses */
@ -442,12 +442,12 @@ public:
BluetoothHandsfreeCallState aCallSetupState,
int aSignal, int aRoam, int aBattChg,
const nsAString& aBdAddr,
BluetoothHandsfreeResultHandler* aRes);
BluetoothHandsfreeResultHandler* aRes) override;
void FormattedAtResponse(const char* aRsp, const nsAString& aBdAddr,
BluetoothHandsfreeResultHandler* aRes);
BluetoothHandsfreeResultHandler* aRes) override;
void AtResponse(BluetoothHandsfreeAtResponse aResponseCode, int aErrorCode,
const nsAString& aBdAddr,
BluetoothHandsfreeResultHandler* aRes);
BluetoothHandsfreeResultHandler* aRes) override;
void ClccResponse(int aIndex, BluetoothHandsfreeCallDirection aDir,
BluetoothHandsfreeCallState aState,
BluetoothHandsfreeCallMode aMode,
@ -455,7 +455,7 @@ public:
const nsAString& aNumber,
BluetoothHandsfreeCallAddressType aType,
const nsAString& aBdAddr,
BluetoothHandsfreeResultHandler* aRes);
BluetoothHandsfreeResultHandler* aRes) override;
/* Phone State */
@ -463,12 +463,12 @@ public:
BluetoothHandsfreeCallState aCallSetupState,
const nsAString& aNumber,
BluetoothHandsfreeCallAddressType aType,
BluetoothHandsfreeResultHandler* aRes);
BluetoothHandsfreeResultHandler* aRes) override;
/* Wide Band Speech */
void ConfigureWbs(const nsAString& aBdAddr,
BluetoothHandsfreeWbsConfig aConfig,
BluetoothHandsfreeResultHandler* aRes);
BluetoothHandsfreeResultHandler* aRes) override;
private:
void DispatchError(BluetoothHandsfreeResultHandler* aRes,

View File

@ -34,7 +34,8 @@ static const int sRetryInterval = 100; // ms
class BluetoothDaemonSetupModule
{
public:
virtual nsresult Send(DaemonSocketPDU* aPDU, void* aUserData) = 0;
virtual nsresult Send(DaemonSocketPDU* aPDU,
DaemonSocketResultHandler* aRes) = 0;
// Commands
//
@ -107,7 +108,7 @@ protected:
// Called to handle PDUs with Service field equal to 0x00, which
// contains internal operations for setup and configuration.
void HandleSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU, void* aUserData)
DaemonSocketPDU& aPDU, DaemonSocketResultHandler* aRes)
{
static void (BluetoothDaemonSetupModule::* const HandleRsp[])(
const DaemonSocketPDUHeader&,
@ -125,27 +126,15 @@ protected:
}
nsRefPtr<BluetoothSetupResultHandler> res =
already_AddRefed<BluetoothSetupResultHandler>(
static_cast<BluetoothSetupResultHandler*>(aUserData));
static_cast<BluetoothSetupResultHandler*>(aRes);
if (!res) {
if (!aRes) {
return; // Return early if no result handler has been set
}
(this->*(HandleRsp[aHeader.mOpcode]))(aHeader, aPDU, res);
}
nsresult Send(DaemonSocketPDU* aPDU, BluetoothSetupResultHandler* aRes)
{
nsRefPtr<BluetoothSetupResultHandler> res(aRes);
nsresult rv = Send(aPDU, static_cast<void*>(res.get()));
if (NS_FAILED(rv)) {
return rv;
}
unused << res.forget(); // Keep reference for response
return NS_OK;
}
private:
// Responses
@ -211,7 +200,8 @@ public:
static const int MAX_NUM_CLIENTS;
virtual nsresult Send(DaemonSocketPDU* aPDU, void* aUserData) = 0;
virtual nsresult Send(DaemonSocketPDU* aPDU,
DaemonSocketResultHandler* aRes) = 0;
nsresult EnableCmd(BluetoothResultHandler* aRes)
{
@ -600,28 +590,18 @@ public:
protected:
void HandleSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU, void* aUserData)
DaemonSocketPDU& aPDU, DaemonSocketResultHandler* aRes)
{
static void (BluetoothDaemonCoreModule::* const HandleOp[])(
const DaemonSocketPDUHeader&, DaemonSocketPDU&, void*) = {
const DaemonSocketPDUHeader&, DaemonSocketPDU&,
DaemonSocketResultHandler*) = {
[0] = &BluetoothDaemonCoreModule::HandleRsp,
[1] = &BluetoothDaemonCoreModule::HandleNtf
};
MOZ_ASSERT(!NS_IsMainThread());
(this->*(HandleOp[!!(aHeader.mOpcode & 0x80)]))(aHeader, aPDU, aUserData);
}
nsresult Send(DaemonSocketPDU* aPDU, BluetoothResultHandler* aRes)
{
nsRefPtr<BluetoothResultHandler> res(aRes);
nsresult rv = Send(aPDU, static_cast<void*>(res.get()));
if (NS_FAILED(rv)) {
return rv;
}
unused << res.forget(); // Keep reference for response
return NS_OK;
(this->*(HandleOp[!!(aHeader.mOpcode & 0x80)]))(aHeader, aPDU, aRes);
}
private:
@ -825,7 +805,7 @@ private:
}
void HandleRsp(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU, void* aUserData)
DaemonSocketPDU& aPDU, DaemonSocketResultHandler* aRes)
{
static void (BluetoothDaemonCoreModule::* const HandleRsp[])(
const DaemonSocketPDUHeader&,
@ -862,8 +842,7 @@ private:
}
nsRefPtr<BluetoothResultHandler> res =
already_AddRefed<BluetoothResultHandler>(
static_cast<BluetoothResultHandler*>(aUserData));
static_cast<BluetoothResultHandler*>(aRes);
if (!res) {
return; // Return early if no result handler has been set for response
@ -1345,7 +1324,7 @@ private:
}
void HandleNtf(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU, void* aUserData)
DaemonSocketPDU& aPDU, DaemonSocketResultHandler* aRes)
{
static void (BluetoothDaemonCoreModule::* const HandleNtf[])(
const DaemonSocketPDUHeader&, DaemonSocketPDU&) = {
@ -1453,35 +1432,44 @@ public:
// Outgoing PDUs
//
nsresult Send(DaemonSocketPDU* aPDU, void* aUserData) override;
nsresult Send(DaemonSocketPDU* aPDU,
DaemonSocketResultHandler* aRes) override;
void StoreUserData(const DaemonSocketPDU& aPDU) override;
void StoreResultHandler(const DaemonSocketPDU& aPDU) override;
// Incoming PUDs
//
void Handle(DaemonSocketPDU& aPDU) override;
void* FetchUserData(const DaemonSocketPDUHeader& aHeader);
already_AddRefed<DaemonSocketResultHandler> FetchResultHandler(
const DaemonSocketPDUHeader& aHeader);
private:
void HandleSetupSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU, void* aUserData);
DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aRes);
void HandleCoreSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU, void* aUserData);
DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aRes);
void HandleSocketSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU, void* aUserData);
DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aRes);
void HandleHandsfreeSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU, void* aUserData);
DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aRes);
void HandleA2dpSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU, void* aUserData);
DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aUserData);
void HandleAvrcpSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU, void* aUserData);
DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aRes);
void HandleGattSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU, void* aUserData);
DaemonSocketPDU& aPDU,
DaemonSocketResultHandler* aRes);
DaemonSocket* mConnection;
nsTArray<void*> mUserDataQ;
nsTArray<nsRefPtr<DaemonSocketResultHandler>> mResQ;
};
BluetoothDaemonProtocol::BluetoothDaemonProtocol()
@ -1510,13 +1498,14 @@ BluetoothDaemonProtocol::UnregisterModule(uint8_t aId,
}
nsresult
BluetoothDaemonProtocol::Send(DaemonSocketPDU* aPDU, void* aUserData)
BluetoothDaemonProtocol::Send(DaemonSocketPDU* aPDU,
DaemonSocketResultHandler* aRes)
{
MOZ_ASSERT(mConnection);
MOZ_ASSERT(aPDU);
aPDU->SetConsumer(this);
aPDU->SetUserData(aUserData);
aPDU->SetResultHandler(aRes);
aPDU->UpdateHeader();
if (mConnection->GetConnectionStatus() == SOCKET_DISCONNECTED) {
@ -1532,64 +1521,65 @@ BluetoothDaemonProtocol::Send(DaemonSocketPDU* aPDU, void* aUserData)
void
BluetoothDaemonProtocol::HandleSetupSvc(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
void* aUserData)
DaemonSocketResultHandler* aRes)
{
BluetoothDaemonSetupModule::HandleSvc(aHeader, aPDU, aUserData);
BluetoothDaemonSetupModule::HandleSvc(aHeader, aPDU, aRes);
}
void
BluetoothDaemonProtocol::HandleCoreSvc(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
void* aUserData)
DaemonSocketResultHandler* aRes)
{
BluetoothDaemonCoreModule::HandleSvc(aHeader, aPDU, aUserData);
BluetoothDaemonCoreModule::HandleSvc(aHeader, aPDU, aRes);
}
void
BluetoothDaemonProtocol::HandleSocketSvc(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
void* aUserData)
DaemonSocketResultHandler* aRes)
{
BluetoothDaemonSocketModule::HandleSvc(aHeader, aPDU, aUserData);
BluetoothDaemonSocketModule::HandleSvc(aHeader, aPDU, aRes);
}
void
BluetoothDaemonProtocol::HandleHandsfreeSvc(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
void* aUserData)
DaemonSocketResultHandler* aRes)
{
BluetoothDaemonHandsfreeModule::HandleSvc(aHeader, aPDU, aUserData);
BluetoothDaemonHandsfreeModule::HandleSvc(aHeader, aPDU, aRes);
}
void
BluetoothDaemonProtocol::HandleA2dpSvc(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
void* aUserData)
DaemonSocketResultHandler* aRes)
{
BluetoothDaemonA2dpModule::HandleSvc(aHeader, aPDU, aUserData);
BluetoothDaemonA2dpModule::HandleSvc(aHeader, aPDU, aRes);
}
void
BluetoothDaemonProtocol::HandleAvrcpSvc(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
void* aUserData)
DaemonSocketResultHandler* aRes)
{
BluetoothDaemonAvrcpModule::HandleSvc(aHeader, aPDU, aUserData);
BluetoothDaemonAvrcpModule::HandleSvc(aHeader, aPDU, aRes);
}
void
BluetoothDaemonProtocol::HandleGattSvc(
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
void* aUserData)
DaemonSocketResultHandler* aRes)
{
BluetoothDaemonGattModule::HandleSvc(aHeader, aPDU, aUserData);
BluetoothDaemonGattModule::HandleSvc(aHeader, aPDU, aRes);
}
void
BluetoothDaemonProtocol::Handle(DaemonSocketPDU& aPDU)
{
static void (BluetoothDaemonProtocol::* const HandleSvc[])(
const DaemonSocketPDUHeader&, DaemonSocketPDU&, void*) = {
const DaemonSocketPDUHeader&, DaemonSocketPDU&,
DaemonSocketResultHandler*) = {
[0x00] = &BluetoothDaemonProtocol::HandleSetupSvc,
[0x01] = &BluetoothDaemonProtocol::HandleCoreSvc,
[0x02] = &BluetoothDaemonProtocol::HandleSocketSvc,
@ -1614,19 +1604,22 @@ BluetoothDaemonProtocol::Handle(DaemonSocketPDU& aPDU)
return;
}
(this->*(HandleSvc[header.mService]))(header, aPDU, FetchUserData(header));
nsRefPtr<DaemonSocketResultHandler> res = FetchResultHandler(header);
(this->*(HandleSvc[header.mService]))(header, aPDU, res);
}
void
BluetoothDaemonProtocol::StoreUserData(const DaemonSocketPDU& aPDU)
BluetoothDaemonProtocol::StoreResultHandler(const DaemonSocketPDU& aPDU)
{
MOZ_ASSERT(!NS_IsMainThread());
mUserDataQ.AppendElement(aPDU.GetUserData());
mResQ.AppendElement(aPDU.GetResultHandler());
}
void*
BluetoothDaemonProtocol::FetchUserData(const DaemonSocketPDUHeader& aHeader)
already_AddRefed<DaemonSocketResultHandler>
BluetoothDaemonProtocol::FetchResultHandler(
const DaemonSocketPDUHeader& aHeader)
{
MOZ_ASSERT(!NS_IsMainThread());
@ -1634,10 +1627,10 @@ BluetoothDaemonProtocol::FetchUserData(const DaemonSocketPDUHeader& aHeader)
return nullptr; // Ignore notifications
}
void* userData = mUserDataQ.ElementAt(0);
mUserDataQ.RemoveElementAt(0);
nsRefPtr<DaemonSocketResultHandler> userData = mResQ.ElementAt(0);
mResQ.RemoveElementAt(0);
return userData;
return userData.forget();
}
//

View File

@ -46,80 +46,82 @@ public:
static BluetoothDaemonInterface* GetInstance();
void Init(BluetoothNotificationHandler* aNotificationHandler,
BluetoothResultHandler* aRes);
void Cleanup(BluetoothResultHandler* aRes);
BluetoothResultHandler* aRes) override;
void Cleanup(BluetoothResultHandler* aRes) override;
void Enable(BluetoothResultHandler* aRes);
void Disable(BluetoothResultHandler* aRes);
void Enable(BluetoothResultHandler* aRes) override;
void Disable(BluetoothResultHandler* aRes) override;
/* Adapter Properties */
void GetAdapterProperties(BluetoothResultHandler* aRes);
void GetAdapterProperties(BluetoothResultHandler* aRes) override;
void GetAdapterProperty(const nsAString& aName,
BluetoothResultHandler* aRes);
BluetoothResultHandler* aRes) override;
void SetAdapterProperty(const BluetoothNamedValue& aProperty,
BluetoothResultHandler* aRes);
BluetoothResultHandler* aRes) override;
/* Remote Device Properties */
void GetRemoteDeviceProperties(const nsAString& aRemoteAddr,
BluetoothResultHandler* aRes);
BluetoothResultHandler* aRes) override;
void GetRemoteDeviceProperty(const nsAString& aRemoteAddr,
const nsAString& aName,
BluetoothResultHandler* aRes);
BluetoothResultHandler* aRes) override;
void SetRemoteDeviceProperty(const nsAString& aRemoteAddr,
const BluetoothNamedValue& aProperty,
BluetoothResultHandler* aRes);
BluetoothResultHandler* aRes) override;
/* Remote Services */
void GetRemoteServiceRecord(const nsAString& aRemoteAddr,
const uint8_t aUuid[16],
BluetoothResultHandler* aRes);
BluetoothResultHandler* aRes) override;
void GetRemoteServices(const nsAString& aRemoteAddr,
BluetoothResultHandler* aRes);
BluetoothResultHandler* aRes) override;
/* Discovery */
void StartDiscovery(BluetoothResultHandler* aRes);
void CancelDiscovery(BluetoothResultHandler* aRes);
void StartDiscovery(BluetoothResultHandler* aRes) override;
void CancelDiscovery(BluetoothResultHandler* aRes) override;
/* Bonds */
void CreateBond(const nsAString& aBdAddr, BluetoothTransport aTransport,
BluetoothResultHandler* aRes);
void RemoveBond(const nsAString& aBdAddr, BluetoothResultHandler* aRes);
void CancelBond(const nsAString& aBdAddr, BluetoothResultHandler* aRes);
BluetoothResultHandler* aRes) override;
void RemoveBond(const nsAString& aBdAddr,
BluetoothResultHandler* aRes) override;
void CancelBond(const nsAString& aBdAddr,
BluetoothResultHandler* aRes) override;
/* Connection */
void GetConnectionState(const nsAString& aBdAddr,
BluetoothResultHandler* aRes);
BluetoothResultHandler* aRes) override;
/* Authentication */
void PinReply(const nsAString& aBdAddr, bool aAccept,
const nsAString& aPinCode,
BluetoothResultHandler* aRes);
BluetoothResultHandler* aRes) override;
void SspReply(const nsAString& aBdAddr, BluetoothSspVariant aVariant,
bool aAccept, uint32_t aPasskey,
BluetoothResultHandler* aRes);
BluetoothResultHandler* aRes) override;
/* DUT Mode */
void DutModeConfigure(bool aEnable, BluetoothResultHandler* aRes);
void DutModeSend(uint16_t aOpcode, uint8_t* aBuf, uint8_t aLen,
BluetoothResultHandler* aRes);
BluetoothResultHandler* aRes) override;
/* LE Mode */
void LeTestMode(uint16_t aOpcode, uint8_t* aBuf, uint8_t aLen,
BluetoothResultHandler* aRes);
BluetoothResultHandler* aRes) override;
/* Energy Information */
void ReadEnergyInfo(BluetoothResultHandler* aRes);
void ReadEnergyInfo(BluetoothResultHandler* aRes) override;
/* Profile Interfaces */

View File

@ -8,14 +8,14 @@
#define mozilla_dom_bluetooth_bluedroid_BluetoothDaemonSetupInterface_h
#include "BluetoothCommon.h"
#include "mozilla/ipc/DaemonSocketMessageHandlers.h"
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothSetupResultHandler
: public mozilla::ipc::DaemonSocketResultHandler
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BluetoothSetupResultHandler)
virtual void OnError(BluetoothStatus aStatus);
virtual void RegisterModule();
virtual void UnregisterModule();

View File

@ -160,7 +160,7 @@ BluetoothDaemonSocketModule::CloseCmd(BluetoothSocketResultHandler* aRes)
void
BluetoothDaemonSocketModule::HandleSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU,
void* aUserData)
DaemonSocketResultHandler* aRes)
{
static void (BluetoothDaemonSocketModule::* const HandleRsp[])(
const DaemonSocketPDUHeader&,
@ -177,8 +177,7 @@ BluetoothDaemonSocketModule::HandleSvc(const DaemonSocketPDUHeader& aHeader,
}
nsRefPtr<BluetoothSocketResultHandler> res =
already_AddRefed<BluetoothSocketResultHandler>(
static_cast<BluetoothSocketResultHandler*>(aUserData));
static_cast<BluetoothSocketResultHandler*>(aRes);
if (!res) {
return; // Return early if no result handler has been set
@ -187,19 +186,6 @@ BluetoothDaemonSocketModule::HandleSvc(const DaemonSocketPDUHeader& aHeader,
(this->*(HandleRsp[aHeader.mOpcode]))(aHeader, aPDU, res);
}
nsresult
BluetoothDaemonSocketModule::Send(DaemonSocketPDU* aPDU,
BluetoothSocketResultHandler* aRes)
{
nsRefPtr<BluetoothSocketResultHandler> res(aRes);
nsresult rv = Send(aPDU, static_cast<void*>(res.get()));
if (NS_FAILED(rv)) {
return rv;
}
unused << res.forget(); // Keep reference for response
return NS_OK;
}
uint8_t
BluetoothDaemonSocketModule::SocketFlags(bool aEncrypt, bool aAuth)
{

View File

@ -15,13 +15,15 @@ BEGIN_BLUETOOTH_NAMESPACE
using mozilla::ipc::DaemonSocketPDU;
using mozilla::ipc::DaemonSocketPDUHeader;
using mozilla::ipc::DaemonSocketResultHandler;
class BluetoothDaemonSocketModule
{
public:
static const int MAX_NUM_CLIENTS;
virtual nsresult Send(DaemonSocketPDU* aPDU, void* aUserData) = 0;
virtual nsresult Send(DaemonSocketPDU* aPDU,
DaemonSocketResultHandler* aRes) = 0;
// Commands
//
@ -45,9 +47,7 @@ public:
protected:
void HandleSvc(const DaemonSocketPDUHeader& aHeader,
DaemonSocketPDU& aPDU, void* aUserData);
nsresult Send(DaemonSocketPDU* aPDU, BluetoothSocketResultHandler* aRes);
DaemonSocketPDU& aPDU, DaemonSocketResultHandler* aRes);
private:
class AcceptWatcher;
@ -100,17 +100,17 @@ public:
const nsAString& aServiceName,
const uint8_t aServiceUuid[16],
int aChannel, bool aEncrypt, bool aAuth,
BluetoothSocketResultHandler* aRes);
BluetoothSocketResultHandler* aRes) override;
void Connect(const nsAString& aBdAddr,
BluetoothSocketType aType,
const uint8_t aUuid[16],
int aChannel, bool aEncrypt, bool aAuth,
BluetoothSocketResultHandler* aRes);
BluetoothSocketResultHandler* aRes) override;
void Accept(int aFd, BluetoothSocketResultHandler* aRes);
void Accept(int aFd, BluetoothSocketResultHandler* aRes) override;
void Close(BluetoothSocketResultHandler* aRes);
void Close(BluetoothSocketResultHandler* aRes) override;
private:
void DispatchError(BluetoothSocketResultHandler* aRes,

View File

@ -1399,9 +1399,7 @@ BluetoothHfpManager::ConnectionStateNotification(
} else if (aState == HFP_CONNECTION_STATE_CONNECTED) {
// Once RFCOMM is connected, enable NREC before each new SLC connection
mNrecEnabled = HFP_NREC_STARTED;
NotifyConnectionStateChanged(
NS_LITERAL_STRING(BLUETOOTH_HFP_NREC_STATUS_CHANGED_ID));
NRECNotification(HFP_NREC_STARTED, mDeviceAddress);
}
}
@ -1478,6 +1476,12 @@ BluetoothHfpManager::DtmfNotification(char aDtmf, const nsAString& aBdAddress)
NotifyDialer(NS_ConvertUTF8toUTF16(message));
}
/**
* NREC status will be set when:
* 1. Get an AT command from HF device.
* (Bluetooth HFP spec v1.6 merely defines for the "Disable" part.)
* 2. Once RFCOMM is connected, enable NREC before each new SLC connection.
*/
void
BluetoothHfpManager::NRECNotification(BluetoothHandsfreeNRECState aNrec,
const nsAString& aBdAddr)
@ -1488,7 +1492,6 @@ BluetoothHfpManager::NRECNotification(BluetoothHandsfreeNRECState aNrec,
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
NS_ENSURE_TRUE_VOID(obs);
// Set NREC status once getting AT command
mNrecEnabled = static_cast<bool>(aNrec);
// Notify audio manager

View File

@ -9,6 +9,7 @@
#include "BluetoothCommon.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
#include "mozilla/ipc/DaemonSocketMessageHandlers.h"
BEGIN_BLUETOOTH_NAMESPACE
@ -17,10 +18,9 @@ BEGIN_BLUETOOTH_NAMESPACE
//
class BluetoothSocketResultHandler
: public mozilla::ipc::DaemonSocketResultHandler
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BluetoothSocketResultHandler)
virtual void OnError(BluetoothStatus aStatus)
{
BT_WARNING("Received error code %d", (int)aStatus);
@ -155,10 +155,9 @@ protected:
};
class BluetoothHandsfreeResultHandler
: public mozilla::ipc::DaemonSocketResultHandler
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BluetoothHandsfreeResultHandler)
virtual void OnError(BluetoothStatus aStatus)
{
BT_WARNING("Received error code %d", (int)aStatus);
@ -303,10 +302,9 @@ protected:
};
class BluetoothA2dpResultHandler
: public mozilla::ipc::DaemonSocketResultHandler
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BluetoothA2dpResultHandler)
virtual void OnError(BluetoothStatus aStatus)
{
BT_WARNING("Received error code %d", (int)aStatus);
@ -406,10 +404,9 @@ protected:
};
class BluetoothAvrcpResultHandler
: public mozilla::ipc::DaemonSocketResultHandler
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BluetoothAvrcpResultHandler)
virtual void OnError(BluetoothStatus aStatus)
{
BT_WARNING("Received error code %d", (int)aStatus);
@ -720,10 +717,9 @@ protected:
};
class BluetoothGattResultHandler
: public mozilla::ipc::DaemonSocketResultHandler
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BluetoothGattResultHandler)
virtual void OnError(BluetoothStatus aStatus)
{
BT_WARNING("Received error code %d", (int)aStatus);
@ -1048,10 +1044,9 @@ protected:
};
class BluetoothResultHandler
: public mozilla::ipc::DaemonSocketResultHandler
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BluetoothResultHandler)
virtual void OnError(BluetoothStatus aStatus)
{
BT_LOGR("Received error code %d", aStatus);

View File

@ -23,7 +23,7 @@ public:
virtual ~DaemonSocketIOConsumer();
virtual void Handle(DaemonSocketPDU& aPDU) = 0;
virtual void StoreUserData(const DaemonSocketPDU& aPDU) = 0;
virtual void StoreResultHandler(const DaemonSocketPDU& aPDU) = 0;
protected:
DaemonSocketIOConsumer();

View File

@ -0,0 +1,40 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Message handlers
*
* This file contains base classes for message handling.
*/
#ifndef mozilla_ipc_DaemonSocketMessageHandlers_h
#define mozilla_ipc_DaemonSocketMessageHandlers_h
#include "nsISupportsImpl.h" // for ref-counting
namespace mozilla {
namespace ipc {
/**
* |DaemonSocketResultHandler| is the base class for all protocol-specific
* result handlers. It currently only manages the reference counting.
*/
class DaemonSocketResultHandler
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DaemonSocketResultHandler);
protected:
DaemonSocketResultHandler()
{ }
virtual ~DaemonSocketResultHandler()
{ }
};
} // namespace ipc
} // namespace mozilla
#endif // mozilla_ipc_DaemonSocketMessageHandlers_h

View File

@ -29,9 +29,8 @@ namespace ipc {
//
DaemonSocketPDU::DaemonSocketPDU(uint8_t aService, uint8_t aOpcode,
uint16_t aPayloadSize)
uint16_t aPayloadSize)
: mConsumer(nullptr)
, mUserData(nullptr)
{
MOZ_COUNT_CTOR_INHERITED(DaemonSocketPDU, UnixSocketIOBuffer);
@ -51,7 +50,6 @@ DaemonSocketPDU::DaemonSocketPDU(uint8_t aService, uint8_t aOpcode,
DaemonSocketPDU::DaemonSocketPDU(size_t aPayloadSize)
: mConsumer(nullptr)
, mUserData(nullptr)
{
MOZ_COUNT_CTOR_INHERITED(DaemonSocketPDU, UnixSocketIOBuffer);
@ -102,8 +100,8 @@ DaemonSocketPDU::Send(int aFd)
if (mConsumer) {
// We successfully sent a PDU, now store the
// result runnable in the consumer.
mConsumer->StoreUserData(*this);
// result handler in the consumer.
mConsumer->StoreResultHandler(*this);
}
return res;

View File

@ -9,6 +9,7 @@
#include "mozilla/FileUtils.h"
#include "mozilla/ipc/SocketBase.h"
#include "mozilla/ipc/DaemonSocketMessageHandlers.h"
namespace mozilla {
namespace ipc {
@ -55,14 +56,14 @@ public:
mConsumer = aConsumer;
}
void SetUserData(void* aUserData)
void SetResultHandler(DaemonSocketResultHandler* aRes)
{
mUserData = aUserData;
mRes = aRes;
}
void* GetUserData() const
DaemonSocketResultHandler* GetResultHandler() const
{
return mUserData;
return mRes;
}
void GetHeader(uint8_t& aService, uint8_t& aOpcode,
@ -80,7 +81,7 @@ private:
void OnError(const char* aFunction, int aErrno);
DaemonSocketIOConsumer* mConsumer;
void* mUserData;
nsRefPtr<DaemonSocketResultHandler> mRes;
ScopedClose mReceivedFd;
};

View File

@ -9,6 +9,7 @@ EXPORTS.mozilla.ipc += [
'DaemonSocket.h',
'DaemonSocketConnector.h',
'DaemonSocketConsumer.h',
'DaemonSocketMessageHandlers.h',
'DaemonSocketPDU.h',
'DaemonSocketPDUHelpers.h'
]

View File

@ -0,0 +1,254 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.fxa.login.Engaged;
import org.mozilla.gecko.fxa.login.State;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.setup.SyncAccounts;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.NativeEventListener;
import org.mozilla.gecko.util.NativeJSObject;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
/**
* Helper class to manage Android Accounts corresponding to Firefox Accounts.
*/
public class AccountsHelper implements NativeEventListener {
public static final String LOGTAG = "GeckoAccounts";
protected final Context mContext;
protected final GeckoProfile mProfile;
public AccountsHelper(Context context, GeckoProfile profile) {
mContext = context;
mProfile = profile;
EventDispatcher dispatcher = EventDispatcher.getInstance();
if (dispatcher == null) {
Log.e(LOGTAG, "Gecko event dispatcher must not be null", new RuntimeException());
return;
}
dispatcher.registerGeckoThreadListener(this,
"Accounts:CreateFirefoxAccountFromJSON",
"Accounts:UpdateFirefoxAccountFromJSON",
"Accounts:Create",
"Accounts:DeleteFirefoxAccount",
"Accounts:Exist");
}
public synchronized void uninit() {
EventDispatcher dispatcher = EventDispatcher.getInstance();
if (dispatcher == null) {
Log.e(LOGTAG, "Gecko event dispatcher must not be null", new RuntimeException());
return;
}
dispatcher.unregisterGeckoThreadListener(this,
"Accounts:CreateFirefoxAccountFromJSON",
"Accounts:UpdateFirefoxAccountFromJSON",
"Accounts:Create",
"Accounts:DeleteFirefoxAccount",
"Accounts:Exist");
}
@Override
public void handleMessage(String event, NativeJSObject message, final EventCallback callback) {
if ("Accounts:CreateFirefoxAccountFromJSON".equals(event)) {
AndroidFxAccount fxAccount = null;
try {
final NativeJSObject json = message.getObject("json");
final String email = json.getString("email");
final String uid = json.getString("uid");
final boolean verified = json.optBoolean("verified", false);
final byte[] unwrapkB = Utils.hex2Byte(json.getString("unwrapBKey"));
final byte[] sessionToken = Utils.hex2Byte(json.getString("sessionToken"));
final byte[] keyFetchToken = Utils.hex2Byte(json.getString("keyFetchToken"));
final String authServerEndpoint =
json.optString("authServerEndpoint", FxAccountConstants.DEFAULT_AUTH_SERVER_ENDPOINT);
final String tokenServerEndpoint =
json.optString("tokenServerEndpoint", FxAccountConstants.DEFAULT_TOKEN_SERVER_ENDPOINT);
final String profileServerEndpoint =
json.optString("profileServerEndpoint", FxAccountConstants.DEFAULT_PROFILE_SERVER_ENDPOINT);
// TODO: handle choose what to Sync.
State state = new Engaged(email, uid, verified, unwrapkB, sessionToken, keyFetchToken);
fxAccount = AndroidFxAccount.addAndroidAccount(mContext,
email,
mProfile.getName(),
authServerEndpoint,
tokenServerEndpoint,
profileServerEndpoint,
state,
AndroidFxAccount.DEFAULT_AUTHORITIES_TO_SYNC_AUTOMATICALLY_MAP);
} catch (URISyntaxException | GeneralSecurityException | UnsupportedEncodingException e) {
Log.w(LOGTAG, "Got exception creating Firefox Account from JSON; ignoring.", e);
if (callback != null) {
callback.sendError("Could not create Firefox Account from JSON: " + e.toString());
return;
}
}
if (callback != null) {
callback.sendSuccess(fxAccount != null);
}
} else if ("Accounts:UpdateFirefoxAccountFromJSON".equals(event)) {
try {
final Account account = FirefoxAccounts.getFirefoxAccount(mContext);
if (account == null) {
if (callback != null) {
callback.sendError("Could not update Firefox Account since none exists");
}
return;
}
final NativeJSObject json = message.getObject("json");
final String email = json.getString("email");
final String uid = json.getString("uid");
// Protect against cross-connecting accounts.
if (account.name == null || !account.name.equals(email)) {
final String errorMessage = "Cannot update Firefox Account from JSON: datum has different email address!";
Log.e(LOGTAG, errorMessage);
if (callback != null) {
callback.sendError(errorMessage);
}
return;
}
final boolean verified = json.optBoolean("verified", false);
final byte[] unwrapkB = Utils.hex2Byte(json.getString("unwrapBKey"));
final byte[] sessionToken = Utils.hex2Byte(json.getString("sessionToken"));
final byte[] keyFetchToken = Utils.hex2Byte(json.getString("keyFetchToken"));
final State state = new Engaged(email, uid, verified, unwrapkB, sessionToken, keyFetchToken);
final AndroidFxAccount fxAccount = new AndroidFxAccount(mContext, account);
fxAccount.setState(state);
if (callback != null) {
callback.sendSuccess(true);
}
} catch (NativeJSObject.InvalidPropertyException e) {
Log.w(LOGTAG, "Got exception updating Firefox Account from JSON; ignoring.", e);
if (callback != null) {
callback.sendError("Could not update Firefox Account from JSON: " + e.toString());
return;
}
}
} else if ("Accounts:Create".equals(event)) {
// Do exactly the same thing as if you tapped 'Sync' in Settings.
final Intent intent = new Intent(FxAccountConstants.ACTION_FXA_GET_STARTED);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final NativeJSObject extras = message.optObject("extras", null);
if (extras != null) {
intent.putExtra("extras", extras.toString());
}
mContext.startActivity(intent);
} else if ("Accounts:DeleteFirefoxAccount".equals(event)) {
try {
final Account account = FirefoxAccounts.getFirefoxAccount(mContext);
if (account == null) {
Log.w(LOGTAG, "Could not delete Firefox Account since none exists!");
if (callback != null) {
callback.sendError("Could not delete Firefox Account since none exists");
}
return;
}
final AccountManagerCallback<Boolean> accountManagerCallback = new AccountManagerCallback<Boolean>() {
@Override
public void run(AccountManagerFuture<Boolean> future) {
try {
final boolean result = future.getResult();
Log.i(LOGTAG, "Account named like " + Utils.obfuscateEmail(account.name) + " removed: " + result);
if (callback != null) {
callback.sendSuccess(result);
}
} catch (OperationCanceledException | IOException | AuthenticatorException e) {
if (callback != null) {
callback.sendError("Could not delete Firefox Account: " + e.toString());
}
}
}
};
AccountManager.get(mContext).removeAccount(account, accountManagerCallback, null);
} catch (Exception e) {
Log.w(LOGTAG, "Got exception updating Firefox Account from JSON; ignoring.", e);
if (callback != null) {
callback.sendError("Could not update Firefox Account from JSON: " + e.toString());
return;
}
}
} else if ("Accounts:Exist".equals(event)) {
if (callback == null) {
Log.w(LOGTAG, "Accounts:Exist requires a callback");
return;
}
final String kind = message.optString("kind", null);
final JSONObject response = new JSONObject();
try {
if ("any".equals(kind)) {
response.put("exists", SyncAccounts.syncAccountsExist(mContext) ||
FirefoxAccounts.firefoxAccountsExist(mContext));
callback.sendSuccess(response);
} else if ("fxa".equals(kind)) {
final Account account = FirefoxAccounts.getFirefoxAccount(mContext);
response.put("exists", account != null);
if (account != null) {
response.put("email", account.name);
// We should always be able to extract the server endpoints.
final AndroidFxAccount fxAccount = new AndroidFxAccount(mContext, account);
response.put("authServerEndpoint", fxAccount.getAccountServerURI());
response.put("profileServerEndpoint", fxAccount.getProfileServerURI());
response.put("tokenServerEndpoint", fxAccount.getTokenServerURI());
try {
// It is possible for the state fetch to fail and us to not be able to provide a UID.
// Long term, the UID (and verification flag) will be attached to the Android account
// user data and not the internal state representation.
final State state = fxAccount.getState();
response.put("uid", state.uid);
} catch (Exception e) {
Log.w(LOGTAG, "Got exception extracting account UID; ignoring.", e);
}
}
callback.sendSuccess(response);
} else if ("sync11".equals(kind)) {
response.put("exists", SyncAccounts.syncAccountsExist(mContext));
callback.sendSuccess(response);
} else {
callback.sendError("Could not query account existence: unknown kind.");
}
} catch (JSONException e) {
Log.w(LOGTAG, "Got exception querying account existence; ignoring.", e);
callback.sendError("Could not query account existence: " + e.toString());
return;
}
}
}
}

View File

@ -24,12 +24,6 @@ import org.mozilla.gecko.favicons.LoadFaviconTask;
import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
import org.mozilla.gecko.favicons.decoders.IconDirectoryEntry;
import org.mozilla.gecko.firstrun.FirstrunPane;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.fxa.login.Engaged;
import org.mozilla.gecko.fxa.login.State;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.gfx.DynamicToolbarAnimator;
import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
@ -57,9 +51,7 @@ import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.gecko.prompts.Prompt;
import org.mozilla.gecko.prompts.PromptListItem;
import org.mozilla.gecko.restrictions.Restriction;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.repositories.android.FennecTabsRepository;
import org.mozilla.gecko.sync.setup.SyncAccounts;
import org.mozilla.gecko.tabqueue.TabQueueHelper;
import org.mozilla.gecko.tabqueue.TabQueuePrompt;
import org.mozilla.gecko.tabs.TabHistoryController;
@ -262,6 +254,8 @@ public class BrowserApp extends GeckoApp
private ReadingListHelper mReadingListHelper;
private AccountsHelper mAccountsHelper;
// The tab to be selected on editing mode exit.
private Integer mTargetTabForEditingMode;
@ -839,12 +833,9 @@ public class BrowserApp extends GeckoApp
"Menu:Update",
"LightweightTheme:Update",
"Search:Keyword",
"Prompt:ShowTop",
"Accounts:Exist");
"Prompt:ShowTop");
EventDispatcher.getInstance().registerGeckoThreadListener((NativeEventListener)this,
"Accounts:Create",
"Accounts:CreateFirefoxAccountFromJSON",
"CharEncoding:Data",
"CharEncoding:State",
"Favicon:CacheLoad",
@ -872,6 +863,7 @@ public class BrowserApp extends GeckoApp
mOrderedBroadcastHelper = new OrderedBroadcastHelper(appContext);
mBrowserHealthReporter = new BrowserHealthReporter();
mReadingListHelper = new ReadingListHelper(appContext, getProfile(), this);
mAccountsHelper = new AccountsHelper(appContext, getProfile());
if (AppConstants.MOZ_ANDROID_BEAM) {
NfcAdapter nfc = NfcAdapter.getDefaultAdapter(this);
@ -1415,6 +1407,12 @@ public class BrowserApp extends GeckoApp
mReadingListHelper.uninit();
mReadingListHelper = null;
}
if (mAccountsHelper != null) {
mAccountsHelper.uninit();
mAccountsHelper = null;
}
if (mZoomedView != null) {
mZoomedView.destroy();
}
@ -1424,12 +1422,9 @@ public class BrowserApp extends GeckoApp
"Menu:Update",
"LightweightTheme:Update",
"Search:Keyword",
"Prompt:ShowTop",
"Accounts:Exist");
"Prompt:ShowTop");
EventDispatcher.getInstance().unregisterGeckoThreadListener((NativeEventListener) this,
"Accounts:Create",
"Accounts:CreateFirefoxAccountFromJSON",
"CharEncoding:Data",
"CharEncoding:State",
"Favicon:CacheLoad",
@ -1688,53 +1683,7 @@ public class BrowserApp extends GeckoApp
@Override
public void handleMessage(final String event, final NativeJSObject message,
final EventCallback callback) {
if ("Accounts:CreateFirefoxAccountFromJSON".equals(event)) {
AndroidFxAccount fxAccount = null;
try {
final NativeJSObject json = message.getObject("json");
final String email = json.getString("email");
final String uid = json.getString("uid");
final boolean verified = json.optBoolean("verified", false);
final byte[] unwrapkB = Utils.hex2Byte(json.getString("unwrapBKey"));
final byte[] sessionToken = Utils.hex2Byte(json.getString("sessionToken"));
final byte[] keyFetchToken = Utils.hex2Byte(json.getString("keyFetchToken"));
final String authServerEndpoint =
json.optString("authServerEndpoint", FxAccountConstants.DEFAULT_AUTH_SERVER_ENDPOINT);
final String tokenServerEndpoint =
json.optString("tokenServerEndpoint", FxAccountConstants.DEFAULT_TOKEN_SERVER_ENDPOINT);
final String profileServerEndpoint =
json.optString("profileServerEndpoint", FxAccountConstants.DEFAULT_PROFILE_SERVER_ENDPOINT);
// TODO: handle choose what to Sync.
State state = new Engaged(email, uid, verified, unwrapkB, sessionToken, keyFetchToken);
fxAccount = AndroidFxAccount.addAndroidAccount(this,
email,
getProfile().getName(),
authServerEndpoint,
tokenServerEndpoint,
profileServerEndpoint,
state,
AndroidFxAccount.DEFAULT_AUTHORITIES_TO_SYNC_AUTOMATICALLY_MAP);
} catch (Exception e) {
Log.w(LOGTAG, "Got exception creating Firefox Account from JSON; ignoring.", e);
if (callback == null) {
callback.sendError("Could not create Firefox Account from JSON: " + e.toString());
}
}
if (callback != null) {
callback.sendSuccess(fxAccount != null);
}
} else if ("Accounts:Create".equals(event)) {
// Do exactly the same thing as if you tapped 'Sync' in Settings.
final Intent intent = new Intent(getContext(), FxAccountGetStartedActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final NativeJSObject extras = message.optObject("extras", null);
if (extras != null) {
intent.putExtra("extras", extras.toString());
}
getContext().startActivity(intent);
} else if ("CharEncoding:Data".equals(event)) {
if ("CharEncoding:Data".equals(event)) {
final NativeJSObject[] charsets = message.getObjectArray("charsets");
final int selected = message.getInt("selected");
@ -2033,24 +1982,6 @@ public class BrowserApp extends GeckoApp
bringToFrontIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
bringToFrontIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(bringToFrontIntent);
} else if (event.equals("Accounts:Exist")) {
final String kind = message.getString("kind");
final JSONObject response = new JSONObject();
if ("any".equals(kind)) {
response.put("exists", SyncAccounts.syncAccountsExist(getContext()) ||
FirefoxAccounts.firefoxAccountsExist(getContext()));
EventDispatcher.sendResponse(message, response);
} else if ("fxa".equals(kind)) {
response.put("exists", FirefoxAccounts.firefoxAccountsExist(getContext()));
EventDispatcher.sendResponse(message, response);
} else if ("sync11".equals(kind)) {
response.put("exists", SyncAccounts.syncAccountsExist(getContext()));
EventDispatcher.sendResponse(message, response);
} else {
response.put("error", "Unknown kind");
EventDispatcher.sendError(message, response);
}
} else {
super.handleMessage(event, message);
}

View File

@ -32,7 +32,7 @@ public final class ThumbnailHelper {
public static final float TABS_PANEL_THUMBNAIL_ASPECT_RATIO = 0.8333333f;
public static final float TOP_SITES_THUMBNAIL_ASPECT_RATIO = 0.571428571f; // this is a 4:7 ratio (as per UX decision)
private static final float THUMBNAIL_ASPECT_RATIO;
public static final float THUMBNAIL_ASPECT_RATIO;
static {
// As we only want to generate one thumbnail for each tab, we calculate the

View File

@ -853,16 +853,21 @@ sync_java_files = [
'fxa/activities/FxAccountAbstractSetupActivity.java',
'fxa/activities/FxAccountAbstractUpdateCredentialsActivity.java',
'fxa/activities/FxAccountConfirmAccountActivity.java',
'fxa/activities/FxAccountConfirmAccountActivityWeb.java',
'fxa/activities/FxAccountCreateAccountActivity.java',
'fxa/activities/FxAccountCreateAccountNotAllowedActivity.java',
'fxa/activities/FxAccountFinishMigratingActivity.java',
'fxa/activities/FxAccountFinishMigratingActivityWeb.java',
'fxa/activities/FxAccountGetStartedActivity.java',
'fxa/activities/FxAccountGetStartedActivityWeb.java',
'fxa/activities/FxAccountMigrationFinishedActivity.java',
'fxa/activities/FxAccountSignInActivity.java',
'fxa/activities/FxAccountStatusActivity.java',
'fxa/activities/FxAccountStatusFragment.java',
'fxa/activities/FxAccountUpdateCredentialsActivity.java',
'fxa/activities/FxAccountUpdateCredentialsActivityWeb.java',
'fxa/activities/FxAccountVerifiedAccountActivity.java',
'fxa/activities/FxAccountWebFlowActivity.java',
'fxa/activities/PicassoPreferenceIconTarget.java',
'fxa/authenticator/AccountPickler.java',
'fxa/authenticator/AndroidFxAccount.java',

View File

@ -13,7 +13,7 @@ import android.view.ViewGroup;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity;
import org.mozilla.gecko.fxa.FxAccountConstants;
public class WelcomePanel extends FirstrunPanel {
public static final int TITLE_RES = R.string.firstrun_panel_title_welcome;
@ -26,8 +26,9 @@ public class WelcomePanel extends FirstrunPanel {
public void onClick(View v) {
Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.BUTTON, "firstrun-sync");
final Intent accountIntent = new Intent(getActivity(), FxAccountGetStartedActivity.class);
startActivity(accountIntent);
final Intent intent = new Intent(FxAccountConstants.ACTION_FXA_GET_STARTED);
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivity(intent);
close();
}

View File

@ -94,4 +94,10 @@ public class FxAccountConstants {
* Account type.
*/
public static final String ACCOUNT_STATE_CHANGED_ACTION = AppConstants.MOZ_ANDROID_SHARED_FXACCOUNT_TYPE + ".accounts.ACCOUNT_STATE_CHANGED_ACTION";
public static final String ACTION_FXA_CONFIRM_ACCOUNT = AppConstants.ANDROID_PACKAGE_NAME + ".ACTION_FXA_CONFIRM_ACCOUNT";
public static final String ACTION_FXA_FINISH_MIGRATING = AppConstants.ANDROID_PACKAGE_NAME + ".ACTION_FXA_FINISH_MIGRATING";
public static final String ACTION_FXA_GET_STARTED = AppConstants.ANDROID_PACKAGE_NAME + ".ACTION_FXA_GET_STARTED";
public static final String ACTION_FXA_STATUS = AppConstants.ANDROID_PACKAGE_NAME + ".ACTION_FXA_STATUS";
public static final String ACTION_FXA_UPDATE_CREDENTIALS = AppConstants.ANDROID_PACKAGE_NAME + ".ACTION_FXA_UPDATE_CREDENTIALS";
}

View File

@ -4,13 +4,6 @@
package org.mozilla.gecko.fxa.activities;
import org.mozilla.gecko.Locales.LocaleAwareActivity;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.fxa.FxAccountAgeLockoutHelper;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
import android.accounts.Account;
import android.app.Activity;
import android.content.Intent;
@ -18,6 +11,13 @@ import android.os.SystemClock;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
import org.mozilla.gecko.Locales.LocaleAwareActivity;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.fxa.FxAccountAgeLockoutHelper;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
public abstract class FxAccountAbstractActivity extends LocaleAwareActivity {
private static final String LOG_TAG = FxAccountAbstractActivity.class.getSimpleName();
@ -43,26 +43,30 @@ public abstract class FxAccountAbstractActivity extends LocaleAwareActivity {
* exists or if account creation is locked out due to an age verification
* check failing (getting started, create account, sign in). This function
* redirects as appropriate.
*
* @return true if redirected.
*/
protected void redirectIfAppropriate() {
protected boolean redirectIfAppropriate() {
if (cannotResumeWhenAccountsExist || cannotResumeWhenNoAccountsExist) {
final Account account = FirefoxAccounts.getFirefoxAccount(this);
if (cannotResumeWhenAccountsExist && account != null) {
redirectToActivity(FxAccountStatusActivity.class);
return;
redirectToAction(FxAccountConstants.ACTION_FXA_STATUS);
return true;
}
if (cannotResumeWhenNoAccountsExist && account == null) {
redirectToActivity(FxAccountGetStartedActivity.class);
return;
redirectToAction(FxAccountConstants.ACTION_FXA_GET_STARTED);
return true;
}
}
if (cannotResumeWhenLockedOut) {
if (FxAccountAgeLockoutHelper.isLockedOut(SystemClock.elapsedRealtime())) {
this.setResult(RESULT_CANCELED);
redirectToActivity(FxAccountCreateAccountNotAllowedActivity.class);
return;
launchActivity(FxAccountCreateAccountNotAllowedActivity.class);
finish();
return true;
}
}
return false;
}
@Override
@ -85,8 +89,12 @@ public abstract class FxAccountAbstractActivity extends LocaleAwareActivity {
startActivity(intent);
}
protected void redirectToActivity(Class<? extends Activity> activityClass) {
launchActivity(activityClass);
protected void redirectToAction(final String action) {
final Intent intent = new Intent(action);
// Per http://stackoverflow.com/a/8992365, this triggers a known bug with
// the soft keyboard not being shown for the started activity. Why, Android, why?
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivity(intent);
finish();
}

View File

@ -4,9 +4,15 @@
package org.mozilla.gecko.fxa.activities;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import org.mozilla.gecko.R;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.common.telemetry.TelemetryWrapper;
@ -18,6 +24,7 @@ import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClient
import org.mozilla.gecko.background.fxa.FxAccountUtils;
import org.mozilla.gecko.background.fxa.PasswordStretcher;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.fxa.login.Engaged;
import org.mozilla.gecko.fxa.login.State;
@ -25,15 +32,8 @@ import org.mozilla.gecko.fxa.tasks.FxAccountSignInTask;
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
import org.mozilla.gecko.sync.telemetry.TelemetryContract;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* Abstract activity which displays a screen for updating the local password.
@ -109,7 +109,7 @@ public abstract class FxAccountAbstractUpdateCredentialsActivity extends FxAccou
final State state = fxAccount.getState();
fxAccount.setState(state.makeDoghouseState());
// The status activity will say that the user needs to upgrade.
redirectToActivity(FxAccountStatusActivity.class);
redirectToAction(FxAccountConstants.ACTION_FXA_STATUS);
return;
}
showRemoteError(e, R.string.fxaccount_update_credentials_unknown_error);

View File

@ -4,18 +4,6 @@
package org.mozilla.gecko.fxa.activities;
import org.mozilla.gecko.R;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.fxa.SyncStatusListener;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.fxa.login.Engaged;
import org.mozilla.gecko.fxa.login.State;
import org.mozilla.gecko.fxa.login.State.Action;
import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper;
import org.mozilla.gecko.fxa.tasks.FxAccountCodeResender;
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
import android.accounts.Account;
import android.app.Activity;
import android.content.Context;
@ -24,6 +12,18 @@ import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
import org.mozilla.gecko.R;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.fxa.SyncStatusListener;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.fxa.login.Engaged;
import org.mozilla.gecko.fxa.login.State;
import org.mozilla.gecko.fxa.login.State.Action;
import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper;
import org.mozilla.gecko.fxa.tasks.FxAccountCodeResender;
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
/**
* Activity which displays account created successfully screen to the user, and
@ -146,7 +146,7 @@ public class FxAccountConfirmAccountActivity extends FxAccountAbstractActivity i
Logger.warn(LOG_TAG, "No need to verify Firefox Account that needs action " + neededAction.toString() +
" (in state " + state.getStateLabel() + ").");
setResult(RESULT_CANCELED);
this.redirectToActivity(FxAccountStatusActivity.class);
redirectToAction(FxAccountConstants.ACTION_FXA_STATUS);
return;
}

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/. */
package org.mozilla.gecko.fxa.activities;
public class FxAccountConfirmAccountActivityWeb extends FxAccountWebFlowActivity {
public FxAccountConfirmAccountActivityWeb() {
super(CANNOT_RESUME_WHEN_NO_ACCOUNTS_EXIST, "settings");
}
}

View File

@ -429,7 +429,8 @@ public class FxAccountCreateAccountActivity extends FxAccountAbstractSetupActivi
FxAccountUtils.pii(LOG_TAG, "Failed age check!");
FxAccountAgeLockoutHelper.lockOut(SystemClock.elapsedRealtime());
setResult(RESULT_CANCELED);
redirectToActivity(FxAccountCreateAccountNotAllowedActivity.class);
launchActivity(FxAccountCreateAccountNotAllowedActivity.class);
finish();
}
}
});

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/. */
package org.mozilla.gecko.fxa.activities;
public class FxAccountFinishMigratingActivityWeb extends FxAccountWebFlowActivity {
public FxAccountFinishMigratingActivityWeb() {
super(CANNOT_RESUME_WHEN_NO_ACCOUNTS_EXIST, "signin", "migration=sync11");
}
}

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/. */
package org.mozilla.gecko.fxa.activities;
public class FxAccountGetStartedActivityWeb extends FxAccountWebFlowActivity {
public FxAccountGetStartedActivityWeb() {
super(CANNOT_RESUME_WHEN_ACCOUNTS_EXIST, "signin");
}
}

View File

@ -4,15 +4,6 @@
package org.mozilla.gecko.fxa.activities;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.Locales.LocaleAwareFragmentActivity;
import org.mozilla.gecko.R;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.fxa.FxAccountUtils;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.sync.Utils;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
@ -32,6 +23,15 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.Toast;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.Locales.LocaleAwareFragmentActivity;
import org.mozilla.gecko.R;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.fxa.FxAccountUtils;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.sync.Utils;
/**
* Activity which displays account status.
@ -85,7 +85,7 @@ public class FxAccountStatusActivity extends LocaleAwareFragmentActivity {
Logger.warn(LOG_TAG, "Could not get Firefox Account.");
// Gracefully redirect to get started.
Intent intent = new Intent(this, FxAccountGetStartedActivity.class);
final Intent intent = new Intent(FxAccountConstants.ACTION_FXA_GET_STARTED);
// Per http://stackoverflow.com/a/8992365, this triggers a known bug with
// the soft keyboard not being shown for the started activity. Why, Android, why?
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);

View File

@ -4,32 +4,6 @@
package org.mozilla.gecko.fxa.activities;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.R;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.fxa.FxAccountUtils;
import org.mozilla.gecko.background.preferences.PreferenceFragment;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.fxa.SyncStatusListener;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.fxa.login.Married;
import org.mozilla.gecko.fxa.login.State;
import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper;
import org.mozilla.gecko.fxa.tasks.FxAccountCodeResender;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate;
import org.mozilla.gecko.sync.SyncConfiguration;
import org.mozilla.gecko.util.HardwareUtils;
import org.mozilla.gecko.util.ThreadUtils;
import android.accounts.Account;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@ -49,9 +23,33 @@ import android.preference.PreferenceScreen;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.text.format.DateUtils;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.R;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.fxa.FxAccountUtils;
import org.mozilla.gecko.background.preferences.PreferenceFragment;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.fxa.SyncStatusListener;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.fxa.login.Married;
import org.mozilla.gecko.fxa.login.State;
import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper;
import org.mozilla.gecko.fxa.tasks.FxAccountCodeResender;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate;
import org.mozilla.gecko.sync.SyncConfiguration;
import org.mozilla.gecko.util.HardwareUtils;
import org.mozilla.gecko.util.ThreadUtils;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* A fragment that displays the status of an AndroidFxAccount.
@ -237,7 +235,7 @@ public class FxAccountStatusFragment
@Override
public boolean onPreferenceClick(Preference preference) {
if (preference == needsPasswordPreference) {
Intent intent = new Intent(getActivity(), FxAccountUpdateCredentialsActivity.class);
final Intent intent = new Intent(FxAccountConstants.ACTION_FXA_UPDATE_CREDENTIALS);
final Bundle extras = getExtrasForAccount();
if (extras != null) {
intent.putExtras(extras);
@ -251,7 +249,7 @@ public class FxAccountStatusFragment
}
if (preference == needsFinishMigratingPreference) {
final Intent intent = new Intent(getActivity(), FxAccountFinishMigratingActivity.class);
final Intent intent = new Intent(FxAccountConstants.ACTION_FXA_FINISH_MIGRATING);
final Bundle extras = getExtrasForAccount();
if (extras != null) {
intent.putExtras(extras);
@ -265,9 +263,11 @@ public class FxAccountStatusFragment
}
if (preference == needsVerificationPreference) {
FxAccountCodeResender.resendCode(getActivity().getApplicationContext(), fxAccount);
if (AppConstants.MOZ_ANDROID_NATIVE_ACCOUNT_UI) {
FxAccountCodeResender.resendCode(getActivity().getApplicationContext(), fxAccount);
}
Intent intent = new Intent(getActivity(), FxAccountConfirmAccountActivity.class);
final Intent intent = new Intent(FxAccountConstants.ACTION_FXA_CONFIRM_ACCOUNT);
// Per http://stackoverflow.com/a/8992365, this triggers a known bug with
// the soft keyboard not being shown for the started activity. Why, Android, why?
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);

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/. */
package org.mozilla.gecko.fxa.activities;
public class FxAccountUpdateCredentialsActivityWeb extends FxAccountWebFlowActivity {
public FxAccountUpdateCredentialsActivityWeb() {
super(CANNOT_RESUME_WHEN_NO_ACCOUNTS_EXIST, "force_auth");
}
}

View File

@ -0,0 +1,64 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.fxa.activities;
import android.os.Bundle;
import org.mozilla.gecko.Locales;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
/**
* Activity which shows the status activity or passes through to web flow.
*/
public class FxAccountWebFlowActivity extends FxAccountAbstractActivity {
protected static final String LOG_TAG = FxAccountWebFlowActivity.class.getSimpleName();
protected static final String ABOUT_ACCOUNTS = "about:accounts";
private final String action;
private final String extras;
public FxAccountWebFlowActivity(int resume, String action) {
this(resume, action, null);
}
public FxAccountWebFlowActivity(int resume, String action, String extras) {
super(resume);
this.action = action;
this.extras = (extras != null) ? ("&" + extras) : "";
}
/**
* {@inheritDoc}
*/
@Override
public void onCreate(Bundle icicle) {
Logger.setThreadLogTag(FxAccountConstants.GLOBAL_LOG_TAG);
Logger.debug(LOG_TAG, "onCreate(" + icicle + ")");
Locales.initializeLocale(getApplicationContext());
super.onCreate(icicle);
}
protected boolean redirectIfAppropriate() {
final boolean redirected = super.redirectIfAppropriate();
if (redirected) {
return true;
}
ActivityUtils.openURLInFennec(getApplicationContext(),
ABOUT_ACCOUNTS + "?action=" + action + extras);
return true;
}
@Override
public void onResume() {
super.onResume();
// We are always redirected.
this.finish();
}
}

View File

@ -4,10 +4,14 @@
package org.mozilla.gecko.fxa.authenticator;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.accounts.NetworkErrorException;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.fxa.FxAccountClient;
import org.mozilla.gecko.background.fxa.FxAccountClient20;
@ -19,7 +23,6 @@ import org.mozilla.gecko.background.fxa.oauth.FxAccountOAuthClient10.Authorizati
import org.mozilla.gecko.browserid.BrowserIDKeyPair;
import org.mozilla.gecko.browserid.JSONWebTokenUtils;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity;
import org.mozilla.gecko.fxa.login.FxAccountLoginStateMachine;
import org.mozilla.gecko.fxa.login.FxAccountLoginStateMachine.LoginStateMachineDelegate;
import org.mozilla.gecko.fxa.login.FxAccountLoginTransition.Transition;
@ -30,14 +33,9 @@ import org.mozilla.gecko.fxa.login.StateFactory;
import org.mozilla.gecko.fxa.sync.FxAccountNotificationManager;
import org.mozilla.gecko.fxa.sync.FxAccountSyncAdapter;
import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.accounts.NetworkErrorException;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class FxAccountAuthenticator extends AbstractAccountAuthenticator {
public static final String LOG_TAG = FxAccountAuthenticator.class.getSimpleName();
@ -71,7 +69,7 @@ public class FxAccountAuthenticator extends AbstractAccountAuthenticator {
return res;
}
Intent intent = new Intent(context, FxAccountGetStartedActivity.class);
final Intent intent = new Intent(FxAccountConstants.ACTION_FXA_GET_STARTED);
res.putParcelable(AccountManager.KEY_INTENT, intent);
return res;
}

View File

@ -4,24 +4,22 @@
package org.mozilla.gecko.fxa.sync;
import org.mozilla.gecko.BrowserLocaleManager;
import org.mozilla.gecko.R;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.common.telemetry.TelemetryWrapper;
import org.mozilla.gecko.background.fxa.FxAccountUtils;
import org.mozilla.gecko.fxa.activities.FxAccountFinishMigratingActivity;
import org.mozilla.gecko.fxa.activities.FxAccountStatusActivity;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.fxa.login.State;
import org.mozilla.gecko.fxa.login.State.Action;
import org.mozilla.gecko.sync.telemetry.TelemetryContract;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationCompat.Builder;
import org.mozilla.gecko.BrowserLocaleManager;
import org.mozilla.gecko.R;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.common.telemetry.TelemetryWrapper;
import org.mozilla.gecko.background.fxa.FxAccountUtils;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.fxa.login.State;
import org.mozilla.gecko.fxa.login.State.Action;
import org.mozilla.gecko.sync.telemetry.TelemetryContract;
/**
* Abstraction that manages notifications shown or hidden for a Firefox Account.
@ -89,11 +87,11 @@ public class FxAccountNotificationManager {
title = context.getResources().getString(R.string.fxaccount_sync_finish_migrating_notification_title);
text = context.getResources().getString(R.string.fxaccount_sync_finish_migrating_notification_text, state.email);
notificationIntent = new Intent(context, FxAccountFinishMigratingActivity.class);
notificationIntent = new Intent(FxAccountConstants.ACTION_FXA_FINISH_MIGRATING);
} else {
title = context.getResources().getString(R.string.fxaccount_sync_sign_in_error_notification_title);
text = context.getResources().getString(R.string.fxaccount_sync_sign_in_error_notification_text, state.email);
notificationIntent = new Intent(context, FxAccountStatusActivity.class);
notificationIntent = new Intent(FxAccountConstants.ACTION_FXA_STATUS);
}
Logger.info(LOG_TAG, "State " + state.getStateLabel() + " needs action; offering notification with title: " + title);
FxAccountUtils.pii(LOG_TAG, "And text: " + text);

View File

@ -4,22 +4,19 @@
package org.mozilla.gecko.home;
import java.util.EnumSet;
import java.util.Locale;
import org.mozilla.gecko.R;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.fxa.activities.FxAccountCreateAccountActivity;
import org.mozilla.gecko.fxa.activities.FxAccountFinishMigratingActivity;
import org.mozilla.gecko.fxa.activities.FxAccountUpdateCredentialsActivity;
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import org.mozilla.gecko.R;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
import java.util.EnumSet;
import java.util.Locale;
/**
* A <code>HomeFragment</code> which displays one of a small set of static views
@ -102,8 +99,7 @@ public class RemoteTabsStaticFragment extends HomeFragment implements OnClickLis
final int id = v.getId();
if (id == R.id.remote_tabs_setup_get_started) {
// This Activity will redirect to the correct Activity as needed.
final Intent intent = new Intent(getActivity(), FxAccountCreateAccountActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final Intent intent = new Intent(FxAccountConstants.ACTION_FXA_GET_STARTED);
startActivity(intent);
} else if (id == R.id.remote_tabs_setup_old_sync_link) {
final String url = FirefoxAccounts.getOldSyncUpgradeURL(getResources(), Locale.getDefault());
@ -118,11 +114,11 @@ public class RemoteTabsStaticFragment extends HomeFragment implements OnClickLis
final EnumSet<OnUrlOpenListener.Flags> flags = EnumSet.noneOf(OnUrlOpenListener.Flags.class);
mUrlOpenListener.onUrlOpen(CONFIRM_ACCOUNT_SUPPORT_URL, flags);
} else if (id == R.id.remote_tabs_needs_password_sign_in) {
final Intent intent = new Intent(getActivity(), FxAccountUpdateCredentialsActivity.class);
final Intent intent = new Intent(FxAccountConstants.ACTION_FXA_UPDATE_CREDENTIALS);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} else if (id == R.id.remote_tabs_needs_finish_migrating_sign_in) {
final Intent intent = new Intent(getActivity(), FxAccountFinishMigratingActivity.class);
final Intent intent = new Intent(FxAccountConstants.ACTION_FXA_FINISH_MIGRATING);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}

View File

@ -145,6 +145,7 @@ if CONFIG['MOZ_WEBRTC']:
gbjar = add_java_jar('gecko-browser')
gbjar.sources += [
'AboutPages.java',
'AccountsHelper.java',
'ActionModeCompat.java',
'ActionModeCompatView.java',
'ActivityHandlerHelper.java',

View File

@ -6,7 +6,6 @@ package org.mozilla.gecko.overlays.service.sharemethods;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@ -15,7 +14,6 @@ import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.R;
import org.mozilla.gecko.db.BrowserDB;
@ -23,8 +21,6 @@ import org.mozilla.gecko.db.RemoteClient;
import org.mozilla.gecko.db.TabsAccessor;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity;
import org.mozilla.gecko.fxa.activities.FxAccountStatusActivity;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.fxa.login.State;
import org.mozilla.gecko.overlays.OverlayConstants;
@ -159,7 +155,7 @@ public class SendTab extends ShareMethod {
Log.w(LOGTAG, "Firefox Account named like " + fxAccount.getObfuscatedEmail() +
" needs action before it can send a tab; redirecting to status activity.");
setOverrideIntent(FxAccountStatusActivity.class);
setOverrideIntentAction(FxAccountConstants.ACTION_FXA_STATUS);
return;
}
@ -184,7 +180,7 @@ public class SendTab extends ShareMethod {
}
// Have registered UIs offer to set up a Firefox Account.
setOverrideIntent(FxAccountGetStartedActivity.class);
setOverrideIntentAction(FxAccountConstants.ACTION_FXA_GET_STARTED);
}
/**
@ -206,7 +202,7 @@ public class SendTab extends ShareMethod {
if (validGUIDs.isEmpty()) {
// Guess we'd better override. We have no clients.
// This does the broadcast for us.
setOverrideIntent(FxAccountGetStartedActivity.class);
setOverrideIntentAction(FxAccountConstants.ACTION_FXA_GET_STARTED);
return;
}
@ -223,10 +219,10 @@ public class SendTab extends ShareMethod {
* dispatch this intent instead of attempting to share with this ShareMethod whenever it is
* non-null.
*
* @param activityClass The class of the activity we wish to launch instead of invoking a share.
* @param action to launch instead of invoking a share.
*/
protected void setOverrideIntent(Class<? extends Activity> activityClass) {
Intent intent = new Intent(context, activityClass);
protected void setOverrideIntentAction(final String action) {
Intent intent = new Intent(action);
// Per http://stackoverflow.com/a/8992365, this triggers a known bug with
// the soft keyboard not being shown for the started activity. Why, Android, why?
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);

View File

@ -5,18 +5,16 @@
package org.mozilla.gecko.preferences;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.TelemetryContract.Method;
import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity;
import org.mozilla.gecko.sync.setup.SyncAccounts;
import org.mozilla.gecko.sync.setup.activities.SetupSyncActivity;
import org.mozilla.gecko.util.HardwareUtils;
import android.content.Context;
import android.content.Intent;
import android.preference.Preference;
import android.util.AttributeSet;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.TelemetryContract.Method;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.sync.setup.SyncAccounts;
import org.mozilla.gecko.sync.setup.activities.SetupSyncActivity;
class SyncPreference extends Preference {
private static final boolean DEFAULT_TO_FXA = true;
@ -41,13 +39,9 @@ class SyncPreference extends Preference {
}
private void launchFxASetup() {
Intent intent = new Intent(mContext, FxAccountGetStartedActivity.class);
final Intent intent = new Intent(FxAccountConstants.ACTION_FXA_GET_STARTED);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (HardwareUtils.IS_KINDLE_DEVICE) {
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
}
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
mContext.startActivity(intent);
}

View File

@ -5,6 +5,7 @@
package org.mozilla.gecko.tabs;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.R;
import org.mozilla.gecko.ThumbnailHelper;
import org.mozilla.gecko.widget.CropImageView;
@ -34,7 +35,11 @@ public class TabsPanelThumbnailView extends CropImageView {
@Override
protected float getAspectRatio() {
return ThumbnailHelper.TABS_PANEL_THUMBNAIL_ASPECT_RATIO;
if (AppConstants.NIGHTLY_BUILD) {
return ThumbnailHelper.TABS_PANEL_THUMBNAIL_ASPECT_RATIO;
} else {
return ThumbnailHelper.TOP_SITES_THUMBNAIL_ASPECT_RATIO;
}
}
@Override

View File

@ -546,6 +546,16 @@ var BrowserApp = {
InitLater(() => AccessFu.attach(window), window, "AccessFu");
}
if (!AppConstants.MOZ_ANDROID_NATIVE_ACCOUNT_UI) {
// We can't delay registering WebChannel listeners: if the first page is
// about:accounts, which can happen when starting the Firefox Account flow
// from the first run experience, or via the Firefox Account Status
// Activity, we can and do miss messages from the fxa-content-server.
console.log("browser.js: loading Firefox Accounts WebChannel");
Cu.import("resource://gre/modules/FxAccountsWebChannel.jsm");
EnsureFxAccountsWebChannel();
}
// Notify Java that Gecko has loaded.
Messaging.sendRequest({ type: "Gecko:Ready" });

View File

@ -117,3 +117,6 @@ MOZ_ADDON_SIGNING=1
if test "$NIGHTLY_BUILD"; then
MOZ_SWITCHBOARD=1
fi
# Use native Firefox Accounts UI regardless of channel.
MOZ_ANDROID_NATIVE_ACCOUNT_UI=1

View File

@ -0,0 +1,16 @@
# 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/.
# LOCALIZATION NOTE (relinkDenied.message): Ideally, this string is short (it's
# a toast message).
relinkDenied.message = Already signed in to Sync!
# LOCALIZATION NOTE (relinkDenied.openPrefs): Ideally, this string is short (it's a
# button label) and upper-case, to match Google and Android's convention.
relinkDenied.openPrefs = PREFS
relinkVerify.title = Are you sure you want to sign in to Sync?
# LOCALIZATION NOTE (relinkVerify.message): Email address of a user previously signed in to Sync.
relinkVerify.message = You were previously signed in to Sync with a different email address. Signing in will merge this browsers bookmarks, passwords and other settings with %S
relinkVerify.continue = Continue
relinkVerify.cancel = Cancel

View File

@ -7,6 +7,9 @@
@AB_CD@.jar:
% locale browser @AB_CD@ %locale/@AB_CD@/browser/
locale/@AB_CD@/browser/about.dtd (%chrome/about.dtd)
#ifndef MOZ_ANDROID_NATIVE_ACCOUNT_UI
locale/@AB_CD@/browser/aboutAccounts.properties (%chrome/aboutAccounts.properties)
#endif
locale/@AB_CD@/browser/aboutAddons.dtd (%chrome/aboutAddons.dtd)
locale/@AB_CD@/browser/aboutAddons.properties (%chrome/aboutAddons.properties)
#ifdef MOZ_DEVICES

View File

@ -8,9 +8,9 @@ this.EXPORTED_SYMBOLS = ["Accounts"];
const { utils: Cu } = Components;
Cu.import("resource://gre/modules/Messaging.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/Messaging.jsm"); /*global Messaging */
Cu.import("resource://gre/modules/Promise.jsm"); /*global Promise */
Cu.import("resource://gre/modules/Services.jsm"); /*global Services */
/**
* A promise-based API for querying the existence of Sync accounts,
@ -68,6 +68,19 @@ var Accounts = Object.freeze({
});
},
_addDefaultEndpoints: function (json) {
let newData = Cu.cloneInto(json, {}, { cloneFunctions: false });
let associations = {
authServerEndpoint: 'identity.fxaccounts.auth.uri',
profileServerEndpoint: 'identity.fxaccounts.remote.profile.uri',
tokenServerEndpoint: 'identity.sync.tokenserver.uri'
};
for (let key in associations) {
newData[key] = newData[key] || Services.urlFormatter.formatURLPref(associations[key]);
}
return newData;
},
/**
* Create a new Android Account corresponding to the given
* fxa-content-server "login" JSON datum. The new account will be
@ -78,9 +91,58 @@ var Accounts = Object.freeze({
* Returns a Promise that resolves to a boolean indicating success.
*/
createFirefoxAccountFromJSON: function (json) {
return Messaging.sendRequestForResponse({
return Messaging.sendRequestForResult({
type: "Accounts:CreateFirefoxAccountFromJSON",
json: json
json: this._addDefaultEndpoints(json)
});
},
/**
* Move an existing Android Account to the "Engaged" state with the given
* fxa-content-server "login" JSON datum. The account will (re)start
* syncing immediately, unless the user has manually configured the account
* to not Sync.
*
* It is an error if no Android Account exists.
*
* Returns a Promise that resolves to a boolean indicating success.
*/
updateFirefoxAccountFromJSON: function (json) {
return Messaging.sendRequestForResult({
type: "Accounts:UpdateFirefoxAccountFromJSON",
json: this._addDefaultEndpoints(json)
});
},
/**
* Fetch information about an existing Android Firefox Account.
*
* Returns a Promise that resolves to null if no Android Firefox Account
* exists, or an object including at least a string-valued 'email' key.
*/
getFirefoxAccount: function () {
return Messaging.sendRequestForResult({
type: "Accounts:Exist",
kind: "fxa",
}).then(data => {
if (!data || !data.exists) {
return null;
}
delete data.exists;
return data;
});
},
/**
* Delete an existing Android Firefox Account.
*
* It is an error if no Android Account exists.
*
* Returns a Promise that resolves to a boolean indicating success.
*/
deleteFirefoxAccount: function () {
return Messaging.sendRequestForResult({
type: "Accounts:DeleteFirefoxAccount",
});
}
});

View File

@ -0,0 +1,368 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* 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/. */
/**
* Firefox Accounts Web Channel.
*
* Use the WebChannel component to receive messages about account
* state changes.
*/
this.EXPORTED_SYMBOLS = ["EnsureFxAccountsWebChannel"];
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; /*global Components */
Cu.import("resource://gre/modules/Accounts.jsm"); /*global Accounts */
Cu.import("resource://gre/modules/Notifications.jsm"); /*global Notifications */
Cu.import("resource://gre/modules/Prompt.jsm"); /*global Prompt */
Cu.import("resource://gre/modules/Services.jsm"); /*global Services */
Cu.import("resource://gre/modules/WebChannel.jsm"); /*global WebChannel */
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); /*global XPCOMUtils */
const log = Cu.import("resource://gre/modules/AndroidLog.jsm", {}).AndroidLog.bind("FxAccounts");
const WEBCHANNEL_ID = "account_updates";
const COMMAND_LOADED = "fxaccounts:loaded";
const COMMAND_CAN_LINK_ACCOUNT = "fxaccounts:can_link_account";
const COMMAND_LOGIN = "fxaccounts:login";
const COMMAND_CHANGE_PASSWORD = "fxaccounts:change_password";
const COMMAND_DELETE_ACCOUNT = "fxaccounts:delete_account";
const PREF_LAST_FXA_USER = "identity.fxaccounts.lastSignedInUserHash";
XPCOMUtils.defineLazyGetter(this, "strings",
() => Services.strings.createBundle("chrome://browser/locale/aboutAccounts.properties")); /*global strings */
Object.defineProperty(this, "NativeWindow",
{ get: () => Services.wm.getMostRecentWindow("navigator:browser").NativeWindow }); /*global NativeWindow */
this.FxAccountsWebChannelHelpers = function() {
};
this.FxAccountsWebChannelHelpers.prototype = {
/**
* Get the hash of account name of the previously signed in account.
*/
getPreviousAccountNameHashPref() {
try {
return Services.prefs.getComplexValue(PREF_LAST_FXA_USER, Ci.nsISupportsString).data;
} catch (_) {
return "";
}
},
/**
* Given an account name, set the hash of the previously signed in account.
*
* @param acctName the account name of the user's account.
*/
setPreviousAccountNameHashPref(acctName) {
let string = Cc["@mozilla.org/supports-string;1"]
.createInstance(Ci.nsISupportsString);
string.data = this.sha256(acctName);
Services.prefs.setComplexValue(PREF_LAST_FXA_USER, Ci.nsISupportsString, string);
},
/**
* Given a string, returns the SHA265 hash in base64.
*/
sha256(str) {
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
// Data is an array of bytes.
let data = converter.convertToByteArray(str, {});
let hasher = Cc["@mozilla.org/security/hash;1"]
.createInstance(Ci.nsICryptoHash);
hasher.init(hasher.SHA256);
hasher.update(data, data.length);
return hasher.finish(true);
},
};
/**
* Create a new FxAccountsWebChannel to listen for account updates.
*
* @param {Object} options Options
* @param {Object} options
* @param {String} options.content_uri
* The FxA Content server uri
* @param {String} options.channel_id
* The ID of the WebChannel
* @param {String} options.helpers
* Helpers functions. Should only be passed in for testing.
* @constructor
*/
this.FxAccountsWebChannel = function(options) {
if (!options) {
throw new Error("Missing configuration options");
}
if (!options["content_uri"]) {
throw new Error("Missing 'content_uri' option");
}
this._contentUri = options.content_uri;
if (!options["channel_id"]) {
throw new Error("Missing 'channel_id' option");
}
this._webChannelId = options.channel_id;
// options.helpers is only specified by tests.
this._helpers = options.helpers || new FxAccountsWebChannelHelpers(options);
this._setupChannel();
};
this.FxAccountsWebChannel.prototype = {
/**
* WebChannel that is used to communicate with content page
*/
_channel: null,
/**
* WebChannel ID.
*/
_webChannelId: null,
/**
* WebChannel origin, used to validate origin of messages
*/
_webChannelOrigin: null,
/**
* Release all resources that are in use.
*/
tearDown() {
this._channel.stopListening();
this._channel = null;
this._channelCallback = null;
},
/**
* Configures and registers a new WebChannel
*
* @private
*/
_setupChannel() {
// if this.contentUri is present but not a valid URI, then this will throw an error.
try {
this._webChannelOrigin = Services.io.newURI(this._contentUri, null, null);
this._registerChannel();
} catch (e) {
log.e(e.toString());
throw e;
}
},
/**
* Create a new channel with the WebChannelBroker, setup a callback listener
* @private
*/
_registerChannel() {
/**
* Processes messages that are called back from the FxAccountsChannel
*
* @param webChannelId {String}
* Command webChannelId
* @param message {Object}
* Command message
* @param sendingContext {Object}
* Message sending context.
* @param sendingContext.browser {browser}
* The <browser> object that captured the
* WebChannelMessageToChrome.
* @param sendingContext.eventTarget {EventTarget}
* The <EventTarget> where the message was sent.
* @param sendingContext.principal {Principal}
* The <Principal> of the EventTarget where the message was sent.
* @private
*
*/
let listener = (webChannelId, message, sendingContext) => {
if (message) {
let command = message.command;
let data = message.data;
log.d("FxAccountsWebChannel message received, command: " + command);
// Respond to the message with true or false.
let respond = (data) => {
let response = {
command: command,
messageId: message.messageId,
data: data
};
log.d("Sending response to command: " + command);
this._channel.send(response, sendingContext);
};
switch (command) {
case COMMAND_LOADED:
let mm = sendingContext.browser.docShell
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIContentFrameMessageManager);
mm.sendAsyncMessage(COMMAND_LOADED);
break;
case COMMAND_CAN_LINK_ACCOUNT:
Accounts.getFirefoxAccount().then(account => {
if (account) {
// If we /have/ an Android Account, we never allow the user to
// login to a different account. They need to manually delete
// the first Android Account and then create a new one.
if (account.email == data.email) {
// In future, we should use a UID for this comparison.
log.d("Relinking existing Android Account: email addresses agree.");
respond({ok: true});
} else {
log.w("Not relinking existing Android Account: email addresses disagree!");
let message = strings.GetStringFromName("relinkDenied.message");
let buttonLabel = strings.GetStringFromName("relinkDenied.openPrefs");
NativeWindow.toast.show(message, "long", {
button: {
icon: "drawable://switch_button_icon",
label: buttonLabel,
callback: () => {
// We have an account, so this opens Sync native preferences.
Accounts.launchSetup();
},
}
});
respond({ok: false});
}
} else {
// If we /don't have/ an Android Account, we warn if we're
// connecting to a new Account. This is to minimize surprise;
// we never did this when changing accounts via the native UI.
let prevAcctHash = this._helpers.getPreviousAccountNameHashPref();
let shouldShowWarning = prevAcctHash && (prevAcctHash != this._helpers.sha256(data.email));
if (shouldShowWarning) {
log.w("Warning about creating a new Android Account: previously linked to different email address!");
let message = strings.formatStringFromName("relinkVerify.message", [data.email], 1);
new Prompt({
title: strings.GetStringFromName("relinkVerify.title"),
message: message,
buttons: [
// This puts Cancel on the right.
strings.GetStringFromName("relinkVerify.cancel"),
strings.GetStringFromName("relinkVerify.continue"),
],
}).show(result => respond({ok: result && result.button == 1}));
} else {
log.d("Not warning about creating a new Android Account: no previously linked email address.");
respond({ok: true});
}
}
}).catch(e => {
log.e(e.toString());
respond({ok: false});
});
break;
case COMMAND_LOGIN:
// Either create a new Android Account or re-connect an existing
// Android Account here. There's not much to be done if we don't
// succeed or get an error.
Accounts.getFirefoxAccount().then(account => {
if (!account) {
return Accounts.createFirefoxAccountFromJSON(data).then(success => {
if (!success) {
throw new Error("Could not create Firefox Account!");
}
return success;
});
} else {
return Accounts.updateFirefoxAccountFromJSON(data).then(success => {
if (!success) {
throw new Error("Could not update Firefox Account!");
}
return success;
});
}
})
.then(success => {
if (!success) {
throw new Error("Could not create or update Firefox Account!");
}
// Remember who it is so we can show a relink warning when appropriate.
this._helpers.setPreviousAccountNameHashPref(data.email);
log.i("Created or updated Firefox Account.");
})
.catch(e => {
log.e(e.toString());
});
break;
case COMMAND_CHANGE_PASSWORD:
// Only update an existing Android Account.
Accounts.getFirefoxAccount().then(account => {
if (!account) {
throw new Error("Can't change password of non-existent Firefox Account!");
}
return Accounts.updateFirefoxAccountFromJSON(data);
})
.then(success => {
if (!success) {
throw new Error("Could not change Firefox Account password!");
}
log.i("Changed Firefox Account password.");
})
.catch(e => {
log.e(e.toString());
});
break;
case COMMAND_DELETE_ACCOUNT:
// The fxa-content-server has already confirmed the user's intent.
// Bombs away. There's no recovery from failure, and not even a
// real need to check an account exists (although we do, for error
// messaging only).
Accounts.getFirefoxAccount().then(account => {
if (!account) {
throw new Error("Can't delete non-existent Firefox Account!");
}
return Accounts.deleteFirefoxAccount().then(success => {
if (!success) {
throw new Error("Could not delete Firefox Account!");
}
log.i("Firefox Account deleted.");
});
}).catch(e => {
log.e(e.toString());
});
break;
default:
log.w("Ignoring unrecognized FxAccountsWebChannel command: " + JSON.stringify(command));
break;
}
}
};
this._channelCallback = listener;
this._channel = new WebChannel(this._webChannelId, this._webChannelOrigin);
this._channel.listen(listener);
log.d("FxAccountsWebChannel registered: " + this._webChannelId + " with origin " + this._webChannelOrigin.prePath);
}
};
let singleton;
// The entry-point for this module, which ensures only one of our channels is
// ever created - we require this because the WebChannel is global in scope and
// allowing multiple channels would cause such notifications to be sent multiple
// times.
this.EnsureFxAccountsWebChannel = function() {
if (!singleton) {
let contentUri = Services.urlFormatter.formatURLPref("identity.fxaccounts.remote.webchannel.uri");
// The FxAccountsWebChannel listens for events and updates the Java layer.
singleton = new this.FxAccountsWebChannel({
content_uri: contentUri,
channel_id: WEBCHANNEL_ID,
});
}
};

View File

@ -11,6 +11,7 @@ EXTRA_JS_MODULES += [
'dbg-browser-actors.js',
'DelayedInit.jsm',
'DownloadNotifications.jsm',
'FxAccountsWebChannel.jsm',
'HelperApps.jsm',
'Home.jsm',
'HomeProvider.jsm',

View File

@ -12,6 +12,10 @@
<action android:name="android.intent.action.MAIN" />
<!-- <category android:name="android.intent.category.LAUNCHER" /> -->
</intent-filter>
<intent-filter>
<action android:name="@ANDROID_PACKAGE_NAME@.ACTION_FXA_STATUS"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity
@ -28,6 +32,12 @@
<action android:name="android.intent.action.MAIN" />
<!-- <category android:name="android.intent.category.LAUNCHER" /> -->
</intent-filter>
#ifdef MOZ_ANDROID_NATIVE_ACCOUNT_UI
<intent-filter>
<action android:name="@ANDROID_PACKAGE_NAME@.ACTION_FXA_GET_STARTED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
#endif
</activity>
<activity
@ -43,6 +53,12 @@
android:configChanges="locale|layoutDirection"
android:noHistory="true"
android:windowSoftInputMode="adjustResize">
#ifdef MOZ_ANDROID_NATIVE_ACCOUNT_UI
<intent-filter>
<action android:name="@ANDROID_PACKAGE_NAME@.ACTION_FXA_CONFIRM_ACCOUNT"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
#endif
</activity>
<activity
@ -65,6 +81,12 @@
android:name="org.mozilla.gecko.fxa.activities.FxAccountUpdateCredentialsActivity"
android:configChanges="locale|layoutDirection"
android:windowSoftInputMode="adjustResize">
#ifdef MOZ_ANDROID_NATIVE_ACCOUNT_UI
<intent-filter>
<action android:name="@ANDROID_PACKAGE_NAME@.ACTION_FXA_UPDATE_CREDENTIALS"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
#endif
</activity>
<activity
@ -72,6 +94,12 @@
android:name="org.mozilla.gecko.fxa.activities.FxAccountFinishMigratingActivity"
android:configChanges="locale|layoutDirection"
android:windowSoftInputMode="adjustResize">
#ifdef MOZ_ANDROID_NATIVE_ACCOUNT_UI
<intent-filter>
<action android:name="@ANDROID_PACKAGE_NAME@.ACTION_FXA_FINISH_MIGRATING"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
#endif
</activity>
<activity
@ -105,3 +133,41 @@
<data android:scheme="package"/>
</intent-filter>
</receiver>
#ifndef MOZ_ANDROID_NATIVE_ACCOUNT_UI
<activity
android:exported="false"
android:name="org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivityWeb">
<intent-filter>
<action android:name="@ANDROID_PACKAGE_NAME@.ACTION_FXA_GET_STARTED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity
android:exported="false"
android:name="org.mozilla.gecko.fxa.activities.FxAccountUpdateCredentialsActivityWeb">
<intent-filter>
<action android:name="@ANDROID_PACKAGE_NAME@.ACTION_FXA_UPDATE_CREDENTIALS"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity
android:exported="false"
android:name="org.mozilla.gecko.fxa.activities.FxAccountFinishMigratingActivityWeb">
<intent-filter>
<action android:name="@ANDROID_PACKAGE_NAME@.ACTION_FXA_FINISH_MIGRATING"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity
android:exported="false"
android:name="org.mozilla.gecko.fxa.activities.FxAccountConfirmAccountActivityWeb">
<intent-filter>
<action android:name="@ANDROID_PACKAGE_NAME@.ACTION_FXA_CONFIRM_ACCOUNT"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
#endif

View File

@ -2074,57 +2074,54 @@ Engine.prototype = {
case "https":
case "ftp":
// No use downloading the icon if the engine file is read-only
if (!this._readOnly ||
getBoolPref(BROWSER_SEARCH_PREF + "cache.enabled", true)) {
LOG("_setIcon: Downloading icon: \"" + uri.spec +
"\" for engine: \"" + this.name + "\"");
var chan = NetUtil.ioService.newChannelFromURI2(uri,
null, // aLoadingNode
Services.scriptSecurityManager.getSystemPrincipal(),
null, // aTriggeringPrincipal
Ci.nsILoadInfo.SEC_NORMAL,
Ci.nsIContentPolicy.TYPE_IMAGE);
LOG("_setIcon: Downloading icon: \"" + uri.spec +
"\" for engine: \"" + this.name + "\"");
var chan = NetUtil.ioService.newChannelFromURI2(uri,
null, // aLoadingNode
Services.scriptSecurityManager.getSystemPrincipal(),
null, // aTriggeringPrincipal
Ci.nsILoadInfo.SEC_NORMAL,
Ci.nsIContentPolicy.TYPE_IMAGE);
let iconLoadCallback = function (aByteArray, aEngine) {
// This callback may run after we've already set a preferred icon,
// so check again.
if (aEngine._hasPreferredIcon && !aIsPreferred)
return;
let iconLoadCallback = function (aByteArray, aEngine) {
// This callback may run after we've already set a preferred icon,
// so check again.
if (aEngine._hasPreferredIcon && !aIsPreferred)
return;
if (!aByteArray || aByteArray.length > MAX_ICON_SIZE) {
LOG("iconLoadCallback: load failed, or the icon was too large!");
return;
}
var str = btoa(String.fromCharCode.apply(null, aByteArray));
let dataURL = ICON_DATAURL_PREFIX + str;
aEngine._iconURI = makeURI(dataURL);
if (aWidth && aHeight) {
aEngine._addIconToMap(aWidth, aHeight, dataURL)
}
// The engine might not have a file yet, if it's being downloaded,
// because the request for the engine file itself (_onLoad) may not
// yet be complete. In that case, this change will be written to
// file when _onLoad is called. For readonly engines, we'll store
// the changes in the cache once notified below.
if (aEngine._file && !aEngine._readOnly)
aEngine._serializeToFile();
notifyAction(aEngine, SEARCH_ENGINE_CHANGED);
aEngine._hasPreferredIcon = aIsPreferred;
if (!aByteArray || aByteArray.length > MAX_ICON_SIZE) {
LOG("iconLoadCallback: load failed, or the icon was too large!");
return;
}
// If we're currently acting as an "update engine", then the callback
// should set the icon on the engine we're updating and not us, since
// |this| might be gone by the time the callback runs.
var engineToSet = this._engineToUpdate || this;
var str = btoa(String.fromCharCode.apply(null, aByteArray));
let dataURL = ICON_DATAURL_PREFIX + str;
aEngine._iconURI = makeURI(dataURL);
var listener = new loadListener(chan, engineToSet, iconLoadCallback);
chan.notificationCallbacks = listener;
chan.asyncOpen(listener, null);
if (aWidth && aHeight) {
aEngine._addIconToMap(aWidth, aHeight, dataURL)
}
// The engine might not have a file yet, if it's being downloaded,
// because the request for the engine file itself (_onLoad) may not
// yet be complete. In that case, this change will be written to
// file when _onLoad is called. For readonly engines, we'll store
// the changes in the cache once notified below.
if (aEngine._file && !aEngine._readOnly)
aEngine._serializeToFile();
notifyAction(aEngine, SEARCH_ENGINE_CHANGED);
aEngine._hasPreferredIcon = aIsPreferred;
}
// If we're currently acting as an "update engine", then the callback
// should set the icon on the engine we're updating and not us, since
// |this| might be gone by the time the callback runs.
var engineToSet = this._engineToUpdate || this;
var listener = new loadListener(chan, engineToSet, iconLoadCallback);
chan.notificationCallbacks = listener;
chan.asyncOpen(listener, null);
break;
}
},
@ -3614,9 +3611,6 @@ SearchService.prototype = {
},
_buildCache: function SRCH_SVC__buildCache() {
if (!getBoolPref(BROWSER_SEARCH_PREF + "cache.enabled", true))
return;
TelemetryStopwatch.start("SEARCH_SERVICE_BUILD_CACHE_MS");
let cache = {};
let locale = getLocale();
@ -3697,13 +3691,10 @@ SearchService.prototype = {
LOG("_syncLoadEngines: start");
// See if we have a cache file so we don't have to parse a bunch of XML.
let cache = {};
let cacheEnabled = getBoolPref(BROWSER_SEARCH_PREF + "cache.enabled", true);
if (cacheEnabled) {
let cacheFile = getDir(NS_APP_USER_PROFILE_50_DIR);
cacheFile.append("search.json");
if (cacheFile.exists())
cache = this._readCacheFile(cacheFile);
}
let cacheFile = getDir(NS_APP_USER_PROFILE_50_DIR);
cacheFile.append("search.json");
if (cacheFile.exists())
cache = this._readCacheFile(cacheFile);
let [chromeFiles, chromeURIs] = this._findJAREngines();
@ -3756,7 +3747,7 @@ SearchService.prototype = {
this._visibleDefaultEngines.some(notInCacheVisibleEngines) ||
toLoad.some(modifiedDir);
if (!cacheEnabled || rebuildCache) {
if (rebuildCache) {
LOG("_loadEngines: Absent or outdated cache. Loading engines from disk.");
distDirs.forEach(this._loadEnginesFromDir, this);
@ -3764,8 +3755,7 @@ SearchService.prototype = {
otherDirs.forEach(this._loadEnginesFromDir, this);
if (cacheEnabled)
this._buildCache();
this._buildCache();
return;
}
@ -3787,11 +3777,8 @@ SearchService.prototype = {
LOG("_asyncLoadEngines: start");
// See if we have a cache file so we don't have to parse a bunch of XML.
let cache = {};
let cacheEnabled = getBoolPref(BROWSER_SEARCH_PREF + "cache.enabled", true);
if (cacheEnabled) {
let cacheFilePath = OS.Path.join(OS.Constants.Path.profileDir, "search.json");
cache = yield checkForSyncCompletion(this._asyncReadCacheFile(cacheFilePath));
}
let cacheFilePath = OS.Path.join(OS.Constants.Path.profileDir, "search.json");
cache = yield checkForSyncCompletion(this._asyncReadCacheFile(cacheFilePath));
Services.obs.notifyObservers(null, SEARCH_SERVICE_TOPIC, "find-jar-engines");
let [chromeFiles, chromeURIs] =
@ -3882,7 +3869,7 @@ SearchService.prototype = {
this._visibleDefaultEngines.some(notInCacheVisibleEngines) ||
(yield checkForSyncCompletion(hasModifiedDir(toLoad)));
if (!cacheEnabled || rebuildCache) {
if (rebuildCache) {
LOG("_asyncLoadEngines: Absent or outdated cache. Loading engines from disk.");
let engines = [];
for (let loadDir of distDirs) {
@ -3902,8 +3889,7 @@ SearchService.prototype = {
for (let engine of engines) {
this._addEngineToStore(engine);
}
if (cacheEnabled)
this._buildCache();
this._buildCache();
return;
}
@ -5545,12 +5531,6 @@ var engineUpdateService = {
if (!getBoolPref(BROWSER_SEARCH_PREF + "update", true) || !engine._hasUpdates)
return;
// We use the cache to store updated app engines, so refuse to update if the
// cache is disabled.
if (engine._readOnly &&
!getBoolPref(BROWSER_SEARCH_PREF + "cache.enabled", true))
return;
let testEngine = null;
let updateURL = engine._getURLOfType(URLTYPE_OPENSEARCH);
let updateURI = (updateURL && updateURL._hasRelation("self")) ?

View File

@ -7352,6 +7352,24 @@
"n_buckets": 20,
"description": "The amount of time spent in a specific performance tool view, keyed by view name (waterfall, js-calltree, js-flamegraph, etc)."
},
"VIEW_SOURCE_IN_BROWSER_OPENED_BOOLEAN": {
"alert_emails": ["mozilla-dev-developer-tools@lists.mozilla.org", "jryans@mozilla.com"],
"expires_in_version": "53",
"kind": "boolean",
"description": "How many times has view source in browser / tab been opened?"
},
"VIEW_SOURCE_IN_WINDOW_OPENED_BOOLEAN": {
"alert_emails": ["mozilla-dev-developer-tools@lists.mozilla.org", "jryans@mozilla.com"],
"expires_in_version": "53",
"kind": "boolean",
"description": "How many times has view source in a new window been opened?"
},
"VIEW_SOURCE_EXTERNAL_RESULT_BOOLEAN": {
"alert_emails": ["mozilla-dev-developer-tools@lists.mozilla.org", "jryans@mozilla.com"],
"expires_in_version": "53",
"kind": "boolean",
"description": "How many times has view source in an external editor been opened, and did it succeed?"
},
"BROWSER_IS_USER_DEFAULT": {
"expires_in_version": "never",
"kind": "boolean",
@ -8755,21 +8773,21 @@
"description": "Reports results from the graphics sanity test to track which drivers are having problems (0=TEST_PASSED, 1=TEST_FAILED_RENDER, 2=TEST_FAILED_VIDEO, 3=TEST_CRASHED)"
},
"READER_MODE_SERIALIZE_DOM_MS": {
"expires_in_version": "42",
"expires_in_version": "50",
"kind": "exponential",
"high": "5000",
"n_buckets": 15,
"description": "Time (ms) to serialize a DOM to send to the reader worker"
},
"READER_MODE_WORKER_PARSE_MS": {
"expires_in_version": "42",
"expires_in_version": "50",
"kind": "exponential",
"high": "10000",
"n_buckets": 30,
"description": "Time (ms) for the reader worker to parse a document"
},
"READER_MODE_DOWNLOAD_MS": {
"expires_in_version": "42",
"expires_in_version": "50",
"kind": "exponential",
"low": 50,
"high": "40000",
@ -8777,13 +8795,13 @@
"description": "Time (ms) to download a document to show in reader mode"
},
"READER_MODE_PARSE_RESULT" : {
"expires_in_version": "42",
"expires_in_version": "50",
"kind": "enumerated",
"n_values": 5,
"description": "The result of trying to parse a document to show in reader view (0=Success, 1=Error too many elements, 2=Error in worker, 3=Error no article)"
},
"READER_MODE_DOWNLOAD_RESULT" : {
"expires_in_version": "42",
"expires_in_version": "50",
"kind": "enumerated",
"n_values": 5,
"description": "The result of trying to download a document to show in reader view (0=Success, 1=Error XHR, 2=Error no document)"

View File

@ -88,6 +88,9 @@ var gViewSourceUtils = {
* The line number to focus on once the source is loaded.
*/
viewSourceInBrowser: function(aArgs) {
Services.telemetry
.getHistogramById("VIEW_SOURCE_IN_BROWSER_OPENED_BOOLEAN")
.add(true);
let viewSourceBrowser = new ViewSourceBrowser(aArgs.viewSourceBrowser);
viewSourceBrowser.loadViewSource(aArgs);
},
@ -156,6 +159,9 @@ var gViewSourceUtils = {
} catch (ex) {
}
}
Services.telemetry
.getHistogramById("VIEW_SOURCE_IN_WINDOW_OPENED_BOOLEAN")
.add(true);
openDialog("chrome://global/content/viewSource.xul",
"_blank",
"all,dialog=no",
@ -340,7 +346,10 @@ var gViewSourceUtils = {
// Calls the callback, keeping in mind undefined or null values.
handleCallBack: function(aCallBack, result, data)
{
// ifcallback is undefined, default to the internal viewer
Services.telemetry
.getHistogramById("VIEW_SOURCE_EXTERNAL_RESULT_BOOLEAN")
.add(result);
// if callback is undefined, default to the internal viewer
if (aCallBack === undefined) {
this.internalViewerFallback(result, data);
} else if (aCallBack) {

View File

@ -39,6 +39,8 @@ function* check_installed(inProfile, ...versions) {
do_check_eq(addon.version, versions[i]);
do_check_true(addon.isActive);
do_check_false(addon.foreignInstall);
do_check_false(hasFlag(addon.permissions, AddonManager.PERM_CAN_UPGRADE));
do_check_false(hasFlag(addon.permissions, AddonManager.PERM_CAN_UNINSTALL));
// Verify the add-ons file is in the right place
let file = expectedDir.clone();