mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
merge fx-team to mozilla-central a=merge
This commit is contained in:
commit
fc51638ed5
@ -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();
|
||||
},
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
166
browser/components/loop/test/desktop-local/contacts_test.js
Normal file
166
browser/components/loop/test/desktop-local/contacts_test.js
Normal 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");
|
||||
});
|
||||
});
|
||||
});
|
@ -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);
|
||||
|
@ -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.
|
||||
// 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;
|
||||
}
|
||||
|
@ -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">
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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" />
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -4215,27 +4215,31 @@ Tab.prototype = {
|
||||
this.tilesData = null;
|
||||
}
|
||||
|
||||
if (!Reader.isEnabledForParseOnLoad)
|
||||
if (!Reader.isEnabledForParseOnLoad) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
let resetReaderFlags = currentURL => {
|
||||
// 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")) {
|
||||
// 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 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user