Merge m-c to b2g-inbound a=merge

This commit is contained in:
Wes Kocher 2014-11-06 19:09:40 -08:00
Родитель 52e5b44b7b 7c3f7b9ce1
Коммит d6ef25a0ca
305 изменённых файлов: 4804 добавлений и 3468 удалений

Просмотреть файл

@ -22,4 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Bug 1087560 changed a mochitest manifest without touching its moz.build.
Bug 1095234 - Bug 1091260 stopped packaging a devtools file with EXTRA_JS_MODULES while making it require pre-processing.

Просмотреть файл

@ -1650,7 +1650,7 @@ pref("loop.learnMoreUrl", "https://www.firefox.com/hello/");
pref("loop.legal.ToS_url", "https://hello.firefox.com/legal/terms/");
pref("loop.legal.privacy_url", "https://www.mozilla.org/privacy/");
pref("loop.do_not_disturb", false);
pref("loop.ringtone", "chrome://browser/content/loop/shared/sounds/Firefox-Long.ogg");
pref("loop.ringtone", "chrome://browser/content/loop/shared/sounds/ringtone.ogg");
pref("loop.retry_delay.start", 60000);
pref("loop.retry_delay.limit", 300000);
pref("loop.feedback.baseUrl", "https://input.mozilla.org/api/v1/feedback");
@ -1660,9 +1660,9 @@ pref("loop.debug.dispatcher", false);
pref("loop.debug.websocket", false);
pref("loop.debug.sdk", false);
#ifdef DEBUG
pref("loop.CSP", "default-src 'self' about: file: chrome: http://localhost:*; img-src 'self' data: http://www.gravatar.com/ about: file: chrome:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net http://localhost:* ws://localhost:*");
pref("loop.CSP", "default-src 'self' about: file: chrome: http://localhost:*; img-src 'self' data: http://www.gravatar.com/ about: file: chrome:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net http://localhost:* ws://localhost:*; media-src blob:");
#else
pref("loop.CSP", "default-src 'self' about: file: chrome:; img-src 'self' data: http://www.gravatar.com/ about: file: chrome:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net");
pref("loop.CSP", "default-src 'self' about: file: chrome:; img-src 'self' data: http://www.gravatar.com/ about: file: chrome:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net; media-src blob:");
#endif
pref("loop.oauth.google.redirect_uri", "urn:ietf:wg:oauth:2.0:oob:auto");
pref("loop.oauth.google.scope", "https://www.google.com/m8/feeds");
@ -1809,6 +1809,10 @@ pref("browser.polaris.enabled", false);
pref("privacy.trackingprotection.ui.enabled", false);
#endif
#ifdef NIGHTLY_BUILD
pref("browser.tabs.remote.autostart.1", true);
#endif
// Temporary pref to allow printing in e10s windows on some platforms.
#ifdef UNIX_BUT_NOT_MAC
pref("print.enable_e10s_testing", false);

Просмотреть файл

@ -787,9 +787,6 @@ statuspanel[inactive][previoustype=overLink] {
max-width: 32em;
}
/* highlighter */
%include highlighter.css
/* gcli */
html|*#gcli-tooltip-frame,

Просмотреть файл

@ -4551,15 +4551,14 @@ function nsBrowserAccess() { }
nsBrowserAccess.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow, Ci.nsISupports]),
_openURIInNewTab: function(aURI, aOpener, aIsExternal) {
_openURIInNewTab: function(aURI, aReferrer, aIsPrivate, aIsExternal) {
let win, needToFocusWin;
// try the current window. if we're in a popup, fall back on the most recent browser window
if (window.toolbar.visible)
win = window;
else {
let isPrivate = PrivateBrowsingUtils.isWindowPrivate(aOpener || window);
win = RecentWindow.getMostRecentBrowserWindow({private: isPrivate});
win = RecentWindow.getMostRecentBrowserWindow({private: aIsPrivate});
needToFocusWin = true;
}
@ -4575,10 +4574,9 @@ nsBrowserAccess.prototype = {
}
let loadInBackground = gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground");
let referrer = aOpener ? makeURI(aOpener.location.href) : null;
let tab = win.gBrowser.loadOneTab(aURI ? aURI.spec : "about:blank", {
referrerURI: referrer,
referrerURI: aReferrer,
fromExternal: aIsExternal,
inBackground: loadInBackground});
let browser = win.gBrowser.getBrowserForTab(tab);
@ -4615,7 +4613,9 @@ nsBrowserAccess.prototype = {
newWindow = openDialog(getBrowserURL(), "_blank", "all,dialog=no", url, null, null, null);
break;
case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB :
let browser = this._openURIInNewTab(aURI, aOpener, isExternal);
let referrer = aOpener ? makeURI(aOpener.location.href) : null;
let isPrivate = PrivateBrowsingUtils.isWindowPrivate(aOpener || window);
let browser = this._openURIInNewTab(aURI, referrer, isPrivate, isExternal);
if (browser)
newWindow = browser.contentWindow;
break;
@ -4634,14 +4634,14 @@ nsBrowserAccess.prototype = {
return newWindow;
},
openURIInFrame: function browser_openURIInFrame(aURI, aOpener, aWhere, aContext) {
openURIInFrame: function browser_openURIInFrame(aURI, aParams, aWhere, aContext) {
if (aWhere != Ci.nsIBrowserDOMWindow.OPEN_NEWTAB) {
dump("Error: openURIInFrame can only open in new tabs");
return null;
}
var isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
let browser = this._openURIInNewTab(aURI, aOpener, isExternal);
let browser = this._openURIInNewTab(aURI, aParams.referrer, aParams.isPrivate, isExternal);
if (browser)
return browser.QueryInterface(Ci.nsIFrameLoaderOwner);

Просмотреть файл

@ -123,7 +123,7 @@ chatBrowserAccess.prototype = {
return browser ? browser.contentWindow : null;
},
openURIInFrame: function browser_openURIInFrame(aURI, aOpener, aWhere, aContext) {
openURIInFrame: function browser_openURIInFrame(aURI, aParams, aWhere, aContext) {
let browser = this._openURIInNewTab(aURI, aWhere);
return browser ? browser.QueryInterface(Ci.nsIFrameLoaderOwner) : null;
},

Просмотреть файл

@ -1,80 +0,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/. */
.highlighter-container {
pointer-events: none;
}
/*
* Box model highlighter
*/
svg|svg.box-model-root[hidden],
svg|line.box-model-guide-top[hidden],
svg|line.box-model-guide-right[hidden],
svg|line.box-model-guide-left[hidden],
svg|line.box-model-guide-bottom[hidden] {
display: none;
}
/*
* Node Infobar
*/
.highlighter-nodeinfobar-container {
position: relative;
}
.highlighter-nodeinfobar-positioner {
position: absolute;
max-width: 95%;
}
.highlighter-nodeinfobar-positioner[hidden] {
opacity: 0;
pointer-events: none;
display: -moz-box;
}
.highlighter-nodeinfobar-text {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
direction: ltr;
}
html|*.highlighter-nodeinfobar-id,
html|*.highlighter-nodeinfobar-classes,
html|*.highlighter-nodeinfobar-pseudo-classes,
html|*.highlighter-nodeinfobar-dimensions,
html|*.highlighter-nodeinfobar-tagname {
-moz-user-select: text;
-moz-user-focus: normal;
cursor: text;
}
.highlighter-nodeinfobar-arrow {
display: none;
}
.highlighter-nodeinfobar-positioner[position="top"]:not([hide-arrow]) > .highlighter-nodeinfobar-arrow-bottom {
display: block;
}
.highlighter-nodeinfobar-positioner[position="bottom"]:not([hide-arrow]) > .highlighter-nodeinfobar-arrow-top {
display: block;
}
.highlighter-nodeinfobar-positioner[disabled] {
visibility: hidden;
}
html|*.highlighter-nodeinfobar-tagname {
text-transform: lowercase;
}
/*
* Css transform highlighter
*/
svg|svg.css-transform-root[hidden] {
display: none;
}

Просмотреть файл

@ -74,7 +74,7 @@ Sanitizer.prototype = {
itemsToClear.splice(openWindowsIndex, 1);
let item = this.items.openWindows;
function onWindowsCleaned() {
let ok = item.clear(() => {
try {
let clearedPromise = this.sanitize(itemsToClear);
clearedPromise.then(deferred.resolve, deferred.reject);
@ -83,9 +83,7 @@ Sanitizer.prototype = {
Cu.reportError(error);
deferred.reject(error);
}
}
let ok = item.clear(onWindowsCleaned.bind(this));
});
// When cancelled, reject immediately
if (!ok) {
deferred.reject("Sanitizer canceled closing windows");

Просмотреть файл

@ -2,6 +2,8 @@
* 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/. */
Components.utils.import("resource://gre/modules/Promise.jsm", this);
let chatbar = document.getElementById("pinnedchats");
add_chat_task(function* testOpenCloseChat() {

Просмотреть файл

@ -4,6 +4,7 @@
// Tests the focus functionality.
Components.utils.import("resource://gre/modules/Promise.jsm", this);
const CHAT_URL = "https://example.com/browser/browser/base/content/test/chat/chat.html";
// Is the currently opened tab focused?

Просмотреть файл

@ -2,6 +2,8 @@
* 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/. */
Components.utils.import("resource://gre/modules/Promise.jsm", this);
let chatbar = document.getElementById("pinnedchats");
function promiseNewWindowLoaded() {

Просмотреть файл

@ -300,11 +300,11 @@ skip-if = e10s # Bug 1093155 - tries to use context menu from browser-chrome and
skip-if = os == 'win' || e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
[browser_bug1064280_changeUrlInPinnedTab.js]
[browser_canonizeURL.js]
skip-if = e10s # Bug ?????? - [JavaScript Error: "Error in AboutHome.sendAboutHomeData TypeError: target.messageManager is undefined" {file: "resource:///modules/AboutHome.jsm" line: 208}]
skip-if = e10s # Bug 1094510 - test hits the network in e10s mode only
[browser_contentAreaClick.js]
skip-if = e10s
[browser_contextSearchTabPosition.js]
skip-if = os == "mac" || e10s # bug 967013, bug 926729
skip-if = os == "mac" || e10s # bug 967013; e10s: bug 1094761 - test hits the network in e10s, causing next test to crash
[browser_ctrlTab.js]
skip-if = e10s # Bug ????? - thumbnail captures need e10s love (tabPreviews_capture fails with Argument 1 of CanvasRenderingContext2D.drawWindow does not implement interface Window.)
[browser_customize_popupNotification.js]

Просмотреть файл

@ -49,6 +49,11 @@ let check_history = Task.async(function*() {
}
});
function clear_history() {
gExpectedHistory.index = -1;
gExpectedHistory.entries = [];
}
// Waits for a load and updates the known history
let waitForLoad = Task.async(function*(uri) {
info("Loading " + uri);
@ -63,6 +68,28 @@ let waitForLoad = Task.async(function*(uri) {
});
});
// Waits for a load and updates the known history
let waitForLoadWithFlags = Task.async(function*(uri, flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE) {
info("Loading " + uri + " flags = " + flags);
gBrowser.selectedBrowser.loadURIWithFlags(uri, flags, null, null, null);
yield waitForDocLoadComplete();
if (!(flags & Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY)) {
if (flags & Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY) {
gExpectedHistory.entries.pop();
}
else {
gExpectedHistory.index++;
}
gExpectedHistory.entries.push({
uri: gBrowser.currentURI.spec,
title: gBrowser.contentTitle
});
}
});
let back = Task.async(function*() {
info("Going back");
gBrowser.goBack();
@ -80,10 +107,7 @@ let forward = Task.async(function*() {
// Tests that navigating from a page that should be in the remote process and
// a page that should be in the main process works and retains history
add_task(function* test_navigation() {
SimpleTest.requestCompleteLog();
let remoting = Services.prefs.getBoolPref("browser.tabs.remote.autostart");
let expectedRemote = remoting ? "true" : "";
let expectedRemote = gMultiProcessBrowser ? "true" : "";
info("1");
// Create a tab and load a remote page in it
@ -154,13 +178,13 @@ add_task(function* test_navigation() {
info("11");
gBrowser.removeCurrentTab();
clear_history();
});
// Tests that calling gBrowser.loadURI or browser.loadURI to load a page in a
// different process updates the browser synchronously
add_task(function* test_synchronous() {
let remoting = Services.prefs.getBoolPref("browser.tabs.remote.autostart");
let expectedRemote = remoting ? "true" : "";
let expectedRemote = gMultiProcessBrowser ? "true" : "";
info("1");
// Create a tab and load a remote page in it
@ -194,4 +218,42 @@ add_task(function* test_synchronous() {
info("4");
gBrowser.removeCurrentTab();
clear_history();
});
// Tests that load flags are correctly passed through to the child process with
// normal loads
add_task(function* test_loadflags() {
let expectedRemote = gMultiProcessBrowser ? "true" : "";
info("1");
// Create a tab and load a remote page in it
gBrowser.selectedTab = gBrowser.addTab("about:blank", {skipAnimation: true});
yield waitForLoadWithFlags("about:robots");
is(gBrowser.selectedTab.getAttribute("remote"), "", "Remote attribute should be correct");
yield check_history();
info("2");
// Load a page in the remote process with some custom flags
yield waitForLoadWithFlags("http://example.com/" + DUMMY_PATH, Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY);
is(gBrowser.selectedTab.getAttribute("remote"), expectedRemote, "Remote attribute should be correct");
yield check_history();
info("3");
// Load a non-remote page
yield waitForLoadWithFlags("about:robots");
is(gBrowser.selectedTab.getAttribute("remote"), "", "Remote attribute should be correct");
yield check_history();
info("4");
// Load another remote page
yield waitForLoadWithFlags("http://example.org/" + DUMMY_PATH, Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY);
is(gBrowser.selectedTab.getAttribute("remote"), expectedRemote, "Remote attribute should be correct");
yield check_history();
is(gExpectedHistory.entries.length, 2, "Should end with the right number of history entries");
info("5");
gBrowser.removeCurrentTab();
clear_history();
});

Просмотреть файл

@ -10,11 +10,20 @@
* ALL need to match an error in order for that error not to cause a test
* failure. */
const kWhitelist = [
{sourceName: /cleopatra.*(tree|ui)\.css/i}, /* Cleopatra is imported as-is, see bug 1004421 */
{sourceName: /codemirror\.css/i}, /* CodeMirror is imported as-is, see bug 1004423 */
{sourceName: /web\/viewer\.css/i, errorMessage: /Unknown pseudo-class.*(fullscreen|selection)/i }, /* PDFjs is futureproofing its pseudoselectors, and those rules are dropped. */
{sourceName: /aboutaccounts\/(main|normalize)\.css/i}, /* Tracked in bug 1004428 */
{sourceName: /loop\/.*sdk-content\/.*\.css$/i /* TokBox SDK assets, see bug 1032469 */}
// Cleopatra is imported as-is, see bug 1004421.
{sourceName: /cleopatra.*(tree|ui)\.css/i},
// CodeMirror is imported as-is, see bug 1004423.
{sourceName: /codemirror\.css/i},
// PDFjs is futureproofing its pseudoselectors, and those rules are dropped.
{sourceName: /web\/viewer\.css/i,
errorMessage: /Unknown pseudo-class.*(fullscreen|selection)/i},
// Tracked in bug 1004428.
{sourceName: /aboutaccounts\/(main|normalize)\.css/i},
// TokBox SDK assets, see bug 1032469.
{sourceName: /loop\/.*sdk-content\/.*\.css$/i},
// Highlighter CSS uses chrome-only pseudo-class, see bug 985597.
{sourceName: /highlighter\.css/i,
errorMessage: /Unknown pseudo-class.*moz-native-anonymous/i}
];
let moduleLocation = gTestPath.replace(/\/[^\/]*$/i, "/parsingTestHelpers.jsm");

Просмотреть файл

@ -27,7 +27,8 @@ add_task(function testWrapUnwrap() {
// Creating and destroying a widget should correctly deal with panel placeholders
add_task(function testPanelPlaceholders() {
let panel = document.getElementById(CustomizableUI.AREA_PANEL);
is(panel.querySelectorAll(".panel-customization-placeholder").length, isInWin8() ? 1 : 2, "The number of placeholders should be correct.");
let expectedPlaceholders = (isInWin8() ? 1 : 2) + (isInDevEdition() ? 1 : 0);
is(panel.querySelectorAll(".panel-customization-placeholder").length, expectedPlaceholders, "The number of placeholders should be correct.");
CustomizableUI.createWidget({id: kTestWidget2, label: 'Pretty label', tooltiptext: 'Pretty tooltip', defaultArea: CustomizableUI.AREA_PANEL});
let elem = document.getElementById(kTestWidget2);
let wrapper = document.getElementById("wrapper-" + kTestWidget2);
@ -35,7 +36,8 @@ add_task(function testPanelPlaceholders() {
ok(wrapper, "There should be a wrapper");
is(wrapper.firstChild.id, kTestWidget2, "Wrapper should have test widget");
is(wrapper.parentNode, panel, "Wrapper should be in panel");
is(panel.querySelectorAll(".panel-customization-placeholder").length, isInWin8() ? 3 : 1, "The number of placeholders should be correct.");
expectedPlaceholders = (isInWin8() ? 3 : 1) + (isInDevEdition() ? 1 : 0);
is(panel.querySelectorAll(".panel-customization-placeholder").length, expectedPlaceholders, "The number of placeholders should be correct.");
CustomizableUI.destroyWidget(kTestWidget2);
wrapper = document.getElementById("wrapper-" + kTestWidget2);
ok(!wrapper, "There should be a wrapper");

Просмотреть файл

@ -18,6 +18,9 @@ XPCOMUtils.defineLazyGetter(this, "eventEmitter", function() {
this.EXPORTED_SYMBOLS = ["LoopRooms", "roomsPushNotification"];
// The maximum number of clients that we support currently.
const CLIENT_MAX_SIZE = 2;
const roomsPushNotification = function(version, channelID) {
return LoopRoomsInternal.onNotification(version, channelID);
};
@ -271,6 +274,81 @@ let LoopRoomsInternal = {
}, error => callback(error)).catch(error => callback(error));
},
/**
* Internal function to handle POSTs to a room.
*
* @param {String} roomToken The room token.
* @param {Object} postData The data to post to the room.
* @param {Function} callback Function that will be invoked once the operation
* finished. The first argument passed will be an
* `Error` object or `null`.
*/
_postToRoom(roomToken, postData, callback) {
let url = "/rooms/" + encodeURIComponent(roomToken);
MozLoopService.hawkRequest(this.sessionType, url, "POST", postData).then(response => {
// Delete doesn't have a body return.
var joinData = response.body ? JSON.parse(response.body) : {};
callback(null, joinData);
}, error => callback(error)).catch(error => callback(error));
},
/**
* Joins a room
*
* @param {String} roomToken The room token.
* @param {Function} callback Function that will be invoked once the operation
* finished. The first argument passed will be an
* `Error` object or `null`.
*/
join: function(roomToken, callback) {
this._postToRoom(roomToken, {
action: "join",
displayName: MozLoopService.userProfile.email,
clientMaxSize: CLIENT_MAX_SIZE
}, callback);
},
/**
* Refreshes a room
*
* @param {String} roomToken The room token.
* @param {String} sessionToken The session token for the session that has been
* joined
* @param {Function} callback Function that will be invoked once the operation
* finished. The first argument passed will be an
* `Error` object or `null`.
*/
refreshMembership: function(roomToken, sessionToken, callback) {
this._postToRoom(roomToken, {
action: "refresh",
sessionToken: sessionToken
}, callback);
},
/**
* Leaves a room. Although this is an sync function, no data is returned
* from the server.
*
* @param {String} roomToken The room token.
* @param {String} sessionToken The session token for the session that has been
* joined
* @param {Function} callback Optional. Function that will be invoked once the operation
* finished. The first argument passed will be an
* `Error` object or `null`.
*/
leave: function(roomToken, sessionToken, callback) {
if (!callback) {
callback = function(error) {
if (error) {
MozLoopService.log.error(error);
}
};
}
this._postToRoom(roomToken, {
action: "leave",
sessionToken: sessionToken
}, callback);
},
/**
* Callback used to indicate changes to rooms data on the LoopServer.
@ -322,6 +400,19 @@ this.LoopRooms = {
return LoopRoomsInternal.delete(roomToken, callback);
},
join: function(roomToken, callback) {
return LoopRoomsInternal.join(roomToken, callback);
},
refreshMembership: function(roomToken, sessionToken, callback) {
return LoopRoomsInternal.refreshMembership(roomToken, sessionToken,
callback);
},
leave: function(roomToken, sessionToken, callback) {
return LoopRoomsInternal.leave(roomToken, sessionToken, callback);
},
promise: function(method, ...params) {
return new Promise((resolve, reject) => {
this[method](...params, (error, result) => {

Просмотреть файл

@ -13,6 +13,7 @@ Cu.import("resource:///modules/loop/LoopCalls.jsm");
Cu.import("resource:///modules/loop/MozLoopService.jsm");
Cu.import("resource:///modules/loop/LoopRooms.jsm");
Cu.import("resource:///modules/loop/LoopContacts.jsm");
Cu.importGlobalProperties(["Blob"]);
XPCOMUtils.defineLazyModuleGetter(this, "LoopContacts",
"resource:///modules/loop/LoopContacts.jsm");
@ -685,6 +686,31 @@ function injectLoopAPI(targetWindow) {
return MozLoopService.generateUUID();
}
},
getAudioBlob: {
enumerable: true,
writable: true,
value: function(name, callback) {
let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Ci.nsIXMLHttpRequest);
let url = `chrome://browser/content/loop/shared/sounds/${name}.ogg`;
request.open("GET", url, true);
request.responseType = "arraybuffer";
request.onload = () => {
if (request.status < 200 || request.status >= 300) {
let error = new Error(request.status + " " + request.statusText);
callback(cloneValueInto(error, targetWindow));
return;
}
let blob = new Blob([request.response], {type: "audio/ogg"});
callback(null, cloneValueInto(blob, targetWindow));
};
request.send();
}
}
};
function onStatusChanged(aSubject, aTopic, aData) {

Просмотреть файл

@ -21,7 +21,7 @@ loop.conversation = (function(mozL10n) {
var DesktopRoomView = loop.roomViews.DesktopRoomView;
var IncomingCallView = React.createClass({displayName: 'IncomingCallView',
mixins: [sharedMixins.DropdownMenuMixin],
mixins: [sharedMixins.DropdownMenuMixin, sharedMixins.AudioMixin],
propTypes: {
model: React.PropTypes.object.isRequired,
@ -185,10 +185,16 @@ loop.conversation = (function(mozL10n) {
* incoming call views (bug 1088672).
*/
var GenericFailureView = React.createClass({displayName: 'GenericFailureView',
mixins: [sharedMixins.AudioMixin],
propTypes: {
cancelCall: React.PropTypes.func.isRequired
},
componentDidMount: function() {
this.play("failure");
},
render: function() {
document.title = mozL10n.get("generic_failure_title");
@ -665,7 +671,11 @@ loop.conversation = (function(mozL10n) {
window.addEventListener("unload", function(event) {
// Handle direct close of dialog box via [x] control.
// XXX Move to the conversation models, when we transition
// incoming calls to flux (bug 1088672).
navigator.mozLoop.calls.clearCallInProgress(windowId);
dispatcher.dispatch(new sharedActions.WindowUnload());
});
React.renderComponent(AppControllerView({

Просмотреть файл

@ -21,7 +21,7 @@ loop.conversation = (function(mozL10n) {
var DesktopRoomView = loop.roomViews.DesktopRoomView;
var IncomingCallView = React.createClass({
mixins: [sharedMixins.DropdownMenuMixin],
mixins: [sharedMixins.DropdownMenuMixin, sharedMixins.AudioMixin],
propTypes: {
model: React.PropTypes.object.isRequired,
@ -185,10 +185,16 @@ loop.conversation = (function(mozL10n) {
* incoming call views (bug 1088672).
*/
var GenericFailureView = React.createClass({
mixins: [sharedMixins.AudioMixin],
propTypes: {
cancelCall: React.PropTypes.func.isRequired
},
componentDidMount: function() {
this.play("failure");
},
render: function() {
document.title = mozL10n.get("generic_failure_title");
@ -665,7 +671,11 @@ loop.conversation = (function(mozL10n) {
window.addEventListener("unload", function(event) {
// Handle direct close of dialog box via [x] control.
// XXX Move to the conversation models, when we transition
// incoming calls to flux (bug 1088672).
navigator.mozLoop.calls.clearCallInProgress(windowId);
dispatcher.dispatch(new sharedActions.WindowUnload());
});
React.renderComponent(<AppControllerView

Просмотреть файл

@ -14,6 +14,7 @@ loop.conversationViews = (function(mozL10n) {
var sharedActions = loop.shared.actions;
var sharedUtils = loop.shared.utils;
var sharedViews = loop.shared.views;
var sharedMixins = loop.shared.mixins;
// This duplicates a similar function in contacts.jsx that isn't used in the
// conversation window. If we get too many of these, we might want to consider
@ -133,6 +134,8 @@ loop.conversationViews = (function(mozL10n) {
* pending/ringing strings.
*/
var PendingConversationView = React.createClass({displayName: 'PendingConversationView',
mixins: [sharedMixins.AudioMixin],
propTypes: {
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
callState: React.PropTypes.string,
@ -146,6 +149,10 @@ loop.conversationViews = (function(mozL10n) {
};
},
componentDidMount: function() {
this.play("ringtone", {loop: true});
},
cancelCall: function() {
this.props.dispatcher.dispatch(new sharedActions.CancelCall());
},
@ -186,7 +193,7 @@ loop.conversationViews = (function(mozL10n) {
* Call failed view. Displayed when a call fails.
*/
var CallFailedView = React.createClass({displayName: 'CallFailedView',
mixins: [Backbone.Events],
mixins: [Backbone.Events, sharedMixins.AudioMixin],
propTypes: {
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
@ -205,6 +212,7 @@ loop.conversationViews = (function(mozL10n) {
},
componentDidMount: function() {
this.play("failure");
this.listenTo(this.props.store, "change:emailLink",
this._onEmailLinkReceived);
this.listenTo(this.props.store, "error:emailLink",

Просмотреть файл

@ -14,6 +14,7 @@ loop.conversationViews = (function(mozL10n) {
var sharedActions = loop.shared.actions;
var sharedUtils = loop.shared.utils;
var sharedViews = loop.shared.views;
var sharedMixins = loop.shared.mixins;
// This duplicates a similar function in contacts.jsx that isn't used in the
// conversation window. If we get too many of these, we might want to consider
@ -133,6 +134,8 @@ loop.conversationViews = (function(mozL10n) {
* pending/ringing strings.
*/
var PendingConversationView = React.createClass({
mixins: [sharedMixins.AudioMixin],
propTypes: {
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
callState: React.PropTypes.string,
@ -146,6 +149,10 @@ loop.conversationViews = (function(mozL10n) {
};
},
componentDidMount: function() {
this.play("ringtone", {loop: true});
},
cancelCall: function() {
this.props.dispatcher.dispatch(new sharedActions.CancelCall());
},
@ -186,7 +193,7 @@ loop.conversationViews = (function(mozL10n) {
* Call failed view. Displayed when a call fails.
*/
var CallFailedView = React.createClass({
mixins: [Backbone.Events],
mixins: [Backbone.Events, sharedMixins.AudioMixin],
propTypes: {
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
@ -205,6 +212,7 @@ loop.conversationViews = (function(mozL10n) {
},
componentDidMount: function() {
this.play("failure");
this.listenTo(this.props.store, "change:emailLink",
this._onEmailLinkReceived);
this.listenTo(this.props.store, "error:emailLink",

Просмотреть файл

@ -10,6 +10,8 @@ var loop = loop || {};
loop.roomViews = (function(mozL10n) {
"use strict";
var ROOM_STATES = loop.store.ROOM_STATES;
var DesktopRoomView = React.createClass({displayName: 'DesktopRoomView',
mixins: [Backbone.Events, loop.shared.mixins.DocumentTitleMixin],
@ -19,7 +21,7 @@ loop.roomViews = (function(mozL10n) {
},
getInitialState: function() {
return this.props.roomStore.getStoreState();
return this.props.roomStore.getStoreState("activeRoom");
},
componentWillMount: function() {
@ -41,13 +43,28 @@ loop.roomViews = (function(mozL10n) {
this.stopListening(this.props.roomStore);
},
/**
* Closes the window if the cancel button is pressed in the generic failure view.
*/
closeWindow: function() {
window.close();
},
render: function() {
if (this.state.serverData && this.state.serverData.roomName) {
this.setTitle(this.state.serverData.roomName);
if (this.state.roomName) {
this.setTitle(this.state.roomName);
}
if (this.state.roomState === ROOM_STATES.FAILED) {
return (loop.conversation.GenericFailureView({
cancelCall: this.closeWindow}
));
}
return (
React.DOM.div({className: "goat"})
React.DOM.div(null,
React.DOM.div(null, mozL10n.get("invite_header_text"))
)
);
}
});

Просмотреть файл

@ -10,6 +10,8 @@ var loop = loop || {};
loop.roomViews = (function(mozL10n) {
"use strict";
var ROOM_STATES = loop.store.ROOM_STATES;
var DesktopRoomView = React.createClass({
mixins: [Backbone.Events, loop.shared.mixins.DocumentTitleMixin],
@ -19,7 +21,7 @@ loop.roomViews = (function(mozL10n) {
},
getInitialState: function() {
return this.props.roomStore.getStoreState();
return this.props.roomStore.getStoreState("activeRoom");
},
componentWillMount: function() {
@ -41,13 +43,28 @@ loop.roomViews = (function(mozL10n) {
this.stopListening(this.props.roomStore);
},
/**
* Closes the window if the cancel button is pressed in the generic failure view.
*/
closeWindow: function() {
window.close();
},
render: function() {
if (this.state.serverData && this.state.serverData.roomName) {
this.setTitle(this.state.serverData.roomName);
if (this.state.roomName) {
this.setTitle(this.state.roomName);
}
if (this.state.roomState === ROOM_STATES.FAILED) {
return (<loop.conversation.GenericFailureView
cancelCall={this.closeWindow}
/>);
}
return (
<div className="goat"/>
<div>
<div>{mozL10n.get("invite_header_text")}</div>
</div>
);
}
});

Просмотреть файл

@ -67,6 +67,12 @@ loop.shared.actions = (function() {
windowType: String
}),
/**
* Used to signal when the window is being unloaded.
*/
WindowUnload: Action.define("windowUnload", {
}),
/**
* Fetch a new call url from the server, intended to be sent over email when
* a contact can't be reached.
@ -227,6 +233,46 @@ loop.shared.actions = (function() {
*/
CopyRoomUrl: Action.define("copyRoomUrl", {
roomUrl: String
}),
/**
* XXX: should move to some roomActions module - refs bug 1079284
*/
RoomFailure: Action.define("roomFailure", {
error: Object
}),
/**
* Updates the room information when it is received.
* XXX: should move to some roomActions module - refs bug 1079284
*
* @see https://wiki.mozilla.org/Loop/Architecture/Rooms#GET_.2Frooms.2F.7Btoken.7D
*/
UpdateRoomInfo: Action.define("updateRoomInfo", {
roomName: String,
roomOwner: String,
roomToken: String,
roomUrl: String
}),
/**
* Starts the process for the user to join the room.
* XXX: should move to some roomActions module - refs bug 1079284
*/
JoinRoom: Action.define("joinRoom", {
}),
/**
* Signals the user has successfully joined the room on the loop-server.
* XXX: should move to some roomActions module - refs bug 1079284
*
* @see https://wiki.mozilla.org/Loop/Architecture/Rooms#Joining_a_Room
*/
JoinedRoom: Action.define("joinedRoom", {
apiKey: String,
sessionToken: String,
sessionId: String,
expires: Number
})
};
})();

Просмотреть файл

@ -11,6 +11,19 @@ loop.store.ActiveRoomStore = (function() {
var sharedActions = loop.shared.actions;
var ROOM_STATES = loop.store.ROOM_STATES = {
// The initial state of the room
INIT: "room-init",
// The store is gathering the room data
GATHER: "room-gather",
// The store has got the room data
READY: "room-ready",
// The room is known to be joined on the loop-server
JOINED: "room-joined",
// There was an issue with the room
FAILED: "room-failed"
};
/**
* Store for things that are local to this instance (in this profile, on
* this machine) of this roomRoom store, in addition to a mirror of some
@ -29,53 +42,78 @@ loop.store.ActiveRoomStore = (function() {
if (!options.dispatcher) {
throw new Error("Missing option dispatcher");
}
this.dispatcher = options.dispatcher;
this._dispatcher = options.dispatcher;
if (!options.mozLoop) {
throw new Error("Missing option mozLoop");
}
this.mozLoop = options.mozLoop;
this._mozLoop = options.mozLoop;
this.dispatcher.register(this, [
"setupWindowData"
this._dispatcher.register(this, [
"roomFailure",
"setupWindowData",
"updateRoomInfo",
"joinRoom",
"joinedRoom",
"windowUnload"
]);
}
ActiveRoomStore.prototype = _.extend({
/**
* Stored data reflecting the local state of a given room, used to drive
* the room's views.
*
* @property {Object} serverData - local cache of the data returned by
* MozLoop.getRoomData for this room.
* @see https://wiki.mozilla.org/Loop/Architecture/Rooms#GET_.2Frooms.2F.7Btoken.7D
* for the main data. Additional properties below.
*
* @property {ROOM_STATES} roomState - the state of the room.
* @property {Error=} error - if the room is an error state, this will be
* set to an Error object reflecting the problem;
* otherwise it will be unset.
*/
_storeState: {
},
this._storeState = {
roomState: ROOM_STATES.INIT
};
}
ActiveRoomStore.prototype = _.extend({
/**
* The time factor to adjust the expires time to ensure that we send a refresh
* before the expiry. Currently set as 90%.
*/
expiresTimeFactor: 0.9,
getStoreState: function() {
return this._storeState;
},
setStoreState: function(state) {
this._storeState = state;
setStoreState: function(newState) {
for (var key in newState) {
this._storeState[key] = newState[key];
}
this.trigger("change");
},
/**
* 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.
* Handles a room failure. Currently this prints the error to the console
* and sets the roomState to failed.
*
* When the room name gets set, that will trigger the view to display
* that name.
* @param {sharedActions.RoomFailure} actionData
*/
roomFailure: function(actionData) {
console.error("Error in state `" + this._storeState.roomState + "`:",
actionData.error);
this.setStoreState({
error: actionData.error,
roomState: ROOM_STATES.FAILED
});
},
/**
* Execute setupWindowData event action from the dispatcher. This gets
* the room data from the mozLoop api, and dispatches an UpdateRoomInfo event.
* It also dispatches JoinRoom as this action is only applicable to the desktop
* client, and needs to auto-join.
*
* @param {sharedActions.SetupWindowData} actionData
*/
@ -85,14 +123,144 @@ loop.store.ActiveRoomStore = (function() {
return;
}
this.mozLoop.rooms.get(actionData.roomToken,
this.setStoreState({
roomState: ROOM_STATES.GATHER
});
// Get the window data from the mozLoop api.
this._mozLoop.rooms.get(actionData.roomToken,
function(error, roomData) {
this.setStoreState({
error: error,
if (error) {
this._dispatcher.dispatch(new sharedActions.RoomFailure({
error: error
}));
return;
}
this._dispatcher.dispatch(
new sharedActions.UpdateRoomInfo({
roomToken: actionData.roomToken,
serverData: roomData
});
roomName: roomData.roomName,
roomOwner: roomData.roomOwner,
roomUrl: roomData.roomUrl
}));
// For the conversation window, we need to automatically
// join the room.
this._dispatcher.dispatch(new sharedActions.JoinRoom());
}.bind(this));
},
/**
* Handles the updateRoomInfo action. Updates the room data and
* sets the state to `READY`.
*
* @param {sharedActions.UpdateRoomInfo} actionData
*/
updateRoomInfo: function(actionData) {
this.setStoreState({
roomName: actionData.roomName,
roomOwner: actionData.roomOwner,
roomState: ROOM_STATES.READY,
roomToken: actionData.roomToken,
roomUrl: actionData.roomUrl
});
},
/**
* Handles the action to join to a room.
*/
joinRoom: function() {
this._mozLoop.rooms.join(this._storeState.roomToken,
function(error, responseData) {
if (error) {
this._dispatcher.dispatch(
new sharedActions.RoomFailure({error: error}));
return;
}
this._dispatcher.dispatch(new sharedActions.JoinedRoom({
apiKey: responseData.apiKey,
sessionToken: responseData.sessionToken,
sessionId: responseData.sessionId,
expires: responseData.expires
}));
}.bind(this));
},
/**
* Handles the data received from joining a room. It stores the relevant
* data, and sets up the refresh timeout for ensuring membership of the room
* is refreshed regularly.
*
* @param {sharedActions.JoinedRoom} actionData
*/
joinedRoom: function(actionData) {
this.setStoreState({
apiKey: actionData.apiKey,
sessionToken: actionData.sessionToken,
sessionId: actionData.sessionId,
roomState: ROOM_STATES.JOINED
});
this._setRefreshTimeout(actionData.expires);
},
/**
* Handles the window being unloaded. Ensures the room is left.
*/
windowUnload: function() {
this._leaveRoom();
},
/**
* Handles setting of the refresh timeout callback.
*
* @param {Integer} expireTime The time until expiry (in seconds).
*/
_setRefreshTimeout: function(expireTime) {
this._timeout = setTimeout(this._refreshMembership.bind(this),
expireTime * this.expiresTimeFactor * 1000);
},
/**
* Refreshes the membership of the room with the server, and then
* sets up the refresh for the next cycle.
*/
_refreshMembership: function() {
this._mozLoop.rooms.refreshMembership(this._storeState.roomToken,
this._storeState.sessionToken,
function(error, responseData) {
if (error) {
this._dispatcher.dispatch(
new sharedActions.RoomFailure({error: error}));
return;
}
this._setRefreshTimeout(responseData.expires);
}.bind(this));
},
/**
* Handles leaving a room. Clears any membership timeouts, then
* signals to the server the leave of the room.
*/
_leaveRoom: function() {
if (this._storeState.roomState !== ROOM_STATES.JOINED) {
return;
}
if (this._timeout) {
clearTimeout(this._timeout);
delete this._timeout;
}
this._mozLoop.rooms.leave(this._storeState.roomToken,
this._storeState.sessionToken);
this.setStoreState({
roomState: ROOM_STATES.READY
});
}
}, Backbone.Events);

Просмотреть файл

@ -141,6 +141,7 @@ loop.shared.mixins = (function() {
*/
var AudioMixin = {
audio: null,
_audioRequest: null,
_isLoopDesktop: function() {
return typeof rootObject.navigator.mozLoop === "object";
@ -149,27 +150,62 @@ loop.shared.mixins = (function() {
/**
* Starts playing an audio file, stopping any audio that is already in progress.
*
* @param {String} filename The filename to play (excluding the extension).
* @param {String} name The filename to play (excluding the extension).
*/
play: function(filename, options) {
if (this._isLoopDesktop()) {
// XXX: We need navigator.mozLoop.playSound(name), see Bug 1089585.
return;
}
play: function(name, options) {
options = options || {};
options.loop = options.loop || false;
this._ensureAudioStopped();
this.audio = new Audio('shared/sounds/' + filename + ".ogg");
this.audio.loop = options.loop;
this.audio.play();
this._getAudioBlob(name, function(error, blob) {
if (error) {
console.error(error);
return;
}
var url = URL.createObjectURL(blob);
this.audio = new Audio(url);
this.audio.loop = options.loop;
this.audio.play();
}.bind(this));
},
_getAudioBlob: function(name, callback) {
if (this._isLoopDesktop()) {
rootObject.navigator.mozLoop.getAudioBlob(name, callback);
return;
}
var url = "shared/sounds/" + name + ".ogg";
this._audioRequest = new XMLHttpRequest();
this._audioRequest.open("GET", url, true);
this._audioRequest.responseType = "arraybuffer";
this._audioRequest.onload = function() {
var request = this._audioRequest;
var error;
if (request.status < 200 || request.status >= 300) {
error = new Error(request.status + " " + request.statusText);
callback(error);
return;
}
var type = request.getResponseHeader("Content-Type");
var blob = new Blob([request.response], {type: type});
callback(null, blob);
}.bind(this);
this._audioRequest.send(null);
},
/**
* Ensures audio is stopped playing, and removes the object from memory.
*/
_ensureAudioStopped: function() {
if (this._audioRequest) {
this._audioRequest.abort();
delete this._audioRequest;
}
if (this.audio) {
this.audio.pause();
this.audio.removeAttribute("src");

Просмотреть файл

@ -99,10 +99,10 @@ loop.store = loop.store || {};
maxRoomCreationSize: 2,
/**
* The number of hours for which the room will exist.
* The number of hours for which the room will exist - default 8 weeks
* @type {Number}
*/
defaultExpiresIn: 5,
defaultExpiresIn: 24 * 7 * 8,
/**
* Internal store state representation.

Просмотреть файл

@ -540,6 +540,8 @@ loop.shared.views = (function(_, OT, l10n) {
* Feedback view.
*/
var FeedbackView = React.createClass({displayName: 'FeedbackView',
mixins: [sharedMixins.AudioMixin],
propTypes: {
// A loop.FeedbackAPIClient instance
feedbackApiClient: React.PropTypes.object.isRequired,
@ -556,6 +558,10 @@ loop.shared.views = (function(_, OT, l10n) {
return {step: "start"};
},
componentDidMount: function() {
this.play("terminated");
},
reset: function() {
this.setState(this.getInitialState());
},

Просмотреть файл

@ -540,6 +540,8 @@ loop.shared.views = (function(_, OT, l10n) {
* Feedback view.
*/
var FeedbackView = React.createClass({
mixins: [sharedMixins.AudioMixin],
propTypes: {
// A loop.FeedbackAPIClient instance
feedbackApiClient: React.PropTypes.object.isRequired,
@ -556,6 +558,10 @@ loop.shared.views = (function(_, OT, l10n) {
return {step: "start"};
},
componentDidMount: function() {
this.play("terminated");
},
reset: function() {
this.setState(this.getInitialState());
},

Двоичный файл не отображается.

Просмотреть файл

@ -81,7 +81,11 @@ browser.jar:
content/browser/loop/shared/libs/backbone-1.1.2.js (content/shared/libs/backbone-1.1.2.js)
# Shared sounds
content/browser/loop/shared/sounds/Firefox-Long.ogg (content/shared/sounds/Firefox-Long.ogg)
content/browser/loop/shared/sounds/ringtone.ogg (content/shared/sounds/ringtone.ogg)
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/failure.ogg (content/shared/sounds/failure.ogg)
# Partner SDK assets
content/browser/loop/libs/sdk.js (content/shared/libs/sdk.js)

Просмотреть файл

@ -46,6 +46,7 @@
<script type="text/javascript" src="shared/js/validate.js"></script>
<script type="text/javascript" src="shared/js/dispatcher.js"></script>
<script type="text/javascript" src="shared/js/websocket.js"></script>
<script type="text/javascript" src="shared/js/activeRoomStore.js"></script>
<script type="text/javascript" src="js/standaloneAppStore.js"></script>
<script type="text/javascript" src="js/standaloneClient.js"></script>
<script type="text/javascript" src="js/standaloneRoomViews.js"></script>

Просмотреть файл

@ -11,8 +11,42 @@ loop.standaloneRoomViews = (function() {
"use strict";
var StandaloneRoomView = React.createClass({displayName: 'StandaloneRoomView',
mixins: [Backbone.Events],
propTypes: {
activeRoomStore:
React.PropTypes.instanceOf(loop.store.ActiveRoomStore).isRequired
},
getInitialState: function() {
return this.props.activeRoomStore.getStoreState();
},
componentWillMount: function() {
this.listenTo(this.props.activeRoomStore, "change",
this._onActiveRoomStateChanged);
},
/**
* Handles a "change" event on the roomStore, and updates this.state
* to match the store.
*
* @private
*/
_onActiveRoomStateChanged: function() {
this.setState(this.props.activeRoomStore.getStoreState());
},
componentWillUnmount: function() {
this.stopListening(this.props.activeRoomStore);
},
render: function() {
return (React.DOM.div(null, "Room"));
return (
React.DOM.div(null,
React.DOM.div(null, this.state.roomState)
)
);
}
});

Просмотреть файл

@ -11,8 +11,42 @@ loop.standaloneRoomViews = (function() {
"use strict";
var StandaloneRoomView = React.createClass({
mixins: [Backbone.Events],
propTypes: {
activeRoomStore:
React.PropTypes.instanceOf(loop.store.ActiveRoomStore).isRequired
},
getInitialState: function() {
return this.props.activeRoomStore.getStoreState();
},
componentWillMount: function() {
this.listenTo(this.props.activeRoomStore, "change",
this._onActiveRoomStateChanged);
},
/**
* Handles a "change" event on the roomStore, and updates this.state
* to match the store.
*
* @private
*/
_onActiveRoomStateChanged: function() {
this.setState(this.props.activeRoomStore.getStoreState());
},
componentWillUnmount: function() {
this.stopListening(this.props.activeRoomStore);
},
render: function() {
return (<div>Room</div>);
return (
<div>
<div>{this.state.roomState}</div>
</div>
);
}
});

Просмотреть файл

@ -286,7 +286,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
},
_handleRingingProgress: function() {
this.play("ringing", {loop: true});
this.play("ringtone", {loop: true});
this.setState({callState: "ringing"});
},
@ -534,8 +534,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
* Ended conversation view.
*/
var EndedConversationView = React.createClass({displayName: 'EndedConversationView',
mixins: [sharedMixins.AudioMixin],
propTypes: {
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
.isRequired,
@ -544,10 +542,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
onAfterFeedbackReceived: React.PropTypes.func.isRequired
},
componentDidMount: function() {
this.play("terminated");
},
render: function() {
document.title = mozL10n.get("standalone_title_with_status",
{clientShortname: mozL10n.get("clientShortname2"),
@ -897,7 +891,9 @@ loop.webapp = (function($, _, OT, mozL10n) {
// XXX New types for flux style
standaloneAppStore: React.PropTypes.instanceOf(
loop.store.StandaloneAppStore).isRequired
loop.store.StandaloneAppStore).isRequired,
activeRoomStore: React.PropTypes.instanceOf(
loop.store.ActiveRoomStore).isRequired
},
getInitialState: function() {
@ -939,7 +935,11 @@ loop.webapp = (function($, _, OT, mozL10n) {
);
}
case "room": {
return loop.standaloneRoomViews.StandaloneRoomView(null);
return (
loop.standaloneRoomViews.StandaloneRoomView({
activeRoomStore: this.props.activeRoomStore}
)
);
}
case "home": {
return HomeView(null);
@ -989,6 +989,12 @@ loop.webapp = (function($, _, OT, mozL10n) {
helper: helper,
sdk: OT
});
var activeRoomStore = new loop.store.ActiveRoomStore({
dispatcher: dispatcher,
// XXX Bug 1074702 will introduce a mozLoop compatible object for
// the standalone rooms.
mozLoop: {}
});
React.renderComponent(WebappRootView({
client: client,
@ -997,7 +1003,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
notifications: notifications,
sdk: OT,
feedbackApiClient: feedbackApiClient,
standaloneAppStore: standaloneAppStore}
standaloneAppStore: standaloneAppStore,
activeRoomStore: activeRoomStore}
), document.querySelector("#main"));
// Set the 'lang' and 'dir' attributes to <html> when the page is translated

Просмотреть файл

@ -286,7 +286,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
},
_handleRingingProgress: function() {
this.play("ringing", {loop: true});
this.play("ringtone", {loop: true});
this.setState({callState: "ringing"});
},
@ -534,8 +534,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
* Ended conversation view.
*/
var EndedConversationView = React.createClass({
mixins: [sharedMixins.AudioMixin],
propTypes: {
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
.isRequired,
@ -544,10 +542,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
onAfterFeedbackReceived: React.PropTypes.func.isRequired
},
componentDidMount: function() {
this.play("terminated");
},
render: function() {
document.title = mozL10n.get("standalone_title_with_status",
{clientShortname: mozL10n.get("clientShortname2"),
@ -897,7 +891,9 @@ loop.webapp = (function($, _, OT, mozL10n) {
// XXX New types for flux style
standaloneAppStore: React.PropTypes.instanceOf(
loop.store.StandaloneAppStore).isRequired
loop.store.StandaloneAppStore).isRequired,
activeRoomStore: React.PropTypes.instanceOf(
loop.store.ActiveRoomStore).isRequired
},
getInitialState: function() {
@ -939,7 +935,11 @@ loop.webapp = (function($, _, OT, mozL10n) {
);
}
case "room": {
return <loop.standaloneRoomViews.StandaloneRoomView/>;
return (
<loop.standaloneRoomViews.StandaloneRoomView
activeRoomStore={this.props.activeRoomStore}
/>
);
}
case "home": {
return <HomeView />;
@ -989,6 +989,12 @@ loop.webapp = (function($, _, OT, mozL10n) {
helper: helper,
sdk: OT
});
var activeRoomStore = new loop.store.ActiveRoomStore({
dispatcher: dispatcher,
// XXX Bug 1074702 will introduce a mozLoop compatible object for
// the standalone rooms.
mozLoop: {}
});
React.renderComponent(<WebappRootView
client={client}
@ -998,6 +1004,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
sdk={OT}
feedbackApiClient={feedbackApiClient}
standaloneAppStore={standaloneAppStore}
activeRoomStore={activeRoomStore}
/>, document.querySelector("#main"));
// Set the 'lang' and 'dir' attributes to <html> when the page is translated

Просмотреть файл

@ -7,7 +7,7 @@ describe("loop.conversationViews", function () {
"use strict";
var sharedUtils = loop.shared.utils;
var sandbox, oldTitle, view, dispatcher, contact;
var sandbox, oldTitle, view, dispatcher, contact, fakeAudioXHR;
var CALL_STATES = loop.store.CALL_STATES;
@ -30,11 +30,39 @@ describe("loop.conversationViews", function () {
pref: true
}]
};
fakeAudioXHR = {
open: sinon.spy(),
send: function() {},
abort: function() {},
getResponseHeader: function(header) {
if (header === "Content-Type")
return "audio/ogg";
},
responseType: null,
response: new ArrayBuffer(10),
onload: null
};
navigator.mozLoop = {
getLoopCharPref: sinon.stub().returns("http://fakeurl"),
composeEmail: sinon.spy(),
get appVersionInfo() {
return {
version: "42",
channel: "test",
platform: "test"
};
},
getAudioBlob: sinon.spy(function(name, callback) {
callback(null, new Blob([new ArrayBuffer(10)], {type: "audio/ogg"}));
})
};
});
afterEach(function() {
document.title = oldTitle;
view = undefined;
delete navigator.mozLoop;
sandbox.restore();
});
@ -202,7 +230,7 @@ describe("loop.conversationViews", function () {
});
describe("CallFailedView", function() {
var store;
var store, fakeAudio;
function mountTestComponent(props) {
return TestUtils.renderIntoDocument(
@ -219,6 +247,12 @@ describe("loop.conversationViews", function () {
client: {},
sdkDriver: {}
});
fakeAudio = {
play: sinon.spy(),
pause: sinon.spy(),
removeAttribute: sinon.spy()
};
sandbox.stub(window, "Audio").returns(fakeAudio);
});
it("should dispatch a retryCall action when the retry button is pressed",
@ -306,6 +340,16 @@ describe("loop.conversationViews", function () {
expect(view.getDOMNode().querySelector(".btn-email").disabled).eql(false);
});
it("should play a failure sound, once", function() {
view = mountTestComponent();
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);
});
});
describe("OngoingConversationView", function() {
@ -412,11 +456,6 @@ describe("loop.conversationViews", function () {
}
beforeEach(function() {
navigator.mozLoop = {
getLoopCharPref: function() { return "fake"; },
appVersionInfo: sinon.spy()
};
store = new loop.store.ConversationStore({}, {
dispatcher: dispatcher,
client: {},
@ -424,10 +463,6 @@ describe("loop.conversationViews", function () {
});
});
afterEach(function() {
delete navigator.mozLoop;
});
it("should render the CallFailedView when the call state is 'terminated'",
function() {
store.set({callState: CALL_STATES.TERMINATED});

Просмотреть файл

@ -57,7 +57,10 @@ describe("loop.conversation", function() {
channel: "test",
platform: "test"
};
}
},
getAudioBlob: sinon.spy(function(name, callback) {
callback(null, new Blob([new ArrayBuffer(10)], {type: 'audio/ogg'}));
})
};
// XXX These stubs should be hoisted in a common file
@ -690,8 +693,8 @@ describe("loop.conversation", function() {
function() {
conversation.trigger("session:network-disconnected");
TestUtils.findRenderedComponentWithType(icView,
loop.conversation.GenericFailureView);
TestUtils.findRenderedComponentWithType(icView,
loop.conversation.GenericFailureView);
});
it("should update the conversation window toolbar title",
@ -747,7 +750,7 @@ describe("loop.conversation", function() {
});
describe("IncomingCallView", function() {
var view, model;
var view, model, fakeAudio;
beforeEach(function() {
var Model = Backbone.Model.extend({
@ -757,6 +760,13 @@ describe("loop.conversation", function() {
sandbox.spy(model, "trigger");
sandbox.stub(model, "set");
fakeAudio = {
play: sinon.spy(),
pause: sinon.spy(),
removeAttribute: sinon.spy()
};
sandbox.stub(window, "Audio").returns(fakeAudio);
view = TestUtils.renderIntoDocument(loop.conversation.IncomingCallView({
model: model,
video: true
@ -896,4 +906,32 @@ describe("loop.conversation", function() {
});
});
});
describe("GenericFailureView", function() {
var view, fakeAudio;
beforeEach(function() {
fakeAudio = {
play: sinon.spy(),
pause: sinon.spy(),
removeAttribute: sinon.spy()
};
sandbox.stub(window, "Audio").returns(fakeAudio);
view = TestUtils.renderIntoDocument(
loop.conversation.GenericFailureView({
cancelCall: function() {}
})
);
});
it("should play a failure sound, once", function() {
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);
});
});
});

Просмотреть файл

@ -3,17 +3,32 @@ var expect = chai.expect;
describe("loop.roomViews", function () {
"use strict";
var sandbox, dispatcher, roomStore, activeRoomStore, fakeWindow, fakeMozLoop,
fakeRoomId;
var ROOM_STATES = loop.store.ROOM_STATES;
var sandbox, dispatcher, roomStore, activeRoomStore, fakeWindow;
beforeEach(function() {
sandbox = sinon.sandbox.create();
dispatcher = new loop.Dispatcher();
fakeWindow = { document: {} };
fakeWindow = {
document: {},
navigator: {
mozLoop: {
getAudioBlob: sinon.stub()
}
}
};
loop.shared.mixins.setRootObject(fakeWindow);
// XXX These stubs should be hoisted in a common file
// Bug 1040968
sandbox.stub(document.mozL10n, "get", function(x) {
return x;
});
activeRoomStore = new loop.store.ActiveRoomStore({
dispatcher: dispatcher,
mozLoop: {}
@ -26,11 +41,13 @@ describe("loop.roomViews", function () {
});
afterEach(function() {
sinon.sandbox.restore();
sandbox.restore();
loop.shared.mixins.setRootObject(window);
});
describe("DesktopRoomView", function() {
var view;
function mountTestComponent() {
return TestUtils.renderIntoDocument(
new loop.roomViews.DesktopRoomView({
@ -43,10 +60,22 @@ describe("loop.roomViews", function () {
it("should set document.title to store.serverData.roomName", function() {
mountTestComponent();
activeRoomStore.setStoreState({serverData: {roomName: "fakeName"}});
activeRoomStore.setStoreState({roomName: "fakeName"});
expect(fakeWindow.document.title).to.equal("fakeName");
});
it("should render the GenericFailureView if the roomState is `FAILED`", function() {
activeRoomStore.setStoreState({roomState: ROOM_STATES.FAILED});
view = mountTestComponent();
TestUtils.findRenderedComponentWithType(view,
loop.conversation.GenericFailureView);
});
// XXX Implement this when we do the rooms views in bug 1074686 and others.
it("should display the main view");
});
});
});

Просмотреть файл

@ -6,6 +6,8 @@
* effects - rather than just testing MozLoopAPI alone.
*/
Components.utils.import("resource://gre/modules/Promise.jsm", this);
add_task(loadLoopPanel);
add_task(function* test_mozLoop_appVersionInfo() {

Просмотреть файл

@ -6,6 +6,8 @@
* effects - rather than just testing MozLoopAPI alone.
*/
Components.utils.import("resource://gre/modules/Promise.jsm", this);
add_task(loadLoopPanel);
add_task(function* test_mozLoop_doNotDisturb() {

Просмотреть файл

@ -6,6 +6,8 @@
* effects - rather than just testing MozLoopAPI alone.
*/
Components.utils.import("resource://gre/modules/Promise.jsm", this);
add_task(loadLoopPanel);
add_task(function* test_mozLoop_pluralStrings() {

Просмотреть файл

@ -6,6 +6,8 @@
* effects - rather than just testing MozLoopAPI alone.
*/
Components.utils.import("resource://gre/modules/Promise.jsm", this);
add_task(loadLoopPanel);
add_task(function* test_mozLoop_charPref() {

Просмотреть файл

@ -4,6 +4,7 @@
/*
* This file contains tests for the mozLoop telemetry API.
*/
Components.utils.import("resource://gre/modules/Promise.jsm", this);
add_task(loadLoopPanel);

Просмотреть файл

@ -7,6 +7,8 @@
"use strict";
Components.utils.import("resource://gre/modules/Promise.jsm", this);
registerCleanupFunction(function*() {
MozLoopService.doNotDisturb = false;
MozLoopServiceInternal.fxAOAuthProfile = null;

Просмотреть файл

@ -6,11 +6,27 @@ var sharedActions = loop.shared.actions;
describe("loop.store.ActiveRoomStore", function () {
"use strict";
var sandbox, dispatcher;
var ROOM_STATES = loop.store.ROOM_STATES;
var sandbox, dispatcher, store, fakeMozLoop;
beforeEach(function() {
sandbox = sinon.sandbox.create();
sandbox.useFakeTimers();
dispatcher = new loop.Dispatcher();
sandbox.stub(dispatcher, "dispatch");
fakeMozLoop = {
rooms: {
get: sandbox.stub(),
join: sandbox.stub(),
refreshMembership: sandbox.stub(),
leave: sandbox.stub()
}
};
store = new loop.store.ActiveRoomStore(
{mozLoop: fakeMozLoop, dispatcher: dispatcher});
});
afterEach(function() {
@ -31,73 +47,96 @@ describe("loop.store.ActiveRoomStore", function () {
});
});
describe("#roomFailure", function() {
var fakeError;
beforeEach(function() {
sandbox.stub(console, "error");
fakeError = new Error("fake");
store.setStoreState({
roomState: ROOM_STATES.READY
});
});
it("should log the error", function() {
store.roomFailure({error: fakeError});
sinon.assert.calledOnce(console.error);
sinon.assert.calledWith(console.error,
sinon.match(ROOM_STATES.READY), fakeError);
});
it("should set the state to `FAILED`", function() {
store.roomFailure({error: fakeError});
expect(store._storeState.roomState).eql(ROOM_STATES.FAILED);
});
});
describe("#setupWindowData", function() {
var store, fakeMozLoop, fakeToken, fakeRoomName;
var fakeToken, fakeRoomData;
beforeEach(function() {
fakeToken = "337-ff-54";
fakeRoomName = "Monkeys";
fakeMozLoop = {
rooms: { get: sandbox.stub() }
fakeRoomData = {
roomName: "Monkeys",
roomOwner: "Alfred",
roomUrl: "http://invalid"
};
store = new loop.store.ActiveRoomStore(
{mozLoop: fakeMozLoop, dispatcher: dispatcher});
fakeMozLoop.rooms.get.
withArgs(fakeToken).
callsArgOnWith(1, // index of callback argument
store, // |this| to call it on
null, // args to call the callback with...
{roomName: fakeRoomName}
fakeRoomData
);
});
it("should trigger a change event", function(done) {
store.on("change", function() {
done();
});
dispatcher.dispatch(new sharedActions.SetupWindowData({
windowId: "42",
type: "room",
roomToken: fakeToken
}));
});
it("should set roomToken on the store from the action data",
function(done) {
store.once("change", function () {
expect(store.getStoreState()).
to.have.property('roomToken', fakeToken);
done();
});
dispatcher.dispatch(new sharedActions.SetupWindowData({
it("should set the state to `GATHER`",
function() {
store.setupWindowData(new sharedActions.SetupWindowData({
windowId: "42",
type: "room",
roomToken: fakeToken
}));
expect(store.getStoreState()).
to.have.property('roomState', ROOM_STATES.GATHER);
});
it("should set serverData.roomName from the getRoomData callback",
function(done) {
store.once("change", function () {
expect(store.getStoreState()).to.have.deep.property(
'serverData.roomName', fakeRoomName);
done();
});
dispatcher.dispatch(new sharedActions.SetupWindowData({
it("should dispatch an UpdateRoomInfo action if the get is successful",
function() {
store.setupWindowData(new sharedActions.SetupWindowData({
windowId: "42",
type: "room",
roomToken: fakeToken
}));
sinon.assert.calledTwice(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.UpdateRoomInfo(_.extend({
roomToken: fakeToken
}, fakeRoomData)));
});
it("should set error on the store when getRoomData calls back an error",
function(done) {
it("should dispatch a JoinRoom action if the get is successful",
function() {
store.setupWindowData(new sharedActions.SetupWindowData({
windowId: "42",
type: "room",
roomToken: fakeToken
}));
sinon.assert.calledTwice(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.JoinRoom());
});
it("should dispatch a RoomFailure action if the get fails",
function() {
var fakeError = new Error("fake error");
fakeMozLoop.rooms.get.
@ -106,17 +145,201 @@ describe("loop.store.ActiveRoomStore", function () {
store, // |this| to call it on
fakeError); // args to call the callback with...
store.once("change", function() {
expect(this.getStoreState()).to.have.property('error', fakeError);
done();
});
dispatcher.dispatch(new sharedActions.SetupWindowData({
store.setupWindowData(new sharedActions.SetupWindowData({
windowId: "42",
type: "room",
roomToken: fakeToken
}));
});
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.RoomFailure({
error: fakeError
}));
});
});
describe("#updateRoomInfo", function() {
var fakeRoomInfo;
beforeEach(function() {
fakeRoomInfo = {
roomName: "Its a room",
roomOwner: "Me",
roomToken: "fakeToken",
roomUrl: "http://invalid"
};
});
it("should set the state to READY", function() {
store.updateRoomInfo(fakeRoomInfo);
expect(store._storeState.roomState).eql(ROOM_STATES.READY);
});
it("should save the room information", function() {
store.updateRoomInfo(fakeRoomInfo);
var state = store.getStoreState();
expect(state.roomName).eql(fakeRoomInfo.roomName);
expect(state.roomOwner).eql(fakeRoomInfo.roomOwner);
expect(state.roomToken).eql(fakeRoomInfo.roomToken);
expect(state.roomUrl).eql(fakeRoomInfo.roomUrl);
});
});
describe("#joinRoom", function() {
beforeEach(function() {
store.setStoreState({roomToken: "tokenFake"});
});
it("should call rooms.join on mozLoop", function() {
store.joinRoom();
sinon.assert.calledOnce(fakeMozLoop.rooms.join);
sinon.assert.calledWith(fakeMozLoop.rooms.join, "tokenFake");
});
it("should dispatch `JoinedRoom` on success", function() {
var responseData = {
apiKey: "keyFake",
sessionToken: "14327659860",
sessionId: "1357924680",
expires: 8
};
fakeMozLoop.rooms.join.callsArgWith(1, null, responseData);
store.joinRoom();
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWith(dispatcher.dispatch,
new sharedActions.JoinedRoom(responseData));
});
it("should dispatch `RoomFailure` on error", function() {
var fakeError = new Error("fake");
fakeMozLoop.rooms.join.callsArgWith(1, fakeError);
store.joinRoom();
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWith(dispatcher.dispatch,
new sharedActions.RoomFailure({error: fakeError}));
});
});
describe("#joinedRoom", function() {
var fakeJoinedData;
beforeEach(function() {
fakeJoinedData = {
apiKey: "9876543210",
sessionToken: "12563478",
sessionId: "15263748",
expires: 20
};
store.setStoreState({
roomToken: "fakeToken"
});
});
it("should set the state to `JOINED`", function() {
store.joinedRoom(fakeJoinedData);
expect(store._storeState.roomState).eql(ROOM_STATES.JOINED);
});
it("should store the session and api values", function() {
store.joinedRoom(fakeJoinedData);
var state = store.getStoreState();
expect(state.apiKey).eql(fakeJoinedData.apiKey);
expect(state.sessionToken).eql(fakeJoinedData.sessionToken);
expect(state.sessionId).eql(fakeJoinedData.sessionId);
});
it("should call mozLoop.rooms.refreshMembership before the expiresTime",
function() {
store.joinedRoom(fakeJoinedData);
sandbox.clock.tick(fakeJoinedData.expires * 1000);
sinon.assert.calledOnce(fakeMozLoop.rooms.refreshMembership);
sinon.assert.calledWith(fakeMozLoop.rooms.refreshMembership,
"fakeToken", "12563478");
});
it("should call mozLoop.rooms.refreshMembership before the next expiresTime",
function() {
fakeMozLoop.rooms.refreshMembership.callsArgWith(2,
null, {expires: 40});
store.joinedRoom(fakeJoinedData);
// Clock tick for the first expiry time (which
// sets up the refreshMembership).
sandbox.clock.tick(fakeJoinedData.expires * 1000);
// Clock tick for expiry time in the refresh membership response.
sandbox.clock.tick(40000);
sinon.assert.calledTwice(fakeMozLoop.rooms.refreshMembership);
sinon.assert.calledWith(fakeMozLoop.rooms.refreshMembership,
"fakeToken", "12563478");
});
it("should dispatch `RoomFailure` if the refreshMembership call failed",
function() {
var fakeError = new Error("fake");
fakeMozLoop.rooms.refreshMembership.callsArgWith(2, fakeError);
store.joinedRoom(fakeJoinedData);
// Clock tick for the first expiry time (which
// sets up the refreshMembership).
sandbox.clock.tick(fakeJoinedData.expires * 1000);
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWith(dispatcher.dispatch,
new sharedActions.RoomFailure({
error: fakeError
}));
});
});
describe("#windowUnload", function() {
beforeEach(function() {
store.setStoreState({
roomState: ROOM_STATES.JOINED,
roomToken: "fakeToken",
sessionToken: "1627384950"
});
});
it("should clear any existing timeout", function() {
sandbox.stub(window, "clearTimeout");
store._timeout = {};
store.windowUnload();
sinon.assert.calledOnce(clearTimeout);
});
it("should call mozLoop.rooms.leave", function() {
store.windowUnload();
sinon.assert.calledOnce(fakeMozLoop.rooms.leave);
sinon.assert.calledWithExactly(fakeMozLoop.rooms.leave,
"fakeToken", "1627384950");
});
it("should set the state to ready", function() {
store.windowUnload();
expect(store._storeState.roomState).eql(ROOM_STATES.READY);
});
});
});

Просмотреть файл

@ -15,7 +15,7 @@ describe("loop.shared.views", function() {
var sharedModels = loop.shared.models,
sharedViews = loop.shared.views,
getReactElementByClass = TestUtils.findRenderedDOMComponentWithClass,
sandbox;
sandbox, fakeAudioXHR;
beforeEach(function() {
sandbox = sinon.sandbox.create();
@ -23,6 +23,18 @@ describe("loop.shared.views", function() {
sandbox.stub(l10n, "get", function(x) {
return "translated:" + x;
});
fakeAudioXHR = {
open: sinon.spy(),
send: function() {},
abort: function() {},
getResponseHeader: function(header) {
if (header === "Content-Type")
return "audio/ogg";
},
responseType: null,
response: new ArrayBuffer(10),
onload: null
};
});
afterEach(function() {
@ -368,16 +380,55 @@ describe("loop.shared.views", function() {
it("should play a connected sound, once, on session:connected",
function() {
var url = "shared/sounds/connected.ogg";
sandbox.stub(window, "XMLHttpRequest").returns(fakeAudioXHR);
model.trigger("session:connected");
sinon.assert.calledOnce(window.Audio);
sinon.assert.calledWithExactly(
window.Audio, "shared/sounds/connected.ogg");
fakeAudioXHR.onload();
sinon.assert.called(fakeAudioXHR.open);
sinon.assert.calledWithExactly(fakeAudioXHR.open, "GET", url, true);
sinon.assert.calledOnce(fakeAudio.play);
expect(fakeAudio.loop).to.not.equal(true);
});
});
describe("for desktop", function() {
var origMozLoop;
beforeEach(function() {
origMozLoop = navigator.mozLoop;
navigator.mozLoop = {
getAudioBlob: sinon.spy(function(name, callback) {
var data = new ArrayBuffer(10);
callback(null, new Blob([data], {type: "audio/ogg"}));
})
};
});
afterEach(function() {
navigator.mozLoop = origMozLoop;
});
it("should play a connected sound, once, on session:connected",
function() {
var url = "chrome://browser/content/loop/shared/sounds/connected.ogg";
model.trigger("session:connected");
sinon.assert.calledOnce(navigator.mozLoop.getAudioBlob);
sinon.assert.calledWithExactly(navigator.mozLoop.getAudioBlob,
"connected", sinon.match.func);
sinon.assert.calledOnce(fakeAudio.play);
expect(fakeAudio.loop).to.not.equal(true);
});
});
describe("for both (standalone and desktop)", function() {
beforeEach(function() {
sandbox.stub(window, "XMLHttpRequest").returns(fakeAudioXHR);
});
it("should start streaming on session:connected", function() {
model.trigger("session:connected");
@ -458,6 +509,7 @@ describe("loop.shared.views", function() {
beforeEach(function() {
fakeFeedbackApiClient = {send: sandbox.stub()};
sandbox.stub(window, "XMLHttpRequest").returns(fakeAudioXHR);
comp = TestUtils.renderIntoDocument(sharedViews.FeedbackView({
feedbackApiClient: fakeFeedbackApiClient
}));

Просмотреть файл

@ -40,6 +40,7 @@
<script src="../../content/shared/js/actions.js"></script>
<script src="../../content/shared/js/validate.js"></script>
<script src="../../content/shared/js/dispatcher.js"></script>
<script src="../../content/shared/js/activeRoomStore.js"></script>
<script src="../../standalone/content/js/multiplexGum.js"></script>
<script src="../../standalone/content/js/standaloneAppStore.js"></script>
<script src="../../standalone/content/js/standaloneClient.js"></script>

Просмотреть файл

@ -18,7 +18,8 @@ describe("loop.webapp", function() {
sandbox,
notifications,
feedbackApiClient,
stubGetPermsAndCacheMedia;
stubGetPermsAndCacheMedia,
fakeAudioXHR;
beforeEach(function() {
sandbox = sinon.sandbox.create();
@ -29,6 +30,19 @@ describe("loop.webapp", function() {
stubGetPermsAndCacheMedia = sandbox.stub(
loop.standaloneMedia._MultiplexGum.prototype, "getPermsAndCacheMedia");
fakeAudioXHR = {
open: sinon.spy(),
send: function() {},
abort: function() {},
getResponseHeader: function(header) {
if (header === "Content-Type")
return "audio/ogg";
},
responseType: null,
response: new ArrayBuffer(10),
onload: null
};
});
afterEach(function() {
@ -219,6 +233,7 @@ describe("loop.webapp", function() {
describe("state: terminate, reason: reject", function() {
beforeEach(function() {
sandbox.stub(notifications, "errorL10n");
sandbox.stub(window, "XMLHttpRequest").returns(fakeAudioXHR);
});
it("should display the FailedConversationView", function() {
@ -307,6 +322,7 @@ describe("loop.webapp", function() {
promiseConnectStub =
sandbox.stub(loop.CallConnectionWebSocket.prototype, "promiseConnect");
promiseConnectStub.returns(new Promise(function(resolve, reject) {}));
sandbox.stub(window, "XMLHttpRequest").returns(fakeAudioXHR);
});
describe("call:outgoing", function() {
@ -526,6 +542,8 @@ describe("loop.webapp", function() {
var view, conversation, client, fakeAudio;
beforeEach(function() {
sandbox.stub(window, "XMLHttpRequest").returns(fakeAudioXHR);
fakeAudio = {
play: sinon.spy(),
pause: sinon.spy(),
@ -541,6 +559,7 @@ describe("loop.webapp", function() {
});
conversation.set("loopToken", "fakeToken");
sandbox.stub(client, "requestCallUrlInfo");
view = React.addons.TestUtils.renderIntoDocument(
loop.webapp.FailedConversationView({
conversation: conversation,
@ -550,9 +569,12 @@ describe("loop.webapp", function() {
});
it("should play a failure sound, once", function() {
sinon.assert.calledOnce(window.Audio);
sinon.assert.calledWithExactly(window.Audio,
"shared/sounds/failure.ogg");
fakeAudioXHR.onload();
sinon.assert.called(fakeAudioXHR.open);
sinon.assert.calledWithExactly(
fakeAudioXHR.open, "GET", "shared/sounds/failure.ogg", true);
sinon.assert.calledOnce(fakeAudio.play);
expect(fakeAudio.loop).to.equal(false);
});
});
@ -560,7 +582,7 @@ describe("loop.webapp", function() {
describe("WebappRootView", function() {
var helper, sdk, conversationModel, client, props, standaloneAppStore;
var dispatcher;
var dispatcher, activeRoomStore;
function mountTestComponent() {
return TestUtils.renderIntoDocument(
@ -571,7 +593,8 @@ describe("loop.webapp", function() {
sdk: sdk,
conversation: conversationModel,
feedbackApiClient: feedbackApiClient,
standaloneAppStore: standaloneAppStore
standaloneAppStore: standaloneAppStore,
activeRoomStore: activeRoomStore
}));
}
@ -587,6 +610,10 @@ describe("loop.webapp", function() {
baseServerUrl: "fakeUrl"
});
dispatcher = new loop.Dispatcher();
activeRoomStore = new loop.store.ActiveRoomStore({
dispatcher: dispatcher,
mozLoop: {}
});
standaloneAppStore = new loop.store.StandaloneAppStore({
dispatcher: dispatcher,
sdk: sdk,
@ -678,6 +705,7 @@ describe("loop.webapp", function() {
removeAttribute: sinon.spy()
};
sandbox.stub(window, "Audio").returns(fakeAudio);
sandbox.stub(window, "XMLHttpRequest").returns(fakeAudioXHR);
view = React.addons.TestUtils.renderIntoDocument(
loop.webapp.PendingConversationView({
@ -689,8 +717,12 @@ describe("loop.webapp", function() {
describe("#componentDidMount", function() {
it("should play a looped connecting sound", function() {
sinon.assert.calledOnce(window.Audio);
sinon.assert.calledWithExactly(window.Audio, "shared/sounds/connecting.ogg");
fakeAudioXHR.onload();
sinon.assert.called(fakeAudioXHR.open);
sinon.assert.calledWithExactly(
fakeAudioXHR.open, "GET", "shared/sounds/connecting.ogg", true);
sinon.assert.calledOnce(fakeAudio.play);
expect(fakeAudio.loop).to.equal(true);
});
@ -727,8 +759,13 @@ describe("loop.webapp", function() {
it("should play a looped ringing sound", function() {
websocket.trigger("progress:alerting");
fakeAudioXHR.onload();
sinon.assert.calledWithExactly(window.Audio, "shared/sounds/ringing.ogg");
sinon.assert.called(fakeAudioXHR.open);
sinon.assert.calledWithExactly(
fakeAudioXHR.open, "GET", "shared/sounds/ringtone.ogg", true);
sinon.assert.called(fakeAudio.play);
expect(fakeAudio.loop).to.equal(true);
});
});
@ -997,6 +1034,7 @@ describe("loop.webapp", function() {
conversation = new sharedModels.ConversationModel({}, {
sdk: {}
});
sandbox.stub(window, "XMLHttpRequest").returns(fakeAudioXHR);
view = React.addons.TestUtils.renderIntoDocument(
loop.webapp.EndedConversationView({
conversation: conversation,
@ -1018,8 +1056,13 @@ describe("loop.webapp", function() {
describe("#componentDidMount", function() {
it("should play a terminating sound, once", function() {
sinon.assert.calledOnce(window.Audio);
sinon.assert.calledWithExactly(window.Audio, "shared/sounds/terminated.ogg");
fakeAudioXHR.onload();
sinon.assert.called(fakeAudioXHR.open);
sinon.assert.calledWithExactly(
fakeAudioXHR.open, "GET", "shared/sounds/terminated.ogg", true);
sinon.assert.calledOnce(fakeAudio.play);
expect(fakeAudio.loop).to.not.equal(true);
});

Просмотреть файл

@ -212,7 +212,16 @@ add_task(function* setup_server() {
// Add a request handler for each room in the list.
[...kRooms.values()].forEach(function(room) {
loopServer.registerPathHandler("/rooms/" + encodeURIComponent(room.roomToken), (req, res) => {
returnRoomDetails(res, room.roomName);
if (req.method == "POST") {
let body = CommonUtils.readBytesFromInputStream(req.bodyInputStream);
let data = JSON.parse(body);
res.setStatusLine(null, 200, "OK");
res.write(JSON.stringify(data));
res.processAsync();
res.finish();
} else {
returnRoomDetails(res, room.roomName);
}
});
});
@ -321,6 +330,39 @@ add_task(function* test_roomUpdates() {
yield waitForCondition(() => Object.getOwnPropertyNames(gExpectedJoins).length === 0);
});
// Test if joining a room works as expected.
add_task(function* test_joinRoom() {
// We need these set up for getting the email address.
Services.prefs.setCharPref("loop.fxa_oauth.profile", JSON.stringify({
email: "fake@invalid.com"
}));
Services.prefs.setCharPref("loop.fxa_oauth.tokendata", JSON.stringify({
token_type: "bearer"
}));
let roomToken = "_nxD4V4FflQ";
let joinedData = yield LoopRooms.promise("join", roomToken);
Assert.equal(joinedData.action, "join");
Assert.equal(joinedData.displayName, "fake@invalid.com");
});
// Test if refreshing a room works as expected.
add_task(function* test_refreshMembership() {
let roomToken = "_nxD4V4FflQ";
let refreshedData = yield LoopRooms.promise("refreshMembership", roomToken,
"fakeSessionToken");
Assert.equal(refreshedData.action, "refresh");
Assert.equal(refreshedData.sessionToken, "fakeSessionToken");
});
// Test if leaving a room works as expected.
add_task(function* test_leaveRoom() {
let roomToken = "_nxD4V4FflQ";
let leaveData = yield LoopRooms.promise("leave", roomToken, "fakeLeaveSessionToken");
Assert.equal(leaveData.action, "leave");
Assert.equal(leaveData.sessionToken, "fakeLeaveSessionToken");
});
// 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");
@ -339,6 +381,9 @@ function run_test() {
// Revert original Chat.open implementation
Chat.open = openChatOrig;
Services.prefs.clearUserPref("loop.fxa_oauth.profile");
Services.prefs.clearUserPref("loop.fxa_oauth.tokendata");
LoopRooms.off("add", onRoomAdded);
LoopRooms.off("update", onRoomUpdated);
LoopRooms.off("joined", onRoomJoined);

Просмотреть файл

@ -2411,7 +2411,7 @@ let DefaultBrowserCheck = {
let E10SUINotification = {
// Increase this number each time we want to roll out an
// e10s testing period to Nightly users.
CURRENT_NOTICE_COUNT: 1,
CURRENT_NOTICE_COUNT: 2,
CURRENT_PROMPT_PREF: "browser.displayedE10SPrompt.1",
PREVIOUS_PROMPT_PREF: "browser.displayedE10SPrompt",
@ -2514,7 +2514,7 @@ let E10SUINotification = {
Services.prefs.setIntPref("browser.displayedE10SNotice", this.CURRENT_NOTICE_COUNT);
let nb = win.document.getElementById("high-priority-global-notificationbox");
let message = "Thanks for helping to test multiprocess Firefox (e10s). Some functions might not work yet."
let message = "You're now helping to test Process Separation (e10s)! Please report problems you find.";
let buttons = [
{
label: "Learn More",

Просмотреть файл

@ -92,9 +92,32 @@ var gMainPane = {
setEventListener("e10sAutoStart", "command",
gMainPane.enableE10SChange);
let e10sCheckbox = document.getElementById("e10sAutoStart");
let e10sPref = document.getElementById("browser.tabs.remote.autostart");
let e10sTempPref = document.getElementById("e10sTempPref");
e10sCheckbox.checked = e10sPref.value || e10sTempPref.value;
e10sCheckbox.checked = Services.appinfo.browserTabsRemoteAutostart;
// If e10s is blocked for some reason unrelated to prefs, we want to disable
// the checkbox.
if (!Services.appinfo.browserTabsRemoteAutostart) {
let e10sBlockedReason = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
let appinfo = Services.appinfo.QueryInterface(Ci.nsIObserver);
appinfo.observe(e10sBlockedReason, "getE10SBlocked", "")
if (e10sBlockedReason.data) {
if (e10sBlockedReason.data == "Safe mode") {
// If the only reason we're disabled is because of safe mode, then
// we want to allow the user to un-toggle the pref.
// We're relying on the nsAppRunner code only specifying "Safe mode"
// as the reason if the pref is otherwise enabled, and there are no
// other reasons to block e10s.
// Update the checkbox to reflect the pref state.
e10sCheckbox.checked = true;
} else {
e10sCheckbox.disabled = true;
e10sCheckbox.label += " (disabled: " + e10sBlockedReason.data + ")";
}
}
}
// If E10S is blocked because of safe mode, we want the checkbox to be
// enabled
#endif
#ifdef MOZ_DEV_EDITION

Просмотреть файл

@ -5,6 +5,7 @@
// This test makes sure that the URL bar is focused when entering the private window.
"use strict";
Components.utils.import("resource://gre/modules/Promise.jsm", this);
function checkUrlbarFocus(win) {
let urlbar = win.gURLBar;

Просмотреть файл

@ -198,7 +198,7 @@ ContentRestoreInternal.prototype = {
}
let referrer = loadArguments.referrer ?
Utils.makeURI(loadArguments.referrer) : null;
webNavigation.loadURI(loadArguments.uri, loadArguments.loadFlags,
webNavigation.loadURI(loadArguments.uri, loadArguments.flags,
referrer, null, null);
} else if (tabData.userTypedValue && tabData.userTypedClear) {
// If the user typed a URL into the URL bar and hit enter right before

Просмотреть файл

@ -1,6 +1,8 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
Components.utils.import("resource://gre/modules/Promise.jsm", this);
function test() {
waitForExplicitFinish();
newWindowWithTabView(onTabViewWindowLoaded);

Просмотреть файл

@ -1,6 +1,8 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
Components.utils.import("resource://gre/modules/Promise.jsm", this);
function test() {
let cw;
let win;

Просмотреть файл

@ -1,6 +1,8 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
Components.utils.import("resource://gre/modules/Promise.jsm", this);
function test() {
let cw;
let win;

Просмотреть файл

@ -15,6 +15,8 @@ const TEST_URL = 'data:text/html,<script>window.onbeforeunload=' +
let contentWindow;
let activeGroup;
Components.utils.import("resource://gre/modules/Promise.jsm", this);
function test() {
waitForExplicitFinish();

Просмотреть файл

@ -1,6 +1,8 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
Components.utils.import("resource://gre/modules/Promise.jsm", this);
function test() {
waitForExplicitFinish();

Просмотреть файл

@ -3,6 +3,8 @@
let cw;
Components.utils.import("resource://gre/modules/Promise.jsm", this);
function test() {
requestLongerTimeout(2);
waitForExplicitFinish();

Просмотреть файл

@ -1,6 +1,8 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
Components.utils.import("resource://gre/modules/Promise.jsm", this);
function test() {
waitForExplicitFinish();

Просмотреть файл

@ -2,6 +2,7 @@
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Components.utils.import("resource://gre/modules/Promise.jsm", this);
const STATE = {
windows: [{

Просмотреть файл

@ -14,28 +14,29 @@ function test() {
info("highlighting the body node");
yield runCommand("highlight body", options);
is(getHighlighters().length, 1, "The highlighter element exists for body");
is(getHighlighterNumber(), 1, "The highlighter element exists for body");
info("highlighting the div node");
yield runCommand("highlight div", options);
is(getHighlighters().length, 1, "The highlighter element exists for div");
is(getHighlighterNumber(), 1, "The highlighter element exists for div");
info("highlighting the body node again, asking to keep the div");
yield runCommand("highlight body --keep", options);
is(getHighlighters().length, 2, "2 highlighter elements have been created");
is(getHighlighterNumber(), 2, "2 highlighter elements have been created");
info("unhighlighting all nodes");
yield runCommand("unhighlight", options);
is(getHighlighters().length, 0, "All highlighters have been removed");
is(getHighlighterNumber(), 0, "All highlighters have been removed");
yield helpers.closeToolbar(options);
yield helpers.closeTab(options);
}).then(finish, helpers.handleError);
}
function getHighlighters() {
return gBrowser.selectedBrowser.parentNode
.querySelectorAll(".highlighter-container");
function getHighlighterNumber() {
// Note that this only works as long as gcli tests aren't run with e10s on.
// To make this e10s ready, execute this in a content frame script instead.
return require("gcli/commands/highlight").highlighters.length;
}
function* runCommand(cmd, options) {

Просмотреть файл

@ -215,6 +215,8 @@ let DebuggerController = {
yield this._startTracingTab(traceActor);
}
}
this._hideUnsupportedFeatures();
}),
/**
@ -231,6 +233,16 @@ let DebuggerController = {
this.activeThread = null;
},
_hideUnsupportedFeatures: function() {
if (this.client.mainRoot.traits.noPrettyPrinting) {
DebuggerView.Sources.hidePrettyPrinting();
}
if (this.client.mainRoot.traits.noBlackBoxing) {
DebuggerView.Sources.hideBlackBoxing();
}
},
/**
* Called for each location change in the debugged tab.
*

Просмотреть файл

@ -546,6 +546,24 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
}
},
hidePrettyPrinting: function() {
this._prettyPrintButton.style.display = 'none';
if (this._blackBoxButton.style.display === 'none') {
let sep = document.querySelector('#sources-toolbar .devtools-separator');
sep.style.display = 'none';
}
},
hideBlackBoxing: function() {
this._blackBoxButton.style.display = 'none';
if (this._prettyPrintButton.style.display === 'none') {
let sep = document.querySelector('#sources-toolbar .devtools-separator');
sep.style.display = 'none';
}
},
/**
* Marks a breakpoint as selected in this sources container.
*

Просмотреть файл

@ -134,19 +134,19 @@ skip-if = e10s
[browser_dbg_break-on-dom-01.js]
skip-if = e10s
[browser_dbg_break-on-dom-02.js]
skip-if = e10s
skip-if = e10s && debug
[browser_dbg_break-on-dom-03.js]
skip-if = e10s
skip-if = e10s && debug
[browser_dbg_break-on-dom-04.js]
skip-if = e10s
skip-if = e10s && debug
[browser_dbg_break-on-dom-05.js]
skip-if = e10s
skip-if = e10s && debug
[browser_dbg_break-on-dom-06.js]
skip-if = e10s
skip-if = e10s && debug
[browser_dbg_break-on-dom-07.js]
skip-if = e10s
skip-if = e10s && debug
[browser_dbg_break-on-dom-08.js]
skip-if = e10s
skip-if = e10s && debug
[browser_dbg_break-on-dom-event-01.js]
skip-if = e10s || os == "mac" || e10s # Bug 895426
[browser_dbg_break-on-dom-event-02.js]
@ -235,6 +235,8 @@ skip-if = e10s
skip-if = e10s
[browser_dbg_globalactor.js]
skip-if = e10s
[browser_dbg_hide-toolbar-buttons.js]
skip-if = e10s
[browser_dbg_hit-counts-01.js]
skip-if = e10s
[browser_dbg_hit-counts-02.js]
@ -308,33 +310,33 @@ skip-if = e10s
[browser_dbg_paused-keybindings.js]
skip-if = e10s
[browser_dbg_pretty-print-01.js]
skip-if = e10s
skip-if = e10s && debug
[browser_dbg_pretty-print-02.js]
skip-if = e10s
skip-if = e10s && debug
[browser_dbg_pretty-print-03.js]
skip-if = e10s
skip-if = e10s && debug
[browser_dbg_pretty-print-04.js]
skip-if = e10s
skip-if = e10s && debug
[browser_dbg_pretty-print-05.js]
skip-if = e10s
skip-if = e10s && debug
[browser_dbg_pretty-print-06.js]
skip-if = e10s
skip-if = e10s && debug
[browser_dbg_pretty-print-07.js]
skip-if = e10s
skip-if = e10s && debug
[browser_dbg_pretty-print-08.js]
skip-if = e10s
skip-if = e10s && debug
[browser_dbg_pretty-print-09.js]
skip-if = e10s
skip-if = e10s && debug
[browser_dbg_pretty-print-10.js]
skip-if = e10s
skip-if = e10s && debug
[browser_dbg_pretty-print-11.js]
skip-if = e10s
skip-if = e10s && debug
[browser_dbg_pretty-print-12.js]
skip-if = e10s
skip-if = e10s && debug
[browser_dbg_pretty-print-13.js]
skip-if = e10s
skip-if = e10s && debug
[browser_dbg_pretty-print-on-paused.js]
skip-if = e10s
skip-if = e10s && debug
[browser_dbg_progress-listener-bug.js]
skip-if = e10s
[browser_dbg_reload-preferred-script-01.js]

Просмотреть файл

@ -9,7 +9,7 @@
const TAB_URL = EXAMPLE_URL + "doc_event-listeners-02.html";
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
let gDebugger = aPanel.panelWin;
let gView = gDebugger.DebuggerView;
let gEvents = gView.EventListeners;

Просмотреть файл

@ -8,7 +8,7 @@
const TAB_URL = EXAMPLE_URL + "doc_event-listeners-02.html";
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
let gDebugger = aPanel.panelWin;
let gView = gDebugger.DebuggerView;
let gEvents = gView.EventListeners;

Просмотреть файл

@ -9,7 +9,7 @@
const TAB_URL = EXAMPLE_URL + "doc_event-listeners-02.html";
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
let gDebugger = aPanel.panelWin;
let gView = gDebugger.DebuggerView;
let gController = gDebugger.DebuggerController

Просмотреть файл

@ -10,7 +10,7 @@
const TAB_URL = EXAMPLE_URL + "doc_event-listeners-02.html";
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
let gDebugger = aPanel.panelWin;
let gView = gDebugger.DebuggerView;
let gController = gDebugger.DebuggerController

Просмотреть файл

@ -9,7 +9,7 @@
const TAB_URL = EXAMPLE_URL + "doc_event-listeners-02.html";
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
let gDebugger = aPanel.panelWin;
let gView = gDebugger.DebuggerView;
let gController = gDebugger.DebuggerController

Просмотреть файл

@ -6,7 +6,7 @@
*/
function test() {
initDebugger("about:blank").then(([aTab, aDebuggee, aPanel]) => {
initDebugger("about:blank").then(([aTab,, aPanel]) => {
let gDebugger = aPanel.panelWin;
let gView = gDebugger.DebuggerView;
let gEvents = gView.EventListeners;

Просмотреть файл

@ -8,14 +8,15 @@
const TAB_URL = EXAMPLE_URL + "doc_event-listeners-02.html";
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
let gTab = aTab;
let gDebugger = aPanel.panelWin;
let gView = gDebugger.DebuggerView;
let gEvents = gView.EventListeners;
Task.spawn(function() {
yield waitForSourceShown(aPanel, ".html");
aDebuggee.addBodyClickEventListener();
yield callInTab(gTab, "addBodyClickEventListener");
let fetched = waitForDebuggerEvents(aPanel, gDebugger.EVENTS.EVENT_LISTENERS_FETCHED);
gView.toggleInstrumentsPane({ visible: true, animated: false }, 1);
@ -33,11 +34,7 @@ function test() {
yield ensureThreadClientState(aPanel, "resumed");
let paused = waitForCaretAndScopes(aPanel, 48);
// Spin the event loop before causing the debuggee to pause, to allow
// this function to yield first.
executeSoon(() => {
EventUtils.sendMouseEvent({ type: "click" }, aDebuggee.document.body, aDebuggee);
});
sendMouseClickToTab(gTab, content.document.body);
yield paused;
yield ensureThreadClientState(aPanel, "paused");

Просмотреть файл

@ -0,0 +1,32 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Bug 1093349: Test that the pretty-printing and blackboxing buttons
* are hidden if the server doesn't support them
*/
const TAB_URL = EXAMPLE_URL + "doc_auto-pretty-print-01.html";
let devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
let { RootActor } = devtools.require("devtools/server/actors/root");
function test() {
let gTab, gDebuggee, gPanel, gDebugger;
let gEditor, gSources, gBreakpoints, gBreakpointsAdded, gBreakpointsRemoving;
RootActor.prototype.traits.noBlackBoxing = true;
RootActor.prototype.traits.noPrettyPrinting = true;
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
let document = aPanel.panelWin.document;
let ppButton = document.querySelector('#pretty-print');
let bbButton = document.querySelector('#black-box');
let sep = document.querySelector('#sources-toolbar .devtools-separator');
is(ppButton.style.display, 'none', 'The pretty-print button is hidden');
is(bbButton.style.display, 'none', 'The blackboxing button is hidden');
is(sep.style.display, 'none', 'The separator is hidden');
closeDebuggerAndFinish(aPanel)
});
}

Просмотреть файл

@ -7,13 +7,12 @@
const TAB_URL = EXAMPLE_URL + "doc_pretty-print.html";
let gTab, gDebuggee, gPanel, gDebugger;
let gTab, gPanel, gDebugger;
let gEditor, gSources;
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gEditor = gDebugger.DebuggerView.editor;
@ -76,7 +75,6 @@ function testSourceIsStillPretty() {
registerCleanupFunction(function() {
gTab = null;
gDebuggee = null;
gPanel = null;
gDebugger = null;
gEditor = null;

Просмотреть файл

@ -8,13 +8,12 @@
const TAB_URL = EXAMPLE_URL + "doc_pretty-print.html";
let gTab, gDebuggee, gPanel, gDebugger;
let gTab, gPanel, gDebugger;
let gEditor, gContextMenu;
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gEditor = gDebugger.DebuggerView.editor;
@ -49,7 +48,6 @@ function testSourceIsPretty() {
registerCleanupFunction(function() {
gTab = null;
gDebuggee = null;
gPanel = null;
gDebugger = null;
gEditor = null;

Просмотреть файл

@ -7,12 +7,11 @@
const TAB_URL = EXAMPLE_URL + "doc_pretty-print.html";
let gTab, gDebuggee, gPanel, gDebugger;
let gTab, gPanel, gDebugger;
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
@ -35,8 +34,7 @@ function test() {
function runCodeAndPause() {
const deferred = promise.defer();
once(gDebugger.gThreadClient, "paused").then(deferred.resolve);
// Have to executeSoon so that we don't pause before this function returns.
executeSoon(gDebuggee.foo);
callInTab(gTab, "foo");
return deferred.promise;
}
@ -46,7 +44,6 @@ function clickPrettyPrintButton() {
registerCleanupFunction(function() {
gTab = null;
gDebuggee = null;
gPanel = null;
gDebugger = null;
});

Просмотреть файл

@ -7,13 +7,12 @@
const TAB_URL = EXAMPLE_URL + "doc_pretty-print.html";
let gTab, gDebuggee, gPanel, gDebugger;
let gTab, gPanel, gDebugger;
let gSearchBox;
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
@ -65,7 +64,6 @@ function testPrettyPrintedSearch() {
registerCleanupFunction(function() {
gTab = null;
gDebuggee = null;
gPanel = null;
gDebugger = null;
gSearchBox = null;

Просмотреть файл

@ -7,13 +7,12 @@
const TAB_URL = EXAMPLE_URL + "doc_included-script.html";
let gTab, gDebuggee, gPanel, gDebugger;
let gTab, gPanel, gDebugger;
let gEditor, gSources, gControllerSources;
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gEditor = gDebugger.DebuggerView.editor;
@ -68,7 +67,6 @@ function prepareDebugger(aPanel) {
registerCleanupFunction(function() {
gTab = null;
gDebuggee = null;
gPanel = null;
gDebugger = null;
gEditor = null;

Просмотреть файл

@ -8,13 +8,12 @@
const TAB_URL = EXAMPLE_URL + "doc_included-script.html";
const JS_URL = EXAMPLE_URL + "code_location-changes.js";
let gTab, gDebuggee, gPanel, gDebugger, gClient;
let gTab, gPanel, gDebugger, gClient;
let gEditor, gSources, gControllerSources, gPrettyPrinted;
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gClient = gDebugger.gClient;
@ -83,7 +82,6 @@ function clickPrettyPrintButton() {
registerCleanupFunction(function() {
gTab = null;
gDebuggee = null;
gPanel = null;
gDebugger = null;
gClient = null;

Просмотреть файл

@ -5,14 +5,13 @@
// Test basic pretty printing functionality. Would be an xpcshell test, except
// for bug 921252.
let gTab, gDebuggee, gPanel, gClient, gThreadClient, gSource;
let gTab, gPanel, gClient, gThreadClient, gSource;
const TAB_URL = EXAMPLE_URL + "doc_pretty-print-2.html";
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gDebuggee = aDebuggee;
gPanel = aPanel;
gClient = gPanel.panelWin.gClient;
gThreadClient = gPanel.panelWin.DebuggerController.activeThread;
@ -52,5 +51,5 @@ function testUgly({ error, source }) {
}
registerCleanupFunction(function() {
gTab = gDebuggee = gPanel = gClient = gThreadClient = gSource = null;
gTab = gPanel = gClient = gThreadClient = gSource = null;
});

Просмотреть файл

@ -4,19 +4,17 @@
// Test stepping through pretty printed sources.
let gTab, gDebuggee, gPanel, gClient, gThreadClient, gSource;
let gTab, gPanel, gClient, gThreadClient, gSource;
const TAB_URL = EXAMPLE_URL + "doc_pretty-print-2.html";
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gDebuggee = aDebuggee;
gPanel = aPanel;
gClient = gPanel.panelWin.gClient;
gThreadClient = gPanel.panelWin.DebuggerController.activeThread;
gDebuggee.noop = x => x;
findSource();
});
}
@ -47,7 +45,7 @@ function prettyPrintSource(source) {
function runCode({ error }) {
ok(!error);
gClient.addOneTimeListener("paused", testDbgStatement);
gDebuggee.main3();
callInTab(gTab, "main3");
}
function testDbgStatement(event, { why, frame }) {
@ -90,5 +88,5 @@ function testHitBreakpoint() {
}
registerCleanupFunction(function() {
gTab = gDebuggee = gPanel = gClient = gThreadClient = gSource = null;
gTab = gPanel = gClient = gThreadClient = gSource = null;
});

Просмотреть файл

@ -4,19 +4,17 @@
// Test pretty printing source mapped sources.
var gDebuggee;
var gClient;
var gThreadClient;
var gSource;
let gTab, gDebuggee, gPanel, gClient, gThreadClient, gSource;
let gTab, gPanel, gClient, gThreadClient, gSource;
const TAB_URL = EXAMPLE_URL + "doc_pretty-print-2.html";
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gDebuggee = aDebuggee;
gPanel = aPanel;
gClient = gPanel.panelWin.gClient;
gThreadClient = gPanel.panelWin.DebuggerController.activeThread;
@ -50,7 +48,7 @@ function prettyPrint() {
function runCode({ error }) {
ok(!error);
gClient.addOneTimeListener("paused", testDbgStatement);
gDebuggee.a();
callInTab(gTab, "a");
}
function testDbgStatement(event, { frame, why }) {
@ -85,5 +83,5 @@ function testFrame({ frames: [frame] }) {
}
registerCleanupFunction(function() {
gTab = gDebuggee = gPanel = gClient = gThreadClient = null;
gTab = gPanel = gClient = gThreadClient = null;
});

Просмотреть файл

@ -8,13 +8,12 @@
const TAB_URL = EXAMPLE_URL + "doc_pretty-print.html";
let gTab, gDebuggee, gPanel, gDebugger;
let gTab, gPanel, gDebugger;
let gEditor, gSources;
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gEditor = gDebugger.DebuggerView.editor;
@ -55,7 +54,6 @@ function testSourceIsStillUgly() {
registerCleanupFunction(function() {
gTab = null;
gDebuggee = null;
gPanel = null;
gDebugger = null;
gEditor = null;

Просмотреть файл

@ -7,13 +7,12 @@
const TAB_URL = EXAMPLE_URL + "doc_pretty-print.html";
let gTab, gDebuggee, gPanel, gDebugger;
let gTab, gPanel, gDebugger;
let gEditor, gSources;
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gEditor = gDebugger.DebuggerView.editor;
@ -52,7 +51,6 @@ function testSourceIsPretty() {
registerCleanupFunction(function() {
gTab = null;
gDebuggee = null;
gPanel = null;
gDebugger = null;
gEditor = null;

Просмотреть файл

@ -8,13 +8,12 @@
const TAB_URL = EXAMPLE_URL + "doc_blackboxing.html";
let gTab, gDebuggee, gPanel, gDebugger;
let gTab, gPanel, gDebugger;
let gEditor, gSources;
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gEditor = gDebugger.DebuggerView.editor;
@ -46,7 +45,6 @@ function testButtonIsntChecked() {
registerCleanupFunction(function() {
gTab = null;
gDebuggee = null;
gPanel = null;
gDebugger = null;
gEditor = null;

Просмотреть файл

@ -9,13 +9,12 @@
const TAB_URL = EXAMPLE_URL + "doc_pretty-print-3.html";
let gTab, gDebuggee, gPanel, gDebugger;
let gTab, gPanel, gDebugger;
let gEditor, gSources;
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gEditor = gDebugger.DebuggerView.editor;
@ -79,7 +78,6 @@ function testSourceIsStillPretty() {
registerCleanupFunction(function() {
gTab = null;
gDebuggee = null;
gPanel = null;
gDebugger = null;
gEditor = null;

Просмотреть файл

@ -8,14 +8,13 @@
const TAB_URL = EXAMPLE_URL + "doc_pretty-print-on-paused.html";
let gTab, gDebuggee, gPanel, gDebugger, gThreadClient, gSources;
let gTab, gPanel, gDebugger, gThreadClient, gSources;
const SECOND_SOURCE_VALUE = EXAMPLE_URL + "code_ugly-2.js";
function test(){
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gThreadClient = gDebugger.gThreadClient;
@ -33,9 +32,7 @@ function test(){
yield doResume(gPanel);
const bpHit = waitForCaretAndScopes(gPanel, 6);
// Get the debuggee call off this tick so that we aren't accidentally
// blocking the yielding of bpHit which causes a deadlock.
executeSoon(() => gDebuggee.secondCall());
callInTab(gTab, "secondCall");
yield bpHit;
info("Switch to the second source.");
@ -59,7 +56,6 @@ function test(){
registerCleanupFunction(function() {
gTab = null;
gDebuggee = null;
gPanel = null;
gDebugger = null;
gThreadClient = null;

Просмотреть файл

@ -1,9 +1,25 @@
"use strict";
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
const { loadSubScript } = Cc['@mozilla.org/moz/jssubscript-loader;1'].
getService(Ci.mozIJSSubScriptLoader);
const EventUtils = {};
loadSubScript("chrome://marionette/content/EventUtils.js", EventUtils);
dump("Frame script loaded.\n");
addMessageListener("test:call", function (message) {
dump("Calling function with name " + message.data + ".\n");
XPCNativeWrapper.unwrap(content)[message.data]();
sendAsyncMessage("test:call");
});
addMessageListener("test:click", function (message) {
dump("Sending mouse click.\n");
let target = message.objects.target;
EventUtils.synthesizeMouseAtCenter(target, {},
target.ownerDocument.defaultView);
});

Просмотреть файл

@ -8,3 +8,8 @@
<script src="code_ugly-2.js"></script>
<script src="code_ugly-3.js"></script>
<script src="code_ugly-4.js"></script>
<script>
function noop(x) {
return x;
}
</script>

Просмотреть файл

@ -964,4 +964,13 @@ function callInTab(tab, name) {
info("Calling function with name " + name + " in tab.");
sendMessageToTab(tab, "test:call", name);
waitForMessageFromTab(tab, "test:call");
}
function sendMouseClickToTab(tab, target) {
info("Sending mouse click to tab.");
sendMessageToTab(tab, "test:click", undefined, {
target: target
});
}

Просмотреть файл

@ -2,6 +2,11 @@
* 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/. */
window {
-moz-appearance: none;
background-color: transparent;
}
#doorhanger-container {
width: 450px;
}
@ -10,7 +15,20 @@
padding: 20px;
background: #343c45; /* toolbars */
color: #8fa1b2; /* body text */
/*
* Sloppy preprocessing since UNIX_BUT_NOT_MAC is only defined
* in `browser/app/profile/firefox.js`, which this file cannot
* depend on. Must style font-size to target linux.
*/
%ifdef XP_UNIX
%ifndef XP_MACOSX
font-size: 13px;
%else
font-size: 15px;
%endif
%else
font-size: 15px;
%endif
line-height: 19px;
min-height: 100px;
}
@ -60,6 +78,8 @@
border-radius: 5px;
height: 30px;
width: 450px;
/* Override embossed borders on Windows/Linux */
border: none;
}
#close {

Просмотреть файл

@ -1,22 +1,24 @@
[DEFAULT]
skip-if = e10s # Bug ?????? - devtools tests disabled with e10s
skip-if = e10s # Bug 1074836 - inspector tests disabled with e10s
subsuite = devtools
support-files =
doc_frame_script.js
doc_inspector_breadcrumbs.html
doc_inspector_delete-selected-node-01.html
doc_inspector_delete-selected-node-02.html
doc_inspector_gcli-inspect-command.html
doc_inspector_highlight_after_transition.html
doc_inspector_highlighter-comments.html
doc_inspector_highlighter_csstransform.html
doc_inspector_highlighter.html
browser_inspector_infobar_01.html
browser_inspector_infobar_02.html
doc_inspector_infobar_01.html
doc_inspector_infobar_02.html
doc_inspector_menu.html
doc_inspector_remove-iframe-during-load.html
doc_inspector_search.html
doc_inspector_search-suggestions.html
doc_inspector_select-last-selected-01.html
doc_inspector_select-last-selected-02.html
browser_inspector_highlight_after_transition.html
head.js
[browser_inspector_breadcrumbs.js]
@ -29,11 +31,20 @@ support-files =
[browser_inspector_highlighter-01.js]
[browser_inspector_highlighter-02.js]
[browser_inspector_highlighter-03.js]
[browser_inspector_highlighter-04.js]
[browser_inspector_highlighter-by-type.js]
[browser_inspector_highlighter-comments.js]
[browser_inspector_highlighter-csstransform_01.js]
[browser_inspector_highlighter-csstransform_02.js]
[browser_inspector_highlighter-hover_01.js]
[browser_inspector_highlighter-hover_02.js]
[browser_inspector_highlighter-hover_03.js]
[browser_inspector_highlighter-iframes.js]
[browser_inspector_highlighter-options.js]
[browser_inspector_highlighter-selector_01.js]
[browser_inspector_highlighter-selector_02.js]
[browser_inspector_iframe-navigation.js]
[browser_inspector_infobar_01.js]
[browser_inspector_infobar_02.js]
[browser_inspector_initialization.js]
[browser_inspector_inspect-object-element.js]
[browser_inspector_invalidate.js]
@ -57,4 +68,3 @@ support-files =
[browser_inspector_sidebarstate.js]
[browser_inspector_switch-to-inspector-on-pick.js]
[browser_inspector_update-on-navigation.js]

Просмотреть файл

@ -7,70 +7,67 @@
const TEST_URI = TEST_URL_ROOT + "doc_inspector_breadcrumbs.html";
const NODES = [
{nodeId: "#i1111", result: "i1 i11 i111 i1111"},
{nodeId: "#i22", result: "i2 i22 i221"},
{nodeId: "#i2111", result: "i2 i21 i211 i2111"},
{nodeId: "#i21", result: "i2 i21 i211 i2111"},
{nodeId: "#i22211", result: "i2 i22 i222 i2221 i22211"},
{nodeId: "#i22", result: "i2 i22 i222 i2221 i22211"},
{selector: "#i1111", result: "i1 i11 i111 i1111"},
{selector: "#i22", result: "i2 i22 i221"},
{selector: "#i2111", result: "i2 i21 i211 i2111"},
{selector: "#i21", result: "i2 i21 i211 i2111"},
{selector: "#i22211", result: "i2 i22 i222 i2221 i22211"},
{selector: "#i22", result: "i2 i22 i222 i2221 i22211"},
];
let test = asyncTest(function*() {
add_task(function*() {
let { inspector } = yield openInspectorForURL(TEST_URI);
let container = inspector.panelDoc.getElementById("inspector-breadcrumbs");
for (let node of NODES) {
info("Testing node " + node.nodeId);
let documentNode = getNode(node.nodeId);
info("Testing node " + node.selector);
info("Selecting node and waiting for breadcrumbs to update");
let breadcrumbsUpdated = inspector.once("breadcrumbs-updated");
let nodeSelected = selectNode(documentNode, inspector);
yield selectNode(node.selector, inspector);
yield breadcrumbsUpdated;
yield Promise.all([breadcrumbsUpdated, nodeSelected]);
info("Performing checks for node " + node.nodeId);
info("Performing checks for node " + node.selector);
let buttonsLabelIds = node.result.split(" ");
// html > body > …
is(container.childNodes.length, buttonsLabelIds.length + 2,
"Node " + node.nodeId + ": Items count");
"Node " + node.selector + ": Items count");
for (let i = 2; i < container.childNodes.length; i++) {
let expectedId = "#" + buttonsLabelIds[i - 2];
let button = container.childNodes[i];
let labelId = button.querySelector(".breadcrumbs-widget-item-id");
is(labelId.textContent, expectedId,
"Node #" + node.nodeId + ": button " + i + " matches");
"Node #" + node.selector + ": button " + i + " matches");
}
let checkedButton = container.querySelector("button[checked]");
let labelId = checkedButton.querySelector(".breadcrumbs-widget-item-id");
let id = inspector.selection.node.id;
let id = inspector.selection.nodeFront.id;
is(labelId.textContent, "#" + id,
"Node #" + node.nodeId + ": selection matches");
"Node #" + node.selector + ": selection matches");
}
yield testPseudoElements(inspector, container);
});
function *testPseudoElements(inspector, container) {
function* testPseudoElements(inspector, container) {
info ("Checking for pseudo elements");
let pseudoParent = getNodeFront(getNode("#pseudo-container"));
let pseudoParent = yield getNodeFront("#pseudo-container", inspector);
let children = yield inspector.walker.children(pseudoParent);
is (children.nodes.length, 2, "Pseudo children returned from walker");
let beforeElement = children.nodes[0];
let breadcrumbsUpdated = inspector.once("breadcrumbs-updated");
let nodeSelected = selectNode(beforeElement, inspector);
yield Promise.all([breadcrumbsUpdated, nodeSelected]);
yield selectNode(beforeElement, inspector);
yield breadcrumbsUpdated;
is(container.childNodes[3].textContent, "::before", "::before shows up in breadcrumb");
let afterElement = children.nodes[1];
breadcrumbsUpdated = inspector.once("breadcrumbs-updated");
nodeSelected = selectNode(afterElement, inspector);
yield Promise.all([breadcrumbsUpdated, nodeSelected]);
yield selectNode(afterElement, inspector);
yield breadcrumbsUpdated;
is(container.childNodes[3].textContent, "::after", "::before shows up in breadcrumb");
}

Просмотреть файл

@ -6,7 +6,7 @@
// Test that hovering over nodes on the breadcrumb buttons in the inspector shows the highlighter over
// those nodes
let test = asyncTest(function*() {
add_task(function*() {
info("Loading the test document and opening the inspector");
yield addTab("data:text/html;charset=utf-8,<h1>foo</h1><span>bar</span>");
let {toolbox, inspector} = yield openInspector();
@ -18,15 +18,23 @@ let test = asyncTest(function*() {
let button = bcButtons.childNodes[1];
EventUtils.synthesizeMouseAtCenter(button, {type: "mousemove"}, button.ownerDocument.defaultView);
yield onNodeHighlighted;
ok(isHighlighting(), "The highlighter is shown on a markup container hover");
is(getHighlitNode(), getNode("body"), "The highlighter highlights the right node");
let isVisible = yield isHighlighting(toolbox);
ok(isVisible, "The highlighter is shown on a markup container hover");
let highlightedNode = yield getHighlitNode(toolbox);
is(highlightedNode, getNode("body"), "The highlighter highlights the right node");
onNodeHighlighted = toolbox.once("node-highlight");
button = bcButtons.childNodes[2];
EventUtils.synthesizeMouseAtCenter(button, {type: "mousemove"}, button.ownerDocument.defaultView);
yield onNodeHighlighted;
ok(isHighlighting(), "The highlighter is shown on a markup container hover");
is(getHighlitNode(), getNode("span"), "The highlighter highlights the right node");
isVisible = yield isHighlighting(toolbox);
ok(isVisible, "The highlighter is shown on a markup container hover");
highlightedNode = yield getHighlitNode(toolbox);
is(highlightedNode, getNode("span"), "The highlighter highlights the right node");
gBrowser.removeCurrentTab();
});

Просмотреть файл

@ -7,22 +7,18 @@
const TEST_URL = TEST_URL_ROOT + "doc_inspector_delete-selected-node-01.html";
let test = asyncTest(function* () {
let { inspector } = yield openInspectorForURL(TEST_URL);
let iframe = getNode("iframe");
let span = getNode("span", { document: iframe.contentDocument });
add_task(function* () {
let {inspector} = yield openInspectorForURL(TEST_URL);
let span = yield getNodeFrontInFrame("span", "iframe", inspector);
yield selectNode(span, inspector);
info("Removing selected <span> element.");
let parentNode = span.parentNode;
span.remove();
let lh = new LayoutHelpers(window.content);
ok(!lh.isNodeConnected(span), "Node considered as disconnected.");
let parentNode = span.parentNode();
yield inspector.walker.removeNode(span);
// Wait for the inspector to process the mutation
yield inspector.once("inspector-updated");
is(inspector.selection.node, parentNode,
is(inspector.selection.nodeFront, parentNode,
"Parent node of selected <span> got selected.");
});

Просмотреть файл

@ -12,7 +12,7 @@
const TEST_PAGE = TEST_URL_ROOT +
"doc_inspector_delete-selected-node-02.html";
let test = asyncTest(function* () {
add_task(function* () {
let { inspector } = yield openInspectorForURL(TEST_PAGE);
yield testManuallyDeleteSelectedNode();
@ -23,11 +23,10 @@ let test = asyncTest(function* () {
info("Selecting a node, deleting it via context menu and checking that " +
"its parent node is selected and breadcrumbs are updated.");
let div = getNode("#deleteManually");
yield selectNode(div, inspector);
yield selectNode("#deleteManually", inspector);
info("Getting the node container in the markup view.");
let container = getContainerForRawNode(inspector.markup, div);
let container = yield getContainerForSelector("#deleteManually", inspector);
info("Simulating right-click on the markup view container.");
EventUtils.synthesizeMouse(container.tagLine, 2, 2,
@ -43,26 +42,24 @@ let test = asyncTest(function* () {
yield inspector.once("inspector-updated");
info("Inspector updated, performing checks.");
let parent = getNode("#deleteChildren");
assertNodeSelectedAndPanelsUpdated(parent, "ul#deleteChildren");
yield assertNodeSelectedAndPanelsUpdated("#deleteChildren", "ul#deleteChildren");
}
function* testAutomaticallyDeleteSelectedNode() {
info("Selecting a node, deleting it via javascript and checking that " +
"its parent node is selected and breadcrumbs are updated.");
let div = getNode("#deleteAutomatically");
let div = yield getNodeFront("#deleteAutomatically", inspector);
yield selectNode(div, inspector);
info("Deleting selected node via javascript.");
div.remove();
yield inspector.walker.removeNode(div);
info("Waiting for inspector to update.");
yield inspector.once("inspector-updated");
info("Inspector updated, performing checks.");
let parent = getNode("#deleteChildren");
assertNodeSelectedAndPanelsUpdated(parent, "ul#deleteChildren");
yield assertNodeSelectedAndPanelsUpdated("#deleteChildren", "ul#deleteChildren");
}
function* testDeleteSelectedNodeContainerFrame() {
@ -71,23 +68,23 @@ let test = asyncTest(function* () {
"breadcrumbs are updated.");
info("Selecting an element inside iframe.");
let iframe = getNode("#deleteIframe");
let div = iframe.contentDocument.getElementById("deleteInIframe");
let iframe = yield getNodeFront("#deleteIframe", inspector);
let div = yield getNodeFrontInFrame("#deleteInIframe", iframe, inspector);
yield selectNode(div, inspector);
info("Deleting selected node via javascript.");
iframe.remove();
yield inspector.walker.removeNode(iframe);
info("Waiting for inspector to update.");
yield inspector.once("inspector-updated");
info("Inspector updated, performing checks.");
assertNodeSelectedAndPanelsUpdated(getNode("body"), "body");
yield assertNodeSelectedAndPanelsUpdated("body", "body");
}
function assertNodeSelectedAndPanelsUpdated(node, crumbLabel) {
is(inspector.selection.nodeFront, getNodeFront(node),
"The right node is selected");
function* assertNodeSelectedAndPanelsUpdated(selector, crumbLabel) {
let nodeFront = yield getNodeFront(selector, inspector);
is(inspector.selection.nodeFront, nodeFront, "The right node is selected");
let breadcrumbs = inspector.panelDoc.getElementById("inspector-breadcrumbs");
is(breadcrumbs.querySelector("button[checked=true]").textContent, crumbLabel,

Просмотреть файл

@ -8,19 +8,18 @@
const TEST_URL = TEST_URL_ROOT + "doc_inspector_delete-selected-node-01.html";
let test = asyncTest(function* () {
add_task(function* () {
let { inspector } = yield openInspectorForURL(TEST_URL);
let iframe = getNode("iframe");
let node = getNode("span", { document: iframe.contentDocument });
let iframe = yield getNodeFront("iframe", inspector);
let node = yield getNodeFrontInFrame("span", iframe, inspector);
yield selectNode(node, inspector);
info("Removing iframe.");
iframe.remove();
yield inspector.walker.removeNode(iframe);
let lh = new LayoutHelpers(window.content);
ok(!lh.isNodeConnected(node), "Node considered as disconnected.");
ok(!inspector.selection.isConnected(), "Selection considered as disconnected.");
let body = yield getNodeFront("body", inspector);
is(inspector.selection.nodeFront, body, "Selection is now the body node");
yield inspector.once("inspector-updated");
});

Просмотреть файл

@ -8,7 +8,7 @@
const URL_1 = "data:text/plain;charset=UTF-8,abcde";
const URL_2 = "data:text/plain;charset=UTF-8,12345";
let test = asyncTest(function* () {
add_task(function* () {
let { toolbox } = yield openInspectorForURL(URL_1);
info("Navigating to different URL.");

Просмотреть файл

@ -7,7 +7,7 @@
const TEST_URI = TEST_URL_ROOT + "doc_inspector_gcli-inspect-command.html";
let test = asyncTest(function* () {
add_task(function* () {
return helpers.addTabWithToolbar(TEST_URI, function(options) {
return helpers.audit(options, [
{

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше