Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2015-09-11 16:39:15 +02:00
commit 6fc4b6ff96
62 changed files with 24100 additions and 7476 deletions

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e935894ef5f27e2f04b9e929a45a958e6288a223">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="6280500a6cb8d1b178cdd163450e36d22846fbed"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="758c75ee087ea3722213ea2c185cca1d952c8a29"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e935894ef5f27e2f04b9e929a45a958e6288a223">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="6280500a6cb8d1b178cdd163450e36d22846fbed"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="758c75ee087ea3722213ea2c185cca1d952c8a29"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="6280500a6cb8d1b178cdd163450e36d22846fbed"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="758c75ee087ea3722213ea2c185cca1d952c8a29"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="27eb2f04e149fc2c9976d881b1b5984bbe7ee089"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="6280500a6cb8d1b178cdd163450e36d22846fbed"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="758c75ee087ea3722213ea2c185cca1d952c8a29"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="c6cace53426b5be7e56c0fd202118009689bc707"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e935894ef5f27e2f04b9e929a45a958e6288a223">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="6280500a6cb8d1b178cdd163450e36d22846fbed"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="758c75ee087ea3722213ea2c185cca1d952c8a29"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="6280500a6cb8d1b178cdd163450e36d22846fbed"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="758c75ee087ea3722213ea2c185cca1d952c8a29"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="6280500a6cb8d1b178cdd163450e36d22846fbed"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="758c75ee087ea3722213ea2c185cca1d952c8a29"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="27eb2f04e149fc2c9976d881b1b5984bbe7ee089"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e935894ef5f27e2f04b9e929a45a958e6288a223">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="6280500a6cb8d1b178cdd163450e36d22846fbed"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="758c75ee087ea3722213ea2c185cca1d952c8a29"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "6280500a6cb8d1b178cdd163450e36d22846fbed",
"git_revision": "758c75ee087ea3722213ea2c185cca1d952c8a29",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "dc781340a4ceea99ea5f3e1be005befeff2be31b",
"revision": "df25432ca94953b1b9072fe01b705ebbf34478d1",
"repo_path": "integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="6280500a6cb8d1b178cdd163450e36d22846fbed"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="758c75ee087ea3722213ea2c185cca1d952c8a29"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="c6cace53426b5be7e56c0fd202118009689bc707"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="6280500a6cb8d1b178cdd163450e36d22846fbed"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="758c75ee087ea3722213ea2c185cca1d952c8a29"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -275,13 +275,7 @@ pref("browser.urlbar.doubleClickSelectsAll", false);
pref("browser.urlbar.autoFill", true);
pref("browser.urlbar.autoFill.typed", true);
#ifdef NIGHTLY_BUILD
// Use the new unifiedComplete component
pref("browser.urlbar.unifiedcomplete", true);
#else
// Don't use the new unifiedComplete component
pref("browser.urlbar.unifiedcomplete", false);
#endif
// 0: Match anywhere (e.g., middle of words)
// 1: Match on word boundaries and then try matching anywhere

View File

@ -127,12 +127,15 @@ let wrapper = {
url += (url.includes("?") ? "&" : "?") + urlParamStr;
}
this.url = url;
iframe.src = url;
// Set the iframe's location with loadURI/LOAD_FLAGS_BYPASS_HISTORY to
// avoid having a new history entry being added.
let webNav = iframe.frameLoader.docShell.QueryInterface(Ci.nsIWebNavigation);
webNav.loadURI(url, Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY, null, null, null);
},
retry: function () {
let webNav = this.iframe.frameLoader.docShell.QueryInterface(Ci.nsIWebNavigation);
webNav.loadURI(this.url, null, null, null, null);
webNav.loadURI(this.url, Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY, null, null, null);
},
iframeListener: {

View File

@ -125,6 +125,21 @@ let gSyncUI = {
firstSync == "notReady";
},
_needsVerification() {
// For callers who care about the distinction between "needs setup" and
// "setup but needs verification"
// See _needsSetup for the subtleties here.
if (Weave.Status._authManager._signedInUser === undefined) {
// a legacy sync user - no "verified" concept there.
return false;
}
if (!Weave.Status._authManager._signedInUser) {
// no user configured at all, so not in a "need verification" state.
return false;
}
return !Weave.Status._authManager._signedInUser.verified;
},
// Note that we don't show login errors in a notification bar here, but do
// still need to track a login-failed state so the "Tools" menu updates
// with the correct state.
@ -154,21 +169,7 @@ let gSyncUI = {
document.getElementById("sync-syncnow-state").hidden = false;
}
if (!gBrowser)
return;
let syncButton = document.getElementById("sync-button");
let statusButton = document.getElementById("PanelUI-fxa-icon");
if (needsSetup) {
if (syncButton) {
syncButton.removeAttribute("tooltiptext");
}
if (statusButton) {
statusButton.removeAttribute("tooltiptext");
}
}
this._updateLastSyncTime();
this._updateSyncButtonsTooltip();
},
// Functions called by observers
@ -311,39 +312,60 @@ let gSyncUI = {
gFxAccounts.openSignInAgainPage(entryPoint);
},
// Helpers
_updateLastSyncTime: function SUI__updateLastSyncTime() {
/* Update the tooltip for the Sync Toolbar button and the Sync spinner in the
FxA hamburger area.
If Sync is configured, the tooltip is when the last sync occurred,
otherwise the tooltip reflects the fact that Sync needs to be
(re-)configured.
*/
_updateSyncButtonsTooltip: function() {
if (!gBrowser)
return;
let syncButton = document.getElementById("sync-button");
let statusButton = document.getElementById("PanelUI-fxa-icon");
let lastSync;
let email;
try {
lastSync = new Date(Services.prefs.getCharPref("services.sync.lastSync"));
}
catch (e) { };
if (!lastSync || this._needsSetup()) {
if (syncButton) {
syncButton.removeAttribute("tooltiptext");
}
if (statusButton) {
statusButton.removeAttribute("tooltiptext");
}
return;
}
email = Services.prefs.getCharPref("services.sync.username");
} catch (ex) {}
// Show the day-of-week and time (HH:MM) of last sync
let lastSyncDateString = lastSync.toLocaleFormat("%a %H:%M");
let lastSyncLabel =
this._stringBundle.formatStringFromName("lastSync2.label", [lastSyncDateString], 1);
if (syncButton) {
syncButton.setAttribute("tooltiptext", lastSyncLabel);
// This is a little messy as the Sync buttons are 1/2 Sync related and
// 1/2 FxA related - so for some strings we use Sync strings, but for
// others we reach into gFxAccounts for strings.
let tooltiptext;
if (this._needsVerification()) {
// "needs verification"
tooltiptext = gFxAccounts.strings.formatStringFromName("verifyDescription", [email], 1);
} else if (this._needsSetup()) {
// "needs setup".
tooltiptext = this._stringBundle.GetStringFromName("signInToSync.description");
} else if (this._loginFailed()) {
// "need to reconnect/re-enter your password"
tooltiptext = gFxAccounts.strings.formatStringFromName("reconnectDescription", [email], 1);
} else {
// Sync appears configured - format the "last synced at" time.
try {
let lastSync = new Date(Services.prefs.getCharPref("services.sync.lastSync"));
// Show the day-of-week and time (HH:MM) of last sync
let lastSyncDateString = lastSync.toLocaleFormat("%a %H:%M");
tooltiptext = this._stringBundle.formatStringFromName("lastSync2.label", [lastSyncDateString], 1);
}
catch (e) {
// pref doesn't exist (which will be the case until we've seen the
// first successful sync) or is invalid (which should be impossible!)
// Just leave tooltiptext as the empty string in these cases, which
// will cause the tooltip to be removed below.
}
}
if (statusButton) {
statusButton.setAttribute("tooltiptext", lastSyncLabel);
for (let button of [syncButton, statusButton]) {
if (button) {
if (tooltiptext) {
button.setAttribute("tooltiptext", tooltiptext);
} else {
button.removeAttribute("tooltiptext");
}
}
}
},

View File

@ -6933,8 +6933,8 @@ var gIdentityHandler = {
// Chrome URIs however get special treatment. Some chrome URIs are
// whitelisted to provide a positive security signal to the user.
let whitelist = /^about:(accounts|addons|app-manager|config|crashes|customizing|downloads|healthreport|home|license|newaddon|permissions|preferences|privatebrowsing|rights|sessionrestore|support|welcomeback)/i;
let isChromeUI = uri.schemeIs("about") && whitelist.test(uri.spec);
let whitelist = /^(?:accounts|addons|app-manager|cache|config|crashes|customizing|downloads|healthreport|home|license|newaddon|permissions|preferences|privatebrowsing|rights|sessionrestore|support|welcomeback)(?:[?#]|$)/i;
let isChromeUI = uri.schemeIs("about") && whitelist.test(uri.path);
let mode = this.IDENTITY_MODE_UNKNOWN;
if (isChromeUI) {
@ -7215,11 +7215,17 @@ var gIdentityHandler = {
// Create a channel for the sole purpose of getting the resolved URI
// of the request to determine if it's loaded from the file system.
let chanOptions = {uri, loadUsingSystemPrincipal: true};
let resolvedURI = NetUtil.newChannel(chanOptions).URI;
if (resolvedURI.schemeIs("jar")) {
// Given a URI "jar:<jar-file-uri>!/<jar-entry>"
// create a new URI using <jar-file-uri>!/<jar-entry>
resolvedURI = NetUtil.newURI(resolvedURI.path);
let resolvedURI;
try {
resolvedURI = NetUtil.newChannel(chanOptions).URI;
if (resolvedURI.schemeIs("jar")) {
// Given a URI "jar:<jar-file-uri>!/<jar-entry>"
// create a new URI using <jar-file-uri>!/<jar-entry>
resolvedURI = NetUtil.newURI(resolvedURI.path);
}
} catch (ex) {
// NetUtil's methods will throw for malformed URIs and the like
return false;
}
// Check the URI again after resolving.

View File

@ -34,23 +34,60 @@ function promiseObserver(topic) {
});
}
function checkButtonTooltips(stringPrefix) {
for (let butId of ["sync-button", "PanelUI-fxa-icon"]) {
let text = document.getElementById(butId).getAttribute("tooltiptext");
let desc = `Text is "${text}", expecting it to start with "${stringPrefix}"`
Assert.ok(text.startsWith(stringPrefix), desc);
}
}
add_task(function* prepare() {
// add the Sync button to the toolbar so we can get it!
CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_NAVBAR);
registerCleanupFunction(() => {
CustomizableUI.removeWidgetFromArea("sync-button");
});
let xps = Components.classes["@mozilla.org/weave/service;1"]
.getService(Components.interfaces.nsISupports)
.wrappedJSObject;
yield xps.whenLoaded();
// Put Sync and the UI into a known state.
Weave.Status.login = Weave.LOGIN_FAILED_NO_USERNAME;
Services.obs.notifyObservers(null, "weave:ui:clear-error", null);
checkBroadcasterVisible("sync-setup-state");
checkButtonTooltips("Sign In To Sync");
// mock out the "_needsSetup()" function so we don't short-circuit.
let oldNeedsSetup = window.gSyncUI._needsSetup;
window.gSyncUI._needsSetup = () => false;
registerCleanupFunction(() => {
window.gSyncUI._needsSetup = oldNeedsSetup;
// and an observer to set the state back to what it should be now we've
// restored the stub.
Services.obs.notifyObservers(null, "weave:ui:clear-error", null);
});
// and a notification to have the state change away from "needs setup"
Services.obs.notifyObservers(null, "weave:ui:clear-error", null);
checkBroadcasterVisible("sync-syncnow-state");
});
add_task(function* testSyncNeedsVerification() {
Assert.equal(Notifications.notifications.length, 0, "start with no notifications");
// mock out the "_needsVerification()" function
let oldNeedsVerification = window.gSyncUI._needsVerification;
window.gSyncUI._needsVerification = () => true;
try {
// a notification for the state change
Services.obs.notifyObservers(null, "weave:ui:clear-error", null);
checkButtonTooltips("Verify");
} finally {
window.gSyncUI._needsVerification = oldNeedsVerification;
}
});
add_task(function* testSyncLoginError() {
Assert.equal(Notifications.notifications.length, 0, "start with no notifications");
checkBroadcasterVisible("sync-syncnow-state");
@ -63,6 +100,8 @@ add_task(function* testSyncLoginError() {
Assert.equal(Notifications.notifications.length, 0, "no notifications shown on login error");
// But the menu *should* reflect the login error.
checkBroadcasterVisible("sync-reauth-state");
// The tooltips for the buttons should also reflect it.
checkButtonTooltips("Reconnect");
// Now pretend we just had a successful login - the error notification should go away.
Weave.Status.sync = Weave.STATUS_OK;
@ -96,27 +135,36 @@ function testButtonActions(startNotification, endNotification) {
checkButtonsStatus(false);
}
add_task(function* testButtonActivities() {
// add the Sync button to the panel so we can get it!
function doTestButtonActivities() {
testButtonActions("weave:service:login:start", "weave:service:login:finish");
testButtonActions("weave:service:login:start", "weave:service:login:error");
testButtonActions("weave:service:sync:start", "weave:service:sync:finish");
testButtonActions("weave:service:sync:start", "weave:service:sync:error");
// and ensure the counters correctly handle multiple in-flight syncs
Services.obs.notifyObservers(null, "weave:service:sync:start", null);
checkButtonsStatus(true);
// sync stops.
Services.obs.notifyObservers(null, "weave:service:sync:finish", null);
// Button should not be active.
checkButtonsStatus(false);
}
add_task(function* testButtonActivitiesInNavBar() {
// check the button's functionality while the button is in the NavBar - which
// it already is.
doTestButtonActivities();
});
add_task(function* testButtonActivitiesInPanel() {
// check the button's functionality while the button is in the panel - it's
// currently in the NavBar - move it to the panel and open it.
CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_PANEL);
// check the button's functionality
yield PanelUI.show();
try {
testButtonActions("weave:service:login:start", "weave:service:login:finish");
testButtonActions("weave:service:login:start", "weave:service:login:error");
testButtonActions("weave:service:sync:start", "weave:service:sync:finish");
testButtonActions("weave:service:sync:start", "weave:service:sync:error");
// and ensure the counters correctly handle multiple in-flight syncs
Services.obs.notifyObservers(null, "weave:service:sync:start", null);
checkButtonsStatus(true);
// sync stops.
Services.obs.notifyObservers(null, "weave:service:sync:finish", null);
// Button should not be active.
checkButtonsStatus(false);
doTestButtonActivities();
} finally {
PanelUI.hide();
CustomizableUI.removeWidgetFromArea("sync-button");
}
});

View File

@ -36,7 +36,7 @@ addEventListener("DOMContentLoaded", function domContentLoaded(event) {
return;
}
removeEventListener("DOMContentLoaded", iframeLoaded, true);
sendAsyncMessage("test:iframe:load", {url: iframe.getAttribute("src")});
sendAsyncMessage("test:iframe:load", {url: iframe.contentDocument.location.href});
// And an event listener for the test responses, which we send to the test
// via a message.
iframe.contentWindow.addEventListener("FirefoxAccountsTestResponse", function (event) {
@ -79,7 +79,7 @@ addMessageListener("test:load-with-mocked-profile-path", function (message) {
}
iframe.removeEventListener("load", iframeLoaded, true);
sendAsyncMessage("test:load-with-mocked-profile-path-response",
{url: iframe.getAttribute("src")});
{url: iframe.contentDocument.location.href});
}, true);
});
let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);

View File

@ -2,6 +2,8 @@
support-files =
head.js
[browser_displayURI.js]
skip-if = (os == "linux" && (debug || asan))
[browser_popupNotification.js]
skip-if = (os == "linux" && (debug || asan)) || e10s # e10s - Bug ?????? - popup notification test probably confused re content process notifications etc
[browser_popupNotification_2.js]

View File

@ -0,0 +1,28 @@
/*
* Make sure that the origin is shown for ContentPermissionPrompt
* consumers e.g. geolocation.
*/
add_task(function* test_displayURI() {
yield BrowserTestUtils.withNewTab({
gBrowser,
url: "https://test1.example.com/",
}, function*(browser) {
let popupShownPromise = new Promise((resolve, reject) => {
onPopupEvent("popupshown", function() {
resolve(this);
});
});
yield ContentTask.spawn(browser, null, function*() {
content.navigator.geolocation.getCurrentPosition(function (pos) {
// Do nothing
});
});
let panel = yield popupShownPromise;
let notification = panel.children[0];
let body = document.getAnonymousElementByAttribute(notification,
"class",
"popup-notification-body");
ok(body.innerHTML.includes("example.com"), "Check that at least the eTLD+1 is present in the markup");
});
});

View File

@ -155,7 +155,7 @@ loop.contacts = (function(_, mozL10n) {
const ContactDropdown = React.createClass({displayName: "ContactDropdown",
propTypes: {
// If the contact is blocked or not.
blocked: React.PropTypes.bool.isRequired,
blocked: React.PropTypes.bool,
canEdit: React.PropTypes.bool,
handleAction: React.PropTypes.func.isRequired
},
@ -175,6 +175,13 @@ loop.contacts = (function(_, mozL10n) {
let menuNodeRect = menuNode.getBoundingClientRect();
let listNode = document.getElementsByClassName("contact-list")[0];
// XXX Workaround the contact-list element not being available in tests.
// Assumptions about the embedded DOM are a bad idea, and this needs
// reworking. For example, tests use a virtual DOM. Really we should
// rework this view with the DropdownMenuMixin, which would save some of this pain.
if (!listNode) {
return;
}
let listNodeRect = listNode.getBoundingClientRect();
if (menuNodeRect.top + menuNodeRect.height >=
@ -198,6 +205,22 @@ loop.contacts = (function(_, mozL10n) {
return (
React.createElement("ul", {className: cx({ "dropdown-menu": true,
"dropdown-menu-up": this.state.openDirUp })},
React.createElement("li", {className: cx({ "dropdown-menu-item": true,
"disabled": this.props.blocked,
"video-call-item": true }),
"data-action": "video-call",
onClick: this.onItemClick},
React.createElement("i", {className: "icon icon-video-call"}),
mozL10n.get("video_call_menu_button")
),
React.createElement("li", {className: cx({ "dropdown-menu-item": true,
"disabled": this.props.blocked,
"audio-call-item": true }),
"data-action": "audio-call",
onClick: this.onItemClick},
React.createElement("i", {className: "icon icon-audio-call"}),
mozL10n.get("audio_call_menu_button")
),
React.createElement("li", {className: cx({ "dropdown-menu-item": true,
"disabled": !this.props.canEdit }),
"data-action": "edit",
@ -308,7 +331,7 @@ loop.contacts = (function(_, mozL10n) {
React.createElement("div", {className: "icons"},
React.createElement("i", {className: "icon icon-contact-video-call",
onClick: this.handleAction.bind(null, "video-call")}),
React.createElement("i", {className: "icon icon-vertical-ellipsis",
React.createElement("i", {className: "icon icon-vertical-ellipsis icon-contact-menu-button",
onClick: this.showDropdownMenu})
),
this.state.showMenu

View File

@ -155,7 +155,7 @@ loop.contacts = (function(_, mozL10n) {
const ContactDropdown = React.createClass({
propTypes: {
// If the contact is blocked or not.
blocked: React.PropTypes.bool.isRequired,
blocked: React.PropTypes.bool,
canEdit: React.PropTypes.bool,
handleAction: React.PropTypes.func.isRequired
},
@ -175,6 +175,13 @@ loop.contacts = (function(_, mozL10n) {
let menuNodeRect = menuNode.getBoundingClientRect();
let listNode = document.getElementsByClassName("contact-list")[0];
// XXX Workaround the contact-list element not being available in tests.
// Assumptions about the embedded DOM are a bad idea, and this needs
// reworking. For example, tests use a virtual DOM. Really we should
// rework this view with the DropdownMenuMixin, which would save some of this pain.
if (!listNode) {
return;
}
let listNodeRect = listNode.getBoundingClientRect();
if (menuNodeRect.top + menuNodeRect.height >=
@ -198,6 +205,22 @@ loop.contacts = (function(_, mozL10n) {
return (
<ul className={cx({ "dropdown-menu": true,
"dropdown-menu-up": this.state.openDirUp })}>
<li className={cx({ "dropdown-menu-item": true,
"disabled": this.props.blocked,
"video-call-item": true })}
data-action="video-call"
onClick={this.onItemClick}>
<i className="icon icon-video-call" />
{mozL10n.get("video_call_menu_button")}
</li>
<li className={cx({ "dropdown-menu-item": true,
"disabled": this.props.blocked,
"audio-call-item": true })}
data-action="audio-call"
onClick={this.onItemClick}>
<i className="icon icon-audio-call" />
{mozL10n.get("audio_call_menu_button")}
</li>
<li className={cx({ "dropdown-menu-item": true,
"disabled": !this.props.canEdit })}
data-action="edit"
@ -308,7 +331,7 @@ loop.contacts = (function(_, mozL10n) {
<div className="icons">
<i className="icon icon-contact-video-call"
onClick={this.handleAction.bind(null, "video-call")} />
<i className="icon icon-vertical-ellipsis"
<i className="icon icon-vertical-ellipsis icon-contact-menu-button"
onClick={this.showDropdownMenu} />
</div>
{this.state.showMenu

View File

@ -8,121 +8,129 @@ describe("loop.contacts", function() {
var expect = chai.expect;
var TestUtils = React.addons.TestUtils;
var CALL_TYPES = loop.shared.utils.CALL_TYPES;
var fakeAddContactButtonText = "Fake Add Contact Button";
var fakeAddContactTitleText = "Fake Add Contact Title";
var fakeEditContactButtonText = "Fake Edit Contact";
var fakeDoneButtonText = "Fake Done";
// The fake contacts array is copied each time mozLoop.contacts.getAll() is called.
var fakeManyContacts = [{
id: 1,
_guid: 1,
name: ["Ally Avocado"],
email: [{
"pref": true,
"type": ["work"],
"value": "ally@mail.com"
}],
tel: [{
"pref": true,
"type": ["mobile"],
"value": "+31-6-12345678"
}],
category: ["google"],
published: 1406798311748,
updated: 1406798311748
}, {
id: 2,
_guid: 2,
name: ["Bob Banana"],
email: [{
"pref": true,
"type": ["work"],
"value": "bob@gmail.com"
}],
tel: [{
"pref": true,
"type": ["mobile"],
"value": "+1-214-5551234"
}],
category: ["local"],
published: 1406798311748,
updated: 1406798311748
}, {
id: 3,
_guid: 3,
name: ["Caitlin Cantaloupe"],
email: [{
"pref": true,
"type": ["work"],
"value": "caitlin.cant@hotmail.com"
}],
category: ["local"],
published: 1406798311748,
updated: 1406798311748
}, {
id: 4,
_guid: 4,
name: ["Dave Dragonfruit"],
email: [{
"pref": true,
"type": ["work"],
"value": "dd@dragons.net"
}],
category: ["google"],
published: 1406798311748,
updated: 1406798311748
}, {
id: 5,
_guid: 5,
name: ["Erin J. Bazile"],
email: [{
"pref": true,
"type": ["work"],
"value": "erinjbazile@armyspy.com"
}],
category: ["google"],
published: 1406798311748,
updated: 1406798311748
}, {
id: 6,
_guid: 6,
name: ["Kelly F. Maldanado"],
email: [{
"pref": true,
"type": ["work"],
"value": "kellyfmaldonado@jourrapide.com"
}],
category: ["google"],
published: 1406798311748,
updated: 1406798311748
}, {
id: 7,
_guid: 7,
name: ["John J. Brown"],
email: [{
"pref": true,
"type": ["work"],
"value": "johnjbrow@johndoe.com"
}],
category: ["google"],
published: 1406798311748,
updated: 1406798311748,
blocked: true
}];
var fakeFewerContacts = fakeManyContacts.slice(0, 4);
var sandbox;
var fakeWindow;
var notifications;
var listView;
var sandbox, fakeWindow, fakeMozLoop, mozL10nGetSpy, listView, notifications;
var fakeManyContacts, fakeFewerContacts;
var oldMozLoop = navigator.mozLoop;
var mozL10nGetSpy;
// The fake contacts array is copied each time mozLoop.contacts.getAll() is called.
function getFakeContacts() {
// Return a copy, so that tests that affect it, don't have impact on each other.
return [].concat([
{
id: 1,
_guid: 1,
name: ["Ally Avocado"],
email: [{
"pref": true,
"type": ["work"],
"value": "ally@mail.com"
}],
tel: [{
"pref": true,
"type": ["mobile"],
"value": "+31-6-12345678"
}],
category: ["google"],
published: 1406798311748,
updated: 1406798311748
}, {
id: 2,
_guid: 2,
name: ["Bob Banana"],
email: [{
"pref": true,
"type": ["work"],
"value": "bob@gmail.com"
}],
tel: [{
"pref": true,
"type": ["mobile"],
"value": "+1-214-5551234"
}],
category: ["local"],
published: 1406798311748,
updated: 1406798311748
}, {
id: 3,
_guid: 3,
name: ["Caitlin Cantaloupe"],
email: [{
"pref": true,
"type": ["work"],
"value": "caitlin.cant@hotmail.com"
}],
category: ["local"],
published: 1406798311748,
updated: 1406798311748
}, {
id: 4,
_guid: 4,
name: ["Dave Dragonfruit"],
email: [{
"pref": true,
"type": ["work"],
"value": "dd@dragons.net"
}],
category: ["google"],
published: 1406798311748,
updated: 1406798311748
}, {
id: 5,
_guid: 5,
name: ["Erin J. Bazile"],
email: [{
"pref": true,
"type": ["work"],
"value": "erinjbazile@armyspy.com"
}],
category: ["google"],
published: 1406798311748,
updated: 1406798311748
}, {
id: 6,
_guid: 6,
name: ["Kelly F. Maldanado"],
email: [{
"pref": true,
"type": ["work"],
"value": "kellyfmaldonado@jourrapide.com"
}],
category: ["google"],
published: 1406798311748,
updated: 1406798311748
}, {
id: 7,
_guid: 7,
name: ["John J. Brown"],
email: [{
"pref": true,
"type": ["work"],
"value": "johnjbrow@johndoe.com"
}],
category: ["google"],
published: 1406798311748,
updated: 1406798311748,
blocked: true
}
]);
}
beforeEach(function() {
sandbox = sinon.sandbox.create();
mozL10nGetSpy = sandbox.spy(document.mozL10n, "get");
navigator.mozLoop = {
fakeManyContacts = getFakeContacts();
fakeFewerContacts = fakeManyContacts.slice(0, 4);
fakeMozLoop = navigator.mozLoop = {
getStrings: function(entityName) {
var textContentValue = "fakeText";
if (entityName === "add_contact_title") {
@ -146,7 +154,7 @@ describe("loop.contacts", function() {
},
setLoopPref: sandbox.stub(),
getUserAvatar: function() {
if (navigator.mozLoop.getLoopPref("contacts.gravatars.show")) {
if (this.getLoopPref("contacts.gravatars.show")) {
return "gravatarsEnabled";
}
return "gravatarsDisabled";
@ -159,8 +167,8 @@ describe("loop.contacts", function() {
on: sandbox.stub()
},
calls: {
startDirectCall: function() {},
clearCallInProgress: function() {}
startDirectCall: sinon.stub(),
clearCallInProgress: sinon.stub()
},
generateUUID: sandbox.stub()
};
@ -172,7 +180,7 @@ describe("loop.contacts", function() {
notifications = new loop.shared.models.NotificationCollection();
document.mozL10n.initialize(navigator.mozLoop);
document.mozL10n.initialize(fakeMozLoop);
});
afterEach(function() {
@ -197,7 +205,7 @@ describe("loop.contacts", function() {
it("should show the gravatars promo box", function() {
listView = TestUtils.renderIntoDocument(
React.createElement(loop.contacts.ContactsList, {
mozLoop: navigator.mozLoop,
mozLoop: fakeMozLoop,
notifications: notifications,
switchToContactAdd: sandbox.stub(),
switchToContactEdit: sandbox.stub()
@ -213,7 +221,7 @@ describe("loop.contacts", function() {
});
it("should not show the gravatars promo box when the 'contacts.gravatars.promo' pref is set", function() {
sandbox.stub(navigator.mozLoop, "getLoopPref", function(pref) {
sandbox.stub(fakeMozLoop, "getLoopPref", function(pref) {
if (pref === "contacts.gravatars.promo") {
return false;
} else if (pref === "contacts.gravatars.show") {
@ -224,7 +232,7 @@ describe("loop.contacts", function() {
listView = TestUtils.renderIntoDocument(
React.createElement(loop.contacts.ContactsList, {
mozLoop: navigator.mozLoop,
mozLoop: fakeMozLoop,
notifications: notifications,
switchToContactAdd: sandbox.stub(),
switchToContactEdit: sandbox.stub()
@ -239,7 +247,7 @@ describe("loop.contacts", function() {
it("should hide the gravatars promo box when the 'use' button is clicked", function() {
listView = TestUtils.renderIntoDocument(
React.createElement(loop.contacts.ContactsList, {
mozLoop: navigator.mozLoop,
mozLoop: fakeMozLoop,
notifications: notifications,
switchToContactAdd: sandbox.stub(),
switchToContactEdit: sandbox.stub()
@ -248,7 +256,7 @@ describe("loop.contacts", function() {
React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
".contacts-gravatar-promo .secondary:last-child"));
sinon.assert.calledTwice(navigator.mozLoop.setLoopPref);
sinon.assert.calledTwice(fakeMozLoop.setLoopPref);
var promo = listView.getDOMNode().querySelector(".contacts-gravatar-promo");
expect(promo).to.equal(null);
@ -257,7 +265,7 @@ describe("loop.contacts", function() {
it("should should set the prefs correctly when the 'use' button is clicked", function() {
listView = TestUtils.renderIntoDocument(
React.createElement(loop.contacts.ContactsList, {
mozLoop: navigator.mozLoop,
mozLoop: fakeMozLoop,
notifications: notifications,
switchToContactAdd: sandbox.stub(),
switchToContactEdit: sandbox.stub()
@ -266,15 +274,15 @@ describe("loop.contacts", function() {
React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
".contacts-gravatar-promo .secondary:last-child"));
sinon.assert.calledTwice(navigator.mozLoop.setLoopPref);
sinon.assert.calledWithExactly(navigator.mozLoop.setLoopPref, "contacts.gravatars.promo", false);
sinon.assert.calledWithExactly(navigator.mozLoop.setLoopPref, "contacts.gravatars.show", true);
sinon.assert.calledTwice(fakeMozLoop.setLoopPref);
sinon.assert.calledWithExactly(fakeMozLoop.setLoopPref, "contacts.gravatars.promo", false);
sinon.assert.calledWithExactly(fakeMozLoop.setLoopPref, "contacts.gravatars.show", true);
});
it("should hide the gravatars promo box when the 'close' button is clicked", function() {
listView = TestUtils.renderIntoDocument(
React.createElement(loop.contacts.ContactsList, {
mozLoop: navigator.mozLoop,
mozLoop: fakeMozLoop,
notifications: notifications,
switchToContactAdd: sandbox.stub(),
switchToContactEdit: sandbox.stub()
@ -290,7 +298,7 @@ describe("loop.contacts", function() {
it("should set prefs correctly when the 'close' button is clicked", function() {
listView = TestUtils.renderIntoDocument(
React.createElement(loop.contacts.ContactsList, {
mozLoop: navigator.mozLoop,
mozLoop: fakeMozLoop,
notifications: notifications,
switchToContactAdd: sandbox.stub(),
switchToContactEdit: sandbox.stub()
@ -299,15 +307,15 @@ describe("loop.contacts", function() {
React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
".contacts-gravatar-promo .secondary:first-child"));
sinon.assert.calledOnce(navigator.mozLoop.setLoopPref);
sinon.assert.calledWithExactly(navigator.mozLoop.setLoopPref,
sinon.assert.calledOnce(fakeMozLoop.setLoopPref);
sinon.assert.calledWithExactly(fakeMozLoop.setLoopPref,
"contacts.gravatars.promo", false);
});
it("should hide the gravatars promo box when the 'close' X button is clicked", function() {
listView = TestUtils.renderIntoDocument(
React.createElement(loop.contacts.ContactsList, {
mozLoop: navigator.mozLoop,
mozLoop: fakeMozLoop,
notifications: notifications,
switchToContactAdd: sandbox.stub(),
switchToContactEdit: sandbox.stub()
@ -323,7 +331,7 @@ describe("loop.contacts", function() {
it("should set prefs correctly when the 'close' X button is clicked", function() {
listView = TestUtils.renderIntoDocument(
React.createElement(loop.contacts.ContactsList, {
mozLoop: navigator.mozLoop,
mozLoop: fakeMozLoop,
notifications: notifications,
switchToContactAdd: sandbox.stub(),
switchToContactEdit: sandbox.stub()
@ -332,8 +340,8 @@ describe("loop.contacts", function() {
React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
".contacts-gravatar-promo .button-close"));
sinon.assert.calledOnce(navigator.mozLoop.setLoopPref);
sinon.assert.calledWithExactly(navigator.mozLoop.setLoopPref,
sinon.assert.calledOnce(fakeMozLoop.setLoopPref);
sinon.assert.calledWithExactly(fakeMozLoop.setLoopPref,
"contacts.gravatars.promo", false);
});
});
@ -345,7 +353,7 @@ describe("loop.contacts", function() {
view = TestUtils.renderIntoDocument(
React.createElement(loop.contacts.ContactsControllerView, {
initialSelectedTabComponent: "contactAdd",
mozLoop: navigator.mozLoop,
mozLoop: fakeMozLoop,
notifications: notifications
}));
});
@ -364,7 +372,7 @@ describe("loop.contacts", function() {
view = TestUtils.renderIntoDocument(
React.createElement(loop.contacts.ContactsControllerView, {
initialSelectedTabComponent: "contactEdit",
mozLoop: navigator.mozLoop,
mozLoop: fakeMozLoop,
notifications: notifications
}));
});
@ -383,7 +391,7 @@ describe("loop.contacts", function() {
view = TestUtils.renderIntoDocument(
React.createElement(loop.contacts.ContactsControllerView, {
initialSelectedTabComponent: "contactList",
mozLoop: navigator.mozLoop,
mozLoop: fakeMozLoop,
notifications: notifications
}));
});
@ -403,19 +411,15 @@ describe("loop.contacts", function() {
describe("ContactsList", function () {
var node;
beforeEach(function() {
sandbox.stub(navigator.mozLoop.calls, "startDirectCall");
sandbox.stub(navigator.mozLoop.calls, "clearCallInProgress");
});
describe("#RenderNoContacts", function() {
beforeEach(function() {
sandbox.stub(navigator.mozLoop.contacts, "getAll", function(cb) {
sandbox.stub(fakeMozLoop.contacts, "getAll", function(cb) {
cb(null, []);
});
listView = TestUtils.renderIntoDocument(
React.createElement(loop.contacts.ContactsList, {
mozLoop: navigator.mozLoop,
mozLoop: fakeMozLoop,
notifications: notifications,
switchToContactAdd: sandbox.stub(),
switchToContactEdit: sandbox.stub()
@ -442,12 +446,12 @@ describe("loop.contacts", function() {
describe("#RenderWithContacts", function() {
beforeEach(function() {
sandbox.stub(navigator.mozLoop.contacts, "getAll", function(cb) {
sandbox.stub(fakeMozLoop.contacts, "getAll", function(cb) {
cb(null, [].concat(fakeFewerContacts));
});
listView = TestUtils.renderIntoDocument(
React.createElement(loop.contacts.ContactsList, {
mozLoop: navigator.mozLoop,
mozLoop: fakeMozLoop,
notifications: notifications,
switchToContactAdd: sandbox.stub(),
switchToContactEdit: sandbox.stub()
@ -471,7 +475,7 @@ describe("loop.contacts", function() {
describe("ContactsFiltering", function() {
beforeEach(function() {
navigator.mozLoop.contacts = {
fakeMozLoop.contacts = {
getAll: function(callback) {
callback(null, [].concat(fakeManyContacts));
},
@ -479,7 +483,7 @@ describe("loop.contacts", function() {
};
listView = TestUtils.renderIntoDocument(
React.createElement(loop.contacts.ContactsList, {
mozLoop: navigator.mozLoop,
mozLoop: fakeMozLoop,
notifications: notifications,
switchToContactAdd: sandbox.stub(),
switchToContactEdit: sandbox.stub()
@ -574,42 +578,12 @@ describe("loop.contacts", function() {
});
});
describe("#handleContactAction", function() {
beforeEach(function() {
sandbox.stub(navigator.mozLoop.contacts, "getAll", function(cb) {
cb(null, []);
});
listView = TestUtils.renderIntoDocument(
React.createElement(loop.contacts.ContactsList, {
mozLoop: navigator.mozLoop,
notifications: notifications,
switchToContactAdd: sandbox.stub(),
switchToContactEdit: sandbox.stub()
}));
node = listView.getDOMNode();
});
it("should call window.close when called with 'video-call' action",
function() {
listView.handleContactAction({}, "video-call");
sinon.assert.calledOnce(fakeWindow.close);
});
it("should call window.close when called with 'audio-call' action",
function() {
listView.handleContactAction({}, "audio-call");
sinon.assert.calledOnce(fakeWindow.close);
});
});
describe("#handleContactAddEdit", function() {
beforeEach(function() {
listView = TestUtils.renderIntoDocument(
React.createElement(loop.contacts.ContactsList, {
mozLoop: navigator.mozLoop,
mozLoop: fakeMozLoop,
notifications: notifications,
switchToContactAdd: sandbox.stub(),
switchToContactEdit: sandbox.stub()
@ -635,12 +609,12 @@ describe("loop.contacts", function() {
describe("#handleImportButtonClick", function() {
beforeEach(function() {
sandbox.stub(navigator.mozLoop.contacts, "getAll", function(cb) {
sandbox.stub(fakeMozLoop.contacts, "getAll", function(cb) {
cb(null, []);
});
listView = TestUtils.renderIntoDocument(
React.createElement(loop.contacts.ContactsList, {
mozLoop: navigator.mozLoop,
mozLoop: fakeMozLoop,
notifications: notifications,
switchToContactAdd: sandbox.stub(),
switchToContactEdit: sandbox.stub()
@ -650,7 +624,7 @@ describe("loop.contacts", function() {
it("should notify the end user from a successful import", function() {
sandbox.stub(notifications, "successL10n");
navigator.mozLoop.startImport = function(opts, cb) {
fakeMozLoop.startImport = function(opts, cb) {
cb(null, {success: 42});
};
@ -665,7 +639,7 @@ describe("loop.contacts", function() {
it("should notify the end user from any encountered error", function() {
sandbox.stub(notifications, "errorL10n");
navigator.mozLoop.startImport = function(opts, cb) {
fakeMozLoop.startImport = function(opts, cb) {
cb(new Error("fake error"));
};
@ -675,6 +649,92 @@ describe("loop.contacts", function() {
"import_contacts_failure_message");
});
});
describe("Individual Contacts", function() {
describe("Contact Menu", function() {
var view, contactMenu, contact;
function mountTestComponent(options) {
var props = _.extend({
mozLoop: fakeMozLoop,
notifications: notifications,
switchToContactAdd: sandbox.stub(),
switchToContactEdit: sandbox.stub()
}, options);
return TestUtils.renderIntoDocument(
React.createElement(loop.contacts.ContactsList, props));
}
beforeEach(function() {
contact = fakeFewerContacts[0];
fakeMozLoop.contacts.getAll = function(callback) {
callback(null, [contact]);
};
view = mountTestComponent();
node = view.getDOMNode();
// Open the menu
var menuButton = node.querySelector(".icon-contact-menu-button");
TestUtils.Simulate.click(menuButton);
// Get the menu for use in the tests.
contactMenu = node.querySelector(".contact > .dropdown-menu");
});
describe("Video Conversation button", function() {
it("should call startDirectCall when the button is clicked", function() {
TestUtils.Simulate.click(contactMenu.querySelector(".video-call-item"));
sinon.assert.calledOnce(fakeMozLoop.calls.startDirectCall);
sinon.assert.calledWithExactly(fakeMozLoop.calls.startDirectCall,
contact,
CALL_TYPES.AUDIO_VIDEO);
});
it("should close the window when the button is clicked", function() {
TestUtils.Simulate.click(contactMenu.querySelector(".video-call-item"));
sinon.assert.calledOnce(fakeWindow.close);
});
it("should not do anything if the contact is blocked", function() {
contact.blocked = true;
TestUtils.Simulate.click(contactMenu.querySelector(".video-call-item"));
sinon.assert.notCalled(fakeMozLoop.calls.startDirectCall);
sinon.assert.notCalled(fakeWindow.close);
});
});
describe("Audio Conversation button", function() {
it("should call startDirectCall when the button is clicked", function() {
TestUtils.Simulate.click(contactMenu.querySelector(".audio-call-item"));
sinon.assert.calledOnce(fakeMozLoop.calls.startDirectCall);
sinon.assert.calledWithExactly(fakeMozLoop.calls.startDirectCall,
contact,
CALL_TYPES.AUDIO_ONLY);
});
it("should close the window when the button is clicked", function() {
TestUtils.Simulate.click(contactMenu.querySelector(".audio-call-item"));
sinon.assert.calledOnce(fakeWindow.close);
});
it("should not do anything if the contact is blocked", function() {
contact.blocked = true;
TestUtils.Simulate.click(contactMenu.querySelector(".audio-call-item"));
sinon.assert.notCalled(fakeMozLoop.calls.startDirectCall);
sinon.assert.notCalled(fakeWindow.close);
});
});
});
});
});
describe("ContactDetailsForm", function() {
@ -687,7 +747,7 @@ describe("loop.contacts", function() {
React.createElement(loop.contacts.ContactDetailsForm, {
contactFormData: {},
mode: "add",
mozLoop: navigator.mozLoop,
mozLoop: fakeMozLoop,
switchToInitialView: sandbox.stub()
}));
});
@ -821,7 +881,7 @@ describe("loop.contacts", function() {
React.createElement(loop.contacts.ContactDetailsForm, {
contactFormData: {},
mode: "edit",
mozLoop: navigator.mozLoop,
mozLoop: fakeMozLoop,
switchToInitialView: sandbox.stub()
}));
});

View File

@ -2526,7 +2526,7 @@ ContentPermissionPrompt.prototype = {
if (!aOptions)
aOptions = {};
aOptions.displayOrigin = requestPrincipal.URI;
aOptions.displayURI = requestPrincipal.URI;
return chromeWin.PopupNotifications.show(browser, aNotificationId, aMessage, aAnchorId,
mainAction, secondaryActions, aOptions);

View File

@ -96,17 +96,17 @@ skip-if = os == 'linux' # bug 1186322
skip-if = os == 'linux' # bug 1186322
[browser_perf-recording-notices-05.js]
skip-if = os == 'linux' # bug 1186322
[browser_perf_recordings-io-01.js]
[browser_perf-recordings-io-01.js]
skip-if = os == 'linux' # bug 1186322
[browser_perf_recordings-io-02.js]
[browser_perf-recordings-io-02.js]
skip-if = os == 'linux' # bug 1186322
[browser_perf_recordings-io-03.js]
[browser_perf-recordings-io-03.js]
skip-if = os == 'linux' # bug 1186322
[browser_perf_recordings-io-04.js]
[browser_perf-recordings-io-04.js]
skip-if = os == 'linux' # bug 1186322
[browser_perf_recordings-io-05.js]
[browser_perf-recordings-io-05.js]
skip-if = os == 'linux' # bug 1186322
[browser_perf_recordings-io-06.js]
[browser_perf-recordings-io-06.js]
skip-if = os == 'linux' # bug 1186322
[browser_perf-range-changed-render.js]
skip-if = os == 'linux' # bug 1186322

View File

@ -3,7 +3,7 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!ENTITY pageInfoWindow.width "600">
<!ENTITY pageInfoWindow.height "500">
<!ENTITY pageInfoWindow.height "550">
<!ENTITY copy.key "C">
<!ENTITY copy.label "Copy">

View File

@ -9,6 +9,7 @@
#include "BluetoothService.h"
#include "BluetoothSocket.h"
#include "BluetoothUtils.h"
#include "BluetoothUuid.h"
#include "ObexBase.h"
@ -348,7 +349,7 @@ BluetoothMapSmsManager::MasDataHandler(UnixSocketBuffer* aMessage)
if (type.EqualsLiteral("x-obex/folder-listing")) {
HandleSmsMmsFolderListing(pktHeaders);
} else if (type.EqualsLiteral("x-bt/MAP-msg-listing")) {
// TODO: Implement this feature in Bug 1166675
HandleSmsMmsMsgListing(pktHeaders);
} else if (type.EqualsLiteral("x-bt/message")) {
// TODO: Implement this feature in Bug 1166679
} else {
@ -688,6 +689,140 @@ BluetoothMapSmsManager::HandleSmsMmsFolderListing(const ObexHeaderSet& aHeader)
SendMasObexData(resp, ObexResponseCode::Success, index);
}
void
BluetoothMapSmsManager::AppendBtNamedValueByTagId(
const ObexHeaderSet& aHeader,
InfallibleTArray<BluetoothNamedValue>& aValues,
const Map::AppParametersTagId aTagId)
{
uint8_t buf[64];
if (!aHeader.GetAppParameter(aTagId, buf, 64)) {
return;
}
/*
* Follow MAP 6.3.1 Application Parameter Header
*/
switch (aTagId) {
case Map::AppParametersTagId::MaxListCount: {
uint16_t maxListCount = *((uint16_t *)buf);
// convert big endian to little endian
maxListCount = (maxListCount >> 8) | (maxListCount << 8);
BT_LOGR("max list count: %d", maxListCount);
AppendNamedValue(aValues, "maxListCount", maxListCount);
break;
}
case Map::AppParametersTagId::StartOffset: {
uint16_t startOffset = *((uint16_t *)buf);
// convert big endian to little endian
startOffset = (startOffset >> 8) | (startOffset << 8);
BT_LOGR("start offset : %d", startOffset);
AppendNamedValue(aValues, "startOffset", startOffset);
break;
}
case Map::AppParametersTagId::SubjectLength: {
uint8_t subLength = *((uint8_t *)buf);
// convert big endian to little endian
subLength = (subLength >> 8) | (subLength << 8);
BT_LOGR("msg subLength : %d", subLength);
AppendNamedValue(aValues, "subLength", subLength);
break;
}
case Map::AppParametersTagId::ParameterMask: {
// 4 bytes
uint32_t parameterMask = *((uint32_t *)buf);
// convert big endian to little endian
parameterMask = (parameterMask >> 8) | (parameterMask << 8);
BT_LOGR("msg parameterMask : %d", parameterMask);
AppendNamedValue(aValues, "parameterMask", parameterMask);
break;
}
case Map::AppParametersTagId::FilterMessageType: {
uint8_t filterMessageType = *((uint8_t *)buf);
// convert big endian to little endian
filterMessageType = (filterMessageType >> 8) | (filterMessageType << 8);
BT_LOGR("msg filterMessageType : %d", filterMessageType);
AppendNamedValue(aValues, "filterMessageType", filterMessageType);
break;
}
case Map::AppParametersTagId::FilterPeriodBegin: {
nsCString filterPeriodBegin((char *) buf);
BT_LOGR("msg FilterPeriodBegin : %s", filterPeriodBegin.get());
AppendNamedValue(aValues, "filterPeriodBegin", filterPeriodBegin);
break;
}
case Map::AppParametersTagId::FilterPeriodEnd: {
nsCString filterPeriodEnd((char*)buf);
BT_LOGR("msg filterPeriodEnd : %s", filterPeriodEnd.get());
AppendNamedValue(aValues, "filterPeriodEnd", filterPeriodEnd);
break;
}
case Map::AppParametersTagId::FilterReadStatus: {
uint8_t filterReadStatus = *((uint8_t *)buf);
// convert big endian to little endian
filterReadStatus = (filterReadStatus >> 8) | (filterReadStatus << 8);
BT_LOGR("msg filter read status : %d", filterReadStatus);
AppendNamedValue(aValues, "filterReadStatus", filterReadStatus);
break;
}
case Map::AppParametersTagId::FilterRecipient: {
nsCString filterRecipient((char*) buf);
BT_LOGR("msg filterRecipient : %s", filterRecipient.get());
AppendNamedValue(aValues, "filterRecipient", filterRecipient);
break;
}
case Map::AppParametersTagId::FilterOriginator: {
nsCString filterOriginator((char*) buf);
BT_LOGR("msg filter Originator : %s", filterOriginator.get());
AppendNamedValue(aValues, "filterOriginator", filterOriginator);
break;
}
case Map::AppParametersTagId::FilterPriority: {
uint8_t filterPriority = *((uint8_t *)buf);
// convert big endian to little endian
filterPriority = (filterPriority >> 8) | (filterPriority << 8);
BT_LOGR("msg filter priority: %d", filterPriority);
AppendNamedValue(aValues, "filterPriority", filterPriority);
break;
}
default:
BT_LOGR("Unsupported AppParameterTag: %x", aTagId);
break;
}
}
void
BluetoothMapSmsManager::HandleSmsMmsMsgListing(const ObexHeaderSet& aHeader)
{
MOZ_ASSERT(NS_IsMainThread());
BluetoothService* bs = BluetoothService::Get();
InfallibleTArray<BluetoothNamedValue> data;
static Map::AppParametersTagId sMsgListingParameters[] = {
[0] = Map::AppParametersTagId::MaxListCount,
[1] = Map::AppParametersTagId::StartOffset,
[2] = Map::AppParametersTagId::SubjectLength,
[3] = Map::AppParametersTagId::ParameterMask,
[4] = Map::AppParametersTagId::FilterMessageType,
[5] = Map::AppParametersTagId::FilterPeriodBegin,
[6] = Map::AppParametersTagId::FilterPeriodEnd,
[7] = Map::AppParametersTagId::FilterReadStatus,
[8] = Map::AppParametersTagId::FilterRecipient,
[9] = Map::AppParametersTagId::FilterOriginator,
[10] = Map::AppParametersTagId::FilterPriority
};
for (uint8_t i = 0; i < MOZ_ARRAY_LENGTH(sMsgListingParameters); i++) {
AppendBtNamedValueByTagId(aHeader, data, sMsgListingParameters[i]);
}
bs->DistributeSignal(NS_LITERAL_STRING(MAP_MESSAGES_LISTING_REQ_ID),
NS_LITERAL_STRING(KEY_ADAPTER),
data);
}
void
BluetoothMapSmsManager::BuildDefaultFolderStructure()
{

View File

@ -96,6 +96,10 @@ private:
void HandleEventReport(const ObexHeaderSet& aHeader);
void HandleMessageStatus(const ObexHeaderSet& aHeader);
void HandleSmsMmsFolderListing(const ObexHeaderSet& aHeader);
void HandleSmsMmsMsgListing(const ObexHeaderSet& aHeader);
void AppendBtNamedValueByTagId(const ObexHeaderSet& aHeader,
InfallibleTArray<BluetoothNamedValue>& aValues,
const Map::AppParametersTagId aTagId);
void SendMasObexData(uint8_t* aData, uint8_t aOpcode, int aSize);
void SendMnsObexData(uint8_t* aData, uint8_t aOpcode, int aSize);

View File

@ -275,14 +275,14 @@ BluetoothPbapManager::ReceiveSocketData(BluetoothSocket* aSocket,
// All OBEX request messages shall be sent as one OBEX packet containing
// all of the headers. I.e. OBEX GET with opcode 0x83 shall always be
// used. OBEX GET with opcode 0x03 shall never be used.
BT_LOGR("PBAP shall always uses OBEX GetFinal instead of Get.");
BT_LOGR("PBAP shall always use OBEX GetFinal instead of Get.");
// no break. Treat 'Get' as 'GetFinal' for error tolerance.
case ObexRequestCode::GetFinal: {
// As long as 'mVCardDataStream' requires multiple response packets to
// complete, the client should continue to issue GET requests until the
// final body information (in an End-of-Body header) arrives, along with
// the response code 0xA0 Success.
// When |mVCardDataStream| requires multiple response packets to complete,
// the client should continue to issue GET requests until the final body
// information (i.e., End-of-Body header) arrives, along with
// ObexResponseCode::Success
if (mVCardDataStream) {
if (!ReplyToGet()) {
BT_LOGR("Failed to reply to PBAP GET request.");
@ -731,7 +731,7 @@ BluetoothPbapManager::PackPropertiesMask(uint8_t* aData, int aSize)
(aData[5] << 16) | (aData[4] << 24);
uint32_t count = 0;
while (!x) {
while (x) {
if (x & 1) {
propSelector.AppendElement(count);
}

View File

@ -185,6 +185,12 @@ extern bool gBluetoothDebugFlag;
#define PULL_VCARD_ENTRY_REQ_ID "pullvcardentryreq"
#define PULL_VCARD_LISTING_REQ_ID "pullvcardlistingreq"
/**
* When receiving a MAP request of 'messages listing' from a remote device,
* we'll dispatch an event.
*/
#define MAP_MESSAGES_LISTING_REQ_ID "mapmessageslistingreq"
/**
* When the value of a characteristic of a remote BLE device changes, we'll
* dispatch an event

View File

@ -39,6 +39,9 @@ GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_USER_2] = "user-2";
const CB_MESSAGE_SIZE_GSM = 88;
const CB_MESSAGE_SIZE_ETWS = 56;
const CB_UMTS_MESSAGE_TYPE_CBS = 1;
const CB_UMTS_MESSAGE_PAGE_SIZE = 82;
const CB_GSM_MESSAGEID_ETWS_BEGIN = 0x1100;
const CB_GSM_MESSAGEID_ETWS_END = 0x1107;
@ -201,18 +204,28 @@ function ensureCellBroadcast() {
SpecialPowers.pushPermissions(permissions, function() {
ok(true, "permissions pushed: " + JSON.stringify(permissions));
cbManager = window.navigator.mozCellBroadcast;
if (cbManager) {
log("navigator.mozCellBroadcast is instance of " + cbManager.constructor);
} else {
log("navigator.mozCellBroadcast is undefined.");
}
// Permission changes can't change existing Navigator.prototype
// objects, so grab our objects from a new Navigator.
let workingFrame = document.createElement("iframe");
workingFrame.addEventListener("load", function load() {
workingFrame.removeEventListener("load", load);
if (cbManager instanceof window.MozCellBroadcast) {
deferred.resolve(cbManager);
} else {
deferred.reject();
}
cbManager = workingFrame.contentWindow.navigator.mozCellBroadcast;
if (cbManager) {
log("navigator.mozCellBroadcast is instance of " + cbManager.constructor);
} else {
log("navigator.mozCellBroadcast is undefined.");
}
if (cbManager instanceof window.MozCellBroadcast) {
deferred.resolve(cbManager);
} else {
deferred.reject();
}
});
document.body.appendChild(workingFrame);
});
return deferred.promise;

View File

@ -5,5 +5,7 @@ qemu = true
[test_cellbroadcast_etws.js]
[test_cellbroadcast_gsm.js]
[test_cellbroadcast_gsm_language_and_body.js]
[test_cellbroadcast_multi_sim.js]
[test_cellbroadcast_umts.js]
[test_cellbroadcast_umts.js]
[test_cellbroadcast_umts_language_and_body.js]

View File

@ -1,7 +1,7 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 20000;
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
function testReceiving_ETWS_MessageAttributes() {

View File

@ -110,62 +110,6 @@ function testReceiving_GSM_MessageId() {
return promise;
}
function testReceiving_GSM_Language_and_Body() {
log("Test receiving GSM Cell Broadcast - Language & Body");
let promise = Promise.resolve();
let testDcs = [];
let dcs = 0;
while (dcs <= 0xFF) {
try {
let dcsInfo = { dcs: dcs };
[ dcsInfo.encoding, dcsInfo.language,
dcsInfo.indicator, dcsInfo.messageClass ] = decodeGsmDataCodingScheme(dcs);
testDcs.push(dcsInfo);
} catch (e) {
// Unsupported coding group, skip.
dcs = (dcs & PDU_DCS_CODING_GROUP_BITS) + 0x10;
}
dcs++;
}
let verifyCBMessage = (aMessage, aDcsInfo) => {
if (aDcsInfo.language) {
is(aMessage.language, aDcsInfo.language, "aMessage.language");
} else if (aDcsInfo.indicator) {
is(aMessage.language, "@@", "aMessage.language");
} else {
ok(aMessage.language == null, "aMessage.language");
}
switch (aDcsInfo.encoding) {
case PDU_DCS_MSG_CODING_7BITS_ALPHABET:
is(aMessage.body, aDcsInfo.indicator ? DUMMY_BODY_7BITS_IND : DUMMY_BODY_7BITS, "aMessage.body");
break;
case PDU_DCS_MSG_CODING_8BITS_ALPHABET:
ok(aMessage.body == null, "aMessage.body");
break;
case PDU_DCS_MSG_CODING_16BITS_ALPHABET:
is(aMessage.body, aDcsInfo.indicator ? DUMMY_BODY_UCS2_IND : DUMMY_BODY_UCS2, "aMessage.body");
break;
}
is(aMessage.messageClass, aDcsInfo.messageClass, "aMessage.messageClass");
};
testDcs.forEach(function(aDcsInfo) {
let pdu = buildHexStr(0, 8)
+ buildHexStr(aDcsInfo.dcs, 2)
+ buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 5) * 2);
promise = promise
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
.then((aMessage) => verifyCBMessage(aMessage, aDcsInfo));
});
return promise;
}
function testReceiving_GSM_Timestamp() {
log("Test receiving GSM Cell Broadcast - Timestamp");
@ -351,7 +295,6 @@ startTestCommon(function testCaseMain() {
.then(() => testReceiving_GSM_GeographicalScope())
.then(() => testReceiving_GSM_MessageCode())
.then(() => testReceiving_GSM_MessageId())
.then(() => testReceiving_GSM_Language_and_Body())
.then(() => testReceiving_GSM_Timestamp())
.then(() => testReceiving_GSM_WarningType())
.then(() => testReceiving_GSM_EmergencyUserAlert())

View File

@ -0,0 +1,64 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 90000;
MARIONETTE_HEAD_JS = 'head.js';
function testReceiving_GSM_Language_and_Body() {
log("Test receiving GSM Cell Broadcast - Language & Body");
let promise = Promise.resolve();
let testDcs = [];
let dcs = 0;
while (dcs <= 0xFF) {
try {
let dcsInfo = { dcs: dcs };
[ dcsInfo.encoding, dcsInfo.language,
dcsInfo.indicator, dcsInfo.messageClass ] = decodeGsmDataCodingScheme(dcs);
testDcs.push(dcsInfo);
} catch (e) {
// Unsupported coding group, skip.
dcs = (dcs & PDU_DCS_CODING_GROUP_BITS) + 0x10;
}
dcs++;
}
let verifyCBMessage = (aMessage, aDcsInfo) => {
if (aDcsInfo.language) {
is(aMessage.language, aDcsInfo.language, "aMessage.language");
} else if (aDcsInfo.indicator) {
is(aMessage.language, "@@", "aMessage.language");
} else {
ok(aMessage.language == null, "aMessage.language");
}
switch (aDcsInfo.encoding) {
case PDU_DCS_MSG_CODING_7BITS_ALPHABET:
is(aMessage.body, aDcsInfo.indicator ? DUMMY_BODY_7BITS_IND : DUMMY_BODY_7BITS, "aMessage.body");
break;
case PDU_DCS_MSG_CODING_8BITS_ALPHABET:
ok(aMessage.body == null, "aMessage.body");
break;
case PDU_DCS_MSG_CODING_16BITS_ALPHABET:
is(aMessage.body, aDcsInfo.indicator ? DUMMY_BODY_UCS2_IND : DUMMY_BODY_UCS2, "aMessage.body");
break;
}
is(aMessage.messageClass, aDcsInfo.messageClass, "aMessage.messageClass");
};
ok(testDcs.length, "testDcs.length");
testDcs.forEach(function(aDcsInfo) {
let pdu = buildHexStr(0, 8)
+ buildHexStr(aDcsInfo.dcs, 2)
+ buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 5) * 2);
promise = promise
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
.then((aMessage) => verifyCBMessage(aMessage, aDcsInfo));
});
return promise;
}
startTestCommon(() => testReceiving_GSM_Language_and_Body());

View File

@ -1,7 +1,7 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 10000;
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
function testReceiving_MultiSIM() {

View File

@ -1,9 +1,9 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 90000;
MARIONETTE_HEAD_JS = 'head.js';
const CB_UMTS_MESSAGE_TYPE_CBS = 1;
const CB_UMTS_MESSAGE_PAGE_SIZE = 82;
function testReceiving_UMTS_MessageAttributes() {
log("Test receiving UMTS Cell Broadcast - Message Attributes");
@ -126,70 +126,6 @@ function testReceiving_UMTS_MessageId() {
return promise;
}
function testReceiving_UMTS_Language_and_Body() {
log("Test receiving UMTS Cell Broadcast - Language & Body");
let promise = Promise.resolve();
let testDcs = [];
let dcs = 0;
while (dcs <= 0xFF) {
try {
let dcsInfo = { dcs: dcs };
[ dcsInfo.encoding, dcsInfo.language,
dcsInfo.indicator, dcsInfo.messageClass ] = decodeGsmDataCodingScheme(dcs);
testDcs.push(dcsInfo);
} catch (e) {
// Unsupported coding group, skip.
dcs = (dcs & PDU_DCS_CODING_GROUP_BITS) + 0x10;
}
dcs++;
}
let verifyCBMessage = (aMessage, aDcsInfo) => {
if (aDcsInfo.language) {
is(aMessage.language, aDcsInfo.language, "aMessage.language");
} else if (aDcsInfo.indicator) {
is(aMessage.language, "@@", "aMessage.language");
} else {
ok(aMessage.language == null, "aMessage.language");
}
switch (aDcsInfo.encoding) {
case PDU_DCS_MSG_CODING_7BITS_ALPHABET:
is(aMessage.body,
aDcsInfo.indicator ? DUMMY_BODY_7BITS_IND : DUMMY_BODY_7BITS,
"aMessage.body");
break;
case PDU_DCS_MSG_CODING_8BITS_ALPHABET:
ok(aMessage.body == null, "aMessage.body");
break;
case PDU_DCS_MSG_CODING_16BITS_ALPHABET:
is(aMessage.body,
aDcsInfo.indicator ? DUMMY_BODY_UCS2_IND : DUMMY_BODY_UCS2,
"aMessage.body");
break;
}
is(aMessage.messageClass, aDcsInfo.messageClass, "aMessage.messageClass");
};
testDcs.forEach(function(aDcsInfo) {
let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
+ buildHexStr(0, 4) // skip msg_id
+ buildHexStr(0, 4) // skip SN
+ buildHexStr(aDcsInfo.dcs, 2) // set dcs
+ buildHexStr(1, 2) // set num_of_pages to 1
+ buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
+ buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2); // msg_info_length
promise = promise
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
.then((aMessage) => verifyCBMessage(aMessage, aDcsInfo));
});
return promise;
}
function testReceiving_UMTS_Timestamp() {
log("Test receiving UMTS Cell Broadcast - Timestamp");
@ -441,7 +377,6 @@ startTestCommon(function testCaseMain() {
.then(() => testReceiving_UMTS_GeographicalScope())
.then(() => testReceiving_UMTS_MessageCode())
.then(() => testReceiving_UMTS_MessageId())
.then(() => testReceiving_UMTS_Language_and_Body())
.then(() => testReceiving_UMTS_Timestamp())
.then(() => testReceiving_UMTS_WarningType())
.then(() => testReceiving_UMTS_EmergencyUserAlert())

View File

@ -0,0 +1,71 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 90000;
MARIONETTE_HEAD_JS = 'head.js';
function testReceiving_UMTS_Language_and_Body() {
log("Test receiving UMTS Cell Broadcast - Language & Body");
let promise = Promise.resolve();
let testDcs = [];
let dcs = 0;
while (dcs <= 0xFF) {
try {
let dcsInfo = { dcs: dcs };
[ dcsInfo.encoding, dcsInfo.language,
dcsInfo.indicator, dcsInfo.messageClass ] = decodeGsmDataCodingScheme(dcs);
testDcs.push(dcsInfo);
} catch (e) {
// Unsupported coding group, skip.
dcs = (dcs & PDU_DCS_CODING_GROUP_BITS) + 0x10;
}
dcs++;
}
let verifyCBMessage = (aMessage, aDcsInfo) => {
if (aDcsInfo.language) {
is(aMessage.language, aDcsInfo.language, "aMessage.language");
} else if (aDcsInfo.indicator) {
is(aMessage.language, "@@", "aMessage.language");
} else {
ok(aMessage.language == null, "aMessage.language");
}
switch (aDcsInfo.encoding) {
case PDU_DCS_MSG_CODING_7BITS_ALPHABET:
is(aMessage.body,
aDcsInfo.indicator ? DUMMY_BODY_7BITS_IND : DUMMY_BODY_7BITS,
"aMessage.body");
break;
case PDU_DCS_MSG_CODING_8BITS_ALPHABET:
ok(aMessage.body == null, "aMessage.body");
break;
case PDU_DCS_MSG_CODING_16BITS_ALPHABET:
is(aMessage.body,
aDcsInfo.indicator ? DUMMY_BODY_UCS2_IND : DUMMY_BODY_UCS2,
"aMessage.body");
break;
}
is(aMessage.messageClass, aDcsInfo.messageClass, "aMessage.messageClass");
};
testDcs.forEach(function(aDcsInfo) {
let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
+ buildHexStr(0, 4) // skip msg_id
+ buildHexStr(0, 4) // skip SN
+ buildHexStr(aDcsInfo.dcs, 2) // set dcs
+ buildHexStr(1, 2) // set num_of_pages to 1
+ buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
+ buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2); // msg_info_length
promise = promise
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
.then((aMessage) => verifyCBMessage(aMessage, aDcsInfo));
});
return promise;
}
startTestCommon(() => testReceiving_UMTS_Language_and_Body());

View File

@ -8,6 +8,10 @@ client.name2 = %1$S's %2$S on %3$S
# %S is the date and time at which the last sync successfully completed
lastSync2.label = Last sync: %S
# signInToSync.description is the tooltip for the Sync buttons when Sync is
# not configured.
signInToSync.description = Sign In To Sync
mobile.label = Mobile Bookmarks
remote.pending.label = Remote tabs are being synced…

View File

@ -8,7 +8,7 @@ const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const WEAVE_SYNC_PREFS = "services.sync.prefs.sync.";
const PREF_SYNC_PREFS_PREFIX = "services.sync.prefs.sync.";
Cu.import("resource://services-sync/engines.js");
Cu.import("resource://services-sync/record.js");
@ -43,7 +43,7 @@ PrefsEngine.prototype = {
syncPriority: 1,
getChangedIDs: function getChangedIDs() {
getChangedIDs: function () {
// No need for a proper timestamp (no conflict resolution needed).
let changedIDs = {};
if (this._tracker.modified)
@ -51,12 +51,12 @@ PrefsEngine.prototype = {
return changedIDs;
},
_wipeClient: function _wipeClient() {
_wipeClient: function () {
SyncEngine.prototype._wipeClient.call(this);
this.justWiped = true;
},
_reconcile: function _reconcile(item) {
_reconcile: function (item) {
// Apply the incoming item if we don't care about the local data
if (this.justWiped) {
this.justWiped = false;
@ -78,24 +78,25 @@ PrefStore.prototype = {
__prefs: null,
get _prefs() {
if (!this.__prefs)
if (!this.__prefs) {
this.__prefs = new Preferences();
}
return this.__prefs;
},
_getSyncPrefs: function _getSyncPrefs() {
_getSyncPrefs: function () {
let syncPrefs = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefService)
.getBranch(WEAVE_SYNC_PREFS)
.getBranch(PREF_SYNC_PREFS_PREFIX)
.getChildList("", {});
// Also sync preferences that determine which prefs get synced.
return syncPrefs.concat(
syncPrefs.map(function (pref) { return WEAVE_SYNC_PREFS + pref; }));
let controlPrefs = syncPrefs.map(pref => PREF_SYNC_PREFS_PREFIX + pref);
return controlPrefs.concat(syncPrefs);
},
_isSynced: function _isSyncedPref(pref) {
return (pref.indexOf(WEAVE_SYNC_PREFS) == 0)
|| this._prefs.get(WEAVE_SYNC_PREFS + pref, false);
_isSynced: function (pref) {
return pref.startsWith(PREF_SYNC_PREFS_PREFIX) ||
this._prefs.get(PREF_SYNC_PREFS_PREFIX + pref, false);
},
_getAllPrefs: function () {
@ -109,15 +110,21 @@ PrefStore.prototype = {
return values;
},
_setAllPrefs: function PrefStore__setAllPrefs(values) {
_setAllPrefs: function (values) {
let selectedThemeIDPref = "lightweightThemes.selectedThemeID";
let selectedThemeIDBefore = this._prefs.get(selectedThemeIDPref, null);
for (let [pref, value] in Iterator(values)) {
if (!this._isSynced(pref))
// Update 'services.sync.prefs.sync.foo.pref' before 'foo.pref', otherwise
// _isSynced returns false when 'foo.pref' doesn't exist (e.g., on a new device).
let prefs = Object.keys(values).sort(a => -a.indexOf(PREF_SYNC_PREFS_PREFIX));
for (let pref of prefs) {
if (!this._isSynced(pref)) {
continue;
}
// Pref has gone missing, best we can do is reset it.
let value = values[pref];
// Pref has gone missing. The best we can do is reset it.
if (value == null) {
this._prefs.reset(pref);
continue;
@ -141,22 +148,22 @@ PrefStore.prototype = {
}
},
getAllIDs: function PrefStore_getAllIDs() {
getAllIDs: function () {
/* We store all prefs in just one WBO, with just one GUID */
let allprefs = {};
allprefs[PREFS_GUID] = true;
return allprefs;
},
changeItemID: function PrefStore_changeItemID(oldID, newID) {
changeItemID: function (oldID, newID) {
this._log.trace("PrefStore GUID is constant!");
},
itemExists: function FormStore_itemExists(id) {
itemExists: function (id) {
return (id === PREFS_GUID);
},
createRecord: function createRecord(id, collection) {
createRecord: function (id, collection) {
let record = new PrefRec(collection, id);
if (id == PREFS_GUID) {
@ -168,15 +175,15 @@ PrefStore.prototype = {
return record;
},
create: function PrefStore_create(record) {
create: function (record) {
this._log.trace("Ignoring create request");
},
remove: function PrefStore_remove(record) {
remove: function (record) {
this._log.trace("Ignoring remove request");
},
update: function PrefStore_update(record) {
update: function (record) {
// Silently ignore pref updates that are for other apps.
if (record.id != PREFS_GUID)
return;
@ -185,7 +192,7 @@ PrefStore.prototype = {
this._setAllPrefs(record.value);
},
wipe: function PrefStore_wipe() {
wipe: function () {
this._log.trace("Ignoring wipe request");
}
};
@ -241,8 +248,8 @@ PrefTracker.prototype = {
case "nsPref:changed":
// Trigger a sync for MULTI-DEVICE for a change that determines
// which prefs are synced or a regular pref change.
if (data.indexOf(WEAVE_SYNC_PREFS) == 0 ||
this._prefs.get(WEAVE_SYNC_PREFS + data, false)) {
if (data.indexOf(PREF_SYNC_PREFS_PREFIX) == 0 ||
this._prefs.get(PREF_SYNC_PREFS_PREFIX + data, false)) {
this.score += SCORE_INCREMENT_XLARGE;
this.modified = true;
this._log.trace("Preference " + data + " changed");

View File

@ -81,6 +81,7 @@ function run_test() {
"testing.string": "im in ur prefs",
"testing.bool": false,
"testing.deleteme": null,
"testing.somepref": "im a new pref from other device",
"services.sync.prefs.sync.testing.somepref": true
};
store.update(record);
@ -89,6 +90,7 @@ function run_test() {
do_check_eq(prefs.get("testing.bool"), false);
do_check_eq(prefs.get("testing.deleteme"), undefined);
do_check_eq(prefs.get("testing.dont.change"), "Please don't change me.");
do_check_eq(prefs.get("testing.somepref"), "im a new pref from other device");
do_check_eq(Svc.Prefs.get("prefs.sync.testing.somepref"), true);
_("Enable persona");

View File

@ -3,6 +3,7 @@
<head>
<title>Modal Prompts Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="prompt_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
@ -770,7 +771,7 @@ function handleDialog(ui, testNum) {
}
function runTests() {
function* runTests() {
let ioService = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
ok(true, "Running tests (isTabModal=" + isTabModal + ", usePromptService=" + usePromptService + ")");
@ -808,6 +809,8 @@ function runTests() {
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 2 =====
// AlertCheck (null checkbox label)
testNum++;
@ -818,6 +821,7 @@ function runTests() {
prompter.alertCheck.apply(null, promptArgs);
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 3 =====
// AlertCheck
@ -831,6 +835,7 @@ function runTests() {
ok(didDialog, "handleDialog was invoked");
is(checkVal.value, true, "checkbox was checked");
yield Promise.resolve();
// ===== test 4 =====
// Confirm (ok)
@ -843,6 +848,8 @@ function runTests() {
is(isOK, true, "checked expected retval");
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 5 =====
// Confirm (cancel)
testNum++;
@ -854,6 +861,7 @@ function runTests() {
is(isOK, false, "checked expected retval");
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 6 =====
// ConfirmCheck (ok, null checkbox label)
@ -866,6 +874,8 @@ function runTests() {
is(isOK, true, "checked expected retval");
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 7 =====
// ConfirmCheck (cancel, null checkbox label)
testNum++;
@ -877,6 +887,8 @@ function runTests() {
is(isOK, false, "checked expected retval");
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 8 =====
// ConfirmCheck (ok)
testNum++;
@ -890,6 +902,8 @@ function runTests() {
is(checkVal.value, true, "expected checkbox setting");
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 9 =====
// ConfirmCheck (cancel)
testNum++;
@ -903,6 +917,7 @@ function runTests() {
is(checkVal.value, true, "expected checkbox setting");
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 10 =====
// Prompt (ok, no default text)
@ -917,6 +932,8 @@ function runTests() {
is(textVal.value, "bacon", "checking expected text value");
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 11 =====
// Prompt (ok, default text)
testNum++;
@ -930,6 +947,8 @@ function runTests() {
is(textVal.value, "kittens", "checking expected text value");
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 12 =====
// Prompt (cancel, default text)
testNum++;
@ -943,6 +962,8 @@ function runTests() {
is(textVal.value, "puppies", "checking expected text value");
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 13 =====
// Prompt (cancel, default text modified)
testNum++;
@ -956,6 +977,8 @@ function runTests() {
is(textVal.value, "puppies", "checking expected text value");
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 14 =====
// Prompt (ok, with checkbox)
testNum++;
@ -971,6 +994,8 @@ function runTests() {
is(checkVal.value, true, "expected checkbox setting");
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 15 =====
// Prompt (cancel, with checkbox)
testNum++;
@ -986,6 +1011,7 @@ function runTests() {
is(checkVal.value, false, "expected checkbox setting");
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 16 =====
// PromptUsernameAndPassword (ok)
@ -1005,6 +1031,8 @@ function runTests() {
is(checkVal.value, true, "expected checkbox setting");
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 17 =====
// PromptUsernameAndPassword (cancel)
testNum++;
@ -1022,6 +1050,7 @@ function runTests() {
is(checkVal.value, false, "expected checkbox setting");
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 18 =====
// PromptPassword (ok)
@ -1038,6 +1067,8 @@ function runTests() {
is(checkVal.value, true, "expected checkbox setting");
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 19 =====
// PromptPassword (cancel)
testNum++;
@ -1053,6 +1084,8 @@ function runTests() {
is(checkVal.value, false, "expected checkbox setting");
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 20 =====
// ConfirmEx (ok/cancel, ok)
testNum++;
@ -1065,6 +1098,8 @@ function runTests() {
is(clickedButton, 0, "checked expected button num click");
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 21 =====
// ConfirmEx (yes/no, cancel)
testNum++;
@ -1077,6 +1112,8 @@ function runTests() {
is(clickedButton, 1, "checked expected button num click");
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 22 =====
// ConfirmEx (buttons from args, checkbox, ok)
testNum++;
@ -1095,6 +1132,8 @@ function runTests() {
is(checkVal.value, true, "expected checkbox setting");
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 23 =====
// ConfirmEx (buttons from args, checkbox, cancel)
testNum++;
@ -1114,6 +1153,8 @@ function runTests() {
is(checkVal.value, true, "expected checkbox setting");
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 24 =====
// ConfirmEx (buttons from args, checkbox, button3)
testNum++;
@ -1133,6 +1174,8 @@ function runTests() {
is(checkVal.value, true, "expected checkbox setting");
ok(didDialog, "handleDialog was invoked");
yield Promise.resolve();
// ===== test 25 =====
// Alert, no window
// (skipped for tabmodal tests: window is required)
@ -1146,6 +1189,7 @@ function runTests() {
ok(didDialog, "handleDialog was invoked");
}
yield Promise.resolve();
// ===== test 26 =====
// ConfirmEx (delay, ok)
@ -1183,6 +1227,8 @@ function runTests() {
realm : ""
};
yield Promise.resolve();
// ===== test 100 =====
// promptAuth with empty realm
// (promptAuth is only accessible from the prompt service)
@ -1198,6 +1244,8 @@ function runTests() {
ok(didDialog, "handleDialog was invoked");
}
yield Promise.resolve();
// ===== test 101 =====
// promptAuth with long realm
// (promptAuth is only accessible from the prompt service)
@ -1230,16 +1278,19 @@ let pollTimer;
* - 3rd pass: with tab-modal prompts. Can't opt into these via * nsIPromptService.
*/
isTabModal = false; usePromptService = true;
runTests();
add_task(function* runPromptTests() {
isTabModal = false; usePromptService = false;
runTests();
isTabModal = false; usePromptService = true;
yield* runTests();
if (getTabModalPromptBox(window)) {
isTabModal = true; usePromptService = false;
runTests();
}
isTabModal = false; usePromptService = false;
yield* runTests();
if (getTabModalPromptBox(window)) {
isTabModal = true; usePromptService = false;
yield* runTests();
}
});
</script>
</pre>

File diff suppressed because it is too large Load Diff

View File

@ -1,771 +0,0 @@
/* -*- Mode: js; js-indent-level: 2; -*- */
/*
* Copyright 2011 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE or:
* http://opensource.org/licenses/BSD-3-Clause
*/
/*
* WARNING!
*
* Do not edit this file directly, it is built from the sources at
* https://github.com/mozilla/source-map/
*/
let loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Components.interfaces.mozIJSSubScriptLoader);
let exports = {};
loader.loadSubScript("resource://gre/modules/devtools/sourcemap/source-map.js", exports);
// Also bind on `this` for b2g bug on EXPORTED_SYMBOLS
let define = this.define = exports.define;
let require = exports.require;
this.EXPORTED_SYMBOLS = [ "define", "runSourceMapTests" ];
/* -*- Mode: js; js-indent-level: 2; -*- */
/*
* Copyright 2011 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE or:
* http://opensource.org/licenses/BSD-3-Clause
*/
define('test/source-map/assert', ['require', 'exports'], function (require, exports) {
let do_throw = function (msg) {
throw new Error(msg);
};
exports.init = function (throw_fn) {
do_throw = throw_fn;
};
exports.doesNotThrow = function (fn) {
try {
fn();
}
catch (e) {
do_throw(e.message);
}
};
exports.equal = function (actual, expected, msg) {
msg = msg || String(actual) + ' != ' + String(expected);
if (actual != expected) {
do_throw(msg);
}
};
exports.ok = function (val, msg) {
msg = msg || String(val) + ' is falsey';
if (!Boolean(val)) {
do_throw(msg);
}
};
exports.strictEqual = function (actual, expected, msg) {
msg = msg || String(actual) + ' !== ' + String(expected);
if (actual !== expected) {
do_throw(msg);
}
};
exports.throws = function (fn) {
try {
fn();
do_throw('Expected an error to be thrown, but it wasn\'t.');
}
catch (e) {
}
};
});
/* -*- Mode: js; js-indent-level: 2; -*- */
/*
* Copyright 2011 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE or:
* http://opensource.org/licenses/BSD-3-Clause
*/
define('test/source-map/util', ['require', 'exports', 'module' , 'lib/source-map/util'], function(require, exports, module) {
var util = require('source-map/util');
// This is a test mapping which maps functions from two different files
// (one.js and two.js) to a minified generated source.
//
// Here is one.js:
//
// ONE.foo = function (bar) {
// return baz(bar);
// };
//
// Here is two.js:
//
// TWO.inc = function (n) {
// return n + 1;
// };
//
// And here is the generated code (min.js):
//
// ONE.foo=function(a){return baz(a);};
// TWO.inc=function(a){return a+1;};
exports.testGeneratedCode = " ONE.foo=function(a){return baz(a);};\n"+
" TWO.inc=function(a){return a+1;};";
exports.testMap = {
version: 3,
file: 'min.js',
names: ['bar', 'baz', 'n'],
sources: ['one.js', 'two.js'],
sourceRoot: '/the/root',
mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
};
exports.testMapNoSourceRoot = {
version: 3,
file: 'min.js',
names: ['bar', 'baz', 'n'],
sources: ['one.js', 'two.js'],
mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
};
exports.testMapEmptySourceRoot = {
version: 3,
file: 'min.js',
names: ['bar', 'baz', 'n'],
sources: ['one.js', 'two.js'],
sourceRoot: '',
mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
};
// This mapping is identical to above, but uses the indexed format instead.
exports.indexedTestMap = {
version: 3,
file: 'min.js',
sections: [
{
offset: {
line: 0,
column: 0
},
map: {
version: 3,
sources: [
"one.js"
],
sourcesContent: [
' ONE.foo = function (bar) {\n' +
' return baz(bar);\n' +
' };',
],
names: [
"bar",
"baz"
],
mappings: "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID",
file: "min.js",
sourceRoot: "/the/root"
}
},
{
offset: {
line: 1,
column: 0
},
map: {
version: 3,
sources: [
"two.js"
],
sourcesContent: [
' TWO.inc = function (n) {\n' +
' return n + 1;\n' +
' };'
],
names: [
"n"
],
mappings: "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOA",
file: "min.js",
sourceRoot: "/the/root"
}
}
]
};
exports.indexedTestMapDifferentSourceRoots = {
version: 3,
file: 'min.js',
sections: [
{
offset: {
line: 0,
column: 0
},
map: {
version: 3,
sources: [
"one.js"
],
sourcesContent: [
' ONE.foo = function (bar) {\n' +
' return baz(bar);\n' +
' };',
],
names: [
"bar",
"baz"
],
mappings: "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID",
file: "min.js",
sourceRoot: "/the/root"
}
},
{
offset: {
line: 1,
column: 0
},
map: {
version: 3,
sources: [
"two.js"
],
sourcesContent: [
' TWO.inc = function (n) {\n' +
' return n + 1;\n' +
' };'
],
names: [
"n"
],
mappings: "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOA",
file: "min.js",
sourceRoot: "/different/root"
}
}
]
};
exports.testMapWithSourcesContent = {
version: 3,
file: 'min.js',
names: ['bar', 'baz', 'n'],
sources: ['one.js', 'two.js'],
sourcesContent: [
' ONE.foo = function (bar) {\n' +
' return baz(bar);\n' +
' };',
' TWO.inc = function (n) {\n' +
' return n + 1;\n' +
' };'
],
sourceRoot: '/the/root',
mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
};
exports.testMapRelativeSources = {
version: 3,
file: 'min.js',
names: ['bar', 'baz', 'n'],
sources: ['./one.js', './two.js'],
sourcesContent: [
' ONE.foo = function (bar) {\n' +
' return baz(bar);\n' +
' };',
' TWO.inc = function (n) {\n' +
' return n + 1;\n' +
' };'
],
sourceRoot: '/the/root',
mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
};
exports.emptyMap = {
version: 3,
file: 'min.js',
names: [],
sources: [],
mappings: ''
};
function assertMapping(generatedLine, generatedColumn, originalSource,
originalLine, originalColumn, name, bias, map, assert,
dontTestGenerated, dontTestOriginal) {
if (!dontTestOriginal) {
var origMapping = map.originalPositionFor({
line: generatedLine,
column: generatedColumn,
bias: bias
});
assert.equal(origMapping.name, name,
'Incorrect name, expected ' + JSON.stringify(name)
+ ', got ' + JSON.stringify(origMapping.name));
assert.equal(origMapping.line, originalLine,
'Incorrect line, expected ' + JSON.stringify(originalLine)
+ ', got ' + JSON.stringify(origMapping.line));
assert.equal(origMapping.column, originalColumn,
'Incorrect column, expected ' + JSON.stringify(originalColumn)
+ ', got ' + JSON.stringify(origMapping.column));
var expectedSource;
if (originalSource && map.sourceRoot && originalSource.indexOf(map.sourceRoot) === 0) {
expectedSource = originalSource;
} else if (originalSource) {
expectedSource = map.sourceRoot
? util.join(map.sourceRoot, originalSource)
: originalSource;
} else {
expectedSource = null;
}
assert.equal(origMapping.source, expectedSource,
'Incorrect source, expected ' + JSON.stringify(expectedSource)
+ ', got ' + JSON.stringify(origMapping.source));
}
if (!dontTestGenerated) {
var genMapping = map.generatedPositionFor({
source: originalSource,
line: originalLine,
column: originalColumn,
bias: bias
});
assert.equal(genMapping.line, generatedLine,
'Incorrect line, expected ' + JSON.stringify(generatedLine)
+ ', got ' + JSON.stringify(genMapping.line));
assert.equal(genMapping.column, generatedColumn,
'Incorrect column, expected ' + JSON.stringify(generatedColumn)
+ ', got ' + JSON.stringify(genMapping.column));
}
}
exports.assertMapping = assertMapping;
function assertEqualMaps(assert, actualMap, expectedMap) {
assert.equal(actualMap.version, expectedMap.version, "version mismatch");
assert.equal(actualMap.file, expectedMap.file, "file mismatch");
assert.equal(actualMap.names.length,
expectedMap.names.length,
"names length mismatch: " +
actualMap.names.join(", ") + " != " + expectedMap.names.join(", "));
for (var i = 0; i < actualMap.names.length; i++) {
assert.equal(actualMap.names[i],
expectedMap.names[i],
"names[" + i + "] mismatch: " +
actualMap.names.join(", ") + " != " + expectedMap.names.join(", "));
}
assert.equal(actualMap.sources.length,
expectedMap.sources.length,
"sources length mismatch: " +
actualMap.sources.join(", ") + " != " + expectedMap.sources.join(", "));
for (var i = 0; i < actualMap.sources.length; i++) {
assert.equal(actualMap.sources[i],
expectedMap.sources[i],
"sources[" + i + "] length mismatch: " +
actualMap.sources.join(", ") + " != " + expectedMap.sources.join(", "));
}
assert.equal(actualMap.sourceRoot,
expectedMap.sourceRoot,
"sourceRoot mismatch: " +
actualMap.sourceRoot + " != " + expectedMap.sourceRoot);
assert.equal(actualMap.mappings, expectedMap.mappings,
"mappings mismatch:\nActual: " + actualMap.mappings + "\nExpected: " + expectedMap.mappings);
if (actualMap.sourcesContent) {
assert.equal(actualMap.sourcesContent.length,
expectedMap.sourcesContent.length,
"sourcesContent length mismatch");
for (var i = 0; i < actualMap.sourcesContent.length; i++) {
assert.equal(actualMap.sourcesContent[i],
expectedMap.sourcesContent[i],
"sourcesContent[" + i + "] mismatch");
}
}
}
exports.assertEqualMaps = assertEqualMaps;
});
/* -*- Mode: js; js-indent-level: 2; -*- */
/*
* Copyright 2011 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE or:
* http://opensource.org/licenses/BSD-3-Clause
*/
define('lib/source-map/util', ['require', 'exports', 'module' , ], function(require, exports, module) {
/**
* This is a helper function for getting values from parameter/options
* objects.
*
* @param args The object we are extracting values from
* @param name The name of the property we are getting.
* @param defaultValue An optional value to return if the property is missing
* from the object. If this is not specified and the property is missing, an
* error will be thrown.
*/
function getArg(aArgs, aName, aDefaultValue) {
if (aName in aArgs) {
return aArgs[aName];
} else if (arguments.length === 3) {
return aDefaultValue;
} else {
throw new Error('"' + aName + '" is a required argument.');
}
}
exports.getArg = getArg;
var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
var dataUrlRegexp = /^data:.+\,.+$/;
function urlParse(aUrl) {
var match = aUrl.match(urlRegexp);
if (!match) {
return null;
}
return {
scheme: match[1],
auth: match[2],
host: match[3],
port: match[4],
path: match[5]
};
}
exports.urlParse = urlParse;
function urlGenerate(aParsedUrl) {
var url = '';
if (aParsedUrl.scheme) {
url += aParsedUrl.scheme + ':';
}
url += '//';
if (aParsedUrl.auth) {
url += aParsedUrl.auth + '@';
}
if (aParsedUrl.host) {
url += aParsedUrl.host;
}
if (aParsedUrl.port) {
url += ":" + aParsedUrl.port
}
if (aParsedUrl.path) {
url += aParsedUrl.path;
}
return url;
}
exports.urlGenerate = urlGenerate;
/**
* Normalizes a path, or the path portion of a URL:
*
* - Replaces consequtive slashes with one slash.
* - Removes unnecessary '.' parts.
* - Removes unnecessary '<dir>/..' parts.
*
* Based on code in the Node.js 'path' core module.
*
* @param aPath The path or url to normalize.
*/
function normalize(aPath) {
var path = aPath;
var url = urlParse(aPath);
if (url) {
if (!url.path) {
return aPath;
}
path = url.path;
}
var isAbsolute = exports.isAbsolute(path);
var parts = path.split(/\/+/);
for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
part = parts[i];
if (part === '.') {
parts.splice(i, 1);
} else if (part === '..') {
up++;
} else if (up > 0) {
if (part === '') {
// The first part is blank if the path is absolute. Trying to go
// above the root is a no-op. Therefore we can remove all '..' parts
// directly after the root.
parts.splice(i + 1, up);
up = 0;
} else {
parts.splice(i, 2);
up--;
}
}
}
path = parts.join('/');
if (path === '') {
path = isAbsolute ? '/' : '.';
}
if (url) {
url.path = path;
return urlGenerate(url);
}
return path;
}
exports.normalize = normalize;
/**
* Joins two paths/URLs.
*
* @param aRoot The root path or URL.
* @param aPath The path or URL to be joined with the root.
*
* - If aPath is a URL or a data URI, aPath is returned, unless aPath is a
* scheme-relative URL: Then the scheme of aRoot, if any, is prepended
* first.
* - Otherwise aPath is a path. If aRoot is a URL, then its path portion
* is updated with the result and aRoot is returned. Otherwise the result
* is returned.
* - If aPath is absolute, the result is aPath.
* - Otherwise the two paths are joined with a slash.
* - Joining for example 'http://' and 'www.example.com' is also supported.
*/
function join(aRoot, aPath) {
if (aRoot === "") {
aRoot = ".";
}
if (aPath === "") {
aPath = ".";
}
var aPathUrl = urlParse(aPath);
var aRootUrl = urlParse(aRoot);
if (aRootUrl) {
aRoot = aRootUrl.path || '/';
}
// `join(foo, '//www.example.org')`
if (aPathUrl && !aPathUrl.scheme) {
if (aRootUrl) {
aPathUrl.scheme = aRootUrl.scheme;
}
return urlGenerate(aPathUrl);
}
if (aPathUrl || aPath.match(dataUrlRegexp)) {
return aPath;
}
// `join('http://', 'www.example.com')`
if (aRootUrl && !aRootUrl.host && !aRootUrl.path) {
aRootUrl.host = aPath;
return urlGenerate(aRootUrl);
}
var joined = aPath.charAt(0) === '/'
? aPath
: normalize(aRoot.replace(/\/+$/, '') + '/' + aPath);
if (aRootUrl) {
aRootUrl.path = joined;
return urlGenerate(aRootUrl);
}
return joined;
}
exports.join = join;
exports.isAbsolute = function (aPath) {
return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp);
};
/**
* Make a path relative to a URL or another path.
*
* @param aRoot The root path or URL.
* @param aPath The path or URL to be made relative to aRoot.
*/
function relative(aRoot, aPath) {
if (aRoot === "") {
aRoot = ".";
}
aRoot = aRoot.replace(/\/$/, '');
// It is possible for the path to be above the root. In this case, simply
// checking whether the root is a prefix of the path won't work. Instead, we
// need to remove components from the root one by one, until either we find
// a prefix that fits, or we run out of components to remove.
var level = 0;
while (aPath.indexOf(aRoot + '/') !== 0) {
var index = aRoot.lastIndexOf("/");
if (index < 0) {
return aPath;
}
// If the only part of the root that is left is the scheme (i.e. http://,
// file:///, etc.), one or more slashes (/), or simply nothing at all, we
// have exhausted all components, so the path is not relative to the root.
aRoot = aRoot.slice(0, index);
if (aRoot.match(/^([^\/]+:\/)?\/*$/)) {
return aPath;
}
++level;
}
// Make sure we add a "../" for each component we removed from the root.
return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1);
}
exports.relative = relative;
/**
* Because behavior goes wacky when you set `__proto__` on objects, we
* have to prefix all the strings in our set with an arbitrary character.
*
* See https://github.com/mozilla/source-map/pull/31 and
* https://github.com/mozilla/source-map/issues/30
*
* @param String aStr
*/
function toSetString(aStr) {
return '$' + aStr;
}
exports.toSetString = toSetString;
function fromSetString(aStr) {
return aStr.substr(1);
}
exports.fromSetString = fromSetString;
/**
* Comparator between two mappings where the original positions are compared.
*
* Optionally pass in `true` as `onlyCompareGenerated` to consider two
* mappings with the same original source/line/column, but different generated
* line and column the same. Useful when searching for a mapping with a
* stubbed out mapping.
*/
function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
var cmp = mappingA.source - mappingB.source;
if (cmp !== 0) {
return cmp;
}
cmp = mappingA.originalLine - mappingB.originalLine;
if (cmp !== 0) {
return cmp;
}
cmp = mappingA.originalColumn - mappingB.originalColumn;
if (cmp !== 0 || onlyCompareOriginal) {
return cmp;
}
cmp = mappingA.generatedColumn - mappingB.generatedColumn;
if (cmp !== 0) {
return cmp;
}
cmp = mappingA.generatedLine - mappingB.generatedLine;
if (cmp !== 0) {
return cmp;
}
return mappingA.name - mappingB.name;
};
exports.compareByOriginalPositions = compareByOriginalPositions;
/**
* Comparator between two mappings with deflated source and name indices where
* the generated positions are compared.
*
* Optionally pass in `true` as `onlyCompareGenerated` to consider two
* mappings with the same generated line and column, but different
* source/name/original line and column the same. Useful when searching for a
* mapping with a stubbed out mapping.
*/
function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGenerated) {
var cmp = mappingA.generatedLine - mappingB.generatedLine;
if (cmp !== 0) {
return cmp;
}
cmp = mappingA.generatedColumn - mappingB.generatedColumn;
if (cmp !== 0 || onlyCompareGenerated) {
return cmp;
}
cmp = mappingA.source - mappingB.source;
if (cmp !== 0) {
return cmp;
}
cmp = mappingA.originalLine - mappingB.originalLine;
if (cmp !== 0) {
return cmp;
}
cmp = mappingA.originalColumn - mappingB.originalColumn;
if (cmp !== 0) {
return cmp;
}
return mappingA.name - mappingB.name;
};
exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;
function strcmp(aStr1, aStr2) {
if (aStr1 === aStr2) {
return 0;
}
if (aStr1 > aStr2) {
return 1;
}
return -1;
}
/**
* Comparator between two mappings with inflated source and name strings where
* the generated positions are compared.
*/
function compareByGeneratedPositionsInflated(mappingA, mappingB) {
var cmp = mappingA.generatedLine - mappingB.generatedLine;
if (cmp !== 0) {
return cmp;
}
cmp = mappingA.generatedColumn - mappingB.generatedColumn;
if (cmp !== 0) {
return cmp;
}
cmp = strcmp(mappingA.source, mappingB.source);
if (cmp !== 0) {
return cmp;
}
cmp = mappingA.originalLine - mappingB.originalLine;
if (cmp !== 0) {
return cmp;
}
cmp = mappingA.originalColumn - mappingB.originalColumn;
if (cmp !== 0) {
return cmp;
}
return strcmp(mappingA.name, mappingB.name);
};
exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated;
});
/* -*- Mode: js; js-indent-level: 2; -*- */
/*
* Copyright 2011 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE or:
* http://opensource.org/licenses/BSD-3-Clause
*/
function runSourceMapTests(modName, do_throw) {
let mod = require(modName);
let assert = require('test/source-map/assert');
let util = require('test/source-map/util');
assert.init(do_throw);
for (let k in mod) {
if (/^test/.test(k)) {
mod[k](assert, util);
}
}
}
this.runSourceMapTests = runSourceMapTests;

View File

@ -6,3 +6,13 @@ const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
function doesNotThrow(f) {
try {
f();
} catch(e) {
ok(false, e + e.stack);
}
}
var assert = this;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -2,12 +2,12 @@
tags = devtools
head = head_sourcemap.js
tail =
support-files = Utils.jsm
[test_util.js]
[test_source_node.js]
[test_source_map_generator.js]
[test_source_map_consumer.js]
[test_quick_sort.js]
[test_dog_fooding.js]
[test_binary_search.js]
[test_base64_vlq.js]