Merge m-c to inbound a=merge CLOSED TREE
|
@ -15,9 +15,9 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7b92615bdc97e5c675cd385ec68bc5e47e0c5288"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="55766f4d12f57b5e7289d068a81a3dc501cf10db"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="a42f56ffac1c52e5bbc82143a949feeed359d528"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
<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="7b92615bdc97e5c675cd385ec68bc5e47e0c5288"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="a42f56ffac1c52e5bbc82143a949feeed359d528"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="55766f4d12f57b5e7289d068a81a3dc501cf10db"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7b92615bdc97e5c675cd385ec68bc5e47e0c5288"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="a42f56ffac1c52e5bbc82143a949feeed359d528"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="55766f4d12f57b5e7289d068a81a3dc501cf10db"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ca2008ac50b163d31244ef9f036cb224f4f229b"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7b92615bdc97e5c675cd385ec68bc5e47e0c5288"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="55766f4d12f57b5e7289d068a81a3dc501cf10db"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="a42f56ffac1c52e5bbc82143a949feeed359d528"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
<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="7b92615bdc97e5c675cd385ec68bc5e47e0c5288"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="a42f56ffac1c52e5bbc82143a949feeed359d528"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="55766f4d12f57b5e7289d068a81a3dc501cf10db"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7b92615bdc97e5c675cd385ec68bc5e47e0c5288"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="55766f4d12f57b5e7289d068a81a3dc501cf10db"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="a42f56ffac1c52e5bbc82143a949feeed359d528"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7b92615bdc97e5c675cd385ec68bc5e47e0c5288"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="a42f56ffac1c52e5bbc82143a949feeed359d528"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="55766f4d12f57b5e7289d068a81a3dc501cf10db"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ca2008ac50b163d31244ef9f036cb224f4f229b"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "9d8c254abad2ceaad8f1ae22a18e0f36af5860c4",
|
||||
"revision": "ce7bf759bbf4ecdfa46b64cdccc16ccf7531178d",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
<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="7b92615bdc97e5c675cd385ec68bc5e47e0c5288"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="a42f56ffac1c52e5bbc82143a949feeed359d528"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="55766f4d12f57b5e7289d068a81a3dc501cf10db"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
<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="7b92615bdc97e5c675cd385ec68bc5e47e0c5288"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="a42f56ffac1c52e5bbc82143a949feeed359d528"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="55766f4d12f57b5e7289d068a81a3dc501cf10db"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7b92615bdc97e5c675cd385ec68bc5e47e0c5288"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="a42f56ffac1c52e5bbc82143a949feeed359d528"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="55766f4d12f57b5e7289d068a81a3dc501cf10db"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6ca2008ac50b163d31244ef9f036cb224f4f229b"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
<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="7b92615bdc97e5c675cd385ec68bc5e47e0c5288"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="a42f56ffac1c52e5bbc82143a949feeed359d528"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="55766f4d12f57b5e7289d068a81a3dc501cf10db"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
|
|
|
@ -1623,6 +1623,9 @@ pref("loop.CSP", "default-src 'self' about: file: chrome:; img-src 'self' data:
|
|||
#endif
|
||||
pref("loop.oauth.google.redirect_uri", "urn:ietf:wg:oauth:2.0:oob:auto");
|
||||
pref("loop.oauth.google.scope", "https://www.google.com/m8/feeds");
|
||||
pref("loop.rooms.enabled", false);
|
||||
pref("loop.fxa_oauth.tokendata", "");
|
||||
pref("loop.fxa_oauth.profile", "");
|
||||
|
||||
// serverURL to be assigned by services team
|
||||
pref("services.push.serverURL", "wss://push.services.mozilla.com/");
|
||||
|
|
|
@ -59,9 +59,3 @@
|
|||
<label id="pointerLock-cancel" value="&pointerLock.notification.message;"/>
|
||||
</popupnotificationcontent>
|
||||
</popupnotification>
|
||||
|
||||
#ifdef E10S_TESTING_ONLY
|
||||
<popupnotification id="enable-e10s-notification" hidden="true">
|
||||
<popupnotificationcontent orient="vertical"/>
|
||||
</popupnotification>
|
||||
#endif
|
||||
|
|
|
@ -461,8 +461,10 @@ this.GoogleImporter.prototype = {
|
|||
contact.org = [];
|
||||
contact.jobTitle = [];
|
||||
for (let [,orgNode] of Iterator(orgNodes)) {
|
||||
contact.org.push(orgNode.getElementsByTagNameNS(kNS_GD, "orgName")[0].firstChild.nodeValue);
|
||||
contact.jobTitle.push(orgNode.getElementsByTagNameNS(kNS_GD, "orgTitle")[0].firstChild.nodeValue);
|
||||
let orgElement = orgNode.getElementsByTagNameNS(kNS_GD, "orgName")[0];
|
||||
let titleElement = orgNode.getElementsByTagNameNS(kNS_GD, "orgTitle")[0];
|
||||
contact.org.push(orgElement ? orgElement.firstChild.nodeValue : "")
|
||||
contact.jobTitle.push(titleElement ? titleElement.firstChild.nodeValue : "");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -77,19 +77,27 @@ XPCOMUtils.defineLazyGetter(this, "log", () => {
|
|||
return new ConsoleAPI(consoleOptions);
|
||||
});
|
||||
|
||||
function setJSONPref(aName, aValue) {
|
||||
let value = !!aValue ? JSON.stringify(aValue) : "";
|
||||
Services.prefs.setCharPref(aName, value);
|
||||
}
|
||||
|
||||
function getJSONPref(aName) {
|
||||
let value = Services.prefs.getCharPref(aName);
|
||||
return !!value ? JSON.parse(value) : null;
|
||||
}
|
||||
|
||||
// The current deferred for the registration process. This is set if in progress
|
||||
// or the registration was successful. This is null if a registration attempt was
|
||||
// unsuccessful.
|
||||
let gRegisteredDeferred = null;
|
||||
let gPushHandler = null;
|
||||
let gHawkClient = null;
|
||||
let gLocalizedStrings = null;
|
||||
let gLocalizedStrings = null;
|
||||
let gInitializeTimer = null;
|
||||
let gFxAEnabled = true;
|
||||
let gFxAOAuthClientPromise = null;
|
||||
let gFxAOAuthClient = null;
|
||||
let gFxAOAuthTokenData = null;
|
||||
let gFxAOAuthProfile = null;
|
||||
let gErrors = new Map();
|
||||
|
||||
/**
|
||||
|
@ -306,6 +314,38 @@ let MozLoopServiceInternal = {
|
|||
return this.expiryTimeSeconds * 1000 > Date.now();
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves MozLoopService Firefox Accounts OAuth token.
|
||||
*
|
||||
* @return {Object} OAuth token
|
||||
*/
|
||||
get fxAOAuthTokenData() {
|
||||
return getJSONPref("loop.fxa_oauth.tokendata");
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets MozLoopService Firefox Accounts OAuth token.
|
||||
* If the tokenData is being cleared, will also clear the
|
||||
* profile since the profile is dependent on the token data.
|
||||
*
|
||||
* @param {Object} aTokenData OAuth token
|
||||
*/
|
||||
set fxAOAuthTokenData(aTokenData) {
|
||||
setJSONPref("loop.fxa_oauth.tokendata", aTokenData);
|
||||
if (!aTokenData) {
|
||||
this.fxAOAuthProfile = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets MozLoopService Firefox Accounts Profile data.
|
||||
*
|
||||
* @param {Object} aProfileData Profile data
|
||||
*/
|
||||
set fxAOAuthProfile(aProfileData) {
|
||||
setJSONPref("loop.fxa_oauth.profile", aProfileData);
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves MozLoopService "do not disturb" pref value.
|
||||
*
|
||||
|
@ -420,9 +460,8 @@ let MozLoopServiceInternal = {
|
|||
let result = gRegisteredDeferred.promise;
|
||||
|
||||
gPushHandler = mockPushHandler || MozLoopPushHandler;
|
||||
|
||||
gPushHandler.initialize(this.onPushRegistered.bind(this),
|
||||
this.onHandleNotification.bind(this));
|
||||
this.onHandleNotification.bind(this));
|
||||
|
||||
return result;
|
||||
},
|
||||
|
@ -575,12 +614,15 @@ let MozLoopServiceInternal = {
|
|||
if (!gRegisteredDeferred) {
|
||||
return;
|
||||
}
|
||||
gRegisteredDeferred.resolve();
|
||||
gRegisteredDeferred.resolve("registered to guest status");
|
||||
// No need to clear the promise here, everything was good, so we don't need
|
||||
// to re-register.
|
||||
}, (error) => {
|
||||
}, error => {
|
||||
log.error("Failed to register with Loop server: ", error);
|
||||
gRegisteredDeferred.reject(error.errno);
|
||||
// registerWithLoopServer may have already made this null.
|
||||
if (gRegisteredDeferred) {
|
||||
gRegisteredDeferred.reject(error);
|
||||
}
|
||||
gRegisteredDeferred = null;
|
||||
});
|
||||
},
|
||||
|
@ -616,6 +658,8 @@ let MozLoopServiceInternal = {
|
|||
|
||||
log.error("Failed to register with the loop server. Error: ", error);
|
||||
this.setError("registration", error);
|
||||
gRegisteredDeferred.reject(error);
|
||||
gRegisteredDeferred = null;
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
|
@ -1069,15 +1113,36 @@ let MozLoopServiceInternal = {
|
|||
};
|
||||
Object.freeze(MozLoopServiceInternal);
|
||||
|
||||
let gInitializeTimerFunc = () => {
|
||||
// Kick off the push notification service into registering after a timeout
|
||||
// this ensures we're not doing too much straight after the browser's finished
|
||||
let gInitializeTimerFunc = (deferredInitialization, mockPushHandler, mockWebSocket) => {
|
||||
// Kick off the push notification service into registering after a timeout.
|
||||
// This ensures we're not doing too much straight after the browser's finished
|
||||
// starting up.
|
||||
gInitializeTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
gInitializeTimer.initWithCallback(() => {
|
||||
MozLoopService.register();
|
||||
gInitializeTimer.initWithCallback(Task.async(function* initializationCallback() {
|
||||
yield MozLoopService.register(mockPushHandler, mockWebSocket).then(Task.async(function*() {
|
||||
if (!MozLoopServiceInternal.fxAOAuthTokenData) {
|
||||
log.debug("MozLoopService: Initialized without an already logged-in account");
|
||||
deferredInitialization.resolve("initialized to guest status");
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("MozLoopService: Initializing with already logged-in account");
|
||||
let registeredPromise =
|
||||
MozLoopServiceInternal.registerWithLoopServer(LOOP_SESSION_TYPE.FXA,
|
||||
gPushHandler.pushUrl);
|
||||
registeredPromise.then(() => {
|
||||
deferredInitialization.resolve("initialized to logged-in status");
|
||||
}, error => {
|
||||
log.debug("MozLoopService: error logging in using cached auth token");
|
||||
MozLoopServiceInternal.setError("login", error);
|
||||
deferredInitialization.reject("error logging in using cached auth token");
|
||||
});
|
||||
}), error => {
|
||||
log.debug("MozLoopService: Failure of initial registration", error);
|
||||
deferredInitialization.reject(error);
|
||||
});
|
||||
gInitializeTimer = null;
|
||||
},
|
||||
}),
|
||||
MozLoopServiceInternal.initialRegistrationDelayMilliseconds, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
};
|
||||
|
||||
|
@ -1094,9 +1159,10 @@ this.MozLoopService = {
|
|||
/**
|
||||
* Initialized the loop service, and starts registration with the
|
||||
* push and loop servers.
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
initialize: function() {
|
||||
|
||||
initialize: Task.async(function*(mockPushHandler, mockWebSocket) {
|
||||
// Do this here, rather than immediately after definition, so that we can
|
||||
// stub out API functions for unit testing
|
||||
Object.freeze(this);
|
||||
|
@ -1104,21 +1170,34 @@ this.MozLoopService = {
|
|||
// Don't do anything if loop is not enabled.
|
||||
if (!Services.prefs.getBoolPref("loop.enabled") ||
|
||||
Services.prefs.getBoolPref("loop.throttled")) {
|
||||
return;
|
||||
return Promise.reject("loop is not enabled");
|
||||
}
|
||||
|
||||
if (Services.prefs.getPrefType("loop.fxa.enabled") == Services.prefs.PREF_BOOL) {
|
||||
gFxAEnabled = Services.prefs.getBoolPref("loop.fxa.enabled");
|
||||
if (!gFxAEnabled) {
|
||||
this.logOutFromFxA();
|
||||
yield this.logOutFromFxA();
|
||||
}
|
||||
}
|
||||
|
||||
// If expiresTime is in the future then kick-off registration.
|
||||
if (MozLoopServiceInternal.urlExpiryTimeIsInFuture()) {
|
||||
gInitializeTimerFunc();
|
||||
// If expiresTime is not in the future and the user hasn't
|
||||
// previously authenticated then skip registration.
|
||||
if (!MozLoopServiceInternal.urlExpiryTimeIsInFuture() &&
|
||||
!MozLoopServiceInternal.fxAOAuthTokenData) {
|
||||
return Promise.resolve("registration not needed");
|
||||
}
|
||||
},
|
||||
|
||||
let deferredInitialization = Promise.defer();
|
||||
gInitializeTimerFunc(deferredInitialization, mockPushHandler, mockWebSocket);
|
||||
|
||||
return deferredInitialization.promise.catch(error => {
|
||||
if (typeof(error) == "object") {
|
||||
// This never gets cleared since there is no UI to recover. Only restarting will work.
|
||||
MozLoopServiceInternal.setError("initialization", error);
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* If we're operating the service in "soft start" mode, and this browser
|
||||
|
@ -1248,7 +1327,7 @@ this.MozLoopService = {
|
|||
* sooner, this function is a no-op; this ensures we always have the latest
|
||||
* expiry time for a url.
|
||||
*
|
||||
* This is used to deterimine whether or not we should be registering with the
|
||||
* This is used to determine whether or not we should be registering with the
|
||||
* push server on start.
|
||||
*
|
||||
* @param {Integer} expiryTimeSeconds The seconds since epoch of the expiry time
|
||||
|
@ -1305,8 +1384,16 @@ this.MozLoopService = {
|
|||
return gFxAEnabled;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the user profile, but only if there is
|
||||
* tokenData present. Without tokenData, the
|
||||
* profile is meaningless.
|
||||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
get userProfile() {
|
||||
return gFxAOAuthProfile;
|
||||
return getJSONPref("loop.fxa_oauth.tokendata") &&
|
||||
getJSONPref("loop.fxa_oauth.profile");
|
||||
},
|
||||
|
||||
get errors() {
|
||||
|
@ -1435,15 +1522,15 @@ this.MozLoopService = {
|
|||
* @return {Promise} that resolves when the FxA login flow is complete.
|
||||
*/
|
||||
logInToFxA: function() {
|
||||
log.debug("logInToFxA with gFxAOAuthTokenData:", !!gFxAOAuthTokenData);
|
||||
if (gFxAOAuthTokenData) {
|
||||
return Promise.resolve(gFxAOAuthTokenData);
|
||||
log.debug("logInToFxA with fxAOAuthTokenData:", !!MozLoopServiceInternal.fxAOAuthTokenData);
|
||||
if (MozLoopServiceInternal.fxAOAuthTokenData) {
|
||||
return Promise.resolve(MozLoopServiceInternal.fxAOAuthTokenData);
|
||||
}
|
||||
|
||||
return MozLoopServiceInternal.promiseFxAOAuthAuthorization().then(response => {
|
||||
return MozLoopServiceInternal.promiseFxAOAuthToken(response.code, response.state);
|
||||
}).then(tokenData => {
|
||||
gFxAOAuthTokenData = tokenData;
|
||||
MozLoopServiceInternal.fxAOAuthTokenData = tokenData;
|
||||
return tokenData;
|
||||
}).then(tokenData => {
|
||||
return gRegisteredDeferred.promise.then(Task.async(function*() {
|
||||
|
@ -1454,7 +1541,7 @@ this.MozLoopService = {
|
|||
}
|
||||
MozLoopServiceInternal.clearError("login");
|
||||
MozLoopServiceInternal.clearError("profile");
|
||||
return gFxAOAuthTokenData;
|
||||
return MozLoopServiceInternal.fxAOAuthTokenData;
|
||||
}));
|
||||
}).then(tokenData => {
|
||||
let client = new FxAccountsProfileClient({
|
||||
|
@ -1462,18 +1549,18 @@ this.MozLoopService = {
|
|||
token: tokenData.access_token
|
||||
});
|
||||
client.fetchProfile().then(result => {
|
||||
gFxAOAuthProfile = result;
|
||||
MozLoopServiceInternal.fxAOAuthProfile = result;
|
||||
MozLoopServiceInternal.notifyStatusChanged("login");
|
||||
}, error => {
|
||||
log.error("Failed to retrieve profile", error);
|
||||
this.setError("profile", error);
|
||||
gFxAOAuthProfile = null;
|
||||
MozLoopServiceInternal.fxAOAuthProfile = null;
|
||||
MozLoopServiceInternal.notifyStatusChanged();
|
||||
});
|
||||
return tokenData;
|
||||
}).catch(error => {
|
||||
gFxAOAuthTokenData = null;
|
||||
gFxAOAuthProfile = null;
|
||||
MozLoopServiceInternal.fxAOAuthTokenData = null;
|
||||
MozLoopServiceInternal.fxAOAuthProfile = null;
|
||||
throw error;
|
||||
}).catch((error) => {
|
||||
MozLoopServiceInternal.setError("login", error);
|
||||
|
@ -1498,8 +1585,8 @@ this.MozLoopService = {
|
|||
MozLoopServiceInternal.clearSessionToken(LOOP_SESSION_TYPE.FXA);
|
||||
}
|
||||
|
||||
gFxAOAuthTokenData = null;
|
||||
gFxAOAuthProfile = null;
|
||||
MozLoopServiceInternal.fxAOAuthTokenData = null;
|
||||
MozLoopServiceInternal.fxAOAuthProfile = null;
|
||||
|
||||
// Reset the client since the initial promiseFxAOAuthParameters() call is
|
||||
// what creates a new session.
|
||||
|
|
|
@ -14,6 +14,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
var sharedViews = loop.shared.views;
|
||||
var sharedModels = loop.shared.models;
|
||||
var sharedMixins = loop.shared.mixins;
|
||||
var sharedActions = loop.shared.actions;
|
||||
var Button = sharedViews.Button;
|
||||
var ButtonGroup = sharedViews.ButtonGroup;
|
||||
var ContactsList = loop.contacts.ContactsList;
|
||||
|
@ -21,12 +22,23 @@ loop.panel = (function(_, mozL10n) {
|
|||
var __ = mozL10n.get; // aliasing translation function as __ for concision
|
||||
|
||||
var TabView = React.createClass({displayName: 'TabView',
|
||||
getInitialState: function() {
|
||||
propTypes: {
|
||||
buttonsHidden: React.PropTypes.bool,
|
||||
// The selectedTab prop is used by the UI showcase.
|
||||
selectedTab: React.PropTypes.string
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
buttonsHidden: false,
|
||||
selectedTab: "call"
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {selectedTab: this.props.selectedTab};
|
||||
},
|
||||
|
||||
handleSelectTab: function(event) {
|
||||
var tabName = event.target.dataset.tabName;
|
||||
this.setState({selectedTab: tabName});
|
||||
|
@ -37,6 +49,10 @@ loop.panel = (function(_, mozL10n) {
|
|||
var tabButtons = [];
|
||||
var tabs = [];
|
||||
React.Children.forEach(this.props.children, function(tab, i) {
|
||||
// Filter out null tabs (eg. rooms when the feature is disabled)
|
||||
if (!tab) {
|
||||
return;
|
||||
}
|
||||
var tabName = tab.props.name;
|
||||
var isSelected = (this.state.selectedTab == tabName);
|
||||
if (!tab.props.hidden) {
|
||||
|
@ -442,6 +458,121 @@ loop.panel = (function(_, mozL10n) {
|
|||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Room list entry.
|
||||
*/
|
||||
var RoomEntry = React.createClass({displayName: 'RoomEntry',
|
||||
propTypes: {
|
||||
openRoom: React.PropTypes.func.isRequired,
|
||||
room: React.PropTypes.instanceOf(loop.store.Room).isRequired
|
||||
},
|
||||
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
return nextProps.room.ctime > this.props.room.ctime;
|
||||
},
|
||||
|
||||
handleClickRoom: function(event) {
|
||||
event.preventDefault();
|
||||
this.props.openRoom(this.props.room);
|
||||
},
|
||||
|
||||
_isActive: function() {
|
||||
// XXX bug 1074679 will implement this properly
|
||||
return this.props.room.currSize > 0;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var room = this.props.room;
|
||||
var roomClasses = React.addons.classSet({
|
||||
"room-entry": true,
|
||||
"room-active": this._isActive()
|
||||
});
|
||||
|
||||
return (
|
||||
React.DOM.div({className: roomClasses},
|
||||
React.DOM.h2(null,
|
||||
React.DOM.span({className: "room-notification"}),
|
||||
room.roomName
|
||||
),
|
||||
React.DOM.p(null,
|
||||
React.DOM.a({ref: "room", href: "#", onClick: this.handleClickRoom},
|
||||
room.roomUrl
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Room list.
|
||||
*/
|
||||
var RoomList = React.createClass({displayName: 'RoomList',
|
||||
mixins: [Backbone.Events],
|
||||
|
||||
propTypes: {
|
||||
store: React.PropTypes.instanceOf(loop.store.RoomListStore).isRequired,
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
rooms: React.PropTypes.array
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
var storeState = this.props.store.getStoreState();
|
||||
return {
|
||||
error: this.props.error || storeState.error,
|
||||
rooms: this.props.rooms || storeState.rooms,
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.listenTo(this.props.store, "change", this._onRoomListChanged);
|
||||
|
||||
this.props.dispatcher.dispatch(new sharedActions.GetAllRooms());
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
this.stopListening(this.props.store);
|
||||
},
|
||||
|
||||
_onRoomListChanged: function() {
|
||||
var storeState = this.props.store.getStoreState();
|
||||
this.setState({
|
||||
error: storeState.error,
|
||||
rooms: storeState.rooms
|
||||
});
|
||||
},
|
||||
|
||||
_getListHeading: function() {
|
||||
var numRooms = this.state.rooms.length;
|
||||
if (numRooms === 0) {
|
||||
return mozL10n.get("rooms_list_no_current_conversations");
|
||||
}
|
||||
return mozL10n.get("rooms_list_current_conversations", {num: numRooms});
|
||||
},
|
||||
|
||||
openRoom: function(room) {
|
||||
// XXX implement me; see bug 1074678
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (this.state.error) {
|
||||
// XXX Better end user reporting of errors.
|
||||
console.error(this.state.error);
|
||||
}
|
||||
|
||||
return (
|
||||
React.DOM.div({className: "room-list"},
|
||||
React.DOM.h1(null, this._getListHeading()),
|
||||
|
||||
this.state.rooms.map(function(room, i) {
|
||||
return RoomEntry({key: i, room: room, openRoom: this.openRoom});
|
||||
}, this)
|
||||
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Panel view.
|
||||
*/
|
||||
|
@ -453,6 +584,10 @@ loop.panel = (function(_, mozL10n) {
|
|||
callUrl: React.PropTypes.string,
|
||||
userProfile: React.PropTypes.object,
|
||||
showTabButtons: React.PropTypes.bool,
|
||||
selectedTab: React.PropTypes.string,
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
roomListStore:
|
||||
React.PropTypes.instanceOf(loop.store.RoomListStore).isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
|
@ -498,6 +633,22 @@ loop.panel = (function(_, mozL10n) {
|
|||
this.updateServiceErrors();
|
||||
},
|
||||
|
||||
/**
|
||||
* The rooms feature is hidden by default for now. Once it gets mainstream,
|
||||
* this method can be safely removed.
|
||||
*/
|
||||
_renderRoomsTab: function() {
|
||||
if (!navigator.mozLoop.getLoopBoolPref("rooms.enabled")) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
Tab({name: "rooms"},
|
||||
RoomList({dispatcher: this.props.dispatcher,
|
||||
store: this.props.roomListStore})
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
startForm: function(name, contact) {
|
||||
this.refs[name].initForm(contact);
|
||||
this.selectTab(name);
|
||||
|
@ -527,7 +678,8 @@ loop.panel = (function(_, mozL10n) {
|
|||
React.DOM.div(null,
|
||||
NotificationListView({notifications: this.props.notifications,
|
||||
clearOnDocumentHidden: true}),
|
||||
TabView({ref: "tabView", buttonsHidden: !this.state.userProfile && !this.props.showTabButtons},
|
||||
TabView({ref: "tabView", selectedTab: this.props.selectedTab,
|
||||
buttonsHidden: !this.state.userProfile && !this.props.showTabButtons},
|
||||
Tab({name: "call"},
|
||||
React.DOM.div({className: "content-area"},
|
||||
CallUrlResult({client: this.props.client,
|
||||
|
@ -536,6 +688,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
ToSView(null)
|
||||
)
|
||||
),
|
||||
this._renderRoomsTab(),
|
||||
Tab({name: "contacts"},
|
||||
ContactsList({selectTab: this.selectTab,
|
||||
startForm: this.startForm})
|
||||
|
@ -575,11 +728,19 @@ loop.panel = (function(_, mozL10n) {
|
|||
mozL10n.initialize(navigator.mozLoop);
|
||||
|
||||
var client = new loop.Client();
|
||||
var notifications = new sharedModels.NotificationCollection()
|
||||
var notifications = new sharedModels.NotificationCollection();
|
||||
var dispatcher = new loop.Dispatcher();
|
||||
var roomListStore = new loop.store.RoomListStore({
|
||||
mozLoop: navigator.mozLoop,
|
||||
dispatcher: dispatcher
|
||||
});
|
||||
|
||||
React.renderComponent(PanelView({
|
||||
client: client,
|
||||
notifications: notifications}), document.querySelector("#main"));
|
||||
notifications: notifications,
|
||||
roomListStore: roomListStore,
|
||||
dispatcher: dispatcher}
|
||||
), document.querySelector("#main"));
|
||||
|
||||
document.body.classList.add(loop.shared.utils.getTargetPlatform());
|
||||
document.body.setAttribute("dir", mozL10n.getDirection());
|
||||
|
@ -597,6 +758,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
AvailabilityDropdown: AvailabilityDropdown,
|
||||
CallUrlResult: CallUrlResult,
|
||||
PanelView: PanelView,
|
||||
RoomList: RoomList,
|
||||
SettingsDropdown: SettingsDropdown,
|
||||
ToSView: ToSView
|
||||
};
|
||||
|
|
|
@ -14,6 +14,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
var sharedViews = loop.shared.views;
|
||||
var sharedModels = loop.shared.models;
|
||||
var sharedMixins = loop.shared.mixins;
|
||||
var sharedActions = loop.shared.actions;
|
||||
var Button = sharedViews.Button;
|
||||
var ButtonGroup = sharedViews.ButtonGroup;
|
||||
var ContactsList = loop.contacts.ContactsList;
|
||||
|
@ -21,12 +22,23 @@ loop.panel = (function(_, mozL10n) {
|
|||
var __ = mozL10n.get; // aliasing translation function as __ for concision
|
||||
|
||||
var TabView = React.createClass({
|
||||
getInitialState: function() {
|
||||
propTypes: {
|
||||
buttonsHidden: React.PropTypes.bool,
|
||||
// The selectedTab prop is used by the UI showcase.
|
||||
selectedTab: React.PropTypes.string
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
buttonsHidden: false,
|
||||
selectedTab: "call"
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {selectedTab: this.props.selectedTab};
|
||||
},
|
||||
|
||||
handleSelectTab: function(event) {
|
||||
var tabName = event.target.dataset.tabName;
|
||||
this.setState({selectedTab: tabName});
|
||||
|
@ -37,6 +49,10 @@ loop.panel = (function(_, mozL10n) {
|
|||
var tabButtons = [];
|
||||
var tabs = [];
|
||||
React.Children.forEach(this.props.children, function(tab, i) {
|
||||
// Filter out null tabs (eg. rooms when the feature is disabled)
|
||||
if (!tab) {
|
||||
return;
|
||||
}
|
||||
var tabName = tab.props.name;
|
||||
var isSelected = (this.state.selectedTab == tabName);
|
||||
if (!tab.props.hidden) {
|
||||
|
@ -442,6 +458,121 @@ loop.panel = (function(_, mozL10n) {
|
|||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Room list entry.
|
||||
*/
|
||||
var RoomEntry = React.createClass({
|
||||
propTypes: {
|
||||
openRoom: React.PropTypes.func.isRequired,
|
||||
room: React.PropTypes.instanceOf(loop.store.Room).isRequired
|
||||
},
|
||||
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
return nextProps.room.ctime > this.props.room.ctime;
|
||||
},
|
||||
|
||||
handleClickRoom: function(event) {
|
||||
event.preventDefault();
|
||||
this.props.openRoom(this.props.room);
|
||||
},
|
||||
|
||||
_isActive: function() {
|
||||
// XXX bug 1074679 will implement this properly
|
||||
return this.props.room.currSize > 0;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var room = this.props.room;
|
||||
var roomClasses = React.addons.classSet({
|
||||
"room-entry": true,
|
||||
"room-active": this._isActive()
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={roomClasses}>
|
||||
<h2>
|
||||
<span className="room-notification" />
|
||||
{room.roomName}
|
||||
</h2>
|
||||
<p>
|
||||
<a ref="room" href="#" onClick={this.handleClickRoom}>
|
||||
{room.roomUrl}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Room list.
|
||||
*/
|
||||
var RoomList = React.createClass({
|
||||
mixins: [Backbone.Events],
|
||||
|
||||
propTypes: {
|
||||
store: React.PropTypes.instanceOf(loop.store.RoomListStore).isRequired,
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
rooms: React.PropTypes.array
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
var storeState = this.props.store.getStoreState();
|
||||
return {
|
||||
error: this.props.error || storeState.error,
|
||||
rooms: this.props.rooms || storeState.rooms,
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.listenTo(this.props.store, "change", this._onRoomListChanged);
|
||||
|
||||
this.props.dispatcher.dispatch(new sharedActions.GetAllRooms());
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
this.stopListening(this.props.store);
|
||||
},
|
||||
|
||||
_onRoomListChanged: function() {
|
||||
var storeState = this.props.store.getStoreState();
|
||||
this.setState({
|
||||
error: storeState.error,
|
||||
rooms: storeState.rooms
|
||||
});
|
||||
},
|
||||
|
||||
_getListHeading: function() {
|
||||
var numRooms = this.state.rooms.length;
|
||||
if (numRooms === 0) {
|
||||
return mozL10n.get("rooms_list_no_current_conversations");
|
||||
}
|
||||
return mozL10n.get("rooms_list_current_conversations", {num: numRooms});
|
||||
},
|
||||
|
||||
openRoom: function(room) {
|
||||
// XXX implement me; see bug 1074678
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (this.state.error) {
|
||||
// XXX Better end user reporting of errors.
|
||||
console.error(this.state.error);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="room-list">
|
||||
<h1>{this._getListHeading()}</h1>
|
||||
{
|
||||
this.state.rooms.map(function(room, i) {
|
||||
return <RoomEntry key={i} room={room} openRoom={this.openRoom} />;
|
||||
}, this)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Panel view.
|
||||
*/
|
||||
|
@ -453,6 +584,10 @@ loop.panel = (function(_, mozL10n) {
|
|||
callUrl: React.PropTypes.string,
|
||||
userProfile: React.PropTypes.object,
|
||||
showTabButtons: React.PropTypes.bool,
|
||||
selectedTab: React.PropTypes.string,
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
roomListStore:
|
||||
React.PropTypes.instanceOf(loop.store.RoomListStore).isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
|
@ -498,6 +633,22 @@ loop.panel = (function(_, mozL10n) {
|
|||
this.updateServiceErrors();
|
||||
},
|
||||
|
||||
/**
|
||||
* The rooms feature is hidden by default for now. Once it gets mainstream,
|
||||
* this method can be safely removed.
|
||||
*/
|
||||
_renderRoomsTab: function() {
|
||||
if (!navigator.mozLoop.getLoopBoolPref("rooms.enabled")) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Tab name="rooms">
|
||||
<RoomList dispatcher={this.props.dispatcher}
|
||||
store={this.props.roomListStore} />
|
||||
</Tab>
|
||||
);
|
||||
},
|
||||
|
||||
startForm: function(name, contact) {
|
||||
this.refs[name].initForm(contact);
|
||||
this.selectTab(name);
|
||||
|
@ -527,7 +678,8 @@ loop.panel = (function(_, mozL10n) {
|
|||
<div>
|
||||
<NotificationListView notifications={this.props.notifications}
|
||||
clearOnDocumentHidden={true} />
|
||||
<TabView ref="tabView" buttonsHidden={!this.state.userProfile && !this.props.showTabButtons}>
|
||||
<TabView ref="tabView" selectedTab={this.props.selectedTab}
|
||||
buttonsHidden={!this.state.userProfile && !this.props.showTabButtons}>
|
||||
<Tab name="call">
|
||||
<div className="content-area">
|
||||
<CallUrlResult client={this.props.client}
|
||||
|
@ -536,6 +688,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
<ToSView />
|
||||
</div>
|
||||
</Tab>
|
||||
{this._renderRoomsTab()}
|
||||
<Tab name="contacts">
|
||||
<ContactsList selectTab={this.selectTab}
|
||||
startForm={this.startForm} />
|
||||
|
@ -575,11 +728,19 @@ loop.panel = (function(_, mozL10n) {
|
|||
mozL10n.initialize(navigator.mozLoop);
|
||||
|
||||
var client = new loop.Client();
|
||||
var notifications = new sharedModels.NotificationCollection()
|
||||
var notifications = new sharedModels.NotificationCollection();
|
||||
var dispatcher = new loop.Dispatcher();
|
||||
var roomListStore = new loop.store.RoomListStore({
|
||||
mozLoop: navigator.mozLoop,
|
||||
dispatcher: dispatcher
|
||||
});
|
||||
|
||||
React.renderComponent(<PanelView
|
||||
client={client}
|
||||
notifications={notifications} />, document.querySelector("#main"));
|
||||
notifications={notifications}
|
||||
roomListStore={roomListStore}
|
||||
dispatcher={dispatcher}
|
||||
/>, document.querySelector("#main"));
|
||||
|
||||
document.body.classList.add(loop.shared.utils.getTargetPlatform());
|
||||
document.body.setAttribute("dir", mozL10n.getDirection());
|
||||
|
@ -597,6 +758,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
AvailabilityDropdown: AvailabilityDropdown,
|
||||
CallUrlResult: CallUrlResult,
|
||||
PanelView: PanelView,
|
||||
RoomList: RoomList,
|
||||
SettingsDropdown: SettingsDropdown,
|
||||
ToSView: ToSView
|
||||
};
|
||||
|
|
|
@ -25,6 +25,10 @@
|
|||
<script type="text/javascript" src="loop/shared/js/models.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/mixins.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/views.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/validate.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/actions.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/dispatcher.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/roomListStore.js"></script>
|
||||
<script type="text/javascript" src="loop/js/client.js"></script>
|
||||
<script type="text/javascript;version=1.8" src="loop/js/contacts.js"></script>
|
||||
<script type="text/javascript" src="loop/js/panel.js"></script>
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
body {
|
||||
background: none;
|
||||
}
|
||||
|
||||
/* Panel styles */
|
||||
|
||||
.panel {
|
||||
|
@ -26,7 +30,6 @@
|
|||
flex-direction: row;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
background-color: #fbfbfb;
|
||||
color: #000;
|
||||
border-top-right-radius: 2px;
|
||||
border-top-left-radius: 2px;
|
||||
|
@ -120,6 +123,70 @@
|
|||
box-shadow: 0 0 4px #c43c3e;
|
||||
}
|
||||
|
||||
/* Rooms */
|
||||
.room-list {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.room-list > h1 {
|
||||
font-weight: bold;
|
||||
color: #999;
|
||||
padding: .5rem 1rem;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.room-list > .room-entry {
|
||||
padding: 1rem 1rem 0 .5rem;
|
||||
}
|
||||
|
||||
.room-list > .room-entry > h2 {
|
||||
font-size: .85rem;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.room-list > .room-entry.room-active > h2 {
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.room-list > .room-entry > h2 > .room-notification {
|
||||
display: inline-block;
|
||||
background: transparent;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
margin-right: .3rem;
|
||||
}
|
||||
|
||||
.room-list > .room-entry.room-active > h2 > .room-notification {
|
||||
background-color: #00a0ec;
|
||||
}
|
||||
|
||||
.room-list > .room-entry:hover {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
.room-list > .room-entry:not(:last-child) {
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.room-list > .room-entry > p {
|
||||
margin: 0;
|
||||
padding: .2em 0 1rem .8rem;
|
||||
}
|
||||
|
||||
.room-list > .room-entry > p > a {
|
||||
color: #777;
|
||||
opacity: .5;
|
||||
transition: opacity .1s ease-in-out 0s;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.room-list > .room-entry > p > a:hover {
|
||||
opacity: 1;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
|
||||
.button-group {
|
||||
|
|
|
@ -118,6 +118,13 @@ loop.shared.actions = (function() {
|
|||
type: String,
|
||||
// Whether or not to enable the stream.
|
||||
enabled: Boolean
|
||||
}),
|
||||
|
||||
/**
|
||||
* Retrieves room list.
|
||||
* XXX: should move to some roomActions module - refs bug 1079284
|
||||
*/
|
||||
GetAllRooms: Action.define("getAllRooms", {
|
||||
})
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
/* global loop:true */
|
||||
|
||||
var loop = loop || {};
|
||||
loop.store = (function() {
|
||||
loop.store = loop.store || {};
|
||||
|
||||
loop.store.ConversationStore = (function() {
|
||||
var sharedActions = loop.shared.actions;
|
||||
var CALL_TYPES = loop.shared.utils.CALL_TYPES;
|
||||
|
||||
|
@ -14,7 +15,7 @@ loop.store = (function() {
|
|||
* Websocket states taken from:
|
||||
* https://docs.services.mozilla.com/loop/apis.html#call-progress-state-change-progress
|
||||
*/
|
||||
var WS_STATES = {
|
||||
var WS_STATES = loop.store.WS_STATES = {
|
||||
// The call is starting, and the remote party is not yet being alerted.
|
||||
INIT: "init",
|
||||
// The called party is being alerted.
|
||||
|
@ -31,7 +32,7 @@ loop.store = (function() {
|
|||
CONNECTED: "connected"
|
||||
};
|
||||
|
||||
var CALL_STATES = {
|
||||
var CALL_STATES = loop.store.CALL_STATES = {
|
||||
// The initial state of the view.
|
||||
INIT: "cs-init",
|
||||
// The store is gathering the call data from the server.
|
||||
|
@ -52,7 +53,6 @@ loop.store = (function() {
|
|||
TERMINATED: "cs-terminated"
|
||||
};
|
||||
|
||||
|
||||
var ConversationStore = Backbone.Model.extend({
|
||||
defaults: {
|
||||
// The current state of the call
|
||||
|
@ -402,9 +402,5 @@ loop.store = (function() {
|
|||
}
|
||||
});
|
||||
|
||||
return {
|
||||
CALL_STATES: CALL_STATES,
|
||||
ConversationStore: ConversationStore,
|
||||
WS_STATES: WS_STATES
|
||||
};
|
||||
return ConversationStore;
|
||||
})();
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* global loop:true */
|
||||
|
||||
var loop = loop || {};
|
||||
loop.store = loop.store || {};
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Room validation schema. See validate.js.
|
||||
* @type {Object}
|
||||
*/
|
||||
var roomSchema = {
|
||||
roomToken: String,
|
||||
roomUrl: String,
|
||||
roomName: String,
|
||||
maxSize: Number,
|
||||
currSize: Number,
|
||||
ctime: Number
|
||||
};
|
||||
|
||||
/**
|
||||
* Temporary sample raw room list data.
|
||||
* XXX Should be removed when we plug the real mozLoop API for rooms.
|
||||
* See bug 1074664.
|
||||
* @type {Array}
|
||||
*/
|
||||
var temporaryRawRoomList = [{
|
||||
roomToken: "_nxD4V4FflQ",
|
||||
roomUrl: "http://sample/_nxD4V4FflQ",
|
||||
roomName: "First Room Name",
|
||||
maxSize: 2,
|
||||
currSize: 0,
|
||||
ctime: 1405517546
|
||||
}, {
|
||||
roomToken: "QzBbvGmIZWU",
|
||||
roomUrl: "http://sample/QzBbvGmIZWU",
|
||||
roomName: "Second Room Name",
|
||||
maxSize: 2,
|
||||
currSize: 0,
|
||||
ctime: 1405517418
|
||||
}, {
|
||||
roomToken: "3jKS_Els9IU",
|
||||
roomUrl: "http://sample/3jKS_Els9IU",
|
||||
roomName: "Third Room Name",
|
||||
maxSize: 3,
|
||||
clientMaxSize: 2,
|
||||
currSize: 1,
|
||||
ctime: 1405518241
|
||||
}];
|
||||
|
||||
/**
|
||||
* Room type. Basically acts as a typed object constructor.
|
||||
*
|
||||
* @param {Object} values Room property values.
|
||||
*/
|
||||
function Room(values) {
|
||||
var validatedData = new loop.validate.Validator(roomSchema || {})
|
||||
.validate(values || {});
|
||||
for (var prop in validatedData) {
|
||||
this[prop] = validatedData[prop];
|
||||
}
|
||||
}
|
||||
|
||||
loop.store.Room = Room;
|
||||
|
||||
/**
|
||||
* Room store.
|
||||
*
|
||||
* Options:
|
||||
* - {loop.Dispatcher} dispatcher The dispatcher for dispatching actions and
|
||||
* registering to consume actions.
|
||||
* - {mozLoop} mozLoop The MozLoop API object.
|
||||
*
|
||||
* @extends {Backbone.Events}
|
||||
* @param {Object} options Options object.
|
||||
*/
|
||||
function RoomListStore(options) {
|
||||
options = options || {};
|
||||
this.storeState = {error: null, rooms: []};
|
||||
|
||||
if (!options.dispatcher) {
|
||||
throw new Error("Missing option dispatcher");
|
||||
}
|
||||
this.dispatcher = options.dispatcher;
|
||||
|
||||
if (!options.mozLoop) {
|
||||
throw new Error("Missing option mozLoop");
|
||||
}
|
||||
this.mozLoop = options.mozLoop;
|
||||
|
||||
this.dispatcher.register(this, [
|
||||
"getAllRooms",
|
||||
"openRoom"
|
||||
]);
|
||||
}
|
||||
|
||||
RoomListStore.prototype = _.extend({
|
||||
/**
|
||||
* Retrieves current store state.
|
||||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
getStoreState: function() {
|
||||
return this.storeState;
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates store states and trigger a "change" event.
|
||||
*
|
||||
* @param {Object} state The new store state.
|
||||
*/
|
||||
setStoreState: function(state) {
|
||||
this.storeState = state;
|
||||
this.trigger("change");
|
||||
},
|
||||
|
||||
/**
|
||||
* Proxy to navigator.mozLoop.rooms.getAll.
|
||||
* XXX Could probably be removed when bug 1074664 lands.
|
||||
*
|
||||
* @param {Function} cb Callback(error, roomList)
|
||||
*/
|
||||
_fetchRoomList: function(cb) {
|
||||
// Faking this.mozLoop.rooms until it's available; bug 1074664.
|
||||
if (!this.mozLoop.hasOwnProperty("rooms")) {
|
||||
cb(null, temporaryRawRoomList);
|
||||
return;
|
||||
}
|
||||
this.mozLoop.rooms.getAll(cb);
|
||||
},
|
||||
|
||||
/**
|
||||
* Maps and sorts the raw room list received from the mozLoop API.
|
||||
*
|
||||
* @param {Array} rawRoomList Raw room list.
|
||||
* @return {Array}
|
||||
*/
|
||||
_processRawRoomList: function(rawRoomList) {
|
||||
if (!rawRoomList) {
|
||||
return [];
|
||||
}
|
||||
return rawRoomList
|
||||
.map(function(rawRoom) {
|
||||
return new Room(rawRoom);
|
||||
})
|
||||
.slice()
|
||||
.sort(function(a, b) {
|
||||
return b.ctime - a.ctime;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Gather the list of all available rooms from the MozLoop API.
|
||||
*/
|
||||
getAllRooms: function() {
|
||||
this._fetchRoomList(function(err, rawRoomList) {
|
||||
this.setStoreState({
|
||||
error: err,
|
||||
rooms: this._processRawRoomList(rawRoomList)
|
||||
});
|
||||
}.bind(this));
|
||||
}
|
||||
}, Backbone.Events);
|
||||
|
||||
loop.store.RoomListStore = RoomListStore;
|
||||
})();
|
|
@ -55,6 +55,7 @@ browser.jar:
|
|||
# Shared scripts
|
||||
content/browser/loop/shared/js/actions.js (content/shared/js/actions.js)
|
||||
content/browser/loop/shared/js/conversationStore.js (content/shared/js/conversationStore.js)
|
||||
content/browser/loop/shared/js/roomListStore.js (content/shared/js/roomListStore.js)
|
||||
content/browser/loop/shared/js/dispatcher.js (content/shared/js/dispatcher.js)
|
||||
content/browser/loop/shared/js/feedbackApiClient.js (content/shared/js/feedbackApiClient.js)
|
||||
content/browser/loop/shared/js/models.js (content/shared/js/models.js)
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
<script src="../../content/shared/js/validate.js"></script>
|
||||
<script src="../../content/shared/js/dispatcher.js"></script>
|
||||
<script src="../../content/shared/js/otSdkDriver.js"></script>
|
||||
<script src="../../content/shared/js/roomListStore.js"></script>
|
||||
<script src="../../content/js/client.js"></script>
|
||||
<script src="../../content/js/conversationViews.js"></script>
|
||||
<script src="../../content/js/conversation.js"></script>
|
||||
|
|
|
@ -7,13 +7,14 @@
|
|||
|
||||
var expect = chai.expect;
|
||||
var TestUtils = React.addons.TestUtils;
|
||||
var sharedActions = loop.shared.actions;
|
||||
|
||||
describe("loop.panel", function() {
|
||||
"use strict";
|
||||
|
||||
var sandbox, notifications, fakeXHR, requests = [];
|
||||
|
||||
beforeEach(function() {
|
||||
beforeEach(function(done) {
|
||||
sandbox = sinon.sandbox.create();
|
||||
fakeXHR = sandbox.useFakeXMLHttpRequest();
|
||||
requests = [];
|
||||
|
@ -32,8 +33,12 @@ describe("loop.panel", function() {
|
|||
get locale() {
|
||||
return "en-US";
|
||||
},
|
||||
getLoopBoolPref: sandbox.stub(),
|
||||
setLoopCharPref: sandbox.stub(),
|
||||
getLoopCharPref: sandbox.stub().returns("unseen"),
|
||||
getPluralForm: function() {
|
||||
return "fakeText";
|
||||
},
|
||||
copyString: sandbox.stub(),
|
||||
noteCallUrlExpiry: sinon.spy(),
|
||||
composeEmail: sinon.spy(),
|
||||
|
@ -47,6 +52,8 @@ describe("loop.panel", function() {
|
|||
};
|
||||
|
||||
document.mozL10n.initialize(navigator.mozLoop);
|
||||
// XXX prevent a race whenever mozL10n hasn't been initialized yet
|
||||
setTimeout(done, 0);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
|
@ -126,7 +133,7 @@ describe("loop.panel", function() {
|
|||
});
|
||||
|
||||
describe("loop.panel.PanelView", function() {
|
||||
var fakeClient, callUrlData, view, callTab, contactsTab;
|
||||
var fakeClient, dispatcher, roomListStore, callUrlData;
|
||||
|
||||
beforeEach(function() {
|
||||
callUrlData = {
|
||||
|
@ -140,31 +147,94 @@ describe("loop.panel", function() {
|
|||
}
|
||||
};
|
||||
|
||||
view = TestUtils.renderIntoDocument(loop.panel.PanelView({
|
||||
dispatcher = new loop.Dispatcher();
|
||||
roomListStore = new loop.store.RoomListStore({
|
||||
dispatcher: dispatcher,
|
||||
mozLoop: navigator.mozLoop
|
||||
});
|
||||
});
|
||||
|
||||
function createTestPanelView() {
|
||||
return TestUtils.renderIntoDocument(loop.panel.PanelView({
|
||||
notifications: notifications,
|
||||
client: fakeClient,
|
||||
showTabButtons: true,
|
||||
dispatcher: dispatcher,
|
||||
roomListStore: roomListStore
|
||||
}));
|
||||
|
||||
[callTab, contactsTab] =
|
||||
TestUtils.scryRenderedDOMComponentsWithClass(view, "tab");
|
||||
});
|
||||
}
|
||||
|
||||
describe('TabView', function() {
|
||||
it("should select contacts tab when clicking tab button", function() {
|
||||
TestUtils.Simulate.click(
|
||||
view.getDOMNode().querySelector('li[data-tab-name="contacts"]'));
|
||||
var view, callTab, roomsTab, contactsTab;
|
||||
|
||||
expect(contactsTab.getDOMNode().classList.contains("selected"))
|
||||
.to.be.true;
|
||||
describe("loop.rooms.enabled on", function() {
|
||||
beforeEach(function() {
|
||||
navigator.mozLoop.getLoopBoolPref = function(pref) {
|
||||
if (pref === "rooms.enabled") {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
view = createTestPanelView();
|
||||
|
||||
[callTab, roomsTab, contactsTab] =
|
||||
TestUtils.scryRenderedDOMComponentsWithClass(view, "tab");
|
||||
});
|
||||
|
||||
it("should select contacts tab when clicking tab button", function() {
|
||||
TestUtils.Simulate.click(
|
||||
view.getDOMNode().querySelector("li[data-tab-name=\"contacts\"]"));
|
||||
|
||||
expect(contactsTab.getDOMNode().classList.contains("selected"))
|
||||
.to.be.true;
|
||||
});
|
||||
|
||||
it("should select rooms tab when clicking tab button", function() {
|
||||
TestUtils.Simulate.click(
|
||||
view.getDOMNode().querySelector("li[data-tab-name=\"rooms\"]"));
|
||||
|
||||
expect(roomsTab.getDOMNode().classList.contains("selected"))
|
||||
.to.be.true;
|
||||
});
|
||||
|
||||
it("should select call tab when clicking tab button", function() {
|
||||
TestUtils.Simulate.click(
|
||||
view.getDOMNode().querySelector("li[data-tab-name=\"call\"]"));
|
||||
|
||||
expect(callTab.getDOMNode().classList.contains("selected"))
|
||||
.to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
it("should select call tab when clicking tab button", function() {
|
||||
TestUtils.Simulate.click(
|
||||
view.getDOMNode().querySelector('li[data-tab-name="call"]'));
|
||||
describe("loop.rooms.enabled off", function() {
|
||||
beforeEach(function() {
|
||||
navigator.mozLoop.getLoopBoolPref = function(pref) {
|
||||
if (pref === "rooms.enabled") {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
expect(callTab.getDOMNode().classList.contains("selected"))
|
||||
.to.be.true;
|
||||
view = createTestPanelView();
|
||||
|
||||
[callTab, contactsTab] =
|
||||
TestUtils.scryRenderedDOMComponentsWithClass(view, "tab");
|
||||
});
|
||||
|
||||
it("should select contacts tab when clicking tab button", function() {
|
||||
TestUtils.Simulate.click(
|
||||
view.getDOMNode().querySelector("li[data-tab-name=\"contacts\"]"));
|
||||
|
||||
expect(contactsTab.getDOMNode().classList.contains("selected"))
|
||||
.to.be.true;
|
||||
});
|
||||
|
||||
it("should select call tab when clicking tab button", function() {
|
||||
TestUtils.Simulate.click(
|
||||
view.getDOMNode().querySelector("li[data-tab-name=\"call\"]"));
|
||||
|
||||
expect(callTab.getDOMNode().classList.contains("selected"))
|
||||
.to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -174,6 +244,8 @@ describe("loop.panel", function() {
|
|||
navigator.mozLoop.loggedInToFxA = false;
|
||||
navigator.mozLoop.logInToFxA = sandbox.stub();
|
||||
|
||||
var view = createTestPanelView();
|
||||
|
||||
TestUtils.Simulate.click(
|
||||
view.getDOMNode().querySelector(".signin-link a"));
|
||||
|
||||
|
@ -193,8 +265,6 @@ describe("loop.panel", function() {
|
|||
});
|
||||
|
||||
describe("SettingsDropdown", function() {
|
||||
var view;
|
||||
|
||||
beforeEach(function() {
|
||||
navigator.mozLoop.logInToFxA = sandbox.stub();
|
||||
navigator.mozLoop.logOutFromFxA = sandbox.stub();
|
||||
|
@ -288,6 +358,8 @@ describe("loop.panel", function() {
|
|||
|
||||
describe("#render", function() {
|
||||
it("should render a ToSView", function() {
|
||||
var view = createTestPanelView();
|
||||
|
||||
TestUtils.findRenderedComponentWithType(view, loop.panel.ToSView);
|
||||
});
|
||||
});
|
||||
|
@ -550,6 +622,34 @@ describe("loop.panel", function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe("loop.panel.RoomList", function() {
|
||||
var roomListStore, dispatcher;
|
||||
|
||||
beforeEach(function() {
|
||||
dispatcher = new loop.Dispatcher();
|
||||
roomListStore = new loop.store.RoomListStore({
|
||||
dispatcher: dispatcher,
|
||||
mozLoop: navigator.mozLoop
|
||||
});
|
||||
});
|
||||
|
||||
function createTestComponent() {
|
||||
return TestUtils.renderIntoDocument(loop.panel.RoomList({
|
||||
store: roomListStore,
|
||||
dispatcher: dispatcher
|
||||
}));
|
||||
}
|
||||
|
||||
it("should dispatch a GetAllRooms action on mount", function() {
|
||||
var dispatch = sandbox.stub(dispatcher, "dispatch");
|
||||
|
||||
createTestComponent();
|
||||
|
||||
sinon.assert.calledOnce(dispatch);
|
||||
sinon.assert.calledWithExactly(dispatch, new sharedActions.GetAllRooms());
|
||||
});
|
||||
});
|
||||
|
||||
describe('loop.panel.ToSView', function() {
|
||||
|
||||
it("should render when the value of loop.seenToS is not set", function() {
|
||||
|
|
|
@ -131,12 +131,12 @@ class Test1BrowserCall(MarionetteTestCase):
|
|||
self.marionette.set_context("chrome")
|
||||
button = self.marionette.find_element(By.CLASS_NAME, "btn-hangup")
|
||||
|
||||
# XXX For whatever reason, the click doesn't take effect unless we
|
||||
# wait for a bit (even if we wait for the element to actually be
|
||||
# displayed first, which we're not currently bothering with). It's
|
||||
# not entirely clear whether the click is being delivered in this case,
|
||||
# or whether there's a Marionette bug here.
|
||||
sleep(2)
|
||||
# XXX bug 1080095 For whatever reason, the click doesn't take effect
|
||||
# unless we wait for a bit (even if we wait for the element to
|
||||
# actually be displayed first, which we're not currently bothering
|
||||
# with). It's not entirely clear whether the click is being
|
||||
# delivered in this case, or whether there's a Marionette bug here.
|
||||
sleep(5)
|
||||
button.click()
|
||||
|
||||
# check that the feedback form is displayed
|
||||
|
|
|
@ -7,11 +7,6 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
gFxAOAuthTokenData,
|
||||
gFxAOAuthProfile,
|
||||
} = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
|
||||
|
||||
const BASE_URL = "http://mochi.test:8888/browser/browser/components/loop/test/mochitest/loop_fxa.sjs?";
|
||||
|
||||
function* checkFxA401() {
|
||||
|
@ -211,6 +206,8 @@ add_task(function* registrationWithInvalidState() {
|
|||
},
|
||||
error => {
|
||||
is(error.code, 400, "Check error code");
|
||||
checkFxAOAuthTokenData(null);
|
||||
is(MozLoopService.userProfile, null, "Profile should be empty after invalid login");
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -232,6 +229,8 @@ add_task(function* registrationWith401() {
|
|||
},
|
||||
error => {
|
||||
is(error.code, 401, "Check error code");
|
||||
checkFxAOAuthTokenData(null);
|
||||
is(MozLoopService.userProfile, null, "Profile should be empty after invalid login");
|
||||
});
|
||||
|
||||
yield checkFxA401();
|
||||
|
@ -321,7 +320,7 @@ add_task(function* loginWithParams401() {
|
|||
},
|
||||
error => {
|
||||
ise(error.code, 401, "Check error code");
|
||||
ise(gFxAOAuthTokenData, null, "Check there is no saved token data");
|
||||
checkFxAOAuthTokenData(null);
|
||||
});
|
||||
|
||||
yield checkFxA401();
|
||||
|
@ -387,7 +386,7 @@ add_task(function* loginWithRegistration401() {
|
|||
},
|
||||
error => {
|
||||
ise(error.code, 401, "Check error code");
|
||||
ise(gFxAOAuthTokenData, null, "Check there is no saved token data");
|
||||
checkFxAOAuthTokenData(null);
|
||||
});
|
||||
|
||||
yield checkFxA401();
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
registerCleanupFunction(function*() {
|
||||
MozLoopService.doNotDisturb = false;
|
||||
setInternalLoopGlobal("gFxAOAuthProfile", null);
|
||||
MozLoopServiceInternal.fxAOAuthProfile = null;
|
||||
yield MozLoopServiceInternal.clearError("testing");
|
||||
});
|
||||
|
||||
|
@ -25,7 +25,8 @@ add_task(function* test_doNotDisturb_with_login() {
|
|||
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state");
|
||||
yield MozLoopService.doNotDisturb = true;
|
||||
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "disabled", "Check button is in disabled state");
|
||||
setInternalLoopGlobal("gFxAOAuthProfile", {email: "test@example.com", uid: "abcd1234"});
|
||||
MozLoopServiceInternal.fxAOAuthTokenData = {token_type:"bearer",access_token:"1bad3e44b12f77a88fe09f016f6a37c42e40f974bc7a8b432bb0d2f0e37e1752",scope:"profile"};
|
||||
MozLoopServiceInternal.fxAOAuthProfile = {email: "test@example.com", uid: "abcd1234"};
|
||||
yield MozLoopServiceInternal.notifyStatusChanged("login");
|
||||
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "active", "Check button is in active state");
|
||||
yield loadLoopPanel();
|
||||
|
@ -34,7 +35,7 @@ add_task(function* test_doNotDisturb_with_login() {
|
|||
loopPanel.hidePopup();
|
||||
yield MozLoopService.doNotDisturb = false;
|
||||
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state");
|
||||
setInternalLoopGlobal("gFxAOAuthProfile", null);
|
||||
MozLoopServiceInternal.fxAOAuthTokenData = null;
|
||||
yield MozLoopServiceInternal.notifyStatusChanged();
|
||||
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state");
|
||||
});
|
||||
|
@ -51,26 +52,27 @@ add_task(function* test_error_with_login() {
|
|||
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state");
|
||||
yield MozLoopServiceInternal.setError("testing", {});
|
||||
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "error", "Check button is in error state");
|
||||
setInternalLoopGlobal("gFxAOAuthProfile", {email: "test@example.com", uid: "abcd1234"});
|
||||
MozLoopServiceInternal.fxAOAuthProfile = {email: "test@example.com", uid: "abcd1234"};
|
||||
MozLoopServiceInternal.notifyStatusChanged("login");
|
||||
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "error", "Check button is in error state");
|
||||
yield MozLoopServiceInternal.clearError("testing");
|
||||
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state");
|
||||
setInternalLoopGlobal("gFxAOAuthProfile", null);
|
||||
MozLoopServiceInternal.fxAOAuthProfile = null;
|
||||
MozLoopServiceInternal.notifyStatusChanged();
|
||||
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state");
|
||||
});
|
||||
|
||||
add_task(function* test_active() {
|
||||
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state");
|
||||
setInternalLoopGlobal("gFxAOAuthProfile", {email: "test@example.com", uid: "abcd1234"});
|
||||
MozLoopServiceInternal.fxAOAuthTokenData = {token_type:"bearer",access_token:"1bad3e44b12f77a88fe09f016f6a37c42e40f974bc7a8b432bb0d2f0e37e1752",scope:"profile"};
|
||||
MozLoopServiceInternal.fxAOAuthProfile = {email: "test@example.com", uid: "abcd1234"};
|
||||
yield MozLoopServiceInternal.notifyStatusChanged("login");
|
||||
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "active", "Check button is in active state");
|
||||
yield loadLoopPanel();
|
||||
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state after opening panel");
|
||||
let loopPanel = document.getElementById("loop-notification-panel");
|
||||
loopPanel.hidePopup();
|
||||
setInternalLoopGlobal("gFxAOAuthProfile", null);
|
||||
MozLoopServiceInternal.fxAOAuthTokenData = null;
|
||||
MozLoopServiceInternal.notifyStatusChanged();
|
||||
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state");
|
||||
});
|
||||
|
|
|
@ -120,8 +120,8 @@ function* resetFxA() {
|
|||
global.gHawkClient = null;
|
||||
global.gFxAOAuthClientPromise = null;
|
||||
global.gFxAOAuthClient = null;
|
||||
global.gFxAOAuthTokenData = null;
|
||||
global.gFxAOAuthProfile = null;
|
||||
MozLoopServiceInternal.fxAOAuthProfile = null;
|
||||
MozLoopServiceInternal.fxAOAuthTokenData = null;
|
||||
const fxASessionPref = MozLoopServiceInternal.getSessionTokenPrefName(LOOP_SESSION_TYPE.FXA);
|
||||
Services.prefs.clearUserPref(fxASessionPref);
|
||||
MozLoopService.errors.clear();
|
||||
|
@ -130,17 +130,16 @@ function* resetFxA() {
|
|||
yield notified;
|
||||
}
|
||||
|
||||
function setInternalLoopGlobal(aName, aValue) {
|
||||
let global = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
|
||||
global[aName] = aValue;
|
||||
function checkFxAOAuthTokenData(aValue) {
|
||||
ise(MozLoopServiceInternal.fxAOAuthTokenData, aValue, "fxAOAuthTokenData should be " + aValue);
|
||||
}
|
||||
|
||||
function checkLoggedOutState() {
|
||||
let global = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
|
||||
ise(global.gFxAOAuthClientPromise, null, "gFxAOAuthClientPromise should be cleared");
|
||||
ise(global.gFxAOAuthProfile, null, "gFxAOAuthProfile should be cleared");
|
||||
ise(MozLoopService.userProfile, null, "fxAOAuthProfile should be cleared");
|
||||
ise(global.gFxAOAuthClient, null, "gFxAOAuthClient should be cleared");
|
||||
ise(global.gFxAOAuthTokenData, null, "gFxAOAuthTokenData should be cleared");
|
||||
checkFxAOAuthTokenData(null);
|
||||
const fxASessionPref = MozLoopServiceInternal.getSessionTokenPrefName(LOOP_SESSION_TYPE.FXA);
|
||||
ise(Services.prefs.getPrefType(fxASessionPref), Services.prefs.PREF_INVALID,
|
||||
"FxA hawk session should be cleared anyways");
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
<script src="../../content/shared/js/dispatcher.js"></script>
|
||||
<script src="../../content/shared/js/otSdkDriver.js"></script>
|
||||
<script src="../../content/shared/js/conversationStore.js"></script>
|
||||
<script src="../../content/shared/js/roomListStore.js"></script>
|
||||
|
||||
<!-- Test scripts -->
|
||||
<script src="models_test.js"></script>
|
||||
|
@ -56,6 +57,7 @@
|
|||
<script src="dispatcher_test.js"></script>
|
||||
<script src="conversationStore_test.js"></script>
|
||||
<script src="otSdkDriver_test.js"></script>
|
||||
<script src="roomListStore_test.js"></script>
|
||||
<script>
|
||||
mocha.run(function () {
|
||||
$("#mocha").append("<p id='complete'>Complete.</p>");
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var expect = chai.expect;
|
||||
|
||||
describe("loop.store.Room", function () {
|
||||
"use strict";
|
||||
describe("#constructor", function() {
|
||||
it("should validate room values", function() {
|
||||
expect(function() {
|
||||
new loop.store.Room();
|
||||
}).to.Throw(Error, /missing required/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("loop.store.RoomListStore", function () {
|
||||
"use strict";
|
||||
|
||||
var sharedActions = loop.shared.actions;
|
||||
var sandbox, dispatcher;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
dispatcher = new loop.Dispatcher();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe("#constructor", function() {
|
||||
it("should throw an error if the dispatcher is missing", function() {
|
||||
expect(function() {
|
||||
new loop.store.RoomListStore({mozLoop: {}});
|
||||
}).to.Throw(/dispatcher/);
|
||||
});
|
||||
|
||||
it("should throw an error if mozLoop is missing", function() {
|
||||
expect(function() {
|
||||
new loop.store.RoomListStore({dispatcher: dispatcher});
|
||||
}).to.Throw(/mozLoop/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#getAllRooms", function() {
|
||||
var store, fakeMozLoop;
|
||||
var fakeRoomList = [{
|
||||
roomToken: "_nxD4V4FflQ",
|
||||
roomUrl: "http://sample/_nxD4V4FflQ",
|
||||
roomName: "First Room Name",
|
||||
maxSize: 2,
|
||||
currSize: 0,
|
||||
ctime: 1405517546
|
||||
}, {
|
||||
roomToken: "QzBbvGmIZWU",
|
||||
roomUrl: "http://sample/QzBbvGmIZWU",
|
||||
roomName: "Second Room Name",
|
||||
maxSize: 2,
|
||||
currSize: 0,
|
||||
ctime: 1405517418
|
||||
}, {
|
||||
roomToken: "3jKS_Els9IU",
|
||||
roomUrl: "http://sample/3jKS_Els9IU",
|
||||
roomName: "Third Room Name",
|
||||
maxSize: 3,
|
||||
clientMaxSize: 2,
|
||||
currSize: 1,
|
||||
ctime: 1405518241
|
||||
}];
|
||||
|
||||
beforeEach(function() {
|
||||
fakeMozLoop = {
|
||||
rooms: {
|
||||
getAll: function(cb) {
|
||||
cb(null, fakeRoomList);
|
||||
}
|
||||
}
|
||||
};
|
||||
store = new loop.store.RoomListStore({
|
||||
dispatcher: dispatcher,
|
||||
mozLoop: fakeMozLoop
|
||||
});
|
||||
});
|
||||
|
||||
it("should trigger a list:changed event", function(done) {
|
||||
store.on("change", function() {
|
||||
done();
|
||||
});
|
||||
|
||||
dispatcher.dispatch(new sharedActions.GetAllRooms());
|
||||
});
|
||||
|
||||
it("should fetch the room list from the mozLoop API", function(done) {
|
||||
store.once("change", function() {
|
||||
expect(store.getStoreState().error).to.be.a.null;
|
||||
expect(store.getStoreState().rooms).to.have.length.of(3);
|
||||
done();
|
||||
});
|
||||
|
||||
dispatcher.dispatch(new sharedActions.GetAllRooms());
|
||||
});
|
||||
|
||||
it("should order the room list using ctime desc", function(done) {
|
||||
store.once("change", function() {
|
||||
var storeState = store.getStoreState();
|
||||
expect(storeState.error).to.be.a.null;
|
||||
expect(storeState.rooms[0].ctime).eql(1405518241);
|
||||
expect(storeState.rooms[1].ctime).eql(1405517546);
|
||||
expect(storeState.rooms[2].ctime).eql(1405517418);
|
||||
done();
|
||||
});
|
||||
|
||||
dispatcher.dispatch(new sharedActions.GetAllRooms());
|
||||
});
|
||||
|
||||
it("should report an error", function() {
|
||||
fakeMozLoop.rooms.getAll = function(cb) {
|
||||
cb("fakeError");
|
||||
};
|
||||
|
||||
store.once("change", function() {
|
||||
var storeState = store.getStoreState();
|
||||
expect(storeState.error).eql("fakeError");
|
||||
});
|
||||
|
||||
dispatcher.dispatch(new sharedActions.GetAllRooms());
|
||||
});
|
||||
});
|
||||
});
|
|
@ -52,9 +52,10 @@ function run_test()
|
|||
{
|
||||
setupFakeLoopServer();
|
||||
|
||||
// Setup fake login (profile) state so we get FxA requests.
|
||||
const serviceGlobal = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
|
||||
serviceGlobal.gFxAOAuthProfile = {email: "test@example.com", uid: "abcd1234"};
|
||||
// Setup fake login state so we get FxA requests.
|
||||
const MozLoopServiceInternal = Cu.import("resource:///modules/loop/MozLoopService.jsm", {}).MozLoopServiceInternal;
|
||||
MozLoopServiceInternal.fxAOAuthTokenData = {token_type:"bearer",access_token:"1bad3e44b12f77a88fe09f016f6a37c42e40f974bc7a8b432bb0d2f0e37e1752",scope:"profile"};
|
||||
MozLoopServiceInternal.fxAOAuthProfile = {email: "test@example.com", uid: "abcd1234"};
|
||||
|
||||
// For each notification received from the PushServer, MozLoopService will first query
|
||||
// for any pending calls on the FxA hawk session and then again using the guest session.
|
||||
|
@ -102,7 +103,7 @@ function run_test()
|
|||
Chat.open = openChatOrig;
|
||||
|
||||
// Revert fake login state
|
||||
serviceGlobal.gFxAOAuthProfile = null;
|
||||
MozLoopServiceInternal.fxAOAuthTokenData = null;
|
||||
|
||||
// clear test pref
|
||||
Services.prefs.clearUserPref("loop.seenToS");
|
||||
|
|
|
@ -10,8 +10,9 @@ var startTimerCalled = false;
|
|||
add_task(function test_initialize_no_expiry() {
|
||||
startTimerCalled = false;
|
||||
|
||||
MozLoopService.initialize();
|
||||
|
||||
let initializedPromise = yield MozLoopService.initialize();
|
||||
Assert.equal(initializedPromise, "registration not needed",
|
||||
"Promise should be fulfilled");
|
||||
Assert.equal(startTimerCalled, false,
|
||||
"should not register when no expiry time is set");
|
||||
});
|
||||
|
|
|
@ -40,7 +40,7 @@ add_test(function test_register_websocket_success_loop_server_fail() {
|
|||
}, err => {
|
||||
// 404 is an expected failure indicated by the lack of route being set
|
||||
// up on the Loop server mock. This is added in the next test.
|
||||
Assert.equal(err, 404, "Expected no errors in websocket registration");
|
||||
Assert.equal(err.errno, 404, "Expected no errors in websocket registration");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const FAKE_FXA_TOKEN_DATA = JSON.stringify({
|
||||
"token_type": "bearer",
|
||||
"access_token": "1bad3e44b12f77a88fe09f016f6a37c42e40f974bc7a8b432bb0d2f0e37e1752",
|
||||
"scope": "profile"
|
||||
});
|
||||
const FAKE_FXA_PROFILE = JSON.stringify({
|
||||
"email": "test@example.com",
|
||||
"uid": "999999994d9f4b08a2cbfc0999999999",
|
||||
"avatar": null
|
||||
});
|
||||
const LOOP_FXA_TOKEN_PREF = "loop.fxa_oauth.tokendata";
|
||||
const LOOP_FXA_PROFILE_PREF = "loop.fxa_oauth.profile";
|
||||
const LOOP_URL_EXPIRY_PREF = "loop.urlsExpiryTimeSeconds";
|
||||
const LOOP_INITIAL_DELAY_PREF = "loop.initialDelay";
|
||||
|
||||
/**
|
||||
* This file is to test restart+reauth.
|
||||
*/
|
||||
|
||||
add_task(function test_initialize_with_expired_urls_and_no_auth_token() {
|
||||
// Set time to be 2 seconds in the past.
|
||||
var nowSeconds = Date.now() / 1000;
|
||||
Services.prefs.setIntPref(LOOP_URL_EXPIRY_PREF, nowSeconds - 2);
|
||||
Services.prefs.clearUserPref(LOOP_FXA_TOKEN_PREF);
|
||||
|
||||
yield MozLoopService.initialize(mockPushHandler).then((msg) => {
|
||||
Assert.equal(msg, "registration not needed", "Initialize should not register when the " +
|
||||
"URLs are expired and there are no auth tokens");
|
||||
}, (error) => {
|
||||
Assert.ok(false, error, "should have resolved the promise that initialize returned");
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function test_initialize_with_urls_and_no_auth_token() {
|
||||
Services.prefs.setIntPref(LOOP_URL_EXPIRY_PREF, Date.now() / 1000 + 10);
|
||||
Services.prefs.clearUserPref(LOOP_FXA_TOKEN_PREF);
|
||||
|
||||
loopServer.registerPathHandler("/registration", (request, response) => {
|
||||
response.setStatusLine(null, 200, "OK");
|
||||
});
|
||||
|
||||
yield MozLoopService.initialize(mockPushHandler).then((msg) => {
|
||||
Assert.equal(msg, "initialized to guest status", "Initialize should register as a " +
|
||||
"guest when no auth tokens but expired URLs");
|
||||
}, (error) => {
|
||||
Assert.ok(false, error, "should have resolved the promise that initialize returned");
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function test_initialize_with_invalid_fxa_token() {
|
||||
Services.prefs.setCharPref(LOOP_FXA_PROFILE_PREF, FAKE_FXA_PROFILE);
|
||||
Services.prefs.setCharPref(LOOP_FXA_TOKEN_PREF, FAKE_FXA_TOKEN_DATA);
|
||||
|
||||
// Only need to implement the FxA registration because the previous
|
||||
// test registered as a guest.
|
||||
loopServer.registerPathHandler("/registration", (request, response) => {
|
||||
response.setStatusLine(null, 401, "Unauthorized");
|
||||
response.write(JSON.stringify({
|
||||
code: 401,
|
||||
errno: 110,
|
||||
error: "Unauthorized",
|
||||
message: "Unknown credentials",
|
||||
}));
|
||||
});
|
||||
|
||||
yield MozLoopService.initialize(mockPushHandler).then(() => {
|
||||
Assert.ok(false, "Initializing with an invalid token should reject the promise");
|
||||
},
|
||||
(error) => {
|
||||
let pushHandler = Cu.import("resource:///modules/loop/MozLoopService.jsm", {}).gPushHandler;
|
||||
Assert.equal(pushHandler.pushUrl, kEndPointUrl, "Push URL should match");
|
||||
Assert.equal(Services.prefs.getCharPref(LOOP_FXA_TOKEN_PREF), "",
|
||||
"FXA pref should be cleared if token was invalid");
|
||||
Assert.equal(Services.prefs.getCharPref(LOOP_FXA_PROFILE_PREF), "",
|
||||
"FXA profile pref should be cleared if token was invalid");
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function test_initialize_with_fxa_token() {
|
||||
Services.prefs.setCharPref(LOOP_FXA_PROFILE_PREF, FAKE_FXA_PROFILE);
|
||||
Services.prefs.setCharPref(LOOP_FXA_TOKEN_PREF, FAKE_FXA_TOKEN_DATA);
|
||||
loopServer.registerPathHandler("/registration", (request, response) => {
|
||||
response.setStatusLine(null, 200, "OK");
|
||||
});
|
||||
|
||||
yield MozLoopService.initialize(mockPushHandler).then(() => {
|
||||
Assert.equal(Services.prefs.getCharPref(LOOP_FXA_TOKEN_PREF), FAKE_FXA_TOKEN_DATA,
|
||||
"FXA pref should still be set after initialization");
|
||||
Assert.equal(Services.prefs.getCharPref(LOOP_FXA_PROFILE_PREF), FAKE_FXA_PROFILE,
|
||||
"FXA profile should still be set after initialization");
|
||||
});
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
setupFakeLoopServer();
|
||||
// Note, this is just used to speed up the test.
|
||||
Services.prefs.setIntPref(LOOP_INITIAL_DELAY_PREF, 0);
|
||||
mockPushHandler.pushUrl = kEndPointUrl;
|
||||
|
||||
do_register_cleanup(function() {
|
||||
Services.prefs.clearUserPref(LOOP_INITIAL_DELAY_PREF);
|
||||
Services.prefs.clearUserPref(LOOP_FXA_TOKEN_PREF);
|
||||
Services.prefs.clearUserPref(LOOP_FXA_PROFILE_PREF);
|
||||
Services.prefs.clearUserPref(LOOP_URL_EXPIRY_PREF);
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
};
|
|
@ -18,11 +18,8 @@ add_test(function test_registration_invalid_token() {
|
|||
response.write(JSON.stringify({
|
||||
code: 401,
|
||||
errno: 110,
|
||||
error: {
|
||||
error: "Unauthorized",
|
||||
message: "Unknown credentials",
|
||||
statusCode: 401
|
||||
}
|
||||
error: "Unauthorized",
|
||||
message: "Unknown credentials",
|
||||
}));
|
||||
} else {
|
||||
// We didn't have an authorization header, so check the pref has been cleared.
|
||||
|
|
|
@ -15,6 +15,7 @@ skip-if = toolkit == 'gonk'
|
|||
[test_loopservice_locales.js]
|
||||
[test_loopservice_notification.js]
|
||||
[test_loopservice_registration.js]
|
||||
[test_loopservice_restart.js]
|
||||
[test_loopservice_token_invalid.js]
|
||||
[test_loopservice_token_save.js]
|
||||
[test_loopservice_token_send.js]
|
||||
|
|
|
@ -9,7 +9,12 @@
|
|||
navigator.mozLoop = {
|
||||
ensureRegistered: function() {},
|
||||
getLoopCharPref: function() {},
|
||||
getLoopBoolPref: function() {},
|
||||
getLoopBoolPref: function(pref) {
|
||||
// Ensure UI for rooms is displayed in the showcase.
|
||||
if (pref === "rooms.enabled") {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
releaseCallData: function() {},
|
||||
contacts: {
|
||||
getAll: function(callback) {
|
||||
|
|
|
@ -38,7 +38,10 @@
|
|||
<script src="../content/shared/js/mixins.js"></script>
|
||||
<script src="../content/shared/js/views.js"></script>
|
||||
<script src="../content/shared/js/websocket.js"></script>
|
||||
<script src="../content/shared/js/validate.js"></script>
|
||||
<script src="../content/shared/js/dispatcher.js"></script>
|
||||
<script src="../content/shared/js/conversationStore.js"></script>
|
||||
<script src="../content/shared/js/roomListStore.js"></script>
|
||||
<script src="../content/js/conversationViews.js"></script>
|
||||
<script src="../content/js/client.js"></script>
|
||||
<script src="../standalone/content/js/webapp.js"></script>
|
||||
|
|
|
@ -69,9 +69,16 @@
|
|||
font-weight: bold;
|
||||
border-bottom: 1px dashed #aaa;
|
||||
margin: 1em 0;
|
||||
margin-top: -14em;
|
||||
padding-top: 14em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.showcase > section .example > h3 a {
|
||||
text-decoration: none;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.showcase p.note {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
|
|
@ -56,6 +56,12 @@
|
|||
}
|
||||
);
|
||||
|
||||
var dispatcher = new loop.Dispatcher();
|
||||
var roomListStore = new loop.store.RoomListStore({
|
||||
dispatcher: dispatcher,
|
||||
mozLoop: {}
|
||||
});
|
||||
|
||||
// Local mocks
|
||||
|
||||
var mockContact = {
|
||||
|
@ -93,11 +99,18 @@
|
|||
});
|
||||
|
||||
var Example = React.createClass({displayName: 'Example',
|
||||
makeId: function(prefix) {
|
||||
return (prefix || "") + this.props.summary.toLowerCase().replace(/\s/g, "-");
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var cx = React.addons.classSet;
|
||||
return (
|
||||
React.DOM.div({className: "example"},
|
||||
React.DOM.h3(null, this.props.summary),
|
||||
React.DOM.h3({id: this.makeId()},
|
||||
this.props.summary,
|
||||
React.DOM.a({href: this.makeId("#")}, " ¶")
|
||||
),
|
||||
React.DOM.div({className: cx({comp: true, dashed: this.props.dashed}),
|
||||
style: this.props.style || {}},
|
||||
this.props.children
|
||||
|
@ -150,26 +163,45 @@
|
|||
),
|
||||
Example({summary: "Call URL retrieved", dashed: "true", style: {width: "332px"}},
|
||||
PanelView({client: mockClient, notifications: notifications,
|
||||
callUrl: "http://invalid.example.url/"})
|
||||
callUrl: "http://invalid.example.url/",
|
||||
dispatcher: dispatcher,
|
||||
roomListStore: roomListStore})
|
||||
),
|
||||
Example({summary: "Call URL retrieved - authenticated", dashed: "true", style: {width: "332px"}},
|
||||
PanelView({client: mockClient, notifications: notifications,
|
||||
callUrl: "http://invalid.example.url/",
|
||||
userProfile: {email: "test@example.com"}})
|
||||
userProfile: {email: "test@example.com"},
|
||||
dispatcher: dispatcher,
|
||||
roomListStore: roomListStore})
|
||||
),
|
||||
Example({summary: "Pending call url retrieval", dashed: "true", style: {width: "332px"}},
|
||||
PanelView({client: mockClient, notifications: notifications})
|
||||
PanelView({client: mockClient, notifications: notifications,
|
||||
dispatcher: dispatcher,
|
||||
roomListStore: roomListStore})
|
||||
),
|
||||
Example({summary: "Pending call url retrieval - authenticated", dashed: "true", style: {width: "332px"}},
|
||||
PanelView({client: mockClient, notifications: notifications,
|
||||
userProfile: {email: "test@example.com"}})
|
||||
userProfile: {email: "test@example.com"},
|
||||
dispatcher: dispatcher,
|
||||
roomListStore: roomListStore})
|
||||
),
|
||||
Example({summary: "Error Notification", dashed: "true", style: {width: "332px"}},
|
||||
PanelView({client: mockClient, notifications: errNotifications})
|
||||
PanelView({client: mockClient, notifications: errNotifications,
|
||||
dispatcher: dispatcher,
|
||||
roomListStore: roomListStore})
|
||||
),
|
||||
Example({summary: "Error Notification - authenticated", dashed: "true", style: {width: "332px"}},
|
||||
PanelView({client: mockClient, notifications: errNotifications,
|
||||
userProfile: {email: "test@example.com"}})
|
||||
userProfile: {email: "test@example.com"},
|
||||
dispatcher: dispatcher,
|
||||
roomListStore: roomListStore})
|
||||
),
|
||||
Example({summary: "Room list tab", dashed: "true", style: {width: "332px"}},
|
||||
PanelView({client: mockClient, notifications: notifications,
|
||||
userProfile: {email: "test@example.com"},
|
||||
dispatcher: dispatcher,
|
||||
roomListStore: roomListStore,
|
||||
selectedTab: "rooms"})
|
||||
)
|
||||
),
|
||||
|
||||
|
@ -247,12 +279,15 @@
|
|||
Section({name: "PendingConversationView"},
|
||||
Example({summary: "Pending conversation view (connecting)", dashed: "true"},
|
||||
React.DOM.div({className: "standalone"},
|
||||
PendingConversationView({websocket: mockWebSocket})
|
||||
PendingConversationView({websocket: mockWebSocket,
|
||||
dispatcher: dispatcher})
|
||||
)
|
||||
),
|
||||
Example({summary: "Pending conversation view (ringing)", dashed: "true"},
|
||||
React.DOM.div({className: "standalone"},
|
||||
PendingConversationView({websocket: mockWebSocket, callState: "ringing"})
|
||||
PendingConversationView({websocket: mockWebSocket,
|
||||
dispatcher: dispatcher,
|
||||
callState: "ringing"})
|
||||
)
|
||||
)
|
||||
),
|
||||
|
@ -262,7 +297,8 @@
|
|||
style: {width: "260px", height: "265px"}},
|
||||
React.DOM.div({className: "fx-embedded"},
|
||||
DesktopPendingConversationView({callState: "gather",
|
||||
contact: mockContact})
|
||||
contact: mockContact,
|
||||
dispatcher: dispatcher})
|
||||
)
|
||||
)
|
||||
),
|
||||
|
@ -271,7 +307,7 @@
|
|||
Example({summary: "Call Failed", dashed: "true",
|
||||
style: {width: "260px", height: "265px"}},
|
||||
React.DOM.div({className: "fx-embedded"},
|
||||
CallFailedView(null)
|
||||
CallFailedView({dispatcher: dispatcher})
|
||||
)
|
||||
)
|
||||
),
|
||||
|
|
|
@ -56,6 +56,12 @@
|
|||
}
|
||||
);
|
||||
|
||||
var dispatcher = new loop.Dispatcher();
|
||||
var roomListStore = new loop.store.RoomListStore({
|
||||
dispatcher: dispatcher,
|
||||
mozLoop: {}
|
||||
});
|
||||
|
||||
// Local mocks
|
||||
|
||||
var mockContact = {
|
||||
|
@ -93,11 +99,18 @@
|
|||
});
|
||||
|
||||
var Example = React.createClass({
|
||||
makeId: function(prefix) {
|
||||
return (prefix || "") + this.props.summary.toLowerCase().replace(/\s/g, "-");
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var cx = React.addons.classSet;
|
||||
return (
|
||||
<div className="example">
|
||||
<h3>{this.props.summary}</h3>
|
||||
<h3 id={this.makeId()}>
|
||||
{this.props.summary}
|
||||
<a href={this.makeId("#")}> ¶</a>
|
||||
</h3>
|
||||
<div className={cx({comp: true, dashed: this.props.dashed})}
|
||||
style={this.props.style || {}}>
|
||||
{this.props.children}
|
||||
|
@ -150,26 +163,45 @@
|
|||
</p>
|
||||
<Example summary="Call URL retrieved" dashed="true" style={{width: "332px"}}>
|
||||
<PanelView client={mockClient} notifications={notifications}
|
||||
callUrl="http://invalid.example.url/" />
|
||||
callUrl="http://invalid.example.url/"
|
||||
dispatcher={dispatcher}
|
||||
roomListStore={roomListStore} />
|
||||
</Example>
|
||||
<Example summary="Call URL retrieved - authenticated" dashed="true" style={{width: "332px"}}>
|
||||
<PanelView client={mockClient} notifications={notifications}
|
||||
callUrl="http://invalid.example.url/"
|
||||
userProfile={{email: "test@example.com"}} />
|
||||
userProfile={{email: "test@example.com"}}
|
||||
dispatcher={dispatcher}
|
||||
roomListStore={roomListStore} />
|
||||
</Example>
|
||||
<Example summary="Pending call url retrieval" dashed="true" style={{width: "332px"}}>
|
||||
<PanelView client={mockClient} notifications={notifications} />
|
||||
<PanelView client={mockClient} notifications={notifications}
|
||||
dispatcher={dispatcher}
|
||||
roomListStore={roomListStore} />
|
||||
</Example>
|
||||
<Example summary="Pending call url retrieval - authenticated" dashed="true" style={{width: "332px"}}>
|
||||
<PanelView client={mockClient} notifications={notifications}
|
||||
userProfile={{email: "test@example.com"}} />
|
||||
userProfile={{email: "test@example.com"}}
|
||||
dispatcher={dispatcher}
|
||||
roomListStore={roomListStore} />
|
||||
</Example>
|
||||
<Example summary="Error Notification" dashed="true" style={{width: "332px"}}>
|
||||
<PanelView client={mockClient} notifications={errNotifications}/>
|
||||
<PanelView client={mockClient} notifications={errNotifications}
|
||||
dispatcher={dispatcher}
|
||||
roomListStore={roomListStore} />
|
||||
</Example>
|
||||
<Example summary="Error Notification - authenticated" dashed="true" style={{width: "332px"}}>
|
||||
<PanelView client={mockClient} notifications={errNotifications}
|
||||
userProfile={{email: "test@example.com"}} />
|
||||
userProfile={{email: "test@example.com"}}
|
||||
dispatcher={dispatcher}
|
||||
roomListStore={roomListStore} />
|
||||
</Example>
|
||||
<Example summary="Room list tab" dashed="true" style={{width: "332px"}}>
|
||||
<PanelView client={mockClient} notifications={notifications}
|
||||
userProfile={{email: "test@example.com"}}
|
||||
dispatcher={dispatcher}
|
||||
roomListStore={roomListStore}
|
||||
selectedTab="rooms" />
|
||||
</Example>
|
||||
</Section>
|
||||
|
||||
|
@ -247,12 +279,15 @@
|
|||
<Section name="PendingConversationView">
|
||||
<Example summary="Pending conversation view (connecting)" dashed="true">
|
||||
<div className="standalone">
|
||||
<PendingConversationView websocket={mockWebSocket}/>
|
||||
<PendingConversationView websocket={mockWebSocket}
|
||||
dispatcher={dispatcher} />
|
||||
</div>
|
||||
</Example>
|
||||
<Example summary="Pending conversation view (ringing)" dashed="true">
|
||||
<div className="standalone">
|
||||
<PendingConversationView websocket={mockWebSocket} callState="ringing"/>
|
||||
<PendingConversationView websocket={mockWebSocket}
|
||||
dispatcher={dispatcher}
|
||||
callState="ringing"/>
|
||||
</div>
|
||||
</Example>
|
||||
</Section>
|
||||
|
@ -262,7 +297,8 @@
|
|||
style={{width: "260px", height: "265px"}}>
|
||||
<div className="fx-embedded">
|
||||
<DesktopPendingConversationView callState={"gather"}
|
||||
contact={mockContact} />
|
||||
contact={mockContact}
|
||||
dispatcher={dispatcher} />
|
||||
</div>
|
||||
</Example>
|
||||
</Section>
|
||||
|
@ -271,7 +307,7 @@
|
|||
<Example summary="Call Failed" dashed="true"
|
||||
style={{width: "260px", height: "265px"}}>
|
||||
<div className="fx-embedded">
|
||||
<CallFailedView />
|
||||
<CallFailedView dispatcher={dispatcher} />
|
||||
</div>
|
||||
</Example>
|
||||
</Section>
|
||||
|
|
|
@ -107,11 +107,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "SignInToWebsiteUX",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
|
||||
"resource:///modules/ContentSearch.jsm");
|
||||
|
||||
#ifdef E10S_TESTING_ONLY
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
#endif
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "ShellService", function() {
|
||||
try {
|
||||
return Cc["@mozilla.org/browser/shell-service;1"].
|
||||
|
@ -748,7 +743,10 @@ BrowserGlue.prototype = {
|
|||
|
||||
// XXX: Temporary hack to allow Loop FxA login after a restart to work.
|
||||
// Remove this once bug 1071247 is deployed.
|
||||
Services.prefs.clearUserPref("loop.hawk-session-token.fxa");
|
||||
if (Services.prefs.getPrefType("loop.autologin-after-restart") != Ci.nsIPrefBranch.PREF_BOOL ||
|
||||
!Services.prefs.getBoolPref("loop.autologin-after-restart")) {
|
||||
Services.prefs.clearUserPref("loop.hawk-session-token.fxa");
|
||||
}
|
||||
},
|
||||
|
||||
// All initial windows have opened.
|
||||
|
@ -2270,15 +2268,12 @@ let DefaultBrowserCheck = {
|
|||
let E10SUINotification = {
|
||||
// Increase this number each time we want to roll out an
|
||||
// e10s testing period to Nightly users.
|
||||
CURRENT_NOTICE_COUNT: 1,
|
||||
CURRENT_PROMPT_PREF: "browser.displayedE10SPrompt.1",
|
||||
PREVIOUS_PROMPT_PREF: "browser.displayedE10SPrompt",
|
||||
CURRENT_NOTICE_COUNT: 0,
|
||||
|
||||
checkStatus: function() {
|
||||
let skipE10sChecks = false;
|
||||
try {
|
||||
skipE10sChecks = (UpdateChannel.get() != "nightly") ||
|
||||
Services.prefs.getBoolPref("browser.tabs.remote.autostart.disabled-because-using-a11y");
|
||||
skipE10sChecks = Services.prefs.getBoolPref("browser.tabs.remote.autostart.disabled-because-using-a11y");
|
||||
} catch(e) {}
|
||||
|
||||
if (skipE10sChecks) {
|
||||
|
@ -2324,26 +2319,17 @@ let E10SUINotification = {
|
|||
|
||||
let e10sPromptShownCount = 0;
|
||||
try {
|
||||
e10sPromptShownCount = Services.prefs.getIntPref(this.CURRENT_PROMPT_PREF);
|
||||
e10sPromptShownCount = Services.prefs.getIntPref("browser.displayedE10SPrompt");
|
||||
} catch(e) {}
|
||||
|
||||
let isHardwareAccelerated = true;
|
||||
try {
|
||||
let win = RecentWindow.getMostRecentBrowserWindow();
|
||||
let winutils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||
isHardwareAccelerated = winutils.layerManagerType != "Basic";
|
||||
} catch (e) {}
|
||||
|
||||
if (!Services.appinfo.inSafeMode &&
|
||||
!Services.appinfo.accessibilityEnabled &&
|
||||
!Services.appinfo.keyboardMayHaveIME &&
|
||||
isHardwareAccelerated &&
|
||||
e10sPromptShownCount < 5) {
|
||||
Services.tm.mainThread.dispatch(() => {
|
||||
try {
|
||||
this._showE10SPrompt();
|
||||
Services.prefs.setIntPref(this.CURRENT_PROMPT_PREF, e10sPromptShownCount + 1);
|
||||
Services.prefs.clearUserPref(this.PREVIOUS_PROMPT_PREF);
|
||||
Services.prefs.setIntPref("browser.displayedE10SPrompt", e10sPromptShownCount + 1);
|
||||
} catch (ex) {
|
||||
Cu.reportError("Failed to show e10s prompt: " + ex);
|
||||
}
|
||||
|
@ -2390,7 +2376,7 @@ let E10SUINotification = {
|
|||
|
||||
let browser = win.gBrowser.selectedBrowser;
|
||||
|
||||
let promptMessage = "Would you like to help us test multiprocess Nightly (e10s)? You can also enable e10s in Nightly preferences. Notable fixes:";
|
||||
let promptMessage = "Would you like to help us test multiprocess Nightly (e10s)? You can also enable e10s in Nightly preferences.";
|
||||
let mainAction = {
|
||||
label: "Enable and Restart",
|
||||
accessKey: "E",
|
||||
|
@ -2410,7 +2396,7 @@ let E10SUINotification = {
|
|||
label: "No thanks",
|
||||
accessKey: "N",
|
||||
callback: function () {
|
||||
Services.prefs.setIntPref(E10SUINotification.CURRENT_PROMPT_PREF, 5);
|
||||
Services.prefs.setIntPref("browser.displayedE10SPrompt", 5);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
@ -2420,21 +2406,7 @@ let E10SUINotification = {
|
|||
persistWhileVisible: true
|
||||
};
|
||||
|
||||
win.PopupNotifications.show(browser, "enable-e10s", promptMessage, null, mainAction, secondaryActions, options);
|
||||
|
||||
let highlights = [
|
||||
"Less crashing!",
|
||||
"Improved add-on compatibility and DevTools",
|
||||
"PDF.js, Web Console, Spellchecking, WebRTC now work"
|
||||
];
|
||||
|
||||
let doorhangerExtraContent = win.document.getElementById("enable-e10s-notification")
|
||||
.querySelector("popupnotificationcontent");
|
||||
for (let highlight of highlights) {
|
||||
let highlightLabel = win.document.createElement("label");
|
||||
highlightLabel.setAttribute("value", highlight);
|
||||
doorhangerExtraContent.appendChild(highlightLabel);
|
||||
}
|
||||
win.PopupNotifications.show(browser, "enable_e10s", promptMessage, null, mainAction, secondaryActions, options);
|
||||
},
|
||||
|
||||
_warnedAboutAccessibility: false,
|
||||
|
|
|
@ -406,6 +406,10 @@ DevTools.prototype = {
|
|||
|
||||
this._toolboxes.set(target, toolbox);
|
||||
|
||||
toolbox.once("destroy", () => {
|
||||
this.emit("toolbox-destroy", target);
|
||||
});
|
||||
|
||||
toolbox.once("destroyed", () => {
|
||||
this._toolboxes.delete(target);
|
||||
this.emit("toolbox-destroyed", target);
|
||||
|
@ -429,7 +433,7 @@ DevTools.prototype = {
|
|||
* Target value e.g. the target that owns this toolbox
|
||||
*
|
||||
* @return {Toolbox} toolbox
|
||||
* The toobox that is debugging the given target
|
||||
* The toolbox that is debugging the given target
|
||||
*/
|
||||
getToolbox: function DT_getToolbox(target) {
|
||||
return this._toolboxes.get(target);
|
||||
|
@ -440,7 +444,7 @@ DevTools.prototype = {
|
|||
*
|
||||
* @return promise
|
||||
* This promise will resolve to false if no toolbox was found
|
||||
* associated to the target. true, if the toolbox was successfuly
|
||||
* associated to the target. true, if the toolbox was successfully
|
||||
* closed.
|
||||
*/
|
||||
closeToolbox: function DT_closeToolbox(target) {
|
||||
|
@ -606,11 +610,11 @@ let gDevToolsBrowser = {
|
|||
* selectToolCommand's behavior:
|
||||
* - if the toolbox is closed,
|
||||
* we open the toolbox and select the tool
|
||||
* - if the toolbox is open, and the targetted tool is not selected,
|
||||
* - if the toolbox is open, and the targeted tool is not selected,
|
||||
* we select it
|
||||
* - if the toolbox is open, and the targetted tool is selected,
|
||||
* - if the toolbox is open, and the targeted tool is selected,
|
||||
* and the host is NOT a window, we close the toolbox
|
||||
* - if the toolbox is open, and the targetted tool is selected,
|
||||
* - if the toolbox is open, and the targeted tool is selected,
|
||||
* and the host is a window, we raise the toolbox window
|
||||
*/
|
||||
selectToolCommand: function(gBrowser, toolId) {
|
||||
|
|
|
@ -8,6 +8,7 @@ support-files =
|
|||
doc_theme.css
|
||||
|
||||
[browser_devtools_api.js]
|
||||
[browser_devtools_api_destroy.js]
|
||||
skip-if = e10s # Bug 1070837 - devtools/framework/toolbox.js |doc| getter not e10s friendly
|
||||
[browser_dynamic_tool_enabling.js]
|
||||
[browser_keybindings.js]
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests devtools API
|
||||
|
||||
const Cu = Components.utils;
|
||||
|
||||
function test() {
|
||||
addTab("about:blank").then(runTests);
|
||||
}
|
||||
|
||||
function runTests(aTab) {
|
||||
let toolDefinition = {
|
||||
id: "testTool",
|
||||
visibilityswitch: "devtools.testTool.enabled",
|
||||
isTargetSupported: function() true,
|
||||
url: "about:blank",
|
||||
label: "someLabel",
|
||||
build: function(iframeWindow, toolbox) {
|
||||
let deferred = promise.defer();
|
||||
executeSoon(() => {
|
||||
deferred.resolve({
|
||||
target: toolbox.target,
|
||||
toolbox: toolbox,
|
||||
isReady: true,
|
||||
destroy: function(){},
|
||||
});
|
||||
});
|
||||
return deferred.promise;
|
||||
},
|
||||
};
|
||||
|
||||
gDevTools.registerTool(toolDefinition);
|
||||
|
||||
let collectedEvents = [];
|
||||
|
||||
let target = TargetFactory.forTab(aTab);
|
||||
gDevTools.showToolbox(target, toolDefinition.id).then(function(toolbox) {
|
||||
let panel = toolbox.getPanel(toolDefinition.id);
|
||||
ok(panel, "Tool open");
|
||||
|
||||
gDevTools.once("toolbox-destroy", (event, toolbox, iframe) => {
|
||||
collectedEvents.push(event);
|
||||
});
|
||||
|
||||
gDevTools.once(toolDefinition.id + "-destroy", (event, toolbox, iframe) => {
|
||||
collectedEvents.push("gDevTools-" + event);
|
||||
});
|
||||
|
||||
toolbox.once("destroy", (event) => {
|
||||
collectedEvents.push(event);
|
||||
});
|
||||
|
||||
toolbox.once(toolDefinition.id + "-destroy", (event) => {
|
||||
collectedEvents.push("toolbox-" + event);
|
||||
});
|
||||
|
||||
toolbox.destroy().then(function() {
|
||||
is(collectedEvents.join(":"),
|
||||
"toolbox-destroy:destroy:gDevTools-testTool-destroy:toolbox-testTool-destroy",
|
||||
"Found the right amount of collected events.");
|
||||
|
||||
gDevTools.unregisterTool(toolDefinition.id);
|
||||
gBrowser.removeCurrentTab();
|
||||
|
||||
executeSoon(function() {
|
||||
finish();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1516,6 +1516,8 @@ Toolbox.prototype = {
|
|||
return this._destroyer;
|
||||
}
|
||||
|
||||
this.emit("destroy");
|
||||
|
||||
this._target.off("navigate", this._refreshHostTitle);
|
||||
this._target.off("frame-update", this._updateFrames);
|
||||
this.off("select", this._refreshHostTitle);
|
||||
|
@ -1537,6 +1539,9 @@ Toolbox.prototype = {
|
|||
let outstanding = [];
|
||||
for (let [id, panel] of this._toolPanels) {
|
||||
try {
|
||||
gDevTools.emit(id + "-destroy", this, panel);
|
||||
this.emit(id + "-destroy", panel);
|
||||
|
||||
outstanding.push(panel.destroy());
|
||||
} catch (e) {
|
||||
// We don't want to stop here if any panel fail to close.
|
||||
|
|
|
@ -185,11 +185,12 @@ let test = asyncTest(function* () {
|
|||
|
||||
contextMenuClick(getContainerForRawNode(inspector.markup, node).tagLine);
|
||||
|
||||
let onNodeReselected = inspector.markup.once("reselectedonremoved");
|
||||
let menu = inspector.panelDoc.getElementById("node-menu-pasteouterhtml");
|
||||
dispatchCommandEvent(menu);
|
||||
|
||||
info("Waiting for inspector selection to update");
|
||||
yield inspector.selection.once("new-node");
|
||||
yield onNodeReselected;
|
||||
|
||||
ok(content.document.body.outerHTML.contains(clipboard.get()),
|
||||
"Clipboard content was pasted into the node's outer HTML.");
|
||||
|
@ -198,6 +199,8 @@ let test = asyncTest(function* () {
|
|||
|
||||
function* testDeleteNode() {
|
||||
info("Testing 'Delete Node' menu item for normal elements.");
|
||||
|
||||
yield selectNode("p", inspector);
|
||||
let deleteNode = inspector.panelDoc.getElementById("node-menu-delete");
|
||||
ok(deleteNode, "the popup menu has a delete menu item");
|
||||
|
||||
|
|
|
@ -620,8 +620,6 @@ MarkupView.prototype = {
|
|||
*/
|
||||
_mutationObserver: function(aMutations) {
|
||||
let requiresLayoutChange = false;
|
||||
let reselectParent;
|
||||
let reselectChildIndex;
|
||||
|
||||
for (let mutation of aMutations) {
|
||||
let type = mutation.type;
|
||||
|
@ -651,22 +649,9 @@ MarkupView.prototype = {
|
|||
requiresLayoutChange = true;
|
||||
}
|
||||
} else if (type === "childList") {
|
||||
let isFromOuterHTML = mutation.removed.some((n) => {
|
||||
return n === this._outerHTMLNode;
|
||||
});
|
||||
|
||||
// Keep track of which node should be reselected after mutations.
|
||||
if (isFromOuterHTML) {
|
||||
reselectParent = target;
|
||||
reselectChildIndex = this._outerHTMLChildIndex;
|
||||
|
||||
delete this._outerHTMLNode;
|
||||
delete this._outerHTMLChildIndex;
|
||||
}
|
||||
|
||||
container.childrenDirty = true;
|
||||
// Update the children to take care of changes in the markup view DOM.
|
||||
this._updateChildren(container, {flash: !isFromOuterHTML});
|
||||
this._updateChildren(container, {flash: true});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -680,22 +665,6 @@ MarkupView.prototype = {
|
|||
// Since the htmlEditor is absolutely positioned, a mutation may change
|
||||
// the location in which it should be shown.
|
||||
this.htmlEditor.refresh();
|
||||
|
||||
// If a node has had its outerHTML set, the parent node will be selected.
|
||||
// Reselect the original node immediately.
|
||||
if (this._inspector.selection.nodeFront === reselectParent) {
|
||||
this.walker.children(reselectParent).then((o) => {
|
||||
let node = o.nodes[reselectChildIndex];
|
||||
let container = this.getContainer(node);
|
||||
if (node && container) {
|
||||
this.markNodeAsSelected(node, "outerhtml");
|
||||
if (container.hasChildren) {
|
||||
this.expandNode(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -847,26 +816,70 @@ MarkupView.prototype = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Retrieve the index of a child within its parent's children list.
|
||||
* @param aNode The NodeFront to find the index of.
|
||||
* @returns A promise that will be resolved with the integer index.
|
||||
* If the child cannot be found, returns -1
|
||||
* Listen to mutations, expect a given node to be removed and try and select
|
||||
* the node that sits at the same place instead.
|
||||
* This is useful when changing the outerHTML or the tag name so that the
|
||||
* newly inserted node gets selected instead of the one that just got removed.
|
||||
*/
|
||||
getNodeChildIndex: function(aNode) {
|
||||
let def = promise.defer();
|
||||
let parentNode = aNode.parentNode();
|
||||
reselectOnRemoved: function(removedNode, reason) {
|
||||
// Only allow one removed node reselection at a time, so that when there are
|
||||
// more than 1 request in parallel, the last one wins.
|
||||
this.cancelReselectOnRemoved();
|
||||
|
||||
// Node may have been removed from the DOM, instead of throwing an error,
|
||||
// return -1 indicating that it isn't inside of its parent children list.
|
||||
if (!parentNode) {
|
||||
def.resolve(-1);
|
||||
} else {
|
||||
this.walker.children(parentNode).then(children => {
|
||||
def.resolve(children.nodes.indexOf(aNode));
|
||||
});
|
||||
// Get the removedNode index in its parent node to reselect the right node.
|
||||
let isHTMLTag = removedNode.tagName.toLowerCase() === "html";
|
||||
let oldContainer = this.getContainer(removedNode);
|
||||
let parentContainer = this.getContainer(removedNode.parentNode());
|
||||
let childIndex = parentContainer.getChildContainers().indexOf(oldContainer);
|
||||
|
||||
let onMutations = this._removedNodeObserver = (e, mutations) => {
|
||||
let isNodeRemovalMutation = false;
|
||||
for (let mutation of mutations) {
|
||||
let containsRemovedNode = mutation.removed &&
|
||||
mutation.removed.some(n => n === removedNode);
|
||||
if (mutation.type === "childList" && (containsRemovedNode || isHTMLTag)) {
|
||||
isNodeRemovalMutation = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isNodeRemovalMutation) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._inspector.off("markupmutation", onMutations);
|
||||
this._removedNodeObserver = null;
|
||||
|
||||
// Don't select the new node if the user has already changed the current
|
||||
// selection.
|
||||
if (this._inspector.selection.nodeFront === parentContainer.node ||
|
||||
(this._inspector.selection.nodeFront === removedNode && isHTMLTag)) {
|
||||
let childContainers = parentContainer.getChildContainers();
|
||||
if (childContainers && childContainers[childIndex]) {
|
||||
this.markNodeAsSelected(childContainers[childIndex].node, reason);
|
||||
if (childContainers[childIndex].hasChildren) {
|
||||
this.expandNode(childContainers[childIndex].node);
|
||||
}
|
||||
this.emit("reselectedonremoved");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Start listening for mutations until we find a childList change that has
|
||||
// removedNode removed.
|
||||
this._inspector.on("markupmutation", onMutations);
|
||||
},
|
||||
|
||||
/**
|
||||
* Make sure to stop listening for node removal markupmutations and not
|
||||
* reselect the corresponding node when that happens.
|
||||
* Useful when the outerHTML/tagname edition failed.
|
||||
*/
|
||||
cancelReselectOnRemoved: function() {
|
||||
if (this._removedNodeObserver) {
|
||||
this._inspector.off("markupmutation", this._removedNodeObserver);
|
||||
this._removedNodeObserver = null;
|
||||
this.emit("canceledreselectonremoved");
|
||||
}
|
||||
|
||||
return def.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -883,20 +896,12 @@ MarkupView.prototype = {
|
|||
return promise.reject();
|
||||
}
|
||||
|
||||
let def = promise.defer();
|
||||
|
||||
this.getNodeChildIndex(aNode).then((i) => {
|
||||
this._outerHTMLChildIndex = i;
|
||||
this._outerHTMLNode = aNode;
|
||||
|
||||
container.undo.do(() => {
|
||||
this.walker.setOuterHTML(aNode, newValue).then(def.resolve, def.reject);
|
||||
}, () => {
|
||||
this.walker.setOuterHTML(aNode, oldValue).then(def.resolve, def.reject);
|
||||
});
|
||||
// Changing the outerHTML removes the node which outerHTML was changed.
|
||||
// Listen to this removal to reselect the right node afterwards.
|
||||
this.reselectOnRemoved(aNode, "outerhtml");
|
||||
return this.walker.setOuterHTML(aNode, newValue).then(null, () => {
|
||||
this.cancelReselectOnRemoved();
|
||||
});
|
||||
|
||||
return def.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1426,6 +1431,18 @@ MarkupContainer.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* If the node has children, return the list of containers for all these
|
||||
* children.
|
||||
*/
|
||||
getChildContainers: function() {
|
||||
if (!this.hasChildren) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [...this.children.children].map(node => node.container);
|
||||
},
|
||||
|
||||
/**
|
||||
* True if the node has been visually expanded in the tree.
|
||||
*/
|
||||
|
@ -1828,7 +1845,15 @@ RootContainer.prototype = {
|
|||
hasChildren: true,
|
||||
expanded: true,
|
||||
update: function() {},
|
||||
destroy: function() {}
|
||||
destroy: function() {},
|
||||
|
||||
/**
|
||||
* If the node has children, return the list of containers for all these
|
||||
* children.
|
||||
*/
|
||||
getChildContainers: function() {
|
||||
return [...this.children.children].map(node => node.container);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1969,13 +1994,9 @@ function ElementEditor(aContainer, aNode) {
|
|||
// Create the main editor
|
||||
this.template("element", this);
|
||||
|
||||
if (aNode.isLocal_toBeDeprecated()) {
|
||||
this.rawNode = aNode.rawNode();
|
||||
}
|
||||
|
||||
// Make the tag name editable (unless this is a remote node or
|
||||
// a document element)
|
||||
if (this.rawNode && !aNode.isDocumentElement) {
|
||||
if (!aNode.isDocumentElement) {
|
||||
this.tag.setAttribute("tabindex", "0");
|
||||
editableField({
|
||||
element: this.tag,
|
||||
|
@ -2207,57 +2228,19 @@ ElementEditor.prototype = {
|
|||
/**
|
||||
* Called when the tag name editor has is done editing.
|
||||
*/
|
||||
onTagEdit: function(aVal, aCommit) {
|
||||
if (!aCommit || aVal == this.rawNode.tagName) {
|
||||
onTagEdit: function(newTagName, isCommit) {
|
||||
if (!isCommit || newTagName == this.node.tagName ||
|
||||
!("editTagName" in this.markup.walker)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new element with the same attributes as the
|
||||
// current element and prepare to replace the current node
|
||||
// with it.
|
||||
try {
|
||||
var newElt = nodeDocument(this.rawNode).createElement(aVal);
|
||||
} catch(x) {
|
||||
// Failed to create a new element with that tag name, ignore
|
||||
// the change.
|
||||
return;
|
||||
}
|
||||
|
||||
let attrs = this.rawNode.attributes;
|
||||
|
||||
for (let i = 0 ; i < attrs.length; i++) {
|
||||
newElt.setAttribute(attrs[i].name, attrs[i].value);
|
||||
}
|
||||
let newFront = this.markup.walker.frontForRawNode(newElt);
|
||||
let newContainer = this.markup.importNode(newFront);
|
||||
|
||||
// Retain the two nodes we care about here so we can undo.
|
||||
let walker = this.markup.walker;
|
||||
promise.all([
|
||||
walker.retainNode(newFront), walker.retainNode(this.node)
|
||||
]).then(() => {
|
||||
function swapNodes(aOld, aNew) {
|
||||
aOld.parentNode.insertBefore(aNew, aOld);
|
||||
while (aOld.firstChild) {
|
||||
aNew.appendChild(aOld.firstChild);
|
||||
}
|
||||
aOld.parentNode.removeChild(aOld);
|
||||
}
|
||||
|
||||
this.container.undo.do(() => {
|
||||
swapNodes(this.rawNode, newElt);
|
||||
this.markup.setNodeExpanded(newFront, this.container.expanded);
|
||||
if (this.container.selected) {
|
||||
this.markup.navigate(newContainer);
|
||||
}
|
||||
}, () => {
|
||||
swapNodes(newElt, this.rawNode);
|
||||
this.markup.setNodeExpanded(this.node, newContainer.expanded);
|
||||
if (newContainer.selected) {
|
||||
this.markup.navigate(this.container);
|
||||
}
|
||||
});
|
||||
}).then(null, console.error);
|
||||
// Changing the tagName removes the node. Make sure the replacing node gets
|
||||
// selected afterwards.
|
||||
this.markup.reselectOnRemoved(this.node, "edittagname");
|
||||
this.markup.walker.editTagName(this.node, newTagName).then(null, () => {
|
||||
// Failed to edit the tag name, cancel the reselection.
|
||||
this.markup.cancelReselectOnRemoved();
|
||||
});
|
||||
},
|
||||
|
||||
destroy: function() {}
|
||||
|
|
|
@ -80,13 +80,13 @@ skip-if = e10s # Bug 1036409 - The last selected node isn't reselected
|
|||
[browser_markupview_tag_edit_01.js]
|
||||
[browser_markupview_tag_edit_02.js]
|
||||
[browser_markupview_tag_edit_03.js]
|
||||
skip-if = e10s # Bug 1036421 - Tag editing isn't remote-safe
|
||||
[browser_markupview_tag_edit_04.js]
|
||||
[browser_markupview_tag_edit_05.js]
|
||||
[browser_markupview_tag_edit_06.js]
|
||||
[browser_markupview_tag_edit_07.js]
|
||||
[browser_markupview_tag_edit_08.js]
|
||||
[browser_markupview_tag_edit_09.js]
|
||||
[browser_markupview_tag_edit_10.js]
|
||||
[browser_markupview_textcontent_edit_01.js]
|
||||
[browser_markupview_toggle_01.js]
|
||||
[browser_markupview_toggle_02.js]
|
||||
|
|
|
@ -107,10 +107,9 @@ function* testBody(inspector) {
|
|||
let bodyFront = yield getNodeFront("body", inspector);
|
||||
let doc = content.document;
|
||||
|
||||
let mutated = inspector.once("markupmutation");
|
||||
inspector.markup.updateNodeOuterHTML(bodyFront, bodyHTML, body.outerHTML);
|
||||
|
||||
let mutations = yield mutated;
|
||||
let onReselected = inspector.markup.once("reselectedonremoved");
|
||||
yield inspector.markup.updateNodeOuterHTML(bodyFront, bodyHTML, body.outerHTML);
|
||||
yield onReselected;
|
||||
|
||||
is(getNode("body").outerHTML, bodyHTML, "<body> HTML has been updated");
|
||||
is(doc.querySelectorAll("head").length, 1, "no extra <head>s have been added");
|
||||
|
@ -120,14 +119,15 @@ function* testBody(inspector) {
|
|||
|
||||
function* testHead(inspector) {
|
||||
let head = getNode("head");
|
||||
yield selectNode("head", inspector);
|
||||
|
||||
let headHTML = '<head id="updated"><title>New Title</title><script>window.foo="bar";</script></head>';
|
||||
let headFront = yield getNodeFront("head", inspector);
|
||||
let doc = content.document;
|
||||
|
||||
let mutated = inspector.once("markupmutation");
|
||||
inspector.markup.updateNodeOuterHTML(headFront, headHTML, head.outerHTML);
|
||||
|
||||
let mutations = yield mutated;
|
||||
let onReselected = inspector.markup.once("reselectedonremoved");
|
||||
yield inspector.markup.updateNodeOuterHTML(headFront, headHTML, head.outerHTML);
|
||||
yield onReselected;
|
||||
|
||||
is(doc.title, "New Title", "New title has been added");
|
||||
is(doc.defaultView.foo, undefined, "Script has not been executed");
|
||||
|
@ -143,10 +143,9 @@ function* testDocumentElement(inspector) {
|
|||
let docElementHTML = '<html id="updated" foo="bar"><head><title>Updated from document element</title><script>window.foo="bar";</script></head><body><p>Hello</p></body></html>';
|
||||
let docElementFront = yield inspector.markup.walker.documentElement();
|
||||
|
||||
let mutated = inspector.once("markupmutation");
|
||||
inspector.markup.updateNodeOuterHTML(docElementFront, docElementHTML, docElement.outerHTML);
|
||||
|
||||
let mutations = yield mutated;
|
||||
let onReselected = inspector.markup.once("reselectedonremoved");
|
||||
yield inspector.markup.updateNodeOuterHTML(docElementFront, docElementHTML, docElement.outerHTML);
|
||||
yield onReselected;
|
||||
|
||||
is(doc.title, "Updated from document element", "New title has been added");
|
||||
is(doc.defaultView.foo, undefined, "Script has not been executed");
|
||||
|
@ -165,10 +164,9 @@ function* testDocumentElement2(inspector) {
|
|||
let docElementHTML = '<html class="updated" id="somethingelse"><head><title>Updated again from document element</title><script>window.foo="bar";</script></head><body><p>Hello again</p></body></html>';
|
||||
let docElementFront = yield inspector.markup.walker.documentElement();
|
||||
|
||||
let mutated = inspector.once("markupmutation");
|
||||
let onReselected = inspector.markup.once("reselectedonremoved");
|
||||
inspector.markup.updateNodeOuterHTML(docElementFront, docElementHTML, docElement.outerHTML);
|
||||
|
||||
let mutations = yield mutated;
|
||||
yield onReselected;
|
||||
|
||||
is(doc.title, "Updated again from document element", "New title has been added");
|
||||
is(doc.defaultView.foo, undefined, "Script has not been executed");
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
// Tests that a node's tagname can be edited in the markup-view
|
||||
|
||||
const TEST_URL = "data:text/html,<div id='retag-me'><div id='retag-me-2'></div></div>";
|
||||
const TEST_URL = "data:text/html;charset=utf-8,<div id='retag-me'><div id='retag-me-2'></div></div>";
|
||||
|
||||
let test = asyncTest(function*() {
|
||||
let {toolbox, inspector} = yield addTab(TEST_URL).then(openInspector);
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Tests that invalid tagname updates are handled correctly
|
||||
|
||||
const TEST_URL = "data:text/html;charset=utf-8,<div></div>";
|
||||
|
||||
let test = asyncTest(function*() {
|
||||
let {toolbox, inspector} = yield addTab(TEST_URL).then(openInspector);
|
||||
yield inspector.markup.expandAll();
|
||||
yield selectNode("div", inspector);
|
||||
|
||||
info("Updating the DIV tagname to an invalid value");
|
||||
let container = yield getContainerForSelector("div", inspector);
|
||||
let onCancelReselect = inspector.markup.once("canceledreselectonremoved");
|
||||
let tagEditor = container.editor.tag;
|
||||
setEditableFieldValue(tagEditor, "<<<", inspector);
|
||||
yield onCancelReselect;
|
||||
ok(true, "The markup-view emitted the canceledreselectonremoved event");
|
||||
is(inspector.selection.nodeFront, container.node, "The test DIV is still selected");
|
||||
|
||||
info("Updating the DIV tagname to a valid value this time");
|
||||
let onReselect = inspector.markup.once("reselectedonremoved");
|
||||
setEditableFieldValue(tagEditor, "span", inspector);
|
||||
yield onReselect;
|
||||
ok(true, "The markup-view emitted the reselectedonremoved event");
|
||||
|
||||
let spanFront = yield getNodeFront("span", inspector);
|
||||
is(inspector.selection.nodeFront, spanFront, "The seelected node is now the SPAN");
|
||||
});
|
|
@ -44,25 +44,11 @@ function* runEditOuterHTMLTest(test, inspector) {
|
|||
|
||||
let onUpdated = inspector.once("inspector-updated");
|
||||
|
||||
info("Listening for the markupmutation event");
|
||||
// This event fires once the outerHTML is set, with a target as the parent node and a type of "childList".
|
||||
let mutated = inspector.once("markupmutation");
|
||||
info("Editing the outerHTML");
|
||||
inspector.markup.updateNodeOuterHTML(inspector.selection.nodeFront, test.newHTML, test.oldHTML);
|
||||
let mutations = yield mutated;
|
||||
ok(true, "The markupmutation event has fired, mutation done");
|
||||
|
||||
info("Check to make the sure the correct mutation event was fired, and that the parent is selected");
|
||||
let nodeFront = inspector.selection.nodeFront;
|
||||
let mutation = mutations[0];
|
||||
let isFromOuterHTML = mutation.removed.some(n => n === oldNodeFront);
|
||||
|
||||
ok(isFromOuterHTML, "The node is in the 'removed' list of the mutation");
|
||||
is(mutation.type, "childList", "Mutation is a childList after updating outerHTML");
|
||||
is(mutation.target, nodeFront, "Parent node is selected immediately after setting outerHTML");
|
||||
|
||||
// Wait for node to be reselected after outerHTML has been set
|
||||
yield inspector.selection.once("new-node-front");
|
||||
info("Listen for reselectedonremoved and edit the outerHTML");
|
||||
let onReselected = inspector.markup.once("reselectedonremoved");
|
||||
yield inspector.markup.updateNodeOuterHTML(inspector.selection.nodeFront,
|
||||
test.newHTML, test.oldHTML);
|
||||
yield onReselected;
|
||||
|
||||
// Typically selectedNode will === pageNode, but if a new element has been injected in front
|
||||
// of it, this will not be the case. If this happens.
|
||||
|
|
|
@ -69,14 +69,12 @@ var ResourceContainer = Class({
|
|||
this.elt.appendChild(this.children);
|
||||
|
||||
this.line.addEventListener("click", (evt) => {
|
||||
if (!this.selected) {
|
||||
this.select();
|
||||
this.expanded = true;
|
||||
evt.stopPropagation();
|
||||
}
|
||||
this.select();
|
||||
this.toggleExpansion();
|
||||
evt.stopPropagation();
|
||||
}, false);
|
||||
this.expander.addEventListener("click", (evt) => {
|
||||
this.expanded = !this.expanded;
|
||||
this.toggleExpansion();
|
||||
this.select();
|
||||
evt.stopPropagation();
|
||||
}, true);
|
||||
|
@ -87,6 +85,14 @@ var ResourceContainer = Class({
|
|||
this.update();
|
||||
},
|
||||
|
||||
toggleExpansion: function() {
|
||||
if (!this.resource.isRoot) {
|
||||
this.expanded = !this.expanded;
|
||||
} else {
|
||||
this.expanded = true;
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.elt.remove();
|
||||
this.expander.remove();
|
||||
|
|
|
@ -39,10 +39,16 @@ function selectFileFirstLoad(projecteditor, resource) {
|
|||
|
||||
if (resource.isRoot) {
|
||||
ok (container.expanded, "The root directory is expanded by default.");
|
||||
container.line.click();
|
||||
ok (container.expanded, "Clicking on the line does not toggles expansion.");
|
||||
return;
|
||||
}
|
||||
if (resource.isDir) {
|
||||
ok (!container.expanded, "A directory is not expanded by default.");
|
||||
container.line.click();
|
||||
ok (container.expanded, "Clicking on the line toggles expansion.");
|
||||
container.line.click();
|
||||
ok (!container.expanded, "Clicking on the line toggles expansion.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -272,3 +272,8 @@ feedback_rejoin_button=Rejoin
|
|||
## LOCALIZATION NOTE (feedback_report_user_button): Used to report a user in the case of
|
||||
## an abusive user.
|
||||
feedback_report_user_button=Report User
|
||||
|
||||
## LOCALIZATION NOTE (rooms_list_current_conversations): We prefer to have no
|
||||
## number in the string, but if you need it for your language please use {{num}}.
|
||||
rooms_list_current_conversations=Current conversation;Current conversations
|
||||
rooms_list_no_current_conversations=No current conversations
|
||||
|
|
|
@ -368,6 +368,11 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
if (aConnectionStatus != 0) {
|
||||
mImpl->mConsumer->NotifyError();
|
||||
return;
|
||||
}
|
||||
|
||||
mImpl->mConsumer->SetAddress(aBdAddress);
|
||||
XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new AcceptTask(mImpl, aFd));
|
||||
}
|
||||
|
@ -376,6 +381,14 @@ public:
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
BT_LOGR("BluetoothSocketInterface::Accept failed: %d", (int)aStatus);
|
||||
|
||||
if (!mImpl->IsShutdownOnMainThread()) {
|
||||
// Instead of NotifyError(), call NotifyDisconnect() to trigger
|
||||
// BluetoothOppManager::OnSocketDisconnect() as
|
||||
// DroidSocketImpl::OnFileCanReadWithoutBlocking() in Firefox OS 2.0 in
|
||||
// order to keep the same behavior and reduce regression risk.
|
||||
mImpl->mConsumer->NotifyDisconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -504,9 +517,17 @@ public:
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!mImpl->IsShutdownOnMainThread()) {
|
||||
mImpl->mConsumer->SetAddress(aBdAddress);
|
||||
if (mImpl->IsShutdownOnMainThread()) {
|
||||
BT_LOGD("mConsumer is null, aborting send!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (aConnectionStatus != 0) {
|
||||
mImpl->mConsumer->NotifyError();
|
||||
return;
|
||||
}
|
||||
|
||||
mImpl->mConsumer->SetAddress(aBdAddress);
|
||||
XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
|
||||
new SocketConnectTask(mImpl, aFd));
|
||||
}
|
||||
|
@ -515,6 +536,14 @@ public:
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
BT_WARNING("Connect failed: %d", (int)aStatus);
|
||||
|
||||
if (!mImpl->IsShutdownOnMainThread()) {
|
||||
// Instead of NotifyError(), call NotifyDisconnect() to trigger
|
||||
// BluetoothOppManager::OnSocketDisconnect() as
|
||||
// DroidSocketImpl::OnFileCanReadWithoutBlocking() in Firefox OS 2.0 in
|
||||
// order to keep the same behavior and reduce regression risk.
|
||||
mImpl->mConsumer->NotifyDisconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -286,7 +286,7 @@ private:
|
|||
msg.msg_iovlen = 1;
|
||||
|
||||
ssize_t res = TEMP_FAILURE_RETRY(recvmsg(mFd, &msg, MSG_NOSIGNAL));
|
||||
if (res < 0) {
|
||||
if (res <= 0) {
|
||||
return STATUS_FAIL;
|
||||
}
|
||||
|
||||
|
@ -311,7 +311,7 @@ private:
|
|||
msg.msg_controllen = sizeof(cmsgbuf);
|
||||
|
||||
ssize_t res = TEMP_FAILURE_RETRY(recvmsg(mFd, &msg, MSG_NOSIGNAL));
|
||||
if (res < 0) {
|
||||
if (res <= 0) {
|
||||
return STATUS_FAIL;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,10 +16,22 @@ XPCOMUtils.defineLazyGetter(this, "RIL", function () {
|
|||
return obj;
|
||||
});
|
||||
|
||||
const kMozSettingsChangedObserverTopic = "mozsettings-changed";
|
||||
const kSettingsCellBroadcastDisabled = "ril.cellbroadcast.disabled";
|
||||
const kSettingsCellBroadcastSearchList = "ril.cellbroadcast.searchlist";
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
|
||||
"@mozilla.org/system-message-internal;1",
|
||||
"nsISystemMessagesInternal");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
|
||||
"@mozilla.org/settingsService;1",
|
||||
"nsISettingsService");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gRadioInterfaceLayer",
|
||||
"@mozilla.org/ril;1",
|
||||
"nsIRadioInterfaceLayer");
|
||||
|
||||
const GONK_CELLBROADCAST_SERVICE_CONTRACTID =
|
||||
"@mozilla.org/cellbroadcast/gonkservice;1";
|
||||
const GONK_CELLBROADCAST_SERVICE_CID =
|
||||
|
@ -41,7 +53,36 @@ function CellBroadcastService() {
|
|||
|
||||
this._updateDebugFlag();
|
||||
|
||||
let lock = gSettingsService.createLock();
|
||||
|
||||
/**
|
||||
* Read the settings of the toggle of Cellbroadcast Service:
|
||||
*
|
||||
* Simple Format: Boolean
|
||||
* true if CBS is disabled. The value is applied to all RadioInterfaces.
|
||||
* Enhanced Format: Array of Boolean
|
||||
* Each element represents the toggle of CBS per RadioInterface.
|
||||
*/
|
||||
lock.get(kSettingsCellBroadcastDisabled, this);
|
||||
|
||||
/**
|
||||
* Read the Cell Broadcast Search List setting to set listening channels:
|
||||
*
|
||||
* Simple Format:
|
||||
* String of integers or integer ranges separated by comma.
|
||||
* For example, "1, 2, 4-6"
|
||||
* Enhanced Format:
|
||||
* Array of Objects with search lists specified in gsm/cdma network.
|
||||
* For example, [{'gsm' : "1, 2, 4-6", 'cdma' : "1, 50, 99"},
|
||||
* {'cdma' : "3, 6, 8-9"}]
|
||||
* This provides the possibility to
|
||||
* 1. set gsm/cdma search list individually for CDMA+LTE device.
|
||||
* 2. set search list per RadioInterface.
|
||||
*/
|
||||
lock.get(kSettingsCellBroadcastSearchList, this);
|
||||
|
||||
Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
||||
Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
|
||||
}
|
||||
CellBroadcastService.prototype = {
|
||||
classID: GONK_CELLBROADCAST_SERVICE_CID,
|
||||
|
@ -55,11 +96,15 @@ CellBroadcastService.prototype = {
|
|||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsICellBroadcastService,
|
||||
Ci.nsIGonkCellBroadcastService,
|
||||
Ci.nsISettingsServiceCallback,
|
||||
Ci.nsIObserver]),
|
||||
|
||||
// An array of nsICellBroadcastListener instances.
|
||||
_listeners: null,
|
||||
|
||||
// Setting values of Cell Broadcast SearchList.
|
||||
_cellBroadcastSearchList: null,
|
||||
|
||||
_updateDebugFlag: function() {
|
||||
try {
|
||||
DEBUG = RIL.DEBUG_RIL ||
|
||||
|
@ -85,6 +130,74 @@ CellBroadcastService.prototype = {
|
|||
: RIL.CB_ETWS_WARNING_TYPE_NAMES[aWarningType];
|
||||
},
|
||||
|
||||
_retrieveSettingValueByClient: function(aClientId, aSettings) {
|
||||
return Array.isArray(aSettings) ? aSettings[aClientId] : aSettings;
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper function to set CellBroadcastDisabled to each RadioInterface.
|
||||
*/
|
||||
setCellBroadcastDisabled: function(aSettings) {
|
||||
let numOfRilClients = gRadioInterfaceLayer.numRadioInterfaces;
|
||||
let responses = [];
|
||||
for (let clientId = 0; clientId < numOfRilClients; clientId++) {
|
||||
gRadioInterfaceLayer
|
||||
.getRadioInterface(clientId)
|
||||
.sendWorkerMessage("setCellBroadcastDisabled",
|
||||
{ disabled: this._retrieveSettingValueByClient(clientId, aSettings) });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper function to set CellBroadcastSearchList to each RadioInterface.
|
||||
*/
|
||||
setCellBroadcastSearchList: function(aSettings) {
|
||||
let numOfRilClients = gRadioInterfaceLayer.numRadioInterfaces;
|
||||
let responses = [];
|
||||
for (let clientId = 0; clientId < numOfRilClients; clientId++) {
|
||||
let newSearchList = this._retrieveSettingValueByClient(clientId, aSettings);
|
||||
let oldSearchList = this._retrieveSettingValueByClient(clientId,
|
||||
this._cellBroadcastSearchList);
|
||||
|
||||
if ((newSearchList == oldSearchList) ||
|
||||
(newSearchList && oldSearchList &&
|
||||
newSearchList.gsm == oldSearchList.gsm &&
|
||||
newSearchList.cdma == oldSearchList.cdma)) {
|
||||
return;
|
||||
}
|
||||
|
||||
gRadioInterfaceLayer
|
||||
.getRadioInterface(clientId).sendWorkerMessage("setCellBroadcastSearchList",
|
||||
{ searchList: newSearchList },
|
||||
(function callback(aResponse) {
|
||||
if (DEBUG && !aResponse.success) {
|
||||
debug("Failed to set new search list: " + newSearchList +
|
||||
" to client id: " + clientId);
|
||||
}
|
||||
|
||||
responses.push(aResponse);
|
||||
if (responses.length == numOfRilClients) {
|
||||
let successCount = 0;
|
||||
for (let i = 0; i < responses.length; i++) {
|
||||
if (responses[i].success) {
|
||||
successCount++;
|
||||
}
|
||||
}
|
||||
if (successCount == numOfRilClients) {
|
||||
this._cellBroadcastSearchList = aSettings;
|
||||
} else {
|
||||
// Rollback the change when failure.
|
||||
let lock = gSettingsService.createLock();
|
||||
lock.set(kSettingsCellBroadcastSearchList,
|
||||
this._cellBroadcastSearchList, null);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}).bind(this));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* nsICellBroadcastService interface
|
||||
*/
|
||||
|
@ -178,13 +291,44 @@ CellBroadcastService.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* nsISettingsServiceCallback interface.
|
||||
*/
|
||||
handle: function(aName, aResult) {
|
||||
switch (aName) {
|
||||
case kSettingsCellBroadcastSearchList:
|
||||
if (DEBUG) {
|
||||
debug("'" + kSettingsCellBroadcastSearchList +
|
||||
"' is now " + JSON.stringify(aResult));
|
||||
}
|
||||
|
||||
this.setCellBroadcastSearchList(aResult);
|
||||
break;
|
||||
case kSettingsCellBroadcastDisabled:
|
||||
if (DEBUG) {
|
||||
debug("'" + kSettingsCellBroadcastDisabled +
|
||||
"' is now " + JSON.stringify(aResult));
|
||||
}
|
||||
|
||||
this.setCellBroadcastDisabled(aResult);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* nsIObserver interface.
|
||||
*/
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case kMozSettingsChangedObserverTopic:
|
||||
if ("wrappedJSObject" in aSubject) {
|
||||
aSubject = aSubject.wrappedJSObject;
|
||||
}
|
||||
this.handle(aSubject.key, aSubject.value);
|
||||
break;
|
||||
case NS_XPCOM_SHUTDOWN_OBSERVER_ID:
|
||||
Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
|
||||
Services.obs.removeObserver(this, kMozSettingsChangedObserverTopic);
|
||||
|
||||
// Remove all listeners.
|
||||
this._listeners = [];
|
||||
|
|
|
@ -4,3 +4,4 @@
|
|||
|
||||
component {7ba407ce-21fd-11e4-a836-1bfdee377e5c} CellBroadcastService.js
|
||||
contract @mozilla.org/cellbroadcast/gonkservice;1 {7ba407ce-21fd-11e4-a836-1bfdee377e5c}
|
||||
category profile-after-change CellBroadcastService @mozilla.org/cellbroadcast/gonkservice;1
|
||||
|
|
|
@ -743,20 +743,33 @@ let SettingsRequestManager = {
|
|||
}
|
||||
},
|
||||
|
||||
removeMessageManager: function(aMsgMgr){
|
||||
removeMessageManager: function(aMsgMgr, aPrincipal) {
|
||||
if (DEBUG) debug("Removing message manager");
|
||||
this.removeObserver(aMsgMgr);
|
||||
let closedLockIDs = [];
|
||||
let lockIDs = Object.keys(this.lockInfo);
|
||||
for (let i in lockIDs) {
|
||||
if (this.lockInfo[lockIDs[i]]._mm == aMsgMgr) {
|
||||
if (DEBUG) debug("Removing lock " + lockIDs[i] + " due to process close/crash");
|
||||
closedLockIDs.push(lockIDs[i]);
|
||||
let lock = this.lockInfo[lockIDs[i]];
|
||||
if (lock._mm == aMsgMgr) {
|
||||
let is_finalizing = false;
|
||||
for (let task_index in lock.tasks) {
|
||||
if (lock.tasks[task_index].operation === "finalize") {
|
||||
is_finalizing = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!is_finalizing) {
|
||||
this.queueTask("finalize", {lockID: lockIDs[i]}, aPrincipal).then(
|
||||
function() {
|
||||
if (DEBUG) debug("Lock " + lockIDs[i] + " with dead message manager finalized");
|
||||
},
|
||||
function(error) {
|
||||
if (DEBUG) debug("Lock " + lockIDs[i] + " with dead message manager NOT FINALIZED due to error: " + error);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let i in closedLockIDs) {
|
||||
this.removeLock(closedLockIDs[i]);
|
||||
}
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
|
@ -812,7 +825,7 @@ let SettingsRequestManager = {
|
|||
switch (aMessage.name) {
|
||||
case "child-process-shutdown":
|
||||
if (DEBUG) debug("Child process shutdown received.");
|
||||
this.removeMessageManager(mm);
|
||||
this.removeMessageManager(mm, aMessage.principal);
|
||||
break;
|
||||
case "Settings:RegisterForMessages":
|
||||
if (!SettingsPermissions.hasSomeReadPermission(aMessage.principal)) {
|
||||
|
|
|
@ -697,12 +697,18 @@ MozMtpDatabase::getNumObjects(MtpStorageID aStorageID,
|
|||
MtpObjectFormatList*
|
||||
MozMtpDatabase::getSupportedPlaybackFormats()
|
||||
{
|
||||
static const uint16_t init_data[] = {MTP_FORMAT_UNDEFINED, MTP_FORMAT_ASSOCIATION, MTP_FORMAT_PNG};
|
||||
static const uint16_t init_data[] = {MTP_FORMAT_UNDEFINED, MTP_FORMAT_ASSOCIATION,
|
||||
MTP_FORMAT_TEXT, MTP_FORMAT_HTML, MTP_FORMAT_WAV,
|
||||
MTP_FORMAT_MP3, MTP_FORMAT_MPEG, MTP_FORMAT_EXIF_JPEG,
|
||||
MTP_FORMAT_TIFF_EP, MTP_FORMAT_BMP, MTP_FORMAT_GIF,
|
||||
MTP_FORMAT_PNG, MTP_FORMAT_TIFF, MTP_FORMAT_WMA,
|
||||
MTP_FORMAT_OGG, MTP_FORMAT_AAC, MTP_FORMAT_MP4_CONTAINER,
|
||||
MTP_FORMAT_MP2, MTP_FORMAT_3GP_CONTAINER, MTP_FORMAT_FLAC};
|
||||
|
||||
MtpObjectFormatList *list = new MtpObjectFormatList();
|
||||
list->appendArray(init_data, MOZ_ARRAY_LENGTH(init_data));
|
||||
|
||||
MTP_LOG("returning MTP_FORMAT_UNDEFINED, MTP_FORMAT_ASSOCIATION, MTP_FORMAT_PNG");
|
||||
MTP_LOG("returning Supported Playback Formats");
|
||||
return list;
|
||||
}
|
||||
|
||||
|
|
|
@ -78,8 +78,6 @@ const kSysMsgListenerReadyObserverTopic = "system-message-listener-ready";
|
|||
const kSysClockChangeObserverTopic = "system-clock-change";
|
||||
const kScreenStateChangedTopic = "screen-state-changed";
|
||||
|
||||
const kSettingsCellBroadcastDisabled = "ril.cellbroadcast.disabled";
|
||||
const kSettingsCellBroadcastSearchList = "ril.cellbroadcast.searchlist";
|
||||
const kSettingsClockAutoUpdateEnabled = "time.clock.automatic-update.enabled";
|
||||
const kSettingsClockAutoUpdateAvailable = "time.clock.automatic-update.available";
|
||||
const kSettingsTimezoneAutoUpdateEnabled = "time.timezone.automatic-update.enabled";
|
||||
|
@ -1823,32 +1821,6 @@ function RadioInterface(aClientId, aWorkerMessenger) {
|
|||
// Set "time.timezone.automatic-update.available" to false when starting up.
|
||||
this.setTimezoneAutoUpdateAvailable(false);
|
||||
|
||||
/**
|
||||
* Read the settings of the toggle of Cellbroadcast Service:
|
||||
*
|
||||
* Simple Format: Boolean
|
||||
* true if CBS is disabled. The value is applied to all RadioInterfaces.
|
||||
* Enhanced Format: Array of Boolean
|
||||
* Each element represents the toggle of CBS per RadioInterface.
|
||||
*/
|
||||
lock.get(kSettingsCellBroadcastDisabled, this);
|
||||
|
||||
/**
|
||||
* Read the Cell Broadcast Search List setting to set listening channels:
|
||||
*
|
||||
* Simple Format:
|
||||
* String of integers or integer ranges separated by comma.
|
||||
* For example, "1, 2, 4-6"
|
||||
* Enhanced Format:
|
||||
* Array of Objects with search lists specified in gsm/cdma network.
|
||||
* For example, [{'gsm' : "1, 2, 4-6", 'cdma' : "1, 50, 99"},
|
||||
* {'cdma' : "3, 6, 8-9"}]
|
||||
* This provides the possibility to
|
||||
* 1. set gsm/cdma search list individually for CDMA+LTE device.
|
||||
* 2. set search list per RadioInterface.
|
||||
*/
|
||||
lock.get(kSettingsCellBroadcastSearchList, this);
|
||||
|
||||
Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
|
||||
Services.obs.addObserver(this, kSysClockChangeObserverTopic, false);
|
||||
Services.obs.addObserver(this, kScreenStateChangedTopic, false);
|
||||
|
@ -2241,36 +2213,6 @@ RadioInterface.prototype = {
|
|||
});
|
||||
},
|
||||
|
||||
setCellBroadcastSearchList: function(settings) {
|
||||
let newSearchList =
|
||||
Array.isArray(settings) ? settings[this.clientId] : settings;
|
||||
let oldSearchList =
|
||||
Array.isArray(this._cellBroadcastSearchList) ?
|
||||
this._cellBroadcastSearchList[this.clientId] :
|
||||
this._cellBroadcastSearchList;
|
||||
|
||||
if ((newSearchList == oldSearchList) ||
|
||||
(newSearchList && oldSearchList &&
|
||||
newSearchList.gsm == oldSearchList.gsm &&
|
||||
newSearchList.cdma == oldSearchList.cdma)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.workerMessenger.send("setCellBroadcastSearchList",
|
||||
{ searchList: newSearchList },
|
||||
(function callback(response) {
|
||||
if (!response.success) {
|
||||
let lock = gSettingsService.createLock();
|
||||
lock.set(kSettingsCellBroadcastSearchList,
|
||||
this._cellBroadcastSearchList, null);
|
||||
} else {
|
||||
this._cellBroadcastSearchList = settings;
|
||||
}
|
||||
|
||||
return false;
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
setDataRegistration: function(attach) {
|
||||
let deferred = Promise.defer();
|
||||
this.workerMessenger.send("setDataRegistration",
|
||||
|
@ -3105,25 +3047,6 @@ RadioInterface.prototype = {
|
|||
}
|
||||
}
|
||||
break;
|
||||
case kSettingsCellBroadcastSearchList:
|
||||
if (DEBUG) {
|
||||
this.debug("'" + kSettingsCellBroadcastSearchList +
|
||||
"' is now " + JSON.stringify(aResult));
|
||||
}
|
||||
|
||||
this.setCellBroadcastSearchList(aResult);
|
||||
break;
|
||||
case kSettingsCellBroadcastDisabled:
|
||||
if (DEBUG) {
|
||||
this.debug("'" + kSettingsCellBroadcastDisabled +
|
||||
"' is now " + JSON.stringify(aResult));
|
||||
}
|
||||
|
||||
let setCbsDisabled =
|
||||
Array.isArray(aResult) ? aResult[this.clientId] : aResult;
|
||||
this.workerMessenger.send("setCellBroadcastDisabled",
|
||||
{ disabled: setCbsDisabled });
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -183,38 +183,6 @@ DOMWifiManager.prototype = {
|
|||
return capabilities;
|
||||
},
|
||||
|
||||
_genReadonlyPropDesc: function(value) {
|
||||
return {
|
||||
enumerable: true, configurable: false, writable: false, value: value
|
||||
};
|
||||
},
|
||||
|
||||
_convertWifiCertificateInfo: function(aInfo) {
|
||||
let propList = {};
|
||||
for (let k in aInfo) {
|
||||
propList[k] = this._genReadonlyPropDesc(aInfo[k]);
|
||||
}
|
||||
|
||||
let info = Cu.createObjectIn(this._window);
|
||||
Object.defineProperties(info, propList);
|
||||
Cu.makeObjectPropsNormal(info);
|
||||
|
||||
return info;
|
||||
},
|
||||
|
||||
_convertWifiCertificateList: function(aList) {
|
||||
let propList = {};
|
||||
for (let k in aList) {
|
||||
propList[k] = this._genReadonlyPropDesc(aList[k]);
|
||||
}
|
||||
|
||||
let list = Cu.createObjectIn(this._window);
|
||||
Object.defineProperties(list, propList);
|
||||
Cu.makeObjectPropsNormal(list);
|
||||
|
||||
return list;
|
||||
},
|
||||
|
||||
_sendMessageForRequest: function(name, data, request) {
|
||||
let id = this.getRequestId(request);
|
||||
this._mm.sendAsyncMessage(name, { data: data, rid: id, mid: this._id });
|
||||
|
@ -307,7 +275,7 @@ DOMWifiManager.prototype = {
|
|||
break;
|
||||
|
||||
case "WifiManager:importCert:Return:OK":
|
||||
Services.DOMRequest.fireSuccess(request, this._convertWifiCertificateInfo(msg.data));
|
||||
Services.DOMRequest.fireSuccess(request, Cu.cloneInto(msg.data, this._window));
|
||||
break;
|
||||
|
||||
case "WifiManager:importCert:Return:NO":
|
||||
|
@ -315,7 +283,7 @@ DOMWifiManager.prototype = {
|
|||
break;
|
||||
|
||||
case "WifiManager:getImportedCerts:Return:OK":
|
||||
Services.DOMRequest.fireSuccess(request, this._convertWifiCertificateList(msg.data));
|
||||
Services.DOMRequest.fireSuccess(request, Cu.cloneInto(msg.data, this._window));
|
||||
break;
|
||||
|
||||
case "WifiManager:getImportedCerts:Return:NO":
|
||||
|
|
|
@ -85,7 +85,6 @@ import android.content.ContentValues;
|
|||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
|
@ -160,7 +159,8 @@ public class BrowserApp extends GeckoApp
|
|||
|
||||
// Request ID for startActivityForResult.
|
||||
private static final int ACTIVITY_REQUEST_PREFERENCES = 1001;
|
||||
public static final String ACTION_NEW_PROFILE = "org.mozilla.gecko.NEW_PROFILE";
|
||||
|
||||
public static final String PREF_STARTPANE_ENABLED = "startpane_enabled";
|
||||
|
||||
private BrowserSearch mBrowserSearch;
|
||||
private View mBrowserSearchContainer;
|
||||
|
@ -621,8 +621,6 @@ public class BrowserApp extends GeckoApp
|
|||
"Updater:Launch",
|
||||
"BrowserToolbar:Visibility");
|
||||
|
||||
registerOnboardingReceiver(this);
|
||||
|
||||
Distribution distribution = Distribution.init(this);
|
||||
|
||||
// Init suggested sites engine in BrowserDB.
|
||||
|
@ -688,24 +686,31 @@ public class BrowserApp extends GeckoApp
|
|||
tintManager.setStatusBarTintEnabled(true);
|
||||
}
|
||||
|
||||
private void registerOnboardingReceiver(Context context) {
|
||||
final LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
|
||||
/**
|
||||
* Check and show Onboarding start pane if Firefox has never been launched and
|
||||
* is not opening an external link from another application.
|
||||
*
|
||||
* @param context Context of application; used to show Start Pane if appropriate
|
||||
* @param intentAction Intent that launched this activity
|
||||
*/
|
||||
private void checkStartPane(Context context, String intentAction) {
|
||||
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
|
||||
|
||||
// Receiver for launching first run start pane on new profile creation.
|
||||
mOnboardingReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
launchStartPane(BrowserApp.this);
|
||||
try {
|
||||
final SharedPreferences prefs = GeckoSharedPrefs.forProfile(this);
|
||||
|
||||
if (prefs.getBoolean(PREF_STARTPANE_ENABLED, false)) {
|
||||
if (!Intent.ACTION_VIEW.equals(intentAction)) {
|
||||
final Intent startIntent = new Intent(this, StartPane.class);
|
||||
context.startActivity(startIntent);
|
||||
}
|
||||
// Don't bother trying again to show the v1 minimal first run.
|
||||
prefs.edit().putBoolean(PREF_STARTPANE_ENABLED, false).apply();
|
||||
}
|
||||
};
|
||||
|
||||
lbm.registerReceiver(mOnboardingReceiver, new IntentFilter(ACTION_NEW_PROFILE));
|
||||
}
|
||||
|
||||
private void launchStartPane(Context context) {
|
||||
final Intent startIntent = new Intent(context, StartPane.class);
|
||||
context.startActivity(startIntent);
|
||||
}
|
||||
} finally {
|
||||
StrictMode.setThreadPolicy(savedPolicy);
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> getMediaPlayerManager() {
|
||||
if (AppConstants.MOZ_MEDIA_PLAYER) {
|
||||
|
@ -740,6 +745,12 @@ public class BrowserApp extends GeckoApp
|
|||
super.onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToWindow() {
|
||||
// We can't show Onboarding until Gecko has finished initialization (bug 1077583).
|
||||
checkStartPane(this, getIntent().getAction());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
@ -2668,7 +2679,6 @@ public class BrowserApp extends GeckoApp
|
|||
// or if the user has explicitly enabled the clear on shutdown pref.
|
||||
// (We check the pref last to save the pref read.)
|
||||
// In ICS+, it's easy to kill an app through the task switcher.
|
||||
final SharedPreferences prefs = GeckoSharedPrefs.forProfile(this);
|
||||
final boolean visible = Versions.preICS ||
|
||||
HardwareUtils.isTelevision() ||
|
||||
!PrefUtils.getStringSet(GeckoSharedPrefs.forProfile(this),
|
||||
|
|
|
@ -37,7 +37,7 @@ import android.util.Log;
|
|||
class ChromeCast implements GeckoMediaPlayer {
|
||||
private static final boolean SHOW_DEBUG = false;
|
||||
|
||||
static final String MIRROR_RECIEVER_APP_ID = "D40D28D6";
|
||||
static final String MIRROR_RECIEVER_APP_ID = "5F72F863";
|
||||
|
||||
private final Context context;
|
||||
private final RouteInfo route;
|
||||
|
|
|
@ -27,6 +27,7 @@ import android.app.Activity;
|
|||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
@ -692,9 +693,9 @@ public final class GeckoProfile {
|
|||
Log.w(LOGTAG, "Couldn't write times.json.", e);
|
||||
}
|
||||
|
||||
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(mApplicationContext);
|
||||
final Intent intent = new Intent(BrowserApp.ACTION_NEW_PROFILE);
|
||||
lbm.sendBroadcast(intent);
|
||||
// Initialize pref flag for displaying the start pane for a new profile.
|
||||
final SharedPreferences prefs = GeckoSharedPrefs.forProfile(mApplicationContext);
|
||||
prefs.edit().putBoolean(BrowserApp.PREF_STARTPANE_ENABLED, true).apply();
|
||||
|
||||
return profileDir;
|
||||
}
|
||||
|
|
|
@ -92,9 +92,7 @@ ALL_JARS += webrtc.jar
|
|||
endif
|
||||
|
||||
ifdef MOZ_ANDROID_SEARCH_ACTIVITY
|
||||
extra_packages += org.mozilla.search
|
||||
ALL_JARS += search-activity.jar
|
||||
generated/org/mozilla/search/R.java: .aapt.deps ;
|
||||
endif
|
||||
|
||||
ifdef MOZ_ANDROID_MLS_STUMBLER
|
||||
|
|
|
@ -1013,7 +1013,14 @@ public class LocalBrowserDB {
|
|||
|
||||
try {
|
||||
if (c.moveToFirst()) {
|
||||
return c.getString(c.getColumnIndexOrThrow(History.FAVICON_URL));
|
||||
// Interrupted page loads can leave History items without a valid favicon_id.
|
||||
final int columnIndex = c.getColumnIndexOrThrow(History.FAVICON_URL);
|
||||
if (!c.isNull(columnIndex)) {
|
||||
final String faviconURL = c.getString(columnIndex);
|
||||
if (faviconURL != null) {
|
||||
return faviconURL;
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
c.close();
|
||||
|
|
|
@ -659,9 +659,6 @@ if CONFIG['MOZ_ANDROID_SEARCH_ACTIVITY']:
|
|||
search_source_dir = SRCDIR + '/../search'
|
||||
include('../search/search_activity_sources.mozbuild')
|
||||
|
||||
ANDROID_RES_DIRS += [search_source_dir + '/res']
|
||||
resjar.generated_sources += ['org/mozilla/search/R.java']
|
||||
|
||||
search_activity = add_java_jar('search-activity')
|
||||
search_activity.sources += [search_source_dir + '/' + f for f in search_activity_sources]
|
||||
search_activity.javac_flags += ['-Xlint:all']
|
||||
|
@ -831,17 +828,5 @@ if CONFIG['MOZ_ANDROID_MLS_STUMBLER']:
|
|||
DEFINES['MOZ_STUMBLER_API_KEY'] = CONFIG['MOZ_MOZILLA_API_KEY']
|
||||
|
||||
if CONFIG['MOZ_ANDROID_SEARCH_ACTIVITY']:
|
||||
searchres = add_android_eclipse_library_project('FennecResourcesSearch')
|
||||
# Eclipse generates org.mozilla.search.R for this project, which is
|
||||
# referenced by the search/**/*.java code.
|
||||
searchres.package_name = 'org.mozilla.search'
|
||||
searchres.res = SRCDIR + '/../search/res'
|
||||
|
||||
searchres.included_projects += ['../' + static.name, '../' + generated.name, '../' + branding.name]
|
||||
searchres.referenced_projects += ['../' + static.name, '../' + generated.name, '../' + branding.name]
|
||||
|
||||
resources.included_projects += ['../' + searchres.name]
|
||||
resources.referenced_projects += ['../' + searchres.name]
|
||||
|
||||
# The Search Activity code is built as part of Fennec, so we follow suit in Eclipse.
|
||||
main.add_classpathentry('search', TOPSRCDIR + '/mobile/android/search/java', dstdir='search')
|
||||
|
|
|
@ -43,12 +43,12 @@
|
|||
android:layout_toRightOf="@id/back"
|
||||
android:layout_toLeftOf="@id/menu_items"/>
|
||||
|
||||
<!-- Values of marginLeft are used to animate the forward button so don't change its value. -->
|
||||
<!-- Note: * Values of marginLeft are used to animate the forward button so don't change its value.
|
||||
* We set the padding on the site security icon to increase its tappable area. -->
|
||||
<org.mozilla.gecko.toolbar.ToolbarDisplayLayout android:id="@+id/display_layout"
|
||||
style="@style/UrlBar.Button.Container"
|
||||
android:layout_toRightOf="@id/back"
|
||||
android:layout_toLeftOf="@id/menu_items"
|
||||
android:paddingLeft="6dip"
|
||||
android:paddingRight="4dip"/>
|
||||
|
||||
<LinearLayout android:id="@+id/menu_items"
|
||||
|
|
До Ширина: | Высота: | Размер: 520 B После Ширина: | Высота: | Размер: 520 B |
До Ширина: | Высота: | Размер: 282 B После Ширина: | Высота: | Размер: 282 B |
До Ширина: | Высота: | Размер: 572 B После Ширина: | Высота: | Размер: 572 B |
Двоичные данные
mobile/android/base/resources/drawable-hdpi/lock_identified.png
До Ширина: | Высота: | Размер: 416 B После Ширина: | Высота: | Размер: 622 B |
Двоичные данные
mobile/android/base/resources/drawable-hdpi/lock_verified.png
До Ширина: | Высота: | Размер: 319 B После Ширина: | Высота: | Размер: 532 B |
До Ширина: | Высота: | Размер: 5.2 KiB После Ширина: | Высота: | Размер: 5.2 KiB |
До Ширина: | Высота: | Размер: 3.0 KiB После Ширина: | Высота: | Размер: 3.0 KiB |
До Ширина: | Высота: | Размер: 5.1 KiB После Ширина: | Высота: | Размер: 5.1 KiB |
До Ширина: | Высота: | Размер: 816 B После Ширина: | Высота: | Размер: 816 B |
До Ширина: | Высота: | Размер: 3.4 KiB После Ширина: | Высота: | Размер: 3.4 KiB |
До Ширина: | Высота: | Размер: 3.4 KiB После Ширина: | Высота: | Размер: 3.4 KiB |
До Ширина: | Высота: | Размер: 7.4 KiB После Ширина: | Высота: | Размер: 7.4 KiB |
До Ширина: | Высота: | Размер: 2.9 KiB После Ширина: | Высота: | Размер: 2.9 KiB |
Двоичные данные
mobile/android/base/resources/drawable-hdpi/shield.png
До Ширина: | Высота: | Размер: 520 B После Ширина: | Высота: | Размер: 740 B |
Двоичные данные
mobile/android/base/resources/drawable-hdpi/warning.png
До Ширина: | Высота: | Размер: 456 B После Ширина: | Высота: | Размер: 676 B |
До Ширина: | Высота: | Размер: 397 B После Ширина: | Высота: | Размер: 397 B |
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:maxLevel="0" android:drawable="@drawable/new_tablet_site_security_unknown"/>
|
||||
<item android:maxLevel="1" android:drawable="@drawable/lock_identified"/>
|
||||
<item android:maxLevel="2" android:drawable="@drawable/lock_verified"/>
|
||||
<item android:maxLevel="3" android:drawable="@drawable/shield"/>
|
||||
<item android:maxLevel="4" android:drawable="@drawable/warning"/>
|
||||
|
||||
</level-list>
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!-- The favicon drawable is not the same dimensions as the site security
|
||||
lock icons so we offset it using this drawable to compensate. -->
|
||||
<inset
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:drawable="@drawable/favicon"
|
||||
android:insetTop="@dimen/new_tablet_site_security_unknown_inset_top"
|
||||
android:insetBottom="@dimen/new_tablet_site_security_unknown_inset_bottom"/>
|
До Ширина: | Высота: | Размер: 379 B После Ширина: | Высота: | Размер: 379 B |
До Ширина: | Высота: | Размер: 216 B После Ширина: | Высота: | Размер: 216 B |
До Ширина: | Высота: | Размер: 437 B После Ширина: | Высота: | Размер: 437 B |
Двоичные данные
mobile/android/base/resources/drawable-mdpi/lock_identified.png
До Ширина: | Высота: | Размер: 326 B После Ширина: | Высота: | Размер: 533 B |
Двоичные данные
mobile/android/base/resources/drawable-mdpi/lock_verified.png
До Ширина: | Высота: | Размер: 264 B После Ширина: | Высота: | Размер: 476 B |
До Ширина: | Высота: | Размер: 4.3 KiB После Ширина: | Высота: | Размер: 4.3 KiB |
До Ширина: | Высота: | Размер: 2.9 KiB После Ширина: | Высота: | Размер: 2.9 KiB |
До Ширина: | Высота: | Размер: 4.1 KiB После Ширина: | Высота: | Размер: 4.1 KiB |