Merge m-c to m-i
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="80bc1445959db79e9d2e947cc56e1eb7b0d3d0f0"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1fff49c664f905f11a86426a9835e6df6b58e825"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80bc1445959db79e9d2e947cc56e1eb7b0d3d0f0"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1fff49c664f905f11a86426a9835e6df6b58e825"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="80bc1445959db79e9d2e947cc56e1eb7b0d3d0f0"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1fff49c664f905f11a86426a9835e6df6b58e825"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="80bc1445959db79e9d2e947cc56e1eb7b0d3d0f0"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1fff49c664f905f11a86426a9835e6df6b58e825"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80bc1445959db79e9d2e947cc56e1eb7b0d3d0f0"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1fff49c664f905f11a86426a9835e6df6b58e825"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="80bc1445959db79e9d2e947cc56e1eb7b0d3d0f0"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1fff49c664f905f11a86426a9835e6df6b58e825"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="80bc1445959db79e9d2e947cc56e1eb7b0d3d0f0"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1fff49c664f905f11a86426a9835e6df6b58e825"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "78d735b50d94254ff236fc34a6fbaa5ed27692a0",
|
||||
"revision": "415520315b048f40979e9bac344bec99e18df901",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80bc1445959db79e9d2e947cc56e1eb7b0d3d0f0"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1fff49c664f905f11a86426a9835e6df6b58e825"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80bc1445959db79e9d2e947cc56e1eb7b0d3d0f0"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1fff49c664f905f11a86426a9835e6df6b58e825"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="80bc1445959db79e9d2e947cc56e1eb7b0d3d0f0"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1fff49c664f905f11a86426a9835e6df6b58e825"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80bc1445959db79e9d2e947cc56e1eb7b0d3d0f0"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1fff49c664f905f11a86426a9835e6df6b58e825"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -20,22 +20,52 @@ XPCOMUtils.defineLazyModuleGetter(this, "PanelFrame", "resource:///modules/Panel
|
|||
/**
|
||||
* Opens the panel for Loop and sizes it appropriately.
|
||||
*
|
||||
* @param {event} event The event opening the panel, used to anchor
|
||||
* the panel to the button which triggers it.
|
||||
* @param {event} event The event opening the panel, used to anchor
|
||||
* the panel to the button which triggers it.
|
||||
* @param {String} [tabId] Identifier of the tab to select when the panel is
|
||||
* opened. Example: 'rooms', 'contacts', etc.
|
||||
*/
|
||||
openCallPanel: function(event) {
|
||||
openCallPanel: function(event, tabId = null) {
|
||||
let callback = iframe => {
|
||||
// Helper function to show a specific tab view in the panel.
|
||||
function showTab() {
|
||||
if (!tabId) {
|
||||
return;
|
||||
}
|
||||
|
||||
let win = iframe.contentWindow;
|
||||
let ev = new win.CustomEvent("UIAction", Cu.cloneInto({
|
||||
detail: {
|
||||
action: "selectTab",
|
||||
tab: tabId
|
||||
}
|
||||
}, win));
|
||||
win.dispatchEvent(ev);
|
||||
}
|
||||
|
||||
// If the panel has been opened and initialized before, we can skip waiting
|
||||
// for the content to load - because it's already there.
|
||||
if (("contentWindow" in iframe) && iframe.contentWindow.document.readyState == "complete") {
|
||||
showTab();
|
||||
return;
|
||||
}
|
||||
|
||||
iframe.addEventListener("DOMContentLoaded", function documentDOMLoaded() {
|
||||
iframe.removeEventListener("DOMContentLoaded", documentDOMLoaded, true);
|
||||
injectLoopAPI(iframe.contentWindow);
|
||||
iframe.contentWindow.addEventListener("loopPanelInitialized", function loopPanelInitialized() {
|
||||
iframe.contentWindow.removeEventListener("loopPanelInitialized",
|
||||
loopPanelInitialized);
|
||||
showTab();
|
||||
});
|
||||
}, true);
|
||||
};
|
||||
|
||||
// Used to clear the temporary "login" state from the button.
|
||||
Services.obs.notifyObservers(null, "loop-status-changed", null);
|
||||
|
||||
PanelFrame.showPopup(window, event.target, "loop", null,
|
||||
"about:looppanel", null, callback);
|
||||
PanelFrame.showPopup(window, event ? event.target : this.toolbarButton.node,
|
||||
"loop", null, "about:looppanel", null, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -89,6 +119,67 @@ XPCOMUtils.defineLazyModuleGetter(this, "PanelFrame", "resource:///modules/Panel
|
|||
this.toolbarButton.node.setAttribute("state", state);
|
||||
},
|
||||
|
||||
/**
|
||||
* Show a desktop notification when 'do not disturb' isn't enabled.
|
||||
*
|
||||
* @param {Object} options Set of options that may tweak the appearance and
|
||||
* behavior of the notification.
|
||||
* Option params:
|
||||
* - {String} title Notification title message
|
||||
* - {String} [message] Notification body text
|
||||
* - {String} [icon] Notification icon
|
||||
* - {String} [sound] Sound to play
|
||||
* - {String} [selectTab] Tab to select when the panel
|
||||
* opens
|
||||
* - {Function} [onclick] Callback to invoke when
|
||||
* the notification is clicked.
|
||||
* Opens the panel by default.
|
||||
*/
|
||||
showNotification: function(options) {
|
||||
if (MozLoopService.doNotDisturb) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!options.title) {
|
||||
throw new Error("Missing title, can not display notification");
|
||||
}
|
||||
|
||||
let notificationOptions = {
|
||||
body: options.message || ""
|
||||
};
|
||||
if (options.icon) {
|
||||
notificationOptions.icon = options.icon;
|
||||
}
|
||||
if (options.sound) {
|
||||
// This will not do anything, until bug bug 1105222 is resolved.
|
||||
notificationOptions.mozbehavior = {
|
||||
soundFile: `chrome://browser/content/loop/shared/sounds/${options.sound}.ogg`
|
||||
};
|
||||
}
|
||||
|
||||
let notification = new window.Notification(options.title, notificationOptions);
|
||||
notification.addEventListener("click", e => {
|
||||
if (window.closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
window.focus();
|
||||
} catch (ex) {}
|
||||
|
||||
// We need a setTimeout here, otherwise the panel won't show after the
|
||||
// window received focus.
|
||||
window.setTimeout(() => {
|
||||
if (typeof options.onclick == "function") {
|
||||
options.onclick();
|
||||
} else {
|
||||
// Open the Loop panel as a default action.
|
||||
this.openCallPanel(null, options.selectTab || null);
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Play a sound in this window IF there's no sound playing yet.
|
||||
*
|
||||
|
|
|
@ -5,7 +5,7 @@ let originalTab;
|
|||
let newTab;
|
||||
|
||||
function isCurrent(tab, msg) {
|
||||
const tolerance = 1;
|
||||
const tolerance = 5;
|
||||
const difference = Math.abs(Date.now() - tab.lastAccessed);
|
||||
ok(difference <= tolerance, msg + " (difference: " + difference + ")");
|
||||
}
|
||||
|
@ -14,20 +14,20 @@ function test() {
|
|||
waitForExplicitFinish();
|
||||
|
||||
originalTab = gBrowser.selectedTab;
|
||||
setTimeout(step2, 100);
|
||||
setTimeout(step2, 10);
|
||||
}
|
||||
|
||||
function step2() {
|
||||
isCurrent(originalTab, "selected tab has the current timestamp");
|
||||
newTab = gBrowser.addTab("about:blank", {skipAnimation: true});
|
||||
setTimeout(step3, 100);
|
||||
setTimeout(step3, 10);
|
||||
}
|
||||
|
||||
function step3() {
|
||||
ok(newTab.lastAccessed < Date.now(), "new tab hasn't been selected so far");
|
||||
gBrowser.selectedTab = newTab;
|
||||
isCurrent(newTab, "new tab has the current timestamp after being selected");
|
||||
setTimeout(step4, 100);
|
||||
setTimeout(step4, 10);
|
||||
}
|
||||
|
||||
function step4() {
|
||||
|
|
|
@ -93,7 +93,7 @@ const checkForParticipantsUpdate = function(room, updatedRoom) {
|
|||
// Check for participants that joined.
|
||||
for (participant of updatedRoom.participants) {
|
||||
if (!containsParticipant(room, participant)) {
|
||||
eventEmitter.emit("joined", room.roomToken, participant);
|
||||
eventEmitter.emit("joined", room, participant);
|
||||
eventEmitter.emit("joined:" + room.roomToken, participant);
|
||||
}
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ const checkForParticipantsUpdate = function(room, updatedRoom) {
|
|||
// Check for participants that left.
|
||||
for (participant of room.participants) {
|
||||
if (!containsParticipant(updatedRoom, participant)) {
|
||||
eventEmitter.emit("left", room.roomToken, participant);
|
||||
eventEmitter.emit("left", room, participant);
|
||||
eventEmitter.emit("left:" + room.roomToken, participant);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ const cloneErrorObject = function(error, targetWindow) {
|
|||
if (typeof value != "string" && typeof value != "number") {
|
||||
value = String(value);
|
||||
}
|
||||
|
||||
|
||||
Object.defineProperty(Cu.waiveXrays(obj), prop, {
|
||||
configurable: false,
|
||||
enumerable: true,
|
||||
|
@ -115,6 +115,8 @@ const injectObjectAPI = function(api, targetWindow) {
|
|||
injectedAPI[func] = function(...params) {
|
||||
let lastParam = params.pop();
|
||||
let callbackIsFunction = (typeof lastParam == "function");
|
||||
// Clone params coming from content to the current scope.
|
||||
params = [cloneValueInto(p, api) for (p of params)];
|
||||
|
||||
// If the last parameter is a function, assume its a callback
|
||||
// and wrap it differently.
|
||||
|
@ -134,6 +136,7 @@ const injectObjectAPI = function(api, targetWindow) {
|
|||
});
|
||||
} else {
|
||||
try {
|
||||
lastParam = cloneValueInto(lastParam, api);
|
||||
return cloneValueInto(api[func](...params, lastParam), targetWindow);
|
||||
} catch (ex) {
|
||||
return cloneValueInto(ex, targetWindow);
|
||||
|
|
|
@ -1030,7 +1030,7 @@ this.MozLoopService = {
|
|||
};
|
||||
LoopRooms.on("add", onRoomsChange);
|
||||
LoopRooms.on("update", onRoomsChange);
|
||||
LoopRooms.on("joined", (e, roomToken, participant) => {
|
||||
LoopRooms.on("joined", (e, room, participant) => {
|
||||
// Don't alert if we're in the doNotDisturb mode, or the participant
|
||||
// is the owner - the content code deals with the rest of the sounds.
|
||||
if (MozLoopServiceInternal.doNotDisturb || participant.owner) {
|
||||
|
@ -1039,7 +1039,12 @@ this.MozLoopService = {
|
|||
|
||||
let window = gWM.getMostRecentWindow("navigator:browser");
|
||||
if (window) {
|
||||
window.LoopUI.playSound("room-joined");
|
||||
window.LoopUI.showNotification({
|
||||
sound: "room-joined",
|
||||
title: room.roomName,
|
||||
message: MozLoopServiceInternal.localizedStrings.get("rooms_room_joined_label"),
|
||||
selectTab: "rooms"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -759,6 +759,17 @@ loop.panel = (function(_, mozL10n) {
|
|||
});
|
||||
},
|
||||
|
||||
_UIActionHandler: function(e) {
|
||||
switch (e.detail.action) {
|
||||
case "selectTab":
|
||||
this.selectTab(e.detail.tab);
|
||||
break;
|
||||
default:
|
||||
console.error("Invalid action", e.detail.action);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The rooms feature is hidden by default for now. Once it gets mainstream,
|
||||
* this method can be simplified.
|
||||
|
@ -803,11 +814,13 @@ loop.panel = (function(_, mozL10n) {
|
|||
componentDidMount: function() {
|
||||
window.addEventListener("LoopStatusChanged", this._onStatusChanged);
|
||||
window.addEventListener("GettingStartedSeen", this._gettingStartedSeen);
|
||||
window.addEventListener("UIAction", this._UIActionHandler);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
window.removeEventListener("LoopStatusChanged", this._onStatusChanged);
|
||||
window.removeEventListener("GettingStartedSeen", this._gettingStartedSeen);
|
||||
window.removeEventListener("UIAction", this._UIActionHandler);
|
||||
},
|
||||
|
||||
_getUserDisplayName: function() {
|
||||
|
|
|
@ -759,6 +759,17 @@ loop.panel = (function(_, mozL10n) {
|
|||
});
|
||||
},
|
||||
|
||||
_UIActionHandler: function(e) {
|
||||
switch (e.detail.action) {
|
||||
case "selectTab":
|
||||
this.selectTab(e.detail.tab);
|
||||
break;
|
||||
default:
|
||||
console.error("Invalid action", e.detail.action);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The rooms feature is hidden by default for now. Once it gets mainstream,
|
||||
* this method can be simplified.
|
||||
|
@ -803,11 +814,13 @@ loop.panel = (function(_, mozL10n) {
|
|||
componentDidMount: function() {
|
||||
window.addEventListener("LoopStatusChanged", this._onStatusChanged);
|
||||
window.addEventListener("GettingStartedSeen", this._gettingStartedSeen);
|
||||
window.addEventListener("UIAction", this._UIActionHandler);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
window.removeEventListener("LoopStatusChanged", this._onStatusChanged);
|
||||
window.removeEventListener("GettingStartedSeen", this._gettingStartedSeen);
|
||||
window.removeEventListener("UIAction", this._UIActionHandler);
|
||||
},
|
||||
|
||||
_getUserDisplayName: function() {
|
||||
|
|
|
@ -239,11 +239,16 @@ loop.store = loop.store || {};
|
|||
expiresIn: this.defaultExpiresIn
|
||||
};
|
||||
|
||||
this._mozLoop.rooms.create(roomCreationData, function(err) {
|
||||
this._mozLoop.rooms.create(roomCreationData, function(err, createdRoom) {
|
||||
this.setStoreState({pendingCreation: false});
|
||||
if (err) {
|
||||
this.dispatchAction(new sharedActions.CreateRoomError({error: err}));
|
||||
return;
|
||||
}
|
||||
// Opens the newly created room
|
||||
this.dispatchAction(new sharedActions.OpenRoom({
|
||||
roomToken: createdRoom.roomToken
|
||||
}));
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
|
|
|
@ -80,6 +80,7 @@ describe("loop.store.RoomStore", function () {
|
|||
rooms: {
|
||||
create: function() {},
|
||||
getAll: function() {},
|
||||
open: function() {},
|
||||
on: sandbox.stub()
|
||||
}
|
||||
};
|
||||
|
@ -230,13 +231,28 @@ describe("loop.store.RoomStore", function () {
|
|||
it("should switch the pendingCreation state flag to false once the " +
|
||||
"operation is done", function() {
|
||||
sandbox.stub(fakeMozLoop.rooms, "create", function(data, cb) {
|
||||
cb();
|
||||
cb(null, {roomToken: "fakeToken"});
|
||||
});
|
||||
|
||||
store.createRoom(new sharedActions.CreateRoom(fakeRoomCreationData));
|
||||
|
||||
expect(store.getStoreState().pendingCreation).eql(false);
|
||||
});
|
||||
|
||||
it("should dispatch an OpenRoom action once the operation is done",
|
||||
function() {
|
||||
var dispatch = sandbox.stub(dispatcher, "dispatch");
|
||||
sandbox.stub(fakeMozLoop.rooms, "create", function(data, cb) {
|
||||
cb(null, {roomToken: "fakeToken"});
|
||||
});
|
||||
|
||||
store.createRoom(new sharedActions.CreateRoom(fakeRoomCreationData));
|
||||
|
||||
sinon.assert.calledOnce(dispatch);
|
||||
sinon.assert.calledWithExactly(dispatch, new sharedActions.OpenRoom({
|
||||
roomToken: "fakeToken"
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("#copyRoomUrl", function() {
|
||||
|
|
|
@ -146,25 +146,25 @@ const onRoomDeleted = function(e, room) {
|
|||
gExpectedDeletes.splice(idx, 1);
|
||||
}
|
||||
|
||||
const onRoomJoined = function(e, roomToken, participant) {
|
||||
let participants = gExpectedJoins[roomToken];
|
||||
const onRoomJoined = function(e, room, participant) {
|
||||
let participants = gExpectedJoins[room.roomToken];
|
||||
Assert.ok(participants, "Participant should be expected to join");
|
||||
let idx = participants.indexOf(participant.roomConnectionId);
|
||||
Assert.ok(idx > -1, "Participant should be expected to join");
|
||||
participants.splice(idx, 1);
|
||||
if (!participants.length) {
|
||||
delete gExpectedJoins[roomToken];
|
||||
delete gExpectedJoins[room.roomToken];
|
||||
}
|
||||
};
|
||||
|
||||
const onRoomLeft = function(e, roomToken, participant) {
|
||||
let participants = gExpectedLeaves[roomToken];
|
||||
const onRoomLeft = function(e, room, participant) {
|
||||
let participants = gExpectedLeaves[room.roomToken];
|
||||
Assert.ok(participants, "Participant should be expected to leave");
|
||||
let idx = participants.indexOf(participant.roomConnectionId);
|
||||
Assert.ok(idx > -1, "Participant should be expected to leave");
|
||||
participants.splice(idx, 1);
|
||||
if (!participants.length) {
|
||||
delete gExpectedLeaves[roomToken];
|
||||
delete gExpectedLeaves[room.roomToken];
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -609,17 +609,9 @@ Nfc.prototype = {
|
|||
* Process a message from the gMessageManager.
|
||||
*/
|
||||
receiveMessage: function receiveMessage(message) {
|
||||
let isRFAPI = message.name == "NFC:ChangeRFState";
|
||||
let isSendFile = message.name == "NFC:SendFile";
|
||||
let isInfoAPI = message.name == "NFC:QueryInfo";
|
||||
|
||||
if (!isRFAPI && !isInfoAPI && (this.rfState != NFC.NFC_RF_STATE_DISCOVERY)) {
|
||||
debug("NFC is not enabled. current rfState:" + this.rfState);
|
||||
this.sendNfcErrorResponse(message, NFC.NFC_GECKO_ERROR_NOT_ENABLED);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isRFAPI && !isSendFile && !isInfoAPI) {
|
||||
if (["NFC:ChangeRFState",
|
||||
"NFC:SendFile",
|
||||
"NFC:QueryInfo"].indexOf(message.name) == -1) {
|
||||
// Update the current sessionId before sending to the NFC service.
|
||||
message.data.sessionId = SessionHelper.getId(message.data.sessionToken);
|
||||
}
|
||||
|
|
|
@ -26,14 +26,12 @@ this.DEBUG_NFC = DEBUG_ALL || false;
|
|||
this.NFC_GECKO_SUCCESS = 0;
|
||||
this.NFC_GECKO_ERROR_GENERIC_FAILURE = 1;
|
||||
this.NFC_GECKO_ERROR_P2P_REG_INVALID = 2;
|
||||
this.NFC_GECKO_ERROR_NOT_ENABLED = 3;
|
||||
this.NFC_GECKO_ERROR_SEND_FILE_FAILED = 4;
|
||||
this.NFC_GECKO_ERROR_BAD_SESSION_TOKEN = 5;
|
||||
this.NFC_GECKO_ERROR_SEND_FILE_FAILED = 3;
|
||||
this.NFC_GECKO_ERROR_BAD_SESSION_TOKEN = 4;
|
||||
|
||||
this.NFC_ERROR_MSG = {};
|
||||
this.NFC_ERROR_MSG[this.NFC_GECKO_ERROR_GENERIC_FAILURE] = "NfcGenericFailureError";
|
||||
this.NFC_ERROR_MSG[this.NFC_GECKO_ERROR_P2P_REG_INVALID] = "NfcP2PRegistrationInvalid";
|
||||
this.NFC_ERROR_MSG[this.NFC_GECKO_ERROR_NOT_ENABLED] = "NfcNotEnabledError";
|
||||
this.NFC_ERROR_MSG[this.NFC_GECKO_ERROR_SEND_FILE_FAILED] = "NfcSendFileFailed";
|
||||
this.NFC_ERROR_MSG[this.NFC_GECKO_ERROR_BAD_SESSION_TOKEN] = "NfcBadSessionToken";
|
||||
|
||||
|
|
|
@ -1514,14 +1514,17 @@ public abstract class GeckoApp
|
|||
// External URLs should always be loaded regardless of whether Gecko is
|
||||
// already running.
|
||||
if (isExternalURL) {
|
||||
// Restore tabs before opening an external URL so that the new tab
|
||||
// is animated properly.
|
||||
Tabs.getInstance().notifyListeners(null, Tabs.TabEvents.RESTORED);
|
||||
loadStartupTab(passedUri);
|
||||
} else if (!mIsRestoringActivity) {
|
||||
loadStartupTab(null);
|
||||
}
|
||||
} else {
|
||||
if (!mIsRestoringActivity) {
|
||||
loadStartupTab(null);
|
||||
}
|
||||
|
||||
// We now have tab stubs from the last session. Any future tabs should
|
||||
// be animated.
|
||||
Tabs.getInstance().notifyListeners(null, Tabs.TabEvents.RESTORED);
|
||||
Tabs.getInstance().notifyListeners(null, Tabs.TabEvents.RESTORED);
|
||||
}
|
||||
|
||||
// If we're not restoring, move the session file so it can be read for
|
||||
// the last tabs section.
|
||||
|
|
|
@ -87,7 +87,6 @@ public class Tabs implements GeckoEventListener {
|
|||
|
||||
private Tabs() {
|
||||
EventDispatcher.getInstance().registerGeckoThreadListener(this,
|
||||
"Session:RestoreEnd",
|
||||
"Tab:Added",
|
||||
"Tab:Close",
|
||||
"Tab:Select",
|
||||
|
@ -410,11 +409,6 @@ public class Tabs implements GeckoEventListener {
|
|||
public void handleMessage(String event, JSONObject message) {
|
||||
Log.d(LOGTAG, "handleMessage: " + event);
|
||||
try {
|
||||
if (event.equals("Session:RestoreEnd")) {
|
||||
notifyListeners(null, TabEvents.RESTORED);
|
||||
return;
|
||||
}
|
||||
|
||||
// All other events handled below should contain a tabID property
|
||||
int id = message.getInt("tabID");
|
||||
Tab tab = getTab(id);
|
||||
|
|
До Ширина: | Высота: | Размер: 242 B После Ширина: | Высота: | Размер: 159 B |
До Ширина: | Высота: | Размер: 822 B После Ширина: | Высота: | Размер: 589 B |
До Ширина: | Высота: | Размер: 669 B После Ширина: | Высота: | Размер: 404 B |
До Ширина: | Высота: | Размер: 775 B После Ширина: | Высота: | Размер: 771 B |
До Ширина: | Высота: | Размер: 137 B После Ширина: | Высота: | Размер: 133 B |
До Ширина: | Высота: | Размер: 353 B После Ширина: | Высота: | Размер: 231 B |
До Ширина: | Высота: | Размер: 148 B После Ширина: | Высота: | Размер: 130 B |
До Ширина: | Высота: | Размер: 593 B После Ширина: | Высота: | Размер: 456 B |
До Ширина: | Высота: | Размер: 408 B После Ширина: | Высота: | Размер: 299 B |
До Ширина: | Высота: | Размер: 555 B После Ширина: | Высота: | Размер: 551 B |
До Ширина: | Высота: | Размер: 272 B После Ширина: | Высота: | Размер: 192 B |
До Ширина: | Высота: | Размер: 282 B После Ширина: | Высота: | Размер: 170 B |
До Ширина: | Высота: | Размер: 1.1 KiB После Ширина: | Высота: | Размер: 754 B |
До Ширина: | Высота: | Размер: 806 B После Ширина: | Высота: | Размер: 475 B |
До Ширина: | Высота: | Размер: 139 B После Ширина: | Высота: | Размер: 135 B |
До Ширина: | Высота: | Размер: 417 B После Ширина: | Высота: | Размер: 240 B |
До Ширина: | Высота: | Размер: 470 B После Ширина: | Высота: | Размер: 209 B |
До Ширина: | Высота: | Размер: 1.6 KiB После Ширина: | Высота: | Размер: 1006 B |
До Ширина: | Высота: | Размер: 1.1 KiB После Ширина: | Высота: | Размер: 661 B |
До Ширина: | Высота: | Размер: 163 B После Ширина: | Высота: | Размер: 137 B |
До Ширина: | Высота: | Размер: 606 B После Ширина: | Высота: | Размер: 334 B |
|
@ -103,6 +103,9 @@ public class TabStrip extends ThemedLinearLayout {
|
|||
public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) {
|
||||
switch (msg) {
|
||||
case RESTORED:
|
||||
tabStripView.restoreTabs();
|
||||
break;
|
||||
|
||||
case ADDED:
|
||||
tabStripView.addTab(tab);
|
||||
break;
|
||||
|
|
|
@ -44,6 +44,8 @@ public class TabStripView extends TwoWayView {
|
|||
|
||||
private final TabAnimatorListener animatorListener;
|
||||
|
||||
private boolean isRestoringTabs;
|
||||
|
||||
// Filled by calls to ShapeDrawable.getPadding();
|
||||
// saved to prevent allocation in draw().
|
||||
private final Rect dividerPadding = new Rect();
|
||||
|
@ -205,7 +207,46 @@ public class TabStripView extends TwoWayView {
|
|||
});
|
||||
}
|
||||
|
||||
private void animateRestoredTabs() {
|
||||
getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
getViewTreeObserver().removeOnPreDrawListener(this);
|
||||
|
||||
final List<Animator> childAnimators = new ArrayList<Animator>();
|
||||
|
||||
final int tabHeight = getHeight() - getPaddingTop() - getPaddingBottom();
|
||||
final int childCount = getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
final View child = getChildAt(i);
|
||||
|
||||
childAnimators.add(
|
||||
ObjectAnimator.ofFloat(child, "translationY", tabHeight, 0));
|
||||
}
|
||||
|
||||
final AnimatorSet animatorSet = new AnimatorSet();
|
||||
animatorSet.playTogether(childAnimators);
|
||||
animatorSet.setDuration(ANIM_TIME_MS);
|
||||
animatorSet.setInterpolator(ANIM_INTERPOLATOR);
|
||||
animatorSet.addListener(animatorListener);
|
||||
|
||||
TransitionsTracker.track(animatorSet);
|
||||
|
||||
animatorSet.start();
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void ensurePositionIsVisible(final int position) {
|
||||
// We just want to move the strip to the right position
|
||||
// when restoring tabs on startup.
|
||||
if (isRestoringTabs) {
|
||||
setSelection(position);
|
||||
return;
|
||||
}
|
||||
|
||||
getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
|
@ -244,6 +285,13 @@ public class TabStripView extends TwoWayView {
|
|||
adapter.clear();
|
||||
}
|
||||
|
||||
void restoreTabs() {
|
||||
isRestoringTabs = true;
|
||||
refreshTabs();
|
||||
animateRestoredTabs();
|
||||
isRestoringTabs = false;
|
||||
}
|
||||
|
||||
void addTab(Tab tab) {
|
||||
// Refresh the list to make sure the new tab is
|
||||
// added in the right position.
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
package org.mozilla.gecko.tabs;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.mozilla.gecko.animation.ViewHelper;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
|
@ -18,13 +19,22 @@ import org.mozilla.gecko.Tabs;
|
|||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.PointF;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.GridView;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.Button;
|
||||
import android.widget.GridView;
|
||||
import com.nineoldandroids.animation.Animator;
|
||||
import com.nineoldandroids.animation.AnimatorSet;
|
||||
import com.nineoldandroids.animation.ObjectAnimator;
|
||||
import com.nineoldandroids.animation.PropertyValuesHolder;
|
||||
import com.nineoldandroids.animation.ValueAnimator;
|
||||
|
||||
|
||||
/**
|
||||
* A tabs layout implementation for the tablet redesign (bug 1014156).
|
||||
|
@ -36,12 +46,18 @@ class TabsGridLayout extends GridView
|
|||
Tabs.OnTabsChangedListener {
|
||||
private static final String LOGTAG = "Gecko" + TabsGridLayout.class.getSimpleName();
|
||||
|
||||
private static final int ANIM_TIME_MS = 200;
|
||||
public static final int ANIM_DELAY_MULTIPLE_MS = 20;
|
||||
private static final DecelerateInterpolator ANIM_INTERPOLATOR = new DecelerateInterpolator();
|
||||
|
||||
private final Context mContext;
|
||||
private TabsPanel mTabsPanel;
|
||||
private final SparseArray<PointF> mTabLocations = new SparseArray<PointF>();
|
||||
|
||||
final private boolean mIsPrivate;
|
||||
|
||||
private final TabsLayoutAdapter mTabsAdapter;
|
||||
private final int mColumnWidth;
|
||||
|
||||
public TabsGridLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs, R.attr.tabGridLayoutViewStyle);
|
||||
|
@ -67,9 +83,13 @@ class TabsGridLayout extends GridView
|
|||
setGravity(Gravity.CENTER);
|
||||
setNumColumns(GridView.AUTO_FIT);
|
||||
|
||||
// The clipToPadding setting in the styles.xml doesn't seem to be working (bug 1101784)
|
||||
// so lets set it manually in code for the moment as it's needed for the padding animation
|
||||
setClipToPadding(false);
|
||||
|
||||
final Resources resources = getResources();
|
||||
final int columnWidth = resources.getDimensionPixelSize(R.dimen.new_tablet_tab_panel_column_width);
|
||||
setColumnWidth(columnWidth);
|
||||
mColumnWidth = resources.getDimensionPixelSize(R.dimen.new_tablet_tab_panel_column_width);
|
||||
setColumnWidth(mColumnWidth);
|
||||
|
||||
final int padding = resources.getDimensionPixelSize(R.dimen.new_tablet_tab_panel_grid_padding);
|
||||
final int paddingTop = resources.getDimensionPixelSize(R.dimen.new_tablet_tab_panel_grid_padding_top);
|
||||
|
@ -87,9 +107,7 @@ class TabsGridLayout extends GridView
|
|||
mCloseClickListener = new Button.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
TabsLayoutItemView itemView = (TabsLayoutItemView) v.getTag();
|
||||
Tab tab = Tabs.getInstance().getTab(itemView.getTabId());
|
||||
Tabs.getInstance().closeTab(tab);
|
||||
closeTab(v);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -121,6 +139,47 @@ class TabsGridLayout extends GridView
|
|||
}
|
||||
}
|
||||
|
||||
private void populateTabLocations(final Tab removedTab) {
|
||||
mTabLocations.clear();
|
||||
|
||||
final int firstPosition = getFirstVisiblePosition();
|
||||
final int lastPosition = getLastVisiblePosition();
|
||||
final int numberOfColumns = getNumColumns();
|
||||
final int childCount = getChildCount();
|
||||
final int removedPosition = mTabsAdapter.getPositionForTab(removedTab);
|
||||
|
||||
for (int x = 1, i = (removedPosition - firstPosition) + 1; i < childCount; i++, x++) {
|
||||
final View child = getChildAt(i);
|
||||
if (child != null) {
|
||||
mTabLocations.append(x, new PointF(child.getX(), child.getY()));
|
||||
}
|
||||
}
|
||||
|
||||
final boolean firstChildOffScreen = ((firstPosition > 0) || getChildAt(0).getY() < 0);
|
||||
final boolean lastChildVisible = (lastPosition - childCount == firstPosition - 1);
|
||||
final boolean oneItemOnLastRow = (lastPosition % numberOfColumns == 0);
|
||||
if (firstChildOffScreen && lastChildVisible && oneItemOnLastRow) {
|
||||
// We need to set the view's bottom padding to prevent a sudden jump as the
|
||||
// last item in the row is being removed. We then need to remove the padding
|
||||
// via a sweet animation
|
||||
|
||||
final int removedHeight = getChildAt(0).getMeasuredHeight();
|
||||
final int verticalSpacing = getVerticalSpacing();
|
||||
|
||||
ValueAnimator paddingAnimator = ValueAnimator.ofInt(getPaddingBottom() + removedHeight + verticalSpacing, getPaddingBottom());
|
||||
paddingAnimator.setDuration(ANIM_TIME_MS * 2);
|
||||
|
||||
paddingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), (Integer) animation.getAnimatedValue());
|
||||
}
|
||||
});
|
||||
paddingAnimator.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTabsPanel(TabsPanel panel) {
|
||||
mTabsPanel = panel;
|
||||
|
@ -160,6 +219,9 @@ class TabsGridLayout extends GridView
|
|||
break;
|
||||
|
||||
case CLOSED:
|
||||
if(mTabsAdapter.getCount() > 0) {
|
||||
animateRemoveTab(tab);
|
||||
}
|
||||
if (tab.isPrivate() == mIsPrivate && mTabsAdapter.getCount() > 0) {
|
||||
if (mTabsAdapter.removeTab(tab)) {
|
||||
int selected = mTabsAdapter.getPositionForTab(Tabs.getInstance().getSelectedTab());
|
||||
|
@ -244,4 +306,90 @@ class TabsGridLayout extends GridView
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private View getViewForTab(Tab tab) {
|
||||
final int position = mTabsAdapter.getPositionForTab(tab);
|
||||
return getChildAt(position - getFirstVisiblePosition());
|
||||
}
|
||||
|
||||
void closeTab(View v) {
|
||||
TabsLayoutItemView itemView = (TabsLayoutItemView) v.getTag();
|
||||
Tab tab = Tabs.getInstance().getTab(itemView.getTabId());
|
||||
|
||||
Tabs.getInstance().closeTab(tab);
|
||||
updateSelectedPosition();
|
||||
}
|
||||
|
||||
private void animateRemoveTab(final Tab removedTab) {
|
||||
final int removedPosition = mTabsAdapter.getPositionForTab(removedTab);
|
||||
|
||||
final View removedView = getViewForTab(removedTab);
|
||||
|
||||
// The removed position might not have a matching child view
|
||||
// when it's not within the visible range of positions in the strip.
|
||||
if (removedView == null) {
|
||||
return;
|
||||
}
|
||||
final int removedHeight = removedView.getMeasuredHeight();
|
||||
|
||||
populateTabLocations(removedTab);
|
||||
|
||||
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
getViewTreeObserver().removeOnPreDrawListener(this);
|
||||
// We don't animate the removed child view (it just disappears)
|
||||
// but we still need its size to animate all affected children
|
||||
// within the visible viewport.
|
||||
final int childCount = getChildCount();
|
||||
final int firstPosition = getFirstVisiblePosition();
|
||||
final int numberOfColumns = getNumColumns();
|
||||
|
||||
final List<Animator> childAnimators = new ArrayList<>();
|
||||
|
||||
PropertyValuesHolder translateX, translateY;
|
||||
for (int x = 0, i = removedPosition - firstPosition ; i < childCount; i++, x++) {
|
||||
final View child = getChildAt(i);
|
||||
ObjectAnimator animator;
|
||||
|
||||
if (i % numberOfColumns == numberOfColumns - 1) {
|
||||
// Animate X & Y
|
||||
translateX = PropertyValuesHolder.ofFloat("translationX", -(mColumnWidth * numberOfColumns), 0);
|
||||
translateY = PropertyValuesHolder.ofFloat("translationY", removedHeight, 0);
|
||||
animator = ObjectAnimator.ofPropertyValuesHolder(child, translateX, translateY);
|
||||
} else {
|
||||
// Just animate X
|
||||
translateX = PropertyValuesHolder.ofFloat("translationX", mColumnWidth, 0);
|
||||
animator = ObjectAnimator.ofPropertyValuesHolder(child, translateX);
|
||||
}
|
||||
animator.setStartDelay(x * ANIM_DELAY_MULTIPLE_MS);
|
||||
childAnimators.add(animator);
|
||||
}
|
||||
|
||||
final AnimatorSet animatorSet = new AnimatorSet();
|
||||
animatorSet.playTogether(childAnimators);
|
||||
animatorSet.setDuration(ANIM_TIME_MS);
|
||||
animatorSet.setInterpolator(ANIM_INTERPOLATOR);
|
||||
animatorSet.start();
|
||||
|
||||
// Set the starting position of the child views - because we are delaying the start
|
||||
// of the animation, we need to prevent the items being drawn in their final position
|
||||
// prior to the animation starting
|
||||
for (int x = 1, i = (removedPosition - firstPosition) + 1; i < childCount; i++, x++) {
|
||||
final View child = getChildAt(i);
|
||||
|
||||
final PointF targetLocation = mTabLocations.get(x+1);
|
||||
if (targetLocation == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
child.setX(targetLocation.x);
|
||||
child.setY(targetLocation.y);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -142,11 +142,6 @@ SessionStore.prototype = {
|
|||
selected: true
|
||||
});
|
||||
}
|
||||
|
||||
// Let Java know we're done restoring tabs so tabs added after this can be animated
|
||||
Messaging.sendRequest({
|
||||
type: "Session:RestoreEnd"
|
||||
});
|
||||
}.bind(this)
|
||||
};
|
||||
Services.obs.addObserver(restoreCleanup, "sessionstore-windows-restored", false);
|
||||
|
|
|
@ -1519,6 +1519,25 @@ nsHttpHandler::TimerCallback(nsITimer * aTimer, void * aClosure)
|
|||
thisObject->mCapabilities &= ~NS_HTTP_ALLOW_PIPELINING;
|
||||
}
|
||||
|
||||
static void
|
||||
NormalizeLanguageTag(char *code)
|
||||
{
|
||||
bool is_region = false;
|
||||
while (*code != '\0')
|
||||
{
|
||||
if (*code == '-') {
|
||||
is_region = true;
|
||||
} else {
|
||||
if (is_region) {
|
||||
*code = nsCRT::ToUpper(*code);
|
||||
} else {
|
||||
*code = nsCRT::ToLower(*code);
|
||||
}
|
||||
}
|
||||
code++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates a C string into that contains a ISO 639 language list
|
||||
* notated with HTTP "q" values for output with a HTTP Accept-Language
|
||||
|
@ -1574,6 +1593,8 @@ PrepareAcceptLanguages(const char *i_AcceptLanguages, nsACString &o_AcceptLangua
|
|||
*trim = '\0';
|
||||
|
||||
if (*token != '\0') {
|
||||
NormalizeLanguageTag(token);
|
||||
|
||||
comma = count_n++ != 0 ? "," : ""; // delimiter if not first item
|
||||
uint32_t u = QVAL_TO_UINT(q);
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
var testpath = "/bug1054739";
|
||||
|
||||
function run_test() {
|
||||
let intlPrefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).getBranch("intl.");
|
||||
|
||||
let oldAcceptLangPref = intlPrefs.getCharPref("accept_languages");
|
||||
|
||||
let testData = [
|
||||
["de, en-US, en", "de,en-US;q=0.7,en;q=0.3"],
|
||||
["de,en-us,en", "de,en-US;q=0.7,en;q=0.3"],
|
||||
["en-US, en", "en-US,en;q=0.5"],
|
||||
["EN-US;q=0.2, EN", "en-US,en;q=0.5"],
|
||||
];
|
||||
|
||||
for (let i = 0; i < testData.length; i++) {
|
||||
let acceptLangPref = testData[i][0];
|
||||
let expectedHeader = testData[i][1];
|
||||
|
||||
intlPrefs.setCharPref("accept_languages", acceptLangPref);
|
||||
let acceptLangHeader = setupChannel(testpath).getRequestHeader("Accept-Language");
|
||||
equal(acceptLangHeader, expectedHeader);
|
||||
}
|
||||
|
||||
intlPrefs.setCharPref("accept_languages", oldAcceptLangPref);
|
||||
}
|
||||
|
||||
function setupChannel(path) {
|
||||
let ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
|
||||
let chan = ios.newChannel("http://localhost:4444" + path, "", null);
|
||||
chan.QueryInterface(Ci.nsIHttpChannel);
|
||||
return chan;
|
||||
}
|
|
@ -198,6 +198,7 @@ skip-if = bits != 32
|
|||
[test_gzipped_206.js]
|
||||
[test_head.js]
|
||||
[test_header_Accept-Language.js]
|
||||
[test_header_Accept-Language_case.js]
|
||||
[test_headers.js]
|
||||
[test_http_headers.js]
|
||||
[test_httpauth.js]
|
||||
|
|
|
@ -37,68 +37,67 @@ MobileIdentityVerificationFlow.prototype = {
|
|||
return Promise.reject(ERROR_INTERNAL_UNEXPECTED);
|
||||
}
|
||||
this.sessionToken = registerResult.msisdnSessionToken;
|
||||
return this._doVerification();
|
||||
// We save the timestamp of the start of the verification timeout to be
|
||||
// able to provide to the UI the remaining time on each retry.
|
||||
if (!this.timer) {
|
||||
log.debug("Creating verification code timer");
|
||||
this.timerCreation = Date.now();
|
||||
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
this.timer.initWithCallback(this.onVerificationCodeTimeout.bind(this),
|
||||
VERIFICATIONCODE_TIMEOUT,
|
||||
this.timer.TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
if (!this.verifyStrategy) {
|
||||
return Promise.reject(ERROR_INTERNAL_INVALID_VERIFICATION_FLOW);
|
||||
}
|
||||
|
||||
return this.verifyStrategy()
|
||||
.then(() => {
|
||||
return this._doVerification();
|
||||
}, (reason) => {
|
||||
this.verificationCodeDeferred.reject(reason);
|
||||
});
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
_doVerification: function() {
|
||||
log.debug("_doVerification");
|
||||
// We save the timestamp of the start of the verification timeout to be
|
||||
// able to provide to the UI the remaining time on each retry.
|
||||
if (!this.timer) {
|
||||
log.debug("Creating verification code timer");
|
||||
this.timerCreation = Date.now();
|
||||
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
this.timer.initWithCallback(this.onVerificationCodeTimeout.bind(this),
|
||||
VERIFICATIONCODE_TIMEOUT,
|
||||
this.timer.TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
if (!this.verifyStrategy) {
|
||||
return Promise.reject(ERROR_INTERNAL_INVALID_VERIFICATION_FLOW);
|
||||
}
|
||||
|
||||
this.verificationCodeDeferred = Promise.defer();
|
||||
|
||||
this.verifyStrategy()
|
||||
.then(
|
||||
() => {
|
||||
// If the verification flow can be for an external phone number,
|
||||
// we need to ask the user for the verification code.
|
||||
// In that case we don't do a notification about the verification
|
||||
// process being done until the user enters the verification code
|
||||
// in the UI.
|
||||
if (this.verificationOptions.external) {
|
||||
let timeLeft = 0;
|
||||
if (this.timer) {
|
||||
timeLeft = this.timerCreation + VERIFICATIONCODE_TIMEOUT -
|
||||
Date.now();
|
||||
}
|
||||
this.ui.verificationCodePrompt(this.retries,
|
||||
VERIFICATIONCODE_TIMEOUT / 1000,
|
||||
timeLeft / 1000)
|
||||
.then(
|
||||
(verificationCode) => {
|
||||
if (!verificationCode) {
|
||||
return this.verificationCodeDeferred.reject(
|
||||
ERROR_INTERNAL_INVALID_PROMPT_RESULT);
|
||||
}
|
||||
// If the user got the verification code that means that the
|
||||
// introduced phone number didn't belong to any of the inserted
|
||||
// SIMs.
|
||||
this.ui.verify();
|
||||
this.verificationCodeDeferred.resolve(verificationCode);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.ui.verify();
|
||||
}
|
||||
},
|
||||
(reason) => {
|
||||
this.verificationCodeDeferred.reject(reason);
|
||||
// If the verification flow can be for an external phone number,
|
||||
// we need to ask the user for the verification code.
|
||||
// In that case we don't do a notification about the verification
|
||||
// process being done until the user enters the verification code
|
||||
// in the UI.
|
||||
if (this.verificationOptions.external) {
|
||||
let timeLeft = 0;
|
||||
if (this.timer) {
|
||||
timeLeft = this.timerCreation + VERIFICATIONCODE_TIMEOUT -
|
||||
Date.now();
|
||||
}
|
||||
);
|
||||
this.ui.verificationCodePrompt(this.retries,
|
||||
VERIFICATIONCODE_TIMEOUT / 1000,
|
||||
timeLeft / 1000)
|
||||
.then(
|
||||
(verificationCode) => {
|
||||
if (!verificationCode) {
|
||||
return this.verificationCodeDeferred.reject(
|
||||
ERROR_INTERNAL_INVALID_PROMPT_RESULT);
|
||||
}
|
||||
// If the user got the verification code that means that the
|
||||
// introduced phone number didn't belong to any of the inserted
|
||||
// SIMs.
|
||||
this.ui.verify();
|
||||
this.verificationCodeDeferred.resolve(verificationCode);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.ui.verify();
|
||||
}
|
||||
|
||||
return this.verificationCodeDeferred.promise.then(
|
||||
this.onVerificationCode.bind(this)
|
||||
);
|
||||
|
@ -145,8 +144,11 @@ MobileIdentityVerificationFlow.prototype = {
|
|||
log.error("Retries left " + this.retries);
|
||||
if (!this.retries) {
|
||||
this.ui.error(ERROR_NO_RETRIES_LEFT);
|
||||
this.timer.cancel();
|
||||
this.timer = null;
|
||||
return Promise.reject(ERROR_NO_RETRIES_LEFT);
|
||||
}
|
||||
this.ui.error(ERROR_INVALID_VERIFICATION_CODE);
|
||||
this.verifying = false;
|
||||
if (this.queuedTimeout) {
|
||||
this.onVerificationCodeTimeout();
|
||||
|
|
|
@ -5,6 +5,8 @@ const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
|
|||
|
||||
"use strict";
|
||||
|
||||
const Cm = Components.manager;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
|
@ -17,3 +19,444 @@ Cu.import("resource://gre/modules/Services.jsm");
|
|||
Services.prefs.setCharPref("services.mobileid.server.uri",
|
||||
"https://dummyurl.com");
|
||||
}).call(this);
|
||||
|
||||
const DEBUG = false;
|
||||
|
||||
const GET_ASSERTION_IPC_MSG = "MobileId:GetAssertion";
|
||||
const GET_ASSERTION_RETURN_OK = "MobileId:GetAssertion:Return:OK";
|
||||
const GET_ASSERTION_RETURN_KO = "MobileId:GetAssertion:Return:KO";
|
||||
|
||||
// === Globals ===
|
||||
|
||||
const ORIGIN = "app://afakeorigin";
|
||||
const APP_ID = 1;
|
||||
const PRINCIPAL = {
|
||||
origin: ORIGIN,
|
||||
appId: APP_ID
|
||||
};
|
||||
const PHONE_NUMBER = "+34666555444";
|
||||
const ANOTHER_PHONE_NUMBER = "+44123123123";
|
||||
const VERIFICATION_CODE = "123456";
|
||||
const SESSION_TOKEN = "aSessionToken";
|
||||
const ICC_ID = "aIccId";
|
||||
const ANOTHER_ICC_ID = "anotherIccId";
|
||||
const MNC = "aMnc";
|
||||
const ANOTHER_MNC = "anotherMnc";
|
||||
const MCC = "aMcc";
|
||||
const ANOTHER_MCC = "anotherMcc";
|
||||
const OPERATOR = "aOperator";
|
||||
const ANOTHER_OPERATOR = "anotherOperator";
|
||||
const RADIO_INTERFACE = {
|
||||
rilContext: {
|
||||
iccInfo: {
|
||||
iccid: ICC_ID,
|
||||
mcc: MCC,
|
||||
mnc: MNC,
|
||||
msisdn: PHONE_NUMBER,
|
||||
operator: OPERATOR
|
||||
}
|
||||
},
|
||||
voice: {
|
||||
network: {
|
||||
shortName: OPERATOR
|
||||
},
|
||||
roaming: false
|
||||
},
|
||||
data: {
|
||||
network: {
|
||||
shortName: OPERATOR
|
||||
}
|
||||
}
|
||||
};
|
||||
const ANOTHER_RADIO_INTERFACE = {
|
||||
rilContext: {
|
||||
iccInfo: {
|
||||
iccid: ANOTHER_ICC_ID,
|
||||
mcc: ANOTHER_MCC,
|
||||
mnc: ANOTHER_MNC,
|
||||
msisdn: ANOTHER_PHONE_NUMBER,
|
||||
operator: ANOTHER_OPERATOR
|
||||
}
|
||||
},
|
||||
voice: {
|
||||
network: {
|
||||
shortName: ANOTHER_OPERATOR
|
||||
},
|
||||
roaming: false
|
||||
},
|
||||
data: {
|
||||
network: {
|
||||
shortName: ANOTHER_OPERATOR
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const INVALID_RADIO_INTERFACE = {
|
||||
rilContext: {
|
||||
iccInfo: {
|
||||
iccid: null,
|
||||
mcc: "",
|
||||
mnc: "",
|
||||
msisdn: "",
|
||||
operator: ""
|
||||
}
|
||||
},
|
||||
voice: {
|
||||
network: {
|
||||
shortName: ""
|
||||
},
|
||||
roaming: undefined
|
||||
},
|
||||
data: {
|
||||
network: {
|
||||
shortName: ""
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const CERTIFICATE = "eyJhbGciOiJEUzI1NiJ9.eyJsYXN0QXV0aEF0IjoxNDA0NDY5NzkyODc3LCJ2ZXJpZmllZE1TSVNETiI6IiszMTYxNzgxNTc1OCIsInB1YmxpYy1rZXkiOnsiYWxnb3JpdGhtIjoiRFMiLCJ5IjoiNGE5YzkzNDY3MWZhNzQ3YmM2ZjMyNjE0YTg1MzUyZjY5NDcwMDdhNTRkMDAxMDY4OWU5ZjJjZjc0ZGUwYTEwZTRlYjlmNDk1ZGFmZTA0NGVjZmVlNDlkN2YwOGU4ODQyMDJiOTE5OGRhNWZhZWE5MGUzZjRmNzE1YzZjNGY4Yjc3MGYxZTU4YWZhNDM0NzVhYmFiN2VlZGE1MmUyNjk2YzFmNTljNzMzYjFlYzBhNGNkOTM1YWIxYzkyNzAxYjNiYTA5ZDRhM2E2MzNjNTJmZjE2NGYxMWY3OTg1YzlmZjY3ZThmZDFlYzA2NDU3MTdkMjBiNDE4YmM5M2YzYzVkNCIsInAiOiJmZjYwMDQ4M2RiNmFiZmM1YjQ1ZWFiNzg1OTRiMzUzM2Q1NTBkOWYxYmYyYTk5MmE3YThkYWE2ZGMzNGY4MDQ1YWQ0ZTZlMGM0MjlkMzM0ZWVlYWFlZmQ3ZTIzZDQ4MTBiZTAwZTRjYzE0OTJjYmEzMjViYTgxZmYyZDVhNWIzMDVhOGQxN2ViM2JmNGEwNmEzNDlkMzkyZTAwZDMyOTc0NGE1MTc5MzgwMzQ0ZTgyYTE4YzQ3OTMzNDM4Zjg5MWUyMmFlZWY4MTJkNjljOGY3NWUzMjZjYjcwZWEwMDBjM2Y3NzZkZmRiZDYwNDYzOGMyZWY3MTdmYzI2ZDAyZTE3IiwicSI6ImUyMWUwNGY5MTFkMWVkNzk5MTAwOGVjYWFiM2JmNzc1OTg0MzA5YzMiLCJnIjoiYzUyYTRhMGZmM2I3ZTYxZmRmMTg2N2NlODQxMzgzNjlhNjE1NGY0YWZhOTI5NjZlM2M4MjdlMjVjZmE2Y2Y1MDhiOTBlNWRlNDE5ZTEzMzdlMDdhMmU5ZTJhM2NkNWRlYTcwNGQxNzVmOGViZjZhZjM5N2Q2OWUxMTBiOTZhZmIxN2M3YTAzMjU5MzI5ZTQ4MjliMGQwM2JiYzc4OTZiMTViNGFkZTUzZTEzMDg1OGNjMzRkOTYyNjlhYTg5MDQxZjQwOTEzNmM3MjQyYTM4ODk1YzlkNWJjY2FkNGYzODlhZjFkN2E0YmQxMzk4YmQwNzJkZmZhODk2MjMzMzk3YSJ9LCJwcmluY2lwYWwiOiIwMzgxOTgyYS0xZTgzLTI1NjYtNjgzZS05MDRmNDA0NGM1MGRAbXNpc2RuLWRldi5zdGFnZS5tb3phd3MubmV0IiwiaWF0IjoxNDA0NDY5NzgyODc3LCJleHAiOjE0MDQ0OTEzOTI4NzcsImlzcyI6Im1zaXNkbi1kZXYuc3RhZ2UubW96YXdzLm5ldCJ9."
|
||||
|
||||
// === Helpers ===
|
||||
|
||||
function addPermission(aAction) {
|
||||
let uri = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService)
|
||||
.newURI(ORIGIN, null, null);
|
||||
let _principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager)
|
||||
.getAppCodebasePrincipal(uri, APP_ID, false);
|
||||
let pm = Cc["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Ci.nsIPermissionManager);
|
||||
pm.addFromPrincipal(_principal, MOBILEID_PERM, aAction);
|
||||
}
|
||||
|
||||
function removePermission() {
|
||||
let uri = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService)
|
||||
.newURI(ORIGIN, null, null);
|
||||
let _principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager)
|
||||
.getAppCodebasePrincipal(uri, APP_ID, false);
|
||||
let pm = Cc["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Ci.nsIPermissionManager);
|
||||
pm.removeFromPrincipal(_principal, MOBILEID_PERM);
|
||||
}
|
||||
|
||||
// === Mocks ===
|
||||
|
||||
let Mock = function(aOptions) {
|
||||
if (!aOptions) {
|
||||
aOptions = {};
|
||||
}
|
||||
this._options = aOptions;
|
||||
this._spied = {};
|
||||
};
|
||||
|
||||
Mock.prototype = {
|
||||
_: function(aMethod) {
|
||||
DEBUG && do_print("_ " + aMethod + JSON.stringify(this._spied));
|
||||
let self = this;
|
||||
return {
|
||||
callsLength: function(aNumberOfCalls) {
|
||||
if (aNumberOfCalls == 0) {
|
||||
do_check_eq(self._spied[aMethod], undefined);
|
||||
return;
|
||||
}
|
||||
do_check_eq(self._spied[aMethod].length, aNumberOfCalls);
|
||||
},
|
||||
call: function(aCallNumber) {
|
||||
return {
|
||||
arg: function(aArgNumber, aValue) {
|
||||
let _arg = self._spied[aMethod][aCallNumber - 1][aArgNumber - 1];
|
||||
if (Array.isArray(aValue)) {
|
||||
do_check_eq(_arg.length, aValue.length)
|
||||
for (let i = 0; i < _arg.length; i++) {
|
||||
do_check_eq(_arg[i], aValue[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof aValue === 'object') {
|
||||
do_check_eq(JSON.stringify(_arg), JSON.stringify(aValue));
|
||||
return;
|
||||
}
|
||||
|
||||
do_check_eq(_arg, aValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_spy: function(aMethod, aArgs) {
|
||||
DEBUG && do_print(aMethod + " - " + JSON.stringify(aArgs));
|
||||
if (!this._spied[aMethod]) {
|
||||
this._spied[aMethod] = [];
|
||||
}
|
||||
this._spied[aMethod].push(aArgs);
|
||||
},
|
||||
|
||||
getSpiedCalls: function(aMethod) {
|
||||
return this._spied[aMethod];
|
||||
}
|
||||
};
|
||||
|
||||
// UI Glue mock up.
|
||||
let MockUi = function(aOptions) {
|
||||
Mock.call(this, aOptions);
|
||||
};
|
||||
|
||||
MockUi.prototype = {
|
||||
__proto__: Mock.prototype,
|
||||
|
||||
_startFlowResult: {
|
||||
phoneNumber: PHONE_NUMBER,
|
||||
mcc: MNC
|
||||
},
|
||||
|
||||
_verifyCodePromptResult: {
|
||||
verificationCode: VERIFICATION_CODE
|
||||
},
|
||||
|
||||
startFlow: function() {
|
||||
this._spy("startFlow", arguments);
|
||||
return Promise.resolve(this._options.startFlowResult ||
|
||||
this._startFlowResult);
|
||||
},
|
||||
|
||||
verificationCodePrompt: function() {
|
||||
this._spy("verifyCodePrompt", arguments);
|
||||
return Promise.resolve(this._options.verificationCodePromptResult ||
|
||||
this._verifyCodePromptResult);
|
||||
},
|
||||
|
||||
verify: function() {
|
||||
this._spy("verify", arguments);
|
||||
},
|
||||
|
||||
error: function() {
|
||||
this._spy("error", arguments);
|
||||
},
|
||||
|
||||
verified: function() {
|
||||
this._spy("verified", arguments);
|
||||
},
|
||||
|
||||
set oncancel(aCallback) {
|
||||
},
|
||||
|
||||
set onresendcode(aCallback) {
|
||||
}
|
||||
};
|
||||
|
||||
// Credentials store mock up.
|
||||
let MockCredStore = function(aOptions) {
|
||||
Mock.call(this, aOptions);
|
||||
};
|
||||
|
||||
MockCredStore.prototype = {
|
||||
__proto__: Mock.prototype,
|
||||
|
||||
_getByOriginResult: null,
|
||||
|
||||
_getByMsisdnResult: null,
|
||||
|
||||
_getByIccIdResult: null,
|
||||
|
||||
getByOrigin: function() {
|
||||
this._spy("getByOrigin", arguments);
|
||||
let result = this._getByOriginResult;
|
||||
if (this._options.getByOriginResult) {
|
||||
if (Array.isArray(this._options.getByOriginResult)) {
|
||||
result = this._options.getByOriginResult.length ?
|
||||
this._options.getByOriginResult.shift() : null;
|
||||
} else {
|
||||
result = this._options.getByOriginResult;
|
||||
}
|
||||
}
|
||||
return Promise.resolve(result);
|
||||
},
|
||||
|
||||
getByMsisdn: function() {
|
||||
this._spy("getByMsisdn", arguments);
|
||||
return Promise.resolve(this._options.getByMsisdnResult ||
|
||||
this._getByMsisdnResult);
|
||||
},
|
||||
|
||||
getByIccId: function() {
|
||||
this._spy("getByIccId", arguments);
|
||||
return Promise.resolve(this._options.getByIccIdResult ||
|
||||
this._getByIccIdResult);
|
||||
},
|
||||
|
||||
add: function() {
|
||||
this._spy("add", arguments);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
setDeviceIccIds: function() {
|
||||
this._spy("setDeviceIccIds", arguments);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
removeOrigin: function() {
|
||||
this._spy("removeOrigin", arguments);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
delete: function() {
|
||||
this._spy("delete", arguments);
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
// Client mock up.
|
||||
let MockClient = function(aOptions) {
|
||||
Mock.call(this, aOptions);
|
||||
};
|
||||
|
||||
MockClient.prototype = {
|
||||
|
||||
__proto__: Mock.prototype,
|
||||
|
||||
_discoverResult: {
|
||||
verificationMethods: ["sms/mt"],
|
||||
verificationDetails: {
|
||||
"sms/mt": {
|
||||
mtSender: "123",
|
||||
url: "https://msisdn.accounts.firefox.com/v1/msisdn/sms/mt/verify"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_registerResult: {
|
||||
msisdnSessionToken: SESSION_TOKEN
|
||||
},
|
||||
|
||||
_smsMtVerifyResult: {},
|
||||
|
||||
_verifyCodeResult: {
|
||||
msisdn: PHONE_NUMBER
|
||||
},
|
||||
|
||||
_signResult: {
|
||||
cert: CERTIFICATE
|
||||
},
|
||||
|
||||
hawk: {
|
||||
now: function() {
|
||||
return Date.now();
|
||||
}
|
||||
},
|
||||
|
||||
discover: function() {
|
||||
this._spy("discover", arguments);
|
||||
return Promise.resolve(this._options.discoverResult ||
|
||||
this._discoverResult);
|
||||
},
|
||||
|
||||
register: function() {
|
||||
this._spy("register", arguments);
|
||||
return Promise.resolve(this._options.registerResult ||
|
||||
this._registerResult);
|
||||
},
|
||||
|
||||
smsMtVerify: function() {
|
||||
this._spy("smsMtVerify", arguments);
|
||||
return Promise.resolve(this._options.smsMtVerifyResult ||
|
||||
this._smsMtVerifyResult);
|
||||
},
|
||||
|
||||
verifyCode: function() {
|
||||
this._spy("verifyCode", arguments);
|
||||
if (this._options.verifyCodeError) {
|
||||
let error = Array.isArray(this._options.verifyCodeError) ?
|
||||
this._options.verifyCodeError.shift() :
|
||||
this._options.verifyCodeError;
|
||||
if (!this._options.verifyCodeError.length) {
|
||||
this._options.verifyCodeError = null;
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
return Promise.resolve(this._options.verifyCodeResult ||
|
||||
this._verifyCodeResult);
|
||||
},
|
||||
|
||||
sign: function() {
|
||||
this._spy("sign", arguments);
|
||||
if (this._options.signError) {
|
||||
let error = Array.isArray(this._options.signError) ?
|
||||
this._options.signError.shift() :
|
||||
this._options.signError;
|
||||
return Promise.reject(error);
|
||||
}
|
||||
return Promise.resolve(this._options.signResult || this._signResult);
|
||||
}
|
||||
};
|
||||
|
||||
// Override MobileIdentityUIGlue.
|
||||
const kMobileIdentityUIGlueUUID = "{05df0566-ca8a-4ec7-bc76-78626ebfbe9a}";
|
||||
const kMobileIdentityUIGlueContractID =
|
||||
"@mozilla.org/services/mobileid-ui-glue;1";
|
||||
|
||||
// Save original factory.
|
||||
/*const kMobileIdentityUIGlueFactory =
|
||||
Cm.getClassObject(Cc[kMobileIdentityUIGlueContractID], Ci.nsIFactory);*/
|
||||
|
||||
let fakeMobileIdentityUIGlueFactory = {
|
||||
createInstance: function(aOuter, aIid) {
|
||||
return MobileIdentityUIGlue.QueryInterface(aIid);
|
||||
}
|
||||
};
|
||||
|
||||
// MobileIdentityUIGlue fake component.
|
||||
let MobileIdentityUIGlue = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileIdentityUIGlue]),
|
||||
|
||||
};
|
||||
|
||||
(function registerFakeMobileIdentityUIGlue() {
|
||||
Cm.QueryInterface(Ci.nsIComponentRegistrar)
|
||||
.registerFactory(Components.ID(kMobileIdentityUIGlueUUID),
|
||||
"MobileIdentityUIGlue",
|
||||
kMobileIdentityUIGlueContractID,
|
||||
fakeMobileIdentityUIGlueFactory);
|
||||
})();
|
||||
|
||||
// The tests rely on having an app registered. Otherwise, it will throw.
|
||||
// Override XULAppInfo.
|
||||
const XUL_APP_INFO_UUID = Components.ID("{84fdc459-d96d-421c-9bff-a8193233ae75}");
|
||||
const XUL_APP_INFO_CONTRACT_ID = "@mozilla.org/xre/app-info;1";
|
||||
|
||||
let (XULAppInfo = {
|
||||
vendor: "Mozilla",
|
||||
name: "MobileIdTest",
|
||||
ID: "{230de50e-4cd1-11dc-8314-0800200b9a66}",
|
||||
version: "1",
|
||||
appBuildID: "2007010101",
|
||||
platformVersion: "",
|
||||
platformBuildID: "2007010101",
|
||||
inSafeMode: false,
|
||||
logConsoleErrors: true,
|
||||
OS: "XPCShell",
|
||||
XPCOMABI: "noarch-spidermonkey",
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIXULAppInfo,
|
||||
Ci.nsIXULRuntime,
|
||||
])
|
||||
}) {
|
||||
let XULAppInfoFactory = {
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer != null) {
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
return XULAppInfo.QueryInterface(iid);
|
||||
}
|
||||
};
|
||||
Cm.QueryInterface(Ci.nsIComponentRegistrar)
|
||||
.registerFactory(XUL_APP_INFO_UUID,
|
||||
"XULAppInfo",
|
||||
XUL_APP_INFO_CONTRACT_ID,
|
||||
XULAppInfoFactory);
|
||||
}
|
||||
|
|
|
@ -3,424 +3,16 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const Cm = Components.manager;
|
||||
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/MobileIdentityManager.jsm");
|
||||
Cu.import("resource://gre/modules/MobileIdentityCommon.jsm");
|
||||
|
||||
const DEBUG = false;
|
||||
|
||||
const GET_ASSERTION_IPC_MSG = "MobileId:GetAssertion";
|
||||
const GET_ASSERTION_RETURN_OK = "MobileId:GetAssertion:Return:OK";
|
||||
const GET_ASSERTION_RETURN_KO = "MobileId:GetAssertion:Return:KO";
|
||||
|
||||
// === Globals ===
|
||||
|
||||
const ORIGIN = "app://afakeorigin";
|
||||
const APP_ID = 1;
|
||||
const PRINCIPAL = {
|
||||
origin: ORIGIN,
|
||||
appId: APP_ID
|
||||
};
|
||||
const PHONE_NUMBER = "+34666555444";
|
||||
const ANOTHER_PHONE_NUMBER = "+44123123123";
|
||||
const VERIFICATION_CODE = "123456";
|
||||
const SESSION_TOKEN = "aSessionToken";
|
||||
const ICC_ID = "aIccId";
|
||||
const ANOTHER_ICC_ID = "anotherIccId";
|
||||
const MNC = "aMnc";
|
||||
const ANOTHER_MNC = "anotherMnc";
|
||||
const MCC = "aMcc";
|
||||
const ANOTHER_MCC = "anotherMcc";
|
||||
const OPERATOR = "aOperator";
|
||||
const ANOTHER_OPERATOR = "anotherOperator";
|
||||
const RADIO_INTERFACE = {
|
||||
rilContext: {
|
||||
iccInfo: {
|
||||
iccid: ICC_ID,
|
||||
mcc: MCC,
|
||||
mnc: MNC,
|
||||
msisdn: PHONE_NUMBER,
|
||||
operator: OPERATOR
|
||||
}
|
||||
},
|
||||
voice: {
|
||||
network: {
|
||||
shortName: OPERATOR
|
||||
},
|
||||
roaming: false
|
||||
},
|
||||
data: {
|
||||
network: {
|
||||
shortName: OPERATOR
|
||||
}
|
||||
}
|
||||
};
|
||||
const ANOTHER_RADIO_INTERFACE = {
|
||||
rilContext: {
|
||||
iccInfo: {
|
||||
iccid: ANOTHER_ICC_ID,
|
||||
mcc: ANOTHER_MCC,
|
||||
mnc: ANOTHER_MNC,
|
||||
msisdn: ANOTHER_PHONE_NUMBER,
|
||||
operator: ANOTHER_OPERATOR
|
||||
}
|
||||
},
|
||||
voice: {
|
||||
network: {
|
||||
shortName: ANOTHER_OPERATOR
|
||||
},
|
||||
roaming: false
|
||||
},
|
||||
data: {
|
||||
network: {
|
||||
shortName: ANOTHER_OPERATOR
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const INVALID_RADIO_INTERFACE = {
|
||||
rilContext: {
|
||||
iccInfo: {
|
||||
iccid: null,
|
||||
mcc: "",
|
||||
mnc: "",
|
||||
msisdn: "",
|
||||
operator: ""
|
||||
}
|
||||
},
|
||||
voice: {
|
||||
network: {
|
||||
shortName: ""
|
||||
},
|
||||
roaming: undefined
|
||||
},
|
||||
data: {
|
||||
network: {
|
||||
shortName: ""
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const CERTIFICATE = "eyJhbGciOiJEUzI1NiJ9.eyJsYXN0QXV0aEF0IjoxNDA0NDY5NzkyODc3LCJ2ZXJpZmllZE1TSVNETiI6IiszMTYxNzgxNTc1OCIsInB1YmxpYy1rZXkiOnsiYWxnb3JpdGhtIjoiRFMiLCJ5IjoiNGE5YzkzNDY3MWZhNzQ3YmM2ZjMyNjE0YTg1MzUyZjY5NDcwMDdhNTRkMDAxMDY4OWU5ZjJjZjc0ZGUwYTEwZTRlYjlmNDk1ZGFmZTA0NGVjZmVlNDlkN2YwOGU4ODQyMDJiOTE5OGRhNWZhZWE5MGUzZjRmNzE1YzZjNGY4Yjc3MGYxZTU4YWZhNDM0NzVhYmFiN2VlZGE1MmUyNjk2YzFmNTljNzMzYjFlYzBhNGNkOTM1YWIxYzkyNzAxYjNiYTA5ZDRhM2E2MzNjNTJmZjE2NGYxMWY3OTg1YzlmZjY3ZThmZDFlYzA2NDU3MTdkMjBiNDE4YmM5M2YzYzVkNCIsInAiOiJmZjYwMDQ4M2RiNmFiZmM1YjQ1ZWFiNzg1OTRiMzUzM2Q1NTBkOWYxYmYyYTk5MmE3YThkYWE2ZGMzNGY4MDQ1YWQ0ZTZlMGM0MjlkMzM0ZWVlYWFlZmQ3ZTIzZDQ4MTBiZTAwZTRjYzE0OTJjYmEzMjViYTgxZmYyZDVhNWIzMDVhOGQxN2ViM2JmNGEwNmEzNDlkMzkyZTAwZDMyOTc0NGE1MTc5MzgwMzQ0ZTgyYTE4YzQ3OTMzNDM4Zjg5MWUyMmFlZWY4MTJkNjljOGY3NWUzMjZjYjcwZWEwMDBjM2Y3NzZkZmRiZDYwNDYzOGMyZWY3MTdmYzI2ZDAyZTE3IiwicSI6ImUyMWUwNGY5MTFkMWVkNzk5MTAwOGVjYWFiM2JmNzc1OTg0MzA5YzMiLCJnIjoiYzUyYTRhMGZmM2I3ZTYxZmRmMTg2N2NlODQxMzgzNjlhNjE1NGY0YWZhOTI5NjZlM2M4MjdlMjVjZmE2Y2Y1MDhiOTBlNWRlNDE5ZTEzMzdlMDdhMmU5ZTJhM2NkNWRlYTcwNGQxNzVmOGViZjZhZjM5N2Q2OWUxMTBiOTZhZmIxN2M3YTAzMjU5MzI5ZTQ4MjliMGQwM2JiYzc4OTZiMTViNGFkZTUzZTEzMDg1OGNjMzRkOTYyNjlhYTg5MDQxZjQwOTEzNmM3MjQyYTM4ODk1YzlkNWJjY2FkNGYzODlhZjFkN2E0YmQxMzk4YmQwNzJkZmZhODk2MjMzMzk3YSJ9LCJwcmluY2lwYWwiOiIwMzgxOTgyYS0xZTgzLTI1NjYtNjgzZS05MDRmNDA0NGM1MGRAbXNpc2RuLWRldi5zdGFnZS5tb3phd3MubmV0IiwiaWF0IjoxNDA0NDY5NzgyODc3LCJleHAiOjE0MDQ0OTEzOTI4NzcsImlzcyI6Im1zaXNkbi1kZXYuc3RhZ2UubW96YXdzLm5ldCJ9."
|
||||
|
||||
// === Helpers ===
|
||||
|
||||
function addPermission(aAction) {
|
||||
let uri = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService)
|
||||
.newURI(ORIGIN, null, null);
|
||||
let _principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager)
|
||||
.getAppCodebasePrincipal(uri, APP_ID, false);
|
||||
let pm = Cc["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Ci.nsIPermissionManager);
|
||||
pm.addFromPrincipal(_principal, MOBILEID_PERM, aAction);
|
||||
}
|
||||
|
||||
function removePermission() {
|
||||
let uri = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService)
|
||||
.newURI(ORIGIN, null, null);
|
||||
let _principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager)
|
||||
.getAppCodebasePrincipal(uri, APP_ID, false);
|
||||
let pm = Cc["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Ci.nsIPermissionManager);
|
||||
pm.removeFromPrincipal(_principal, MOBILEID_PERM);
|
||||
}
|
||||
|
||||
// === Mocks ===
|
||||
|
||||
let Mock = function(aOptions) {
|
||||
if (!aOptions) {
|
||||
aOptions = {};
|
||||
}
|
||||
this._options = aOptions;
|
||||
this._spied = {};
|
||||
};
|
||||
|
||||
Mock.prototype = {
|
||||
_: function(aMethod) {
|
||||
DEBUG && do_print("_ " + aMethod + JSON.stringify(this._spied));
|
||||
let self = this;
|
||||
return {
|
||||
callsLength: function(aNumberOfCalls) {
|
||||
if (aNumberOfCalls == 0) {
|
||||
do_check_eq(self._spied[aMethod], undefined);
|
||||
return;
|
||||
}
|
||||
do_check_eq(self._spied[aMethod].length, aNumberOfCalls);
|
||||
},
|
||||
call: function(aCallNumber) {
|
||||
return {
|
||||
arg: function(aArgNumber, aValue) {
|
||||
let _arg = self._spied[aMethod][aCallNumber - 1][aArgNumber - 1];
|
||||
if (Array.isArray(aValue)) {
|
||||
do_check_eq(_arg.length, aValue.length)
|
||||
for (let i = 0; i < _arg.length; i++) {
|
||||
do_check_eq(_arg[i], aValue[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof aValue === 'object') {
|
||||
do_check_eq(JSON.stringify(_arg), JSON.stringify(aValue));
|
||||
return;
|
||||
}
|
||||
|
||||
do_check_eq(_arg, aValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_spy: function(aMethod, aArgs) {
|
||||
DEBUG && do_print(aMethod + " - " + JSON.stringify(aArgs));
|
||||
if (!this._spied[aMethod]) {
|
||||
this._spied[aMethod] = [];
|
||||
}
|
||||
this._spied[aMethod].push(aArgs);
|
||||
},
|
||||
|
||||
getSpiedCalls: function(aMethod) {
|
||||
return this._spied[aMethod];
|
||||
}
|
||||
};
|
||||
|
||||
// UI Glue mock up.
|
||||
let MockUi = function(aOptions) {
|
||||
Mock.call(this, aOptions);
|
||||
};
|
||||
|
||||
MockUi.prototype = {
|
||||
__proto__: Mock.prototype,
|
||||
|
||||
_startFlowResult: {
|
||||
phoneNumber: PHONE_NUMBER,
|
||||
mcc: MNC
|
||||
},
|
||||
|
||||
_verifyCodePromptResult: {
|
||||
verificationCode: VERIFICATION_CODE
|
||||
},
|
||||
|
||||
startFlow: function() {
|
||||
this._spy("startFlow", arguments);
|
||||
return Promise.resolve(this._options.startFlowResult ||
|
||||
this._startFlowResult);
|
||||
},
|
||||
|
||||
verificationCodePrompt: function() {
|
||||
this._spy("verifyCodePrompt", arguments);
|
||||
return Promise.resolve(this._options.verificationCodePromptResult ||
|
||||
this._verifyCodePromptResult);
|
||||
},
|
||||
|
||||
verify: function() {
|
||||
this._spy("verify", arguments);
|
||||
},
|
||||
|
||||
error: function() {
|
||||
this._spy("error", arguments);
|
||||
},
|
||||
|
||||
verified: function() {
|
||||
this._spy("verified", arguments);
|
||||
},
|
||||
|
||||
set oncancel(aCallback) {
|
||||
},
|
||||
|
||||
set onresendcode(aCallback) {
|
||||
}
|
||||
};
|
||||
|
||||
// Save original credential store instance.
|
||||
const kMobileIdentityCredStore = MobileIdentityManager.credStore;
|
||||
|
||||
// Credentials store mock up.
|
||||
let MockCredStore = function(aOptions) {
|
||||
Mock.call(this, aOptions);
|
||||
};
|
||||
|
||||
MockCredStore.prototype = {
|
||||
__proto__: Mock.prototype,
|
||||
|
||||
_getByOriginResult: null,
|
||||
|
||||
_getByMsisdnResult: null,
|
||||
|
||||
_getByIccIdResult: null,
|
||||
|
||||
getByOrigin: function() {
|
||||
this._spy("getByOrigin", arguments);
|
||||
let result = this._getByOriginResult;
|
||||
if (this._options.getByOriginResult) {
|
||||
if (Array.isArray(this._options.getByOriginResult)) {
|
||||
result = this._options.getByOriginResult.length ?
|
||||
this._options.getByOriginResult.shift() : null;
|
||||
} else {
|
||||
result = this._options.getByOriginResult;
|
||||
}
|
||||
}
|
||||
return Promise.resolve(result);
|
||||
},
|
||||
|
||||
getByMsisdn: function() {
|
||||
this._spy("getByMsisdn", arguments);
|
||||
return Promise.resolve(this._options.getByMsisdnResult ||
|
||||
this._getByMsisdnResult);
|
||||
},
|
||||
|
||||
getByIccId: function() {
|
||||
this._spy("getByIccId", arguments);
|
||||
return Promise.resolve(this._options.getByIccIdResult ||
|
||||
this._getByIccIdResult);
|
||||
},
|
||||
|
||||
add: function() {
|
||||
this._spy("add", arguments);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
setDeviceIccIds: function() {
|
||||
this._spy("setDeviceIccIds", arguments);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
removeOrigin: function() {
|
||||
this._spy("removeOrigin", arguments);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
delete: function() {
|
||||
this._spy("delete", arguments);
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
// Save original client instance.
|
||||
const kMobileIdentityClient = MobileIdentityManager.client;
|
||||
|
||||
// Client mock up.
|
||||
let MockClient = function(aOptions) {
|
||||
Mock.call(this, aOptions);
|
||||
};
|
||||
|
||||
MockClient.prototype = {
|
||||
|
||||
__proto__: Mock.prototype,
|
||||
|
||||
_discoverResult: {
|
||||
verificationMethods: ["sms/mt"],
|
||||
verificationDetails: {
|
||||
"sms/mt": {
|
||||
mtSender: "123",
|
||||
url: "https://msisdn.accounts.firefox.com/v1/msisdn/sms/mt/verify"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_registerResult: {
|
||||
msisdnSessionToken: SESSION_TOKEN
|
||||
},
|
||||
|
||||
_smsMtVerifyResult: {},
|
||||
|
||||
_verifyCodeResult: {
|
||||
msisdn: PHONE_NUMBER
|
||||
},
|
||||
|
||||
_signResult: {
|
||||
cert: CERTIFICATE
|
||||
},
|
||||
|
||||
hawk: {
|
||||
now: function() {
|
||||
return Date.now();
|
||||
}
|
||||
},
|
||||
|
||||
discover: function() {
|
||||
this._spy("discover", arguments);
|
||||
return Promise.resolve(this._options.discoverResult ||
|
||||
this._discoverResult);
|
||||
},
|
||||
|
||||
register: function() {
|
||||
this._spy("register", arguments);
|
||||
return Promise.resolve(this._options.registerResult ||
|
||||
this._registerResult);
|
||||
},
|
||||
|
||||
smsMtVerify: function() {
|
||||
this._spy("smsMtVerify", arguments);
|
||||
return Promise.resolve(this._options.smsMtVerifyResult ||
|
||||
this._smsMtVerifyResult);
|
||||
},
|
||||
|
||||
verifyCode: function() {
|
||||
this._spy("verifyCode", arguments);
|
||||
return Promise.resolve(this._options.verifyCodeResult ||
|
||||
this._verifyCodeResult);
|
||||
},
|
||||
|
||||
sign: function() {
|
||||
this._spy("sign", arguments);
|
||||
if (this._options.signError) {
|
||||
let error = Array.isArray(this._options.signError) ?
|
||||
this._options.signError.shift() :
|
||||
this._options.signError;
|
||||
return Promise.reject(error);
|
||||
}
|
||||
return Promise.resolve(this._options.signResult || this._signResult);
|
||||
}
|
||||
};
|
||||
|
||||
// The test rely on having an app registered. Otherwise, it will throw.
|
||||
// Override XULAppInfo.
|
||||
const XUL_APP_INFO_UUID = Components.ID("{84fdc459-d96d-421c-9bff-a8193233ae75}");
|
||||
const XUL_APP_INFO_CONTRACT_ID = "@mozilla.org/xre/app-info;1";
|
||||
|
||||
let (XULAppInfo = {
|
||||
vendor: "Mozilla",
|
||||
name: "MobileIdTest",
|
||||
ID: "{230de50e-4cd1-11dc-8314-0800200b9a66}",
|
||||
version: "1",
|
||||
appBuildID: "2007010101",
|
||||
platformVersion: "",
|
||||
platformBuildID: "2007010101",
|
||||
inSafeMode: false,
|
||||
logConsoleErrors: true,
|
||||
OS: "XPCShell",
|
||||
XPCOMABI: "noarch-spidermonkey",
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIXULAppInfo,
|
||||
Ci.nsIXULRuntime,
|
||||
])
|
||||
}) {
|
||||
let XULAppInfoFactory = {
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer != null) {
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
return XULAppInfo.QueryInterface(iid);
|
||||
}
|
||||
};
|
||||
Cm.QueryInterface(Ci.nsIComponentRegistrar)
|
||||
.registerFactory(XUL_APP_INFO_UUID,
|
||||
"XULAppInfo",
|
||||
XUL_APP_INFO_CONTRACT_ID,
|
||||
XULAppInfoFactory);
|
||||
}
|
||||
|
||||
// === Global cleanup ===
|
||||
|
||||
function cleanup() {
|
||||
MobileIdentityManager.credStore = kMobileIdentityCredStore;
|
||||
MobileIdentityManager.client = kMobileIdentityClient;
|
||||
|
@ -431,7 +23,6 @@ function cleanup() {
|
|||
|
||||
// Unregister mocks and restore original code.
|
||||
do_register_cleanup(cleanup);
|
||||
|
||||
// === Tests ===
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/MobileIdentityVerificationFlow.jsm");
|
||||
|
||||
function verifyStrategy() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function cleanupStrategy() {
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
do_print("= Bug 1101444: Invalid verification code shouldn't restart " +
|
||||
"verification flow =");
|
||||
|
||||
let client = new MockClient({
|
||||
// This will emulate two invalid attempts. The third time it will work.
|
||||
verifyCodeError: ["INVALID", "INVALID"]
|
||||
});
|
||||
let ui = new MockUi();
|
||||
|
||||
let verificationFlow = new MobileIdentityVerificationFlow({
|
||||
external: true,
|
||||
sessionToken: SESSION_TOKEN,
|
||||
msisdn: PHONE_NUMBER
|
||||
}, ui, client, verifyStrategy, cleanupStrategy);
|
||||
|
||||
verificationFlow.doVerification().then(() => {
|
||||
// We should only do the registration process once. We only try registering
|
||||
// again when the timeout fires, but not when we enter an invalid
|
||||
// verification code.
|
||||
client._("register").callsLength(1);
|
||||
client._("verifyCode").callsLength(3);
|
||||
// Because we do two invalid attempts, we should show the invalid code error twice.
|
||||
ui._("error").callsLength(2);
|
||||
});
|
||||
|
||||
do_test_finished();
|
||||
};
|
|
@ -1,7 +1,8 @@
|
|||
[DEFAULT]
|
||||
head = head.js
|
||||
tail =
|
||||
skip-if = toolkit == 'gonk'
|
||||
|
||||
[test_mobileid_manager.js]
|
||||
skip-if = 1
|
||||
[test_mobileid_client.js]
|
||||
[test_mobileid_verification_flow.js]
|
||||
|
|