Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2014-11-12 16:09:39 +01:00
commit 612228e7a9
28 changed files with 410 additions and 91 deletions

View File

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

View File

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

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="5ae28ff11b982e2bd7d1aa097cda131536952bdc"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="65ce58778f65c5b39d9e558e02417c9954d55d6e"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="39072d2ba957dbafbd93496d47acf8f586037b28"/>

View File

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

View File

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

View File

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

View File

@ -17,7 +17,7 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="5ae28ff11b982e2bd7d1aa097cda131536952bdc"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="65ce58778f65c5b39d9e558e02417c9954d55d6e"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="39072d2ba957dbafbd93496d47acf8f586037b28"/>

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "7ca06f787f88fc9571667cbc68ba07319229cd0b",
"revision": "975ad3e4cb19b5d1c1a7788e64406168a4801bf9",
"repo_path": "integration/gaia-central"
}

View File

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

View File

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

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="5ae28ff11b982e2bd7d1aa097cda131536952bdc"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="65ce58778f65c5b39d9e558e02417c9954d55d6e"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="39072d2ba957dbafbd93496d47acf8f586037b28"/>

View File

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

View File

@ -53,8 +53,11 @@ let DevEdition = {
_updateDevtoolsThemeAttribute: function() {
// Set an attribute on root element to make it possible
// to change colors based on the selected devtools theme.
document.documentElement.setAttribute("devtoolstheme",
Services.prefs.getCharPref(this._devtoolsThemePrefName));
let devtoolsTheme = Services.prefs.getCharPref(this._devtoolsThemePrefName);
if (devtoolsTheme != "dark") {
devtoolsTheme = "light";
}
document.documentElement.setAttribute("devtoolstheme", devtoolsTheme);
ToolbarIconColor.inferFromText();
this._updateStyleSheetFromPrefs();
},

View File

@ -65,6 +65,11 @@ function testDevtoolsTheme() {
is (document.documentElement.getAttribute("devtoolstheme"), "dark",
"The documentElement has an attribute based on devtools theme.");
ok (DevEdition.styleSheet, "The devedition stylesheet is still there with the dark devtools theme.");
Services.prefs.setCharPref(PREF_DEVTOOLS_THEME, "foobar");
is (document.documentElement.getAttribute("devtoolstheme"), "light",
"The documentElement has 'light' as a default for the devtoolstheme attribute");
ok (DevEdition.styleSheet, "The devedition stylesheet is still there with the foobar devtools theme.");
}
function dummyLightweightTheme(id) {

View File

@ -33,12 +33,55 @@ loop.contacts = (function(_, mozL10n) {
};
};
let getPreferredEmail = function(contact) {
// A contact may not contain email addresses, but only a phone number.
if (!contact.email || contact.email.length == 0) {
/** Used to retrieve the preferred email or phone number
* for the contact. Both fields are optional.
* @param {object} contact
* The contact object to get the field from.
* @param {string} field
* The field that should be read out of the contact object.
* @returns {object} An object with a 'value' property that hold a string value.
*/
let getPreferred = function(contact, field) {
if (!contact[field] || !contact[field].length) {
return { value: "" };
}
return contact.email.find(e => e.pref) || contact.email[0];
return contact[field].find(e => e.pref) || contact[field][0];
};
/** Used to set the preferred email or phone number
* for the contact. Both fields are optional.
* @param {object} contact
* The contact object to get the field from.
* @param {object} data
* An object that has a 'field' property. If the 'optional' field
* is defined and true, then the field will only be set if the
* 'value' is not empty or if the previous value was defined.
*/
let setPreferred = function(contact, data) {
if (data.optional) {
// Don't clear the field if it doesn't exist.
if (!data.value &&
(!contact[data.field] || !contact[data.field].length)) {
return;
}
}
if (!contact[data.field]) {
contact[data.field] = [];
}
if (!contact[data.field].length) {
contact[data.field][0] = {"value": data.value};
return;
}
// Set the value in the preferred tuple and return.
for (let i in contact[data.field]) {
if (contact[data.field][i].pref) {
contact[data.field][i].value = data.value;
return;
}
}
contact[data.field][0].value = data.value;
};
const ContactDropdown = React.createClass({displayName: 'ContactDropdown',
@ -159,10 +202,12 @@ loop.contacts = (function(_, mozL10n) {
shouldComponentUpdate: function(nextProps, nextState) {
let currContact = this.props.contact;
let nextContact = nextProps.contact;
let currContactEmail = getPreferred(currContact, "email").value;
let nextContactEmail = getPreferred(nextContact, "email").value;
return (
currContact.name[0] !== nextContact.name[0] ||
currContact.blocked !== nextContact.blocked ||
getPreferredEmail(currContact).value !== getPreferredEmail(nextContact).value ||
currContactEmail !== nextContactEmail ||
nextState.showMenu !== this.state.showMenu
);
},
@ -181,7 +226,7 @@ loop.contacts = (function(_, mozL10n) {
render: function() {
let names = getContactNames(this.props.contact);
let email = getPreferredEmail(this.props.contact);
let email = getPreferred(this.props.contact, "email");
let cx = React.addons.classSet;
let contactCSSClass = cx({
contact: true,
@ -435,7 +480,7 @@ loop.contacts = (function(_, mozL10n) {
if (filter) {
let filterFn = contact => {
return contact.name[0].toLocaleLowerCase().contains(filter) ||
getPreferredEmail(contact).value.toLocaleLowerCase().contains(filter);
getPreferred(contact, "email").value.toLocaleLowerCase().contains(filter);
};
if (shownContacts.available) {
shownContacts.available = shownContacts.available.filter(filterFn);
@ -497,6 +542,7 @@ loop.contacts = (function(_, mozL10n) {
pristine: true,
name: "",
email: "",
tel: "",
};
},
@ -505,7 +551,8 @@ loop.contacts = (function(_, mozL10n) {
if (contact) {
state.contact = contact;
state.name = contact.name[0];
state.email = contact.email[0].value;
state.email = getPreferred(contact, "email").value;
state.tel = getPreferred(contact, "tel").value;
}
this.setState(state);
},
@ -528,7 +575,10 @@ loop.contacts = (function(_, mozL10n) {
switch (this.props.mode) {
case "edit":
this.state.contact.name[0] = this.state.name.trim();
this.state.contact.email[0].value = this.state.email.trim();
setPreferred(this.state.contact,
{field: "email", value: this.state.email.trim()});
setPreferred(this.state.contact,
{field: "tel", value: this.state.tel.trim(), optional: true});
contactsAPI.update(this.state.contact, err => {
if (err) {
throw err;
@ -539,7 +589,7 @@ loop.contacts = (function(_, mozL10n) {
});
break;
case "add":
contactsAPI.add({
var contact = {
id: navigator.mozLoop.generateUUID(),
name: [this.state.name.trim()],
email: [{
@ -548,7 +598,16 @@ loop.contacts = (function(_, mozL10n) {
value: this.state.email.trim()
}],
category: ["local"]
}, err => {
};
var tel = this.state.tel.trim();
if (!!tel) {
contact["tel"] = [{
pref: true,
type: ["fxos"],
value: tel
}];
}
contactsAPI.add(contact, err => {
if (err) {
throw err;
}
@ -563,19 +622,24 @@ loop.contacts = (function(_, mozL10n) {
render: function() {
let cx = React.addons.classSet;
// XXX do we need the ref="" attributes below?
return (
React.DOM.div({className: "content-area contact-form"},
React.DOM.header(null, this.props.mode == "add"
? mozL10n.get("add_contact_button")
: mozL10n.get("edit_contact_title")),
React.DOM.label(null, mozL10n.get("edit_contact_name_label")),
React.DOM.input({ref: "name", required: true, pattern: "\\s*\\S.*",
React.DOM.input({ref: "name", required: true, pattern: "\\s*\\S.*", type: "text",
className: cx({pristine: this.state.pristine}),
valueLink: this.linkState("name")}),
React.DOM.label(null, mozL10n.get("edit_contact_email_label")),
React.DOM.input({ref: "email", required: true, type: "email",
className: cx({pristine: this.state.pristine}),
valueLink: this.linkState("email")}),
React.DOM.label(null, mozL10n.get("new_contact_phone_placeholder")),
React.DOM.input({ref: "tel", type: "tel",
className: cx({pristine: this.state.pristine}),
valueLink: this.linkState("tel")}),
ButtonGroup(null,
Button({additionalClass: "button-cancel",
caption: mozL10n.get("cancel_button"),
@ -594,5 +658,7 @@ loop.contacts = (function(_, mozL10n) {
return {
ContactsList: ContactsList,
ContactDetailsForm: ContactDetailsForm,
_getPreferred: getPreferred,
_setPreferred: setPreferred,
};
})(_, document.mozL10n);

View File

@ -33,12 +33,55 @@ loop.contacts = (function(_, mozL10n) {
};
};
let getPreferredEmail = function(contact) {
// A contact may not contain email addresses, but only a phone number.
if (!contact.email || contact.email.length == 0) {
/** Used to retrieve the preferred email or phone number
* for the contact. Both fields are optional.
* @param {object} contact
* The contact object to get the field from.
* @param {string} field
* The field that should be read out of the contact object.
* @returns {object} An object with a 'value' property that hold a string value.
*/
let getPreferred = function(contact, field) {
if (!contact[field] || !contact[field].length) {
return { value: "" };
}
return contact.email.find(e => e.pref) || contact.email[0];
return contact[field].find(e => e.pref) || contact[field][0];
};
/** Used to set the preferred email or phone number
* for the contact. Both fields are optional.
* @param {object} contact
* The contact object to get the field from.
* @param {object} data
* An object that has a 'field' property. If the 'optional' field
* is defined and true, then the field will only be set if the
* 'value' is not empty or if the previous value was defined.
*/
let setPreferred = function(contact, data) {
if (data.optional) {
// Don't clear the field if it doesn't exist.
if (!data.value &&
(!contact[data.field] || !contact[data.field].length)) {
return;
}
}
if (!contact[data.field]) {
contact[data.field] = [];
}
if (!contact[data.field].length) {
contact[data.field][0] = {"value": data.value};
return;
}
// Set the value in the preferred tuple and return.
for (let i in contact[data.field]) {
if (contact[data.field][i].pref) {
contact[data.field][i].value = data.value;
return;
}
}
contact[data.field][0].value = data.value;
};
const ContactDropdown = React.createClass({
@ -159,10 +202,12 @@ loop.contacts = (function(_, mozL10n) {
shouldComponentUpdate: function(nextProps, nextState) {
let currContact = this.props.contact;
let nextContact = nextProps.contact;
let currContactEmail = getPreferred(currContact, "email").value;
let nextContactEmail = getPreferred(nextContact, "email").value;
return (
currContact.name[0] !== nextContact.name[0] ||
currContact.blocked !== nextContact.blocked ||
getPreferredEmail(currContact).value !== getPreferredEmail(nextContact).value ||
currContactEmail !== nextContactEmail ||
nextState.showMenu !== this.state.showMenu
);
},
@ -181,7 +226,7 @@ loop.contacts = (function(_, mozL10n) {
render: function() {
let names = getContactNames(this.props.contact);
let email = getPreferredEmail(this.props.contact);
let email = getPreferred(this.props.contact, "email");
let cx = React.addons.classSet;
let contactCSSClass = cx({
contact: true,
@ -435,7 +480,7 @@ loop.contacts = (function(_, mozL10n) {
if (filter) {
let filterFn = contact => {
return contact.name[0].toLocaleLowerCase().contains(filter) ||
getPreferredEmail(contact).value.toLocaleLowerCase().contains(filter);
getPreferred(contact, "email").value.toLocaleLowerCase().contains(filter);
};
if (shownContacts.available) {
shownContacts.available = shownContacts.available.filter(filterFn);
@ -497,6 +542,7 @@ loop.contacts = (function(_, mozL10n) {
pristine: true,
name: "",
email: "",
tel: "",
};
},
@ -505,7 +551,8 @@ loop.contacts = (function(_, mozL10n) {
if (contact) {
state.contact = contact;
state.name = contact.name[0];
state.email = contact.email[0].value;
state.email = getPreferred(contact, "email").value;
state.tel = getPreferred(contact, "tel").value;
}
this.setState(state);
},
@ -528,7 +575,10 @@ loop.contacts = (function(_, mozL10n) {
switch (this.props.mode) {
case "edit":
this.state.contact.name[0] = this.state.name.trim();
this.state.contact.email[0].value = this.state.email.trim();
setPreferred(this.state.contact,
{field: "email", value: this.state.email.trim()});
setPreferred(this.state.contact,
{field: "tel", value: this.state.tel.trim(), optional: true});
contactsAPI.update(this.state.contact, err => {
if (err) {
throw err;
@ -539,7 +589,7 @@ loop.contacts = (function(_, mozL10n) {
});
break;
case "add":
contactsAPI.add({
var contact = {
id: navigator.mozLoop.generateUUID(),
name: [this.state.name.trim()],
email: [{
@ -548,7 +598,16 @@ loop.contacts = (function(_, mozL10n) {
value: this.state.email.trim()
}],
category: ["local"]
}, err => {
};
var tel = this.state.tel.trim();
if (!!tel) {
contact["tel"] = [{
pref: true,
type: ["fxos"],
value: tel
}];
}
contactsAPI.add(contact, err => {
if (err) {
throw err;
}
@ -563,19 +622,24 @@ loop.contacts = (function(_, mozL10n) {
render: function() {
let cx = React.addons.classSet;
// XXX do we need the ref="" attributes below?
return (
<div className="content-area contact-form">
<header>{this.props.mode == "add"
? mozL10n.get("add_contact_button")
: mozL10n.get("edit_contact_title")}</header>
<label>{mozL10n.get("edit_contact_name_label")}</label>
<input ref="name" required pattern="\s*\S.*"
<input ref="name" required pattern="\s*\S.*" type="text"
className={cx({pristine: this.state.pristine})}
valueLink={this.linkState("name")} />
<label>{mozL10n.get("edit_contact_email_label")}</label>
<input ref="email" required type="email"
className={cx({pristine: this.state.pristine})}
valueLink={this.linkState("email")} />
<label>{mozL10n.get("new_contact_phone_placeholder")}</label>
<input ref="tel" type="tel"
className={cx({pristine: this.state.pristine})}
valueLink={this.linkState("tel")} />
<ButtonGroup>
<Button additionalClass="button-cancel"
caption={mozL10n.get("cancel_button")}
@ -594,5 +658,7 @@ loop.contacts = (function(_, mozL10n) {
return {
ContactsList: ContactsList,
ContactDetailsForm: ContactDetailsForm,
_getPreferred: getPreferred,
_setPreferred: setPreferred,
};
})(_, document.mozL10n);

View File

@ -0,0 +1,166 @@
/* 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/. */
/*jshint newcap:false*/
/*global loop, sinon */
var expect = chai.expect;
var TestUtils = React.addons.TestUtils;
describe("loop.contacts", function() {
"use strict";
var fakeAddContactButtonText = "Fake Add Contact";
var fakeEditContactButtonText = "Fake Edit Contact";
var fakeDoneButtonText = "Fake Done";
beforeEach(function(done) {
navigator.mozLoop = {
getStrings: function(entityName) {
var textContentValue = "fakeText";
if (entityName == "add_contact_button") {
textContentValue = fakeAddContactButtonText;
} else if (entityName == "edit_contact_title") {
textContentValue = fakeEditContactButtonText;
} else if (entityName == "edit_contact_done_button") {
textContentValue = fakeDoneButtonText;
}
return JSON.stringify({textContent: textContentValue});
},
};
document.mozL10n.initialize(navigator.mozLoop);
// XXX prevent a race whenever mozL10n hasn't been initialized yet
setTimeout(done, 0);
});
describe("ContactDetailsForm", function() {
describe("#render", function() {
describe("add mode", function() {
it("should render 'add' header", function() {
var view = TestUtils.renderIntoDocument(
loop.contacts.ContactDetailsForm({mode: "add"}));
var header = view.getDOMNode().querySelector("header");
expect(header).to.not.equal(null);
expect(header.textContent).to.eql(fakeAddContactButtonText);
});
it("should render name input", function() {
var view = TestUtils.renderIntoDocument(
loop.contacts.ContactDetailsForm({mode: "add"}));
var nameInput = view.getDOMNode().querySelector("input[type='text']");
expect(nameInput).to.not.equal(null);
});
it("should render email input", function() {
var view = TestUtils.renderIntoDocument(
loop.contacts.ContactDetailsForm({mode: "add"}));
var emailInput = view.getDOMNode().querySelector("input[type='email']");
expect(emailInput).to.not.equal(null);
});
it("should render tel input", function() {
var view = TestUtils.renderIntoDocument(
loop.contacts.ContactDetailsForm({mode: "add"}));
var telInput = view.getDOMNode().querySelector("input[type='tel']");
expect(telInput).to.not.equal(null);
});
it("should render 'add contact' button", function() {
var view = TestUtils.renderIntoDocument(
loop.contacts.ContactDetailsForm({mode: "add"}));
var addButton = view.getDOMNode().querySelector(".button-accept");
expect(addButton).to.not.equal(null);
expect(addButton.textContent).to.eql(fakeAddContactButtonText);
});
});
describe("edit mode", function() {
it("should render 'edit' header", function() {
var view = TestUtils.renderIntoDocument(
loop.contacts.ContactDetailsForm({mode: "edit"}));
var header = view.getDOMNode().querySelector("header");
expect(header).to.not.equal(null);
expect(header.textContent).to.eql(fakeEditContactButtonText);
});
it("should render name input", function() {
var view = TestUtils.renderIntoDocument(
loop.contacts.ContactDetailsForm({mode: "edit"}));
var nameInput = view.getDOMNode().querySelector("input[type='text']");
expect(nameInput).to.not.equal(null);
});
it("should render email input", function() {
var view = TestUtils.renderIntoDocument(
loop.contacts.ContactDetailsForm({mode: "edit"}));
var emailInput = view.getDOMNode().querySelector("input[type='email']");
expect(emailInput).to.not.equal(null);
});
it("should render tel input", function() {
var view = TestUtils.renderIntoDocument(
loop.contacts.ContactDetailsForm({mode: "edit"}));
var telInput = view.getDOMNode().querySelector("input[type='tel']");
expect(telInput).to.not.equal(null);
});
it("should render 'done' button", function() {
var view = TestUtils.renderIntoDocument(
loop.contacts.ContactDetailsForm({mode: "edit"}));
var doneButton = view.getDOMNode().querySelector(".button-accept");
expect(doneButton).to.not.equal(null);
expect(doneButton.textContent).to.eql(fakeDoneButtonText);
});
});
});
});
describe("_getPreferred", function() {
it("should return an dummy object if the field doesn't exist", function() {
var obj = loop.contacts._getPreferred({}, "fieldThatDoesntExist");
expect(obj.value).to.eql("");
});
it("should return the preferred value when the field exists", function() {
var correctValue = "correct value";
var fakeContact = {fakeField: [{value: "wrong value"}, {value: correctValue, pref: true}]};
var obj = loop.contacts._getPreferred(fakeContact, "fakeField");
expect(obj.value).to.eql(correctValue);
});
});
describe("_setPreferred", function() {
it("should not set the value on the object if the new value is empty," +
" it didn't exist before, and it is optional", function() {
var contact = {};
loop.contacts._setPreferred(contact, {field: "fakeField", value: "", optional: true});
expect(contact).to.not.have.property("fakeField");
});
it("should clear the value on the object if the new value is empty," +
" it existed before, and it is optional", function() {
var contact = {fakeField: [{value: "foobar"}]};
loop.contacts._setPreferred(contact, {field: "fakeField", value: "", optional: true});
expect(contact["fakeField"][0].value).to.eql("");
});
it("should set the value on the object if the new value is empty," +
" and it did not exist before", function() {
var contact = {};
loop.contacts._setPreferred(contact, {field: "fakeField", value: ""});
expect(contact).to.have.property("fakeField");
});
it("should set the value on the object if the new value is empty," +
" and it did not exist before", function() {
var contact = {fakeField: [{value: "foobar"}]};
loop.contacts._setPreferred(contact, {field: "fakeField", value: "barbaz"});
expect(contact["fakeField"][0].value).to.eql("barbaz");
});
});
});

View File

@ -60,6 +60,7 @@
<script src="panel_test.js"></script>
<script src="roomViews_test.js"></script>
<script src="conversationViews_test.js"></script>
<script src="contacts_test.js"></script>
<script>
// Stop the default init functions running to avoid conflicts in tests
document.removeEventListener('DOMContentLoaded', loop.panel.init);

View File

@ -693,9 +693,11 @@ public final class GeckoProfile {
Log.w(LOGTAG, "Couldn't write times.json.", e);
}
// 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();
// Initialize pref flag for displaying the start pane for a new non-webapp profile.
if (!mIsWebAppProfile) {
final SharedPreferences prefs = GeckoSharedPrefs.forProfile(mApplicationContext);
prefs.edit().putBoolean(BrowserApp.PREF_STARTPANE_ENABLED, true).apply();
}
return profileDir;
}

View File

@ -209,6 +209,9 @@ size. -->
demonstrate the font size setting. It is meant to be whimsical and fun. -->
<!ENTITY pref_font_size_preview_text "The quick orange fox jumps over your expectations with more speed, more flexibility and more security. As a non-profit, we\'re free to innovate on your behalf without any pressure to compromise. That means a better experience for you and a brighter future for the Web.">
<!ENTITY pref_media_autoplay_enabled "Allow autoplay">
<!ENTITY pref_media_autoplay_enabled_summary "Control if websites can autoplay videos and other media content">
<!ENTITY pref_use_master_password "Use master password">
<!ENTITY pref_sync "Sync">
<!ENTITY pref_sync_summary "Sync your tabs, bookmarks, passwords, history">
@ -511,8 +514,6 @@ just addresses the organization to follow, e.g. "This site is run by " -->
<!ENTITY searchable_description "Bookmarks and history">
<!ENTITY devtools_remote_debugging_forward "Don\'t forget to set up port forwarding!">
<!-- Updater notifications -->
<!ENTITY updater_start_title2 "Update available for &brandShortName;">
<!ENTITY updater_start_select2 "Touch to download">

View File

@ -708,18 +708,6 @@ OnSharedPreferenceChangeListener
i--;
continue;
}
final Context thisContext = this;
pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
// Display toast to remind setting up tcp forwarding.
if (((CheckBoxPreference) preference).isChecked()) {
Toast.makeText(thisContext, R.string.devtools_remote_debugging_forward, Toast.LENGTH_SHORT).show();
}
return true;
}
});
} else if (PREFS_RESTORE_SESSION.equals(key) ||
PREFS_BROWSER_LOCALE.equals(key)) {
// Set the summary string to the current entry. The summary

View File

@ -18,6 +18,6 @@
<org.mozilla.gecko.preferences.AlignRightLinkPreference android:key="android.not_a_preference.remote_debugging.link"
android:title="@string/pref_learn_more"
android:persistent="false"
url="https://developer.mozilla.org/docs/Tools/Remote_Debugging" />
url="https://developer.mozilla.org/docs/Tools/Remote_Debugging/Debugging_Firefox_for_Android_with_WebIDE" />
</PreferenceScreen>

View File

@ -25,6 +25,10 @@
android:title="@string/pref_scroll_title_bar2"
android:summary="@string/pref_scroll_title_bar_summary" />
<CheckBoxPreference android:key="media.autoplay.enabled"
android:title="@string/pref_media_autoplay_enabled"
android:summary="@string/pref_media_autoplay_enabled_summary" />
<CheckBoxPreference android:key="android.not_a_preference.new_tablet_ui"
android:title="@string/new_tablet_pref"
android:defaultValue="true" />

View File

@ -205,6 +205,8 @@
<string name="pref_font_size_set">&pref_font_size_set;</string>
<string name="pref_font_size_adjust_char">&pref_font_size_adjust_char;</string>
<string name="pref_font_size_preview_text">&pref_font_size_preview_text;</string>
<string name="pref_media_autoplay_enabled">&pref_media_autoplay_enabled;</string>
<string name="pref_media_autoplay_enabled_summary">&pref_media_autoplay_enabled_summary;</string>
<string name="pref_reflow_on_zoom">&pref_reflow_on_zoom4;</string>
<string name="pref_restore">&pref_restore;</string>
<string name="pref_restore_always">&pref_restore_always;</string>
@ -435,8 +437,6 @@
<string name="searchable_description">&searchable_description;</string>
<string name="devtools_remote_debugging_forward">&devtools_remote_debugging_forward;</string>
<!-- Updater notifications -->
<string name="updater_start_title">&updater_start_title2;</string>
<string name="updater_start_select">&updater_start_select2;</string>

View File

@ -28,7 +28,7 @@ public class Themed@VIEW_NAME_SUFFIX@ extends @BASE_TYPE@
private boolean mIsPrivate;
private boolean mIsLight;
private boolean mIsDark;
private boolean mAutoUpdateTheme = true;
private boolean mAutoUpdateTheme; // always false if there's no theme.
public Themed@VIEW_NAME_SUFFIX@(Context context, AttributeSet attrs) {
super(context, attrs);
@ -43,10 +43,11 @@ public class Themed@VIEW_NAME_SUFFIX@ extends @BASE_TYPE@
//#endif
private void initialize(final Context context, final AttributeSet attrs) {
// The theme can be null, particularly for webapps: Bug 1089266.
mTheme = ((GeckoApplication) context.getApplicationContext()).getLightweightTheme();
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LightweightTheme);
mAutoUpdateTheme = a.getBoolean(R.styleable.LightweightTheme_autoUpdateTheme, true);
mAutoUpdateTheme = mTheme != null && a.getBoolean(R.styleable.LightweightTheme_autoUpdateTheme, true);
a.recycle();
}
@ -134,6 +135,10 @@ public class Themed@VIEW_NAME_SUFFIX@ extends @BASE_TYPE@
}
public void setAutoUpdateTheme(boolean autoUpdateTheme) {
if (mTheme == null) {
return;
}
if (mAutoUpdateTheme != autoUpdateTheme) {
mAutoUpdateTheme = autoUpdateTheme;

View File

@ -102,7 +102,10 @@ let Reader = {
let uri = tab.browser.currentURI;
let urlWithoutRef = uri.specIgnoringRef;
let article = yield this.getArticle(urlWithoutRef, tabID);
let article = yield this.getArticle(urlWithoutRef, tabID).catch(e => {
Cu.reportError("Error getting article for tab: " + e);
return null;
});
if (!article) {
// If there was a problem getting the article, just store the
// URL and title from the tab.
@ -145,7 +148,6 @@ let Reader = {
* @param tabId (optional) The id of the tab where we can look for a saved article.
* @return {Promise}
* @resolves JS object representing the article, or null if no article is found.
* @rejects Never.
*/
getArticle: Task.async(function* (url, tabId) {
// First, look for an article object stored on the tab.
@ -168,10 +170,7 @@ let Reader = {
// Article hasn't been found in the cache, we need to
// download the page and parse the article out of it.
return yield this._downloadAndParseDocument(url).catch(e => {
Cu.reportError("Error downloading and parsing article: " + e);
return null;
});
return yield this._downloadAndParseDocument(url);
}),
/**
@ -269,16 +268,18 @@ let Reader = {
return new Promise((resolve, reject) => {
let numTags = doc.getElementsByTagName("*").length;
if (numTags > this.MAX_ELEMS_TO_PARSE) {
reject("Aborting parse for " + uri.spec + "; " + numTags + " elements found");
this.log("Aborting parse for " + uri.spec + "; " + numTags + " elements found");
resolve(null);
return;
}
let worker = new ChromeWorker("readerWorker.js");
worker.onmessage = function (evt) {
worker.onmessage = evt => {
let article = evt.data;
if (!article) {
reject("Worker did not return an article");
this.log("Worker did not return an article");
resolve(null);
return;
}
@ -291,8 +292,8 @@ let Reader = {
resolve(article);
};
worker.onerror = function (evt) {
reject(evt.message);
worker.onerror = evt => {
reject("Error in worker: " + evt.message);
};
try {

View File

@ -507,7 +507,10 @@ AboutReader.prototype = {
_loadArticle: Task.async(function* (url, tabId) {
this._showProgressDelayed();
let article = yield gChromeWin.Reader.getArticle(url, tabId);
let article = yield gChromeWin.Reader.getArticle(url, tabId).catch(e => {
Cu.reportError("Error loading article: " + e);
return null;
});
if (article) {
this._showContent(article);
} else {

View File

@ -4215,27 +4215,31 @@ Tab.prototype = {
this.tilesData = null;
}
if (!Reader.isEnabledForParseOnLoad)
if (!Reader.isEnabledForParseOnLoad) {
return;
}
let resetReaderFlags = currentURL => {
// Don't clear the article for about:reader pages since we want to
// use the article from the previous page.
if (!currentURL.startsWith("about:reader")) {
this.savedArticle = null;
this.readerEnabled = false;
this.readerActive = false;
} else {
this.readerActive = true;
}
};
// Once document is fully loaded, parse it
Reader.parseDocumentFromTab(this).then(article => {
// The loaded page may have changed while we were parsing the document.
// Make sure we've got the current one.
let uri = this.browser.currentURI;
let tabURL = uri.specIgnoringRef;
// Do nothing if there's no article or the page in this tab has
// changed
if (article == null || (article.url != tabURL)) {
// Don't clear the article for about:reader pages since we want to
// use the article from the previous page
if (!tabURL.startsWith("about:reader")) {
this.savedArticle = null;
this.readerEnabled = false;
this.readerActive = false;
} else {
this.readerActive = true;
}
let currentURL = this.browser.currentURI.specIgnoringRef;
// Do nothing if there's no article or the page in this tab has changed.
if (article == null || (article.url != currentURL)) {
resetReaderFlags(currentURL);
return;
}
@ -4246,12 +4250,16 @@ Tab.prototype = {
tabID: this.id
});
if(this.readerActive)
if (this.readerActive) {
this.readerActive = false;
if(!this.readerEnabled)
}
if (!this.readerEnabled) {
this.readerEnabled = true;
}, e => Cu.reportError("Error parsing document from tab: " + e));
}
}).catch(e => {
Cu.reportError("Error parsing document from tab: " + e);
resetReaderFlags(this.browser.currentURI.specIgnoringRef);
});
}
}
},