Merge fx-team to m-c. a=merge
|
@ -14,10 +14,11 @@ let DevEdition = {
|
|||
|
||||
styleSheetLocation: "chrome://browser/skin/devedition.css",
|
||||
styleSheet: null,
|
||||
defaultThemeID: "{972ce4c6-7e08-4474-a285-3208198ce6fd}",
|
||||
|
||||
init: function () {
|
||||
this._updateDevtoolsThemeAttribute();
|
||||
this._updateStyleSheet();
|
||||
this._updateStyleSheetFromPrefs();
|
||||
|
||||
// Listen for changes to all prefs except for complete themes.
|
||||
// No need for this since changing a complete theme requires a
|
||||
|
@ -25,14 +26,27 @@ let DevEdition = {
|
|||
Services.prefs.addObserver(this._lwThemePrefName, this, false);
|
||||
Services.prefs.addObserver(this._prefName, this, false);
|
||||
Services.prefs.addObserver(this._devtoolsThemePrefName, this, false);
|
||||
Services.obs.addObserver(this, "lightweight-theme-styling-update", false);
|
||||
},
|
||||
|
||||
observe: function (subject, topic, data) {
|
||||
if (topic == "lightweight-theme-styling-update") {
|
||||
let newTheme = JSON.parse(data);
|
||||
if (!newTheme || newTheme.id === this.defaultThemeID) {
|
||||
// A lightweight theme has been unapplied, so just re-read prefs.
|
||||
this._updateStyleSheetFromPrefs();
|
||||
} else {
|
||||
// A lightweight theme has been applied, but the pref may not be
|
||||
// set yet if this happened from customize menu or addons page.
|
||||
this._toggleStyleSheet(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (topic == "nsPref:changed") {
|
||||
if (data == this._devtoolsThemePrefName) {
|
||||
this._updateDevtoolsThemeAttribute();
|
||||
} else {
|
||||
this._updateStyleSheet();
|
||||
this._updateStyleSheetFromPrefs();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -42,11 +56,10 @@ let DevEdition = {
|
|||
// to change colors based on the selected devtools theme.
|
||||
document.documentElement.setAttribute("devtoolstheme",
|
||||
Services.prefs.getCharPref(this._devtoolsThemePrefName));
|
||||
this._updateStyleSheetFromPrefs();
|
||||
},
|
||||
|
||||
_updateStyleSheet: function() {
|
||||
// Only try to apply the dev edition theme if it is preffered
|
||||
// on and there are no other themes applied.
|
||||
_updateStyleSheetFromPrefs: function() {
|
||||
let lightweightThemeSelected = false;
|
||||
try {
|
||||
lightweightThemeSelected = Services.prefs.getBoolPref(this._lwThemePrefName);
|
||||
|
@ -57,21 +70,37 @@ let DevEdition = {
|
|||
defaultThemeSelected = Services.prefs.getCharPref(this._themePrefName) == "classic/1.0";
|
||||
} catch(e) {}
|
||||
|
||||
let deveditionThemeEnabled = Services.prefs.getBoolPref(this._prefName) &&
|
||||
!lightweightThemeSelected && defaultThemeSelected;
|
||||
let devtoolsIsDark = false;
|
||||
try {
|
||||
devtoolsIsDark = Services.prefs.getCharPref(this._devtoolsThemePrefName) == "dark";
|
||||
} catch(e) {}
|
||||
|
||||
let deveditionThemeEnabled = Services.prefs.getBoolPref(this._prefName) &&
|
||||
!lightweightThemeSelected && defaultThemeSelected && devtoolsIsDark;
|
||||
|
||||
this._toggleStyleSheet(deveditionThemeEnabled);
|
||||
},
|
||||
|
||||
handleEvent: function(e) {
|
||||
if (e.type === "load") {
|
||||
this.styleSheet.removeEventListener("load", this);
|
||||
gBrowser.tabContainer._positionPinnedTabs();
|
||||
ToolbarIconColor.inferFromText();
|
||||
}
|
||||
},
|
||||
|
||||
_toggleStyleSheet: function(deveditionThemeEnabled) {
|
||||
if (deveditionThemeEnabled && !this.styleSheet) {
|
||||
let styleSheetAttr = `href="${this.styleSheetLocation}" type="text/css"`;
|
||||
let styleSheet = this.styleSheet = document.createProcessingInstruction(
|
||||
this.styleSheet = document.createProcessingInstruction(
|
||||
'xml-stylesheet', styleSheetAttr);
|
||||
this.styleSheet.addEventListener("load", function onLoad() {
|
||||
styleSheet.removeEventListener("load", onLoad);
|
||||
ToolbarIconColor.inferFromText();
|
||||
});
|
||||
this.styleSheet.addEventListener("load", this);
|
||||
document.insertBefore(this.styleSheet, document.documentElement);
|
||||
} else if (!deveditionThemeEnabled && this.styleSheet) {
|
||||
this.styleSheet.removeEventListener("load", this);
|
||||
this.styleSheet.remove();
|
||||
this.styleSheet = null;
|
||||
gBrowser.tabContainer._positionPinnedTabs();
|
||||
ToolbarIconColor.inferFromText();
|
||||
}
|
||||
},
|
||||
|
@ -80,6 +109,10 @@ let DevEdition = {
|
|||
Services.prefs.removeObserver(this._lwThemePrefName, this);
|
||||
Services.prefs.removeObserver(this._prefName, this);
|
||||
Services.prefs.removeObserver(this._devtoolsThemePrefName, this);
|
||||
Services.obs.removeObserver(this, "lightweight-theme-styling-update", false);
|
||||
if (this.styleSheet) {
|
||||
this.styleSheet.removeEventListener("load", this);
|
||||
}
|
||||
this.styleSheet = null;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -108,7 +108,7 @@ support-files =
|
|||
[browser_aboutHealthReport.js]
|
||||
skip-if = os == "linux" # Bug 924307
|
||||
[browser_aboutHome.js]
|
||||
skip-if = e10s # Bug ?????? - no about:home support yet
|
||||
skip-if = e10s # Bug 1093153 - no about:home support yet
|
||||
[browser_aboutSyncProgress.js]
|
||||
[browser_action_keyword.js]
|
||||
skip-if = os == "linux" || e10s # Bug 1073339 - Investigate autocomplete test unreliability on Linux/e10s
|
||||
|
@ -150,7 +150,7 @@ skip-if = e10s
|
|||
[browser_bug416661.js]
|
||||
skip-if = e10s # Bug 691614 - no e10s zoom support yet
|
||||
[browser_bug417483.js]
|
||||
skip-if = e10s # Bug ?????? - no about:home support yet
|
||||
skip-if = e10s # Bug 1093155 - tries to use context menu from browser-chrome and gets in a mess when in e10s mode
|
||||
[browser_bug419612.js]
|
||||
skip-if = e10s # Bug 691614 - no e10s zoom support yet
|
||||
[browser_bug422590.js]
|
||||
|
@ -178,7 +178,7 @@ skip-if = toolkit == "cocoa" || e10s # Bug ?????? - not sure why this is timing
|
|||
[browser_bug462673.js]
|
||||
skip-if = e10s # Bug 924260 - "Window is closed"
|
||||
[browser_bug477014.js]
|
||||
skip-if = e10s # Bug 918634 - swapFrameLoaders not implemented for e10s
|
||||
skip-if = e10s # Bug 1093206 - need to re-enable tests relying on swapFrameLoaders et al for e10s
|
||||
[browser_bug479408.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 918663 - DOMLinkAdded events don't make their way to chrome
|
||||
[browser_bug481560.js]
|
||||
|
@ -188,7 +188,7 @@ skip-if = e10s
|
|||
[browser_bug491431.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_bug495058.js]
|
||||
skip-if = e10s # Bug 918634 - swapFrameLoaders (and thus replaceTabWithWindow) not implemented for e10s
|
||||
skip-if = e10s # Bug 1093206 - need to re-enable tests relying on swapFrameLoaders et al (and thus replaceTabWithWindow) for e10s
|
||||
[browser_bug517902.js]
|
||||
skip-if = e10s # Bug 866413 - PageInfo doesn't work in e10s
|
||||
[browser_bug519216.js]
|
||||
|
@ -198,7 +198,7 @@ skip-if = e10s # Bug ?????? - some weird timing issue with progress listeners th
|
|||
skip-if = e10s # Bug 918663 - DOMLinkAdded events don't make their way to chrome
|
||||
[browser_bug533232.js]
|
||||
[browser_bug537013.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 918634 - swapFrameLoaders not implemented for e10s (test calls replaceTabWithWindow)
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1093206 - need to re-enable tests relying on swapFrameLoaders et al for e10s (test calls replaceTabWithWindow)
|
||||
[browser_bug537474.js]
|
||||
skip-if = e10s # Bug ?????? - test doesn't wait for document to be created before it checks it
|
||||
[browser_bug550565.js]
|
||||
|
@ -429,13 +429,13 @@ skip-if = e10s # Bug ?????? - timeout after logging "Error: Channel closing: too
|
|||
[browser_tabDrop.js]
|
||||
skip-if = buildapp == 'mulet' || e10s
|
||||
[browser_tabMatchesInAwesomebar_perwindowpb.js]
|
||||
skip-if = e10s # Bug 918634 - swapFrameLoaders not implemented for e10s (test uses gBrowser.swapBrowsersAndCloseOther)
|
||||
skip-if = e10s # Bug 1093206 - need to re-enable tests relying on swapFrameLoaders et al for e10s (test calls gBrowser.swapBrowsersAndCloseOther)
|
||||
[browser_tab_drag_drop_perwindow.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_tab_dragdrop.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 918634 - swapFrameLoaders not implemented for e10s (test uses gBrowser.swapBrowsersAndCloseOther)
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1093206 - need to re-enable tests relying on swapFrameLoaders et al for e10s (test calls gBrowser.swapBrowsersAndCloseOther)
|
||||
[browser_tab_dragdrop2.js]
|
||||
skip-if = buildapp == 'mulet' || e10s
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1093206 - need to re-enable tests relying on swapFrameLoaders et al for e10s (test calls gBrowser.swapBrowsersAndCloseOther)
|
||||
[browser_tabbar_big_widgets.js]
|
||||
skip-if = os == "linux" || os == "mac" # No tabs in titlebar on linux
|
||||
# Disabled on OS X because of bug 967917
|
||||
|
|
|
@ -6,14 +6,12 @@
|
|||
*/
|
||||
|
||||
const PREF_DEVEDITION_THEME = "browser.devedition.theme.enabled";
|
||||
const PREF_THEME = "general.skins.selectedSkin";
|
||||
const PREF_LWTHEME = "lightweightThemes.isThemeSelected";
|
||||
const PREF_DEVTOOLS_THEME = "devtools.theme";
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
// Set preferences back to their original values
|
||||
Services.prefs.clearUserPref(PREF_DEVEDITION_THEME);
|
||||
Services.prefs.clearUserPref(PREF_THEME);
|
||||
Services.prefs.clearUserPref(PREF_LWTHEME);
|
||||
Services.prefs.clearUserPref(PREF_DEVTOOLS_THEME);
|
||||
});
|
||||
|
@ -24,6 +22,8 @@ function test() {
|
|||
}
|
||||
|
||||
function startTests() {
|
||||
Services.prefs.setCharPref(PREF_DEVTOOLS_THEME, "dark");
|
||||
|
||||
info ("Setting browser.devedition.theme.enabled to false.");
|
||||
Services.prefs.setBoolPref(PREF_DEVEDITION_THEME, false);
|
||||
ok (!DevEdition.styleSheet, "There is no devedition style sheet when the pref is false.");
|
||||
|
@ -40,30 +40,83 @@ function startTests() {
|
|||
Services.prefs.setBoolPref(PREF_LWTHEME, false);
|
||||
ok (DevEdition.styleSheet, "The devedition stylesheet has been added when a lightweight theme is removed.");
|
||||
|
||||
// There are no listeners for the complete theme pref since applying the theme
|
||||
// requires a restart.
|
||||
info ("Setting general.skins.selectedSkin to a custom string.");
|
||||
Services.prefs.setCharPref(PREF_THEME, "custom-theme");
|
||||
ok (DevEdition.styleSheet, "The devedition stylesheet is still here when a complete theme is added.");
|
||||
|
||||
info ("Resetting general.skins.selectedSkin to default value.");
|
||||
Services.prefs.clearUserPref(PREF_THEME);
|
||||
ok (DevEdition.styleSheet, "The devedition stylesheet is still here when a complete theme is removed.");
|
||||
|
||||
info ("Setting browser.devedition.theme.enabled to false.");
|
||||
Services.prefs.setBoolPref(PREF_DEVEDITION_THEME, false);
|
||||
ok (!DevEdition.styleSheet, "The devedition stylesheet has been removed.");
|
||||
|
||||
info ("Checking :root attributes based on devtools theme.");
|
||||
testDevtoolsTheme();
|
||||
testLightweightThemePreview();
|
||||
finish();
|
||||
}
|
||||
|
||||
function testDevtoolsTheme() {
|
||||
info ("Checking that Australis is shown when the light devtools theme is applied.");
|
||||
|
||||
Services.prefs.setBoolPref(PREF_DEVEDITION_THEME, true);
|
||||
ok (DevEdition.styleSheet, "The devedition stylesheet exists.");
|
||||
|
||||
info ("Checking stylesheet and :root attributes based on devtools theme.");
|
||||
Services.prefs.setCharPref(PREF_DEVTOOLS_THEME, "light");
|
||||
is (document.documentElement.getAttribute("devtoolstheme"), "light",
|
||||
"The documentElement has an attribute based on devtools theme.");
|
||||
ok (!DevEdition.styleSheet, "The devedition stylesheet has been removed because of light devtools theme.");
|
||||
|
||||
Services.prefs.setCharPref(PREF_DEVTOOLS_THEME, "dark");
|
||||
is (document.documentElement.getAttribute("devtoolstheme"), "dark",
|
||||
"The documentElement has an attribute based on devtools theme.");
|
||||
ok (DevEdition.styleSheet, "The devedition stylesheet has been readded because of dark devtools theme.");
|
||||
|
||||
Services.prefs.setCharPref(PREF_DEVTOOLS_THEME, "light");
|
||||
is (document.documentElement.getAttribute("devtoolstheme"), "light",
|
||||
"The documentElement has an attribute based on devtools theme.");
|
||||
ok (!DevEdition.styleSheet, "The devedition stylesheet has been removed because of light devtools theme.");
|
||||
|
||||
finish();
|
||||
Services.prefs.setCharPref(PREF_DEVTOOLS_THEME, "dark");
|
||||
is (document.documentElement.getAttribute("devtoolstheme"), "dark",
|
||||
"The documentElement has an attribute based on devtools theme.");
|
||||
ok (DevEdition.styleSheet, "The devedition stylesheet has been readded because of dark devtools theme.");
|
||||
}
|
||||
|
||||
function dummyLightweightTheme(id) {
|
||||
return {
|
||||
id: id,
|
||||
name: id,
|
||||
headerURL: "http://lwttest.invalid/a.png",
|
||||
footerURL: "http://lwttest.invalid/b.png",
|
||||
textcolor: "red",
|
||||
accentcolor: "blue"
|
||||
};
|
||||
}
|
||||
|
||||
function testLightweightThemePreview() {
|
||||
let {LightweightThemeManager} = Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", {});
|
||||
|
||||
info ("Turning the pref on, then previewing lightweight themes");
|
||||
Services.prefs.setBoolPref(PREF_DEVEDITION_THEME, true);
|
||||
ok (DevEdition.styleSheet, "The devedition stylesheet is enabled.");
|
||||
LightweightThemeManager.previewTheme(dummyLightweightTheme("preview0"));
|
||||
ok (!DevEdition.styleSheet, "The devedition stylesheet is not enabled after a lightweight theme preview.");
|
||||
LightweightThemeManager.resetPreview();
|
||||
LightweightThemeManager.previewTheme(dummyLightweightTheme("preview1"));
|
||||
ok (!DevEdition.styleSheet, "The devedition stylesheet is not enabled after a second lightweight theme preview.");
|
||||
LightweightThemeManager.resetPreview();
|
||||
ok (DevEdition.styleSheet, "The devedition stylesheet is enabled again after resetting the preview.");
|
||||
|
||||
info ("Turning the pref on, then previewing a theme, turning it off and resetting the preview");
|
||||
Services.prefs.setBoolPref(PREF_DEVEDITION_THEME, true);
|
||||
ok (DevEdition.styleSheet, "The devedition stylesheet is enabled.");
|
||||
LightweightThemeManager.previewTheme(dummyLightweightTheme("preview2"));
|
||||
ok (!DevEdition.styleSheet, "The devedition stylesheet is not enabled after a lightweight theme preview.");
|
||||
Services.prefs.setBoolPref(PREF_DEVEDITION_THEME, false);
|
||||
ok (!DevEdition.styleSheet, "The devedition stylesheet is not enabled after pref is turned off.");
|
||||
LightweightThemeManager.resetPreview();
|
||||
ok (!DevEdition.styleSheet, "The devedition stylesheet is still disabled after resetting the preview.");
|
||||
|
||||
info ("Turning the pref on, then previewing the default theme, turning it off and resetting the preview");
|
||||
Services.prefs.setBoolPref(PREF_DEVEDITION_THEME, true);
|
||||
ok (DevEdition.styleSheet, "The devedition stylesheet is enabled.");
|
||||
LightweightThemeManager.previewTheme(dummyLightweightTheme("{972ce4c6-7e08-4474-a285-3208198ce6fd}"));
|
||||
ok (DevEdition.styleSheet, "The devedition stylesheet is still enabled after the default theme is applied.");
|
||||
LightweightThemeManager.resetPreview();
|
||||
ok (DevEdition.styleSheet, "The devedition stylesheet is still enabled after resetting the preview.");
|
||||
}
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
# 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/.
|
||||
|
||||
MOZ_APP_DISPLAYNAME=FirefoxDevEdition
|
||||
MOZ_APP_DISPLAYNAME=FirefoxDeveloperEdition
|
||||
MOZ_APP_REMOTINGNAME=firefox-dev
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
- 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/. -->
|
||||
|
||||
<!ENTITY brandShortName "FirefoxDevEdition">
|
||||
<!ENTITY brandShortName "Firefox Developer Edition">
|
||||
<!ENTITY brandFullName "Firefox Developer Edition">
|
||||
<!ENTITY vendorShortName "Mozilla">
|
||||
<!ENTITY trademarkInfo.part1 " ">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# 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/.
|
||||
|
||||
brandShortName=FirefoxDevEdition
|
||||
brandShortName=Firefox Developer Edition
|
||||
brandFullName=Firefox Developer Edition
|
||||
vendorShortName=Mozilla
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ CallProgressSocket.prototype = {
|
|||
connect: function(onSuccess, onError) {
|
||||
this._onSuccess = onSuccess;
|
||||
this._onError = onError ||
|
||||
(reason => {MozLoopService.logwarn("LoopCalls::callProgessSocket - ", reason);});
|
||||
(reason => {MozLoopService.log.warn("LoopCalls::callProgessSocket - ", reason);});
|
||||
|
||||
if (!onSuccess) {
|
||||
this._onError("missing onSuccess argument");
|
||||
|
@ -126,9 +126,8 @@ CallProgressSocket.prototype = {
|
|||
let msg = {};
|
||||
try {
|
||||
msg = JSON.parse(aMsg);
|
||||
}
|
||||
catch (error) {
|
||||
MozLoopService.logerror("LoopCalls: error parsing progress message - ", error);
|
||||
} catch (error) {
|
||||
MozLoopService.log.error("LoopCalls: error parsing progress message - ", error);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -146,7 +145,7 @@ CallProgressSocket.prototype = {
|
|||
*/
|
||||
_send: function(aMsg) {
|
||||
if (!this._handshakeComplete) {
|
||||
MozLoopService.logwarn("LoopCalls::_send error - handshake not complete");
|
||||
MozLoopService.log.warn("LoopCalls::_send error - handshake not complete");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -179,14 +178,12 @@ CallProgressSocket.prototype = {
|
|||
* and register with the Loop server.
|
||||
*/
|
||||
let LoopCallsInternal = {
|
||||
callsData: {
|
||||
inUse: false,
|
||||
},
|
||||
|
||||
mocks: {
|
||||
webSocket: undefined,
|
||||
},
|
||||
|
||||
conversationInProgress: {},
|
||||
|
||||
/**
|
||||
* Callback from MozLoopPushHandler - A push notification has been received from
|
||||
* the server.
|
||||
|
@ -248,20 +245,19 @@ let LoopCallsInternal = {
|
|||
let respData = JSON.parse(response.body);
|
||||
if (respData.calls && Array.isArray(respData.calls)) {
|
||||
respData.calls.forEach((callData) => {
|
||||
if (!this.callsData.inUse) {
|
||||
callData.sessionType = sessionType;
|
||||
// XXX Bug 1090209 will transiton into a better window id.
|
||||
callData.windowId = callData.callId;
|
||||
this._startCall(callData, "incoming");
|
||||
} else {
|
||||
if ("id" in this.conversationInProgress) {
|
||||
this._returnBusy(callData);
|
||||
} else {
|
||||
callData.sessionType = sessionType;
|
||||
callData.type = "incoming";
|
||||
this._startCall(callData);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
MozLoopService.logwarn("Error: missing calls[] in response");
|
||||
MozLoopService.log.warn("Error: missing calls[] in response");
|
||||
}
|
||||
} catch (err) {
|
||||
MozLoopService.logwarn("Error parsing calls info", err);
|
||||
MozLoopService.log.warn("Error parsing calls info", err);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -269,17 +265,11 @@ let LoopCallsInternal = {
|
|||
* Starts a call, saves the call data, and opens a chat window.
|
||||
*
|
||||
* @param {Object} callData The data associated with the call including an id.
|
||||
* @param {Boolean} conversationType Whether or not the call is "incoming"
|
||||
* or "outgoing"
|
||||
* The data should include the type - "incoming" or
|
||||
* "outgoing".
|
||||
*/
|
||||
_startCall: function(callData, conversationType) {
|
||||
this.callsData.inUse = true;
|
||||
this.callsData.data = callData;
|
||||
MozLoopService.openChatWindow(
|
||||
null,
|
||||
// No title, let the page set that, to avoid flickering.
|
||||
"",
|
||||
"about:loopconversation#" + conversationType + "/" + callData.windowId);
|
||||
_startCall: function(callData) {
|
||||
this.conversationInProgress.id = MozLoopService.openChatWindow(callData);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -290,17 +280,16 @@ let LoopCallsInternal = {
|
|||
* @return true if the call is opened, false if it is not opened (i.e. busy)
|
||||
*/
|
||||
startDirectCall: function(contact, callType) {
|
||||
if (this.callsData.inUse)
|
||||
if ("id" in this.conversationInProgress)
|
||||
return false;
|
||||
|
||||
var callData = {
|
||||
contact: contact,
|
||||
callType: callType,
|
||||
// XXX Really we shouldn't be using random numbers, bug 1090209 will fix this.
|
||||
windowId: Math.floor((Math.random() * 100000000))
|
||||
type: "outgoing"
|
||||
};
|
||||
|
||||
this._startCall(callData, "outgoing");
|
||||
this._startCall(callData);
|
||||
return true;
|
||||
},
|
||||
|
||||
|
@ -341,21 +330,18 @@ this.LoopCalls = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Returns the callData for a specific conversation window id.
|
||||
* Used to signify that a call is in progress.
|
||||
*
|
||||
* The data was retrieved from the LoopServer via a GET/calls/<version> request
|
||||
* triggered by an incoming message from the LoopPushServer.
|
||||
*
|
||||
* @param {Number} conversationWindowId
|
||||
* @return {callData} The callData or undefined if error.
|
||||
* @param {String} The window id for the call in progress.
|
||||
*/
|
||||
getCallData: function(conversationWindowId) {
|
||||
if (LoopCallsInternal.callsData.data &&
|
||||
LoopCallsInternal.callsData.data.windowId == conversationWindowId) {
|
||||
return LoopCallsInternal.callsData.data;
|
||||
} else {
|
||||
return undefined;
|
||||
setCallInProgress: function(conversationWindowId) {
|
||||
if ("id" in LoopCallsInternal.conversationInProgress &&
|
||||
LoopCallsInternal.conversationInProgress.id != conversationWindowId) {
|
||||
MozLoopService.log.error("Starting a new conversation when one is already in progress?");
|
||||
return;
|
||||
}
|
||||
|
||||
LoopCallsInternal.conversationInProgress.id = conversationWindowId;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -365,11 +351,10 @@ this.LoopCalls = {
|
|||
*
|
||||
* @param {Number} conversationWindowId
|
||||
*/
|
||||
releaseCallData: function(conversationWindowId) {
|
||||
if (LoopCallsInternal.callsData.data &&
|
||||
LoopCallsInternal.callsData.data.windowId == conversationWindowId) {
|
||||
LoopCallsInternal.callsData.data = undefined;
|
||||
LoopCallsInternal.callsData.inUse = false;
|
||||
clearCallInProgress: function(conversationWindowId) {
|
||||
if ("id" in LoopCallsInternal.conversationInProgress &&
|
||||
LoopCallsInternal.conversationInProgress.id == conversationWindowId) {
|
||||
delete LoopCallsInternal.conversationInProgress.id;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -41,6 +41,61 @@ const extend = function(target, source) {
|
|||
return target;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether a participant is already part of a room.
|
||||
*
|
||||
* @see https://wiki.mozilla.org/Loop/Architecture/Rooms#User_Identification_in_a_Room
|
||||
*
|
||||
* @param {Object} room A room object that contains a list of current participants
|
||||
* @param {Object} participant Participant to check if it's already there
|
||||
* @returns {Boolean} TRUE when the participant is already a member of the room,
|
||||
* FALSE when it's not.
|
||||
*/
|
||||
const containsParticipant = function(room, participant) {
|
||||
for (let user of room.participants) {
|
||||
if (user.roomConnectionId == participant.roomConnectionId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Compares the list of participants of the room currently in the cache and an
|
||||
* updated version of that room. When a new participant is found, the 'joined'
|
||||
* event is emitted. When a participant is not found in the update, it emits a
|
||||
* 'left' event.
|
||||
*
|
||||
* @param {Object} room A room object to compare the participants list
|
||||
* against
|
||||
* @param {Object} updatedRoom A room object that contains the most up-to-date
|
||||
* list of participants
|
||||
*/
|
||||
const checkForParticipantsUpdate = function(room, updatedRoom) {
|
||||
// Partially fetched rooms don't contain the participants list yet. Skip the
|
||||
// check for now.
|
||||
if (!("participants" in room)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let participant;
|
||||
// Check for participants that joined.
|
||||
for (participant of updatedRoom.participants) {
|
||||
if (!containsParticipant(room, participant)) {
|
||||
eventEmitter.emit("joined", room.roomToken, participant);
|
||||
eventEmitter.emit("joined:" + room.roomToken, participant);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for participants that left.
|
||||
for (participant of room.participants) {
|
||||
if (!containsParticipant(updatedRoom, participant)) {
|
||||
eventEmitter.emit("left", room.roomToken, participant);
|
||||
eventEmitter.emit("left:" + room.roomToken, participant);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The Rooms class.
|
||||
*
|
||||
|
@ -85,11 +140,23 @@ let LoopRoomsInternal = {
|
|||
throw new Error("Missing array of rooms in response.");
|
||||
}
|
||||
|
||||
// Next, request the detailed information for each room. If the request
|
||||
// fails the room data will not be added to the map.
|
||||
for (let room of roomsList) {
|
||||
// See if we already have this room in our cache.
|
||||
let orig = this.rooms.get(room.roomToken);
|
||||
if (orig) {
|
||||
checkForParticipantsUpdate(orig, room);
|
||||
}
|
||||
this.rooms.set(room.roomToken, room);
|
||||
yield LoopRooms.promise("get", room.roomToken);
|
||||
// When a version is specified, all the data is already provided by this
|
||||
// request.
|
||||
if (version) {
|
||||
eventEmitter.emit("update", room);
|
||||
eventEmitter.emit("update" + ":" + room.roomToken, room);
|
||||
} else {
|
||||
// Next, request the detailed information for each room. If the request
|
||||
// fails the room data will not be added to the map.
|
||||
yield LoopRooms.promise("get", room.roomToken);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the 'dirty' flag back to FALSE, since the list is as fresh as can be now.
|
||||
|
@ -113,25 +180,35 @@ let LoopRoomsInternal = {
|
|||
get: function(roomToken, callback) {
|
||||
let room = this.rooms.has(roomToken) ? this.rooms.get(roomToken) : {};
|
||||
// Check if we need to make a request to the server to collect more room data.
|
||||
if (!room || gDirty || !("participants" in room)) {
|
||||
let sessionType = MozLoopService.userProfile ? LOOP_SESSION_TYPE.FXA :
|
||||
LOOP_SESSION_TYPE.GUEST;
|
||||
MozLoopService.hawkRequest(sessionType, "/rooms/" + encodeURIComponent(roomToken), "GET")
|
||||
.then(response => {
|
||||
let eventName = ("roomToken" in room) ? "add" : "update";
|
||||
extend(room, JSON.parse(response.body));
|
||||
// Remove the `currSize` for posterity.
|
||||
if ("currSize" in room) {
|
||||
delete room.currSize;
|
||||
}
|
||||
this.rooms.set(roomToken, room);
|
||||
|
||||
eventEmitter.emit(eventName, room);
|
||||
callback(null, room);
|
||||
}, err => callback(err)).catch(err => callback(err));
|
||||
} else {
|
||||
let needsUpdate = !("participants" in room);
|
||||
if (!gDirty && !needsUpdate) {
|
||||
// Dirty flag is not set AND the necessary data is available, so we can
|
||||
// simply return the room.
|
||||
callback(null, room);
|
||||
return;
|
||||
}
|
||||
|
||||
let sessionType = MozLoopService.userProfile ? LOOP_SESSION_TYPE.FXA :
|
||||
LOOP_SESSION_TYPE.GUEST;
|
||||
MozLoopService.hawkRequest(sessionType, "/rooms/" + encodeURIComponent(roomToken), "GET")
|
||||
.then(response => {
|
||||
let data = JSON.parse(response.body);
|
||||
|
||||
room.roomToken = roomToken;
|
||||
checkForParticipantsUpdate(room, data);
|
||||
extend(room, data);
|
||||
|
||||
// Remove the `currSize` for posterity.
|
||||
if ("currSize" in room) {
|
||||
delete room.currSize;
|
||||
}
|
||||
this.rooms.set(roomToken, room);
|
||||
|
||||
let eventName = !needsUpdate ? "update" : "add";
|
||||
eventEmitter.emit(eventName, room);
|
||||
eventEmitter.emit(eventName + ":" + roomToken, room);
|
||||
callback(null, room);
|
||||
}, err => callback(err)).catch(err => callback(err));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -166,6 +243,15 @@ let LoopRoomsInternal = {
|
|||
}, error => callback(error)).catch(error => callback(error));
|
||||
},
|
||||
|
||||
open: function(roomToken) {
|
||||
let windowData = {
|
||||
roomToken: roomToken,
|
||||
type: "room"
|
||||
};
|
||||
|
||||
MozLoopService.openChatWindow(windowData);
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback used to indicate changes to rooms data on the LoopServer.
|
||||
*
|
||||
|
@ -185,10 +271,13 @@ Object.freeze(LoopRoomsInternal);
|
|||
* LoopRooms implements the EventEmitter interface by exposing three methods -
|
||||
* `on`, `once` and `off` - to subscribe to events.
|
||||
* At this point the following events may be subscribed to:
|
||||
* - 'add': A new room object was successfully added to the data store.
|
||||
* - 'remove': A room was successfully removed from the data store.
|
||||
* - 'update': A room object was successfully updated with changed
|
||||
* properties in the data store.
|
||||
* - 'add[:{room-id}]': A new room object was successfully added to the data
|
||||
* store.
|
||||
* - 'remove[:{room-id}]': A room was successfully removed from the data store.
|
||||
* - 'update[:{room-id}]': A room object was successfully updated with changed
|
||||
* properties in the data store.
|
||||
* - 'joined[:{room-id}]': A participant joined a room.
|
||||
* - 'left[:{room-id}]': A participant left a room.
|
||||
*
|
||||
* See the internal code for the API documentation.
|
||||
*/
|
||||
|
@ -205,6 +294,10 @@ this.LoopRooms = {
|
|||
return LoopRoomsInternal.create(options, callback);
|
||||
},
|
||||
|
||||
open: function(roomToken) {
|
||||
return LoopRoomsInternal.open(roomToken);
|
||||
},
|
||||
|
||||
promise: function(method, ...params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this[method](...params, (error, result) => {
|
||||
|
|
|
@ -104,10 +104,21 @@ const injectObjectAPI = function(api, targetWindow) {
|
|||
// through the priv => unpriv barrier with `Cu.cloneInto()`.
|
||||
Object.keys(api).forEach(func => {
|
||||
injectedAPI[func] = function(...params) {
|
||||
let callback = params.pop();
|
||||
api[func](...params, function(...results) {
|
||||
callback(...[cloneValueInto(r, targetWindow) for (r of results)]);
|
||||
});
|
||||
let lastParam = params.pop();
|
||||
|
||||
// If the last parameter is a function, assume its a callback
|
||||
// and wrap it differently.
|
||||
if (lastParam && typeof lastParam === "function") {
|
||||
api[func](...params, function(...results) {
|
||||
lastParam(...[cloneValueInto(r, targetWindow) for (r of results)]);
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
return cloneValueInto(api[func](...params, lastParam), targetWindow);
|
||||
} catch (ex) {
|
||||
return cloneValueInto(ex, targetWindow);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -135,6 +146,7 @@ function injectLoopAPI(targetWindow) {
|
|||
let appVersionInfo;
|
||||
let contactsAPI;
|
||||
let roomsAPI;
|
||||
let callsAPI;
|
||||
|
||||
let api = {
|
||||
/**
|
||||
|
@ -206,34 +218,20 @@ function injectLoopAPI(targetWindow) {
|
|||
},
|
||||
|
||||
/**
|
||||
* Returns the callData for a specific conversation window id.
|
||||
* Returns the window data for a specific conversation window id.
|
||||
*
|
||||
* The data was retrieved from the LoopServer via a GET/calls/<version> request
|
||||
* triggered by an incoming message from the LoopPushServer.
|
||||
* This data will be relevant to the type of window, e.g. rooms or calls.
|
||||
* See LoopRooms or LoopCalls for more information.
|
||||
*
|
||||
* @param {Number} conversationWindowId
|
||||
* @returns {callData} The callData or undefined if error.
|
||||
* @param {String} conversationWindowId
|
||||
* @returns {Object} The window data or null if error.
|
||||
*/
|
||||
getCallData: {
|
||||
getConversationWindowData: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: function(conversationWindowId) {
|
||||
return Cu.cloneInto(LoopCalls.getCallData(conversationWindowId), targetWindow);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Releases the callData for a specific conversation window id.
|
||||
*
|
||||
* The result of this call will be a free call session slot.
|
||||
*
|
||||
* @param {Number} conversationWindowId
|
||||
*/
|
||||
releaseCallData: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: function(conversationWindowId) {
|
||||
LoopCalls.releaseCallData(conversationWindowId);
|
||||
return Cu.cloneInto(MozLoopService.getConversationWindowData(conversationWindowId),
|
||||
targetWindow);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -273,6 +271,22 @@ function injectLoopAPI(targetWindow) {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the calls API.
|
||||
*
|
||||
* @returns {Object} The rooms API object
|
||||
*/
|
||||
calls: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
if (callsAPI) {
|
||||
return callsAPI;
|
||||
}
|
||||
|
||||
return callsAPI = injectObjectAPI(LoopCalls, targetWindow);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Import a list of (new) contacts from an external data source.
|
||||
*
|
||||
|
@ -670,21 +684,6 @@ function injectLoopAPI(targetWindow) {
|
|||
return MozLoopService.generateUUID();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts a direct call to the contact addresses.
|
||||
*
|
||||
* @param {Object} contact The contact to call
|
||||
* @param {String} callType The type of call, e.g. "audio-video" or "audio-only"
|
||||
* @return true if the call is opened, false if it is not opened (i.e. busy)
|
||||
*/
|
||||
startDirectCall: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: function(contact, callType) {
|
||||
LoopCalls.startDirectCall(contact, callType);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function onStatusChanged(aSubject, aTopic, aData) {
|
||||
|
|
|
@ -116,6 +116,8 @@ let gFxAEnabled = true;
|
|||
let gFxAOAuthClientPromise = null;
|
||||
let gFxAOAuthClient = null;
|
||||
let gErrors = new Map();
|
||||
let gLastWindowId = 0;
|
||||
let gConversationWindowData = new Map();
|
||||
|
||||
/**
|
||||
* Internal helper methods and state
|
||||
|
@ -693,15 +695,20 @@ let MozLoopServiceInternal = {
|
|||
/**
|
||||
* Opens the chat window
|
||||
*
|
||||
* @param {Object} contentWindow The window to open the chat window in, may
|
||||
* be null.
|
||||
* @param {String} title The title of the chat window.
|
||||
* @param {String} url The page to load in the chat window.
|
||||
* @param {Object} conversationWindowData The data to be obtained by the
|
||||
* window when it opens.
|
||||
* @returns {Number} The id of the window.
|
||||
*/
|
||||
openChatWindow: function(contentWindow, title, url) {
|
||||
openChatWindow: function(conversationWindowData) {
|
||||
// So I guess the origin is the loop server!?
|
||||
let origin = this.loopServerUri;
|
||||
url = url.spec || url;
|
||||
let windowId = gLastWindowId++;
|
||||
// Store the id as a string, as that's what we use elsewhere.
|
||||
windowId = windowId.toString();
|
||||
|
||||
gConversationWindowData.set(windowId, conversationWindowData);
|
||||
|
||||
let url = "about:loopconversation#" + windowId;
|
||||
|
||||
let callback = chatbox => {
|
||||
// We need to use DOMContentLoaded as otherwise the injection will happen
|
||||
|
@ -749,7 +756,8 @@ let MozLoopServiceInternal = {
|
|||
}.bind(this), true);
|
||||
};
|
||||
|
||||
Chat.open(contentWindow, origin, title, url, undefined, undefined, callback);
|
||||
Chat.open(null, origin, "", url, undefined, undefined, callback);
|
||||
return windowId;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -996,13 +1004,12 @@ this.MozLoopService = {
|
|||
/**
|
||||
* Opens the chat window
|
||||
*
|
||||
* @param {Object} contentWindow The window to open the chat window in, may
|
||||
* be null.
|
||||
* @param {String} title The title of the chat window.
|
||||
* @param {String} url The page to load in the chat window.
|
||||
* @param {Object} conversationWindowData The data to be obtained by the
|
||||
* window when it opens.
|
||||
* @returns {Number} The id of the window.
|
||||
*/
|
||||
openChatWindow: function(contentWindow, title, url) {
|
||||
MozLoopServiceInternal.openChatWindow(contentWindow, title, url);
|
||||
openChatWindow: function(conversationWindowData) {
|
||||
return MozLoopServiceInternal.openChatWindow(conversationWindowData);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1416,4 +1423,24 @@ this.MozLoopService = {
|
|||
return MozLoopServiceInternal.hawkRequest(sessionType, path, method, payloadObj).catch(
|
||||
error => {MozLoopServiceInternal._hawkRequestError(error);});
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the window data for a specific conversation window id.
|
||||
*
|
||||
* This data will be relevant to the type of window, e.g. rooms or calls.
|
||||
* See LoopRooms or LoopCalls for more information.
|
||||
*
|
||||
* @param {String} conversationWindowId
|
||||
* @returns {Object} The window data or null if error.
|
||||
*/
|
||||
getConversationWindowData: function(conversationWindowId) {
|
||||
if (gConversationWindowData.has(conversationWindowId)) {
|
||||
var conversationData = gConversationWindowData.get(conversationWindowId);
|
||||
gConversationWindowData.delete(conversationWindowId);
|
||||
return conversationData;
|
||||
}
|
||||
|
||||
log.error("Window data was already fetched before. Possible race condition!");
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
<script type="text/javascript" src="loop/shared/js/localRoomStore.js"></script>
|
||||
<script type="text/javascript" src="loop/js/conversationViews.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/websocket.js"></script>
|
||||
<script type="text/javascript" src="loop/js/conversationAppStore.js"></script>
|
||||
<script type="text/javascript" src="loop/js/client.js"></script>
|
||||
<script type="text/javascript" src="loop/js/conversationViews.js"></script>
|
||||
<script type="text/javascript" src="loop/js/roomViews.js"></script>
|
||||
|
|
|
@ -392,12 +392,12 @@ loop.contacts = (function(_, mozL10n) {
|
|||
break;
|
||||
case "video-call":
|
||||
if (!contact.blocked) {
|
||||
navigator.mozLoop.startDirectCall(contact, CALL_TYPES.AUDIO_VIDEO);
|
||||
navigator.mozLoop.calls.startDirectCall(contact, CALL_TYPES.AUDIO_VIDEO);
|
||||
}
|
||||
break;
|
||||
case "audio-call":
|
||||
if (!contact.blocked) {
|
||||
navigator.mozLoop.startDirectCall(contact, CALL_TYPES.AUDIO_ONLY);
|
||||
navigator.mozLoop.calls.startDirectCall(contact, CALL_TYPES.AUDIO_ONLY);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -392,12 +392,12 @@ loop.contacts = (function(_, mozL10n) {
|
|||
break;
|
||||
case "video-call":
|
||||
if (!contact.blocked) {
|
||||
navigator.mozLoop.startDirectCall(contact, CALL_TYPES.AUDIO_VIDEO);
|
||||
navigator.mozLoop.calls.startDirectCall(contact, CALL_TYPES.AUDIO_VIDEO);
|
||||
}
|
||||
break;
|
||||
case "audio-call":
|
||||
if (!contact.blocked) {
|
||||
navigator.mozLoop.startDirectCall(contact, CALL_TYPES.AUDIO_ONLY);
|
||||
navigator.mozLoop.calls.startDirectCall(contact, CALL_TYPES.AUDIO_ONLY);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -179,12 +179,12 @@ loop.conversation = (function(mozL10n) {
|
|||
});
|
||||
|
||||
/**
|
||||
* Incoming Call failed view. Displayed when a call fails.
|
||||
* Something went wrong view. Displayed when there's a big problem.
|
||||
*
|
||||
* XXX Based on CallFailedView, but built specially until we flux-ify the
|
||||
* incoming call views (bug 1088672).
|
||||
*/
|
||||
var IncomingCallFailedView = React.createClass({displayName: 'IncomingCallFailedView',
|
||||
var GenericFailureView = React.createClass({displayName: 'GenericFailureView',
|
||||
propTypes: {
|
||||
cancelCall: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
@ -218,7 +218,9 @@ loop.conversation = (function(mozL10n) {
|
|||
client: React.PropTypes.instanceOf(loop.Client).isRequired,
|
||||
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
sdk: React.PropTypes.object.isRequired
|
||||
sdk: React.PropTypes.object.isRequired,
|
||||
conversationAppStore: React.PropTypes.instanceOf(
|
||||
loop.store.ConversationAppStore).isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
|
@ -283,7 +285,7 @@ loop.conversation = (function(mozL10n) {
|
|||
case "end": {
|
||||
// XXX To be handled with the "failed" view state when bug 1047410 lands
|
||||
if (this.state.callFailed) {
|
||||
return IncomingCallFailedView({
|
||||
return GenericFailureView({
|
||||
cancelCall: this.closeWindow.bind(this)}
|
||||
)
|
||||
}
|
||||
|
@ -352,13 +354,9 @@ loop.conversation = (function(mozL10n) {
|
|||
setupIncomingCall: function() {
|
||||
navigator.mozLoop.startAlerting();
|
||||
|
||||
var callData = navigator.mozLoop.getCallData(this.props.conversation.get("windowId"));
|
||||
if (!callData) {
|
||||
// XXX Not the ideal response, but bug 1047410 will be replacing
|
||||
// this by better "call failed" UI.
|
||||
console.error("Failed to get the call data");
|
||||
return;
|
||||
}
|
||||
// XXX This is a hack until we rework for the flux model in bug 1088672.
|
||||
var callData = this.props.conversationAppStore.getStoreState().windowData;
|
||||
|
||||
this.props.conversation.setIncomingSessionData(callData);
|
||||
this._setupWebSocket();
|
||||
},
|
||||
|
@ -374,7 +372,8 @@ loop.conversation = (function(mozL10n) {
|
|||
* Moves the call to the end state
|
||||
*/
|
||||
endCall: function() {
|
||||
navigator.mozLoop.releaseCallData(this.props.conversation.get("windowId"));
|
||||
navigator.mozLoop.calls.clearCallInProgress(
|
||||
this.props.conversation.get("windowId"));
|
||||
this.setState({callStatus: "end"});
|
||||
},
|
||||
|
||||
|
@ -475,7 +474,8 @@ loop.conversation = (function(mozL10n) {
|
|||
*/
|
||||
_declineCall: function() {
|
||||
this._websocket.decline();
|
||||
navigator.mozLoop.releaseCallData(this.props.conversation.get("windowId"));
|
||||
navigator.mozLoop.calls.clearCallInProgress(
|
||||
this.props.conversation.get("windowId"));
|
||||
this._websocket.close();
|
||||
// Having a timeout here lets the logging for the websocket complete and be
|
||||
// displayed on the console if both are on.
|
||||
|
@ -523,6 +523,8 @@ loop.conversation = (function(mozL10n) {
|
|||
* in progress, and hence, which view to display.
|
||||
*/
|
||||
var AppControllerView = React.createClass({displayName: 'AppControllerView',
|
||||
mixins: [Backbone.Events],
|
||||
|
||||
propTypes: {
|
||||
// XXX Old types required for incoming call view.
|
||||
client: React.PropTypes.instanceOf(loop.Client).isRequired,
|
||||
|
@ -530,51 +532,66 @@ loop.conversation = (function(mozL10n) {
|
|||
.isRequired,
|
||||
sdk: React.PropTypes.object.isRequired,
|
||||
|
||||
// XXX New types for OutgoingConversationView
|
||||
store: React.PropTypes.instanceOf(loop.store.ConversationStore).isRequired,
|
||||
// XXX New types for flux style
|
||||
conversationAppStore: React.PropTypes.instanceOf(
|
||||
loop.store.ConversationAppStore).isRequired,
|
||||
conversationStore: React.PropTypes.instanceOf(loop.store.ConversationStore)
|
||||
.isRequired,
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
|
||||
// if not passed, this is not a room view
|
||||
localRoomStore: React.PropTypes.instanceOf(loop.store.LocalRoomStore)
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return this.props.store.attributes;
|
||||
return this.props.conversationAppStore.getStoreState();
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.props.store.on("change:outgoing", function() {
|
||||
this.setState(this.props.store.attributes);
|
||||
this.listenTo(this.props.conversationAppStore, "change", function() {
|
||||
this.setState(this.props.conversationAppStore.getStoreState());
|
||||
}, this);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
this.stopListening(this.props.conversationAppStore);
|
||||
},
|
||||
|
||||
closeWindow: function() {
|
||||
window.close();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (this.props.localRoomStore) {
|
||||
return (
|
||||
EmptyRoomView({
|
||||
switch(this.state.windowType) {
|
||||
case "incoming": {
|
||||
return (IncomingConversationView({
|
||||
client: this.props.client,
|
||||
conversation: this.props.conversation,
|
||||
sdk: this.props.sdk,
|
||||
conversationAppStore: this.props.conversationAppStore}
|
||||
));
|
||||
}
|
||||
case "outgoing": {
|
||||
return (OutgoingConversationView({
|
||||
store: this.props.conversationStore,
|
||||
dispatcher: this.props.dispatcher}
|
||||
));
|
||||
}
|
||||
case "room": {
|
||||
return (EmptyRoomView({
|
||||
mozLoop: navigator.mozLoop,
|
||||
localRoomStore: this.props.localRoomStore}
|
||||
)
|
||||
);
|
||||
));
|
||||
}
|
||||
case "failed": {
|
||||
return (GenericFailureView({
|
||||
cancelCall: this.closeWindow}
|
||||
));
|
||||
}
|
||||
default: {
|
||||
// If we don't have a windowType, we don't know what we are yet,
|
||||
// so don't display anything.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't display anything, until we know what type of call we are.
|
||||
if (this.state.outgoing === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.state.outgoing) {
|
||||
return (OutgoingConversationView({
|
||||
store: this.props.store,
|
||||
dispatcher: this.props.dispatcher}
|
||||
));
|
||||
}
|
||||
|
||||
return (IncomingConversationView({
|
||||
client: this.props.client,
|
||||
conversation: this.props.conversation,
|
||||
sdk: this.props.sdk}
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -605,11 +622,20 @@ loop.conversation = (function(mozL10n) {
|
|||
sdk: OT
|
||||
});
|
||||
|
||||
// Create the stores.
|
||||
var conversationAppStore = new loop.store.ConversationAppStore({
|
||||
dispatcher: dispatcher,
|
||||
mozLoop: navigator.mozLoop
|
||||
});
|
||||
var conversationStore = new loop.store.ConversationStore({}, {
|
||||
client: client,
|
||||
dispatcher: dispatcher,
|
||||
sdkDriver: sdkDriver
|
||||
});
|
||||
var localRoomStore = new loop.store.LocalRoomStore({
|
||||
dispatcher: dispatcher,
|
||||
mozLoop: navigator.mozLoop
|
||||
});;
|
||||
|
||||
// XXX Old class creation for the incoming conversation view, whilst
|
||||
// we transition across (bug 1072323).
|
||||
|
@ -622,57 +648,31 @@ loop.conversation = (function(mozL10n) {
|
|||
var helper = new loop.shared.utils.Helper();
|
||||
var locationHash = helper.locationData().hash;
|
||||
var windowId;
|
||||
var outgoing;
|
||||
var localRoomStore;
|
||||
|
||||
// XXX removeMe, along with noisy comment at the beginning of
|
||||
// conversation_test.js "when locationHash begins with #room".
|
||||
if (navigator.mozLoop.getLoopBoolPref("test.alwaysUseRooms")) {
|
||||
locationHash = "#room/32";
|
||||
}
|
||||
|
||||
var hash = locationHash.match(/#incoming\/(.*)/);
|
||||
var hash = locationHash.match(/#(.*)/);
|
||||
if (hash) {
|
||||
windowId = hash[1];
|
||||
outgoing = false;
|
||||
} else if (hash = locationHash.match(/#room\/(.*)/)) {
|
||||
localRoomStore = new loop.store.LocalRoomStore({
|
||||
dispatcher: dispatcher,
|
||||
mozLoop: navigator.mozLoop
|
||||
});
|
||||
} else {
|
||||
hash = locationHash.match(/#outgoing\/(.*)/);
|
||||
if (hash) {
|
||||
windowId = hash[1];
|
||||
outgoing = true;
|
||||
}
|
||||
}
|
||||
|
||||
conversation.set({windowId: windowId});
|
||||
|
||||
window.addEventListener("unload", function(event) {
|
||||
// Handle direct close of dialog box via [x] control.
|
||||
navigator.mozLoop.releaseCallData(windowId);
|
||||
navigator.mozLoop.calls.clearCallInProgress(windowId);
|
||||
});
|
||||
|
||||
React.renderComponent(AppControllerView({
|
||||
conversationAppStore: conversationAppStore,
|
||||
localRoomStore: localRoomStore,
|
||||
store: conversationStore,
|
||||
conversationStore: conversationStore,
|
||||
client: client,
|
||||
conversation: conversation,
|
||||
dispatcher: dispatcher,
|
||||
sdk: window.OT}
|
||||
), document.querySelector('#main'));
|
||||
|
||||
if (localRoomStore) {
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.SetupEmptyRoom({localRoomId: hash[1]}));
|
||||
return;
|
||||
}
|
||||
|
||||
dispatcher.dispatch(new loop.shared.actions.GatherCallData({
|
||||
windowId: windowId,
|
||||
outgoing: outgoing
|
||||
dispatcher.dispatch(new sharedActions.GetWindowData({
|
||||
windowId: windowId
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -680,7 +680,7 @@ loop.conversation = (function(mozL10n) {
|
|||
AppControllerView: AppControllerView,
|
||||
IncomingConversationView: IncomingConversationView,
|
||||
IncomingCallView: IncomingCallView,
|
||||
IncomingCallFailedView: IncomingCallFailedView,
|
||||
GenericFailureView: GenericFailureView,
|
||||
init: init
|
||||
};
|
||||
})(document.mozL10n);
|
||||
|
|
|
@ -179,12 +179,12 @@ loop.conversation = (function(mozL10n) {
|
|||
});
|
||||
|
||||
/**
|
||||
* Incoming Call failed view. Displayed when a call fails.
|
||||
* Something went wrong view. Displayed when there's a big problem.
|
||||
*
|
||||
* XXX Based on CallFailedView, but built specially until we flux-ify the
|
||||
* incoming call views (bug 1088672).
|
||||
*/
|
||||
var IncomingCallFailedView = React.createClass({
|
||||
var GenericFailureView = React.createClass({
|
||||
propTypes: {
|
||||
cancelCall: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
@ -218,7 +218,9 @@ loop.conversation = (function(mozL10n) {
|
|||
client: React.PropTypes.instanceOf(loop.Client).isRequired,
|
||||
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
sdk: React.PropTypes.object.isRequired
|
||||
sdk: React.PropTypes.object.isRequired,
|
||||
conversationAppStore: React.PropTypes.instanceOf(
|
||||
loop.store.ConversationAppStore).isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
|
@ -283,7 +285,7 @@ loop.conversation = (function(mozL10n) {
|
|||
case "end": {
|
||||
// XXX To be handled with the "failed" view state when bug 1047410 lands
|
||||
if (this.state.callFailed) {
|
||||
return <IncomingCallFailedView
|
||||
return <GenericFailureView
|
||||
cancelCall={this.closeWindow.bind(this)}
|
||||
/>
|
||||
}
|
||||
|
@ -352,13 +354,9 @@ loop.conversation = (function(mozL10n) {
|
|||
setupIncomingCall: function() {
|
||||
navigator.mozLoop.startAlerting();
|
||||
|
||||
var callData = navigator.mozLoop.getCallData(this.props.conversation.get("windowId"));
|
||||
if (!callData) {
|
||||
// XXX Not the ideal response, but bug 1047410 will be replacing
|
||||
// this by better "call failed" UI.
|
||||
console.error("Failed to get the call data");
|
||||
return;
|
||||
}
|
||||
// XXX This is a hack until we rework for the flux model in bug 1088672.
|
||||
var callData = this.props.conversationAppStore.getStoreState().windowData;
|
||||
|
||||
this.props.conversation.setIncomingSessionData(callData);
|
||||
this._setupWebSocket();
|
||||
},
|
||||
|
@ -374,7 +372,8 @@ loop.conversation = (function(mozL10n) {
|
|||
* Moves the call to the end state
|
||||
*/
|
||||
endCall: function() {
|
||||
navigator.mozLoop.releaseCallData(this.props.conversation.get("windowId"));
|
||||
navigator.mozLoop.calls.clearCallInProgress(
|
||||
this.props.conversation.get("windowId"));
|
||||
this.setState({callStatus: "end"});
|
||||
},
|
||||
|
||||
|
@ -475,7 +474,8 @@ loop.conversation = (function(mozL10n) {
|
|||
*/
|
||||
_declineCall: function() {
|
||||
this._websocket.decline();
|
||||
navigator.mozLoop.releaseCallData(this.props.conversation.get("windowId"));
|
||||
navigator.mozLoop.calls.clearCallInProgress(
|
||||
this.props.conversation.get("windowId"));
|
||||
this._websocket.close();
|
||||
// Having a timeout here lets the logging for the websocket complete and be
|
||||
// displayed on the console if both are on.
|
||||
|
@ -523,6 +523,8 @@ loop.conversation = (function(mozL10n) {
|
|||
* in progress, and hence, which view to display.
|
||||
*/
|
||||
var AppControllerView = React.createClass({
|
||||
mixins: [Backbone.Events],
|
||||
|
||||
propTypes: {
|
||||
// XXX Old types required for incoming call view.
|
||||
client: React.PropTypes.instanceOf(loop.Client).isRequired,
|
||||
|
@ -530,51 +532,66 @@ loop.conversation = (function(mozL10n) {
|
|||
.isRequired,
|
||||
sdk: React.PropTypes.object.isRequired,
|
||||
|
||||
// XXX New types for OutgoingConversationView
|
||||
store: React.PropTypes.instanceOf(loop.store.ConversationStore).isRequired,
|
||||
// XXX New types for flux style
|
||||
conversationAppStore: React.PropTypes.instanceOf(
|
||||
loop.store.ConversationAppStore).isRequired,
|
||||
conversationStore: React.PropTypes.instanceOf(loop.store.ConversationStore)
|
||||
.isRequired,
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
|
||||
// if not passed, this is not a room view
|
||||
localRoomStore: React.PropTypes.instanceOf(loop.store.LocalRoomStore)
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return this.props.store.attributes;
|
||||
return this.props.conversationAppStore.getStoreState();
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.props.store.on("change:outgoing", function() {
|
||||
this.setState(this.props.store.attributes);
|
||||
this.listenTo(this.props.conversationAppStore, "change", function() {
|
||||
this.setState(this.props.conversationAppStore.getStoreState());
|
||||
}, this);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
this.stopListening(this.props.conversationAppStore);
|
||||
},
|
||||
|
||||
closeWindow: function() {
|
||||
window.close();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (this.props.localRoomStore) {
|
||||
return (
|
||||
<EmptyRoomView
|
||||
switch(this.state.windowType) {
|
||||
case "incoming": {
|
||||
return (<IncomingConversationView
|
||||
client={this.props.client}
|
||||
conversation={this.props.conversation}
|
||||
sdk={this.props.sdk}
|
||||
conversationAppStore={this.props.conversationAppStore}
|
||||
/>);
|
||||
}
|
||||
case "outgoing": {
|
||||
return (<OutgoingConversationView
|
||||
store={this.props.conversationStore}
|
||||
dispatcher={this.props.dispatcher}
|
||||
/>);
|
||||
}
|
||||
case "room": {
|
||||
return (<EmptyRoomView
|
||||
mozLoop={navigator.mozLoop}
|
||||
localRoomStore={this.props.localRoomStore}
|
||||
/>
|
||||
);
|
||||
/>);
|
||||
}
|
||||
case "failed": {
|
||||
return (<GenericFailureView
|
||||
cancelCall={this.closeWindow}
|
||||
/>);
|
||||
}
|
||||
default: {
|
||||
// If we don't have a windowType, we don't know what we are yet,
|
||||
// so don't display anything.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't display anything, until we know what type of call we are.
|
||||
if (this.state.outgoing === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.state.outgoing) {
|
||||
return (<OutgoingConversationView
|
||||
store={this.props.store}
|
||||
dispatcher={this.props.dispatcher}
|
||||
/>);
|
||||
}
|
||||
|
||||
return (<IncomingConversationView
|
||||
client={this.props.client}
|
||||
conversation={this.props.conversation}
|
||||
sdk={this.props.sdk}
|
||||
/>);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -605,11 +622,20 @@ loop.conversation = (function(mozL10n) {
|
|||
sdk: OT
|
||||
});
|
||||
|
||||
// Create the stores.
|
||||
var conversationAppStore = new loop.store.ConversationAppStore({
|
||||
dispatcher: dispatcher,
|
||||
mozLoop: navigator.mozLoop
|
||||
});
|
||||
var conversationStore = new loop.store.ConversationStore({}, {
|
||||
client: client,
|
||||
dispatcher: dispatcher,
|
||||
sdkDriver: sdkDriver
|
||||
});
|
||||
var localRoomStore = new loop.store.LocalRoomStore({
|
||||
dispatcher: dispatcher,
|
||||
mozLoop: navigator.mozLoop
|
||||
});;
|
||||
|
||||
// XXX Old class creation for the incoming conversation view, whilst
|
||||
// we transition across (bug 1072323).
|
||||
|
@ -622,57 +648,31 @@ loop.conversation = (function(mozL10n) {
|
|||
var helper = new loop.shared.utils.Helper();
|
||||
var locationHash = helper.locationData().hash;
|
||||
var windowId;
|
||||
var outgoing;
|
||||
var localRoomStore;
|
||||
|
||||
// XXX removeMe, along with noisy comment at the beginning of
|
||||
// conversation_test.js "when locationHash begins with #room".
|
||||
if (navigator.mozLoop.getLoopBoolPref("test.alwaysUseRooms")) {
|
||||
locationHash = "#room/32";
|
||||
}
|
||||
|
||||
var hash = locationHash.match(/#incoming\/(.*)/);
|
||||
var hash = locationHash.match(/#(.*)/);
|
||||
if (hash) {
|
||||
windowId = hash[1];
|
||||
outgoing = false;
|
||||
} else if (hash = locationHash.match(/#room\/(.*)/)) {
|
||||
localRoomStore = new loop.store.LocalRoomStore({
|
||||
dispatcher: dispatcher,
|
||||
mozLoop: navigator.mozLoop
|
||||
});
|
||||
} else {
|
||||
hash = locationHash.match(/#outgoing\/(.*)/);
|
||||
if (hash) {
|
||||
windowId = hash[1];
|
||||
outgoing = true;
|
||||
}
|
||||
}
|
||||
|
||||
conversation.set({windowId: windowId});
|
||||
|
||||
window.addEventListener("unload", function(event) {
|
||||
// Handle direct close of dialog box via [x] control.
|
||||
navigator.mozLoop.releaseCallData(windowId);
|
||||
navigator.mozLoop.calls.clearCallInProgress(windowId);
|
||||
});
|
||||
|
||||
React.renderComponent(<AppControllerView
|
||||
conversationAppStore={conversationAppStore}
|
||||
localRoomStore={localRoomStore}
|
||||
store={conversationStore}
|
||||
conversationStore={conversationStore}
|
||||
client={client}
|
||||
conversation={conversation}
|
||||
dispatcher={dispatcher}
|
||||
sdk={window.OT}
|
||||
/>, document.querySelector('#main'));
|
||||
|
||||
if (localRoomStore) {
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.SetupEmptyRoom({localRoomId: hash[1]}));
|
||||
return;
|
||||
}
|
||||
|
||||
dispatcher.dispatch(new loop.shared.actions.GatherCallData({
|
||||
windowId: windowId,
|
||||
outgoing: outgoing
|
||||
dispatcher.dispatch(new sharedActions.GetWindowData({
|
||||
windowId: windowId
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -680,7 +680,7 @@ loop.conversation = (function(mozL10n) {
|
|||
AppControllerView: AppControllerView,
|
||||
IncomingConversationView: IncomingConversationView,
|
||||
IncomingCallView: IncomingCallView,
|
||||
IncomingCallFailedView: IncomingCallFailedView,
|
||||
GenericFailureView: GenericFailureView,
|
||||
init: init
|
||||
};
|
||||
})(document.mozL10n);
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
/* 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/. */
|
||||
|
||||
/* global loop:true */
|
||||
|
||||
var loop = loop || {};
|
||||
loop.store = loop.store || {};
|
||||
|
||||
/**
|
||||
* Manages the conversation window app controller view. Used to get
|
||||
* the window data and store the window type.
|
||||
*/
|
||||
loop.store.ConversationAppStore = (function() {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param {Object} options Options for the store. Should contain the dispatcher.
|
||||
*/
|
||||
var ConversationAppStore = function(options) {
|
||||
if (!options.dispatcher) {
|
||||
throw new Error("Missing option dispatcher");
|
||||
}
|
||||
if (!options.mozLoop) {
|
||||
throw new Error("Missing option mozLoop");
|
||||
}
|
||||
|
||||
this._dispatcher = options.dispatcher;
|
||||
this._mozLoop = options.mozLoop;
|
||||
this._storeState = {};
|
||||
|
||||
this._dispatcher.register(this, [
|
||||
"getWindowData"
|
||||
]);
|
||||
};
|
||||
|
||||
ConversationAppStore.prototype = _.extend({
|
||||
/**
|
||||
* Retrieves current store state.
|
||||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
getStoreState: function() {
|
||||
return this._storeState;
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates store states and trigger a "change" event.
|
||||
*
|
||||
* @param {Object} state The new store state.
|
||||
*/
|
||||
setStoreState: function(state) {
|
||||
this._storeState = state;
|
||||
this.trigger("change");
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the get window data action - obtains the window data,
|
||||
* updates the store and notifies interested components.
|
||||
*
|
||||
* @param {sharedActions.GetWindowData} actionData The action data
|
||||
*/
|
||||
getWindowData: function(actionData) {
|
||||
var windowData = this._mozLoop.getConversationWindowData(actionData.windowId);
|
||||
|
||||
if (!windowData) {
|
||||
console.error("Failed to get the window data");
|
||||
this.setStoreState({windowType: "failed"});
|
||||
return;
|
||||
}
|
||||
|
||||
// XXX windowData is a hack for the IncomingConversationView until
|
||||
// we rework it for the flux model in bug 1088672.
|
||||
this.setStoreState({
|
||||
windowType: windowData.type,
|
||||
windowData: windowData
|
||||
});
|
||||
|
||||
this._dispatcher.dispatch(new loop.shared.actions.SetupWindowData(_.extend({
|
||||
windowId: actionData.windowId}, windowData)));
|
||||
}
|
||||
}, Backbone.Events);
|
||||
|
||||
return ConversationAppStore;
|
||||
|
||||
})();
|
|
@ -499,6 +499,10 @@ loop.conversationViews = (function(mozL10n) {
|
|||
case CALL_STATES.FINISHED: {
|
||||
return this._renderFeedbackView();
|
||||
}
|
||||
case CALL_STATES.INIT: {
|
||||
// We know what we are, but we haven't got the data yet.
|
||||
return null;
|
||||
}
|
||||
default: {
|
||||
return (PendingConversationView({
|
||||
dispatcher: this.props.dispatcher,
|
||||
|
|
|
@ -499,6 +499,10 @@ loop.conversationViews = (function(mozL10n) {
|
|||
case CALL_STATES.FINISHED: {
|
||||
return this._renderFeedbackView();
|
||||
}
|
||||
case CALL_STATES.INIT: {
|
||||
// We know what we are, but we haven't got the data yet.
|
||||
return null;
|
||||
}
|
||||
default: {
|
||||
return (<PendingConversationView
|
||||
dispatcher={this.props.dispatcher}
|
||||
|
|
|
@ -582,7 +582,9 @@ loop.panel = (function(_, mozL10n) {
|
|||
},
|
||||
|
||||
openRoom: function(room) {
|
||||
// XXX implement me; see bug 1074678
|
||||
this.props.dispatcher.dispatch(new sharedActions.OpenRoom({
|
||||
roomToken: room.roomToken
|
||||
}));
|
||||
},
|
||||
|
||||
render: function() {
|
||||
|
|
|
@ -582,7 +582,9 @@ loop.panel = (function(_, mozL10n) {
|
|||
},
|
||||
|
||||
openRoom: function(room) {
|
||||
// XXX implement me; see bug 1074678
|
||||
this.props.dispatcher.dispatch(new sharedActions.OpenRoom({
|
||||
roomToken: room.roomToken
|
||||
}));
|
||||
},
|
||||
|
||||
render: function() {
|
||||
|
|
|
@ -30,6 +30,27 @@ loop.shared.actions = (function() {
|
|||
};
|
||||
|
||||
return {
|
||||
/**
|
||||
* Get the window data for the provided window id
|
||||
*/
|
||||
GetWindowData: Action.define("getWindowData", {
|
||||
windowId: String
|
||||
}),
|
||||
|
||||
/**
|
||||
* Used to pass round the window data so that stores can
|
||||
* record the appropriate data.
|
||||
*/
|
||||
SetupWindowData: Action.define("setupWindowData", {
|
||||
windowId: String,
|
||||
type: String
|
||||
|
||||
// Optional Items. There are other optional items typically sent
|
||||
// around with this action. They are for the setup of calls and rooms and
|
||||
// depend on the type. See LoopCalls and LoopRooms for the details of this
|
||||
// data.
|
||||
}),
|
||||
|
||||
/**
|
||||
* Fetch a new call url from the server, intended to be sent over email when
|
||||
* a contact can't be reached.
|
||||
|
@ -37,15 +58,6 @@ loop.shared.actions = (function() {
|
|||
FetchEmailLink: Action.define("fetchEmailLink", {
|
||||
}),
|
||||
|
||||
/**
|
||||
* Used to trigger gathering of initial call data.
|
||||
*/
|
||||
GatherCallData: Action.define("gatherCallData", {
|
||||
// Specify the callId for an incoming call.
|
||||
windowId: [String, null],
|
||||
outgoing: Boolean
|
||||
}),
|
||||
|
||||
/**
|
||||
* Used to cancel call setup.
|
||||
*/
|
||||
|
@ -170,13 +182,11 @@ loop.shared.actions = (function() {
|
|||
}),
|
||||
|
||||
/**
|
||||
* Primes localRoomStore with roomLocalId, which triggers the EmptyRoomView
|
||||
* to do any necessary setup.
|
||||
*
|
||||
* XXX should move to localRoomActions module
|
||||
* Opens a room.
|
||||
* XXX: should move to some roomActions module - refs bug 1079284
|
||||
*/
|
||||
SetupEmptyRoom: Action.define("setupEmptyRoom", {
|
||||
localRoomId: String
|
||||
OpenRoom: Action.define("openRoom", {
|
||||
roomToken: String
|
||||
})
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -121,7 +121,7 @@ loop.store.ConversationStore = (function() {
|
|||
this.dispatcher.register(this, [
|
||||
"connectionFailure",
|
||||
"connectionProgress",
|
||||
"gatherCallData",
|
||||
"setupWindowData",
|
||||
"connectCall",
|
||||
"hangupCall",
|
||||
"peerHungupCall",
|
||||
|
@ -188,37 +188,23 @@ loop.store.ConversationStore = (function() {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the gather call data action, setting the state
|
||||
* and starting to get the appropriate data for the type of call.
|
||||
*
|
||||
* @param {sharedActions.GatherCallData} actionData The action data.
|
||||
*/
|
||||
gatherCallData: function(actionData) {
|
||||
if (!actionData.outgoing) {
|
||||
// XXX Other types aren't supported yet, but set the state for the
|
||||
// view selection.
|
||||
this.set({outgoing: false});
|
||||
return;
|
||||
}
|
||||
|
||||
var callData = navigator.mozLoop.getCallData(actionData.windowId);
|
||||
if (!callData) {
|
||||
console.error("Failed to get the call data");
|
||||
this.set({callState: CALL_STATES.TERMINATED});
|
||||
setupWindowData: function(actionData) {
|
||||
var windowType = actionData.type;
|
||||
if (windowType !== "outgoing" &&
|
||||
windowType !== "incoming") {
|
||||
// Not for this store, don't do anything.
|
||||
return;
|
||||
}
|
||||
|
||||
this.set({
|
||||
contact: callData.contact,
|
||||
outgoing: actionData.outgoing,
|
||||
contact: actionData.contact,
|
||||
outgoing: windowType === "outgoing",
|
||||
windowId: actionData.windowId,
|
||||
callType: callData.callType,
|
||||
callState: CALL_STATES.GATHER
|
||||
callType: actionData.callType,
|
||||
callState: CALL_STATES.GATHER,
|
||||
videoMuted: actionData.callType === CALL_TYPES.AUDIO_ONLY
|
||||
});
|
||||
|
||||
this.set({videoMuted: this.get("callType") === CALL_TYPES.AUDIO_ONLY});
|
||||
|
||||
if (this.get("outgoing")) {
|
||||
this._setupOutgoingCall();
|
||||
} // XXX Else, other types aren't supported yet.
|
||||
|
@ -330,6 +316,8 @@ loop.store.ConversationStore = (function() {
|
|||
var contactAddresses = [];
|
||||
var contact = this.get("contact");
|
||||
|
||||
navigator.mozLoop.calls.setCallInProgress(this.get("windowId"));
|
||||
|
||||
function appendContactValues(property, strip) {
|
||||
if (contact.hasOwnProperty(property)) {
|
||||
contact[property].forEach(function(item) {
|
||||
|
@ -409,7 +397,7 @@ loop.store.ConversationStore = (function() {
|
|||
delete this._websocket;
|
||||
}
|
||||
|
||||
navigator.mozLoop.releaseCallData(this.get("windowId"));
|
||||
navigator.mozLoop.calls.clearCallInProgress(this.get("windowId"));
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -36,7 +36,9 @@ loop.store.LocalRoomStore = (function() {
|
|||
}
|
||||
this.mozLoop = options.mozLoop;
|
||||
|
||||
this.dispatcher.register(this, ["setupEmptyRoom"]);
|
||||
this.dispatcher.register(this, [
|
||||
"setupWindowData"
|
||||
]);
|
||||
}
|
||||
|
||||
LocalRoomStore.prototype = _.extend({
|
||||
|
@ -69,24 +71,8 @@ loop.store.LocalRoomStore = (function() {
|
|||
},
|
||||
|
||||
/**
|
||||
* Proxy to mozLoop.rooms.getRoomData for setupEmptyRoom action.
|
||||
*
|
||||
* XXXremoveMe Can probably be removed when bug 1074664 lands.
|
||||
*
|
||||
* @param {sharedActions.setupEmptyRoom} actionData
|
||||
* @param {Function} cb Callback(error, roomData)
|
||||
*/
|
||||
_fetchRoomData: function(actionData, cb) {
|
||||
if (this.mozLoop.rooms && this.mozLoop.rooms.getRoomData) {
|
||||
this.mozLoop.rooms.getRoomData(actionData.localRoomId, cb);
|
||||
} else {
|
||||
cb(null, {roomName: "Donkeys"});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Execute setupEmptyRoom event action from the dispatcher. This primes
|
||||
* the store with the localRoomId, and calls MozLoop.getRoomData on that
|
||||
* Execute setupWindowData event action from the dispatcher. This primes
|
||||
* the store with the roomToken, and calls MozLoop.getRoomData on that
|
||||
* ID. This will return either a reflection of state on the server, or,
|
||||
* if the createRoom call hasn't yet returned, it will have at least the
|
||||
* roomName as specified to the createRoom method.
|
||||
|
@ -94,16 +80,22 @@ loop.store.LocalRoomStore = (function() {
|
|||
* When the room name gets set, that will trigger the view to display
|
||||
* that name.
|
||||
*
|
||||
* @param {sharedActions.setupEmptyRoom} actionData
|
||||
* @param {sharedActions.SetupWindowData} actionData
|
||||
*/
|
||||
setupEmptyRoom: function(actionData) {
|
||||
this._fetchRoomData(actionData, function(error, roomData) {
|
||||
this.setStoreState({
|
||||
error: error,
|
||||
localRoomId: actionData.localRoomId,
|
||||
serverData: roomData
|
||||
});
|
||||
}.bind(this));
|
||||
setupWindowData: function(actionData) {
|
||||
if (actionData.type !== "room") {
|
||||
// Nothing for us to do here, leave it to other stores.
|
||||
return;
|
||||
}
|
||||
|
||||
this.mozLoop.rooms.get(actionData.roomToken,
|
||||
function(error, roomData) {
|
||||
this.setStoreState({
|
||||
error: error,
|
||||
roomToken: actionData.roomToken,
|
||||
serverData: roomData
|
||||
});
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
}, Backbone.Events);
|
||||
|
|
|
@ -328,6 +328,15 @@ loop.store = loop.store || {};
|
|||
rooms: this._processRoomList(actionData.roomList)
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Opens a room
|
||||
*
|
||||
* @param {sharedActions.OpenRoom} actionData The action data.
|
||||
*/
|
||||
openRoom: function(actionData) {
|
||||
this._mozLoop.rooms.open(actionData.roomToken);
|
||||
}
|
||||
}, Backbone.Events);
|
||||
|
||||
loop.store.RoomListStore = RoomListStore;
|
||||
|
|
|
@ -13,6 +13,7 @@ browser.jar:
|
|||
# Desktop script
|
||||
content/browser/loop/js/client.js (content/js/client.js)
|
||||
content/browser/loop/js/conversation.js (content/js/conversation.js)
|
||||
content/browser/loop/js/conversationAppStore.js (content/js/conversationAppStore.js)
|
||||
content/browser/loop/js/otconfig.js (content/js/otconfig.js)
|
||||
content/browser/loop/js/panel.js (content/js/panel.js)
|
||||
content/browser/loop/js/contacts.js (content/js/contacts.js)
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var expect = chai.expect;
|
||||
|
||||
describe("loop.store.ConversationAppStore", function () {
|
||||
|
||||
var sharedActions = loop.shared.actions;
|
||||
var sandbox, dispatcher;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
dispatcher = new loop.Dispatcher();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe("#constructor", function() {
|
||||
it("should throw an error if the dispatcher is missing", function() {
|
||||
expect(function() {
|
||||
new loop.store.ConversationAppStore({mozLoop: {}});
|
||||
}).to.Throw(/dispatcher/);
|
||||
});
|
||||
|
||||
it("should throw an error if mozLoop is missing", function() {
|
||||
expect(function() {
|
||||
new loop.store.ConversationAppStore({dispatcher: dispatcher});
|
||||
}).to.Throw(/mozLoop/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#getWindowData", function() {
|
||||
var fakeWindowData, fakeGetWindowData, fakeMozLoop, store;
|
||||
|
||||
beforeEach(function() {
|
||||
fakeWindowData = {
|
||||
type: "incoming",
|
||||
callId: "123456"
|
||||
};
|
||||
|
||||
fakeGetWindowData = {
|
||||
windowId: "42"
|
||||
};
|
||||
|
||||
fakeMozLoop = {
|
||||
getConversationWindowData: function(windowId) {
|
||||
if (windowId === "42") {
|
||||
return fakeWindowData;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
store = new loop.store.ConversationAppStore({
|
||||
dispatcher: dispatcher,
|
||||
mozLoop: fakeMozLoop
|
||||
});
|
||||
});
|
||||
|
||||
it("should fetch the window type from the mozLoop API", function() {
|
||||
dispatcher.dispatch(new sharedActions.GetWindowData(fakeGetWindowData));
|
||||
|
||||
expect(store.getStoreState()).eql({
|
||||
windowType: "incoming",
|
||||
windowData: fakeWindowData
|
||||
});
|
||||
});
|
||||
|
||||
it("should dispatch a SetupWindowData action with the data from the mozLoop API",
|
||||
function() {
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
|
||||
store.getWindowData(new sharedActions.GetWindowData(fakeGetWindowData));
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.SetupWindowData(_.extend({
|
||||
windowId: fakeGetWindowData.windowId
|
||||
}, fakeWindowData)));
|
||||
});
|
||||
});
|
||||
|
||||
});
|
|
@ -438,10 +438,10 @@ describe("loop.conversationViews", function () {
|
|||
loop.conversationViews.CallFailedView);
|
||||
});
|
||||
|
||||
it("should render the PendingConversationView when the call state is 'init'",
|
||||
it("should render the PendingConversationView when the call state is 'gather'",
|
||||
function() {
|
||||
store.set({
|
||||
callState: CALL_STATES.INIT,
|
||||
callState: CALL_STATES.GATHER,
|
||||
contact: contact
|
||||
});
|
||||
|
||||
|
@ -474,7 +474,7 @@ describe("loop.conversationViews", function () {
|
|||
it("should update the rendered views when the state is changed.",
|
||||
function() {
|
||||
store.set({
|
||||
callState: CALL_STATES.INIT,
|
||||
callState: CALL_STATES.GATHER,
|
||||
contact: contact
|
||||
});
|
||||
|
||||
|
|
|
@ -41,8 +41,9 @@ describe("loop.conversation", function() {
|
|||
setLoopCharPref: sinon.stub(),
|
||||
getLoopCharPref: sinon.stub().returns("http://fakeurl"),
|
||||
getLoopBoolPref: sinon.stub(),
|
||||
getCallData: sinon.stub(),
|
||||
releaseCallData: sinon.stub(),
|
||||
calls: {
|
||||
clearCallInProgress: sinon.stub()
|
||||
},
|
||||
startAlerting: sinon.stub(),
|
||||
stopAlerting: sinon.stub(),
|
||||
ensureRegistered: sinon.stub(),
|
||||
|
@ -80,7 +81,7 @@ describe("loop.conversation", function() {
|
|||
|
||||
sandbox.stub(loop.shared.utils.Helper.prototype,
|
||||
"locationData").returns({
|
||||
hash: "#incoming/42",
|
||||
hash: "#42",
|
||||
pathname: "/"
|
||||
});
|
||||
|
||||
|
@ -112,86 +113,30 @@ describe("loop.conversation", function() {
|
|||
}));
|
||||
});
|
||||
|
||||
describe("when locationHash begins with #room", function () {
|
||||
// XXX must stay in sync with "test.alwaysUseRooms" pref check
|
||||
// in conversation.jsx:init until we remove that code, which should
|
||||
// happen in the second patch in bug 1074686, at which time this comment
|
||||
// can go away as well.
|
||||
var fakeRoomID = "32";
|
||||
|
||||
beforeEach(function() {
|
||||
loop.shared.utils.Helper.prototype.locationData
|
||||
.returns({
|
||||
hash: "#room/" + fakeRoomID,
|
||||
pathname: ""
|
||||
});
|
||||
|
||||
sandbox.stub(loop.store, "LocalRoomStore");
|
||||
});
|
||||
|
||||
it("should create a localRoomStore", function() {
|
||||
loop.conversation.init();
|
||||
|
||||
sinon.assert.calledOnce(loop.store.LocalRoomStore);
|
||||
sinon.assert.calledWithNew(loop.store.LocalRoomStore);
|
||||
sinon.assert.calledWithExactly(loop.store.LocalRoomStore,
|
||||
sinon.match({
|
||||
dispatcher: sinon.match.instanceOf(loop.Dispatcher),
|
||||
mozLoop: sinon.match.same(navigator.mozLoop)
|
||||
}));
|
||||
});
|
||||
|
||||
it("should dispatch SetupEmptyRoom with localRoomId from locationHash",
|
||||
function() {
|
||||
|
||||
loop.conversation.init();
|
||||
|
||||
sinon.assert.calledOnce(loop.Dispatcher.prototype.dispatch);
|
||||
sinon.assert.calledWithExactly(loop.Dispatcher.prototype.dispatch,
|
||||
new loop.shared.actions.SetupEmptyRoom({localRoomId: fakeRoomID}));
|
||||
});
|
||||
});
|
||||
|
||||
it("should trigger a gatherCallData action", function() {
|
||||
it("should trigger a getWindowData action", function() {
|
||||
loop.conversation.init();
|
||||
|
||||
sinon.assert.calledOnce(loop.Dispatcher.prototype.dispatch);
|
||||
sinon.assert.calledWithExactly(loop.Dispatcher.prototype.dispatch,
|
||||
new loop.shared.actions.GatherCallData({
|
||||
windowId: "42",
|
||||
outgoing: false
|
||||
new loop.shared.actions.GetWindowData({
|
||||
windowId: "42"
|
||||
}));
|
||||
});
|
||||
|
||||
it("should trigger an outgoing gatherCallData action for outgoing calls",
|
||||
function() {
|
||||
loop.shared.utils.Helper.prototype.locationData.returns({
|
||||
hash: "#outgoing/24",
|
||||
pathname: "/"
|
||||
});
|
||||
|
||||
loop.conversation.init();
|
||||
|
||||
sinon.assert.calledOnce(loop.Dispatcher.prototype.dispatch);
|
||||
sinon.assert.calledWithExactly(loop.Dispatcher.prototype.dispatch,
|
||||
new loop.shared.actions.GatherCallData({
|
||||
windowId: "24",
|
||||
outgoing: true
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("ConversationControllerView", function() {
|
||||
var store, conversation, client, ccView, oldTitle, dispatcher;
|
||||
describe("AppControllerView", function() {
|
||||
var conversationStore, conversation, client, ccView, oldTitle, dispatcher;
|
||||
var conversationAppStore, localRoomStore;
|
||||
|
||||
function mountTestComponent(localRoomStore) {
|
||||
function mountTestComponent() {
|
||||
return TestUtils.renderIntoDocument(
|
||||
loop.conversation.AppControllerView({
|
||||
client: client,
|
||||
conversation: conversation,
|
||||
localRoomStore: localRoomStore,
|
||||
sdk: {},
|
||||
store: store
|
||||
conversationStore: conversationStore,
|
||||
conversationAppStore: conversationAppStore
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -202,7 +147,7 @@ describe("loop.conversation", function() {
|
|||
sdk: {}
|
||||
});
|
||||
dispatcher = new loop.Dispatcher();
|
||||
store = new loop.store.ConversationStore({
|
||||
conversationStore = new loop.store.ConversationStore({
|
||||
contact: {
|
||||
name: [ "Mr Smith" ],
|
||||
email: [{
|
||||
|
@ -216,6 +161,14 @@ describe("loop.conversation", function() {
|
|||
dispatcher: dispatcher,
|
||||
sdkDriver: {}
|
||||
});
|
||||
localRoomStore = new loop.store.LocalRoomStore({
|
||||
mozLoop: navigator.mozLoop,
|
||||
dispatcher: dispatcher
|
||||
});
|
||||
conversationAppStore = new loop.store.ConversationAppStore({
|
||||
dispatcher: dispatcher,
|
||||
mozLoop: navigator.mozLoop
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
|
@ -224,7 +177,7 @@ describe("loop.conversation", function() {
|
|||
});
|
||||
|
||||
it("should display the OutgoingConversationView for outgoing calls", function() {
|
||||
store.set({outgoing: true});
|
||||
conversationAppStore.setStoreState({windowType: "outgoing"});
|
||||
|
||||
ccView = mountTestComponent();
|
||||
|
||||
|
@ -233,7 +186,14 @@ describe("loop.conversation", function() {
|
|||
});
|
||||
|
||||
it("should display the IncomingConversationView for incoming calls", function() {
|
||||
store.set({outgoing: false});
|
||||
sandbox.stub(conversation, "setIncomingSessionData");
|
||||
sandbox.stub(loop, "CallConnectionWebSocket").returns({
|
||||
promiseConnect: function() {
|
||||
return new Promise(function() {});
|
||||
},
|
||||
on: sandbox.spy()
|
||||
});
|
||||
conversationAppStore.setStoreState({windowType: "incoming"});
|
||||
|
||||
ccView = mountTestComponent();
|
||||
|
||||
|
@ -242,31 +202,34 @@ describe("loop.conversation", function() {
|
|||
});
|
||||
|
||||
it("should display the EmptyRoomView for rooms", function() {
|
||||
navigator.mozLoop.rooms = {
|
||||
addCallback: function() {},
|
||||
removeCallback: function() {}
|
||||
};
|
||||
var localRoomStore = new loop.store.LocalRoomStore({
|
||||
mozLoop: navigator.mozLoop,
|
||||
dispatcher: dispatcher
|
||||
});
|
||||
conversationAppStore.setStoreState({windowType: "room"});
|
||||
|
||||
ccView = mountTestComponent(localRoomStore);
|
||||
ccView = mountTestComponent();
|
||||
|
||||
TestUtils.findRenderedComponentWithType(ccView,
|
||||
loop.roomViews.EmptyRoomView);
|
||||
});
|
||||
|
||||
it("should display the GenericFailureView for failures", function() {
|
||||
conversationAppStore.setStoreState({windowType: "failed"});
|
||||
|
||||
ccView = mountTestComponent();
|
||||
|
||||
TestUtils.findRenderedComponentWithType(ccView,
|
||||
loop.conversation.GenericFailureView);
|
||||
});
|
||||
});
|
||||
|
||||
describe("IncomingConversationView", function() {
|
||||
var conversation, client, icView, oldTitle;
|
||||
var conversationAppStore, conversation, client, icView, oldTitle;
|
||||
|
||||
function mountTestComponent() {
|
||||
return TestUtils.renderIntoDocument(
|
||||
loop.conversation.IncomingConversationView({
|
||||
client: client,
|
||||
conversation: conversation,
|
||||
sdk: {}
|
||||
sdk: {},
|
||||
conversationAppStore: conversationAppStore
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -277,6 +240,11 @@ describe("loop.conversation", function() {
|
|||
sdk: {}
|
||||
});
|
||||
conversation.set({windowId: 42});
|
||||
var dispatcher = new loop.Dispatcher();
|
||||
conversationAppStore = new loop.store.ConversationAppStore({
|
||||
dispatcher: dispatcher,
|
||||
mozLoop: navigator.mozLoop
|
||||
});
|
||||
sandbox.stub(conversation, "setOutgoingSessionData");
|
||||
});
|
||||
|
||||
|
@ -287,13 +255,13 @@ describe("loop.conversation", function() {
|
|||
|
||||
describe("start", function() {
|
||||
it("should set the title to incoming_call_title2", function() {
|
||||
navigator.mozLoop.getCallData = function() {
|
||||
return {
|
||||
conversationAppStore.setStoreState({
|
||||
windowData: {
|
||||
progressURL: "fake",
|
||||
websocketToken: "fake",
|
||||
callId: 42
|
||||
};
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
icView = mountTestComponent();
|
||||
|
||||
|
@ -302,7 +270,8 @@ describe("loop.conversation", function() {
|
|||
});
|
||||
|
||||
describe("componentDidMount", function() {
|
||||
var fakeSessionData;
|
||||
var fakeSessionData, promise, resolveWebSocketConnect;
|
||||
var rejectWebSocketConnect;
|
||||
|
||||
beforeEach(function() {
|
||||
fakeSessionData = {
|
||||
|
@ -315,7 +284,10 @@ describe("loop.conversation", function() {
|
|||
websocketToken: "7b"
|
||||
};
|
||||
|
||||
navigator.mozLoop.getCallData.returns(fakeSessionData);
|
||||
conversationAppStore.setStoreState({
|
||||
windowData: fakeSessionData
|
||||
});
|
||||
|
||||
stubComponent(loop.conversation, "IncomingCallView");
|
||||
stubComponent(sharedView, "ConversationView");
|
||||
});
|
||||
|
@ -326,179 +298,167 @@ describe("loop.conversation", function() {
|
|||
sinon.assert.calledOnce(navigator.mozLoop.startAlerting);
|
||||
});
|
||||
|
||||
it("should call getCallData on navigator.mozLoop", function() {
|
||||
icView = mountTestComponent();
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.getCallData);
|
||||
sinon.assert.calledWith(navigator.mozLoop.getCallData, 42);
|
||||
});
|
||||
|
||||
describe("getCallData successful", function() {
|
||||
var promise, resolveWebSocketConnect,
|
||||
rejectWebSocketConnect;
|
||||
|
||||
describe("Session Data setup", function() {
|
||||
beforeEach(function() {
|
||||
sandbox.stub(loop, "CallConnectionWebSocket").returns({
|
||||
promiseConnect: function () {
|
||||
promise = new Promise(function(resolve, reject) {
|
||||
resolveWebSocketConnect = resolve;
|
||||
rejectWebSocketConnect = reject;
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
on: sinon.stub()
|
||||
});
|
||||
});
|
||||
|
||||
it("should store the session data", function() {
|
||||
sandbox.stub(conversation, "setIncomingSessionData");
|
||||
|
||||
icView = mountTestComponent();
|
||||
|
||||
sinon.assert.calledOnce(conversation.setIncomingSessionData);
|
||||
sinon.assert.calledWithExactly(conversation.setIncomingSessionData,
|
||||
fakeSessionData);
|
||||
});
|
||||
|
||||
it("should setup the websocket connection", function() {
|
||||
icView = mountTestComponent();
|
||||
|
||||
sinon.assert.calledOnce(loop.CallConnectionWebSocket);
|
||||
sinon.assert.calledWithExactly(loop.CallConnectionWebSocket, {
|
||||
callId: "Hello",
|
||||
url: "http://progress.example.com",
|
||||
websocketToken: "7b"
|
||||
});
|
||||
describe("Session Data setup", function() {
|
||||
beforeEach(function() {
|
||||
sandbox.stub(loop, "CallConnectionWebSocket").returns({
|
||||
promiseConnect: function () {
|
||||
promise = new Promise(function(resolve, reject) {
|
||||
resolveWebSocketConnect = resolve;
|
||||
rejectWebSocketConnect = reject;
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
on: sinon.stub()
|
||||
});
|
||||
});
|
||||
|
||||
describe("WebSocket Handling", function() {
|
||||
beforeEach(function() {
|
||||
promise = new Promise(function(resolve, reject) {
|
||||
resolveWebSocketConnect = resolve;
|
||||
rejectWebSocketConnect = reject;
|
||||
});
|
||||
it("should store the session data", function() {
|
||||
sandbox.stub(conversation, "setIncomingSessionData");
|
||||
|
||||
sandbox.stub(loop.CallConnectionWebSocket.prototype, "promiseConnect").returns(promise);
|
||||
icView = mountTestComponent();
|
||||
|
||||
sinon.assert.calledOnce(conversation.setIncomingSessionData);
|
||||
sinon.assert.calledWithExactly(conversation.setIncomingSessionData,
|
||||
fakeSessionData);
|
||||
});
|
||||
|
||||
it("should setup the websocket connection", function() {
|
||||
icView = mountTestComponent();
|
||||
|
||||
sinon.assert.calledOnce(loop.CallConnectionWebSocket);
|
||||
sinon.assert.calledWithExactly(loop.CallConnectionWebSocket, {
|
||||
callId: "Hello",
|
||||
url: "http://progress.example.com",
|
||||
websocketToken: "7b"
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("WebSocket Handling", function() {
|
||||
beforeEach(function() {
|
||||
promise = new Promise(function(resolve, reject) {
|
||||
resolveWebSocketConnect = resolve;
|
||||
rejectWebSocketConnect = reject;
|
||||
});
|
||||
|
||||
it("should set the state to incoming on success", function(done) {
|
||||
sandbox.stub(loop.CallConnectionWebSocket.prototype, "promiseConnect").returns(promise);
|
||||
});
|
||||
|
||||
it("should set the state to incoming on success", function(done) {
|
||||
icView = mountTestComponent();
|
||||
resolveWebSocketConnect("incoming");
|
||||
|
||||
promise.then(function () {
|
||||
expect(icView.state.callStatus).eql("incoming");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should set the state to close on success if the progress " +
|
||||
"state is terminated", function(done) {
|
||||
icView = mountTestComponent();
|
||||
resolveWebSocketConnect("incoming");
|
||||
resolveWebSocketConnect("terminated");
|
||||
|
||||
promise.then(function () {
|
||||
expect(icView.state.callStatus).eql("incoming");
|
||||
expect(icView.state.callStatus).eql("close");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should set the state to close on success if the progress " +
|
||||
"state is terminated", function(done) {
|
||||
icView = mountTestComponent();
|
||||
resolveWebSocketConnect("terminated");
|
||||
// XXX implement me as part of bug 1047410
|
||||
// see https://hg.mozilla.org/integration/fx-team/rev/5d2c69ebb321#l18.259
|
||||
it.skip("should should switch view state to failed", function(done) {
|
||||
icView = mountTestComponent();
|
||||
rejectWebSocketConnect();
|
||||
|
||||
promise.then(function () {
|
||||
expect(icView.state.callStatus).eql("close");
|
||||
promise.then(function() {}, function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("WebSocket Events", function() {
|
||||
describe("Call cancelled or timed out before acceptance", function() {
|
||||
beforeEach(function() {
|
||||
// Mounting the test component automatically calls the required
|
||||
// setup functions
|
||||
icView = mountTestComponent();
|
||||
promise = new Promise(function(resolve, reject) {
|
||||
resolve();
|
||||
});
|
||||
|
||||
sandbox.stub(loop.CallConnectionWebSocket.prototype, "promiseConnect").returns(promise);
|
||||
sandbox.stub(loop.CallConnectionWebSocket.prototype, "close");
|
||||
sandbox.stub(window, "close");
|
||||
});
|
||||
|
||||
describe("progress - terminated (previousState = alerting)", function() {
|
||||
it("should stop alerting", function(done) {
|
||||
promise.then(function() {
|
||||
icView._websocket.trigger("progress", {
|
||||
state: "terminated",
|
||||
reason: "timeout"
|
||||
}, "alerting");
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.stopAlerting);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
// XXX implement me as part of bug 1047410
|
||||
// see https://hg.mozilla.org/integration/fx-team/rev/5d2c69ebb321#l18.259
|
||||
it.skip("should should switch view state to failed", function(done) {
|
||||
icView = mountTestComponent();
|
||||
rejectWebSocketConnect();
|
||||
it("should close the websocket", function(done) {
|
||||
promise.then(function() {
|
||||
icView._websocket.trigger("progress", {
|
||||
state: "terminated",
|
||||
reason: "closed"
|
||||
}, "alerting");
|
||||
|
||||
promise.then(function() {}, function() {
|
||||
done();
|
||||
sinon.assert.calledOnce(icView._websocket.close);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should close the window", function(done) {
|
||||
promise.then(function() {
|
||||
icView._websocket.trigger("progress", {
|
||||
state: "terminated",
|
||||
reason: "answered-elsewhere"
|
||||
}, "alerting");
|
||||
|
||||
sandbox.clock.tick(1);
|
||||
|
||||
sinon.assert.calledOnce(window.close);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("WebSocket Events", function() {
|
||||
describe("Call cancelled or timed out before acceptance", function() {
|
||||
beforeEach(function() {
|
||||
// Mounting the test component automatically calls the required
|
||||
// setup functions
|
||||
icView = mountTestComponent();
|
||||
promise = new Promise(function(resolve, reject) {
|
||||
resolve();
|
||||
describe("progress - terminated (previousState not init" +
|
||||
" nor alerting)",
|
||||
function() {
|
||||
it("should set the state to end", function(done) {
|
||||
promise.then(function() {
|
||||
icView._websocket.trigger("progress", {
|
||||
state: "terminated",
|
||||
reason: "media-fail"
|
||||
}, "connecting");
|
||||
|
||||
expect(icView.state.callStatus).eql("end");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
sandbox.stub(loop.CallConnectionWebSocket.prototype, "promiseConnect").returns(promise);
|
||||
sandbox.stub(loop.CallConnectionWebSocket.prototype, "close");
|
||||
sandbox.stub(window, "close");
|
||||
});
|
||||
|
||||
describe("progress - terminated (previousState = alerting)", function() {
|
||||
it("should stop alerting", function(done) {
|
||||
promise.then(function() {
|
||||
icView._websocket.trigger("progress", {
|
||||
state: "terminated",
|
||||
reason: "timeout"
|
||||
}, "alerting");
|
||||
reason: "media-fail"
|
||||
}, "connecting");
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.stopAlerting);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should close the websocket", function(done) {
|
||||
promise.then(function() {
|
||||
icView._websocket.trigger("progress", {
|
||||
state: "terminated",
|
||||
reason: "closed"
|
||||
}, "alerting");
|
||||
|
||||
sinon.assert.calledOnce(icView._websocket.close);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should close the window", function(done) {
|
||||
promise.then(function() {
|
||||
icView._websocket.trigger("progress", {
|
||||
state: "terminated",
|
||||
reason: "answered-elsewhere"
|
||||
}, "alerting");
|
||||
|
||||
sandbox.clock.tick(1);
|
||||
|
||||
sinon.assert.calledOnce(window.close);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("progress - terminated (previousState not init" +
|
||||
" nor alerting)",
|
||||
function() {
|
||||
it("should set the state to end", function(done) {
|
||||
promise.then(function() {
|
||||
icView._websocket.trigger("progress", {
|
||||
state: "terminated",
|
||||
reason: "media-fail"
|
||||
}, "connecting");
|
||||
|
||||
expect(icView.state.callStatus).eql("end");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should stop alerting", function(done) {
|
||||
promise.then(function() {
|
||||
icView._websocket.trigger("progress", {
|
||||
state: "terminated",
|
||||
reason: "media-fail"
|
||||
}, "connecting");
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.stopAlerting);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -572,8 +532,9 @@ describe("loop.conversation", function() {
|
|||
it("should release callData", function() {
|
||||
icView.decline();
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.releaseCallData);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.releaseCallData, "8699");
|
||||
sinon.assert.calledOnce(navigator.mozLoop.calls.clearCallInProgress);
|
||||
sinon.assert.calledWithExactly(
|
||||
navigator.mozLoop.calls.clearCallInProgress, "8699");
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -644,13 +605,27 @@ describe("loop.conversation", function() {
|
|||
var fakeSessionData;
|
||||
|
||||
beforeEach(function() {
|
||||
icView = mountTestComponent();
|
||||
|
||||
fakeSessionData = {
|
||||
sessionId: "sessionId",
|
||||
sessionToken: "sessionToken",
|
||||
apiKey: "apiKey"
|
||||
};
|
||||
|
||||
conversationAppStore.setStoreState({
|
||||
windowData: fakeSessionData
|
||||
});
|
||||
|
||||
sandbox.stub(conversation, "setIncomingSessionData");
|
||||
sandbox.stub(loop, "CallConnectionWebSocket").returns({
|
||||
promiseConnect: function() {
|
||||
return new Promise(function() {});
|
||||
},
|
||||
on: sandbox.spy()
|
||||
});
|
||||
|
||||
icView = mountTestComponent();
|
||||
|
||||
conversation.set("loopToken", "fakeToken");
|
||||
navigator.mozLoop.getLoopCharPref.returns("http://fake");
|
||||
stubComponent(sharedView, "ConversationView");
|
||||
|
@ -700,7 +675,7 @@ describe("loop.conversation", function() {
|
|||
conversation.trigger("session:network-disconnected");
|
||||
|
||||
TestUtils.findRenderedComponentWithType(icView,
|
||||
loop.conversation.IncomingCallFailedView);
|
||||
loop.conversation.GenericFailureView);
|
||||
});
|
||||
|
||||
it("should update the conversation window toolbar title",
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
<script src="../../content/shared/js/roomListStore.js"></script>
|
||||
<script src="../../content/js/client.js"></script>
|
||||
<script src="../../content/shared/js/localRoomStore.js"></script>
|
||||
<script src="../../content/js/conversationAppStore.js"></script>
|
||||
<script src="../../content/js/roomViews.js"></script>
|
||||
<script src="../../content/js/conversationViews.js"></script>
|
||||
<script src="../../content/js/conversation.js"></script>
|
||||
|
@ -53,6 +54,7 @@
|
|||
<script src="../../content/js/panel.js"></script>
|
||||
|
||||
<!-- Test scripts -->
|
||||
<script src="conversationAppStore_test.js"></script>
|
||||
<script src="client_test.js"></script>
|
||||
<script src="conversation_test.js"></script>
|
||||
<script src="panel_test.js"></script>
|
||||
|
|
|
@ -763,7 +763,7 @@ describe("loop.panel", function() {
|
|||
|
||||
var buttonNode = view.getDOMNode().querySelector("button[disabled]");
|
||||
expect(buttonNode).to.not.equal(null);
|
||||
});
|
||||
});
|
||||
|
||||
it("should disable the create button when a list retrieval operation is pending",
|
||||
function() {
|
||||
|
@ -774,6 +774,20 @@ describe("loop.panel", function() {
|
|||
|
||||
var buttonNode = view.getDOMNode().querySelector("button[disabled]");
|
||||
expect(buttonNode).to.not.equal(null);
|
||||
});
|
||||
|
||||
describe("#openRoom", function() {
|
||||
it("should dispatch an OpenRoom action", function() {
|
||||
var view = createTestComponent();
|
||||
var dispatch = sandbox.stub(dispatcher, "dispatch");
|
||||
|
||||
view.openRoom({roomToken: "42cba"});
|
||||
|
||||
sinon.assert.calledOnce(dispatch);
|
||||
sinon.assert.calledWithExactly(dispatch, new sharedActions.OpenRoom({
|
||||
roomToken: "42cba"
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -79,25 +79,23 @@ const kDanglingContact = {
|
|||
};
|
||||
|
||||
const promiseLoadContacts = function() {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
LoopContacts.removeAll(err => {
|
||||
if (err) {
|
||||
deferred.reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
gExpectedAdds.push(...kContacts);
|
||||
LoopContacts.addMany(kContacts, (err, contacts) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
LoopContacts.removeAll(err => {
|
||||
if (err) {
|
||||
deferred.reject(err);
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
deferred.resolve(contacts);
|
||||
|
||||
gExpectedAdds.push(...kContacts);
|
||||
LoopContacts.addMany(kContacts, (err, contacts) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve(contacts);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
// Get a copy of a contact without private properties.
|
||||
|
@ -162,36 +160,36 @@ add_task(function* () {
|
|||
}
|
||||
|
||||
info("Add a contact.");
|
||||
let deferred = Promise.defer();
|
||||
gExpectedAdds.push(kDanglingContact);
|
||||
LoopContacts.add(kDanglingContact, (err, contact) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
compareContacts(contact, kDanglingContact);
|
||||
|
||||
info("Check if it's persisted.");
|
||||
LoopContacts.get(contact._guid, (err, contact) => {
|
||||
yield new Promise((resolve, reject) => {
|
||||
gExpectedAdds.push(kDanglingContact);
|
||||
LoopContacts.add(kDanglingContact, (err, contact) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
compareContacts(contact, kDanglingContact);
|
||||
deferred.resolve();
|
||||
|
||||
info("Check if it's persisted.");
|
||||
LoopContacts.get(contact._guid, (err, contact) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
compareContacts(contact, kDanglingContact);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_task(function* () {
|
||||
info("Test removing all contacts.");
|
||||
let contacts = yield promiseLoadContacts();
|
||||
|
||||
let deferred = Promise.defer();
|
||||
LoopContacts.removeAll(function(err) {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
LoopContacts.getAll(function(err, found) {
|
||||
yield new Promise((resolve, reject) => {
|
||||
LoopContacts.removeAll(function(err) {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
Assert.equal(found.length, 0, "There shouldn't be any contacts left");
|
||||
deferred.resolve();
|
||||
})
|
||||
LoopContacts.getAll(function(err, found) {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
Assert.equal(found.length, 0, "There shouldn't be any contacts left");
|
||||
resolve();
|
||||
})
|
||||
});
|
||||
});
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
// Test retrieving a contact.
|
||||
|
@ -199,58 +197,58 @@ add_task(function* () {
|
|||
let contacts = yield promiseLoadContacts();
|
||||
|
||||
info("Get a single contact.");
|
||||
let deferred = Promise.defer();
|
||||
LoopContacts.get(contacts[1]._guid, (err, contact) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
compareContacts(contact, kContacts[1]);
|
||||
deferred.resolve();
|
||||
yield new Promise((resolve, reject) => {
|
||||
LoopContacts.get(contacts[1]._guid, (err, contact) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
compareContacts(contact, kContacts[1]);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
yield deferred.promise;
|
||||
|
||||
info("Get a single contact by id.");
|
||||
deferred = Promise.defer();
|
||||
LoopContacts.getByServiceId(2, (err, contact) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
compareContacts(contact, kContacts[1]);
|
||||
deferred.resolve();
|
||||
yield new Promise((resolve, reject) => {
|
||||
LoopContacts.getByServiceId(2, (err, contact) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
compareContacts(contact, kContacts[1]);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
yield deferred.promise;
|
||||
|
||||
info("Get a couple of contacts.");
|
||||
deferred = Promise.defer();
|
||||
let toRetrieve = [contacts[0], contacts[2], contacts[3]];
|
||||
LoopContacts.getMany(toRetrieve.map(contact => contact._guid), (err, result) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
Assert.equal(result.length, toRetrieve.length, "Result list should be the same " +
|
||||
"size as the list of items to retrieve");
|
||||
for (let contact of toRetrieve) {
|
||||
let found = result.filter(c => c._guid == contact._guid);
|
||||
Assert.ok(found.length, "Contact " + contact._guid + " should be in the list");
|
||||
compareContacts(found[0], contact);
|
||||
}
|
||||
deferred.resolve();
|
||||
yield new Promise((resolve, reject) => {
|
||||
let toRetrieve = [contacts[0], contacts[2], contacts[3]];
|
||||
LoopContacts.getMany(toRetrieve.map(contact => contact._guid), (err, result) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
Assert.equal(result.length, toRetrieve.length, "Result list should be the same " +
|
||||
"size as the list of items to retrieve");
|
||||
for (let contact of toRetrieve) {
|
||||
let found = result.filter(c => c._guid == contact._guid);
|
||||
Assert.ok(found.length, "Contact " + contact._guid + " should be in the list");
|
||||
compareContacts(found[0], contact);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
yield deferred.promise;
|
||||
|
||||
info("Get all contacts.");
|
||||
deferred = Promise.defer();
|
||||
LoopContacts.getAll((err, contacts) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
for (let i = 0, l = contacts.length; i < l; ++i) {
|
||||
compareContacts(contacts[i], kContacts[i]);
|
||||
}
|
||||
deferred.resolve();
|
||||
yield new Promise((resolve, reject) => {
|
||||
LoopContacts.getAll((err, contacts) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
for (let i = 0, l = contacts.length; i < l; ++i) {
|
||||
compareContacts(contacts[i], kContacts[i]);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
yield deferred.promise;
|
||||
|
||||
info("Get a non-existent contact.");
|
||||
deferred = Promise.defer();
|
||||
LoopContacts.get(1000, (err, contact) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
Assert.ok(!contact, "There shouldn't be a contact");
|
||||
deferred.resolve();
|
||||
return new Promise((resolve, reject) => {
|
||||
LoopContacts.get(1000, (err, contact) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
Assert.ok(!contact, "There shouldn't be a contact");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
// Test removing a contact.
|
||||
|
@ -258,47 +256,47 @@ add_task(function* () {
|
|||
let contacts = yield promiseLoadContacts();
|
||||
|
||||
info("Remove a single contact.");
|
||||
let deferred = Promise.defer();
|
||||
let toRemove = contacts[2]._guid;
|
||||
gExpectedRemovals.push(toRemove);
|
||||
LoopContacts.remove(toRemove, err => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
|
||||
LoopContacts.get(toRemove, (err, contact) => {
|
||||
yield new Promise((resolve, reject) => {
|
||||
let toRemove = contacts[2]._guid;
|
||||
gExpectedRemovals.push(toRemove);
|
||||
LoopContacts.remove(toRemove, err => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
Assert.ok(!contact, "There shouldn't be a contact");
|
||||
deferred.resolve();
|
||||
|
||||
LoopContacts.get(toRemove, (err, contact) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
Assert.ok(!contact, "There shouldn't be a contact");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
yield deferred.promise;
|
||||
|
||||
info("Remove a non-existing contact.");
|
||||
deferred = Promise.defer();
|
||||
LoopContacts.remove(1000, (err, contact) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
Assert.ok(!contact, "There shouldn't be a contact");
|
||||
deferred.resolve();
|
||||
});
|
||||
yield deferred.promise;
|
||||
|
||||
info("Remove multiple contacts.");
|
||||
deferred = Promise.defer();
|
||||
toRemove = [contacts[0]._guid, contacts[1]._guid];
|
||||
gExpectedRemovals.push(...toRemove);
|
||||
LoopContacts.removeMany(toRemove, err => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
|
||||
LoopContacts.getAll((err, contacts) => {
|
||||
yield new Promise((resolve, reject) => {
|
||||
LoopContacts.remove(1000, (err, contact) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
let ids = contacts.map(contact => contact._guid);
|
||||
Assert.equal(ids.indexOf(toRemove[0]), -1, "Contact '" + toRemove[0] +
|
||||
"' shouldn't be there");
|
||||
Assert.equal(ids.indexOf(toRemove[1]), -1, "Contact '" + toRemove[1] +
|
||||
"' shouldn't be there");
|
||||
deferred.resolve();
|
||||
Assert.ok(!contact, "There shouldn't be a contact");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
info("Remove multiple contacts.");
|
||||
yield new Promise((resolve, reject) => {
|
||||
let toRemove = [contacts[0]._guid, contacts[1]._guid];
|
||||
gExpectedRemovals.push(...toRemove);
|
||||
LoopContacts.removeMany(toRemove, err => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
|
||||
LoopContacts.getAll((err, contacts) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
let ids = contacts.map(contact => contact._guid);
|
||||
Assert.equal(ids.indexOf(toRemove[0]), -1, "Contact '" + toRemove[0] +
|
||||
"' shouldn't be there");
|
||||
Assert.equal(ids.indexOf(toRemove[1]), -1, "Contact '" + toRemove[1] +
|
||||
"' shouldn't be there");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
// Test updating a contact.
|
||||
|
@ -308,40 +306,40 @@ add_task(function* () {
|
|||
const newBday = (new Date(403920000000)).toISOString();
|
||||
|
||||
info("Update a single contact.");
|
||||
let deferred = Promise.defer();
|
||||
let toUpdate = {
|
||||
_guid: contacts[2]._guid,
|
||||
bday: newBday
|
||||
};
|
||||
gExpectedUpdates.push(contacts[2]._guid);
|
||||
LoopContacts.update(toUpdate, (err, result) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
Assert.equal(result, toUpdate._guid, "Result should be the same as the contact ID");
|
||||
|
||||
LoopContacts.get(toUpdate._guid, (err, contact) => {
|
||||
yield new Promise((resolve, reject) => {
|
||||
let toUpdate = {
|
||||
_guid: contacts[2]._guid,
|
||||
bday: newBday
|
||||
};
|
||||
gExpectedUpdates.push(contacts[2]._guid);
|
||||
LoopContacts.update(toUpdate, (err, result) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
Assert.equal(contact.bday, newBday, "Birthday should be the same");
|
||||
info("Check that all other properties were left intact.");
|
||||
contacts[2].bday = newBday;
|
||||
compareContacts(contact, contacts[2]);
|
||||
deferred.resolve();
|
||||
Assert.equal(result, toUpdate._guid, "Result should be the same as the contact ID");
|
||||
|
||||
LoopContacts.get(toUpdate._guid, (err, contact) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
Assert.equal(contact.bday, newBday, "Birthday should be the same");
|
||||
info("Check that all other properties were left intact.");
|
||||
contacts[2].bday = newBday;
|
||||
compareContacts(contact, contacts[2]);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
yield deferred.promise;
|
||||
|
||||
info("Update a non-existing contact.");
|
||||
deferred = Promise.defer();
|
||||
toUpdate = {
|
||||
_guid: 1000,
|
||||
bday: newBday
|
||||
};
|
||||
LoopContacts.update(toUpdate, (err, contact) => {
|
||||
Assert.ok(err, "There should be an error");
|
||||
Assert.equal(err.message, "Contact with _guid '1000' could not be found",
|
||||
"Error message should be correct");
|
||||
deferred.resolve();
|
||||
yield new Promise((resolve, reject) => {
|
||||
let toUpdate = {
|
||||
_guid: 1000,
|
||||
bday: newBday
|
||||
};
|
||||
LoopContacts.update(toUpdate, (err, contact) => {
|
||||
Assert.ok(err, "There should be an error");
|
||||
Assert.equal(err.message, "Contact with _guid '1000' could not be found",
|
||||
"Error message should be correct");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
// Test blocking and unblocking a contact.
|
||||
|
@ -349,62 +347,62 @@ add_task(function* () {
|
|||
let contacts = yield promiseLoadContacts();
|
||||
|
||||
info("Block contact.");
|
||||
let deferred = Promise.defer();
|
||||
let toBlock = contacts[1]._guid;
|
||||
gExpectedUpdates.push(toBlock);
|
||||
LoopContacts.block(toBlock, (err, result) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
Assert.equal(result, toBlock, "Result should be the same as the contact ID");
|
||||
|
||||
LoopContacts.get(toBlock, (err, contact) => {
|
||||
yield new Promise((resolve, reject) => {
|
||||
let toBlock = contacts[1]._guid;
|
||||
gExpectedUpdates.push(toBlock);
|
||||
LoopContacts.block(toBlock, (err, result) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
Assert.strictEqual(contact.blocked, true, "Blocked status should be set");
|
||||
info("Check that all other properties were left intact.");
|
||||
delete contact.blocked;
|
||||
compareContacts(contact, contacts[1]);
|
||||
deferred.resolve();
|
||||
Assert.equal(result, toBlock, "Result should be the same as the contact ID");
|
||||
|
||||
LoopContacts.get(toBlock, (err, contact) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
Assert.strictEqual(contact.blocked, true, "Blocked status should be set");
|
||||
info("Check that all other properties were left intact.");
|
||||
delete contact.blocked;
|
||||
compareContacts(contact, contacts[1]);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
yield deferred.promise;
|
||||
|
||||
info("Block a non-existing contact.");
|
||||
deferred = Promise.defer();
|
||||
LoopContacts.block(1000, err => {
|
||||
Assert.ok(err, "There should be an error");
|
||||
Assert.equal(err.message, "Contact with _guid '1000' could not be found",
|
||||
"Error message should be correct");
|
||||
deferred.resolve();
|
||||
});
|
||||
yield deferred.promise;
|
||||
|
||||
info("Unblock a contact.");
|
||||
deferred = Promise.defer();
|
||||
let toUnblock = contacts[1]._guid;
|
||||
gExpectedUpdates.push(toUnblock);
|
||||
LoopContacts.unblock(toUnblock, (err, result) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
Assert.equal(result, toUnblock, "Result should be the same as the contact ID");
|
||||
|
||||
LoopContacts.get(toUnblock, (err, contact) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
Assert.strictEqual(contact.blocked, false, "Blocked status should be set");
|
||||
info("Check that all other properties were left intact.");
|
||||
delete contact.blocked;
|
||||
compareContacts(contact, contacts[1]);
|
||||
deferred.resolve();
|
||||
yield new Promise((resolve, reject) => {
|
||||
LoopContacts.block(1000, err => {
|
||||
Assert.ok(err, "There should be an error");
|
||||
Assert.equal(err.message, "Contact with _guid '1000' could not be found",
|
||||
"Error message should be correct");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
info("Unblock a contact.");
|
||||
yield new Promise((resolve, reject) => {
|
||||
let toUnblock = contacts[1]._guid;
|
||||
gExpectedUpdates.push(toUnblock);
|
||||
LoopContacts.unblock(toUnblock, (err, result) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
Assert.equal(result, toUnblock, "Result should be the same as the contact ID");
|
||||
|
||||
LoopContacts.get(toUnblock, (err, contact) => {
|
||||
Assert.ok(!err, "There shouldn't be an error");
|
||||
Assert.strictEqual(contact.blocked, false, "Blocked status should be set");
|
||||
info("Check that all other properties were left intact.");
|
||||
delete contact.blocked;
|
||||
compareContacts(contact, contacts[1]);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
yield deferred.promise;
|
||||
|
||||
info("Unblock a non-existing contact.");
|
||||
deferred = Promise.defer();
|
||||
LoopContacts.unblock(1000, err => {
|
||||
Assert.ok(err, "There should be an error");
|
||||
Assert.equal(err.message, "Contact with _guid '1000' could not be found",
|
||||
"Error message should be correct");
|
||||
deferred.resolve();
|
||||
yield new Promise((resolve, reject) => {
|
||||
LoopContacts.unblock(1000, err => {
|
||||
Assert.ok(err, "There should be an error");
|
||||
Assert.equal(err.message, "Contact with _guid '1000' could not be found",
|
||||
"Error message should be correct");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
// Test if the event emitter implementation doesn't leak and is working as expected.
|
||||
|
|
|
@ -418,21 +418,21 @@ add_task(function* openFxASettings() {
|
|||
};
|
||||
yield promiseOAuthParamsSetup(BASE_URL, params);
|
||||
|
||||
let deferredTab = Promise.defer();
|
||||
let progressListener = {
|
||||
onLocationChange: function onLocationChange(aBrowser) {
|
||||
gBrowser.removeTabsProgressListener(progressListener);
|
||||
let contentURI = Services.io.newURI(params.content_uri, null, null);
|
||||
is(aBrowser.currentURI.spec, Services.io.newURI("/settings", null, contentURI).spec,
|
||||
"Check settings tab URL");
|
||||
deferredTab.resolve();
|
||||
},
|
||||
};
|
||||
gBrowser.addTabsProgressListener(progressListener);
|
||||
yield new Promise((resolve, reject) => {
|
||||
let progressListener = {
|
||||
onLocationChange: function onLocationChange(aBrowser) {
|
||||
gBrowser.removeTabsProgressListener(progressListener);
|
||||
let contentURI = Services.io.newURI(params.content_uri, null, null);
|
||||
is(aBrowser.currentURI.spec, Services.io.newURI("/settings", null, contentURI).spec,
|
||||
"Check settings tab URL");
|
||||
resolve();
|
||||
},
|
||||
};
|
||||
gBrowser.addTabsProgressListener(progressListener);
|
||||
|
||||
MozLoopService.openFxASettings();
|
||||
MozLoopService.openFxASettings();
|
||||
});
|
||||
|
||||
yield deferredTab.promise;
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
gBrowser.removeTab(gBrowser.tabs[1]);
|
||||
}
|
||||
|
|
|
@ -92,38 +92,36 @@ add_task(function* token_request_invalid_state() {
|
|||
// Helper methods
|
||||
|
||||
function promiseParams() {
|
||||
let deferred = Promise.defer();
|
||||
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
||||
createInstance(Ci.nsIXMLHttpRequest);
|
||||
xhr.open("POST", BASE_URL + "/fxa-oauth/params", true);
|
||||
xhr.responseType = "json";
|
||||
xhr.addEventListener("load", () => {
|
||||
info("/fxa-oauth/params response:\n" + JSON.stringify(xhr.response, null, 4));
|
||||
deferred.resolve(xhr);
|
||||
return new Promise((resolve, reject) => {
|
||||
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
||||
createInstance(Ci.nsIXMLHttpRequest);
|
||||
xhr.open("POST", BASE_URL + "/fxa-oauth/params", true);
|
||||
xhr.responseType = "json";
|
||||
xhr.addEventListener("load", () => {
|
||||
info("/fxa-oauth/params response:\n" + JSON.stringify(xhr.response, null, 4));
|
||||
resolve(xhr);
|
||||
});
|
||||
xhr.addEventListener("error", reject);
|
||||
xhr.send();
|
||||
});
|
||||
xhr.addEventListener("error", deferred.reject);
|
||||
xhr.send();
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function promiseToken(code, state) {
|
||||
let deferred = Promise.defer();
|
||||
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
||||
createInstance(Ci.nsIXMLHttpRequest);
|
||||
xhr.open("POST", BASE_URL + "/fxa-oauth/token", true);
|
||||
xhr.setRequestHeader("Authorization", "Hawk ...");
|
||||
xhr.responseType = "json";
|
||||
xhr.addEventListener("load", () => {
|
||||
info("/fxa-oauth/token response:\n" + JSON.stringify(xhr.response, null, 4));
|
||||
deferred.resolve(xhr);
|
||||
return new Promise((resolve, reject) => {
|
||||
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
||||
createInstance(Ci.nsIXMLHttpRequest);
|
||||
xhr.open("POST", BASE_URL + "/fxa-oauth/token", true);
|
||||
xhr.setRequestHeader("Authorization", "Hawk ...");
|
||||
xhr.responseType = "json";
|
||||
xhr.addEventListener("load", () => {
|
||||
info("/fxa-oauth/token response:\n" + JSON.stringify(xhr.response, null, 4));
|
||||
resolve(xhr);
|
||||
});
|
||||
xhr.addEventListener("error", reject);
|
||||
let payload = {
|
||||
code: code,
|
||||
state: state,
|
||||
};
|
||||
xhr.send(JSON.stringify(payload, null, 4));
|
||||
});
|
||||
xhr.addEventListener("error", deferred.reject);
|
||||
let payload = {
|
||||
code: code,
|
||||
state: state,
|
||||
};
|
||||
xhr.send(JSON.stringify(payload, null, 4));
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
|
|
@ -17,58 +17,57 @@ const WAS_OFFLINE = Services.io.offline;
|
|||
var gMozLoopAPI;
|
||||
|
||||
function promiseGetMozLoopAPI() {
|
||||
let deferred = Promise.defer();
|
||||
let loopPanel = document.getElementById("loop-notification-panel");
|
||||
let btn = document.getElementById("loop-call-button");
|
||||
return new Promise((resolve, reject) => {
|
||||
let loopPanel = document.getElementById("loop-notification-panel");
|
||||
let btn = document.getElementById("loop-call-button");
|
||||
|
||||
// Wait for the popup to be shown if it's not already, then we can get the iframe and
|
||||
// wait for the iframe's load to be completed.
|
||||
if (loopPanel.state == "closing" || loopPanel.state == "closed") {
|
||||
loopPanel.addEventListener("popupshown", () => {
|
||||
loopPanel.removeEventListener("popupshown", onpopupshown, true);
|
||||
onpopupshown();
|
||||
}, true);
|
||||
// Wait for the popup to be shown if it's not already, then we can get the iframe and
|
||||
// wait for the iframe's load to be completed.
|
||||
if (loopPanel.state == "closing" || loopPanel.state == "closed") {
|
||||
loopPanel.addEventListener("popupshown", () => {
|
||||
loopPanel.removeEventListener("popupshown", onpopupshown, true);
|
||||
onpopupshown();
|
||||
}, true);
|
||||
|
||||
// Now we're setup, click the button.
|
||||
btn.click();
|
||||
} else {
|
||||
setTimeout(onpopupshown, 0);
|
||||
}
|
||||
|
||||
function onpopupshown() {
|
||||
let iframe = document.getElementById(btn.getAttribute("notificationFrameId"));
|
||||
|
||||
if (iframe.contentDocument &&
|
||||
iframe.contentDocument.readyState == "complete") {
|
||||
gMozLoopAPI = iframe.contentWindow.navigator.wrappedJSObject.mozLoop;
|
||||
|
||||
deferred.resolve();
|
||||
// Now we're setup, click the button.
|
||||
btn.click();
|
||||
} else {
|
||||
iframe.addEventListener("load", function panelOnLoad(e) {
|
||||
iframe.removeEventListener("load", panelOnLoad, true);
|
||||
setTimeout(onpopupshown, 0);
|
||||
}
|
||||
|
||||
function onpopupshown() {
|
||||
let iframe = document.getElementById(btn.getAttribute("notificationFrameId"));
|
||||
|
||||
if (iframe.contentDocument &&
|
||||
iframe.contentDocument.readyState == "complete") {
|
||||
gMozLoopAPI = iframe.contentWindow.navigator.wrappedJSObject.mozLoop;
|
||||
|
||||
// We do this in an execute soon to allow any other event listeners to
|
||||
// be handled, just in case.
|
||||
deferred.resolve();
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
resolve();
|
||||
} else {
|
||||
iframe.addEventListener("load", function panelOnLoad(e) {
|
||||
iframe.removeEventListener("load", panelOnLoad, true);
|
||||
|
||||
// Remove the iframe after each test. This also avoids mochitest complaining
|
||||
// about leaks on shutdown as we intentionally hold the iframe open for the
|
||||
// life of the application.
|
||||
registerCleanupFunction(function() {
|
||||
loopPanel.hidePopup();
|
||||
let frameId = btn.getAttribute("notificationFrameId");
|
||||
let frame = document.getElementById(frameId);
|
||||
if (frame) {
|
||||
loopPanel.removeChild(frame);
|
||||
gMozLoopAPI = iframe.contentWindow.navigator.wrappedJSObject.mozLoop;
|
||||
|
||||
// We do this in an execute soon to allow any other event listeners to
|
||||
// be handled, just in case.
|
||||
resolve();
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the iframe after each test. This also avoids mochitest complaining
|
||||
// about leaks on shutdown as we intentionally hold the iframe open for the
|
||||
// life of the application.
|
||||
registerCleanupFunction(function() {
|
||||
loopPanel.hidePopup();
|
||||
let frameId = btn.getAttribute("notificationFrameId");
|
||||
let frame = document.getElementById(frameId);
|
||||
if (frame) {
|
||||
loopPanel.removeChild(frame);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -105,16 +104,15 @@ function loadLoopPanel(aOverrideOptions = {}) {
|
|||
}
|
||||
|
||||
function promiseOAuthParamsSetup(baseURL, params) {
|
||||
let deferred = Promise.defer();
|
||||
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
||||
createInstance(Ci.nsIXMLHttpRequest);
|
||||
xhr.open("POST", baseURL + "/setup_params", true);
|
||||
xhr.setRequestHeader("X-Params", JSON.stringify(params));
|
||||
xhr.addEventListener("load", () => deferred.resolve(xhr));
|
||||
xhr.addEventListener("error", error => deferred.reject(error));
|
||||
xhr.send();
|
||||
|
||||
return deferred.promise;
|
||||
return new Promise((resolve, reject) => {
|
||||
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
||||
createInstance(Ci.nsIXMLHttpRequest);
|
||||
xhr.open("POST", baseURL + "/setup_params", true);
|
||||
xhr.setRequestHeader("X-Params", JSON.stringify(params));
|
||||
xhr.addEventListener("load", () => resolve(xhr));
|
||||
xhr.addEventListener("error", error => reject(error));
|
||||
xhr.send();
|
||||
});
|
||||
}
|
||||
|
||||
function* resetFxA() {
|
||||
|
@ -149,41 +147,39 @@ function checkLoggedOutState() {
|
|||
}
|
||||
|
||||
function promiseDeletedOAuthParams(baseURL) {
|
||||
let deferred = Promise.defer();
|
||||
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
||||
createInstance(Ci.nsIXMLHttpRequest);
|
||||
xhr.open("DELETE", baseURL + "/setup_params", true);
|
||||
xhr.addEventListener("load", () => deferred.resolve(xhr));
|
||||
xhr.addEventListener("error", deferred.reject);
|
||||
xhr.send();
|
||||
|
||||
return deferred.promise;
|
||||
return new Promise((resolve, reject) => {
|
||||
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
||||
createInstance(Ci.nsIXMLHttpRequest);
|
||||
xhr.open("DELETE", baseURL + "/setup_params", true);
|
||||
xhr.addEventListener("load", () => resolve(xhr));
|
||||
xhr.addEventListener("error", reject);
|
||||
xhr.send();
|
||||
});
|
||||
}
|
||||
|
||||
function promiseObserverNotified(aTopic, aExpectedData = null) {
|
||||
let deferred = Promise.defer();
|
||||
Services.obs.addObserver(function onNotification(aSubject, aTopic, aData) {
|
||||
Services.obs.removeObserver(onNotification, aTopic);
|
||||
is(aData, aExpectedData, "observer data should match expected data")
|
||||
deferred.resolve({subject: aSubject, data: aData});
|
||||
}, aTopic, false);
|
||||
return deferred.promise;
|
||||
return new Promise((resolve, reject) => {
|
||||
Services.obs.addObserver(function onNotification(aSubject, aTopic, aData) {
|
||||
Services.obs.removeObserver(onNotification, aTopic);
|
||||
is(aData, aExpectedData, "observer data should match expected data")
|
||||
resolve({subject: aSubject, data: aData});
|
||||
}, aTopic, false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last registration on the test server.
|
||||
*/
|
||||
function promiseOAuthGetRegistration(baseURL) {
|
||||
let deferred = Promise.defer();
|
||||
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
||||
createInstance(Ci.nsIXMLHttpRequest);
|
||||
xhr.open("GET", baseURL + "/get_registration", true);
|
||||
xhr.responseType = "json";
|
||||
xhr.addEventListener("load", () => deferred.resolve(xhr));
|
||||
xhr.addEventListener("error", deferred.reject);
|
||||
xhr.send();
|
||||
|
||||
return deferred.promise;
|
||||
return new Promise((resolve, reject) => {
|
||||
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
||||
createInstance(Ci.nsIXMLHttpRequest);
|
||||
xhr.open("GET", baseURL + "/get_registration", true);
|
||||
xhr.responseType = "json";
|
||||
xhr.addEventListener("load", () => resolve(xhr));
|
||||
xhr.addEventListener("error", reject);
|
||||
xhr.send();
|
||||
});
|
||||
}
|
||||
|
||||
function getLoopString(stringID) {
|
||||
|
|
|
@ -38,7 +38,10 @@ describe("loop.store.ConversationStore", function () {
|
|||
|
||||
navigator.mozLoop = {
|
||||
getLoopBoolPref: sandbox.stub(),
|
||||
releaseCallData: sandbox.stub()
|
||||
calls: {
|
||||
setCallInProgress: sandbox.stub(),
|
||||
clearCallInProgress: sandbox.stub()
|
||||
}
|
||||
};
|
||||
|
||||
dispatcher = new loop.Dispatcher();
|
||||
|
@ -156,8 +159,9 @@ describe("loop.store.ConversationStore", function () {
|
|||
dispatcher.dispatch(
|
||||
new sharedActions.ConnectionFailure({reason: "fake"}));
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.releaseCallData);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.releaseCallData, "42");
|
||||
sinon.assert.calledOnce(navigator.mozLoop.calls.clearCallInProgress);
|
||||
sinon.assert.calledWithExactly(
|
||||
navigator.mozLoop.calls.clearCallInProgress, "42");
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -221,40 +225,29 @@ describe("loop.store.ConversationStore", function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe("#gatherCallData", function() {
|
||||
describe("#setupWindowData", function() {
|
||||
var fakeSetupWindowData;
|
||||
|
||||
beforeEach(function() {
|
||||
store.set({callState: CALL_STATES.INIT});
|
||||
|
||||
navigator.mozLoop = {
|
||||
getCallData: function() {
|
||||
return {
|
||||
contact: contact,
|
||||
callType: sharedUtils.CALL_TYPES.AUDIO_VIDEO
|
||||
};
|
||||
}
|
||||
fakeSetupWindowData = {
|
||||
windowId: "123456",
|
||||
type: "outgoing",
|
||||
contact: contact,
|
||||
callType: sharedUtils.CALL_TYPES.AUDIO_VIDEO
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
delete navigator.mozLoop;
|
||||
});
|
||||
|
||||
it("should set the state to 'gather'", function() {
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.GatherCallData({
|
||||
windowId: "76543218",
|
||||
outgoing: true
|
||||
}));
|
||||
new sharedActions.SetupWindowData(fakeSetupWindowData));
|
||||
|
||||
expect(store.get("callState")).eql(CALL_STATES.GATHER);
|
||||
});
|
||||
|
||||
it("should save the basic call information", function() {
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.GatherCallData({
|
||||
windowId: "123456",
|
||||
outgoing: true
|
||||
}));
|
||||
new sharedActions.SetupWindowData(fakeSetupWindowData));
|
||||
|
||||
expect(store.get("windowId")).eql("123456");
|
||||
expect(store.get("outgoing")).eql(true);
|
||||
|
@ -262,28 +255,16 @@ describe("loop.store.ConversationStore", function () {
|
|||
|
||||
it("should save the basic information from the mozLoop api", function() {
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.GatherCallData({
|
||||
windowId: "123456",
|
||||
outgoing: true
|
||||
}));
|
||||
new sharedActions.SetupWindowData(fakeSetupWindowData));
|
||||
|
||||
expect(store.get("contact")).eql(contact);
|
||||
expect(store.get("callType")).eql(sharedUtils.CALL_TYPES.AUDIO_VIDEO);
|
||||
});
|
||||
|
||||
describe("outgoing calls", function() {
|
||||
var outgoingCallData;
|
||||
|
||||
beforeEach(function() {
|
||||
outgoingCallData = {
|
||||
windowId: "123456",
|
||||
outgoing: true
|
||||
};
|
||||
});
|
||||
|
||||
it("should request the outgoing call data", function() {
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.GatherCallData(outgoingCallData));
|
||||
new sharedActions.SetupWindowData(fakeSetupWindowData));
|
||||
|
||||
sinon.assert.calledOnce(client.setupOutgoingCall);
|
||||
sinon.assert.calledWith(client.setupOutgoingCall,
|
||||
|
@ -291,7 +272,7 @@ describe("loop.store.ConversationStore", function () {
|
|||
});
|
||||
|
||||
it("should include all email addresses in the call data", function() {
|
||||
contact = {
|
||||
fakeSetupWindowData.contact = {
|
||||
name: [ "Mr Smith" ],
|
||||
email: [{
|
||||
type: "home",
|
||||
|
@ -306,7 +287,7 @@ describe("loop.store.ConversationStore", function () {
|
|||
};
|
||||
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.GatherCallData(outgoingCallData));
|
||||
new sharedActions.SetupWindowData(fakeSetupWindowData));
|
||||
|
||||
sinon.assert.calledOnce(client.setupOutgoingCall);
|
||||
sinon.assert.calledWith(client.setupOutgoingCall,
|
||||
|
@ -314,7 +295,7 @@ describe("loop.store.ConversationStore", function () {
|
|||
});
|
||||
|
||||
it("should include trim phone numbers for the call data", function() {
|
||||
contact = {
|
||||
fakeSetupWindowData.contact = {
|
||||
name: [ "Mr Smith" ],
|
||||
tel: [{
|
||||
type: "home",
|
||||
|
@ -324,7 +305,7 @@ describe("loop.store.ConversationStore", function () {
|
|||
};
|
||||
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.GatherCallData(outgoingCallData));
|
||||
new sharedActions.SetupWindowData(fakeSetupWindowData));
|
||||
|
||||
sinon.assert.calledOnce(client.setupOutgoingCall);
|
||||
sinon.assert.calledWith(client.setupOutgoingCall,
|
||||
|
@ -332,7 +313,7 @@ describe("loop.store.ConversationStore", function () {
|
|||
});
|
||||
|
||||
it("should include all email and telephone values in the call data", function() {
|
||||
contact = {
|
||||
fakeSetupWindowData.contact = {
|
||||
name: [ "Mr Smith" ],
|
||||
email: [{
|
||||
type: "home",
|
||||
|
@ -355,7 +336,7 @@ describe("loop.store.ConversationStore", function () {
|
|||
};
|
||||
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.GatherCallData(outgoingCallData));
|
||||
new sharedActions.SetupWindowData(fakeSetupWindowData));
|
||||
|
||||
sinon.assert.calledOnce(client.setupOutgoingCall);
|
||||
sinon.assert.calledWith(client.setupOutgoingCall,
|
||||
|
@ -375,8 +356,8 @@ describe("loop.store.ConversationStore", function () {
|
|||
|
||||
client.setupOutgoingCall.callsArgWith(2, null, callData);
|
||||
|
||||
store.gatherCallData(
|
||||
new sharedActions.GatherCallData(outgoingCallData));
|
||||
store.setupWindowData(
|
||||
new sharedActions.SetupWindowData(fakeSetupWindowData));
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
// Can't use instanceof here, as that matches any action
|
||||
|
@ -389,8 +370,8 @@ describe("loop.store.ConversationStore", function () {
|
|||
it("should dispatch a connection failure action on failure", function() {
|
||||
client.setupOutgoingCall.callsArgWith(2, {});
|
||||
|
||||
store.gatherCallData(
|
||||
new sharedActions.GatherCallData(outgoingCallData));
|
||||
store.setupWindowData(
|
||||
new sharedActions.SetupWindowData(fakeSetupWindowData));
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
// Can't use instanceof here, as that matches any action
|
||||
|
@ -525,8 +506,9 @@ describe("loop.store.ConversationStore", function () {
|
|||
it("should release mozLoop callsData", function() {
|
||||
dispatcher.dispatch(new sharedActions.HangupCall());
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.releaseCallData);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.releaseCallData, "42");
|
||||
sinon.assert.calledOnce(navigator.mozLoop.calls.clearCallInProgress);
|
||||
sinon.assert.calledWithExactly(
|
||||
navigator.mozLoop.calls.clearCallInProgress, "42");
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -565,8 +547,9 @@ describe("loop.store.ConversationStore", function () {
|
|||
it("should release mozLoop callsData", function() {
|
||||
dispatcher.dispatch(new sharedActions.PeerHungupCall());
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.releaseCallData);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.releaseCallData, "42");
|
||||
sinon.assert.calledOnce(navigator.mozLoop.calls.clearCallInProgress);
|
||||
sinon.assert.calledWithExactly(
|
||||
navigator.mozLoop.calls.clearCallInProgress, "42");
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -613,8 +596,9 @@ describe("loop.store.ConversationStore", function () {
|
|||
it("should release mozLoop callsData", function() {
|
||||
dispatcher.dispatch(new sharedActions.CancelCall());
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.releaseCallData);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.releaseCallData, "42");
|
||||
sinon.assert.calledOnce(navigator.mozLoop.calls.clearCallInProgress);
|
||||
sinon.assert.calledWithExactly(
|
||||
navigator.mozLoop.calls.clearCallInProgress, "42");
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -22,31 +22,30 @@ describe("loop.Dispatcher", function () {
|
|||
it("should register a store against an action name", function() {
|
||||
var object = { fake: true };
|
||||
|
||||
dispatcher.register(object, ["gatherCallData"]);
|
||||
dispatcher.register(object, ["getWindowData"]);
|
||||
|
||||
expect(dispatcher._eventData["gatherCallData"][0]).eql(object);
|
||||
expect(dispatcher._eventData["getWindowData"][0]).eql(object);
|
||||
});
|
||||
|
||||
it("should register multiple store against an action name", function() {
|
||||
var object1 = { fake: true };
|
||||
var object2 = { fake2: true };
|
||||
|
||||
dispatcher.register(object1, ["gatherCallData"]);
|
||||
dispatcher.register(object2, ["gatherCallData"]);
|
||||
dispatcher.register(object1, ["getWindowData"]);
|
||||
dispatcher.register(object2, ["getWindowData"]);
|
||||
|
||||
expect(dispatcher._eventData["gatherCallData"][0]).eql(object1);
|
||||
expect(dispatcher._eventData["gatherCallData"][1]).eql(object2);
|
||||
expect(dispatcher._eventData["getWindowData"][0]).eql(object1);
|
||||
expect(dispatcher._eventData["getWindowData"][1]).eql(object2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#dispatch", function() {
|
||||
var gatherStore1, gatherStore2, cancelStore1, connectStore1;
|
||||
var gatherAction, cancelAction, connectAction, resolveCancelStore1;
|
||||
var getDataStore1, getDataStore2, cancelStore1, connectStore1;
|
||||
var getDataAction, cancelAction, connectAction, resolveCancelStore1;
|
||||
|
||||
beforeEach(function() {
|
||||
gatherAction = new sharedActions.GatherCallData({
|
||||
windowId: "42",
|
||||
outgoing: false
|
||||
getDataAction = new sharedActions.GetWindowData({
|
||||
windowId: "42"
|
||||
});
|
||||
|
||||
cancelAction = new sharedActions.CancelCall();
|
||||
|
@ -54,11 +53,11 @@ describe("loop.Dispatcher", function () {
|
|||
sessionData: {}
|
||||
});
|
||||
|
||||
gatherStore1 = {
|
||||
gatherCallData: sinon.stub()
|
||||
getDataStore1 = {
|
||||
getWindowData: sinon.stub()
|
||||
};
|
||||
gatherStore2 = {
|
||||
gatherCallData: sinon.stub()
|
||||
getDataStore2 = {
|
||||
getWindowData: sinon.stub()
|
||||
};
|
||||
cancelStore1 = {
|
||||
cancelCall: sinon.stub()
|
||||
|
@ -67,8 +66,8 @@ describe("loop.Dispatcher", function () {
|
|||
connectCall: function() {}
|
||||
};
|
||||
|
||||
dispatcher.register(gatherStore1, ["gatherCallData"]);
|
||||
dispatcher.register(gatherStore2, ["gatherCallData"]);
|
||||
dispatcher.register(getDataStore1, ["getWindowData"]);
|
||||
dispatcher.register(getDataStore2, ["getWindowData"]);
|
||||
dispatcher.register(cancelStore1, ["cancelCall"]);
|
||||
dispatcher.register(connectStore1, ["connectCall"]);
|
||||
});
|
||||
|
@ -76,33 +75,33 @@ describe("loop.Dispatcher", function () {
|
|||
it("should dispatch an action to the required object", function() {
|
||||
dispatcher.dispatch(cancelAction);
|
||||
|
||||
sinon.assert.notCalled(gatherStore1.gatherCallData);
|
||||
sinon.assert.notCalled(getDataStore1.getWindowData);
|
||||
|
||||
sinon.assert.calledOnce(cancelStore1.cancelCall);
|
||||
sinon.assert.calledWithExactly(cancelStore1.cancelCall, cancelAction);
|
||||
|
||||
sinon.assert.notCalled(gatherStore2.gatherCallData);
|
||||
sinon.assert.notCalled(getDataStore2.getWindowData);
|
||||
});
|
||||
|
||||
it("should dispatch actions to multiple objects", function() {
|
||||
dispatcher.dispatch(gatherAction);
|
||||
dispatcher.dispatch(getDataAction);
|
||||
|
||||
sinon.assert.calledOnce(gatherStore1.gatherCallData);
|
||||
sinon.assert.calledWithExactly(gatherStore1.gatherCallData, gatherAction);
|
||||
sinon.assert.calledOnce(getDataStore1.getWindowData);
|
||||
sinon.assert.calledWithExactly(getDataStore1.getWindowData, getDataAction);
|
||||
|
||||
sinon.assert.notCalled(cancelStore1.cancelCall);
|
||||
|
||||
sinon.assert.calledOnce(gatherStore2.gatherCallData);
|
||||
sinon.assert.calledWithExactly(gatherStore2.gatherCallData, gatherAction);
|
||||
sinon.assert.calledOnce(getDataStore2.getWindowData);
|
||||
sinon.assert.calledWithExactly(getDataStore2.getWindowData, getDataAction);
|
||||
});
|
||||
|
||||
it("should dispatch multiple actions", function() {
|
||||
dispatcher.dispatch(cancelAction);
|
||||
dispatcher.dispatch(gatherAction);
|
||||
dispatcher.dispatch(getDataAction);
|
||||
|
||||
sinon.assert.calledOnce(cancelStore1.cancelCall);
|
||||
sinon.assert.calledOnce(gatherStore1.gatherCallData);
|
||||
sinon.assert.calledOnce(gatherStore2.gatherCallData);
|
||||
sinon.assert.calledOnce(getDataStore1.getWindowData);
|
||||
sinon.assert.calledOnce(getDataStore2.getWindowData);
|
||||
});
|
||||
|
||||
describe("Queued actions", function() {
|
||||
|
@ -110,10 +109,10 @@ describe("loop.Dispatcher", function () {
|
|||
// Restore the stub, so that we can easily add a function to be
|
||||
// returned. Unfortunately, sinon doesn't make this easy.
|
||||
sandbox.stub(connectStore1, "connectCall", function() {
|
||||
dispatcher.dispatch(gatherAction);
|
||||
dispatcher.dispatch(getDataAction);
|
||||
|
||||
sinon.assert.notCalled(gatherStore1.gatherCallData);
|
||||
sinon.assert.notCalled(gatherStore2.gatherCallData);
|
||||
sinon.assert.notCalled(getDataStore1.getWindowData);
|
||||
sinon.assert.notCalled(getDataStore2.getWindowData);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -132,8 +131,8 @@ describe("loop.Dispatcher", function () {
|
|||
|
||||
sinon.assert.calledOnce(connectStore1.connectCall);
|
||||
// These should be called, because the dispatcher synchronously queues actions.
|
||||
sinon.assert.calledOnce(gatherStore1.gatherCallData);
|
||||
sinon.assert.calledOnce(gatherStore2.gatherCallData);
|
||||
sinon.assert.calledOnce(getDataStore1.getWindowData);
|
||||
sinon.assert.calledOnce(getDataStore2.getWindowData);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -31,20 +31,20 @@ describe("loop.store.LocalRoomStore", function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe("#setupEmptyRoom", function() {
|
||||
var store, fakeMozLoop, fakeRoomId, fakeRoomName;
|
||||
describe("#setupWindowData", function() {
|
||||
var store, fakeMozLoop, fakeToken, fakeRoomName;
|
||||
|
||||
beforeEach(function() {
|
||||
fakeRoomId = "337-ff-54";
|
||||
fakeToken = "337-ff-54";
|
||||
fakeRoomName = "Monkeys";
|
||||
fakeMozLoop = {
|
||||
rooms: { getRoomData: sandbox.stub() }
|
||||
rooms: { get: sandbox.stub() }
|
||||
};
|
||||
|
||||
store = new loop.store.LocalRoomStore(
|
||||
{mozLoop: fakeMozLoop, dispatcher: dispatcher});
|
||||
fakeMozLoop.rooms.getRoomData.
|
||||
withArgs(fakeRoomId).
|
||||
fakeMozLoop.rooms.get.
|
||||
withArgs(fakeToken).
|
||||
callsArgOnWith(1, // index of callback argument
|
||||
store, // |this| to call it on
|
||||
null, // args to call the callback with...
|
||||
|
@ -57,8 +57,11 @@ describe("loop.store.LocalRoomStore", function () {
|
|||
done();
|
||||
});
|
||||
|
||||
dispatcher.dispatch(new sharedActions.SetupEmptyRoom(
|
||||
{localRoomId: fakeRoomId}));
|
||||
dispatcher.dispatch(new sharedActions.SetupWindowData({
|
||||
windowId: "42",
|
||||
type: "room",
|
||||
roomToken: fakeToken
|
||||
}));
|
||||
});
|
||||
|
||||
it("should set localRoomId on the store from the action data",
|
||||
|
@ -66,13 +69,16 @@ describe("loop.store.LocalRoomStore", function () {
|
|||
|
||||
store.once("change", function () {
|
||||
expect(store.getStoreState()).
|
||||
to.have.property('localRoomId', fakeRoomId);
|
||||
to.have.property('roomToken', fakeToken);
|
||||
done();
|
||||
});
|
||||
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.SetupEmptyRoom({localRoomId: fakeRoomId}));
|
||||
});
|
||||
dispatcher.dispatch(new sharedActions.SetupWindowData({
|
||||
windowId: "42",
|
||||
type: "room",
|
||||
roomToken: fakeToken
|
||||
}));
|
||||
});
|
||||
|
||||
it("should set serverData.roomName from the getRoomData callback",
|
||||
function(done) {
|
||||
|
@ -83,16 +89,19 @@ describe("loop.store.LocalRoomStore", function () {
|
|||
done();
|
||||
});
|
||||
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.SetupEmptyRoom({localRoomId: fakeRoomId}));
|
||||
dispatcher.dispatch(new sharedActions.SetupWindowData({
|
||||
windowId: "42",
|
||||
type: "room",
|
||||
roomToken: fakeToken
|
||||
}));
|
||||
});
|
||||
|
||||
it("should set error on the store when getRoomData calls back an error",
|
||||
function(done) {
|
||||
|
||||
var fakeError = new Error("fake error");
|
||||
fakeMozLoop.rooms.getRoomData.
|
||||
withArgs(fakeRoomId).
|
||||
fakeMozLoop.rooms.get.
|
||||
withArgs(fakeToken).
|
||||
callsArgOnWith(1, // index of callback argument
|
||||
store, // |this| to call it on
|
||||
fakeError); // args to call the callback with...
|
||||
|
@ -102,8 +111,11 @@ describe("loop.store.LocalRoomStore", function () {
|
|||
done();
|
||||
});
|
||||
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.SetupEmptyRoom({localRoomId: fakeRoomId}));
|
||||
dispatcher.dispatch(new sharedActions.SetupWindowData({
|
||||
windowId: "42",
|
||||
type: "room",
|
||||
roomToken: fakeToken
|
||||
}));
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -326,4 +326,27 @@ describe("loop.store.RoomListStore", function () {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#openRoom", function() {
|
||||
var store, fakeMozLoop;
|
||||
|
||||
beforeEach(function() {
|
||||
fakeMozLoop = {
|
||||
rooms: {
|
||||
open: sinon.spy()
|
||||
}
|
||||
};
|
||||
store = new loop.store.RoomListStore({
|
||||
dispatcher: dispatcher,
|
||||
mozLoop: fakeMozLoop
|
||||
});
|
||||
});
|
||||
|
||||
it("should open the room via mozLoop", function() {
|
||||
dispatcher.dispatch(new sharedActions.OpenRoom({roomToken: "42abc"}));
|
||||
|
||||
sinon.assert.calledOnce(fakeMozLoop.rooms.open);
|
||||
sinon.assert.calledWithExactly(fakeMozLoop.rooms.open, "42abc");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
|
||||
Cu.import("resource://services-common/utils.js");
|
||||
Cu.import("resource:///modules/loop/LoopRooms.jsm");
|
||||
Cu.import("resource:///modules/Chat.jsm");
|
||||
|
||||
let openChatOrig = Chat.open;
|
||||
|
||||
const kRooms = new Map([
|
||||
["_nxD4V4FflQ", {
|
||||
|
@ -51,6 +54,38 @@ let roomDetail = {
|
|||
}]
|
||||
};
|
||||
|
||||
const kRoomUpdates = {
|
||||
"1": {
|
||||
participants: []
|
||||
},
|
||||
"2": {
|
||||
participants: [{
|
||||
displayName: "Alexis",
|
||||
account: "alexis@example.com",
|
||||
roomConnectionId: "2a1787a6-4a73-43b5-ae3e-906ec1e763cb"
|
||||
}]
|
||||
},
|
||||
"3": {
|
||||
participants: [{
|
||||
displayName: "Adam",
|
||||
roomConnectionId: "781f012b-f1ea-4ce1-9105-7cfc36fb4ec7"
|
||||
}]
|
||||
},
|
||||
"4": {
|
||||
participants: [{
|
||||
displayName: "Adam",
|
||||
roomConnectionId: "781f012b-f1ea-4ce1-9105-7cfc36fb4ec7"
|
||||
}, {
|
||||
displayName: "Alexis",
|
||||
account: "alexis@example.com",
|
||||
roomConnectionId: "2a1787a6-4a73-43b5-ae3e-906ec1e763cb"
|
||||
}, {
|
||||
displayName: "Ruharb",
|
||||
roomConnectionId: "5de6281c-6568-455f-af08-c0b0a973100e"
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
const kCreateRoomProps = {
|
||||
roomName: "UX Discussion",
|
||||
expiresIn: 5,
|
||||
|
@ -64,6 +99,76 @@ const kCreateRoomData = {
|
|||
expiresAt: 1405534180
|
||||
};
|
||||
|
||||
const normalizeRoom = function(room) {
|
||||
delete room.currSize;
|
||||
if (!("participants" in room)) {
|
||||
let name = room.roomName;
|
||||
for (let key of Object.getOwnPropertyNames(roomDetail)) {
|
||||
room[key] = roomDetail[key];
|
||||
}
|
||||
room.roomName = name;
|
||||
}
|
||||
return room;
|
||||
};
|
||||
|
||||
const compareRooms = function(room1, room2) {
|
||||
Assert.deepEqual(normalizeRoom(room1), normalizeRoom(room2));
|
||||
};
|
||||
|
||||
// LoopRooms emits various events. Test if they work as expected here.
|
||||
let gExpectedAdds = [];
|
||||
let gExpectedUpdates = [];
|
||||
let gExpectedJoins = {};
|
||||
let gExpectedLeaves = {};
|
||||
|
||||
const onRoomAdded = function(e, room) {
|
||||
let expectedIds = gExpectedAdds.map(room => room.roomToken);
|
||||
let idx = expectedIds.indexOf(room.roomToken);
|
||||
Assert.ok(idx > -1, "Added room should be expected");
|
||||
let expected = gExpectedAdds[idx];
|
||||
compareRooms(room, expected);
|
||||
gExpectedAdds.splice(idx, 1);
|
||||
};
|
||||
|
||||
const onRoomUpdated = function(e, room) {
|
||||
let idx = gExpectedUpdates.indexOf(room.roomToken);
|
||||
Assert.ok(idx > -1, "Updated room should be expected");
|
||||
gExpectedUpdates.splice(idx, 1);
|
||||
};
|
||||
|
||||
const onRoomJoined = function(e, roomToken, participant) {
|
||||
let participants = gExpectedJoins[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];
|
||||
}
|
||||
};
|
||||
|
||||
const onRoomLeft = function(e, roomToken, participant) {
|
||||
let participants = gExpectedLeaves[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];
|
||||
}
|
||||
};
|
||||
|
||||
const parseQueryString = function(qs) {
|
||||
let map = {};
|
||||
let parts = qs.split("=");
|
||||
for (let i = 0, l = parts.length; i < l; ++i) {
|
||||
if (i % 2 === 1) {
|
||||
map[parts[i - 1]] = parts[i];
|
||||
}
|
||||
}
|
||||
return map;
|
||||
};
|
||||
|
||||
add_task(function* setup_server() {
|
||||
loopServer.registerPathHandler("/registration", (req, res) => {
|
||||
res.setStatusLine(null, 200, "OK");
|
||||
|
@ -82,7 +187,14 @@ add_task(function* setup_server() {
|
|||
|
||||
res.write(JSON.stringify(kCreateRoomData));
|
||||
} else {
|
||||
res.write(JSON.stringify([...kRooms.values()]));
|
||||
if (req.queryString) {
|
||||
let qs = parseQueryString(req.queryString);
|
||||
let room = kRooms.get("_nxD4V4FflQ");
|
||||
room.participants = kRoomUpdates[qs.version].participants;
|
||||
res.write(JSON.stringify([room]));
|
||||
} else {
|
||||
res.write(JSON.stringify([...kRooms.values()]));
|
||||
}
|
||||
}
|
||||
|
||||
res.processAsync();
|
||||
|
@ -116,27 +228,13 @@ add_task(function* setup_server() {
|
|||
res.processAsync();
|
||||
res.finish();
|
||||
});
|
||||
|
||||
yield MozLoopService.promiseRegisteredWithServers();
|
||||
});
|
||||
|
||||
const normalizeRoom = function(room) {
|
||||
delete room.currSize;
|
||||
if (!("participants" in room)) {
|
||||
let name = room.roomName;
|
||||
for (let key of Object.getOwnPropertyNames(roomDetail)) {
|
||||
room[key] = roomDetail[key];
|
||||
}
|
||||
room.roomName = name;
|
||||
}
|
||||
return room;
|
||||
};
|
||||
|
||||
const compareRooms = function(room1, room2) {
|
||||
Assert.deepEqual(normalizeRoom(room1), normalizeRoom(room2));
|
||||
};
|
||||
|
||||
// Test if fetching a list of all available rooms works correctly.
|
||||
add_task(function* test_getAllRooms() {
|
||||
yield MozLoopService.promiseRegisteredWithServers();
|
||||
|
||||
gExpectedAdds.push(...kRooms.values());
|
||||
let rooms = yield LoopRooms.promise("getAll");
|
||||
Assert.equal(rooms.length, 3);
|
||||
for (let room of rooms) {
|
||||
|
@ -144,32 +242,97 @@ add_task(function* test_getAllRooms() {
|
|||
}
|
||||
});
|
||||
|
||||
// Test if fetching a room works correctly.
|
||||
add_task(function* test_getRoom() {
|
||||
yield MozLoopService.promiseRegisteredWithServers();
|
||||
|
||||
let roomToken = "_nxD4V4FflQ";
|
||||
let room = yield LoopRooms.promise("get", roomToken);
|
||||
Assert.deepEqual(room, kRooms.get(roomToken));
|
||||
});
|
||||
|
||||
// Test if fetching a room with incorrect token or return values yields an error.
|
||||
add_task(function* test_errorStates() {
|
||||
yield Assert.rejects(LoopRooms.promise("get", "error401"), /Not Found/, "Fetching a non-existent room should fail");
|
||||
yield Assert.rejects(LoopRooms.promise("get", "errorMalformed"), /SyntaxError/, "Wrong message format should reject");
|
||||
});
|
||||
|
||||
// Test if creating a new room works as expected.
|
||||
add_task(function* test_createRoom() {
|
||||
let eventCalled = false;
|
||||
LoopRooms.once("add", (e, room) => {
|
||||
compareRooms(room, kCreateRoomProps);
|
||||
eventCalled = true;
|
||||
});
|
||||
gExpectedAdds.push(kCreateRoomProps);
|
||||
let room = yield LoopRooms.promise("create", kCreateRoomProps);
|
||||
compareRooms(room, kCreateRoomProps);
|
||||
Assert.ok(eventCalled, "Event should have fired");
|
||||
});
|
||||
|
||||
// Test if opening a new room window works correctly.
|
||||
add_task(function* test_openRoom() {
|
||||
let openedUrl;
|
||||
Chat.open = function(contentWindow, origin, title, url) {
|
||||
openedUrl = url;
|
||||
};
|
||||
|
||||
LoopRooms.open("fakeToken");
|
||||
|
||||
Assert.ok(openedUrl, "should open a chat window");
|
||||
|
||||
// Stop the busy kicking in for following tests.
|
||||
let windowId = openedUrl.match(/about:loopconversation\#(\d+)$/)[1];
|
||||
let windowData = MozLoopService.getConversationWindowData(windowId);
|
||||
|
||||
Assert.equal(windowData.type, "room", "window data should contain room as the type");
|
||||
Assert.equal(windowData.roomToken, "fakeToken", "window data should have the roomToken");
|
||||
});
|
||||
|
||||
// Test if push updates function as expected.
|
||||
add_task(function* test_roomUpdates() {
|
||||
gExpectedUpdates.push("_nxD4V4FflQ");
|
||||
gExpectedLeaves["_nxD4V4FflQ"] = [
|
||||
"2a1787a6-4a73-43b5-ae3e-906ec1e763cb",
|
||||
"781f012b-f1ea-4ce1-9105-7cfc36fb4ec7"
|
||||
];
|
||||
roomsPushNotification("1");
|
||||
yield waitForCondition(() => Object.getOwnPropertyNames(gExpectedLeaves).length === 0);
|
||||
|
||||
gExpectedUpdates.push("_nxD4V4FflQ");
|
||||
gExpectedJoins["_nxD4V4FflQ"] = ["2a1787a6-4a73-43b5-ae3e-906ec1e763cb"];
|
||||
roomsPushNotification("2");
|
||||
yield waitForCondition(() => Object.getOwnPropertyNames(gExpectedJoins).length === 0);
|
||||
|
||||
gExpectedUpdates.push("_nxD4V4FflQ");
|
||||
gExpectedJoins["_nxD4V4FflQ"] = ["781f012b-f1ea-4ce1-9105-7cfc36fb4ec7"];
|
||||
gExpectedLeaves["_nxD4V4FflQ"] = ["2a1787a6-4a73-43b5-ae3e-906ec1e763cb"];
|
||||
roomsPushNotification("3");
|
||||
yield waitForCondition(() => Object.getOwnPropertyNames(gExpectedLeaves).length === 0);
|
||||
|
||||
gExpectedUpdates.push("_nxD4V4FflQ");
|
||||
gExpectedJoins["_nxD4V4FflQ"] = [
|
||||
"2a1787a6-4a73-43b5-ae3e-906ec1e763cb",
|
||||
"5de6281c-6568-455f-af08-c0b0a973100e"];
|
||||
roomsPushNotification("4");
|
||||
yield waitForCondition(() => Object.getOwnPropertyNames(gExpectedJoins).length === 0);
|
||||
});
|
||||
|
||||
// Test if the event emitter implementation doesn't leak and is working as expected.
|
||||
add_task(function* () {
|
||||
Assert.strictEqual(gExpectedAdds.length, 0, "No room additions should be expected anymore");
|
||||
Assert.strictEqual(gExpectedUpdates.length, 0, "No room updates should be expected anymore");
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
setupFakeLoopServer();
|
||||
|
||||
LoopRooms.on("add", onRoomAdded);
|
||||
LoopRooms.on("update", onRoomUpdated);
|
||||
LoopRooms.on("joined", onRoomJoined);
|
||||
LoopRooms.on("left", onRoomLeft);
|
||||
|
||||
do_register_cleanup(function () {
|
||||
// Revert original Chat.open implementation
|
||||
Chat.open = openChatOrig;
|
||||
|
||||
LoopRooms.off("add", onRoomAdded);
|
||||
LoopRooms.off("update", onRoomUpdated);
|
||||
LoopRooms.off("joined", onRoomJoined);
|
||||
LoopRooms.off("left", onRoomLeft);
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
|
|
@ -26,8 +26,10 @@ add_test(function test_busy_2guest_calls() {
|
|||
|
||||
MozLoopService.promiseRegisteredWithServers().then(() => {
|
||||
let opened = 0;
|
||||
Chat.open = function() {
|
||||
let windowId;
|
||||
Chat.open = function(contentWindow, origin, title, url) {
|
||||
opened++;
|
||||
windowId = url.match(/about:loopconversation\#(\d+)$/)[1];
|
||||
};
|
||||
|
||||
mockPushHandler.notify(1, MozLoopService.channelIDs.callsGuest);
|
||||
|
@ -35,7 +37,7 @@ add_test(function test_busy_2guest_calls() {
|
|||
waitForCondition(() => {return actionReceived && opened > 0}).then(() => {
|
||||
do_check_true(opened === 1, "should open only one chat window");
|
||||
do_check_true(actionReceived, "should respond with busy/reject to second call");
|
||||
LoopCalls.releaseCallData(firstCallId);
|
||||
LoopCalls.clearCallInProgress(windowId);
|
||||
run_next_test();
|
||||
}, () => {
|
||||
do_throw("should have opened a chat window for first call and rejected second call");
|
||||
|
@ -49,8 +51,10 @@ add_test(function test_busy_1fxa_1guest_calls() {
|
|||
|
||||
MozLoopService.promiseRegisteredWithServers().then(() => {
|
||||
let opened = 0;
|
||||
Chat.open = function() {
|
||||
let windowId;
|
||||
Chat.open = function(contentWindow, origin, title, url) {
|
||||
opened++;
|
||||
windowId = url.match(/about:loopconversation\#(\d+)$/)[1];
|
||||
};
|
||||
|
||||
mockPushHandler.notify(1, MozLoopService.channelIDs.callsFxA);
|
||||
|
@ -59,7 +63,7 @@ add_test(function test_busy_1fxa_1guest_calls() {
|
|||
waitForCondition(() => {return actionReceived && opened > 0}).then(() => {
|
||||
do_check_true(opened === 1, "should open only one chat window");
|
||||
do_check_true(actionReceived, "should respond with busy/reject to second call");
|
||||
LoopCalls.releaseCallData(firstCallId);
|
||||
LoopCalls.clearCallInProgress(windowId);
|
||||
run_next_test();
|
||||
}, () => {
|
||||
do_throw("should have opened a chat window for first call and rejected second call");
|
||||
|
@ -73,8 +77,10 @@ add_test(function test_busy_2fxa_calls() {
|
|||
|
||||
MozLoopService.promiseRegisteredWithServers().then(() => {
|
||||
let opened = 0;
|
||||
Chat.open = function() {
|
||||
let windowId;
|
||||
Chat.open = function(contentWindow, origin, title, url) {
|
||||
opened++;
|
||||
windowId = url.match(/about:loopconversation\#(\d+)$/)[1];
|
||||
};
|
||||
|
||||
mockPushHandler.notify(1, MozLoopService.channelIDs.callsFxA);
|
||||
|
@ -82,7 +88,7 @@ add_test(function test_busy_2fxa_calls() {
|
|||
waitForCondition(() => {return actionReceived && opened > 0}).then(() => {
|
||||
do_check_true(opened === 1, "should open only one chat window");
|
||||
do_check_true(actionReceived, "should respond with busy/reject to second call");
|
||||
LoopCalls.releaseCallData(firstCallId);
|
||||
LoopCalls.clearCallInProgress(windowId);
|
||||
run_next_test();
|
||||
}, () => {
|
||||
do_throw("should have opened a chat window for first call and rejected second call");
|
||||
|
@ -96,8 +102,10 @@ add_test(function test_busy_1guest_1fxa_calls() {
|
|||
|
||||
MozLoopService.promiseRegisteredWithServers().then(() => {
|
||||
let opened = 0;
|
||||
Chat.open = function() {
|
||||
let windowId;
|
||||
Chat.open = function(contentWindow, origin, title, url) {
|
||||
opened++;
|
||||
windowId = url.match(/about:loopconversation\#(\d+)$/)[1];
|
||||
};
|
||||
|
||||
mockPushHandler.notify(1, MozLoopService.channelIDs.callsGuest);
|
||||
|
@ -106,7 +114,7 @@ add_test(function test_busy_1guest_1fxa_calls() {
|
|||
waitForCondition(() => {return actionReceived && opened > 0}).then(() => {
|
||||
do_check_true(opened === 1, "should open only one chat window");
|
||||
do_check_true(actionReceived, "should respond with busy/reject to second call");
|
||||
LoopCalls.releaseCallData(firstCallId);
|
||||
LoopCalls.clearCallInProgress(windowId);
|
||||
run_next_test();
|
||||
}, () => {
|
||||
do_throw("should have opened a chat window for first call and rejected second call");
|
||||
|
|
|
@ -26,11 +26,11 @@ add_task(function test_startDirectCall_opens_window() {
|
|||
do_check_true(!!openedUrl, "should open a chat window");
|
||||
|
||||
// Stop the busy kicking in for following tests.
|
||||
let callId = openedUrl.match(/about:loopconversation\#outgoing\/(.*)/)[1];
|
||||
LoopCalls.releaseCallData(callId);
|
||||
let windowId = openedUrl.match(/about:loopconversation\#(\d+)$/)[1];
|
||||
LoopCalls.clearCallInProgress(windowId);
|
||||
});
|
||||
|
||||
add_task(function test_startDirectCall_getCallData() {
|
||||
add_task(function test_startDirectCall_getConversationWindowData() {
|
||||
let openedUrl;
|
||||
Chat.open = function(contentWindow, origin, title, url) {
|
||||
openedUrl = url;
|
||||
|
@ -38,15 +38,15 @@ add_task(function test_startDirectCall_getCallData() {
|
|||
|
||||
LoopCalls.startDirectCall(contact, "audio-video");
|
||||
|
||||
let callId = openedUrl.match(/about:loopconversation\#outgoing\/(.*)/)[1];
|
||||
let windowId = openedUrl.match(/about:loopconversation\#(\d+)$/)[1];
|
||||
|
||||
let callData = LoopCalls.getCallData(callId);
|
||||
let callData = MozLoopService.getConversationWindowData(windowId);
|
||||
|
||||
do_check_eq(callData.callType, "audio-video", "should have the correct call type");
|
||||
do_check_eq(callData.contact, contact, "should have the contact details");
|
||||
|
||||
// Stop the busy kicking in for following tests.
|
||||
LoopCalls.releaseCallData(callId);
|
||||
LoopCalls.clearCallInProgress(windowId);
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
|
|
|
@ -2352,7 +2352,12 @@ let E10SUINotification = {
|
|||
checkStatus: function() {
|
||||
let skipE10sChecks = false;
|
||||
try {
|
||||
// This order matters, because
|
||||
// browser.tabs.remote.autostart.disabled-because-using-a11y is not
|
||||
// always defined and will throw when not present.
|
||||
// privacy.trackingprotection.enabled is always defined.
|
||||
skipE10sChecks = (UpdateChannel.get() != "nightly") ||
|
||||
Services.prefs.getBoolPref("privacy.trackingprotection.enabled") ||
|
||||
Services.prefs.getBoolPref("browser.tabs.remote.autostart.disabled-because-using-a11y");
|
||||
} catch(e) {}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ ac_add_options --with-l10n-base=../../../l10n
|
|||
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
|
||||
ac_add_options --enable-update-packaging
|
||||
|
||||
if test "${MOZ_UPDATE_CHANNEL}" = "nightly" -o "${MOZ_UPDATE_CHANNEL}" = "aurora"; then
|
||||
if test "${MOZ_UPDATE_CHANNEL}" = "nightly"; then
|
||||
ac_add_options --with-macbundlename-prefix=Firefox
|
||||
fi
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ ac_add_options --enable-dtrace
|
|||
# Nightlies only since this has a cost in performance
|
||||
ac_add_options --enable-js-diagnostics
|
||||
|
||||
if test "${MOZ_UPDATE_CHANNEL}" = "nightly" -o "${MOZ_UPDATE_CHANNEL}" = "aurora"; then
|
||||
if test "${MOZ_UPDATE_CHANNEL}" = "nightly"; then
|
||||
ac_add_options --with-macbundlename-prefix=Firefox
|
||||
fi
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ ac_add_options --with-google-oauth-api-keyfile=/builds/google-oauth-api.key
|
|||
# Needed to enable breakpad in application.ini
|
||||
export MOZILLA_OFFICIAL=1
|
||||
|
||||
if test "${MOZ_UPDATE_CHANNEL}" = "nightly" -o "${MOZ_UPDATE_CHANNEL}" = "aurora"; then
|
||||
if test "${MOZ_UPDATE_CHANNEL}" = "nightly"; then
|
||||
ac_add_options --with-macbundlename-prefix=Firefox
|
||||
fi
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ ac_add_options --disable-unified-compilation
|
|||
# Package js shell.
|
||||
export MOZ_PACKAGE_JSSHELL=1
|
||||
|
||||
if test "${MOZ_UPDATE_CHANNEL}" = "nightly" -o "${MOZ_UPDATE_CHANNEL}" = "aurora"; then
|
||||
if test "${MOZ_UPDATE_CHANNEL}" = "nightly"; then
|
||||
ac_add_options --with-macbundlename-prefix=Firefox
|
||||
fi
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ whitelist['nightly']['linux64'] += [
|
|||
]
|
||||
|
||||
whitelist['nightly']['macosx-universal'] += [
|
||||
'if test "${MOZ_UPDATE_CHANNEL}" = "nightly" -o "${MOZ_UPDATE_CHANNEL}" = "aurora"; then',
|
||||
'if test "${MOZ_UPDATE_CHANNEL}" = "nightly"; then',
|
||||
'ac_add_options --with-macbundlename-prefix=Firefox',
|
||||
'fi',
|
||||
'mk_add_options MOZ_MAKE_FLAGS="-j12"',
|
||||
|
|
|
@ -1817,7 +1817,7 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
|
|||
background-image: -moz-image-rect(url("chrome://global/skin/icons/close.svg"), 0, 64, 16, 48);
|
||||
}
|
||||
|
||||
.tab-close-button:not([selected]):not(:hover):-moz-lwtheme-brighttext {
|
||||
#TabsToolbar[brighttext] .tab-close-button:not([selected]):not(:hover) {
|
||||
background-image: -moz-image-rect(url("chrome://global/skin/icons/close.svg"), 0, 80, 16, 64);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,3 +3,61 @@
|
|||
% file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
%include ../shared/devedition.inc.css
|
||||
|
||||
.tab-close-button[selected]:not(:hover) {
|
||||
background-image: -moz-image-rect(url("chrome://global/skin/icons/close.svg"), 0, 80, 16, 64);
|
||||
}
|
||||
|
||||
/* The menubar should match the dark theme */
|
||||
#toolbar-menubar {
|
||||
-moz-appearance: none;
|
||||
}
|
||||
#main-menubar {
|
||||
color: var(--chrome-color);
|
||||
}
|
||||
#main-menubar > menu:not([open]) {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* Allow buttons with -moz-appearance set to look normal on hover and open states */
|
||||
#navigator-toolbox .toolbarbutton-1:not(.subviewbutton):-moz-any(:hover, [open="true"]),
|
||||
toolbarbutton.bookmark-item:not(.subviewbutton):-moz-any(:hover, [open="true"]) {
|
||||
color: initial;
|
||||
}
|
||||
|
||||
/* Square back and forward buttons */
|
||||
#back-button:not(:-moz-lwtheme) > .toolbarbutton-icon,
|
||||
#forward-button:not(:-moz-lwtheme) > .toolbarbutton-icon {
|
||||
margin: 0;
|
||||
border: none;
|
||||
padding: 2px 6px;
|
||||
background: #252C33;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* Override a box shadow for disabled back button */
|
||||
#main-window:not([customizing]) #back-button[disabled] > .toolbarbutton-icon {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
#back-button:hover:not([disabled="true"]) > .toolbarbutton-icon,
|
||||
#forward-button:hover:not([disabled="true"]) > .toolbarbutton-icon {
|
||||
background: #1B2127 !important;
|
||||
}
|
||||
|
||||
#back-button > .toolbarbutton-icon {
|
||||
border-radius: 2px 0 0 2px !important;
|
||||
}
|
||||
|
||||
.urlbar-history-dropmarker {
|
||||
-moz-appearance: none;
|
||||
padding: 0 3px;
|
||||
list-style-image: var(--urlbar-dropmarker-url);
|
||||
-moz-image-region: var(--urlbar-dropmarker-region);
|
||||
}
|
||||
|
||||
/* Add the proper background for tab overflow */
|
||||
#alltabs-button,
|
||||
#new-tab-button {
|
||||
background: var(--chrome-background-color);
|
||||
}
|
||||
|
|
|
@ -198,6 +198,8 @@ browser.jar:
|
|||
skin/classic/browser/translating-16@2x.png (../shared/translation/translating-16@2x.png)
|
||||
skin/classic/browser/translation-16.png (../shared/translation/translation-16.png)
|
||||
skin/classic/browser/translation-16@2x.png (../shared/translation/translation-16@2x.png)
|
||||
skin/classic/browser/devedition/search.svg (../shared/devedition/search.svg)
|
||||
skin/classic/browser/devedition/urlbar-history-dropmarker.svg (../shared/devedition/urlbar-history-dropmarker.svg)
|
||||
* skin/classic/browser/devtools/common.css (../shared/devtools/common.css)
|
||||
* skin/classic/browser/devtools/dark-theme.css (../shared/devtools/dark-theme.css)
|
||||
* skin/classic/browser/devtools/light-theme.css (../shared/devtools/light-theme.css)
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
0 1px 0 hsla(0,0%,100%,.5) inset;
|
||||
|
||||
--toolbarbutton-active-background: hsla(0,0%,0%,.02) linear-gradient(hsla(0,0%,0%,.12), transparent) border-box;
|
||||
--toolbarbutton-active-bordercolor: hsla(0,0%,0%,.3);
|
||||
--toolbarbutton-active-boxshadow: 0 1px 0 hsla(0,0%,100%,.5),
|
||||
0 1px 0 hsla(0,0%,0%,.05) inset,
|
||||
0 1px 1px hsla(0,0%,0%,.2) inset;
|
||||
|
@ -606,7 +607,7 @@ toolbar .toolbarbutton-1[type="menu-button"]:not(:-moz-any([disabled],[open]))[b
|
|||
toolbar .toolbarbutton-1[type="menu-button"]:not(:-moz-any([disabled],[open],[buttonover])):hover:active > .toolbarbutton-menubutton-dropmarker,
|
||||
toolbar .toolbarbutton-1[type="menu-button"][open]:not([disabled]) > .toolbarbutton-menubutton-dropmarker {
|
||||
background: var(--toolbarbutton-active-background);
|
||||
border-color: hsla(0,0%,0%,.3);
|
||||
border-color: var(--toolbarbutton-active-bordercolor);
|
||||
box-shadow: var(--toolbarbutton-active-boxshadow);
|
||||
transition-duration: 10ms;
|
||||
}
|
||||
|
@ -766,7 +767,7 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-ic
|
|||
}
|
||||
|
||||
#webide-button@toolbarButtonPressed@ {
|
||||
-moz-image-region: rect(18px, 342px, 36px, 324px);
|
||||
-moz-image-region: rect(18px, 738px, 36px, 720px);
|
||||
}
|
||||
|
||||
#new-tab-button@toolbarButtonPressed@ {
|
||||
|
|
|
@ -3,3 +3,85 @@
|
|||
% file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
%include ../shared/devedition.inc.css
|
||||
|
||||
/* Include extra space on left/right for dragging since there is no space above
|
||||
the tabs */
|
||||
#main-window[tabsintitlebar] #TabsToolbar {
|
||||
padding-left: 50px;
|
||||
padding-right: 50px;
|
||||
margin-bottom: 0; /* Don't overlap the inner highlight at the top of the nav-bar */
|
||||
}
|
||||
|
||||
/* Get rid of 1px bright strip at the top of window */
|
||||
#main-window[tabsintitlebar] #titlebar-content {
|
||||
background: var(--chrome-background-color);
|
||||
}
|
||||
|
||||
/* Resize things so that the native titlebar is in line with the tabs */
|
||||
#main-window[tabsintitlebar] > #titlebar > #titlebar-content > #titlebar-buttonbox-container,
|
||||
#main-window[tabsintitlebar] > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > #titlebar-fullscreen-button {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
/* Square back and forward buttons. Need !important on these because there
|
||||
are a lot of more specific selectors sprinkled around elsewhere for changing
|
||||
background / shadows for different states */
|
||||
#back-button,
|
||||
#forward-button {
|
||||
height: 22px !important;
|
||||
box-shadow: none !important;
|
||||
border: none !important;
|
||||
background: #252C33 !important;
|
||||
}
|
||||
|
||||
#back-button:hover:not([disabled="true"]),
|
||||
#forward-button:hover:not([disabled="true"]) {
|
||||
background: #1B2127 !important;
|
||||
}
|
||||
|
||||
#back-button {
|
||||
border-radius: 3px 0 0 3px !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
#back-button:hover:active:not([disabled="true"]) {
|
||||
-moz-image-region: rect(18px, 54px, 36px, 36px);
|
||||
}
|
||||
|
||||
/* Use smaller back button icon */
|
||||
@media (min-resolution: 2dppx) {
|
||||
#back-button {
|
||||
-moz-image-region: rect(0, 108px, 36px, 72px);
|
||||
}
|
||||
|
||||
#back-button:hover:active:not([disabled="true"]) {
|
||||
-moz-image-region: rect(36px, 108px, 72px, 72px);
|
||||
}
|
||||
}
|
||||
|
||||
#forward-button:hover:active:not(:-moz-lwtheme) {
|
||||
background-image: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* Use forward-facing magnifying glass for the search box */
|
||||
.search-go-button {
|
||||
list-style-image: url("chrome://browser/skin/devedition/search.svg#search-icon-mac-inverted");
|
||||
}
|
||||
|
||||
/* Don't use default colors when in full screen */
|
||||
#main-window:not([customizing]) #navigator-toolbox[inFullscreen] > #TabsToolbar:not(:-moz-lwtheme) {
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
||||
/* Tab styling - make sure to use an inverted icon for the selected tab
|
||||
(brighttext only covers the unselected tabs) */
|
||||
.tab-close-button[selected=true]:not(:hover) {
|
||||
-moz-image-region: rect(0, 64px, 16px, 48px);
|
||||
}
|
||||
@media (min-resolution: 2dppx) {
|
||||
.tab-close-button[selected=true]:not(:hover) {
|
||||
-moz-image-region: rect(0, 128px, 32px, 96px);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -319,6 +319,8 @@ browser.jar:
|
|||
skin/classic/browser/translating-16@2x.png (../shared/translation/translating-16@2x.png)
|
||||
skin/classic/browser/translation-16.png (../shared/translation/translation-16.png)
|
||||
skin/classic/browser/translation-16@2x.png (../shared/translation/translation-16@2x.png)
|
||||
skin/classic/browser/devedition/search.svg (../shared/devedition/search.svg)
|
||||
skin/classic/browser/devedition/urlbar-history-dropmarker.svg (../shared/devedition/urlbar-history-dropmarker.svg)
|
||||
* skin/classic/browser/devtools/common.css (../shared/devtools/common.css)
|
||||
* skin/classic/browser/devtools/dark-theme.css (../shared/devtools/dark-theme.css)
|
||||
* skin/classic/browser/devtools/light-theme.css (../shared/devtools/light-theme.css)
|
||||
|
|
|
@ -20,6 +20,15 @@
|
|||
|
||||
%include ../browser.inc
|
||||
|
||||
:root {
|
||||
--panel-ui-button-background-image: linear-gradient(to bottom, transparent, hsla(0,0%,100%,.3) 30%, hsla(0,0%,100%,.3) 70%, transparent),
|
||||
linear-gradient(to bottom, transparent, hsla(210,54%,20%,.3) 30%, hsla(210,54%,20%,.3) 70%, transparent),
|
||||
linear-gradient(to bottom, transparent, hsla(0,0%,100%,.3) 30%, hsla(0,0%,100%,.3) 70%, transparent);
|
||||
--panel-ui-button-background-size: 1px calc(100% - 1px), 1px calc(100% - 1px), 1px calc(100% - 1px) !important;
|
||||
--panel-ui-button-background-position: 0px 0px, 1px 0px, 2px 0px;
|
||||
--panel-ui-button-background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
#PanelUI-popup #PanelUI-contents:empty {
|
||||
height: 128px;
|
||||
}
|
||||
|
@ -87,12 +96,10 @@
|
|||
}
|
||||
|
||||
#PanelUI-button {
|
||||
background-image: linear-gradient(to bottom, transparent, hsla(0,0%,100%,.3) 30%, hsla(0,0%,100%,.3) 70%, transparent),
|
||||
linear-gradient(to bottom, transparent, hsla(210,54%,20%,.3) 30%, hsla(210,54%,20%,.3) 70%, transparent),
|
||||
linear-gradient(to bottom, transparent, hsla(0,0%,100%,.3) 30%, hsla(0,0%,100%,.3) 70%, transparent);
|
||||
background-size: 1px calc(100% - 1px), 1px calc(100% - 1px), 1px calc(100% - 1px) !important;
|
||||
background-position: 0px 0px, 1px 0px, 2px 0px;
|
||||
background-repeat: no-repeat;
|
||||
background-image: var(--panel-ui-button-background-image);
|
||||
background-size: var(--panel-ui-button-background-size);
|
||||
background-position: var(--panel-ui-button-background-position);
|
||||
background-repeat: var(--panel-ui-button-background-repeat);
|
||||
}
|
||||
|
||||
#PanelUI-button:-moz-locale-dir(rtl) {
|
||||
|
|
|
@ -1,3 +1,227 @@
|
|||
% 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/.
|
||||
|
||||
/* devedition.css is loaded in browser.xul after browser.css when it is
|
||||
preffed on. The bulk of the styling is here in the shared file, but
|
||||
there are overrides for each platform in their devedition.css files. */
|
||||
|
||||
:root {
|
||||
/* Chrome */
|
||||
--space-above-tabbar: 1px;
|
||||
--chrome-background-color: #1C2126;
|
||||
--chrome-color: #F5F7FA;
|
||||
--chrome-secondary-background-color: #39424D;
|
||||
--chrome-navigator-toolbox-separator-color: rgba(0,0,0,.2);
|
||||
|
||||
/* Tabs */
|
||||
--tabs-toolbar-color: #F5F7FA;
|
||||
--tab-background-color: #1C2126;
|
||||
--tab-color: #ced3d9;
|
||||
--tab-hover-background-color: hsla(206,37%,4%,.5);
|
||||
--tab-separator-color: #464C50;
|
||||
--tab-selection-color: #f5f7fa;
|
||||
--tab-selection-background-color: #1a4666;
|
||||
--tab-selection-box-shadow: 0 2px 0 #d7f1ff inset,
|
||||
0 8px 3px -5px #2b82bf inset,
|
||||
0 -2px 0 rgba(0,0,0,.2) inset;
|
||||
|
||||
/* Toolbar buttons */
|
||||
--toolbarbutton-hover-background: rgba(25,33, 38,.6) linear-gradient(rgba(25,33,38,.6), rgba(25,33,38,.6)) padding-box;
|
||||
--toolbarbutton-hover-boxshadow: none;
|
||||
--toolbarbutton-hover-bordercolor: rgba(25,33,38,.6);
|
||||
--toolbarbutton-active-background: rgba(25,33,38,1) linear-gradient(rgba(25,33,38,1), rgba(25,33,38,1)) border-box;
|
||||
--toolbarbutton-active-boxshadow: none;
|
||||
--toolbarbutton-active-bordercolor: rgba(25,33,38,.8);
|
||||
--toolbarbutton-checkedhover-backgroundcolor: #1D4F73;
|
||||
--toolbarbutton-combined-boxshadow: none;
|
||||
--toolbarbutton-combined-backgroundimage: linear-gradient(#5F6670 0, #5F6670 18px);
|
||||
--toolbarbutton-text-shadow: none;
|
||||
|
||||
/* Identity box */
|
||||
--identity-box-chrome-color: #46afe3;
|
||||
--identity-box-chrome-background-image: linear-gradient(#5F6670 0, #5F6670 100%);
|
||||
--identity-box-verified-background-image: linear-gradient(#5F6670 0, #5F6670 100%);
|
||||
--verified-identity-box-backgroundcolor: transparent;
|
||||
|
||||
/* Url and search bars */
|
||||
--url-and-searchbar-background-color: #171B1F;
|
||||
--url-and-searchbar-color: #fff;
|
||||
--urlbar-dropmarker-url: url("chrome://browser/skin/devedition/urlbar-history-dropmarker.svg");
|
||||
--urlbar-dropmarker-region: rect(0px, 11px, 14px, 0px);
|
||||
--urlbar-dropmarker-active-region: rect(0px, 22px, 14px, 11px);
|
||||
--urlbar-dropmarker-2x-url: url("chrome://browser/skin/devedition/urlbar-history-dropmarker.svg");
|
||||
--urlbar-dropmarker-2x-region: rect(0px, 11px, 14px, 0px);
|
||||
--urlbar-dropmarker-active-2x-region: rect(0px, 22px, 14px, 11px);
|
||||
|
||||
/* Menu button separator */
|
||||
--panel-ui-button-background-image: linear-gradient(to bottom, #5E6670, #5E6670);
|
||||
--panel-ui-button-background-size: 1px calc(100% - 1px);
|
||||
--panel-ui-button-background-position: 1px 0px;
|
||||
}
|
||||
|
||||
.searchbar-dropmarker-image {
|
||||
--searchbar-dropmarker-url: url("chrome://browser/skin/devtools/dropmarker.svg");
|
||||
--searchbar-dropmarker-2x-url: url("chrome://browser/skin/devtools/dropmarker.svg");
|
||||
}
|
||||
|
||||
/* Give some space to drag the window around while customizing
|
||||
(normal space to left and right of tabs doesn't work in this case) */
|
||||
#main-window[tabsintitlebar][customizing] {
|
||||
--space-above-tabbar: 9px;
|
||||
}
|
||||
|
||||
.tabbrowser-arrowscrollbox > .arrowscrollbox-scrollbox {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
#navigator-toolbox ::-moz-selection {
|
||||
background-color: #074D75;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Change the base colors for the browser chrome */
|
||||
|
||||
#tabbrowser-tabs,
|
||||
#TabsToolbar,
|
||||
#browser-panel {
|
||||
background: var(--chrome-background-color);
|
||||
color: var(--chrome-color);
|
||||
}
|
||||
|
||||
#navigator-toolbox::after {
|
||||
background: var(--chrome-navigator-toolbox-separator-color)
|
||||
}
|
||||
|
||||
#navigator-toolbox > toolbar:not(#TabsToolbar):not(#toolbar-menubar),
|
||||
#browser-bottombox {
|
||||
background: var(--chrome-secondary-background-color) !important;
|
||||
color: var(--chrome-color);
|
||||
}
|
||||
|
||||
#navigator-toolbox .toolbarbutton-1:not(.subviewbutton):not(:hover):not([open]),
|
||||
toolbarbutton.bookmark-item:not(.subviewbutton):not(:hover):not([open]) {
|
||||
color: var(--chrome-color);
|
||||
text-shadow: var(--toolbarbutton-text-shadow);
|
||||
}
|
||||
|
||||
/* Using toolbar[brighttext] instead of important to override linux */
|
||||
toolbar[brighttext] #downloads-indicator-counter {
|
||||
text-shadow: var(--toolbarbutton-text-shadow);
|
||||
color: var(--chrome-color);
|
||||
}
|
||||
|
||||
#TabsToolbar {
|
||||
text-shadow: none !important;
|
||||
color: var(--chrome-color) !important; /* Make sure that the brighttext attribute is added */
|
||||
}
|
||||
|
||||
/* URL bar and search bar*/
|
||||
.searchbar-textbox,
|
||||
#urlbar {
|
||||
background-color: var(--url-and-searchbar-background-color) !important;
|
||||
background-image: none !important;
|
||||
color: var(--url-and-searchbar-color);
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
window:not([chromehidden~="toolbar"]) #urlbar-wrapper {
|
||||
overflow: -moz-hidden-unscrollable;
|
||||
clip-path: none;
|
||||
-moz-margin-start: 0;
|
||||
}
|
||||
|
||||
/* Make the white notication box stick out less. */
|
||||
#notification-popup-box {
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Nav bar specific stuff */
|
||||
#nav-bar {
|
||||
margin-top: 0 !important;
|
||||
border: none !important;
|
||||
border-radius: 0 !important;
|
||||
box-shadow: 0 -1px var(--chrome-navigator-toolbox-separator-color) !important;
|
||||
background-image: none !important;
|
||||
}
|
||||
|
||||
/* No extra vertical padding for nav bar */
|
||||
#nav-bar-customization-target,
|
||||
#nav-bar {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
/* Use smaller back button icon */
|
||||
#back-button {
|
||||
-moz-image-region: rect(0, 54px, 18px, 36px);
|
||||
}
|
||||
|
||||
.search-go-button {
|
||||
/* !important is needed because searchbar.css is loaded after this */
|
||||
-moz-image-region: auto !important;
|
||||
list-style-image: url("chrome://browser/skin/devedition/search.svg#search-icon-inverted");
|
||||
}
|
||||
|
||||
.tab-background {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#tabbrowser-tabs[movingtab] > .tabbrowser-tab[beforeselected]:not([last-visible-tab])::after,
|
||||
.tabbrowser-tab:not([selected]):not([afterselected-visible]):not([afterhovered]):not([first-visible-tab]):not(:hover)::before,
|
||||
#tabbrowser-tabs:not([overflow]) > .tabbrowser-tab[last-visible-tab]:not([selected]):not([beforehovered]):not(:hover)::after {
|
||||
background-image: linear-gradient(to top, #474C50, #474C50);
|
||||
background-position: 1px 0;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 1px 100%;
|
||||
}
|
||||
|
||||
.tabbrowser-arrowscrollbox > .scrollbutton-down,
|
||||
.tabbrowser-arrowscrollbox > .scrollbutton-up {
|
||||
background-color: var(--tab-background-color);
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator:not([collapsed]),
|
||||
.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator:not([collapsed]) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.tabbrowser-tab {
|
||||
/* We normally rely on other tab elements for pointer events, but this
|
||||
theme hides those so we need it set here instead */
|
||||
pointer-events: auto;
|
||||
color: var(--tab-color);
|
||||
background-color: var(--tab-background-color);
|
||||
}
|
||||
|
||||
.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):hover,
|
||||
.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):hover,
|
||||
.tabbrowser-tab:hover {
|
||||
background-color: var(--tab-hover-background-color);
|
||||
color: var(--tab-hover-color);
|
||||
}
|
||||
|
||||
.tabbrowser-tab[selected] {
|
||||
color: var(--tab-selection-color);
|
||||
background-color: var(--tab-selection-background-color);
|
||||
box-shadow: var(--tab-selection-box-shadow);
|
||||
}
|
||||
|
||||
/* New tab buttons */
|
||||
#TabsToolbar > #new-tab-button,
|
||||
.tabs-newtab-button {
|
||||
background-image: none !important;
|
||||
margin: 0 !important;
|
||||
width: 35px !important;
|
||||
}
|
||||
|
||||
#TabsToolbar > #new-tab-button:hover,
|
||||
.tabs-newtab-button:hover {
|
||||
/* Important needed because !important is used in browser.css */
|
||||
background-color: var(--tab-hover-background-color) !important;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px" y="0px"
|
||||
viewBox="0 0 16 16"
|
||||
enable-background="new 0 0 16 16"
|
||||
width="16"
|
||||
height="16"
|
||||
xml:space="preserve">
|
||||
<style>
|
||||
use:not(:target) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
use {
|
||||
fill: #797C80;
|
||||
}
|
||||
|
||||
use[id*="-inverted"] {
|
||||
fill: #B6BABF;
|
||||
}
|
||||
|
||||
use[id*="-mac"] {
|
||||
transform: translate(16px) scaleX(-1);
|
||||
}
|
||||
</style>
|
||||
<defs style="display: none;">
|
||||
<path id="search" fill-rule="evenodd" clip-rule="evenodd" d="M9.356,1.178c-3.014,0-5.458,2.45-5.458,5.472c0,1.086,0.32,2.096,0.864,2.947
|
||||
l-3.279,3.287c-0.396,0.397-0.396,1.041,0,1.438l0.202,0.202c0.396,0.397,1.039,0.397,1.435,0l3.275-3.283
|
||||
c0.854,0.554,1.869,0.88,2.962,0.88c3.014,0,5.458-2.45,5.458-5.471C14.814,3.627,12.371,1.178,9.356,1.178z M9.356,10.001
|
||||
c-1.847,0-3.344-1.501-3.344-3.352c0-1.851,1.497-3.352,3.344-3.352c1.846,0,3.344,1.501,3.344,3.352
|
||||
C12.7,8.501,11.203,10.001,9.356,10.001z"/>
|
||||
</defs>
|
||||
<use id="search-icon" xlink:href="#search"/>
|
||||
<use id="search-icon-inverted" xlink:href="#search"/>
|
||||
<use id="search-icon-mac" xlink:href="#search"/>
|
||||
<use id="search-icon-mac-inverted" xlink:href="#search"/>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 1.4 KiB |
|
@ -0,0 +1,19 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="33" height="14" viewBox="0 0 33 14" enable-background="new 0 0 33 14">
|
||||
<defs style="display: none;">
|
||||
<polygon points="0,0 5.5,7 11,0" id="dropmarker-shape"/>
|
||||
</defs>
|
||||
<style>
|
||||
use {
|
||||
fill: #B6BABF;
|
||||
}
|
||||
.hover {
|
||||
fill: #61BDEB;
|
||||
}
|
||||
.active {
|
||||
fill: #39ACE6;
|
||||
}
|
||||
</style>
|
||||
<use xlink:href="#dropmarker-shape" style="transform: translate(0, 4px)"></use>
|
||||
<use xlink:href="#dropmarker-shape" style="transform: translate(11px, 4px)" class="hover"></use>
|
||||
<use xlink:href="#dropmarker-shape" style="transform: translate(22px, 4px)" class="active"></use>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 691 B |
|
@ -3,3 +3,98 @@
|
|||
% file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
%include ../shared/devedition.inc.css
|
||||
|
||||
#TabsToolbar::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#back-button > .toolbarbutton-icon,
|
||||
#forward-button > .toolbarbutton-icon {
|
||||
background: #252C33 !important;
|
||||
border-radius: 0 !important;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
padding: 2px 6px !important;
|
||||
margin: 0 !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
#back-button > .toolbarbutton-icon {
|
||||
border-radius: 2px 0 0 2px !important;
|
||||
}
|
||||
|
||||
#nav-bar .toolbarbutton-1:not([type=menu-button]),
|
||||
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button,
|
||||
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
#browser-panel,
|
||||
#titlebar-content {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Ensure that the entire background is styled when maximized */
|
||||
#main-window[sizemode="maximized"] #browser-panel {
|
||||
background: var(--chrome-background-color) !important;
|
||||
}
|
||||
|
||||
/* The menu items need to be visible when the entire background is styled */
|
||||
#main-window[sizemode="maximized"] #main-menubar {
|
||||
color: var(--chrome-color);
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#main-window[sizemode="maximized"] #main-menubar > menu:not(:-moz-window-inactive) {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* Restore draggable space on the sides of tabs when maximized */
|
||||
#main-window[sizemode="maximized"] .tabbrowser-arrowscrollbox > .arrowscrollbox-scrollbox {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
@media (-moz-windows-classic) {
|
||||
#main-window[sizemode="maximized"] #TabsToolbar {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
#toolbar-menubar {
|
||||
text-shadow: none !important;
|
||||
}
|
||||
|
||||
#main-window[tabsintitlebar][sizemode="normal"]:not([inFullscreen])[chromehidden~="menubar"] #toolbar-menubar ~ #TabsToolbar,
|
||||
#main-window[tabsintitlebar][sizemode="normal"]:not([inFullscreen]) #toolbar-menubar[autohide="true"][inactive] ~ #TabsToolbar {
|
||||
margin-top: 22px;
|
||||
}
|
||||
|
||||
|
||||
/* Enough space so that the dark background doesn't begin until after the
|
||||
* window resize controls end on Aero Basic, which has different positioning for the
|
||||
* window caption buttons, and therefore needs to be special-cased.
|
||||
*/
|
||||
@media (-moz-windows-default-theme) {
|
||||
@media not all and (-moz-windows-compositor) {
|
||||
#main-window[tabsintitlebar][sizemode="normal"]:not([inFullscreen])[chromehidden~="menubar"] #toolbar-menubar ~ #TabsToolbar,
|
||||
#main-window[tabsintitlebar][sizemode="normal"]:not([inFullscreen]) #toolbar-menubar[autohide="true"][inactive] ~ #TabsToolbar {
|
||||
margin-top: 28px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.searchbar-dropmarker-image {
|
||||
/* Reset image-region from the windows theme */
|
||||
-moz-image-region: auto !important;
|
||||
/* Add margin otherwise it looks weird */
|
||||
-moz-margin-start: 2px;
|
||||
}
|
||||
|
||||
/* Tab styling - make sure to use an inverted icon for the selected tab
|
||||
(brighttext only covers the unselected tabs) */
|
||||
.tab-close-button[selected=true]:not(:hover) {
|
||||
-moz-image-region: rect(0, 64px, 16px, 48px);
|
||||
}
|
||||
|
|
|
@ -233,6 +233,8 @@ browser.jar:
|
|||
skin/classic/browser/translating-16@2x.png (../shared/translation/translating-16@2x.png)
|
||||
skin/classic/browser/translation-16.png (../shared/translation/translation-16.png)
|
||||
skin/classic/browser/translation-16@2x.png (../shared/translation/translation-16@2x.png)
|
||||
skin/classic/browser/devedition/search.svg (../shared/devedition/search.svg)
|
||||
skin/classic/browser/devedition/urlbar-history-dropmarker.svg (../shared/devedition/urlbar-history-dropmarker.svg)
|
||||
* skin/classic/browser/devtools/common.css (../shared/devtools/common.css)
|
||||
* skin/classic/browser/devtools/dark-theme.css (../shared/devtools/dark-theme.css)
|
||||
* skin/classic/browser/devtools/light-theme.css (../shared/devtools/light-theme.css)
|
||||
|
@ -664,6 +666,8 @@ browser.jar:
|
|||
skin/classic/aero/browser/translating-16@2x.png (../shared/translation/translating-16@2x.png)
|
||||
skin/classic/aero/browser/translation-16.png (../shared/translation/translation-16.png)
|
||||
skin/classic/aero/browser/translation-16@2x.png (../shared/translation/translation-16@2x.png)
|
||||
skin/classic/aero/browser/devedition/search.svg (../shared/devedition/search.svg)
|
||||
skin/classic/aero/browser/devedition/urlbar-history-dropmarker.svg (../shared/devedition/urlbar-history-dropmarker.svg)
|
||||
* skin/classic/aero/browser/devtools/common.css (../shared/devtools/common.css)
|
||||
* skin/classic/aero/browser/devtools/dark-theme.css (../shared/devtools/dark-theme.css)
|
||||
* skin/classic/aero/browser/devtools/light-theme.css (../shared/devtools/light-theme.css)
|
||||
|
|
|
@ -2186,6 +2186,10 @@ GK_ATOM(Home, "Home")
|
|||
GK_ATOM(Clear, "Clear")
|
||||
GK_ATOM(VolumeUp, "VolumeUp")
|
||||
GK_ATOM(VolumeDown, "VolumeDown")
|
||||
GK_ATOM(NextTrack, "NextTrack")
|
||||
GK_ATOM(PreviousTrack, "PreviousTrack")
|
||||
GK_ATOM(MediaStop, "MediaStop")
|
||||
GK_ATOM(PlayPause, "PlayPause")
|
||||
GK_ATOM(Menu, "Menu")
|
||||
GK_ATOM(New, "New")
|
||||
GK_ATOM(Open, "Open")
|
||||
|
|
|
@ -33,6 +33,9 @@ public final class ThumbnailHelper {
|
|||
|
||||
public static final float THUMBNAIL_ASPECT_RATIO = 0.571f; // this is a 4:7 ratio (as per UX decision)
|
||||
|
||||
// Should actually be more like 0.83 (140/168) but various roundings mean that 0.9 works better
|
||||
public static final float NEW_TABLET_THUMBNAIL_ASPECT_RATIO = 0.9f;
|
||||
|
||||
public static enum CachePolicy {
|
||||
STORE,
|
||||
NO_STORE
|
||||
|
@ -118,11 +121,15 @@ public final class ThumbnailHelper {
|
|||
// Apply any pending width updates.
|
||||
mWidth = mPendingWidth.get();
|
||||
|
||||
mHeight = Math.round(mWidth * THUMBNAIL_ASPECT_RATIO);
|
||||
if(NewTabletUI.isEnabled(GeckoAppShell.getContext())) {
|
||||
mHeight = Math.round(mWidth * NEW_TABLET_THUMBNAIL_ASPECT_RATIO);
|
||||
} else {
|
||||
mHeight = Math.round(mWidth * THUMBNAIL_ASPECT_RATIO);
|
||||
}
|
||||
|
||||
int pixelSize = (GeckoAppShell.getScreenDepth() == 24) ? 4 : 2;
|
||||
int capacity = mWidth * mHeight * pixelSize;
|
||||
Log.d(LOGTAG, "Using new thumbnail size: " + capacity + " (width " + mWidth + ")");
|
||||
Log.d(LOGTAG, "Using new thumbnail size: " + capacity + " (width " + mWidth + " - height " + mHeight + ")");
|
||||
if (mBuffer == null || mBuffer.capacity() != capacity) {
|
||||
if (mBuffer != null) {
|
||||
mBuffer = DirectBufferAllocator.free(mBuffer);
|
||||
|
|
|
@ -224,7 +224,7 @@ public class TopSitesGridItemView extends RelativeLayout {
|
|||
ImageLoader.with(getContext()).cancelRequest(mThumbnailView);
|
||||
|
||||
mThumbnailView.setScaleType(SCALE_TYPE_THUMBNAIL);
|
||||
mThumbnailView.setImageBitmap(thumbnail);
|
||||
mThumbnailView.setImageBitmap(thumbnail, true);
|
||||
mThumbnailView.setBackgroundDrawable(null);
|
||||
}
|
||||
|
||||
|
@ -279,7 +279,7 @@ public class TopSitesGridItemView extends RelativeLayout {
|
|||
}
|
||||
|
||||
mThumbnailView.setScaleType(SCALE_TYPE_FAVICON);
|
||||
mThumbnailView.setImageBitmap(favicon);
|
||||
mThumbnailView.setImageBitmap(favicon, false);
|
||||
|
||||
if (mFaviconURL != null) {
|
||||
final int bgColor = Favicons.getFaviconColor(mFaviconURL);
|
||||
|
|
|
@ -5,14 +5,18 @@
|
|||
|
||||
package org.mozilla.gecko.home;
|
||||
|
||||
import org.mozilla.gecko.NewTabletUI;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.ThumbnailHelper;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ImageView;
|
||||
|
@ -35,11 +39,16 @@ public class TopSitesThumbnailView extends ImageView {
|
|||
// Paint for drawing the border.
|
||||
private final Paint mBorderPaint;
|
||||
|
||||
private boolean mResize = false;
|
||||
private int mWidth;
|
||||
private int mHeight;
|
||||
|
||||
public TopSitesThumbnailView(Context context) {
|
||||
this(context, null);
|
||||
|
||||
// A border will be drawn if needed.
|
||||
setWillNotDraw(false);
|
||||
|
||||
}
|
||||
|
||||
public TopSitesThumbnailView(Context context, AttributeSet attrs) {
|
||||
|
@ -56,6 +65,35 @@ public class TopSitesThumbnailView extends ImageView {
|
|||
mBorderPaint.setStyle(Paint.Style.STROKE);
|
||||
}
|
||||
|
||||
public void setImageBitmap(Bitmap bm, boolean resize) {
|
||||
super.setImageBitmap(bm);
|
||||
mResize = resize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImageResource(int resId) {
|
||||
super.setImageResource(resId);
|
||||
mResize = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImageDrawable(Drawable drawable) {
|
||||
super.setImageDrawable(drawable);
|
||||
mResize = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
if(NewTabletUI.isEnabled(getContext()) && mResize) {
|
||||
setScaleType(ScaleType.MATRIX);
|
||||
RectF rect = new RectF(0, 0, mWidth, mHeight);
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.setRectToRect(rect, rect, Matrix.ScaleToFit.CENTER);
|
||||
setImageMatrix(matrix);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Measure the view to determine the measured width and height.
|
||||
* The height is constrained by the measured width.
|
||||
|
@ -69,9 +107,9 @@ public class TopSitesThumbnailView extends ImageView {
|
|||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
// Force the height based on the aspect ratio.
|
||||
final int width = getMeasuredWidth();
|
||||
final int height = (int) (width * ThumbnailHelper.THUMBNAIL_ASPECT_RATIO);
|
||||
setMeasuredDimension(width, height);
|
||||
mWidth = getMeasuredWidth();
|
||||
mHeight = (int) (mWidth * ThumbnailHelper.THUMBNAIL_ASPECT_RATIO);
|
||||
setMeasuredDimension(mWidth, mHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
После Ширина: | Высота: | Размер: 7.7 KiB |
До Ширина: | Высота: | Размер: 1.5 KiB |
После Ширина: | Высота: | Размер: 4.3 KiB |
До Ширина: | Высота: | Размер: 1.0 KiB |
После Ширина: | Высота: | Размер: 9.8 KiB |
До Ширина: | Высота: | Размер: 1.9 KiB |
После Ширина: | Высота: | Размер: 17 KiB |
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/background_normal"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<bitmap android:src="@drawable/tab_panel_tab_globe"
|
||||
android:gravity="center"
|
||||
/>
|
||||
</item>
|
||||
</layer-list>
|
|
@ -52,7 +52,7 @@
|
|||
<org.mozilla.gecko.widget.ThumbnailView android:id="@+id/thumbnail"
|
||||
android:layout_width="@dimen/new_tablet_tab_thumbnail_width"
|
||||
android:layout_height="@dimen/new_tablet_tab_thumbnail_height"
|
||||
android:src="@drawable/tab_thumbnail_default"/>
|
||||
/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
|
|
|
@ -24,8 +24,7 @@
|
|||
|
||||
<org.mozilla.gecko.widget.ThumbnailView android:id="@+id/thumbnail"
|
||||
android:layout_width="@dimen/tab_thumbnail_width"
|
||||
android:layout_height="@dimen/tab_thumbnail_height"
|
||||
android:src="@drawable/tab_thumbnail_default"/>
|
||||
android:layout_height="@dimen/tab_thumbnail_height"/>
|
||||
|
||||
<LinearLayout android:layout_width="@dimen/tab_thumbnail_width"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
@ -24,8 +24,7 @@
|
|||
|
||||
<org.mozilla.gecko.widget.ThumbnailView android:id="@+id/thumbnail"
|
||||
android:layout_width="@dimen/tab_thumbnail_width"
|
||||
android:layout_height="@dimen/tab_thumbnail_height"
|
||||
android:src="@drawable/tab_thumbnail_default"/>
|
||||
android:layout_height="@dimen/tab_thumbnail_height"/>
|
||||
|
||||
</org.mozilla.gecko.widget.TabThumbnailWrapper>
|
||||
|
||||
|
|
|
@ -95,16 +95,14 @@ public class TabsLayoutItemView extends LinearLayout
|
|||
mTabId = tab.getId();
|
||||
|
||||
Drawable thumbnailImage = tab.getThumbnail();
|
||||
if (thumbnailImage != null) {
|
||||
setThumbnail(thumbnailImage);
|
||||
} else {
|
||||
mThumbnail.setImageResource(R.drawable.tab_thumbnail_default);
|
||||
}
|
||||
mThumbnail.setImageDrawable(thumbnailImage);
|
||||
|
||||
if (mThumbnailWrapper != null) {
|
||||
mThumbnailWrapper.setRecording(tab.isRecording());
|
||||
}
|
||||
mTitle.setText(tab.getDisplayTitle());
|
||||
mCloseButton.setTag(this);
|
||||
|
||||
}
|
||||
|
||||
public int getTabId() {
|
||||
|
|
|
@ -202,6 +202,7 @@ abstract class BaseTest extends BaseRobocopTest {
|
|||
*/
|
||||
protected final void focusUrlBar() {
|
||||
// Click on the browser toolbar to enter editing mode
|
||||
mSolo.waitForView(R.id.browser_toolbar);
|
||||
final View toolbarView = mSolo.getView(R.id.browser_toolbar);
|
||||
mSolo.clickOnView(toolbarView);
|
||||
|
||||
|
@ -221,10 +222,10 @@ abstract class BaseTest extends BaseRobocopTest {
|
|||
}
|
||||
|
||||
protected final void enterUrl(String url) {
|
||||
final EditText urlEditView = (EditText) mSolo.getView(R.id.url_edit_text);
|
||||
|
||||
focusUrlBar();
|
||||
|
||||
final EditText urlEditView = (EditText) mSolo.getView(R.id.url_edit_text);
|
||||
|
||||
// Send the keys for the URL we want to enter
|
||||
mSolo.clearEditText(urlEditView);
|
||||
mSolo.enterText(urlEditView, url);
|
||||
|
@ -329,25 +330,27 @@ abstract class BaseTest extends BaseRobocopTest {
|
|||
return result;
|
||||
}
|
||||
|
||||
// TODO: With Robotium 4.2, we should use Condition and waitForCondition instead.
|
||||
// Future boolean tests should not use this method.
|
||||
protected final boolean waitForTest(BooleanTest t, int timeout) {
|
||||
long end = SystemClock.uptimeMillis() + timeout;
|
||||
while (SystemClock.uptimeMillis() < end) {
|
||||
if (t.test()) {
|
||||
return true;
|
||||
/**
|
||||
* @deprecated use {@link #waitForCondition(Condition, int)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
protected final boolean waitForTest(final BooleanTest t, final int timeout) {
|
||||
final boolean isSatisfied = mSolo.waitForCondition(new Condition() {
|
||||
@Override
|
||||
public boolean isSatisfied() {
|
||||
return t.test();
|
||||
}
|
||||
mSolo.sleep(100);
|
||||
}, timeout);
|
||||
|
||||
if (!isSatisfied) {
|
||||
// log out wait failure for diagnostic purposes only;
|
||||
// a failed wait may be normal and does not necessarily
|
||||
// warrant a test assertion/failure
|
||||
mAsserter.dumpLog("waitForTest timeout after " + timeout + " ms");
|
||||
}
|
||||
// log out wait failure for diagnostic purposes only;
|
||||
// a failed wait may be normal and does not necessarily
|
||||
// warrant a test assertion/failure
|
||||
mAsserter.dumpLog("waitForTest timeout after "+timeout+" ms");
|
||||
return false;
|
||||
return isSatisfied;
|
||||
}
|
||||
|
||||
// TODO: With Robotium 4.2, we should use Condition and waitForCondition instead.
|
||||
// Future boolean tests should not implement this interface.
|
||||
protected interface BooleanTest {
|
||||
public boolean test();
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
package org.mozilla.gecko.widget;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
|
@ -21,6 +23,7 @@ public class ThumbnailView extends ImageView {
|
|||
private int mWidthSpec = -1;
|
||||
private int mHeightSpec = -1;
|
||||
private boolean mLayoutChanged;
|
||||
private boolean mScale = false;
|
||||
|
||||
public ThumbnailView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
@ -30,13 +33,18 @@ public class ThumbnailView extends ImageView {
|
|||
|
||||
@Override
|
||||
public void onDraw(Canvas canvas) {
|
||||
if (!mScale) {
|
||||
super.onDraw(canvas);
|
||||
return;
|
||||
}
|
||||
|
||||
Drawable d = getDrawable();
|
||||
if (mLayoutChanged) {
|
||||
int w1 = d.getIntrinsicWidth();
|
||||
int h1 = d.getIntrinsicHeight();
|
||||
int w2 = getWidth();
|
||||
int h2 = getHeight();
|
||||
|
||||
|
||||
float scale = (w2/h2 < w1/h1) ? (float)h2/h1 : (float)w2/w1;
|
||||
mMatrix.setScale(scale, scale);
|
||||
}
|
||||
|
@ -59,4 +67,19 @@ public class ThumbnailView extends ImageView {
|
|||
}
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setImageDrawable(Drawable drawable) {
|
||||
if (drawable == null) {
|
||||
drawable = getResources().getDrawable(R.drawable.tab_panel_tab_background);
|
||||
setScaleType(ScaleType.FIT_XY);
|
||||
mScale = false;
|
||||
} else {
|
||||
mScale = true;
|
||||
setScaleType(ScaleType.FIT_CENTER);
|
||||
}
|
||||
|
||||
super.setImageDrawable(drawable);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2519,6 +2519,8 @@ nsNavBookmarks::SetKeywordForBookmark(int64_t aBookmarkId,
|
|||
NS_IMETHODIMP
|
||||
nsNavBookmarks::GetKeywordForURI(nsIURI* aURI, nsAString& aKeyword)
|
||||
{
|
||||
PLACES_WARN_DEPRECATED();
|
||||
|
||||
NS_ENSURE_ARG(aURI);
|
||||
aKeyword.Truncate(0);
|
||||
|
||||
|
|
|
@ -348,10 +348,6 @@ add_task(function test_bookmarks() {
|
|||
let k = bs.getKeywordForBookmark(kwTestItemId);
|
||||
do_check_eq("bar", k);
|
||||
|
||||
// test getKeywordForURI
|
||||
k = bs.getKeywordForURI(uri("http://keywordtest.com/"));
|
||||
do_check_eq("bar", k);
|
||||
|
||||
// test getURIForKeyword
|
||||
let u = bs.getURIForKeyword("bar");
|
||||
do_check_eq("http://keywordtest.com/", u.spec);
|
||||
|
|
|
@ -11,8 +11,16 @@ function check_bookmark_keyword(aItemId, aKeyword)
|
|||
function check_uri_keyword(aURI, aKeyword)
|
||||
{
|
||||
let keyword = aKeyword ? aKeyword.toLowerCase() : null;
|
||||
do_check_eq(PlacesUtils.bookmarks.getKeywordForURI(aURI),
|
||||
keyword);
|
||||
|
||||
for (let bm of PlacesUtils.getBookmarksForURI(aURI)) {
|
||||
let kid = PlacesUtils.bookmarks.getKeywordForBookmark(bm);
|
||||
if (kid && !keyword) {
|
||||
Assert.ok(false, `${aURI.spec} should not have a keyword`);
|
||||
} else if (keyword && kid == keyword) {
|
||||
Assert.equal(kid, keyword, "Found the keyword");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (aKeyword) {
|
||||
// This API can't tell which uri the user wants, so it returns a random one.
|
||||
|
|
|
@ -88,8 +88,6 @@ add_task(function () {
|
|||
|
||||
ok(PlacesUtils.bookmarks.isBookmarked(bookmarkUri),
|
||||
"Bookmark should be bookmarked, data should be retrievable");
|
||||
is(bookmarkKeyword, PlacesUtils.bookmarks.getKeywordForURI(bookmarkUri),
|
||||
"Check bookmark uri keyword");
|
||||
is(getPlacesItemsCount(), count,
|
||||
"Check the new bookmark items count");
|
||||
is(isBookmarkAltered(), false, "Check if bookmark has been visited");
|
||||
|
|
|
@ -2357,15 +2357,25 @@ SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, n
|
|||
// create a default profile
|
||||
nsCOMPtr<nsIToolkitProfile> profile;
|
||||
nsresult rv = aProfileSvc->CreateProfile(nullptr, // choose a default dir for us
|
||||
#ifdef MOZ_DEV_EDITION
|
||||
NS_LITERAL_CSTRING("dev-edition-default"),
|
||||
#else
|
||||
NS_LITERAL_CSTRING("default"),
|
||||
#endif
|
||||
getter_AddRefs(profile));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
#ifndef MOZ_DEV_EDITION
|
||||
aProfileSvc->SetDefaultProfile(profile);
|
||||
#endif
|
||||
aProfileSvc->Flush();
|
||||
rv = profile->Lock(nullptr, aResult);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (aProfileName)
|
||||
#ifdef MOZ_DEV_EDITION
|
||||
aProfileName->AssignLiteral("dev-edition-default");
|
||||
#else
|
||||
aProfileName->AssignLiteral("default");
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
@ -4630,6 +4640,13 @@ mozilla::BrowserTabsRemoteAutostart()
|
|||
}
|
||||
#endif
|
||||
|
||||
bool tpEnabled = Preferences::GetBool("privacy.trackingprotection.enabled",
|
||||
false);
|
||||
if (tpEnabled) {
|
||||
gBrowserTabsRemoteAutostart = false;
|
||||
LogE10sBlockedReason("Tracking protection is enabled");
|
||||
}
|
||||
|
||||
mozilla::Telemetry::Accumulate(mozilla::Telemetry::E10S_AUTOSTART, gBrowserTabsRemoteAutostart);
|
||||
if (Preferences::GetBool("browser.enabledE10SFromPrompt", false)) {
|
||||
mozilla::Telemetry::Accumulate(mozilla::Telemetry::E10S_STILL_ACCEPTED_FROM_PROMPT,
|
||||
|
|
|
@ -250,6 +250,18 @@ nsWindowBase::DispatchCommandEvent(uint32_t aEventCommand)
|
|||
case APPCOMMAND_SEND_MAIL:
|
||||
command = nsGkAtoms::SendMail;
|
||||
break;
|
||||
case APPCOMMAND_MEDIA_NEXTTRACK:
|
||||
command = nsGkAtoms::NextTrack;
|
||||
break;
|
||||
case APPCOMMAND_MEDIA_PREVIOUSTRACK:
|
||||
command = nsGkAtoms::PreviousTrack;
|
||||
break;
|
||||
case APPCOMMAND_MEDIA_STOP:
|
||||
command = nsGkAtoms::MediaStop;
|
||||
break;
|
||||
case APPCOMMAND_MEDIA_PLAY_PAUSE:
|
||||
command = nsGkAtoms::PlayPause;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -287,6 +299,10 @@ nsWindowBase::HandleAppCommandMsg(WPARAM aWParam,
|
|||
case APPCOMMAND_FORWARD_MAIL:
|
||||
case APPCOMMAND_REPLY_TO_MAIL:
|
||||
case APPCOMMAND_SEND_MAIL:
|
||||
case APPCOMMAND_MEDIA_NEXTTRACK:
|
||||
case APPCOMMAND_MEDIA_PREVIOUSTRACK:
|
||||
case APPCOMMAND_MEDIA_STOP:
|
||||
case APPCOMMAND_MEDIA_PLAY_PAUSE:
|
||||
// We shouldn't consume the message always because if we don't handle
|
||||
// the message, the sender (typically, utility of keyboard or mouse)
|
||||
// may send other key messages which indicate well known shortcut key.
|
||||
|
|
|
@ -142,16 +142,17 @@
|
|||
#define APPCOMMAND_BROWSER_FAVORITES 6
|
||||
#define APPCOMMAND_BROWSER_HOME 7
|
||||
|
||||
#define APPCOMMAND_MEDIA_NEXTTRACK 11
|
||||
#define APPCOMMAND_MEDIA_PREVIOUSTRACK 12
|
||||
#define APPCOMMAND_MEDIA_STOP 13
|
||||
#define APPCOMMAND_MEDIA_PLAY_PAUSE 14
|
||||
|
||||
/*
|
||||
* Additional commands currently not in use.
|
||||
*
|
||||
*#define APPCOMMAND_VOLUME_MUTE 8
|
||||
*#define APPCOMMAND_VOLUME_DOWN 9
|
||||
*#define APPCOMMAND_VOLUME_UP 10
|
||||
*#define APPCOMMAND_MEDIA_NEXTTRACK 11
|
||||
*#define APPCOMMAND_MEDIA_PREVIOUSTRACK 12
|
||||
*#define APPCOMMAND_MEDIA_STOP 13
|
||||
*#define APPCOMMAND_MEDIA_PLAY_PAUSE 14
|
||||
*#define APPCOMMAND_LAUNCH_MAIL 15
|
||||
*#define APPCOMMAND_LAUNCH_MEDIA_SELECT 16
|
||||
*#define APPCOMMAND_LAUNCH_APP1 17
|
||||
|
|