Merge f-t to m-c, a=merge

This commit is contained in:
Phil Ringnalda 2015-09-19 20:17:20 -07:00
commit b9276a91d0
11 changed files with 275 additions and 11 deletions

View File

@ -1693,6 +1693,7 @@ pref("image.mem.max_decoded_image_kb", 256000);
pref("loop.enabled", true);
pref("loop.textChat.enabled", true);
pref("loop.server", "https://loop.services.mozilla.com/v0");
pref("loop.linkClicker.url", "https://hello.firefox.com/");
pref("loop.gettingStarted.seen", false);
pref("loop.gettingStarted.url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/hello/start/");
pref("loop.gettingStarted.resumeOnFirstJoin", false);

View File

@ -50,6 +50,7 @@
"SocialShare": false,
"Task": false,
"UITour": false,
"WebChannel": false,
"XPCOMUtils": false,
"uuidgen": true,
// Test Related

View File

@ -15,6 +15,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
"resource://services-common/utils.js");
XPCOMUtils.defineLazyModuleGetter(this, "WebChannel",
"resource://gre/modules/WebChannel.jsm");
XPCOMUtils.defineLazyGetter(this, "eventEmitter", function() {
const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js", {});
return new EventEmitter();
@ -45,6 +48,9 @@ const MAX_TIME_BEFORE_ENCRYPTION = 30 * 60 * 1000;
// Wait time between individual re-encryption cycles (1 second).
const TIME_BETWEEN_ENCRYPTIONS = 1000;
// This is the pref name for the url of the standalone pages.
const LINKCLICKER_URL_PREFNAME = "loop.linkClicker.url";
const roomsPushNotification = function(version, channelID) {
return LoopRoomsInternal.onNotification(version, channelID);
};
@ -58,6 +64,8 @@ var gDirty = true;
var gCurrentUser = null;
// Global variable that keeps track of the room cache.
var gRoomsCache = null;
// Global variable that keeps track of the link clicker channel.
var gLinkClickerChannel = null;
/**
* Extend a `target` object with the properties defined in `source`.
@ -177,6 +185,40 @@ var LoopRoomsInternal = {
}
},
/**
* Initialises the rooms, sets up the link clicker listener.
*/
init: function() {
Services.prefs.addObserver(LINKCLICKER_URL_PREFNAME,
this.setupLinkClickerListener.bind(this), false);
this.setupLinkClickerListener();
},
/**
* Sets up a WebChannel listener for the link clicker so that we can open
* rooms in the Firefox UI.
*/
setupLinkClickerListener: function() {
// Ensure any existing channel is tidied up.
if (gLinkClickerChannel) {
gLinkClickerChannel.stopListening();
gLinkClickerChannel = null;
}
let linkClickerUrl = Services.prefs.getCharPref(LINKCLICKER_URL_PREFNAME);
// Don't do anything if there's no url.
if (!linkClickerUrl) {
return;
}
let uri = Services.io.newURI(linkClickerUrl, null, null);
gLinkClickerChannel = new WebChannel("loop-link-clicker", uri);
gLinkClickerChannel.listen(this._handleLinkClickerMessage.bind(this));
},
/**
* @var {String} sessionType The type of user session. May be 'FXA' or 'GUEST'.
*/
@ -905,6 +947,42 @@ var LoopRoomsInternal = {
eventEmitter.emit("refresh");
this.getAll(null, () => {});
}
},
/**
* Handles a message received from the content channel.
*
* @param {String} id The channel id.
* @param {Object} message The message received.
* @param {Object} sendingContext The context for the sending location.
*/
_handleLinkClickerMessage: function(id, message, sendingContext) {
if (!message) {
return;
}
let sendResponse = response => {
gLinkClickerChannel.send({
response: response
}, sendingContext);
};
let hasRoom = this.rooms.has(message.roomToken);
switch (message.command) {
case "checkWillOpenRoom":
sendResponse(hasRoom);
break;
case "openRoom":
if (hasRoom) {
this.open(message.roomToken);
}
sendResponse(hasRoom);
break;
default:
sendResponse(false);
break;
}
}
};
Object.freeze(LoopRoomsInternal);
@ -926,6 +1004,10 @@ Object.freeze(LoopRoomsInternal);
* See the internal code for the API documentation.
*/
this.LoopRooms = {
init: function() {
LoopRoomsInternal.init();
},
get participantsCount() {
return LoopRoomsInternal.participantsCount;
},
@ -1016,6 +1098,24 @@ this.LoopRooms = {
once: (...params) => eventEmitter.once(...params),
off: (...params) => eventEmitter.off(...params)
off: (...params) => eventEmitter.off(...params),
/**
* Expose the internal rooms map for testing purposes only. This avoids
* needing to mock the server interfaces.
*
* @param {Map} roomsCache The new cache data to set for testing purposes. If
* not specified, it will reset the cache.
*/
_setRoomsCache: function(roomsCache) {
LoopRoomsInternal.rooms.clear();
if (roomsCache) {
// Need a clone as the internal map is read-only.
for (let [key, value] of roomsCache) {
LoopRoomsInternal.rooms.set(key, value);
}
}
}
};
Object.freeze(this.LoopRooms);

View File

@ -1211,6 +1211,9 @@ this.MozLoopService = {
// stub out API functions for unit testing
Object.freeze(this);
// Initialise anything that needs it in rooms.
LoopRooms.init();
// Don't do anything if loop is not enabled.
if (!Services.prefs.getBoolPref("loop.enabled")) {
return Promise.reject(new Error("loop is not enabled"));

View File

@ -46,6 +46,7 @@ TESTS="
${LOOPDIR}/test/mochitest
browser/components/uitour/test/browser_UITour_loop.js
browser/base/content/test/general/browser_devices_get_user_media_about_urls.js
browser/base/content/test/general/browser_parsable_css.js
"
./mach mochitest $TESTS
@ -53,9 +54,3 @@ TESTS="
if [ "$1" != "--skip-e10s" ]; then
./mach mochitest --e10s $TESTS
fi
# This is currently disabled because the test itself is busted. Once bug
# 1062821 is landed, we should see if things work again, and then re-enable it.
# The re-enabling is tracked in bug 1113350.
#
# browser/base/content/test/general/browser_parsable_css.js \

View File

@ -32,5 +32,8 @@
"MozLoopServiceInternal": true,
"LoopRoomsInternal": true,
"LoopUI": false,
// Other items
"Chat": true,
"WebChannel": true
}
}

View File

@ -7,6 +7,7 @@ support-files =
google_service.sjs
head.js
loop_fxa.sjs
test_loopLinkClicker_channel.html
../../../../base/content/test/general/browser_fxa_oauth_with_keys.html
[browser_CardDavImporter.js]
@ -15,6 +16,8 @@ support-files =
skip-if = e10s
[browser_loop_fxa_server.js]
[browser_LoopContacts.js]
[browser_LoopRooms_channel.js]
skip-if = asan && e10s # Bug 1206457
[browser_mozLoop_appVersionInfo.js]
[browser_mozLoop_context.js]
[browser_mozLoop_prefs.js]

View File

@ -0,0 +1,108 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/*
* This file contains tests for checking the channel from the standalone to
* LoopRooms works for checking if rooms can be opened within the conversation
* window.
*/
"use strict";
var {WebChannel} = Cu.import("resource://gre/modules/WebChannel.jsm", {});
var {Chat} = Cu.import("resource:///modules/Chat.jsm", {});
const TEST_URI =
"example.com/browser/browser/components/loop/test/mochitest/test_loopLinkClicker_channel.html";
const TEST_URI_GOOD = Services.io.newURI("https://" + TEST_URI, null, null);
const TEST_URI_BAD = Services.io.newURI("http://" + TEST_URI, null, null);
const ROOM_TOKEN = "fake1234";
const LINKCLICKER_URL_PREFNAME = "loop.linkClicker.url";
var openChatOrig = Chat.open;
var fakeRoomList = new Map([[ ROOM_TOKEN, { roomToken: ROOM_TOKEN } ]]);
// Loads the specified URI in a new tab and waits for it to send us data on our
// test web-channel and resolves with that data.
function promiseNewChannelResponse(uri, hash) {
let waitForChannelPromise = new Promise((resolve, reject) => {
let channel = new WebChannel("test-loop-link-clicker-backchannel", uri);
channel.listen((id, data, target) => {
channel.stopListening();
resolve(data);
});
});
return BrowserTestUtils.withNewTab({
gBrowser: gBrowser,
url: uri.spec + "#" + hash
}, () => waitForChannelPromise);
}
add_task(function* test_loopRooms_webChannel_permissions() {
// We haven't set the allowed web page yet - so even the "good" URI should fail.
let got = yield promiseNewChannelResponse(TEST_URI_GOOD, "checkWillOpenRoom");
// Should have no data.
Assert.ok(got.message === undefined, "should have failed to get any data");
// Add a permission manager entry for our URI.
Services.prefs.setCharPref(LINKCLICKER_URL_PREFNAME, TEST_URI_GOOD.spec);
registerCleanupFunction(() => {
Services.prefs.clearUserPref(LINKCLICKER_URL_PREFNAME);
});
// Try again - now we are expecting a response with actual data.
got = yield promiseNewChannelResponse(TEST_URI_GOOD, "checkWillOpenRoom");
// The room doesn't exist, so we should get a negative response.
Assert.equal(got.message.response, false, "should have got a response of false");
// Now a http:// URI - should get nothing even with the permission setup.
got = yield promiseNewChannelResponse(TEST_URI_BAD, "checkWillOpenRoom");
Assert.ok(got.message === undefined, "should have failed to get any data");
});
add_task(function* test_loopRooms_webchannel_checkWillOpenRoom() {
// We've already tested if the room doesn't exist above, so here we add the
// room and check the result.
LoopRooms._setRoomsCache(fakeRoomList);
let got = yield promiseNewChannelResponse(TEST_URI_GOOD, "checkWillOpenRoom");
Assert.equal(got.message.response, true, "should have got a response of true");
});
add_task(function* test_loopRooms_webchannel_openRoom() {
let openedUrl;
Chat.open = function(contentWindow, origin, title, url) {
openedUrl = url;
};
registerCleanupFunction(() => {
Chat.open = openChatOrig;
});
// Test when the room doesn't exist
LoopRooms._setRoomsCache();
let got = yield promiseNewChannelResponse(TEST_URI_GOOD, "openRoom");
Assert.ok(!openedUrl, "should not open a chat window");
Assert.equal(got.message.response, false, "should have got a response of false");
// Now add a room & check it.
LoopRooms._setRoomsCache(fakeRoomList);
got = yield promiseNewChannelResponse(TEST_URI_GOOD, "openRoom");
// Check the room was opened.
Assert.ok(openedUrl, "should open a chat window");
let windowId = openedUrl.match(/about:loopconversation\#(\w+)$/)[1];
let windowData = MozLoopService.getConversationWindowData(windowId);
Assert.equal(windowData.type, "room", "window data should contain room as the type");
Assert.equal(windowData.roomToken, ROOM_TOKEN, "window data should have the roomToken");
Assert.equal(got.message.response, true, "should have got a response of true");
});

View File

@ -0,0 +1,46 @@
<!DOCTYPE HTML>
<html>
<script>
"use strict";
// Add a listener for responses to our remote requests.
window.addEventListener("WebChannelMessageToContent", function (event) {
if (event.detail.id == "loop-link-clicker") {
// Send what we got back to the test.
var backEvent = new window.CustomEvent("WebChannelMessageToChrome", {
detail: {
id: "test-loop-link-clicker-backchannel",
message: {
message: event.detail.message
}
}
});
window.dispatchEvent(backEvent);
// and stick it in our DOM just for good measure/diagnostics.
document.getElementById("troubleshooting").textContent =
JSON.stringify(event.detail.message, null, 2);
}
});
// Send a message on load requesting that the room is opened.
window.onload = function() {
var hash = window.location.hash;
var event = new window.CustomEvent("WebChannelMessageToChrome", {
detail: {
id: "loop-link-clicker",
message: {
command: hash.substring(1, hash.length),
roomToken: "fake1234"
}
}
});
window.dispatchEvent(event);
};
</script>
<body>
<pre id="troubleshooting"/>
</body>
</html>

View File

@ -684,8 +684,11 @@ AnimationsTimeline.prototype = {
let getTime = time => L10N.getFormatStr("player.timeLabel",
L10N.numberWithDecimals(time / 1000, 2));
let title = L10N.getFormatStr("timeline." + state.type + ".nameLabel",
state.name);
// The type isn't always available, older servers don't send it.
let title =
state.type
? L10N.getFormatStr("timeline." + state.type + ".nameLabel", state.name)
: state.name;
let delay = L10N.getStr("player.animationDelayLabel") + " " +
getTime(state.delay);
let duration = L10N.getStr("player.animationDurationLabel") + " " +

View File

@ -49,14 +49,15 @@ this.BrowserTestUtils = {
* the tab is loaded. The first argument passed to the function is a
* reference to the browser object for the new tab.
*
* @return {Promise}
* @return {} Returns the value that is returned from taskFn.
* @resolves When the tab has been closed.
* @rejects Any exception from taskFn is propagated.
*/
withNewTab: Task.async(function* (options, taskFn) {
let tab = yield BrowserTestUtils.openNewForegroundTab(options.gBrowser, options.url);
yield taskFn(tab.linkedBrowser);
let result = yield taskFn(tab.linkedBrowser);
options.gBrowser.removeTab(tab);
return Promise.resolve(result);
}),
/**