зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to inbound.
This commit is contained in:
Коммит
cd364e9987
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче