Merge m-c to inbound. a=merge

This commit is contained in:
Ryan VanderMeulen 2014-11-17 16:28:46 -05:00
commit 45dc96e55f
43 changed files with 392 additions and 165 deletions

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ddf5b92f43ec27c93ad4fea4fd1207da8936b8e7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ae3a84acaab80a5b35d5542d63e68462273c8a1b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ddf5b92f43ec27c93ad4fea4fd1207da8936b8e7"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ae3a84acaab80a5b35d5542d63e68462273c8a1b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ddf5b92f43ec27c93ad4fea4fd1207da8936b8e7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ae3a84acaab80a5b35d5542d63e68462273c8a1b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="11ad0ea69796915552c9bae148d81fddf9856ddb"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ddf5b92f43ec27c93ad4fea4fd1207da8936b8e7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ae3a84acaab80a5b35d5542d63e68462273c8a1b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ddf5b92f43ec27c93ad4fea4fd1207da8936b8e7"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ae3a84acaab80a5b35d5542d63e68462273c8a1b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ddf5b92f43ec27c93ad4fea4fd1207da8936b8e7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ae3a84acaab80a5b35d5542d63e68462273c8a1b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ddf5b92f43ec27c93ad4fea4fd1207da8936b8e7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ae3a84acaab80a5b35d5542d63e68462273c8a1b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="11ad0ea69796915552c9bae148d81fddf9856ddb"/>

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "8c025d0c8038d8e0c90e294d0501f6c5fb252d59",
"revision": "587d98bf26625137015c17d5b937bf06bd055dd0",
"repo_path": "integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ddf5b92f43ec27c93ad4fea4fd1207da8936b8e7"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ae3a84acaab80a5b35d5542d63e68462273c8a1b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>

View File

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ddf5b92f43ec27c93ad4fea4fd1207da8936b8e7"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ae3a84acaab80a5b35d5542d63e68462273c8a1b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ddf5b92f43ec27c93ad4fea4fd1207da8936b8e7"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ae3a84acaab80a5b35d5542d63e68462273c8a1b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="11ad0ea69796915552c9bae148d81fddf9856ddb"/>

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ddf5b92f43ec27c93ad4fea4fd1207da8936b8e7"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ae3a84acaab80a5b35d5542d63e68462273c8a1b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -1319,7 +1319,7 @@ pref("devtools.devedition.promo.shown", false);
pref("devtools.devedition.promo.url", "https://mozilla.org/firefox/developer");
// Only potentially show in beta release
#ifdef MOZ_UPDATE_CHANNEL == beta
#if MOZ_UPDATE_CHANNEL == beta
pref("devtools.devedition.promo.enabled", true);
#else
pref("devtools.devedition.promo.enabled", false);

View File

@ -50,6 +50,17 @@ let DevEdition = {
}
},
_inferBrightness: function() {
ToolbarIconColor.inferFromText();
// Get an inverted full screen button if the dark theme is applied.
if (this.styleSheet &&
document.documentElement.getAttribute("devtoolstheme") == "dark") {
document.documentElement.setAttribute("brighttitlebarforeground", "true");
} else {
document.documentElement.removeAttribute("brighttitlebarforeground");
}
},
_updateDevtoolsThemeAttribute: function() {
// Set an attribute on root element to make it possible
// to change colors based on the selected devtools theme.
@ -58,7 +69,7 @@ let DevEdition = {
devtoolsTheme = "light";
}
document.documentElement.setAttribute("devtoolstheme", devtoolsTheme);
ToolbarIconColor.inferFromText();
this._inferBrightness();
this._updateStyleSheetFromPrefs();
},
@ -83,7 +94,7 @@ let DevEdition = {
if (e.type === "load") {
this.styleSheet.removeEventListener("load", this);
gBrowser.tabContainer._positionPinnedTabs();
ToolbarIconColor.inferFromText();
this._inferBrightness();
Services.obs.notifyObservers(window, "devedition-theme-state-changed", true);
}
},
@ -102,7 +113,7 @@ let DevEdition = {
this.styleSheet.remove();
this.styleSheet = null;
gBrowser.tabContainer._positionPinnedTabs();
ToolbarIconColor.inferFromText();
this._inferBrightness();
Services.obs.notifyObservers(window, "devedition-theme-state-changed", false);
}
},

View File

@ -11,7 +11,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "PanelFrame", "resource:///modules/Panel
(function() {
LoopUI = {
get toolbarButton() {
delete this.toolbarButton;
@ -84,8 +83,28 @@ XPCOMUtils.defineLazyModuleGetter(this, "PanelFrame", "resource:///modules/Panel
state = "active";
} else if (MozLoopService.doNotDisturb) {
state = "disabled";
} else if (MozLoopService.roomsParticipantsCount > 0) {
state = "active";
}
this.toolbarButton.node.setAttribute("state", state);
},
/**
* Play a sound in this window IF there's no sound playing yet.
*
* @param {String} name Name of the sound, like 'ringtone' or 'room-joined'
*/
playSound: function(name) {
if (this.ActiveSound || MozLoopService.doNotDisturb) {
return;
}
this.activeSound = new window.Audio();
this.activeSound.src = `chrome://browser/content/loop/shared/sounds/${name}.ogg`;
this.activeSound.load();
this.activeSound.play();
this.activeSound.addEventListener("ended", () => this.activeSound = undefined, false);
},
};
})();

View File

@ -87,41 +87,61 @@ addEventListener("blur", function(event) {
LoginManagerContent.onUsernameInput(event);
});
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
let handleContentContextMenu = function (event) {
let defaultPrevented = event.defaultPrevented;
if (!Services.prefs.getBoolPref("dom.event.contextmenu.enabled")) {
let plugin = null;
try {
plugin = event.target.QueryInterface(Ci.nsIObjectLoadingContent);
} catch (e) {}
if (plugin && plugin.displayedType == Ci.nsIObjectLoadingContent.TYPE_PLUGIN) {
// Don't open a context menu for plugins.
return;
}
defaultPrevented = false;
let handleContentContextMenu = function (event) {
let defaultPrevented = event.defaultPrevented;
if (!Services.prefs.getBoolPref("dom.event.contextmenu.enabled")) {
let plugin = null;
try {
plugin = event.target.QueryInterface(Ci.nsIObjectLoadingContent);
} catch (e) {}
if (plugin && plugin.displayedType == Ci.nsIObjectLoadingContent.TYPE_PLUGIN) {
// Don't open a context menu for plugins.
return;
}
if (!defaultPrevented) {
let editFlags = SpellCheckHelper.isEditable(event.target, content);
let spellInfo;
if (editFlags &
(SpellCheckHelper.EDITABLE | SpellCheckHelper.CONTENTEDITABLE)) {
spellInfo =
InlineSpellCheckerContent.initContextMenu(event, editFlags, this);
}
sendSyncMessage("contextmenu", { editFlags, spellInfo }, { event });
}
defaultPrevented = false;
}
Cc["@mozilla.org/eventlistenerservice;1"]
.getService(Ci.nsIEventListenerService)
.addSystemEventListener(global, "contextmenu", handleContentContextMenu, true);
if (defaultPrevented)
return;
let addonInfo = {};
let subject = {
event: event,
addonInfo: addonInfo,
};
subject.wrappedJSObject = subject;
Services.obs.notifyObservers(subject, "content-contextmenu", null);
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
let editFlags = SpellCheckHelper.isEditable(event.target, content);
let spellInfo;
if (editFlags &
(SpellCheckHelper.EDITABLE | SpellCheckHelper.CONTENTEDITABLE)) {
spellInfo =
InlineSpellCheckerContent.initContextMenu(event, editFlags, this);
}
sendSyncMessage("contextmenu", { editFlags, spellInfo, addonInfo }, { event, popupNode: event.target });
}
else {
// Break out to the parent window and pass the add-on info along
let browser = docShell.chromeEventHandler;
let mainWin = browser.ownerDocument.defaultView;
mainWin.gContextMenuContentData = {
isRemote: false,
event: event,
popupNode: event.target,
browser: browser,
addonInfo: addonInfo,
};
}
}
Cc["@mozilla.org/eventlistenerservice;1"]
.getService(Ci.nsIEventListenerService)
.addSystemEventListener(global, "contextmenu", handleContentContextMenu, false);
let AboutNetErrorListener = {
init: function(chromeGlobal) {
chromeGlobal.addEventListener('AboutNetErrorLoad', this, false, true);

View File

@ -528,17 +528,16 @@ nsContextMenu.prototype = {
// Set various context menu attributes based on the state of the world.
setTarget: function (aNode, aRangeParent, aRangeOffset) {
// If gContextMenuContentData is not null, this event was forwarded from a
// child process, so use that information instead.
// gContextMenuContentData.isRemote tells us if the event came from a remote
// process. gContextMenuContentData can be null if something (like tests)
// opens the context menu directly.
let editFlags;
if (gContextMenuContentData) {
this.isRemote = true;
this.isRemote = gContextMenuContentData && gContextMenuContentData.isRemote;
if (this.isRemote) {
aNode = gContextMenuContentData.event.target;
aRangeParent = gContextMenuContentData.event.rangeParent;
aRangeOffset = gContextMenuContentData.event.rangeOffset;
editFlags = gContextMenuContentData.editFlags;
} else {
this.isRemote = false;
}
const xulNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
@ -647,7 +646,7 @@ nsContextMenu.prototype = {
this.onTextInput = (editFlags & SpellCheckHelper.TEXTINPUT) !== 0;
this.onEditableArea = (editFlags & SpellCheckHelper.EDITABLE) !== 0;
if (this.onEditableArea) {
if (gContextMenuContentData) {
if (this.isRemote) {
InlineSpellCheckerUI.initFromRemote(gContextMenuContentData.spellInfo);
}
else {
@ -772,7 +771,7 @@ nsContextMenu.prototype = {
this.hasBGImage = false;
this.isDesignMode = true;
this.onEditableArea = true;
if (gContextMenuContentData) {
if (this.isRemote) {
InlineSpellCheckerUI.initFromRemote(gContextMenuContentData.spellInfo);
}
else {

View File

@ -3082,10 +3082,13 @@
let spellInfo = aMessage.data.spellInfo;
if (spellInfo)
spellInfo.target = aMessage.target.messageManager;
gContextMenuContentData = { event: aMessage.objects.event,
gContextMenuContentData = { isRemote: true,
event: aMessage.objects.event,
popupNode: aMessage.objects.popupNode,
browser: browser,
editFlags: aMessage.data.editFlags,
spellInfo: spellInfo };
spellInfo: spellInfo,
addonInfo: aMessage.data.addonInfo };
let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
let event = gContextMenuContentData.event;
let pos = browser.mapScreenCoordinatesFromContent(event.screenX, event.screenY);

View File

@ -16,12 +16,7 @@ registerCleanupFunction(() => {
Services.prefs.clearUserPref(PREF_DEVTOOLS_THEME);
});
function test() {
waitForExplicitFinish();
startTests();
}
function startTests() {
add_task(function* startTests() {
Services.prefs.setCharPref(PREF_DEVTOOLS_THEME, "dark");
info ("Setting browser.devedition.theme.enabled to false.");
@ -37,40 +32,54 @@ function startTests() {
ok (!DevEdition.styleSheet, "The devedition stylesheet has been removed when a lightweight theme is applied.");
info ("Removing a lightweight theme.");
let onAttributeAdded = waitForBrightTitlebarAttribute();
Services.prefs.setBoolPref(PREF_LWTHEME, false);
ok (DevEdition.styleSheet, "The devedition stylesheet has been added when a lightweight theme is removed.");
yield onAttributeAdded;
is (document.documentElement.getAttribute("brighttitlebarforeground"), "true",
"The brighttitlebarforeground attribute is set on the window.");
info ("Setting browser.devedition.theme.enabled to false.");
Services.prefs.setBoolPref(PREF_DEVEDITION_THEME, false);
ok (!DevEdition.styleSheet, "The devedition stylesheet has been removed.");
testDevtoolsTheme();
testLightweightThemePreview();
finish();
}
ok (!document.documentElement.hasAttribute("brighttitlebarforeground"),
"The brighttitlebarforeground attribute is not set on the window after devedition.theme is false.");
});
function testDevtoolsTheme() {
add_task(function* testDevtoolsTheme() {
info ("Checking that Australis is shown when the light devtools theme is applied.");
let onAttributeAdded = waitForBrightTitlebarAttribute();
Services.prefs.setBoolPref(PREF_DEVEDITION_THEME, true);
ok (DevEdition.styleSheet, "The devedition stylesheet exists.");
yield onAttributeAdded;
ok (document.documentElement.hasAttribute("brighttitlebarforeground"),
"The brighttitlebarforeground attribute is set on the window with dark devtools theme.");
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 is still there with the light devtools theme.");
ok (!document.documentElement.hasAttribute("brighttitlebarforeground"),
"The brighttitlebarforeground attribute is not set on the window with 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 is still there with the dark devtools theme.");
is (document.documentElement.getAttribute("brighttitlebarforeground"), "true",
"The brighttitlebarforeground attribute is set on the window with dark devtools theme.");
Services.prefs.setCharPref(PREF_DEVTOOLS_THEME, "foobar");
is (document.documentElement.getAttribute("devtoolstheme"), "light",
"The documentElement has 'light' as a default for the devtoolstheme attribute");
ok (DevEdition.styleSheet, "The devedition stylesheet is still there with the foobar devtools theme.");
}
ok (!document.documentElement.hasAttribute("brighttitlebarforeground"),
"The brighttitlebarforeground attribute is not set on the window with light devtools theme.");
});
function dummyLightweightTheme(id) {
return {
@ -83,7 +92,7 @@ function dummyLightweightTheme(id) {
};
}
function testLightweightThemePreview() {
add_task(function* testLightweightThemePreview() {
let {LightweightThemeManager} = Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", {});
info ("Turning the pref on, then previewing lightweight themes");
@ -114,4 +123,21 @@ function testLightweightThemePreview() {
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.");
});
// Use a mutation observer to wait for the brighttitlebarforeground
// attribute to change. Using this instead of waiting for the load
// event on the DevEdition styleSheet.
function waitForBrightTitlebarAttribute() {
return new Promise((resolve, reject) => {
let mutationObserver = new MutationObserver(function (mutations) {
for (let mutation of mutations) {
if (mutation.attributeName == "brighttitlebarforeground") {
mutationObserver.disconnect();
resolve();
}
}
});
mutationObserver.observe(document.documentElement, { attributes: true });
});
}

View File

@ -56,7 +56,11 @@ const extend = function(target, source) {
*/
const containsParticipant = function(room, participant) {
for (let user of room.participants) {
if (user.roomConnectionId == participant.roomConnectionId) {
// XXX until a bug 1100318 is implemented and deployed,
// we need to check the "id" field here as well - roomConnectionId is the
// official value for the interface.
if (user.roomConnectionId == participant.roomConnectionId &&
user.id == participant.id) {
return true;
}
}
@ -107,13 +111,34 @@ const checkForParticipantsUpdate = function(room, updatedRoom) {
* violated. You'll notice this as well in the documentation for each method.
*/
let LoopRoomsInternal = {
/**
* @var {Map} rooms Collection of rooms currently in cache.
*/
rooms: new Map(),
/**
* @var {String} sessionType The type of user session. May be 'FXA' or 'GUEST'.
*/
get sessionType() {
return MozLoopService.userProfile ? LOOP_SESSION_TYPE.FXA :
LOOP_SESSION_TYPE.GUEST;
},
/**
* @var {Number} participantsCount The total amount of participants currently
* inside all rooms.
*/
get participantsCount() {
let count = 0;
for (let room of this.rooms.values()) {
if (!("participants" in room)) {
continue;
}
count += room.participants.length;
}
return count;
},
/**
* Fetch a list of rooms that the currently registered user is a member of.
*
@ -154,6 +179,10 @@ let LoopRoomsInternal = {
if (orig) {
checkForParticipantsUpdate(orig, room);
}
// Remove the `currSize` for posterity.
if ("currSize" in room) {
delete room.currSize;
}
this.rooms.set(room.roomToken, room);
// When a version is specified, all the data is already provided by this
// request.
@ -203,11 +232,6 @@ let LoopRoomsInternal = {
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";
@ -382,6 +406,10 @@ Object.freeze(LoopRoomsInternal);
* See the internal code for the API documentation.
*/
this.LoopRooms = {
get participantsCount() {
return LoopRoomsInternal.participantsCount;
},
getAll: function(version, callback) {
return LoopRoomsInternal.getAll(version, callback);
},

View File

@ -81,6 +81,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "gDNSService",
"@mozilla.org/network/dns-service;1",
"nsIDNSService");
XPCOMUtils.defineLazyServiceGetter(this, "gWM",
"@mozilla.org/appshell/window-mediator;1",
"nsIWindowMediator");
// Create a new instance of the ConsoleAPI so we can control the maxLogLevel with a pref.
XPCOMUtils.defineLazyGetter(this, "log", () => {
let ConsoleAPI = Cu.import("resource://gre/modules/devtools/Console.jsm", {}).ConsoleAPI;
@ -965,6 +969,10 @@ this.MozLoopService = {
gInitializeTimerFunc = value;
},
get roomsParticipantsCount() {
return LoopRooms.participantsCount;
},
/**
* Initialized the loop service, and starts registration with the
* push and loop servers.
@ -995,6 +1003,26 @@ this.MozLoopService = {
}
}
// The Loop toolbar button should change icon when the room participant count
// changes from 0 to something.
const onRoomsChange = () => {
MozLoopServiceInternal.notifyStatusChanged();
};
LoopRooms.on("add", onRoomsChange);
LoopRooms.on("update", onRoomsChange);
LoopRooms.on("joined", (e, roomToken, participant) => {
// Don't alert if we're in the doNotDisturb mode, or the participant
// is the owner - the content code deals with the rest of the sounds.
if (MozLoopServiceInternal.doNotDisturb || participant.owner) {
return;
}
let window = gWM.getMostRecentWindow("navigator:browser");
if (window) {
window.LoopUI.playSound("room-joined");
}
});
// If expiresTime is not in the future and the user hasn't
// previously authenticated then skip registration.
if (!MozLoopServiceInternal.urlExpiryTimeIsInFuture() &&

View File

@ -31,13 +31,19 @@ loop.panel = (function(_, mozL10n) {
getDefaultProps: function() {
return {
buttonsHidden: false,
selectedTab: "call"
buttonsHidden: false
};
},
getInitialState: function() {
return {selectedTab: this.props.selectedTab};
// XXX Work around props.selectedTab being undefined initially.
// When we don't need to rely on the pref, this can move back to
// getDefaultProps (bug 1100258).
return {
selectedTab: this.props.selectedTab ||
(navigator.mozLoop.getLoopBoolPref("rooms.enabled") ?
"rooms" : "call")
};
},
handleSelectTab: function(event) {
@ -678,13 +684,17 @@ loop.panel = (function(_, mozL10n) {
}
},
_roomsEnabled: function() {
return navigator.mozLoop.getLoopBoolPref("rooms.enabled");
},
_onStatusChanged: function() {
var profile = navigator.mozLoop.userProfile;
var currUid = this.state.userProfile ? this.state.userProfile.uid : null;
var newUid = profile ? profile.uid : null;
if (currUid != newUid) {
// On profile change (login, logout), switch back to the default tab.
this.selectTab("call");
this.selectTab(this._roomsEnabled() ? "rooms" : "call");
this.setState({userProfile: profile});
}
this.updateServiceErrors();
@ -692,17 +702,28 @@ loop.panel = (function(_, mozL10n) {
/**
* The rooms feature is hidden by default for now. Once it gets mainstream,
* this method can be safely removed.
* this method can be simplified.
*/
_renderRoomsTab: function() {
if (!navigator.mozLoop.getLoopBoolPref("rooms.enabled")) {
return null;
_renderRoomsOrCallTab: function() {
if (!this._roomsEnabled()) {
return (
Tab({name: "call"},
React.DOM.div({className: "content-area"},
CallUrlResult({client: this.props.client,
notifications: this.props.notifications,
callUrl: this.props.callUrl}),
ToSView(null)
)
)
);
}
return (
Tab({name: "rooms"},
RoomList({dispatcher: this.props.dispatcher,
store: this.props.roomStore,
userDisplayName: this._getUserDisplayName()})
userDisplayName: this._getUserDisplayName()}),
ToSView(null)
)
);
},
@ -735,21 +756,14 @@ loop.panel = (function(_, mozL10n) {
render: function() {
var NotificationListView = sharedViews.NotificationListView;
return (
React.DOM.div(null,
NotificationListView({notifications: this.props.notifications,
clearOnDocumentHidden: true}),
TabView({ref: "tabView", selectedTab: this.props.selectedTab,
buttonsHidden: !this.state.userProfile && !this.props.showTabButtons},
Tab({name: "call"},
React.DOM.div({className: "content-area"},
CallUrlResult({client: this.props.client,
notifications: this.props.notifications,
callUrl: this.props.callUrl}),
ToSView(null)
)
),
this._renderRoomsTab(),
this._renderRoomsOrCallTab(),
Tab({name: "contacts"},
ContactsList({selectTab: this.selectTab,
startForm: this.startForm})

View File

@ -31,13 +31,19 @@ loop.panel = (function(_, mozL10n) {
getDefaultProps: function() {
return {
buttonsHidden: false,
selectedTab: "call"
buttonsHidden: false
};
},
getInitialState: function() {
return {selectedTab: this.props.selectedTab};
// XXX Work around props.selectedTab being undefined initially.
// When we don't need to rely on the pref, this can move back to
// getDefaultProps (bug 1100258).
return {
selectedTab: this.props.selectedTab ||
(navigator.mozLoop.getLoopBoolPref("rooms.enabled") ?
"rooms" : "call")
};
},
handleSelectTab: function(event) {
@ -678,13 +684,17 @@ loop.panel = (function(_, mozL10n) {
}
},
_roomsEnabled: function() {
return navigator.mozLoop.getLoopBoolPref("rooms.enabled");
},
_onStatusChanged: function() {
var profile = navigator.mozLoop.userProfile;
var currUid = this.state.userProfile ? this.state.userProfile.uid : null;
var newUid = profile ? profile.uid : null;
if (currUid != newUid) {
// On profile change (login, logout), switch back to the default tab.
this.selectTab("call");
this.selectTab(this._roomsEnabled() ? "rooms" : "call");
this.setState({userProfile: profile});
}
this.updateServiceErrors();
@ -692,17 +702,28 @@ loop.panel = (function(_, mozL10n) {
/**
* The rooms feature is hidden by default for now. Once it gets mainstream,
* this method can be safely removed.
* this method can be simplified.
*/
_renderRoomsTab: function() {
if (!navigator.mozLoop.getLoopBoolPref("rooms.enabled")) {
return null;
_renderRoomsOrCallTab: function() {
if (!this._roomsEnabled()) {
return (
<Tab name="call">
<div className="content-area">
<CallUrlResult client={this.props.client}
notifications={this.props.notifications}
callUrl={this.props.callUrl} />
<ToSView />
</div>
</Tab>
);
}
return (
<Tab name="rooms">
<RoomList dispatcher={this.props.dispatcher}
store={this.props.roomStore}
userDisplayName={this._getUserDisplayName()}/>
<ToSView />
</Tab>
);
},
@ -735,21 +756,14 @@ loop.panel = (function(_, mozL10n) {
render: function() {
var NotificationListView = sharedViews.NotificationListView;
return (
<div>
<NotificationListView notifications={this.props.notifications}
clearOnDocumentHidden={true} />
<TabView ref="tabView" selectedTab={this.props.selectedTab}
buttonsHidden={!this.state.userProfile && !this.props.showTabButtons}>
<Tab name="call">
<div className="content-area">
<CallUrlResult client={this.props.client}
notifications={this.props.notifications}
callUrl={this.props.callUrl} />
<ToSView />
</div>
</Tab>
{this._renderRoomsTab()}
{this._renderRoomsOrCallTab()}
<Tab name="contacts">
<ContactsList selectTab={this.selectTab}
startForm={this.startForm} />

View File

@ -140,7 +140,6 @@ body {
/* Rooms */
.rooms {
background: #f5f5f5;
min-height: 100px;
}

View File

@ -154,6 +154,10 @@ loop.shared.mixins = (function() {
* @param {String} name The filename to play (excluding the extension).
*/
play: function(name, options) {
if (this._isLoopDesktop() && rootObject.navigator.mozLoop.doNotDisturb) {
return;
}
options = options || {};
options.loop = options.loop || false;

View File

@ -86,6 +86,7 @@ browser.jar:
content/browser/loop/shared/sounds/connecting.ogg (content/shared/sounds/connecting.ogg)
content/browser/loop/shared/sounds/connected.ogg (content/shared/sounds/connected.ogg)
content/browser/loop/shared/sounds/terminated.ogg (content/shared/sounds/terminated.ogg)
content/browser/loop/shared/sounds/room-joined.ogg (content/shared/sounds/room-joined.ogg)
content/browser/loop/shared/sounds/failure.ogg (content/shared/sounds/failure.ogg)
# Partner SDK assets

View File

@ -917,6 +917,7 @@ describe("loop.conversation", function() {
pause: sinon.spy(),
removeAttribute: sinon.spy()
};
navigator.mozLoop.doNotDisturb = false;
sandbox.stub(window, "Audio").returns(fakeAudio);
view = TestUtils.renderIntoDocument(

View File

@ -184,7 +184,7 @@ describe("loop.panel", function() {
view = createTestPanelView();
[callTab, roomsTab, contactsTab] =
[roomsTab, contactsTab] =
TestUtils.scryRenderedDOMComponentsWithClass(view, "tab");
});
@ -203,14 +203,6 @@ describe("loop.panel", function() {
expect(roomsTab.getDOMNode().classList.contains("selected"))
.to.be.true;
});
it("should select call tab when clicking tab button", function() {
TestUtils.Simulate.click(
view.getDOMNode().querySelector("li[data-tab-name=\"call\"]"));
expect(callTab.getDOMNode().classList.contains("selected"))
.to.be.true;
});
});
describe("loop.rooms.enabled off", function() {

View File

@ -8,6 +8,7 @@
"use strict";
Components.utils.import("resource://gre/modules/Promise.jsm", this);
const {LoopRoomsInternal} = Components.utils.import("resource:///modules/loop/LoopRooms.jsm", {});
registerCleanupFunction(function*() {
MozLoopService.doNotDisturb = false;
@ -79,3 +80,14 @@ add_task(function* test_active() {
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state");
});
add_task(function* test_room_participants() {
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state");
LoopRoomsInternal.rooms.set("test_room", {participants: [{displayName: "hugh", id: "008"}]});
MozLoopServiceInternal.notifyStatusChanged();
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "active", "Check button is in active state");
LoopRoomsInternal.rooms.set("test_room", {participants: []});
MozLoopServiceInternal.notifyStatusChanged();
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state");
LoopRoomsInternal.rooms.delete("test_room");
});

View File

@ -166,4 +166,52 @@ describe("loop.shared.mixins", function() {
sinon.assert.calledOnce(onDocumentHiddenStub);
});
});
describe("loop.shared.mixins.AudioMixin", function() {
var view, fakeAudio, TestComp;
beforeEach(function() {
navigator.mozLoop = {
doNotDisturb: true,
getAudioBlob: sinon.spy(function(name, callback) {
callback(null, new Blob([new ArrayBuffer(10)], {type: 'audio/ogg'}));
})
};
fakeAudio = {
play: sinon.spy(),
pause: sinon.spy(),
removeAttribute: sinon.spy()
};
sandbox.stub(window, "Audio").returns(fakeAudio);
TestComp = React.createClass({
mixins: [loop.shared.mixins.AudioMixin],
componentDidMount: function() {
this.play("failure");
},
render: function() {
return React.DOM.div();
}
});
});
it("should not play a failure sound when doNotDisturb true", function() {
view = TestUtils.renderIntoDocument(TestComp());
sinon.assert.notCalled(navigator.mozLoop.getAudioBlob);
sinon.assert.notCalled(fakeAudio.play);
});
it("should play a failure sound, once", function() {
navigator.mozLoop.doNotDisturb = false;
view = TestUtils.renderIntoDocument(TestComp());
sinon.assert.calledOnce(navigator.mozLoop.getAudioBlob);
sinon.assert.calledWithExactly(navigator.mozLoop.getAudioBlob,
"failure", sinon.match.func);
sinon.assert.calledOnce(fakeAudio.play);
expect(fakeAudio.loop).to.equal(false);
});
});
});

View File

@ -1529,7 +1529,7 @@ WebGLContext::InitAndValidateGL()
mMinCapability = Preferences::GetBool("webgl.min_capability_mode", false);
mDisableExtensions = Preferences::GetBool("webgl.disable-extensions", false);
mLoseContextOnMemoryPressure = Preferences::GetBool("webgl.lose-context-on-memory-preasure", false);
mLoseContextOnMemoryPressure = Preferences::GetBool("webgl.lose-context-on-memory-pressure", false);
mCanLoseContextInForeground = Preferences::GetBool("webgl.can-lose-context-in-foreground", true);
mRestoreWhenVisible = Preferences::GetBool("webgl.restore-context-when-visible", true);

View File

@ -10,6 +10,7 @@
#include "nsIAudioManager.h"
#include "AudioManager.h"
#include "nsDOMClassInfo.h"
#include "mozilla/LazyIdleThread.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/FMRadioChild.h"
#include "mozilla/dom/ScriptSettings.h"
@ -19,6 +20,8 @@
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/SettingChangeNotificationBinding.h"
#define TUNE_THREAD_TIMEOUT_MS 5000
#define BAND_87500_108000_kHz 1
#define BAND_76000_108000_kHz 2
#define BAND_76000_90000_kHz 3
@ -161,9 +164,10 @@ public:
FMRadioService* fmRadioService = FMRadioService::Singleton();
if (!fmRadioService->mTuneThread) {
// SeekRunnable and SetFrequencyRunnable run on this thread.
// These call ioctls that can stall the main thread, so we run them here.
NS_NewNamedThread("FM Tuning", getter_AddRefs(fmRadioService->mTuneThread));
// SeekRunnable and SetFrequencyRunnable run on this thread. These
// call ioctls that can stall the main thread, so we run them here.
fmRadioService->mTuneThread = new LazyIdleThread(
TUNE_THREAD_TIMEOUT_MS, NS_LITERAL_CSTRING("FM Tuning"));
}
return NS_OK;

View File

@ -3935,8 +3935,25 @@ RilObject.prototype = {
/**
* Helpers for processing call state changes.
*/
_processClassifiedCalls: function(removedCalls, remainedCalls, addedCalls,
failCause) {
_processCalls: function(newCalls, failCause) {
// Let's get the failCause first if there are removed calls. Otherwise, we
// need to trigger another async request when removing call and it cause
// the order of callDisconnected and conferenceCallStateChanged
// unpredictable.
if (failCause === undefined) {
for each (let currentCall in this.currentCalls) {
if (!newCalls[currentCall.callIndex] && !currentCall.hangUpLocal) {
this.getFailCauseCode((function(newCalls, failCause) {
this._processCalls(newCalls, failCause);
}).bind(this, newCalls));
return;
}
}
}
let [removedCalls, remainedCalls, addedCalls] =
this._classifyCalls(newCalls);
// Handle removed calls.
// Only remove it from the map here. Notify callDisconnected later.
for (let call of removedCalls) {
@ -4019,22 +4036,6 @@ RilObject.prototype = {
}
},
_processCalls: function(newCalls) {
let [removed, remained, added] = this._classifyCalls(newCalls);
// Let's get the failCause first if there are removed calls. Otherwise, we
// need to trigger another async request when removing call and it cause
// the order of callDisconnected and conferenceCallStateChanged
// unpredictable.
if (removed.length) {
this.getFailCauseCode((function(removed, remained, added, failCause) {
this._processClassifiedCalls(removed, remained, added, failCause);
}).bind(this, removed, remained, added));
} else {
this._processClassifiedCalls(removed, remained, added);
}
},
_detectAudioState: function() {
let callNum = Object.keys(this.currentCalls).length;
if (!callNum) {
@ -5389,10 +5390,6 @@ RilObject.prototype[REQUEST_GET_CURRENT_CALLS] = function REQUEST_GET_CURRENT_CA
if (length) {
calls_length = Buf.readInt32();
}
if (!calls_length) {
this._processCalls(null);
return;
}
let calls = {};
for (let i = 0; i < calls_length; i++) {
@ -5466,10 +5463,6 @@ RilObject.prototype[REQUEST_HANGUP] = function REQUEST_HANGUP(length, options) {
options.success = options.rilRequestError === 0;
options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
this.sendChromeMessage(options);
if (options.success) {
this.getCurrentCalls();
}
};
RilObject.prototype[REQUEST_HANGUP_WAITING_OR_BACKGROUND] = function REQUEST_HANGUP_WAITING_OR_BACKGROUND(length, options) {
RilObject.prototype[REQUEST_HANGUP].call(this, length, options);
@ -5478,11 +5471,6 @@ RilObject.prototype[REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND] = function REQU
RilObject.prototype[REQUEST_HANGUP].call(this, length, options);
};
RilObject.prototype[REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE] = function REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE(length, options) {
if (options.rilRequestError) {
return;
}
this.getCurrentCalls();
};
RilObject.prototype[REQUEST_CONFERENCE] = function REQUEST_CONFERENCE(length, options) {
options.success = (options.rilRequestError === 0);

View File

@ -72,7 +72,7 @@ add_test(function test_notification() {
do_check_eq(postedMessage.callIndex, resultCallIndex);
// Clear all existed calls.
context.RIL._processCalls(null);
context.RIL._processCalls({});
}
testNotification(oneCall, SUPP_SVC_NOTIFICATION_CODE2_PUT_ON_HOLD, null,

View File

@ -12,6 +12,7 @@ import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewTreeObserver;
@ -93,12 +94,21 @@ public class TabsLayoutItemView extends LinearLayout
public boolean onPreDraw() {
getViewTreeObserver().removeOnPreDrawListener(this);
final Rect r = new Rect();
mCloseButton.getHitRect(r);
r.left -= 25;
r.bottom += 25;
final Rect hitRect = new Rect();
mCloseButton.getHitRect(hitRect);
setTouchDelegate(new TouchDelegate(r, mCloseButton));
// Ideally we want the close button hit area to be 40x40dp but we are constrained by the height of the parent, so
// we make it as tall as the parent view and 40dp across.
final int targetHitArea = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40, getResources().getDisplayMetrics());;
final View parent = ((View) mCloseButton.getParent());
hitRect.top = 0;
hitRect.right = getWidth();
hitRect.left = getWidth() - targetHitArea;
hitRect.bottom = parent.getHeight();
setTouchDelegate(new TouchDelegate(hitRect, mCloseButton));
return true;
}

View File

@ -3834,7 +3834,7 @@ pref("webgl.msaa-force", false);
pref("webgl.prefer-16bpp", false);
pref("webgl.default-no-alpha", false);
pref("webgl.force-layers-readback", false);
pref("webgl.lose-context-on-memory-preasure", false);
pref("webgl.lose-context-on-memory-pressure", false);
pref("webgl.can-lose-context-in-foreground", true);
pref("webgl.restore-context-when-visible", true);
pref("webgl.max-warnings-per-context", 32);

View File

@ -47,6 +47,9 @@ function installAddon(url) {
logLevel: "verbose",
format: "tbpl",
},
console: {
logLevel: "info",
},
}
setPrefs("extensions." + install.addon.id + ".sdk", options);

View File

@ -156,6 +156,9 @@ function testInit() {
logLevel: "verbose",
format: "tbpl",
},
console: {
logLevel: "info",
},
}
setPrefs("extensions." + TEST_ID + ".sdk", options);

View File

@ -103,7 +103,7 @@
height: 0;
width: 0;
border: 14px solid hsl(210,2%,22%);
border: 14px solid hsl(214,13%,24%);
border-left-color: transparent;
border-right-color: transparent;
}

View File

@ -14,7 +14,7 @@ menubar:-moz-lwtheme,
toolbar:-moz-lwtheme {
-moz-appearance: none;
background: none;
border-style: none;
border-color: transparent;
}
menubar {

View File

@ -127,7 +127,7 @@ menubar > menu[_moz-menuactive="true"][open="true"] {
menubar > menu:-moz-lwtheme {
-moz-appearance: none;
border-style: none;
border-color: transparent;
}
menubar > menu:-moz-lwtheme:not([disabled="true"]) {

View File

@ -43,7 +43,7 @@ toolbox:-moz-lwtheme,
toolbar:-moz-lwtheme {
-moz-appearance: none;
background: none;
border-style: none;
border-color: transparent;
}
/* ::::: toolbar decorations ::::: */