This commit is contained in:
Ryan VanderMeulen 2013-12-13 16:16:46 -05:00
Родитель fad917b723 218cec4598
Коммит cd364e9987
65 изменённых файлов: 2996 добавлений и 181 удалений

Просмотреть файл

@ -856,3 +856,9 @@ pref("osfile.reset_worker_delay", 5000);
// The URL of the Firefox Accounts auth server backend
pref("identity.fxaccounts.auth.uri", "https://api-accounts.dev.lcip.org/v1");
// APZC preferences.
//
// Gaia relies heavily on scroll events for now, so lets fire them
// more often than the default value (100).
pref("apz.asyncscroll.throttle", 40);

Просмотреть файл

@ -0,0 +1,131 @@
/* 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/. */
/*
* This defines the look-and-feel styling of the error pages.
* (see: netError.xhtml)
*
* Original styling by William Price <bugzilla@mob.rice.edu>
* Updated for mobile by: Wes Johnston <wjohnston@mozilla.com>
*/
body {
margin: 0;
padding: 0 8px 8px;
font-family: "Nokia Sans", Tahoma, sans-serif !important;
}
h1 {
font-size: 22px;
}
h2 {
font-size: 16px;
}
ul {
margin: 0px;
padding: 0px 0px 0px 1em;
}
li {
margin: 0px;
padding: 8px 0px;
}
#errorPage {
background-color: #CEE6F4;
}
#errorPage.certerror {
background-color: #EFD400;
}
#errorPage.blockedsite {
background-color: #BF0000;
}
#errorTitle {
background: url("chrome://browser/content/images/errorpage-warning.png") left center no-repeat;
/* Scaled by .666 of their actual size */
background-size: 40px 40px;
background-origin: content-box;
min-height: 60px;
margin-left: auto;
margin-right: auto;
max-width: 500px;
margin-left: auto;
margin-right: auto;
}
#errorPage.certerror #errorTitle {
background-image: url("chrome://browser/content/images/errorpage-larry-black.png");
}
#errorPage.blockedsite #errorTitle {
background-image: url("chrome://browser/content/images/errorpage-larry-white.png");
color: white;
}
.errorTitleText {
padding: 0px 0px 0px 50px;
display: inline-block;
vertical-align: middle
}
#errorPageContainer {
background-color: white;
border: 1px solid #999999;
border-radius: 6px;
padding: 6px 20px 20px;
font-size: 14px;
max-width: 500px;
margin-left: auto;
margin-right: auto;
}
#errorShortDesc > p:empty {
display: none;
}
#errorShortDesc > p {
overflow: auto;
border-bottom: 1px solid #999999;
padding-bottom: 1em;
}
#errorPage.blockedsite #errorShortDesc > p {
font-weight: bold;
border-bottom: none;
padding-bottom: 0px;
}
#securityOverrideDiv {
padding-top: 10px;
}
div[collapsed] {
padding-left: 15px;
background-image: url("chrome://browser/skin/images/arrowright-16.png");
background-size: 11px 11px;
background-repeat: no-repeat;
background-position: left 0.3em;
}
div[collapsed="true"] {
background-image: url("chrome://browser/skin/images/arrowright-16.png");
}
div[collapsed="false"] {
background-image: url("chrome://browser/skin/images/arrowdown-16.png");
}
div[collapsed="true"] > p,
div[collapsed="true"] > div {
display: none;
}
button {
padding: 0.3em !important;
}

Просмотреть файл

@ -21,9 +21,10 @@ Cu.import('resource://gre/modules/ErrorPage.jsm');
Cu.import('resource://gre/modules/NetworkStatsService.jsm');
#endif
// identity
// Identity
Cu.import('resource://gre/modules/SignInToWebsite.jsm');
SignInToWebsiteController.init();
Cu.import('resource://gre/modules/FxAccountsMgmtService.jsm');
Cu.import('resource://gre/modules/DownloadsAPI.jsm');
@ -614,6 +615,8 @@ var shell = {
this.sendEvent(window, 'ContentStart');
Services.obs.notifyObservers(null, 'content-start', null);
#ifdef MOZ_WIDGET_GONK
Cu.import('resource://gre/modules/OperatorApps.jsm');
#endif

Просмотреть файл

@ -25,9 +25,11 @@ chrome.jar:
% override chrome://global/skin/media/videocontrols.css chrome://browser/content/touchcontrols.css
% override chrome://global/content/aboutCertError.xhtml chrome://browser/content/aboutCertError.xhtml
% override chrome://global/skin/netError.css chrome://browser/content/netError.css
content/ErrorPage.js (content/ErrorPage.js)
content/aboutCertError.xhtml (content/aboutCertError.xhtml)
content/netError.css (content/netError.css)
content/images/errorpage-larry-black.png (content/images/errorpage-larry-black.png)
content/images/errorpage-larry-white.png (content/images/errorpage-larry-white.png)
content/images/errorpage-warning.png (content/images/errorpage-warning.png)

Просмотреть файл

@ -72,6 +72,10 @@ component {637b0f77-2429-49a0-915f-abf5d0db8b9a} WebappsUpdateTimer.js
contract @mozilla.org/b2g/webapps-update-timer;1 {637b0f77-2429-49a0-915f-abf5d0db8b9a}
category update-timer WebappsUpdateTimer @mozilla.org/b2g/webapps-update-timer;1,getService,background-update-timer,webapps.update.interval,86400
# FxAccountsUIGlue.js
component {51875c14-91d7-4b8c-b65d-3549e101228c} FxAccountsUIGlue.js
contract @mozilla.org/fxaccounts/fxaccounts-ui-glue;1 {51875c14-91d7-4b8c-b65d-3549e101228c}
# HelperAppDialog.js
component {710322af-e6ae-4b0c-b2c9-1474a87b077e} HelperAppDialog.js
contract @mozilla.org/helperapplauncherdialog;1 {710322af-e6ae-4b0c-b2c9-1474a87b077e}

Просмотреть файл

@ -0,0 +1,129 @@
/* 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/. */
/**
* Some specific (certified) apps need to get access to certain Firefox Accounts
* functionality that allows them to manage accounts (this is mostly sign up,
* sign in, logout and delete) and get information about the currently existing
* ones.
*
* This service listens for requests coming from these apps, triggers the
* appropriate Fx Accounts flows and send reponses back to the UI.
*
* The communication mechanism is based in mozFxAccountsContentEvent (for
* messages coming from the UI) and mozFxAccountsChromeEvent (for messages
* sent from the chrome side) custom events.
*/
"use strict";
this.EXPORTED_SYMBOLS = ["FxAccountsMgmtService"];
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/ObjectWrapper.jsm");
Cu.import("resource://gre/modules/FxAccountsCommon.js");
XPCOMUtils.defineLazyModuleGetter(this, "FxAccountsManager",
"resource://gre/modules/FxAccountsManager.jsm");
this.FxAccountsMgmtService = {
_sendChromeEvent: function(aMsg) {
if (!this._shell) {
return;
}
this._shell.sendCustomEvent("mozFxAccountsChromeEvent", aMsg);
},
_onFullfill: function(aMsgId, aData) {
this._sendChromeEvent({
id: aMsgId,
data: aData ? aData : null
});
},
_onReject: function(aMsgId, aReason) {
this._sendChromeEvent({
id: aMsgId,
error: aReason ? aReason : null
});
},
init: function() {
Services.obs.addObserver(this, "content-start", false);
},
observe: function(aSubject, aTopic, aData) {
this._shell = Services.wm.getMostRecentWindow("navigator:browser").shell;
let content = this._shell.contentBrowser.contentWindow;
content.addEventListener("mozFxAccountsContentEvent",
FxAccountsMgmtService);
Services.obs.removeObserver(this, "content-start");
},
handleEvent: function(aEvent) {
let msg = aEvent.detail;
log.debug("Got content msg " + JSON.stringify(msg));
let self = FxAccountsMgmtService;
if (!msg.id) {
return;
}
let data = msg.data;
if (!data) {
return;
}
switch(data.method) {
case "getAccounts":
FxAccountsManager.getAccount().then(
account => {
// We only expose the email and verification status so far.
self._onFullfill(msg.id, account);
},
reason => {
self._onReject(msg.id, reason);
}
).then(null, Components.utils.reportError);
break;
case "logout":
FxAccountsManager.signOut().then(
() => {
self._onFullfill(msg.id);
},
reason => {
self._onReject(msg.id, reason);
}
).then(null, Components.utils.reportError);
break;
case "queryAccount":
FxAccountsManager.queryAccount(data.accountId).then(
result => {
self._onFullfill(msg.id, result);
},
reason => {
self._onReject(msg.id, reason);
}
).then(null, Components.utils.reportError);
break;
case "signIn":
case "signUp":
FxAccountsManager[data.method](data.accountId, data.password).then(
user => {
self._onFullfill(msg.id, user);
},
reason => {
self._onReject(msg.id, reason);
}
).then(null, Components.utils.reportError);
break;
}
}
};
FxAccountsMgmtService.init();

Просмотреть файл

@ -0,0 +1,73 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict"
const { interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/ObjectWrapper.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/FxAccountsCommon.js");
XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
"@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator");
function FxAccountsUIGlue() {
}
FxAccountsUIGlue.prototype = {
_browser: Services.wm.getMostRecentWindow("navigator:browser"),
signInFlow: function() {
let deferred = Promise.defer();
let content = this._browser.getContentWindow();
if (!content) {
deferred.reject("InternalErrorNoContent");
return;
}
let id = uuidgen.generateUUID().toString();
content.addEventListener("mozFxAccountsRPContentEvent",
function onContentEvent(result) {
let msg = result.detail;
if (!msg || !msg.id || msg.id != id) {
deferred.reject("InternalErrorWrongContentEvent");
content.removeEventListener("mozFxAccountsRPContentEvent",
onContentEvent);
return;
}
log.debug("Got content event " + JSON.stringify(msg));
if (msg.error) {
deferred.reject(msg);
} else {
deferred.resolve(msg.result);
}
content.removeEventListener("mozFxAccountsRPContentEvent",
onContentEvent);
});
let detail = {
method: "openFlow",
id: id
};
log.debug("Send chrome event " + JSON.stringify(detail));
this._browser.shell.sendCustomEvent("mozFxAccountsRPChromeEvent", detail);
return deferred.promise;
},
classID: Components.ID("{51875c14-91d7-4b8c-b65d-3549e101228c}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFxAccountsUIGlue])
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([FxAccountsUIGlue]);

Просмотреть файл

@ -13,6 +13,7 @@ EXTRA_COMPONENTS += [
'ContentHandler.js',
'ContentPermissionPrompt.js',
'FilePicker.js',
'FxAccountsUIGlue.js',
'HelperAppDialog.js',
'MailtoProtocolHandler.js',
'PaymentGlue.js',
@ -36,6 +37,7 @@ if CONFIG['MOZ_UPDATER']:
EXTRA_JS_MODULES += [
'ErrorPage.jsm',
'FxAccountsMgmtService.jsm',
'SignInToWebsite.jsm',
'TelURIParser.jsm',
'WebappsUpdater.jsm',

Просмотреть файл

@ -1,4 +1,4 @@
{
"revision": "9271d94f35a54995e4442da711e51cff0244741d",
"revision": "62c0ad5b88f15d5da1cc2496b9534087dbc5d015",
"repo_path": "/integration/gaia-central"
}

Просмотреть файл

@ -789,6 +789,7 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
@BINPATH@/components/TelProtocolHandler.js
@BINPATH@/components/B2GAboutRedirector.js
@BINPATH@/components/FilePicker.js
@BINPATH@/components/FxAccountsUIGlue.js
@BINPATH@/components/HelperAppDialog.js
@BINPATH@/components/DownloadsUI.js
@ -796,6 +797,8 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
@BINPATH@/components/DataStoreService.js
@BINPATH@/components/dom_datastore.xpt
@BINPATH@/components/services_fxaccounts.xpt
#ifdef MOZ_WEBSPEECH
@BINPATH@/components/dom_webspeechsynth.xpt
#endif

Просмотреть файл

@ -197,6 +197,7 @@
<!-- UI tour experience -->
<panel id="UITourTooltip"
type="arrow"
hidden="true"
noautofocus="true"
noautohide="true"
align="start"
@ -206,6 +207,7 @@
<description id="UITourTooltipDescription" flex="1"/>
</panel>
<panel id="UITourHighlightContainer"
hidden="true"
noautofocus="true"
noautohide="true"
consumeoutsideclicks="false">
@ -461,7 +463,7 @@
#ifdef MENUBAR_CAN_AUTOHIDE
toolbarname="&menubarCmd.label;"
accesskey="&menubarCmd.accesskey;"
#ifdef XP_LINUX
#if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT)
autohide="true"
#endif
#endif

Просмотреть файл

@ -522,7 +522,6 @@ MenuPopup.prototype = {
aEvent.charCode); // in unsigned long charCodeArg);
ev.mine = true;
this.commands.dispatchEvent(ev);
switch (aEvent.keyCode) {
case aEvent.DOM_VK_ESCAPE:
@ -537,6 +536,7 @@ MenuPopup.prototype = {
if (Util.isNavigationKey(aEvent.keyCode)) {
aEvent.stopPropagation();
aEvent.preventDefault();
this.commands.dispatchEvent(ev);
} else if (!this._wantTypeBehind) {
// Hide the context menu so you can't type behind it.
aEvent.stopPropagation();

Просмотреть файл

@ -220,6 +220,7 @@ pref("extensions.blocklist.enabled", true);
pref("extensions.blocklist.interval", 86400);
pref("extensions.blocklist.url", "https://addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PING_COUNT%/%TOTAL_PING_COUNT%/%DAYS_SINCE_LAST_PING%/");
pref("extensions.blocklist.detailsURL", "https://www.mozilla.org/%LOCALE%/blocklist/");
pref("extensions.showMismatchUI", false);
/* block popups by default, and notify the user about blocked popups */
pref("dom.disable_open_during_load", true);

Просмотреть файл

@ -464,6 +464,7 @@ this.UITour = {
effect = this.highlightEffects[randomEffect];
}
highlighter.setAttribute("active", effect);
highlighter.parentElement.hidden = false;
let targetRect = aTargetEl.getBoundingClientRect();
@ -514,16 +515,15 @@ this.UITour = {
let tooltipTitle = document.getElementById("UITourTooltipTitle");
let tooltipDesc = document.getElementById("UITourTooltipDescription");
tooltip.hidePopup();
if (tooltip.state == "open") {
tooltip.hidePopup();
}
tooltipTitle.textContent = aTitle;
tooltipDesc.textContent = aDescription;
tooltip.hidden = false;
let alignment = "bottomcenter topright";
if (tooltip.state == "open") {
tooltip.hidePopup();
}
tooltip.openPopup(aAnchorEl, alignment);
}

Просмотреть файл

@ -11,13 +11,13 @@
.devtools-monospace {
%ifdef XP_MACOSX
font-family: Menlo, monospace;
%endif
%ifdef XP_LINUX
font-family: monospace;
font-size: 80%;
%endif
%ifdef XP_WIN
%elifdef XP_WIN
font-family: Consolas, monospace;
%else
font-family: monospace;
%endif
%if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT)
font-size: 80%;
%endif
}
@ -62,7 +62,7 @@
background-image: linear-gradient(to bottom, hsla(209,18%,18%,0.9), hsl(210,11%,16%));
border-radius: 3px;
overflow-x: hidden;
%ifdef XP_LINUX
%if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT)
max-height: 32rem;
%else
max-height: 40rem;

Просмотреть файл

@ -54,7 +54,7 @@ html|*.highlighter-nodeinfobar-pseudo-classes {
padding: 0;
width: 26px;
min-height: 26px;
%ifndef XP_LINUX
%if !defined(MOZ_WIDGET_GTK) && !defined(MOZ_WIDGET_QT)
background-color: transparent;
%endif
}

Просмотреть файл

@ -20,7 +20,7 @@
%ifdef XP_MACOSX
%include ../osx/shared.inc
%elifdef XP_LINUX
%elif defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT)
%include ../linux/linuxShared.inc
%else
%include ../windows/windowsShared.inc

До

Ширина:  |  Высота:  |  Размер: 1.5 KiB

После

Ширина:  |  Высота:  |  Размер: 1.5 KiB

Просмотреть файл

@ -0,0 +1,280 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 et filetype=javascript
* 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/. */
let Promise =
SpecialPowers.Cu.import("resource://gre/modules/Promise.jsm").Promise;
let bluetoothManager;
/* Get mozSettings value specified by @aKey.
*
* Resolve if that mozSettings value is retrieved successfully, reject
* otherwise.
*
* Forfill params:
* The corresponding mozSettings value of the key.
* Reject params: (none)
*
* @param aKey
* A string.
*
* @return A deferred promise.
*/
function getSettings(aKey) {
let deferred = Promise.defer();
let request = navigator.mozSettings.createLock().get(aKey);
request.addEventListener("success", function(aEvent) {
ok(true, "getSettings(" + aKey + ")");
deferred.resolve(aEvent.target.result[aKey]);
});
request.addEventListener("error", function() {
ok(false, "getSettings(" + aKey + ")");
deferred.reject();
});
return deferred.promise;
}
/* Set mozSettings values.
*
* Resolve if that mozSettings value is set successfully, reject otherwise.
*
* Forfill params: (none)
* Reject params: (none)
*
* @param aSettings
* An object of format |{key1: value1, key2: value2, ...}|.
*
* @return A deferred promise.
*/
function setSettings(aSettings) {
let deferred = Promise.defer();
let request = navigator.mozSettings.createLock().set(aSettings);
request.addEventListener("success", function() {
ok(true, "setSettings(" + JSON.stringify(aSettings) + ")");
deferred.resolve();
});
request.addEventListener("error", function() {
ok(false, "setSettings(" + JSON.stringify(aSettings) + ")");
deferred.reject();
});
return deferred.promise;
}
/* Get mozSettings value of 'bluetooth.enabled'.
*
* Resolve if that mozSettings value is retrieved successfully, reject
* otherwise.
*
* Forfill params:
* A boolean value.
* Reject params: (none)
*
* @return A deferred promise.
*/
function getBluetoothEnabled() {
return getSettings("bluetooth.enabled");
}
/* Set mozSettings value of 'bluetooth.enabled'.
*
* Resolve if that mozSettings value is set successfully, reject otherwise.
*
* Forfill params: (none)
* Reject params: (none)
*
* @param aEnabled
* A boolean value.
*
* @return A deferred promise.
*/
function setBluetoothEnabled(aEnabled) {
let obj = {};
obj["bluetooth.enabled"] = aEnabled;
return setSettings(obj);
}
/* Push required permissions and test if |navigator.mozBluetooth| exists.
* Resolve if it does, reject otherwise.
*
* Forfill params:
* bluetoothManager -- an reference to navigator.mozBluetooth.
* Reject params: (none)
*
* @param aPermissions
* Additional permissions to push before any test cases. Could be either
* a string or an array of strings.
*
* @return A deferred promise.
*/
function ensureBluetoothManager(aPermissions) {
let deferred = Promise.defer();
let permissions = ["bluetooth"];
if (aPermissions) {
if (Array.isArray(aPermissions)) {
permissions = permissions.concat(aPermissions);
} else if (typeof aPermissions == "string") {
permissions.push(aPermissions);
}
}
let obj = [];
for (let perm of permissions) {
obj.push({
"type": perm,
"allow": 1,
"context": document,
});
}
SpecialPowers.pushPermissions(obj, function() {
ok(true, "permissions pushed: " + JSON.stringify(permissions));
bluetoothManager = window.navigator.mozBluetooth;
log("navigator.mozBluetooth is " +
(bluetoothManager ? "available" : "unavailable"));
if (bluetoothManager instanceof BluetoothManager) {
deferred.resolve(bluetoothManager);
} else {
deferred.reject();
}
});
return deferred.promise;
}
/* Wait for one named BluetoothManager event.
*
* Resolve if that named event occurs. Never reject.
*
* Forfill params: the DOMEvent passed.
*
* @return A deferred promise.
*/
function waitForManagerEvent(aEventName) {
let deferred = Promise.defer();
bluetoothManager.addEventListener(aEventName, function onevent(aEvent) {
bluetoothManager.removeEventListener(aEventName, onevent);
ok(true, "BluetoothManager event '" + aEventName + "' got.");
deferred.resolve(aEvent);
});
return deferred.promise;
}
/* Convenient function for setBluetoothEnabled and waitForManagerEvent
* combined.
*
* Resolve if that named event occurs. Reject if we can't set settings.
*
* Forfill params: the DOMEvent passed.
* Reject params: (none)
*
* @return A deferred promise.
*/
function setBluetoothEnabledAndWait(aEnabled) {
return setBluetoothEnabled(aEnabled)
.then(waitForManagerEvent.bind(null, aEnabled ? "enabled" : "disabled"));
}
/* Get default adapter.
*
* Resolve if that default adapter is got, reject otherwise.
*
* Forfill params: a BluetoothAdapter instance.
* Reject params: a DOMError, or null if if there is no adapter ready yet.
*
* @return A deferred promise.
*/
function getDefaultAdapter() {
let deferred = Promise.defer();
let request = bluetoothManager.getDefaultAdapter();
request.onsuccess = function(aEvent) {
let adapter = aEvent.target.result;
if (!(adapter instanceof BluetoothAdapter)) {
ok(false, "no BluetoothAdapter ready yet.");
deferred.reject(null);
return;
}
ok(true, "BluetoothAdapter got.");
// TODO: We have an adapter instance now, but some of its attributes may
// still remain unassigned/out-dated. Here we waste a few seconds to
// wait for the property changed events.
//
// See https://bugzilla.mozilla.org/show_bug.cgi?id=932914
window.setTimeout(function() {
deferred.resolve(adapter);
}, 3000);
};
request.onerror = function(aEvent) {
ok(false, "Failed to get default adapter.");
deferred.reject(aEvent.target.error);
};
return deferred.promise;
}
/* Flush permission settings and call |finish()|.
*/
function cleanUp() {
SpecialPowers.flushPermissions(function() {
// Use ok here so that we have at least one test run.
ok(true, "permissions flushed");
finish();
});
}
function startBluetoothTestBase(aPermissions, aTestCaseMain) {
ensureBluetoothManager(aPermissions)
.then(aTestCaseMain)
.then(cleanUp, function() {
ok(false, "Unhandled rejected promise.");
cleanUp();
});
}
function startBluetoothTest(aReenable, aTestCaseMain) {
startBluetoothTestBase(["settings-read", "settings-write"], function() {
let origEnabled, needEnable;
return getBluetoothEnabled()
.then(function(aEnabled) {
origEnabled = aEnabled;
needEnable = !aEnabled;
log("Original 'bluetooth.enabled' is " + origEnabled);
if (aEnabled && aReenable) {
log(" Disable 'bluetooth.enabled' ...");
needEnable = true;
return setBluetoothEnabledAndWait(false);
}
})
.then(function() {
if (needEnable) {
log(" Enable 'bluetooth.enabled' ...");
return setBluetoothEnabledAndWait(true)
.then(waitForManagerEvent.bind(null, "adapteradded"));
}
})
.then(getDefaultAdapter)
.then(aTestCaseMain)
.then(function() {
if (!origEnabled) {
return setBluetoothEnabledAndWait(false);
}
});
});
}

Просмотреть файл

@ -0,0 +1,7 @@
[DEFAULT]
b2g = true
browser = false
qemu = true
[test_dom_BluetoothManager_enabled.js]
[test_dom_BluetoothManager_adapteradded.js]

Просмотреть файл

@ -0,0 +1,44 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 et filetype=javascript
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
// https://github.com/mozilla-b2g/platform_external_qemu/blob/master/vl-android.c#L765
// static int bt_hci_parse(const char *str) {
// ...
// bdaddr.b[0] = 0x52;
// bdaddr.b[1] = 0x54;
// bdaddr.b[2] = 0x00;
// bdaddr.b[3] = 0x12;
// bdaddr.b[4] = 0x34;
// bdaddr.b[5] = 0x56 + nb_hcis;
const EMULATOR_ADDRESS = "56:34:12:00:54:52";
// $ adb shell hciconfig /dev/ttyS2 name
// hci0: Type: BR/EDR Bus: UART
// BD Address: 56:34:12:00:54:52 ACL MTU: 512:1 SCO MTU: 0:0
// Name: 'Full Android on Emulator'
const EMULATOR_NAME = "Full Android on Emulator";
// $ adb shell hciconfig /dev/ttyS2 class
// hci0: Type: BR/EDR Bus: UART
// BD Address: 56:34:12:00:54:52 ACL MTU: 512:1 SCO MTU: 0:0
// Class: 0x58020c
// Service Classes: Capturing, Object Transfer, Telephony
// Device Class: Phone, Smart phone
const EMULATOR_CLASS = 0x58020c;
startBluetoothTest(true, function testCaseMain(aAdapter) {
log("Checking adapter attributes ...");
is(aAdapter.name, EMULATOR_NAME, "adapter.name");
is(aAdapter.class, EMULATOR_CLASS, "adapter.class");
is(aAdapter.address, EMULATOR_ADDRESS, "adapter.address");
is(aAdapter.discovering, false, "adapter.discovering");
is(aAdapter.discoverable, false, "adapter.discoverable");
is(aAdapter.discoverableTimeout, 120, "adapter.discoverableTimeout");
});

Просмотреть файл

@ -0,0 +1,64 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 et filetype=javascript
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
let enabledEventReceived;
function onEnabled() {
enabledEventReceived = true;
}
let disabledEventReceived;
function onDisabled() {
disabledEventReceived = true;
}
function test(aEnabled) {
log("Testing 'bluetooth.enabled' => " + aEnabled);
let deferred = Promise.defer();
enabledEventReceived = false;
disabledEventReceived = false;
setBluetoothEnabled(aEnabled).then(function() {
log(" Settings set. Waiting 3 seconds and examine results.");
window.setTimeout(function() {
is(bluetoothManager.enabled, aEnabled, "bluetoothManager.enabled");
is(enabledEventReceived, aEnabled, "enabledEventReceived");
is(disabledEventReceived, !aEnabled, "disabledEventReceived");
if (bluetoothManager.enabled === aEnabled &&
enabledEventReceived === aEnabled &&
disabledEventReceived === !aEnabled) {
deferred.resolve();
} else {
deferred.reject();
}
}, 3000);
});
return deferred.promise;
}
startBluetoothTestBase(["settings-read", "settings-write"],
function testCaseMain() {
bluetoothManager.addEventListener("enabled", onEnabled);
bluetoothManager.addEventListener("disabled", onDisabled);
return getBluetoothEnabled()
.then(function(aEnabled) {
log("Original 'bluetooth.enabled' is " + aEnabled);
// Set to !aEnabled and reset back to aEnabled.
return test(!aEnabled).then(test.bind(null, aEnabled));
})
.then(function() {
bluetoothManager.removeEventListener("enabled", onEnabled);
bluetoothManager.removeEventListener("disabled", onDisabled);
});
});

Просмотреть файл

@ -198,6 +198,7 @@ function createDOMDownloadObject(aWindow, aDownload) {
function DOMDownloadImpl() {
debug("DOMDownloadImpl constructor ");
this.wrappedJSObject = this;
this.totalBytes = 0;
this.currentBytes = 0;
@ -205,9 +206,11 @@ function DOMDownloadImpl() {
this.path = null;
this.state = "stopped";
this.contentType = null;
this.startTime = Date.now();
this.error = null;
/* fields that require getters/setters */
this._startTime = new Date();
/* private fields */
this.id = null;
}
@ -244,6 +247,19 @@ DOMDownloadImpl.prototype = {
return this.__DOM_IMPL__.getEventHandler("onstatechange");
},
get startTime() {
return this._startTime;
},
set startTime(aStartTime) {
if (aStartTime instanceof Date) {
this._startTime = aStartTime;
}
else {
this._startTime = new Date(aStartTime);
}
},
_init: function(aWindow, aDownload) {
this._window = aWindow;
this.id = aDownload.id;

Просмотреть файл

@ -1502,6 +1502,33 @@ ContentChild::RecvNotifyPhoneStateChange(const nsString& aState)
return true;
}
void
ContentChild::AddIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS)
{
MOZ_ASSERT(aObserver, "null idle observer");
// Make sure aObserver isn't released while we wait for the parent
aObserver->AddRef();
SendAddIdleObserver(reinterpret_cast<uint64_t>(aObserver), aIdleTimeInS);
}
void
ContentChild::RemoveIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS)
{
MOZ_ASSERT(aObserver, "null idle observer");
SendRemoveIdleObserver(reinterpret_cast<uint64_t>(aObserver), aIdleTimeInS);
aObserver->Release();
}
bool
ContentChild::RecvNotifyIdleObserver(const uint64_t& aObserver,
const nsCString& aTopic,
const nsString& aTimeStr)
{
nsIObserver* observer = reinterpret_cast<nsIObserver*>(aObserver);
observer->Observe(nullptr, aTopic.get(), aTimeStr.get());
return true;
}
bool
ContentChild::RecvLoadAndRegisterSheet(const URIParams& aURI, const uint32_t& aType)
{

Просмотреть файл

@ -240,6 +240,12 @@ public:
virtual bool RecvUnregisterSheet(const URIParams& aURI, const uint32_t& aType);
virtual bool RecvNotifyPhoneStateChange(const nsString& state);
void AddIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS);
void RemoveIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS);
virtual bool RecvNotifyIdleObserver(const uint64_t& aObserver,
const nsCString& aTopic,
const nsString& aData);
#ifdef ANDROID
gfxIntSize GetScreenSize() { return mScreenSize; }
#endif

Просмотреть файл

@ -68,6 +68,7 @@
#include "nsIDOMWindow.h"
#include "nsIExternalProtocolService.h"
#include "nsIFilePicker.h"
#include "nsIIdleService.h"
#include "nsIMemoryReporter.h"
#include "nsIMozBrowserFrame.h"
#include "nsIMutable.h"
@ -1082,6 +1083,8 @@ ContentParent::ActorDestroy(ActorDestroyReason why)
obs->NotifyObservers((nsIPropertyBag2*) props, "ipc:content-shutdown", nullptr);
}
mIdleListeners.Clear();
// If the child process was terminated due to a SIGKIL, ShutDownProcess
// might not have been called yet. We must call it to ensure that our
// channel is closed, etc.
@ -3155,5 +3158,47 @@ ContentParent::RecvRecordingDeviceEvents(const nsString& aRecordingStatus,
return true;
}
bool
ContentParent::RecvAddIdleObserver(const uint64_t& aObserver, const uint32_t& aIdleTimeInS)
{
nsresult rv;
nsCOMPtr<nsIIdleService> idleService =
do_GetService("@mozilla.org/widget/idleservice;1", &rv);
NS_ENSURE_SUCCESS(rv, false);
nsCOMPtr<ParentIdleListener> listener = new ParentIdleListener(this, aObserver);
mIdleListeners.Put(aObserver, listener);
idleService->AddIdleObserver(listener, aIdleTimeInS);
return true;
}
bool
ContentParent::RecvRemoveIdleObserver(const uint64_t& aObserver, const uint32_t& aIdleTimeInS)
{
nsresult rv;
nsCOMPtr<nsIIdleService> idleService =
do_GetService("@mozilla.org/widget/idleservice;1", &rv);
NS_ENSURE_SUCCESS(rv, false);
nsCOMPtr<ParentIdleListener> listener;
bool found = mIdleListeners.Get(aObserver, &listener);
if (found) {
mIdleListeners.Remove(aObserver);
idleService->RemoveIdleObserver(listener, aIdleTimeInS);
}
return true;
}
} // namespace dom
} // namespace mozilla
NS_IMPL_ISUPPORTS1(ParentIdleListener, nsIObserver)
NS_IMETHODIMP
ParentIdleListener::Observe(nsISupports*, const char* aTopic, const PRUnichar* aData) {
mozilla::unused << mParent->SendNotifyIdleObserver(mObserver,
nsDependentCString(aTopic),
nsDependentString(aData));
return NS_OK;
}

Просмотреть файл

@ -15,7 +15,9 @@
#include "mozilla/LinkedList.h"
#include "mozilla/StaticPtr.h"
#include "nsDataHashtable.h"
#include "nsFrameMessageManager.h"
#include "nsHashKeys.h"
#include "nsIObserver.h"
#include "nsIThreadInternal.h"
#include "nsIDOMGeoPositionCallback.h"
@ -27,7 +29,7 @@ class mozIApplication;
class nsConsoleService;
class nsIDOMBlob;
class nsIMemoryReporter;
template<class KeyClass,class DataType> class nsDataHashtable;
class ParentIdleListener;
namespace mozilla {
@ -510,6 +512,9 @@ private:
virtual void ProcessingError(Result what) MOZ_OVERRIDE;
virtual bool RecvAddIdleObserver(const uint64_t& observerId, const uint32_t& aIdleTimeInS);
virtual bool RecvRemoveIdleObserver(const uint64_t& observerId, const uint32_t& aIdleTimeInS);
// If you add strong pointers to cycle collected objects here, be sure to
// release these objects in ShutDownProcess. See the comment there for more
// details.
@ -559,9 +564,25 @@ private:
nsRefPtr<nsConsoleService> mConsoleService;
nsConsoleService* GetConsoleService();
nsDataHashtable<nsUint64HashKey, nsCOMPtr<ParentIdleListener> > mIdleListeners;
};
} // namespace dom
} // namespace mozilla
class ParentIdleListener : public nsIObserver {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
ParentIdleListener(mozilla::dom::ContentParent* aParent, uint64_t aObserver)
: mParent(aParent), mObserver(aObserver)
{}
virtual ~ParentIdleListener() {}
private:
nsRefPtr<mozilla::dom::ContentParent> mParent;
uint64_t mObserver;
};
#endif

Просмотреть файл

@ -325,6 +325,11 @@ child:
async UnregisterSheet(URIParams uri, uint32_t type);
NotifyPhoneStateChange(nsString newState);
/**
* Notify idle observers in the child
*/
NotifyIdleObserver(uint64_t observerId, nsCString topic, nsString str);
parent:
/**
* Tell the content process some attributes of itself. This is
@ -502,6 +507,8 @@ parent:
bool isAudio,
bool isVideo);
AddIdleObserver(uint64_t observerId, uint32_t idleTimeInS);
RemoveIdleObserver(uint64_t observerId, uint32_t idleTimeInS);
both:
AsyncMessage(nsString aMessage, ClonedMessageData aData,
CpowEntry[] aCpows, Principal aPrincipal);

Просмотреть файл

@ -246,7 +246,7 @@ PreallocatedProcessManagerImpl::DelayedNuwaFork()
mPreallocateAppProcessTask = nullptr;
if (!mIsNuwaReady) {
if (!mPreallocatedAppProcess && !mShutdown) {
if (!mPreallocatedAppProcess && !mShutdown && mEnabled) {
mPreallocatedAppProcess = ContentParent::RunNuwaProcess();
}
// else mPreallocatedAppProcess is starting. It will NuwaFork() when ready.

Просмотреть файл

@ -41,4 +41,5 @@ qemu = true
[test_thread_subject.js]
[test_mmdb_new.js]
[test_mmdb_setmessagedeliverybyid_sms.js]
[test_mmdb_foreachmatchedmmsdeliveryinfo.js]
[test_replace_short_message_type.js]

Просмотреть файл

@ -0,0 +1,114 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
// TODO: bug 943233 - passing jsm exported objects to |Promise.resolve| gets
// empty object in return.
let mmdb;
const PHONE_0 = "+15555215500";
const PHONE_1 = "+15555215501";
const PHONE_2 = "+15555215502";
const PHONE_2_NET = "5555215502";
const PHONE_3 = "+15555215503";
const PHONE_3_NET = "5555215503";
const EMAIL_1 = "foo@bar.com";
let deliveryInfo = [
{ receiver: PHONE_1 },
{ receiver: PHONE_2 },
{ receiver: PHONE_1 },
{ receiver: PHONE_2_NET },
{ receiver: PHONE_3_NET },
{ receiver: EMAIL_1 },
{ receiver: PHONE_3 },
];
function clearTraversed(aDeliveryInfo) {
for (let element of aDeliveryInfo) {
delete element.traversed;
}
}
function doTest(aMmdb, aNeedle, aVerifyFunc, aCount) {
log(" '" + aNeedle + "': " + aCount);
clearTraversed(deliveryInfo);
let count = 0;
aMmdb.forEachMatchedMmsDeliveryInfo(deliveryInfo, aNeedle, function(aElement) {
ok(true, "checking " + aElement.receiver);
ok(!aElement.hasOwnProperty("traversed"), "element.traversed");
aVerifyFunc(aElement);
aElement.traversed = true;
++count;
});
is(count, aCount, "matched count");
}
function testNotFound(aMmdb) {
log("Testing unavailable");
// TODO: bug 943233 - passing jsm exported objects to |Promise.resolve| gets
// empty object in return.
aMmdb = mmdb;
doTest(aMmdb, PHONE_0, function(aElement) {
ok(false, "Should never have a match");
}, 0);
return aMmdb;
}
function testDirectMatch(aMmdb) {
log("Testing direct matching");
// TODO: bug 943233 - passing jsm exported objects to |Promise.resolve| gets
// empty object in return.
aMmdb = mmdb;
for (let needle of [PHONE_1, EMAIL_1]) {
let count = deliveryInfo.reduce(function(aCount, aElement) {
return aElement.receiver == needle ? aCount + 1 : aCount;
}, 0);
doTest(aMmdb, needle, function(aElement) {
is(aElement.receiver, needle, "element.receiver");
}, count);
}
return aMmdb;
}
function testPhoneMatch(aMmdb) {
log("Testing phone matching");
let verifyFunc = function(aValid, aElement) {
ok(aValid.indexOf(aElement.receiver) >= 0, "element.receiver");
};
// TODO: bug 943233 - passing jsm exported objects to |Promise.resolve| gets
// empty object in return.
aMmdb = mmdb;
let matchingGroups = [
[PHONE_2, PHONE_2_NET],
[PHONE_3, PHONE_3_NET],
];
for (let group of matchingGroups) {
for (let item of group) {
doTest(aMmdb, item, verifyFunc.bind(null, group), group.length);
}
}
return aMmdb;
}
startTestBase(function testCaseMain() {
mmdb = newMobileMessageDB();
return initMobileMessageDB(mmdb, "test_mmdb_foreachmatchedmmsdeliveryinfo", 0)
.then(testNotFound)
.then(testDirectMatch)
.then(testPhoneMatch)
.then(closeMobileMessageDB.bind(null, mmdb));
});

Просмотреть файл

@ -7,6 +7,8 @@ MARIONETTE_HEAD_JS = 'head.js';
startTestBase(function testCaseMain() {
log("Test init MobileMessageDB");
// TODO: bug 943233 - passing jsm exported objects to |Promise.resolve| gets
// empty object in return.
let mmdb = newMobileMessageDB();
let dbName = "test_mmdb_new";
let dbVersion = 0;

Просмотреть файл

@ -491,7 +491,7 @@ XPCOMUtils.defineLazyGetter(this, "gRadioEnabledController", function () {
receiveMessage: function(msg) {
if (DEBUG) debug("setRadioEnabled: receiveMessage: " + JSON.stringify(msg));
this.pendingMessages.push(msg);
if (this.pendingMessages.length === 1) {
if (this.pendingMessages.length === 1 && !this.isDeactivatingDataCalls()) {
this._processNextMessage();
}
},
@ -540,7 +540,6 @@ XPCOMUtils.defineLazyGetter(this, "gRadioEnabledController", function () {
} else {
this.request = (function() {
radioInterface.receiveMessage(msg);
this._processNextMessage();
}).bind(this);
// In some DSDS architecture with only one modem, toggling one radio may
@ -596,6 +595,7 @@ XPCOMUtils.defineLazyGetter(this, "gRadioEnabledController", function () {
this.request();
this.request = null;
}
this._processNextMessage();
}
};
});
@ -1258,6 +1258,10 @@ RadioInterface.prototype = {
case "iccmbdn":
this.handleIccMbdn(message);
break;
case "iccmwis":
gMessageManager.sendVoicemailMessage("RIL:VoicemailNotification",
this.clientId, message.mwi);
break;
case "USSDReceived":
if (DEBUG) this.debug("USSDReceived " + JSON.stringify(message));
this.handleUSSDReceived(message);
@ -2104,11 +2108,6 @@ RadioInterface.prototype = {
return true;
}
// TODO: Bug #768441
// For now we don't store indicators persistently. When the mwi.discard
// flag is false, we'll need to persist the indicator to EFmwis.
// See TS 23.040 9.2.3.24.2
let mwi = message.mwi;
if (mwi) {
mwi.returnNumber = message.sender;

Просмотреть файл

@ -1206,6 +1206,7 @@ this.GECKO_ICC_SERVICES = {
PNN: 51,
OPL: 52,
MDN: 53,
MWIS: 54,
SPDI: 56
},
usim: {
@ -1221,6 +1222,7 @@ this.GECKO_ICC_SERVICES = {
PNN: 45,
OPL: 46,
MDN: 47,
MWIS: 48,
SPDI: 51
},
ruim: {

Просмотреть файл

@ -4206,6 +4206,11 @@ let RIL = {
this.sendChromeMessage(message);
// Update MWI Status into ICC if present.
if (message.mwi && ICCUtilsHelper.isICCServiceAvailable("MWIS")) {
SimRecordHelper.updateMWIS(message.mwi);
}
// We will acknowledge receipt of the SMS after we try to store it
// in the database.
return MOZ_FCS_WAIT_FOR_EXPLICIT_ACK;
@ -6163,7 +6168,7 @@ RIL[UNSOLICITED_RESPONSE_NEW_SMS_STATUS_REPORT] = function UNSOLICITED_RESPONSE_
RIL[UNSOLICITED_RESPONSE_NEW_SMS_ON_SIM] = function UNSOLICITED_RESPONSE_NEW_SMS_ON_SIM(length) {
let recordNumber = Buf.readInt32List()[0];
ICCRecordHelper.readSMS(
SimRecordHelper.readSMS(
recordNumber,
function onsuccess(message) {
if (message && message.simStatus === 3) { //New Unread SMS
@ -10937,6 +10942,7 @@ let ICCFileHelper = {
return EF_PATH_MF_SIM + EF_PATH_DF_TELECOM;
case ICC_EF_AD:
case ICC_EF_MBDN:
case ICC_EF_MWIS:
case ICC_EF_PLMNsel:
case ICC_EF_SPN:
case ICC_EF_SPDI:
@ -10961,6 +10967,7 @@ let ICCFileHelper = {
case ICC_EF_AD:
case ICC_EF_FDN:
case ICC_EF_MBDN:
case ICC_EF_MWIS:
case ICC_EF_UST:
case ICC_EF_MSISDN:
case ICC_EF_SPN:
@ -11916,6 +11923,13 @@ let SimRecordHelper = {
if (DEBUG) debug("MDN: MDN service is not available");
}
if (ICCUtilsHelper.isICCServiceAvailable("MWIS")) {
if (DEBUG) debug("MWIS: MWIS is available");
this.readMWIS();
} else {
if (DEBUG) debug("MWIS: MWIS is not available");
}
if (ICCUtilsHelper.isICCServiceAvailable("SPDI")) {
if (DEBUG) debug("SPDI: SPDI available.");
this.readSPDI();
@ -11985,6 +11999,91 @@ let SimRecordHelper = {
callback: callback.bind(this)});
},
/**
* Read ICC MWIS. (Message Waiting Indication Status)
*
* @see TS 31.102, clause 4.2.63 for USIM and TS 51.011, clause 10.3.45 for SIM.
*/
readMWIS: function readMWIS() {
function callback(options) {
let strLen = Buf.readInt32();
// Each octet is encoded into two chars.
let octetLen = strLen / 2;
let mwis = GsmPDUHelper.readHexOctetArray(octetLen);
Buf.readStringDelimiter(strLen);
if (!mwis) {
return;
}
RIL.iccInfoPrivate.mwis = mwis; //Keep raw MWIS for updateMWIS()
let mwi = {};
// b8 b7 B6 b5 b4 b3 b2 b1 4.2.63, TS 31.102 version 11.6.0
// | | | | | | | |__ Voicemail
// | | | | | | |_____ Fax
// | | | | | |________ Electronic Mail
// | | | | |___________ Other
// | | | |______________ Videomail
// |__|__|_________________ RFU
mwi.active = ((mwis[0] & 0x01) != 0);
if (mwi.active) {
// In TS 23.040 msgCount is in the range from 0 to 255.
// The value 255 shall be taken to mean 255 or greater.
//
// However, There is no definition about 0 when MWI is active.
//
// Normally, when mwi is active, the msgCount must be larger than 0.
// Refer to other reference phone,
// 0 is usually treated as UNKNOWN for storing 2nd level MWI status (DCS).
mwi.msgCount = (mwis[1] === 0) ? GECKO_VOICEMAIL_MESSAGE_COUNT_UNKNOWN
: mwis[1];
} else {
mwi.msgCount = 0;
}
RIL.sendChromeMessage({ rilMessageType: "iccmwis",
mwi: mwi });
}
ICCIOHelper.loadLinearFixedEF({ fileId: ICC_EF_MWIS,
recordNumber: 1, // Get 1st Subscriber Profile.
callback: callback });
},
/**
* Update ICC MWIS. (Message Waiting Indication Status)
*
* @see TS 31.102, clause 4.2.63 for USIM and TS 51.011, clause 10.3.45 for SIM.
*/
updateMWIS: function updateMWIS(mwi) {
if (!RIL.iccInfoPrivate.mwis) {
return;
}
function dataWriter(recordSize) {
let mwis = RIL.iccInfoPrivate.mwis;
let msgCount =
(mwi.msgCount === GECKO_VOICEMAIL_MESSAGE_COUNT_UNKNOWN) ? 0 : mwi.msgCount;
[mwis[0], mwis[1]] = (mwi.active) ? [(mwis[0] | 0x01), msgCount]
: [(mwis[0] & 0xFE), 0];
let strLen = recordSize * 2;
Buf.writeInt32(strLen);
for (let i = 0; i < mwis.length; i++) {
GsmPDUHelper.writeHexOctet(mwis[i]);
}
Buf.writeStringDelimiter(strLen);
}
ICCIOHelper.updateLinearFixedEF({ fileId: ICC_EF_MWIS,
recordNumber: 1, // Update 1st Subscriber Profile.
dataWriter: dataWriter });
},
/**
* Read the SPDI (Service Provider Display Information) from the (U)SIM.
*

Просмотреть файл

@ -2330,6 +2330,10 @@ add_test(function test_reading_optional_efs() {
testEf.splice(testEf.indexOf("MDN"), 1);
};
record.readMWIS = function fakeReadMWIS() {
testEf.splice(testEf.indexOf("MWIS"), 1);
};
io.loadTransparentEF = function fakeLoadTransparentEF(options) {
// Write data size
buf.writeInt32(sst.length * 2);
@ -2356,7 +2360,7 @@ add_test(function test_reading_optional_efs() {
}
// TODO: Add all necessary optional EFs eventually
let supportedEf = ["MSISDN", "MDN"];
let supportedEf = ["MSISDN", "MDN", "MWIS"];
ril.appType = CARD_APPTYPE_SIM;
do_test(buildSST(supportedEf), supportedEf);
ril.appType = CARD_APPTYPE_USIM;
@ -2435,3 +2439,174 @@ add_test(function test_fetch_icc_recodes() {
run_next_test();
});
add_test(function test_read_mwis() {
let worker = newUint8Worker();
let helper = worker.GsmPDUHelper;
let recordHelper = worker.SimRecordHelper;
let buf = worker.Buf;
let io = worker.ICCIOHelper;
let mwisData;
let postedMessage;
worker.postMessage = function fakePostMessage(message) {
postedMessage = message;
};
io.loadLinearFixedEF = function fakeLoadLinearFixedEF(options) {
if (mwisData) {
// Write data size
buf.writeInt32(mwisData.length * 2);
// Write MWIS
for (let i = 0; i < mwisData.length; i++) {
helper.writeHexOctet(mwisData[i]);
}
// Write string delimiter
buf.writeStringDelimiter(mwisData.length * 2);
options.recordSize = mwisData.length;
if (options.callback) {
options.callback(options);
}
} else {
do_print("mwisData[] is not set.");
}
};
function buildMwisData(isActive, msgCount) {
if (msgCount < 0 || msgCount === GECKO_VOICEMAIL_MESSAGE_COUNT_UNKNOWN) {
msgCount = 0;
} else if (msgCount > 255) {
msgCount = 255;
}
mwisData = [ (isActive) ? 0x01 : 0x00,
msgCount,
0xFF, 0xFF, 0xFF ];
}
function do_test(isActive, msgCount) {
buildMwisData(isActive, msgCount);
recordHelper.readMWIS();
do_check_eq("iccmwis", postedMessage.rilMessageType);
do_check_eq(isActive, postedMessage.mwi.active);
do_check_eq((isActive) ? msgCount : 0, postedMessage.mwi.msgCount);
}
do_test(true, GECKO_VOICEMAIL_MESSAGE_COUNT_UNKNOWN);
do_test(true, 1);
do_test(true, 255);
do_test(false, 0);
do_test(false, 255); // Test the corner case when mwi is disable with incorrect msgCount.
run_next_test();
});
add_test(function test_update_mwis() {
let worker = newUint8Worker();
let pduHelper = worker.GsmPDUHelper;
let ril = worker.RIL;
ril.appType = CARD_APPTYPE_USIM;
ril.iccInfoPrivate.mwis = [0x00, 0x00, 0x00, 0x00, 0x00];
let recordHelper = worker.SimRecordHelper;
let buf = worker.Buf;
let ioHelper = worker.ICCIOHelper;
let recordSize = ril.iccInfoPrivate.mwis.length;
let recordNum = 1;
ioHelper.updateLinearFixedEF = function (options) {
options.pathId = worker.ICCFileHelper.getEFPath(options.fileId);
options.command = ICC_COMMAND_UPDATE_RECORD;
options.p1 = options.recordNumber;
options.p2 = READ_RECORD_ABSOLUTE_MODE;
options.p3 = recordSize;
ril.iccIO(options);
};
function do_test(isActive, count) {
let mwis = ril.iccInfoPrivate.mwis;
let isUpdated = false;
function buildMwisData() {
let result = mwis.slice(0);
result[0] = isActive? (mwis[0] | 0x01) : (mwis[0] & 0xFE);
result[1] = (count === GECKO_VOICEMAIL_MESSAGE_COUNT_UNKNOWN) ? 0 : count;
return result;
}
buf.sendParcel = function () {
isUpdated = true;
// Request Type.
do_check_eq(this.readInt32(), REQUEST_SIM_IO);
// Token : we don't care
this.readInt32();
// command.
do_check_eq(this.readInt32(), ICC_COMMAND_UPDATE_RECORD);
// fileId.
do_check_eq(this.readInt32(), ICC_EF_MWIS);
// pathId.
do_check_eq(this.readString(),
EF_PATH_MF_SIM + ((ril.appType === CARD_APPTYPE_USIM) ? EF_PATH_ADF_USIM : EF_PATH_DF_GSM));
// p1.
do_check_eq(this.readInt32(), recordNum);
// p2.
do_check_eq(this.readInt32(), READ_RECORD_ABSOLUTE_MODE);
// p3.
do_check_eq(this.readInt32(), recordSize);
// data.
let strLen = this.readInt32();
do_check_eq(recordSize * 2, strLen);
let expectedMwis = buildMwisData();
for (let i = 0; i < recordSize; i++) {
do_check_eq(expectedMwis[i], pduHelper.readHexOctet());
}
this.readStringDelimiter(strLen);
// pin2.
do_check_eq(this.readString(), null);
if (!worker.RILQUIRKS_V5_LEGACY) {
// AID. Ignore because it's from modem.
this.readInt32();
}
};
do_check_false(isUpdated);
recordHelper.updateMWIS({ active: isActive,
msgCount: count });
do_check_true((ril.iccInfoPrivate.mwis) ? isUpdated : !isUpdated);
}
do_test(true, GECKO_VOICEMAIL_MESSAGE_COUNT_UNKNOWN);
do_test(true, 1);
do_test(true, 255);
do_test(false, 0);
// Test if Path ID is correct for SIM.
ril.appType = CARD_APPTYPE_SIM;
do_test(false, 0);
// Test if loadLinearFixedEF() is not invoked in updateMWIS() when
// EF_MWIS is not loaded/available.
delete ril.iccInfoPrivate.mwis;
do_test(false, 0);
run_next_test();
});

Просмотреть файл

@ -83,6 +83,32 @@ XPCOMUtils.defineLazyGetter(this, "gPhoneNumberUtils", function () {
return ns.PhoneNumberUtils;
});
function SingleCall(options){
this.clientId = options.clientId;
this.callIndex = options.callIndex;
this.state = options.state;
this.number = options.number;
this.isOutgoing = options.isOutgoing;
this.isEmergency = options.isEmergency;
this.isConference = options.isConference;
}
SingleCall.prototype = {
clientId: null,
callIndex: null,
state: null,
number: null,
isOutgoing: false,
isEmergency: false,
isConference: false
};
function ConferenceCall(state){
this.state = state;
}
ConferenceCall.prototype = {
state: null
};
function TelephonyProvider() {
this._numClients = gRadioInterfaceLayer.numRadioInterfaces;
this._listeners = [];
@ -162,14 +188,11 @@ TelephonyProvider.prototype = {
}
},
_matchActiveCall: function _matchActiveCall(aCall) {
if (this._activeCall &&
this._activeCall.callIndex == aCall.callIndex &&
this._activeCall.clientId == aCall.clientId) {
return true;
}
return false;
_matchActiveSingleCall: function _matchActiveSingleCall(aCall) {
return this._activeCall &&
this._activeCall instanceof SingleCall &&
this._activeCall.clientId === aCall.clientId &&
this._activeCall.callIndex === aCall.callIndex;
},
/**
@ -179,17 +202,28 @@ TelephonyProvider.prototype = {
_updateCallAudioState: function _updateCallAudioState(aCall,
aConferenceState) {
if (aConferenceState === nsITelephonyProvider.CALL_STATE_CONNECTED) {
this._activeCall = new ConferenceCall(aConferenceState);
gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_IN_CALL;
if (this.speakerEnabled) {
gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION,
nsIAudioManager.FORCE_SPEAKER);
}
if (DEBUG) {
debug("Active call, put audio system into PHONE_STATE_IN_CALL: " +
gAudioManager.phoneState);
}
return;
}
if (aConferenceState === nsITelephonyProvider.CALL_STATE_UNKNOWN ||
aConferenceState === nsITelephonyProvider.CALL_STATE_HELD) {
if (!this._activeCall) {
if (this._activeCall instanceof ConferenceCall) {
this._activeCall = null;
gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL;
if (DEBUG) {
debug("No active call, put audio system into PHONE_STATE_NORMAL: " +
gAudioManager.phoneState);
}
}
return;
}
@ -199,7 +233,7 @@ TelephonyProvider.prototype = {
}
if (aCall.isConference) {
if (this._matchActiveCall(aCall)) {
if (this._matchActiveSingleCall(aCall)) {
this._activeCall = null;
}
return;
@ -210,7 +244,7 @@ TelephonyProvider.prototype = {
case nsITelephonyProvider.CALL_STATE_ALERTING:
case nsITelephonyProvider.CALL_STATE_CONNECTED:
aCall.isActive = true;
this._activeCall = aCall;
this._activeCall = new SingleCall(aCall);
gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_IN_CALL;
if (this.speakerEnabled) {
gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION,
@ -238,7 +272,7 @@ TelephonyProvider.prototype = {
case nsITelephonyProvider.CALL_STATE_HELD: // Fall through...
case nsITelephonyProvider.CALL_STATE_DISCONNECTED:
aCall.isActive = false;
if (this._matchActiveCall(aCall)) {
if (this._matchActiveSingleCall(aCall)) {
// Previously active call is not active now.
this._activeCall = null;
}
@ -355,7 +389,7 @@ TelephonyProvider.prototype = {
for (let call of response.calls) {
call.clientId = aClientId;
call.state = this._convertRILCallState(call.state);
call.isActive = this._matchActiveCall(call);
call.isActive = this._matchActiveSingleCall(call);
aListener.enumerateCallState(call.clientId, call.callIndex,
call.state, call.number,
@ -494,6 +528,7 @@ TelephonyProvider.prototype = {
};
gSystemMessenger.broadcastMessage("telephony-call-ended", data);
aCall.clientId = aClientId;
this._updateCallAudioState(aCall, null);
if (!aCall.failCause ||
@ -545,6 +580,7 @@ TelephonyProvider.prototype = {
gSystemMessenger.broadcastMessage("telephony-new-call", {});
}
aCall.clientId = aClientId;
this._updateCallAudioState(aCall, null);
this._notifyAllListeners("callStateChanged", [aClientId,

Просмотреть файл

@ -46,3 +46,4 @@ disabled = Bug 821958
[test_dsds_default_service_id.js]
[test_call_mute.js]
[test_dsds_normal_call.js]
[test_audiomanager_phonestate.js]

Просмотреть файл

@ -0,0 +1,265 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
const AUDIO_MANAGER_CONTRACT_ID = "@mozilla.org/telephony/audiomanager;1";
// See nsIAudioManager
const PHONE_STATE_INVALID = -2;
const PHONE_STATE_CURRENT = -1;
const PHONE_STATE_NORMAL = 0;
const PHONE_STATE_RINGTONE = 1;
const PHONE_STATE_IN_CALL = 2;
const PHONE_STATE_IN_COMMUNICATION = 3;
let conference;
function checkTelephonyActiveAndCalls(active, calls) {
is(telephony.active, active, "telephony.active");
is(telephony.calls.length, calls.length, "telephony.calls");
for (let i = 0; i < calls.length; ++i) {
is(telephony.calls[i], calls[i]);
}
}
function checkConferenceStateAndCalls(state, calls) {
is(conference.state, state, "conference.state");
is(conference.calls.length, calls.length, "conference.calls");
for (let i = 0; i < calls.length; i++) {
is(conference.calls[i], calls[i]);
}
}
function checkInitialState() {
log("Verify initial state.");
ok(telephony.calls, 'telephony.call');
checkTelephonyActiveAndCalls(null, []);
ok(conference.calls, 'conference.calls');
checkConferenceStateAndCalls('', []);
}
function checkEventCallState(event, call, state) {
is(call, event.call, "event.call");
is(call.state, state, "call state");
}
function dial(number) {
log("Make an outgoing call: " + number);
let deferred = Promise.defer();
let call = telephony.dial(number);
ok(call);
is(call.number, number);
is(call.state, "dialing");
call.onalerting = function onalerting(event) {
call.onalerting = null;
log("Received 'onalerting' call event.");
checkEventCallState(event, call, "alerting");
deferred.resolve(call);
};
return deferred.promise;
}
function answer(call) {
log("Answering the incoming call.");
let deferred = Promise.defer();
call.onconnecting = function onconnectingIn(event) {
log("Received 'connecting' call event for incoming call.");
call.onconnecting = null;
checkEventCallState(event, call, "connecting");
};
call.onconnected = function onconnectedIn(event) {
log("Received 'connected' call event for incoming call.");
call.onconnected = null;
checkEventCallState(event, call, "connected");
ok(!call.onconnecting);
deferred.resolve(call);
};
call.answer();
return deferred.promise;
}
function remoteDial(number) {
log("Simulating an incoming call.");
let deferred = Promise.defer();
telephony.onincoming = function onincoming(event) {
log("Received 'incoming' call event.");
telephony.onimcoming = null;
let call = event.call;
ok(call);
is(call.number, number);
is(call.state, "incoming");
deferred.resolve(call);
};
emulator.run("gsm call " + number);
return deferred.promise;
}
function remoteAnswer(call) {
log("Remote answering the call.");
let deferred = Promise.defer();
call.onconnected = function onconnected(event) {
log("Received 'connected' call event.");
call.onconnected = null;
checkEventCallState(event, call, "connected");
deferred.resolve(call);
};
emulator.run("gsm accept " + call.number);
return deferred.promise;
}
function remoteHangUp(call) {
log("Remote hanging up the call.");
let deferred = Promise.defer();
call.ondisconnected = function ondisconnected(event) {
log("Received 'disconnected' call event.");
call.ondisconnected = null;
checkEventCallState(event, call, "disconnected");
deferred.resolve(call);
};
emulator.run("gsm cancel " + call.number);
return deferred.promise;
}
function remoteHangUpCalls(calls) {
let promise = Promise.resolve();
for (let call of calls) {
promise = promise.then(remoteHangUp.bind(null, call));
}
return promise;
}
// The length of callsToAdd should be 2.
function addCallsToConference(callsToAdd) {
log("Add " + callsToAdd.length + " calls into conference.");
let deferred = Promise.defer();
conference.onconnected = function() {
deferred.resolve();
};
conference.add(callsToAdd[0], callsToAdd[1]);
return deferred.promise;
}
let audioManager;
function checkStates(speakerEnabled, phoneState) {
if (!audioManager) {
audioManager = SpecialPowers.Cc[AUDIO_MANAGER_CONTRACT_ID]
.getService(SpecialPowers.Ci.nsIAudioManager);
ok(audioManager, "nsIAudioManager instance");
}
is(telephony.speakerEnabled, speakerEnabled, "telephony.speakerEnabled");
if (phoneState == PHONE_STATE_CURRENT) {
ok(audioManager.phoneState === PHONE_STATE_CURRENT ||
audioManager.phoneState === PHONE_STATE_NORMAL, "audioManager.phoneState");
} else {
is(audioManager.phoneState, phoneState, "audioManager.phoneState");
}
}
function check(phoneStateOrig, phoneStateEnabled, phoneStateDisabled) {
checkStates(false, phoneStateOrig);
let canEnableSpeaker = arguments.length > 1;
telephony.speakerEnabled = true;
if (canEnableSpeaker) {
checkStates(true, phoneStateEnabled);
} else {
checkStates(false, phoneStateOrig);
return;
}
telephony.speakerEnabled = false;
checkStates(false, arguments.length > 2 ? phoneStateDisabled : phoneStateOrig);
}
// Start the test
startTest(function() {
conference = telephony.conferenceGroup;
ok(conference);
let outNumber = "5555550101";
let inNumber = "5555550201";
let outCall;
let inCall;
Promise.resolve()
.then(checkInitialState)
.then(() => check(PHONE_STATE_CURRENT, PHONE_STATE_NORMAL, PHONE_STATE_NORMAL))
// Dial in
.then(() => remoteDial(inNumber))
.then(call => { inCall = call; })
// TODO - Bug 948860: should this be {RINGTONE, RINGTONE, RINGTONE}?
// From current UX spec, there is no chance that an user may enable speaker
// during alerting, so basically this 'set speaker enable' action can't
// happen in B2G.
.then(() => check(PHONE_STATE_RINGTONE, PHONE_STATE_NORMAL, PHONE_STATE_NORMAL))
.then(() => answer(inCall))
.then(() => check(PHONE_STATE_IN_CALL, PHONE_STATE_IN_CALL))
// Hang up all
.then(() => remoteHangUp(inCall))
.then(() => check(PHONE_STATE_NORMAL, PHONE_STATE_NORMAL))
// Dial out
.then(() => dial(outNumber))
.then(call => { outCall = call; })
.then(() => check(PHONE_STATE_IN_CALL, PHONE_STATE_IN_CALL))
.then(() => remoteAnswer(outCall))
.then(() => check(PHONE_STATE_IN_CALL, PHONE_STATE_IN_CALL))
// Hang up all
.then(() => remoteHangUp(outCall))
.then(() => check(PHONE_STATE_NORMAL, PHONE_STATE_NORMAL))
// Dial out
.then(() => dial(outNumber))
.then(call => { outCall = call; })
.then(() => check(PHONE_STATE_IN_CALL, PHONE_STATE_IN_CALL))
.then(() => remoteAnswer(outCall))
.then(() => check(PHONE_STATE_IN_CALL, PHONE_STATE_IN_CALL))
// Dial out and dial in
.then(() => remoteDial(inNumber))
.then(call => { inCall = call; })
.then(() => check(PHONE_STATE_IN_CALL, PHONE_STATE_IN_CALL))
.then(() => answer(inCall))
.then(() => check(PHONE_STATE_IN_CALL, PHONE_STATE_IN_CALL))
// Conference
.then(() => addCallsToConference([outCall, inCall]))
.then(() => check(PHONE_STATE_IN_CALL, PHONE_STATE_IN_CALL))
// Hang up all
.then(() => remoteHangUpCalls([outCall, inCall]))
.then(() => check(PHONE_STATE_NORMAL, PHONE_STATE_NORMAL))
// End
.then(null, error => {
ok(false, 'promise rejects during test.');
})
.then(finish);
});

Просмотреть файл

@ -185,9 +185,9 @@ this.Authenticator = {
checked: false,
sec: AUTH_SEC_TYPE[sec],
mac: mac.toUpperCase(),
dataLength: wbxml.length,
data: wbxml
};
authInfo.data.length = wbxml.length;
switch (authInfo.sec) {
case "NETWPIN":

Просмотреть файл

@ -123,6 +123,7 @@ this.WapPushManager = {
contentType: contentType,
content: data.array
};
msg.content.length = data.array.length;
}
let sender = PhoneNumberUtils.normalize(options.sourceAddress, false);

Просмотреть файл

@ -14,6 +14,7 @@ function test_parser(rawDataArray, contentType, expectResult) {
let msg = CP.PduHelper.parse(data, contentType);
do_check_eq(msg.contentType, expectResult.contentType);
do_check_eq(msg.content, expectResult.content);
do_check_eq(msg.content.length, expectResult.content.length);
}
function test_hmac(rawDataArray, mac, key, expectResult) {
@ -21,7 +22,7 @@ function test_hmac(rawDataArray, mac, key, expectResult) {
return key;
});
do_check_eq(authInfo.data, rawDataArray);
do_check_eq(authInfo.dataLength, rawDataArray.length);
do_check_eq(authInfo.data.length, rawDataArray.length);
do_check_eq(authInfo.checked, expectResult.checked);
do_check_eq(authInfo.pass, expectResult.pass);
}

Просмотреть файл

@ -113,6 +113,30 @@ public:
return mCumulativeResolution / mResolution;
}
// Ensure the scrollableRect is at least as big as the compositionBounds
// because the scrollableRect can be smaller if the content is not large
// and the scrollableRect hasn't been updated yet.
// We move the scrollableRect up because we don't know if we can move it
// down. i.e. we know that scrollableRect can go back as far as zero.
// but we don't know how much further ahead it can go.
CSSRect GetExpandedScrollableRect() const
{
CSSRect scrollableRect = mScrollableRect;
if (scrollableRect.width < mCompositionBounds.width) {
scrollableRect.x = std::max(0.f,
scrollableRect.x - (mCompositionBounds.width - scrollableRect.width));
scrollableRect.width = mCompositionBounds.width;
}
if (scrollableRect.height < mCompositionBounds.height) {
scrollableRect.y = std::max(0.f,
scrollableRect.y - (mCompositionBounds.height - scrollableRect.height));
scrollableRect.height = mCompositionBounds.height;
}
return scrollableRect;
}
/**
* Return the scale factor needed to fit the viewport
* into its composition bounds.

Просмотреть файл

@ -1165,25 +1165,8 @@ const CSSRect AsyncPanZoomController::CalculatePendingDisplayPort(
aEstimatedPaintDuration > EPSILON ? aEstimatedPaintDuration : 1.0;
CSSIntRect compositionBounds = gfx::RoundedIn(aFrameMetrics.mCompositionBounds / aFrameMetrics.mZoom);
CSSRect scrollableRect = aFrameMetrics.mScrollableRect;
// Ensure the scrollableRect is at least as big as the compositionBounds
// because the scrollableRect can be smaller if the content is not large
// and the scrollableRect hasn't been updated yet.
// We move the scrollableRect up because we don't know if we can move it
// down. i.e. we know that scrollableRect can go back as far as zero.
// but we don't know how much further ahead it can go.
if (scrollableRect.width < compositionBounds.width) {
scrollableRect.x = std::max(0.f,
scrollableRect.x - (compositionBounds.width - scrollableRect.width));
scrollableRect.width = compositionBounds.width;
}
if (scrollableRect.height < compositionBounds.height) {
scrollableRect.y = std::max(0.f,
scrollableRect.y - (compositionBounds.height - scrollableRect.height));
scrollableRect.height = compositionBounds.height;
}
CSSRect scrollableRect = aFrameMetrics.GetExpandedScrollableRect();
CSSPoint scrollOffset = aFrameMetrics.mScrollOffset;
CSSRect displayPort = CSSRect(compositionBounds);

Просмотреть файл

@ -67,3 +67,16 @@ index 16a735c..0387a71 100644
#endif
+
+#endif
diff --git a/media/libvpx/vp8/common/loopfilter.c b/media/libvpx/vp8/common/loopfilter.c
index 19857a7..3c0fa63 100644
--- a/media/libvpx/vp8/common/loopfilter.c
+++ b/media/libvpx/vp8/common/loopfilter.c
@@ -15,8 +15,6 @@
#include "onyxc_int.h"
#include "vpx_mem/vpx_mem.h"
-typedef unsigned char uc;
-
static void lf_init_lut(loop_filter_info_n *lfi)
{
int filt_lvl;

Просмотреть файл

@ -15,8 +15,6 @@
#include "onyxc_int.h"
#include "vpx_mem/vpx_mem.h"
typedef unsigned char uc;
static void lf_init_lut(loop_filter_info_n *lfi)
{
int filt_lvl;

Просмотреть файл

@ -94,49 +94,12 @@ class GlobalHistory {
GeckoAppShell.notifyUriVisited(uri);
}
// Logic ported from nsNavHistory::CanAddURI.
// http://mxr.mozilla.org/mozilla-central/source/toolkit/components/places/nsNavHistory.cpp#1272
private boolean canAddURI(String uri) {
if (uri == null || uri.length() == 0)
return false;
// First, heck the most common cases (HTTP, HTTPS) to avoid most of the work.
if (uri.startsWith("http:") || uri.startsWith("https:"))
return true;
String scheme = Uri.parse(uri).getScheme();
if (scheme == null)
return false;
// Now check for all bad things.
if (scheme.equals("about") ||
scheme.equals("imap") ||
scheme.equals("news") ||
scheme.equals("mailbox") ||
scheme.equals("moz-anno") ||
scheme.equals("view-source") ||
scheme.equals("chrome") ||
scheme.equals("resource") ||
scheme.equals("data") ||
scheme.equals("wyciwyg") ||
scheme.equals("javascript"))
return false;
return true;
}
public void add(String uri) {
if (!canAddURI(uri))
return;
BrowserDB.updateVisitedHistory(GeckoAppShell.getContext().getContentResolver(), uri);
addToGeckoOnly(uri);
}
public void update(String uri, String title) {
if (!canAddURI(uri))
return;
BrowserDB.updateHistoryTitle(GeckoAppShell.getContext().getContentResolver(), uri, title);
}

Просмотреть файл

@ -84,6 +84,14 @@ nsAndroidHistory::VisitURI(nsIURI *aURI, nsIURI *aLastVisitedURI, uint32_t aFlag
if (!aURI)
return NS_OK;
// Silently return if URI is something we shouldn't add to DB.
bool canAdd;
nsresult rv = CanAddURI(aURI, &canAdd);
NS_ENSURE_SUCCESS(rv, rv);
if (!canAdd) {
return NS_OK;
}
if (!(aFlags & VisitFlags::TOP_LEVEL))
return NS_OK;
@ -94,7 +102,7 @@ nsAndroidHistory::VisitURI(nsIURI *aURI, nsIURI *aLastVisitedURI, uint32_t aFlag
return NS_OK;
nsAutoCString uri;
nsresult rv = aURI->GetSpec(uri);
rv = aURI->GetSpec(uri);
if (NS_FAILED(rv)) return rv;
NS_ConvertUTF8toUTF16 uriString(uri);
GeckoAppShell::MarkURIVisited(uriString);
@ -104,6 +112,14 @@ nsAndroidHistory::VisitURI(nsIURI *aURI, nsIURI *aLastVisitedURI, uint32_t aFlag
NS_IMETHODIMP
nsAndroidHistory::SetURITitle(nsIURI *aURI, const nsAString& aTitle)
{
// Silently return if URI is something we shouldn't add to DB.
bool canAdd;
nsresult rv = CanAddURI(aURI, &canAdd);
NS_ENSURE_SUCCESS(rv, rv);
if (!canAdd) {
return NS_OK;
}
if (AndroidBridge::Bridge()) {
nsAutoCString uri;
nsresult rv = aURI->GetSpec(uri);
@ -144,3 +160,53 @@ nsAndroidHistory::Run()
}
return NS_OK;
}
// Filter out unwanted URIs such as "chrome:", "mailbox:", etc.
//
// The model is if we don't know differently then add which basically means
// we are suppose to try all the things we know not to allow in and then if
// we don't bail go on and allow it in.
//
// Logic ported from nsNavHistory::CanAddURI.
NS_IMETHODIMP
nsAndroidHistory::CanAddURI(nsIURI* aURI, bool* canAdd)
{
NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
NS_ENSURE_ARG(aURI);
NS_ENSURE_ARG_POINTER(canAdd);
nsAutoCString scheme;
nsresult rv = aURI->GetScheme(scheme);
NS_ENSURE_SUCCESS(rv, rv);
// first check the most common cases (HTTP, HTTPS) to allow in to avoid most
// of the work
if (scheme.EqualsLiteral("http")) {
*canAdd = true;
return NS_OK;
}
if (scheme.EqualsLiteral("https")) {
*canAdd = true;
return NS_OK;
}
// now check for all bad things
if (scheme.EqualsLiteral("about") ||
scheme.EqualsLiteral("imap") ||
scheme.EqualsLiteral("news") ||
scheme.EqualsLiteral("mailbox") ||
scheme.EqualsLiteral("moz-anno") ||
scheme.EqualsLiteral("view-source") ||
scheme.EqualsLiteral("chrome") ||
scheme.EqualsLiteral("resource") ||
scheme.EqualsLiteral("data") ||
scheme.EqualsLiteral("wyciwyg") ||
scheme.EqualsLiteral("javascript") ||
scheme.EqualsLiteral("blob")) {
*canAdd = false;
return NS_OK;
}
*canAdd = true;
return NS_OK;
}

Просмотреть файл

@ -34,6 +34,8 @@ private:
nsDataHashtable<nsStringHashKey, nsTArray<mozilla::dom::Link *> *> mListeners;
nsTPriorityQueue<nsString> mPendingURIs;
nsresult CanAddURI(nsIURI* aURI, bool* canAdd);
};
#endif

Просмотреть файл

@ -14,6 +14,7 @@ add_test(function test_working_bid_exchange() {
_("Ensure that working BrowserID token exchange works as expected.");
let service = "http://example.com/foo";
let duration = 300;
let server = httpd_setup({
"/1.0/foo/1.0": function(request, response) {
@ -29,6 +30,7 @@ add_test(function test_working_bid_exchange() {
key: "key",
api_endpoint: service,
uid: "uid",
duration: duration,
});
response.bodyOutputStream.write(body, body.length);
}
@ -40,12 +42,12 @@ add_test(function test_working_bid_exchange() {
client.getTokenFromBrowserIDAssertion(url, "assertion", cb);
let result = cb.wait();
do_check_eq("object", typeof(result));
do_check_attribute_count(result, 4);
do_check_attribute_count(result, 5);
do_check_eq(service, result.endpoint);
do_check_eq("id", result.id);
do_check_eq("key", result.key);
do_check_eq("uid", result.uid);
do_check_eq(duration, result.duration);
server.stop(run_next_test);
});
@ -200,6 +202,7 @@ add_test(function test_403_no_urls() {
add_test(function test_send_conditions_accepted() {
_("Ensures that the condition acceptance header is sent when asked.");
let duration = 300;
let server = httpd_setup({
"/1.0/foo/1.0": function(request, response) {
do_check_true(request.hasHeader("x-conditions-accepted"));
@ -213,6 +216,7 @@ add_test(function test_send_conditions_accepted() {
key: "key",
api_endpoint: "http://example.com/",
uid: "uid",
duration: duration,
});
response.bodyOutputStream.write(body, body.length);
}
@ -384,6 +388,7 @@ add_test(function test_unhandled_media_type() {
add_test(function test_rich_media_types() {
_("Ensure that extra tokens in the media type aren't rejected.");
let duration = 300;
let server = httpd_setup({
"/foo": function(request, response) {
response.setStatusLine(request.httpVersion, 200, "OK");
@ -394,6 +399,7 @@ add_test(function test_rich_media_types() {
key: "key",
api_endpoint: "foo",
uid: "uid",
duration: duration,
});
response.bodyOutputStream.write(body, body.length);
}
@ -411,6 +417,7 @@ add_test(function test_rich_media_types() {
add_test(function test_exception_during_callback() {
_("Ensure that exceptions thrown during callback handling are handled.");
let duration = 300;
let server = httpd_setup({
"/foo": function(request, response) {
response.setStatusLine(request.httpVersion, 200, "OK");
@ -421,6 +428,7 @@ add_test(function test_exception_during_callback() {
key: "key",
api_endpoint: "foo",
uid: "uid",
duration: duration,
});
response.bodyOutputStream.write(body, body.length);
}

Просмотреть файл

@ -359,7 +359,7 @@ TokenServerClient.prototype = {
return;
}
for (let k of ["id", "key", "api_endpoint", "uid"]) {
for (let k of ["id", "key", "api_endpoint", "uid", "duration"]) {
if (!(k in result)) {
let error = new TokenServerClientServerError("Expected key not " +
" present in result: " +
@ -377,6 +377,7 @@ TokenServerClient.prototype = {
key: result.key,
endpoint: result.api_endpoint,
uid: result.uid,
duration: result.duration,
});
}
};

Просмотреть файл

@ -228,6 +228,16 @@ add_test(function test_hawk() {
do_check_eq(result.artifacts.hash, "66DiyapJ0oGgj09IXWdMv8VCg9xk0PL5RqX7bNnQW2k=");
do_check_eq(result.artifacts.mac, "2B++3x5xfHEZbPZGDiK3IwfPZctkV4DUr2ORg1vIHvk=");
// the payload "hash" is also non-urlsafe base64 (+/)
result = compute(makeURI("http://example.net/path"), method,
{ credentials: credentials_sha256,
ts: 1353809207,
nonce: "Ygvqdz",
payload: "something else",
});
do_check_eq(result.artifacts.hash, "lERFXr/IKOaAoYw+eBseDUSwmqZTX0uKZpcWLxsdzt8=");
do_check_eq(result.artifacts.mac, "jiZuhsac35oD7IdcblhFncBr8tJFHcwWLr8NIYWr9PQ=");
/* Test non-ascii hostname. HAWK (via the node.js "url" module) punycodes
* "ëxample.net" into "xn--xample-ova.net" before hashing. I still think
* punycode was a bad joke that got out of the lab and into a spec.

Просмотреть файл

@ -16,34 +16,11 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/FxAccountsClient.jsm");
Cu.import("resource://gre/modules/FxAccountsCommon.js");
XPCOMUtils.defineLazyModuleGetter(this, "jwcrypto",
"resource://gre/modules/identity/jwcrypto.jsm");
const DATA_FORMAT_VERSION = 1;
const DEFAULT_STORAGE_FILENAME = "signedInUser.json";
const ASSERTION_LIFETIME = 1000 * 60 * 5; // 5 minutes
const KEY_LIFETIME = 1000 * 3600 * 12; // 12 hours
const CERT_LIFETIME = 1000 * 3600 * 6; // 6 hours
const POLL_SESSION = 1000 * 60 * 5; // 5 minutes
const POLL_STEP = 1000 * 3; // 3 seconds
// loglevel preference should be one of: "FATAL", "ERROR", "WARN", "INFO",
// "CONFIG", "DEBUG", "TRACE" or "ALL". We will be logging error messages by
// default.
const PREF_LOG_LEVEL = "identity.fxaccounts.loglevel";
try {
this.LOG_LEVEL =
Services.prefs.getPrefType(PREF_LOG_LEVEL) == Ci.nsIPrefBranch.PREF_STRING
&& Services.prefs.getCharPref(PREF_LOG_LEVEL);
} catch (e) {
this.LOG_LEVEL = Log.Level.Error;
}
let log = Log.repository.getLogger("Services.FxAccounts");
log.level = LOG_LEVEL;
log.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));
InternalMethods = function(mock) {
this.cert = null;
this.keyPair = null;

Просмотреть файл

@ -10,6 +10,7 @@ Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://services-common/utils.js");
Cu.import("resource://services-crypto/utils.js");
Cu.import("resource://gre/modules/FxAccountsCommon.js");
// Default can be changed by the preference 'identity.fxaccounts.auth.uri'
let _host = "https://api-accounts.dev.lcip.org/v1";
@ -283,6 +284,9 @@ this.FxAccountsClient.prototype = {
payload = JSON.stringify(jsonPayload);
}
log.debug("(HAWK request) - Path: " + path + " - Method: " + method +
" - Payload: " + payload);
xhr.open(method, URI);
xhr.channel.loadFlags = Ci.nsIChannel.LOAD_BYPASS_CACHE |
Ci.nsIChannel.INHIBIT_CACHING;
@ -300,12 +304,16 @@ this.FxAccountsClient.prototype = {
xhr.onload = function onload() {
try {
let response = JSON.parse(xhr.responseText);
log.debug("(Response) Code: " + xhr.status + " - Status text: " +
xhr.statusText + " - Response text: " + xhr.responseText);
if (xhr.status !== 200 || response.error) {
// In this case, the response is an object with error information.
return deferred.reject(response);
}
deferred.resolve(response);
} catch (e) {
log.error("(Response) Code: " + xhr.status + " - Status text: " +
xhr.statusText);
deferred.reject(constructError(e));
}
};

Просмотреть файл

@ -0,0 +1,112 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const { interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Log.jsm");
// loglevel should be one of "Fatal", "Error", "Warn", "Info", "Config",
// "Debug", "Trace" or "All". If none is specified, "Error" will be used by
// default.
const PREF_LOG_LEVEL = "identity.fxaccounts.loglevel";
XPCOMUtils.defineLazyGetter(this, 'log', function() {
let log = Log.repository.getLogger("FirefoxAccounts");
log.addAppender(new Log.DumpAppender());
log.level = Log.Level.Error;
try {
let level =
Services.prefs.getPrefType(PREF_LOG_LEVEL) == Ci.nsIPrefBranch.PREF_STRING
&& Services.prefs.getCharPref(PREF_LOG_LEVEL);
log.level = Log.Level[level] || Log.Level.Error;
} catch (e) {
log.error(e);
}
return log;
});
this.DATA_FORMAT_VERSION = 1;
this.DEFAULT_STORAGE_FILENAME = "signedInUser.json";
// Token life times.
this.ASSERTION_LIFETIME = 1000 * 60 * 5; // 5 minutes
this.CERT_LIFETIME = 1000 * 3600 * 6; // 6 hours
this.KEY_LIFETIME = 1000 * 3600 * 12; // 12 hours
// Polling timings.
this.POLL_SESSION = 1000 * 60 * 5; // 5 minutes
this.POLL_STEP = 1000 * 3; // 3 seconds
// Server errno.
// From https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md#response-format
this.ERRNO_ACCOUNT_ALREADY_EXISTS = 101;
this.ERRNO_ACCOUNT_DOES_NOT_EXISTS = 102;
this.ERRNO_INCORRECT_PASSWORD = 103;
this.ERRNO_UNVERIFIED_ACCOUNT = 104;
this.ERRNO_INVALID_VERIFICATION_CODE = 105;
this.ERRNO_NOT_VALID_JSON_BODY = 106;
this.ERRNO_INVALID_BODY_PARAMETERS = 107;
this.ERRNO_MISSING_BODY_PARAMETERS = 108;
this.ERRNO_INVALID_REQUEST_SIGNATURE = 109;
this.ERRNO_INVALID_AUTH_TOKEN = 110;
this.ERRNO_INVALID_AUTH_TIMESTAMP = 111;
this.ERRNO_MISSING_CONTENT_LENGTH = 112;
this.ERRNO_REQUEST_BODY_TOO_LARGE = 113;
this.ERRNO_TOO_MANY_CLIENT_REQUESTS = 114;
this.ERRNO_INVALID_AUTH_NONCE = 115;
this.ERRNO_SERVICE_TEMP_UNAVAILABLE = 201;
this.ERRNO_UNKNOWN_ERROR = 999;
// Errors.
this.ERROR_ACCOUNT_ALREADY_EXISTS = "ACCOUNT_ALREADY_EXISTS";
this.ERROR_ACCOUNT_DOES_NOT_EXISTS = "ACCOUNT_DOES_NOT_EXISTS";
this.ERROR_ALREADY_SIGNED_IN_USER = "ALREADY_SIGNED_IN_USER";
this.ERROR_INVALID_ACCOUNTID = "INVALID_ACCOUNTID";
this.ERROR_INVALID_AUDIENCE = "INVALID_AUDIENCE";
this.ERROR_INVALID_AUTH_TOKEN = "INVALID_AUTH_TOKEN";
this.ERROR_INVALID_AUTH_TIMESTAMP = "INVALID_AUTH_TIMESTAMP";
this.ERROR_INVALID_AUTH_NONCE = "INVALID_AUTH_NONCE";
this.ERROR_INVALID_BODY_PARAMETERS = "INVALID_BODY_PARAMETERS";
this.ERROR_INVALID_PASSWORD = "INVALID_PASSWORD";
this.ERROR_INVALID_VERIFICATION_CODE = "INVALID_VERIFICATION_CODE";
this.ERROR_INVALID_REQUEST_SIGNATURE = "INVALID_REQUEST_SIGNATURE";
this.ERROR_INTERNAL_INVALID_USER = "INTERNAL_ERROR_INVALID_USER";
this.ERROR_MISSING_BODY_PARAMETERS = "MISSING_BODY_PARAMETERS";
this.ERROR_MISSING_CONTENT_LENGTH = "MISSING_CONTENT_LENGTH";
this.ERROR_NO_TOKEN_SESSION = "NO_TOKEN_SESSION";
this.ERROR_NOT_VALID_JSON_BODY = "NOT_VALID_JSON_BODY";
this.ERROR_OFFLINE = "OFFLINE";
this.ERROR_REQUEST_BODY_TOO_LARGE = "REQUEST_BODY_TOO_LARGE";
this.ERROR_SERVER_ERROR = "SERVER_ERROR";
this.ERROR_TOO_MANY_CLIENT_REQUESTS = "TOO_MANY_CLIENT_REQUESTS";
this.ERROR_SERVICE_TEMP_UNAVAILABLE = "SERVICE_TEMPORARY_UNAVAILABLE";
this.ERROR_UI_ERROR = "UI_ERROR";
this.ERROR_UNKNOWN = "UNKNOWN_ERROR";
this.ERROR_UNVERIFIED_ACCOUNT = "UNVERIFIED_ACCOUNT";
// Error matching.
this.SERVER_ERRNO_TO_ERROR = {};
SERVER_ERRNO_TO_ERROR[ERRNO_ACCOUNT_ALREADY_EXISTS] = ERROR_ACCOUNT_ALREADY_EXISTS;
SERVER_ERRNO_TO_ERROR[ERRNO_ACCOUNT_DOES_NOT_EXISTS] = ERROR_ACCOUNT_DOES_NOT_EXISTS;
SERVER_ERRNO_TO_ERROR[ERRNO_INCORRECT_PASSWORD] = ERROR_INVALID_PASSWORD;
SERVER_ERRNO_TO_ERROR[ERRNO_UNVERIFIED_ACCOUNT] = ERROR_UNVERIFIED_ACCOUNT;
SERVER_ERRNO_TO_ERROR[ERRNO_INVALID_VERIFICATION_CODE] = ERROR_INVALID_VERIFICATION_CODE;
SERVER_ERRNO_TO_ERROR[ERRNO_NOT_VALID_JSON_BODY] = ERROR_NOT_VALID_JSON_BODY;
SERVER_ERRNO_TO_ERROR[ERRNO_INVALID_BODY_PARAMETERS] = ERROR_INVALID_BODY_PARAMETERS;
SERVER_ERRNO_TO_ERROR[ERRNO_MISSING_BODY_PARAMETERS] = ERROR_MISSING_BODY_PARAMETERS;
SERVER_ERRNO_TO_ERROR[ERRNO_INVALID_REQUEST_SIGNATURE] = ERROR_INVALID_REQUEST_SIGNATURE;
SERVER_ERRNO_TO_ERROR[ERRNO_INVALID_AUTH_TOKEN] = ERROR_INVALID_AUTH_TOKEN;
SERVER_ERRNO_TO_ERROR[ERRNO_INVALID_AUTH_TIMESTAMP] = ERROR_INVALID_AUTH_TIMESTAMP;
SERVER_ERRNO_TO_ERROR[ERRNO_MISSING_CONTENT_LENGTH] = ERROR_MISSING_CONTENT_LENGTH;
SERVER_ERRNO_TO_ERROR[ERRNO_REQUEST_BODY_TOO_LARGE] = ERROR_REQUEST_BODY_TOO_LARGE;
SERVER_ERRNO_TO_ERROR[ERRNO_TOO_MANY_CLIENT_REQUESTS] = ERROR_TOO_MANY_CLIENT_REQUESTS;
SERVER_ERRNO_TO_ERROR[ERRNO_INVALID_AUTH_NONCE] = ERROR_INVALID_AUTH_NONCE;
SERVER_ERRNO_TO_ERROR[ERRNO_SERVICE_TEMP_UNAVAILABLE] = ERROR_SERVICE_TEMP_UNAVAILABLE;
SERVER_ERRNO_TO_ERROR[ERRNO_UNKNOWN_ERROR] = ERROR_UNKNOWN;
// Allow this file to be imported via Components.utils.import().
this.EXPORTED_SYMBOLS = Object.keys(this);

Просмотреть файл

@ -0,0 +1,415 @@
/* 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/. */
/**
* Temporary abstraction layer for common Fx Accounts operations.
* For now, we will be using this module only from B2G but in the end we might
* want this to be merged with FxAccounts.jsm and let other products also use
* it.
*/
"use strict";
this.EXPORTED_SYMBOLS = ["FxAccountsManager"];
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/FxAccounts.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/FxAccountsCommon.js");
XPCOMUtils.defineLazyModuleGetter(this, "FxAccountsClient",
"resource://gre/modules/FxAccountsClient.jsm");
this.FxAccountsManager = {
// We don't really need to save fxAccounts instance but this way we allow
// to mock FxAccounts from tests.
_fxAccounts: fxAccounts,
// We keep the session details here so consumers don't need to deal with
// session tokens and are only required to handle the email.
_activeSession: null,
// We only expose the email and the verified status so far.
get _user() {
if (!this._activeSession || !this._activeSession.email) {
return null;
}
return {
accountId: this._activeSession.email,
verified: this._activeSession.verified
}
},
_getError: function(aServerResponse) {
if (!aServerResponse || !aServerResponse.error || !aServerResponse.error.errno) {
return;
}
let error = SERVER_ERRNO_TO_ERROR[aServerResponse.error.errno];
log.error(error);
return error;
},
_serverError: function(aServerResponse) {
let error = this._getError({ error: aServerResponse });
return Promise.reject({
error: error ? error : ERROR_SERVER_ERROR,
details: aServerResponse
});
},
// As we do with _fxAccounts, we don't really need this factory, but this way
// we allow tests to mock FxAccountsClient.
_createFxAccountsClient: function() {
return new FxAccountsClient();
},
_signInSignUp: function(aMethod, aAccountId, aPassword) {
if (Services.io.offline) {
log.error(ERROR_OFFLINE);
return Promise.reject({
error: ERROR_OFFLINE
});
}
if (!aAccountId) {
log.error(ERROR_INVALID_ACCOUNTID);
return Promise.reject({
error: ERROR_INVALID_ACCOUNTID
});
}
if (!aPassword) {
log.error(ERROR_INVALID_PASSWORD);
return Promise.reject({
error: ERROR_INVALID_PASSWORD
});
}
// Check that there is no signed in account first.
if (this._activeSession) {
log.error(ERROR_ALREADY_SIGNED_IN_USER);
return Promise.reject({
error: ERROR_ALREADY_SIGNED_IN_USER,
details: {
user: this._user
}
});
}
let client = this._createFxAccountsClient();
return this._fxAccounts.getSignedInUser().then(
user => {
if (user) {
log.error(ERROR_ALREADY_SIGNED_IN_USER);
return Promise.reject({
error: ERROR_ALREADY_SIGNED_IN_USER,
details: {
user: user
}
});
}
return client[aMethod](aAccountId, aPassword);
}
).then(
user => {
let error = this._getError(user);
if (!user || !user.uid || !user.sessionToken || error) {
log.error(error ? error : ERROR_INTERNAL_INVALID_USER);
return Promise.reject({
error: error ? error : ERROR_INTERNAL_INVALID_USER,
details: {
user: user
}
});
}
// Save the credentials of the signed in user.
user.email = aAccountId;
return this._fxAccounts.setSignedInUser(user, false).then(
() => {
this._activeSession = user;
log.debug("User signed in: " + JSON.stringify(this._user) +
" - Account created " + (aMethod == "signUp"));
return Promise.resolve({
accountCreated: aMethod === "signUp",
user: this._user
});
}
);
},
reason => { return this._serverError(reason); }
);
},
_getAssertion: function(aAudience) {
return this._fxAccounts.getAssertion(aAudience);
},
_signOut: function() {
if (!this._activeSession) {
return Promise.resolve();
}
return this._fxAccounts.signOut(this._activeSession.sessionToken).then(
() => {
// If there is no connection, removing the local session should be
// enough. The client can create new sessions up to the limit (100?).
// Orphaned tokens on the server will eventually be garbage collected.
if (Services.io.offline) {
this._activeSession = null;
return Promise.resolve();
}
// Otherwise, we try to remove the remote session.
let client = this._createFxAccountsClient();
return client.signOut(this._activeSession.sessionToken).then(
result => {
// Even if there is a remote server error, we remove the local
// session.
this._activeSession = null;
let error = this._getError(result);
if (error) {
return Promise.reject({
error: error,
details: result
});
}
log.debug("Signed out");
return Promise.resolve();
},
reason => {
// Even if there is a remote server error, we remove the local
// session.
this._activeSession = null;
return this._serverError(reason);
}
);
}
);
},
// -- API --
signIn: function(aAccountId, aPassword) {
return this._signInSignUp("signIn", aAccountId, aPassword);
},
signUp: function(aAccountId, aPassword) {
return this._signInSignUp("signUp", aAccountId, aPassword);
},
signOut: function() {
if (!this._activeSession) {
// If there is no cached active session, we try to get it from the
// account storage.
return this.getAccount().then(
result => {
if (!result) {
return Promise.resolve();
}
return this._signOut();
}
);
}
return this._signOut();
},
getAccount: function() {
// We check first if we have session details cached.
if (this._activeSession) {
// If our cache says that the account is not yet verified, we check that
// this information is correct, and update the cached data if not.
if (this._activeSession && !this._activeSession.verified &&
!Services.io.offline) {
return this.verificationStatus(this._activeSession);
}
log.debug("Account " + JSON.stringify(this._user));
return Promise.resolve(this._user);
}
// If no cached information, we try to get it from the persistent storage.
return this._fxAccounts.getSignedInUser().then(
user => {
if (!user || !user.email) {
log.debug("No signed in account");
return Promise.resolve(null);
}
this._activeSession = user;
// If we get a stored information of a not yet verified account,
// we check this information with the server, update the stored
// data if needed and finally return the account details.
if (!user.verified && !Services.io.offline) {
log.debug("Unverified account");
return this.verificationStatus(user);
}
log.debug("Account " + JSON.stringify(this._user));
return Promise.resolve(this._user);
}
);
},
queryAccount: function(aAccountId) {
log.debug("queryAccount " + aAccountId);
if (Services.io.offline) {
log.error(ERROR_OFFLINE);
return Promise.reject({
error: ERROR_OFFLINE
});
}
let deferred = Promise.defer();
if (!aAccountId) {
log.error(ERROR_INVALID_ACCOUNTID);
return Promise.reject({
error: ERROR_INVALID_ACCOUNTID
});
}
let client = this._createFxAccountsClient();
return client.accountExists(aAccountId).then(
result => {
log.debug("Account " + result ? "" : "does not" + " exists");
let error = this._getError(result);
if (error) {
return Promise.reject({
error: error,
details: result
});
}
return Promise.resolve({
registered: result
});
},
reason => { this._serverError(reason); }
);
},
verificationStatus: function() {
log.debug("verificationStatus");
if (!this._activeSession || !this._activeSession.sessionToken) {
log.error(ERROR_NO_TOKEN_SESSION);
return Promise.reject({
error: ERROR_NO_TOKEN_SESSION
});
}
// There is no way to unverify an already verified account, so we just
// return the account details of a verified account
if (this._activeSession.verified) {
log.debug("Account already verified");
return Promise.resolve(this._user);
}
if (Services.io.offline) {
log.error(ERROR_OFFLINE);
return Promise.reject({
error: ERROR_OFFLINE
});
}
let client = this._createFxAccountsClient();
return client.recoveryEmailStatus(this._activeSession.sessionToken).then(
data => {
let error = this._getError(data);
if (error) {
return Promise.reject({
error: error,
details: data
});
}
// If the verification status is different from the one that we have
// stored, we update it and return the session data. If not, we simply
// return the session data.
if (this._activeSession.verified != data.verified) {
this._activeSession.verified = data.verified;
return this._fxAccounts.setSignedInUser(this._activeSession).then(
() => {
log.debug(JSON.stringify(this._user));
return Promise.resolve(this._user);
}
);
}
log.debug(JSON.stringify(this._user));
return Promise.resolve(this._user);
},
reason => { return this._serverError(reason); }
);
},
getAssertion: function(aAudience) {
log.debug("getAssertion " + aAudience);
if (!aAudience) {
log.error(ERROR_INVALID_AUDIENCE);
return Promise.reject({
error: ERROR_INVALID_AUDIENCE
});
}
if (Services.io.offline) {
log.error(ERROR_OFFLINE);
return Promise.reject({
error: ERROR_OFFLINE
});
}
return this.getAccount().then(
user => {
if (user) {
// We cannot get assertions for unverified accounts.
if (user.verified) {
return this._getAssertion(aAudience);
}
log.error(ERROR_UNVERIFIED_ACCOUNT);
return Promise.reject({
error: ERROR_UNVERIFIED_ACCOUNT,
details: {
user: user
}
});
}
log.debug("No signed in user");
// If there is no currently signed in user, we trigger the signIn UI
// flow.
let ui = Cc["@mozilla.org/fxaccounts/fxaccounts-ui-glue;1"]
.createInstance(Ci.nsIFxAccountsUIGlue);
return ui.signInFlow().then(
result => {
// Even if we get a successful result from the UI, the account will
// most likely be unverified, so we cannot get an assertion.
if (result && result.verified) {
return this._getAssertion(aAudience);
}
log.error(ERROR_UNVERIFIED_ACCOUNT);
return Promise.reject({
error: ERROR_UNVERIFIED_ACCOUNT,
details: {
user: result
}
});
},
error => {
log.error(ERROR_UI_ERROR + " " + error);
return Promise.reject({
error: ERROR_UI_ERROR,
details: error
});
}
);
}
);
}
};

Просмотреть файл

@ -0,0 +1,11 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
XPIDL_SOURCES += [
'nsIFxAccountsUIGlue.idl'
]
XPIDL_MODULE = 'services_fxaccounts'

Просмотреть файл

@ -0,0 +1,12 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
[scriptable, uuid(5805ac8b-7cbe-4fbd-97ad-d3ae8cd29f79)]
interface nsIFxAccountsUIGlue : nsISupports
{
// Returns a Promise.
jsval signInFlow();
};

Просмотреть файл

@ -4,8 +4,16 @@
# 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/.
PARALLEL_DIRS += ['interfaces']
TEST_DIRS += ['tests']
EXTRA_JS_MODULES += [
'FxAccounts.jsm',
'FxAccountsClient.jsm'
'FxAccountsClient.jsm',
'FxAccountsCommon.js'
]
# For now, we will only be using the FxA manager in B2G.
if CONFIG['MOZ_B2G']:
EXTRA_JS_MODULES += ['FxAccountsManager.jsm']

Просмотреть файл

@ -0,0 +1,583 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const Cm = Components.manager;
Cu.import("resource://gre/modules/FxAccounts.jsm");
Cu.import("resource://gre/modules/FxAccountsCommon.js");
Cu.import("resource://gre/modules/FxAccountsManager.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
// === Mocks ===
// Override FxAccountsUIGlue.
const kFxAccountsUIGlueUUID = "{8f6d5d87-41ed-4bb5-aa28-625de57564c5}";
const kFxAccountsUIGlueContractID =
"@mozilla.org/fxaccounts/fxaccounts-ui-glue;1";
// Save original FxAccountsUIGlue factory.
const kFxAccountsUIGlueFactory =
Cm.getClassObject(Cc[kFxAccountsUIGlueContractID], Ci.nsIFactory);
let fakeFxAccountsUIGlueFactory = {
createInstance: function(aOuter, aIid) {
return FxAccountsUIGlue.QueryInterface(aIid);
}
};
// FxAccountsUIGlue fake component.
let FxAccountsUIGlue = {
_reject: false,
_error: 'error',
_signInFlowCalled: false,
_reset: function() {
this._reject = false;
this._error = 'error';
this._signInFlowCalled = false;
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFxAccountsUIGlue]),
getUserPermission: function() {},
signInFlow: function() {
this._signInFlowCalled = true;
let deferred = Promise.defer();
if (this._reject) {
deferred.reject(this._error);
} else {
FxAccountsManager._activeSession = {
email: "user@domain.org",
verified: false,
sessionToken: "1234"
};
FxAccountsManager._fxAccounts
.setSignedInUser(FxAccountsManager._activeSession);
deferred.resolve();
}
return deferred.promise;
}
};
(function registerFakeFxAccountsUIGlue() {
Cm.QueryInterface(Ci.nsIComponentRegistrar)
.registerFactory(Components.ID(kFxAccountsUIGlueUUID),
"FxAccountsUIGlue",
kFxAccountsUIGlueContractID,
fakeFxAccountsUIGlueFactory);
})();
// Save original fxAccounts instance
const kFxAccounts = fxAccounts;
// and change it for a mock FxAccounts.
FxAccountsManager._fxAccounts = {
_reject: false,
_getSignedInUserCalled: false,
_setSignedInUserCalled: false,
_error: 'error',
_assertion: 'assertion',
_signedInUser: null,
_reset: function() {
this._getSignedInUserCalled = false;
this._setSignedInUserCalled = false;
this._reject = false;
},
getAssertion: function() {
if (!this._signedInUser) {
return null;
}
let deferred = Promise.defer();
deferred.resolve(this._assertion);
return deferred.promise;
},
getSignedInUser: function() {
this._getSignedInUserCalled = true;
let deferred = Promise.defer();
this._reject ? deferred.reject(this._error)
: deferred.resolve(this._signedInUser);
return deferred.promise;
},
setSignedInUser: function(user) {
this._setSignedInUserCalled = true;
let deferred = Promise.defer();
this._signedInUser = user;
deferred.resolve();
return deferred.promise;
},
signOut: function() {
let deferred = Promise.defer();
this._signedInUser = null;
deferred.resolve();
return deferred.promise;
}
};
// Save original FxAccountsClient factory from FxAccountsManager.
const kFxAccountsClient = FxAccountsManager._createFxAccountsClient;
// and change it for a fake client factory.
let FakeFxAccountsClient = {
_reject: false,
_recoveryEmailStatusCalled: false,
_signInCalled: false,
_signUpCalled: false,
_signOutCalled: false,
_accountExists: false,
_verified: false,
_password: null,
_reset: function() {
this._reject = false;
this._recoveryEmailStatusCalled = false;
this._signInCalled = false;
this._signUpCalled = false;
this._signOutCalled = false;
},
recoveryEmailStatus: function() {
this._recoveryEmailStatusCalled = true;
let deferred = Promise.defer();
this._reject ? deferred.reject()
: deferred.resolve({ verified: this._verified });
return deferred.promise;
},
signIn: function(user, password) {
this._signInCalled = true;
this._password = password;
let deferred = Promise.defer();
this._reject ? deferred.reject()
: deferred.resolve({ email: user,
uid: "whatever",
verified: this._verified,
sessionToken: "1234" });
return deferred.promise;
},
signUp: function(user, password) {
this._signUpCalled = true;
return this.signIn(user, password);
},
signOut: function() {
this._signOutCalled = true;
let deferred = Promise.defer();
this._reject ? deferred.reject()
: deferred.resolve();
return deferred.promise;
},
accountExists: function() {
let deferred = Promise.defer();
this._reject ? deferred.reject()
: deferred.resolve(this._accountExists);
return deferred.promise;
}
};
FxAccountsManager._createFxAccountsClient = function() {
return FakeFxAccountsClient;
}
// === Global cleanup ===
// Unregister mocks and restore original code.
do_register_cleanup(function() {
// Unregister the factory so we do not leak
Cm.QueryInterface(Ci.nsIComponentRegistrar)
.unregisterFactory(Components.ID(kFxAccountsUIGlueUUID),
fakeFxAccountsUIGlueFactory);
// Restore the original factory.
Cm.QueryInterface(Ci.nsIComponentRegistrar)
.registerFactory(Components.ID(kFxAccountsUIGlueUUID),
"FxAccountsUIGlue",
kFxAccountsUIGlueContractID,
kFxAccountsUIGlueFactory);
// Restore the original FxAccounts instance from FxAccountsManager.
FxAccountsManager._fxAccounts = kFxAccounts;
// Restore the FxAccountsClient getter from FxAccountsManager.
FxAccountsManager._createFxAccountsClient = kFxAccountsClient;
});
// === Tests ===
function run_test() {
run_next_test();
}
add_test(function test_initial_state() {
do_print("= Test 0 | Initial state =");
do_check_neq(FxAccountsManager, undefined);
do_check_null(FxAccountsManager._activeSession);
do_check_null(FxAccountsManager._user);
run_next_test();
});
add_test(function(test_getAccount_no_session) {
do_print("= Test 1 | getAccount no session =");
FxAccountsManager.getAccount().then(
result => {
do_check_null(result);
do_check_null(FxAccountsManager._activeSession);
do_check_null(FxAccountsManager._user);
do_check_true(FxAccountsManager._fxAccounts._getSignedInUserCalled);
FxAccountsManager._fxAccounts._reset();
run_next_test();
},
error => {
do_throw("Unexpected error: " + error);
}
);
});
add_test(function(test_getAssertion_no_audience) {
do_print("= Test 2 | getAssertion no audience =");
FxAccountsManager.getAssertion().then(
() => {
do_throw("Unexpected success");
},
error => {
do_check_eq(error.error, ERROR_INVALID_AUDIENCE);
run_next_test();
}
);
});
add_test(function(test_getAssertion_no_session_ui_error) {
do_print("= Test 3 | getAssertion no session, UI error =");
FxAccountsUIGlue._reject = true;
FxAccountsManager.getAssertion("audience").then(
() => {
do_throw("Unexpected success");
},
error => {
do_check_eq(error.error, ERROR_UI_ERROR);
do_check_eq(error.details, "error");
FxAccountsUIGlue._reset();
run_next_test();
}
);
});
add_test(function(test_getAssertion_no_session_ui_success) {
do_print("= Test 4 | getAssertion no session, UI success =");
FxAccountsManager.getAssertion("audience").then(
() => {
do_throw("Unexpected success");
},
error => {
do_check_true(FxAccountsUIGlue._signInFlowCalled);
do_check_eq(error.error, ERROR_UNVERIFIED_ACCOUNT);
FxAccountsUIGlue._reset();
run_next_test();
}
);
});
add_test(function(test_getAssertion_active_session_unverified_account) {
do_print("= Test 5 | getAssertion active session, unverified account =");
FxAccountsManager.getAssertion("audience").then(
result => {
do_throw("Unexpected success");
},
error => {
do_check_false(FxAccountsUIGlue._signInFlowCalled);
do_check_eq(error.error, ERROR_UNVERIFIED_ACCOUNT);
run_next_test();
}
);
});
add_test(function(test_getAssertion_active_session_verified_account) {
do_print("= Test 6 | getAssertion active session, verified account =");
FxAccountsManager._fxAccounts._signedInUser.verified = true;
FxAccountsManager._activeSession.verified = true;
FxAccountsManager.getAssertion("audience").then(
result => {
do_check_false(FxAccountsUIGlue._signInFlowCalled);
do_check_eq(result, "assertion");
FxAccountsManager._fxAccounts._reset();
run_next_test();
},
error => {
do_throw("Unexpected error: " + error);
}
);
});
add_test(function(test_getAccount_existing_verified_session) {
do_print("= Test 7 | getAccount, existing verified session =");
FxAccountsManager.getAccount().then(
result => {
do_check_false(FxAccountsManager._fxAccounts._getSignedInUserCalled);
do_check_eq(result.accountId, FxAccountsManager._user.accountId);
do_check_eq(result.verified, FxAccountsManager._user.verified);
run_next_test();
},
error => {
do_throw("Unexpected error: " + error);
}
);
});
add_test(function(test_getAccount_existing_unverified_session_unverified_user) {
do_print("= Test 8 | getAccount, existing unverified session, unverified user =");
FxAccountsManager._activeSession.verified = false;
FxAccountsManager._fxAccounts._signedInUser.verified = false;
FxAccountsManager.getAccount().then(
result => {
do_check_true(FakeFxAccountsClient._recoveryEmailStatusCalled);
do_check_false(result.verified);
do_check_eq(result.accountId, FxAccountsManager._user.accountId);
FakeFxAccountsClient._reset();
run_next_test();
},
error => {
do_throw("Unexpected error: " + error);
}
);
});
add_test(function(test_getAccount_existing_unverified_session_verified_user) {
do_print("= Test 8 | getAccount, existing unverified session, verified user =");
FxAccountsManager._activeSession.verified = false;
FxAccountsManager._fxAccounts._signedInUser.verified = false;
FakeFxAccountsClient._verified = true;
FxAccountsManager.getAccount().then(
result => {
do_check_true(FakeFxAccountsClient._recoveryEmailStatusCalled);
do_check_true(result.verified);
do_check_eq(result.accountId, FxAccountsManager._user.accountId);
FakeFxAccountsClient._reset();
run_next_test();
},
error => {
do_throw("Unexpected error: " + error);
}
);
});
add_test(function(test_signOut) {
do_print("= Test 9 | signOut =");
do_check_true(FxAccountsManager._activeSession != null);
FxAccountsManager.signOut().then(
result => {
do_check_null(result);
do_check_null(FxAccountsManager._activeSession);
do_check_true(FakeFxAccountsClient._signOutCalled);
run_next_test();
},
error => {
do_throw("Unexpected error: " + error);
}
);
});
add_test(function(test_verificationStatus_no_token_session) {
do_print("= Test 10 | verificationStatus, no token session =");
do_check_null(FxAccountsManager._activeSession);
FxAccountsManager.verificationStatus().then(
() => {
do_throw("Unexpected success");
},
error => {
do_check_eq(error.error, ERROR_NO_TOKEN_SESSION);
run_next_test();
}
);
});
add_test(function(test_signUp_no_accountId) {
do_print("= Test 11 | signUp, no accountId=");
FxAccountsManager.signUp().then(
() => {
do_throw("Unexpected success");
},
error => {
do_check_eq(error.error, ERROR_INVALID_ACCOUNTID);
run_next_test();
}
);
});
add_test(function(test_signIn_no_accountId) {
do_print("= Test 12 | signIn, no accountId=");
FxAccountsManager.signIn().then(
() => {
do_throw("Unexpected success");
},
error => {
do_check_eq(error.error, ERROR_INVALID_ACCOUNTID);
run_next_test();
}
);
});
add_test(function(test_signUp_no_password) {
do_print("= Test 13 | signUp, no accountId=");
FxAccountsManager.signUp("user@domain.org").then(
() => {
do_throw("Unexpected success");
},
error => {
do_check_eq(error.error, ERROR_INVALID_PASSWORD);
run_next_test();
}
);
});
add_test(function(test_signIn_no_accountId) {
do_print("= Test 14 | signIn, no accountId=");
FxAccountsManager.signIn("user@domain.org").then(
() => {
do_throw("Unexpected success");
},
error => {
do_check_eq(error.error, ERROR_INVALID_PASSWORD);
run_next_test();
}
);
});
add_test(function(test_signUp) {
do_print("= Test 15 | signUp =");
FakeFxAccountsClient._verified = false;
FxAccountsManager.signUp("user@domain.org", "password").then(
result => {
do_check_true(FakeFxAccountsClient._signInCalled);
do_check_true(FakeFxAccountsClient._signUpCalled);
do_check_true(FxAccountsManager._fxAccounts._getSignedInUserCalled);
do_check_eq(FxAccountsManager._fxAccounts._signedInUser.email, "user@domain.org");
do_check_eq(FakeFxAccountsClient._password, "password");
do_check_true(result.accountCreated);
do_check_eq(result.user.accountId, "user@domain.org");
do_check_false(result.user.verified);
FakeFxAccountsClient._reset();
FxAccountsManager._fxAccounts._reset();
run_next_test();
},
error => {
do_throw("Unexpected error: " + error.error);
}
);
});
add_test(function(test_signUp_already_signed_user) {
do_print("= Test 16 | signUp, already signed user =");
FxAccountsManager.signUp("user@domain.org", "password").then(
() => {
do_throw("Unexpected success");
},
error => {
do_check_false(FakeFxAccountsClient._signInCalled);
do_check_eq(error.error, ERROR_ALREADY_SIGNED_IN_USER);
do_check_eq(error.details.user.accountId, "user@domain.org");
do_check_false(error.details.user.verified);
run_next_test();
}
);
});
add_test(function(test_signIn_already_signed_user) {
do_print("= Test 17 | signIn, already signed user =");
FxAccountsManager.signIn("user@domain.org", "password").then(
() => {
do_throw("Unexpected success");
},
error => {
do_check_eq(error.error, ERROR_ALREADY_SIGNED_IN_USER);
do_check_eq(error.details.user.accountId, "user@domain.org");
do_check_false(error.details.user.verified);
run_next_test();
}
);
});
add_test(function(test_verificationStatus_unverified_session_unverified_user) {
do_print("= Test 18 | verificationStatus unverified session and user =");
FakeFxAccountsClient._verified = false;
FxAccountsManager.verificationStatus().then(
user => {
do_check_false(user.verified);
do_check_true(FakeFxAccountsClient._recoveryEmailStatusCalled);
do_check_false(FxAccountsManager._fxAccounts._setSignedInUserCalled);
run_next_test();
},
error => {
do_throw("Unexpected error: " + error);
}
);
});
add_test(function(test_verificationStatus_unverified_session_verified_user) {
do_print("= Test 19 | verificationStatus unverified session, verified user =");
FakeFxAccountsClient._verified = true;
FxAccountsManager.verificationStatus().then(
user => {
do_check_true(user.verified);
do_check_true(FakeFxAccountsClient._recoveryEmailStatusCalled);
do_check_true(FxAccountsManager._fxAccounts._setSignedInUserCalled);
run_next_test();
},
error => {
do_throw("Unexpected error: " + error);
}
);
});
add_test(function(test_queryAccount_no_exists) {
do_print("= Test 20 | queryAccount, no exists =");
FxAccountsManager.queryAccount("user@domain.org").then(
result => {
do_check_false(result.registered);
run_next_test();
},
error => {
do_throw("Unexpected error: " + error);
}
);
});
add_test(function(test_queryAccount_exists) {
do_print("= Test 21 | queryAccount, exists =");
FakeFxAccountsClient._accountExists = true;
FxAccountsManager.queryAccount("user@domain.org").then(
result => {
do_check_true(result.registered);
run_next_test();
},
error => {
do_throw("Unexpected error: " + error);
}
);
});
add_test(function(test_queryAccount_no_accountId) {
do_print("= Test 22 | queryAccount, no accountId =");
FxAccountsManager.queryAccount().then(
() => {
do_throw("Unexpected success");
},
error => {
do_check_eq(error.error, ERROR_INVALID_ACCOUNTID);
run_next_test();
}
);
});

Просмотреть файл

@ -4,4 +4,6 @@ tail =
[test_accounts.js]
[test_client.js]
[test_manager.js]
run-if = appname == 'b2g'
reason = FxAccountsManager is only available for B2G for now

Просмотреть файл

@ -15,6 +15,7 @@ skip = false
[include:unit/unit-tests.ini]
; webapi tests
[include:../../../../../dom/bluetooth/tests/marionette/manifest.ini]
[include:../../../../../dom/telephony/test/marionette/manifest.ini]
[include:../../../../../dom/voicemail/test/marionette/manifest.ini]
[include:../../../../../dom/battery/test/marionette/manifest.ini]

Просмотреть файл

@ -574,6 +574,22 @@ ProcessDDE(nsINativeAppSupport* aNative, bool aWait)
}
#endif
/**
* Determines if there is support for showing the profile manager
*
* @return true in all environments except for Windows Metro
*/
static bool
CanShowProfileManager()
{
#if defined(XP_WIN)
return XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop;
#else
return true;
#endif
}
bool gSafeMode = false;
/**
@ -1893,6 +1909,10 @@ static nsresult
ShowProfileManager(nsIToolkitProfileService* aProfileSvc,
nsINativeAppSupport* aNative)
{
if (!CanShowProfileManager()) {
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult rv;
nsCOMPtr<nsIFile> profD, profLD;
@ -2243,7 +2263,10 @@ SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, n
PR_fprintf(PR_STDERR, "Error: argument -p is invalid when argument -osint is specified\n");
return NS_ERROR_FAILURE;
}
return ShowProfileManager(aProfileSvc, aNative);
if (CanShowProfileManager()) {
return ShowProfileManager(aProfileSvc, aNative);
}
}
if (ar) {
ar = CheckArg("osint");
@ -2271,14 +2294,16 @@ SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, n
return ProfileLockedDialog(profile, unlocker, aNative, aResult);
}
return ShowProfileManager(aProfileSvc, aNative);
if (CanShowProfileManager()) {
return ShowProfileManager(aProfileSvc, aNative);
}
}
ar = CheckArg("profilemanager", true);
if (ar == ARG_BAD) {
PR_fprintf(PR_STDERR, "Error: argument -profilemanager is invalid when argument -osint is specified\n");
return NS_ERROR_FAILURE;
} else if (ar == ARG_FOUND) {
} else if (ar == ARG_FOUND && CanShowProfileManager()) {
return ShowProfileManager(aProfileSvc, aNative);
}
@ -2303,8 +2328,9 @@ SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, n
}
bool useDefault = true;
if (count > 1)
if (count > 1 && CanShowProfileManager()) {
aProfileSvc->GetStartWithLastProfile(&useDefault);
}
if (useDefault) {
nsCOMPtr<nsIToolkitProfile> profile;
@ -2359,6 +2385,10 @@ SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, n
}
}
if (!CanShowProfileManager()) {
return NS_ERROR_FAILURE;
}
return ShowProfileManager(aProfileSvc, aNative);
}
@ -4072,12 +4102,6 @@ XREMain::XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
// We have an application restart don't do any shutdown checks here
// In particular we don't want to poison IO for checking late-writes.
gShutdownChecks = SCM_NOTHING;
#if defined(MOZ_METRO) && defined(XP_WIN)
if (rv == NS_SUCCESS_RESTART_METRO_APP) {
LaunchDefaultMetroBrowser();
}
#endif
}
if (!mShuttingDown) {
@ -4114,7 +4138,15 @@ XREMain::XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
MOZ_gdk_display_close(mGdkDisplay);
#endif
rv = LaunchChild(mNativeApp, true);
#if defined(MOZ_METRO) && defined(XP_WIN)
if (rv == NS_SUCCESS_RESTART_METRO_APP) {
LaunchDefaultMetroBrowser();
rv = NS_OK;
} else
#endif
{
rv = LaunchChild(mNativeApp, true);
}
#ifdef MOZ_CRASHREPORTER
if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER)

Просмотреть файл

@ -113,8 +113,8 @@ HRESULT SHCreateShellItemArrayFromShellItemDynamic(IShellItem *psi, REFIID riid,
return hr;
}
BOOL
WinLaunchDeferredMetroFirefox(bool aInMetro)
HRESULT
WinLaunchDeferredMetroFirefox()
{
// Create an instance of the Firefox Metro DEH which is used to launch the browser
const CLSID CLSID_FirefoxMetroDEH = {0x5100FEC1,0x212B, 0x4BF5 ,{0x9B,0xF8, 0x3E,0x65, 0x0F,0xD7,0x94,0xA3}};
@ -126,51 +126,46 @@ WinLaunchDeferredMetroFirefox(bool aInMetro)
IID_IExecuteCommand,
getter_AddRefs(executeCommand));
if (FAILED(hr))
return FALSE;
return hr;
// Get the currently running exe path
WCHAR exePath[MAX_PATH + 1] = { L'\0' };
if (!::GetModuleFileNameW(0, exePath, MAX_PATH))
return FALSE;
return hr;
// Convert the path to a long path since GetModuleFileNameW returns the path
// that was used to launch Firefox which is not necessarily a long path.
if (!::GetLongPathNameW(exePath, exePath, MAX_PATH))
return FALSE;
return hr;
// Create an IShellItem for the current browser path
nsRefPtr<IShellItem> shellItem;
hr = WinUtils::SHCreateItemFromParsingName(exePath, nullptr, IID_IShellItem,
getter_AddRefs(shellItem));
if (FAILED(hr))
return FALSE;
return hr;
// Convert to an IShellItemArray which is used for the path to launch
nsRefPtr<IShellItemArray> shellItemArray;
hr = SHCreateShellItemArrayFromShellItemDynamic(shellItem, IID_IShellItemArray, getter_AddRefs(shellItemArray));
if (FAILED(hr))
return FALSE;
return hr;
// Set the path to launch and parameters needed
nsRefPtr<IObjectWithSelection> selection;
hr = executeCommand->QueryInterface(IID_IObjectWithSelection, getter_AddRefs(selection));
if (FAILED(hr))
return FALSE;
return hr;
hr = selection->SetSelection(shellItemArray);
if (FAILED(hr))
return FALSE;
return hr;
if (aInMetro) {
hr = executeCommand->SetParameters(L"--metro-restart");
} else {
hr = executeCommand->SetParameters(L"--desktop-restart");
}
hr = executeCommand->SetParameters(L"--metro-restart");
if (FAILED(hr))
return FALSE;
return hr;
// Run the default browser through the DEH
hr = executeCommand->Execute();
return SUCCEEDED(hr);
return executeCommand->Execute();
}
// Called by appstartup->run in xre, which is initiated by a call to
@ -200,22 +195,24 @@ MetroAppShell::Run(void)
mozilla::widget::StopAudioSession();
nsCOMPtr<nsIAppStartup> appStartup (do_GetService(NS_APPSTARTUP_CONTRACTID));
bool restartingInMetro = false, restarting = false;
bool restartingInMetro = false, restartingInDesktop = false;
if (appStartup && NS_SUCCEEDED(appStartup->GetRestartingTouchEnvironment(&restartingInMetro)) &&
restartingInMetro) {
WinLaunchDeferredMetroFirefox(true);
}
if (!appStartup || NS_FAILED(appStartup->GetRestarting(&restarting))) {
if (!appStartup || NS_FAILED(appStartup->GetRestarting(&restartingInDesktop))) {
WinUtils::Log("appStartup->GetRestarting() unsuccessful");
}
// This calls XRE_metroShutdown() in xre. This will also destroy
// MessagePump.
if (appStartup && NS_SUCCEEDED(appStartup->GetRestartingTouchEnvironment(&restartingInMetro)) &&
restartingInMetro) {
restartingInDesktop = false;
}
// This calls XRE_metroShutdown() in xre. Shuts down gecko, including
// releasing the profile, and destroys MessagePump.
sMetroApp->ShutdownXPCOM();
if (restarting) {
// Handle update restart or browser switch requests
if (restartingInDesktop) {
WinUtils::Log("Relaunching desktop browser");
SHELLEXECUTEINFOW sinfo;
memset(&sinfo, 0, sizeof(SHELLEXECUTEINFOW));
sinfo.cbSize = sizeof(SHELLEXECUTEINFOW);
@ -228,6 +225,9 @@ MetroAppShell::Run(void)
sinfo.lpParameters = L"--desktop-restart";
sinfo.nShow = SW_SHOWNORMAL;
ShellExecuteEx(&sinfo);
} else if (restartingInMetro) {
HRESULT hresult = WinLaunchDeferredMetroFirefox();
WinUtils::Log("Relaunching metro browser (hr=%X)", hresult);
}
// This will free the real main thread in CoreApplication::Run()

Просмотреть файл

@ -78,8 +78,8 @@ MaybeAlignAndClampDisplayPort(mozilla::layers::FrameMetrics& aFrameMetrics,
- aActualScrollOffset;
}
// Finally, clamp the display port to the scrollable rect.
CSSRect scrollableRect = aFrameMetrics.mScrollableRect;
// Finally, clamp the display port to the expanded scrollable rect.
CSSRect scrollableRect = aFrameMetrics.GetExpandedScrollableRect();
displayPort = scrollableRect.Intersect(displayPort + aActualScrollOffset)
- aActualScrollOffset;
}

Просмотреть файл

@ -11,9 +11,11 @@
#include "nsIServiceManager.h"
#include "nsDebug.h"
#include "nsCOMArray.h"
#include "nsXULAppAPI.h"
#include "prinrval.h"
#include "prlog.h"
#include "prtime.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/Services.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
@ -397,8 +399,10 @@ nsIdleService::nsIdleService() : mCurrentlySetToTimeoutAt(TimeStamp()),
#endif
MOZ_ASSERT(!gIdleService);
gIdleService = this;
mDailyIdle = new nsIdleServiceDaily(this);
mDailyIdle->Init();
if (XRE_GetProcessType() == GeckoProcessType_Default) {
mDailyIdle = new nsIdleServiceDaily(this);
mDailyIdle->Init();
}
}
nsIdleService::~nsIdleService()
@ -417,20 +421,26 @@ NS_IMPL_ISUPPORTS2(nsIdleService, nsIIdleService, nsIIdleServiceInternal)
NS_IMETHODIMP
nsIdleService::AddIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS)
{
NS_ENSURE_ARG_POINTER(aObserver);
// We don't accept idle time at 0, and we can't handle idle time that are too
// high either - no more than ~136 years.
NS_ENSURE_ARG_RANGE(aIdleTimeInS, 1, (UINT32_MAX / 10) - 1);
if (XRE_GetProcessType() == GeckoProcessType_Content) {
dom::ContentChild* cpc = dom::ContentChild::GetSingleton();
cpc->AddIdleObserver(aObserver, aIdleTimeInS);
return NS_OK;
}
PR_LOG(sLog, PR_LOG_DEBUG,
("idleService: Register idle observer %x for %d seconds",
aObserver, aIdleTimeInS));
("idleService: Register idle observer %x for %d seconds",
aObserver, aIdleTimeInS));
#ifdef MOZ_WIDGET_ANDROID
__android_log_print(ANDROID_LOG_INFO, "IdleService",
"Register idle observer %x for %d seconds",
aObserver, aIdleTimeInS);
#endif
NS_ENSURE_ARG_POINTER(aObserver);
// We don't accept idle time at 0, and we can't handle idle time that are too
// high either - no more than ~136 years.
NS_ENSURE_ARG_RANGE(aIdleTimeInS, 1, (UINT32_MAX / 10) - 1);
// Put the time + observer in a struct we can keep:
IdleListener listener(aObserver, aIdleTimeInS);
@ -474,6 +484,13 @@ nsIdleService::RemoveIdleObserver(nsIObserver* aObserver, uint32_t aTimeInS)
NS_ENSURE_ARG_POINTER(aObserver);
NS_ENSURE_ARG(aTimeInS);
if (XRE_GetProcessType() == GeckoProcessType_Content) {
dom::ContentChild* cpc = dom::ContentChild::GetSingleton();
cpc->RemoveIdleObserver(aObserver, aTimeInS);
return NS_OK;
}
IdleListener listener(aObserver, aTimeInS);
// Find the entry and remove it, if it was the last entry, we just let the