mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 01:48:05 +00:00
Merge fx-team to m-c. a=merge
CLOSED TREE
This commit is contained in:
commit
7bcb982330
@ -1718,15 +1718,17 @@ pref("loop.debug.dispatcher", false);
|
||||
pref("loop.debug.websocket", false);
|
||||
pref("loop.debug.sdk", false);
|
||||
#ifdef DEBUG
|
||||
pref("loop.CSP", "default-src 'self' about: file: chrome: http://localhost:*; img-src 'self' data: http://www.gravatar.com/ about: file: chrome:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net http://localhost:* ws://localhost:*; media-src blob:");
|
||||
pref("loop.CSP", "default-src 'self' about: file: chrome: http://localhost:*; img-src 'self' data: https://www.gravatar.com/ about: file: chrome:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net http://localhost:* ws://localhost:*; media-src blob:");
|
||||
#else
|
||||
pref("loop.CSP", "default-src 'self' about: file: chrome:; img-src 'self' data: http://www.gravatar.com/ about: file: chrome:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net; media-src blob:");
|
||||
pref("loop.CSP", "default-src 'self' about: file: chrome:; img-src 'self' data: https://www.gravatar.com/ about: file: chrome:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net; media-src blob:");
|
||||
#endif
|
||||
pref("loop.oauth.google.redirect_uri", "urn:ietf:wg:oauth:2.0:oob:auto");
|
||||
pref("loop.oauth.google.scope", "https://www.google.com/m8/feeds");
|
||||
pref("loop.fxa_oauth.tokendata", "");
|
||||
pref("loop.fxa_oauth.profile", "");
|
||||
pref("loop.support_url", "https://support.mozilla.org/kb/group-conversations-firefox-hello-webrtc");
|
||||
pref("loop.contacts.gravatars.show", false);
|
||||
pref("loop.contacts.gravatars.promo", true);
|
||||
|
||||
// serverURL to be assigned by services team
|
||||
pref("services.push.serverURL", "wss://push.services.mozilla.com/");
|
||||
|
@ -446,7 +446,9 @@
|
||||
</menu>
|
||||
#ifndef XP_MACOSX
|
||||
# Disabled on Mac because we can't fill native menupopups asynchronously
|
||||
<menuseparator/>
|
||||
<menuseparator id="menu_readingListSeparator">
|
||||
<observes element="readingListSidebar" attribute="hidden"/>
|
||||
</menuseparator>
|
||||
<menu id="menu_readingList"
|
||||
class="menu-iconic bookmark-item"
|
||||
label="&readingList.label;"
|
||||
|
@ -117,6 +117,15 @@ const cloneValueInto = function(value, targetWindow) {
|
||||
return clone;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the two-digit hexadecimal code for a byte
|
||||
*
|
||||
* @param {byte} charCode
|
||||
*/
|
||||
const toHexString = function(charCode) {
|
||||
return ("0" + charCode.toString(16)).slice(-2);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject any API containing _only_ function properties into the given window.
|
||||
*
|
||||
@ -740,6 +749,43 @@ function injectLoopAPI(targetWindow) {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Compose a URL pointing to the location of an avatar by email address.
|
||||
* At the moment we use the Gravatar service to match email addresses with
|
||||
* avatars. This might change in the future as avatars might come from another
|
||||
* source.
|
||||
*
|
||||
* @param {String} emailAddress Users' email address
|
||||
* @param {Number} size Size of the avatar image to return in pixels.
|
||||
* Optional. Default value: 40.
|
||||
* @return the URL pointing to an avatar matching the provided email address.
|
||||
*/
|
||||
getUserAvatar: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: function(emailAddress, size = 40) {
|
||||
const kEmptyGif = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
||||
if (!emailAddress || !MozLoopService.getLoopPref("contacts.gravatars.show")) {
|
||||
return kEmptyGif;
|
||||
}
|
||||
|
||||
// Do the MD5 dance.
|
||||
let hasher = Cc["@mozilla.org/security/hash;1"]
|
||||
.createInstance(Ci.nsICryptoHash);
|
||||
hasher.init(Ci.nsICryptoHash.MD5);
|
||||
let stringStream = Cc["@mozilla.org/io/string-input-stream;1"]
|
||||
.createInstance(Ci.nsIStringInputStream);
|
||||
stringStream.data = emailAddress.trim().toLowerCase();
|
||||
hasher.updateFromStream(stringStream, -1);
|
||||
let hash = hasher.finish(false);
|
||||
// Convert the binary hash data to a hex string.
|
||||
let md5Email = [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
|
||||
|
||||
// Compose the Gravatar URL.
|
||||
return "https://www.gravatar.com/avatar/" + md5Email + ".jpg?default=blank&s=" + size;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Associates a session-id and a call-id with a window for debugging.
|
||||
*
|
||||
|
@ -229,3 +229,44 @@
|
||||
.contact-form > .button-group {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.contacts-gravatar-promo {
|
||||
position: relative;
|
||||
border: 1px dashed #c1c1c1;
|
||||
border-radius: 2px;
|
||||
background-color: #fbfbfb;
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.contacts-gravatar-promo > p {
|
||||
margin-top: 2px;
|
||||
margin-bottom: 8px;
|
||||
margin-right: 4px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
body[dir=rtl] .contacts-gravatar-promo > p {
|
||||
margin-right: 0;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.contacts-gravatar-promo > p > a {
|
||||
color: #0295df;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.contacts-gravatar-promo > p > a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.contacts-gravatar-promo > .button-close {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
}
|
||||
|
||||
body[dir=rtl] .contacts-gravatar-promo > .button-close {
|
||||
right: auto;
|
||||
left: 8px;
|
||||
}
|
||||
|
@ -390,6 +390,23 @@ body {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.button-close {
|
||||
background-color: transparent;
|
||||
background-image: url(../shared/img/icons-10x10.svg#close);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 8px 8px;
|
||||
border: none;
|
||||
padding: 0;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.button-close:hover,
|
||||
.button-close:hover:active {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Dropdown menu */
|
||||
|
||||
.dropdown {
|
||||
|
@ -81,6 +81,59 @@ loop.contacts = (function(_, mozL10n) {
|
||||
contact[field][0].value = value;
|
||||
};
|
||||
|
||||
const GravatarPromo = React.createClass({displayName: "GravatarPromo",
|
||||
propTypes: {
|
||||
handleUse: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
showMe: navigator.mozLoop.getLoopPref("contacts.gravatars.promo") &&
|
||||
!navigator.mozLoop.getLoopPref("contacts.gravatars.show")
|
||||
};
|
||||
},
|
||||
|
||||
handleCloseButtonClick: function() {
|
||||
navigator.mozLoop.setLoopPref("contacts.gravatars.promo", false);
|
||||
this.setState({ showMe: false });
|
||||
},
|
||||
|
||||
handleUseButtonClick: function() {
|
||||
navigator.mozLoop.setLoopPref("contacts.gravatars.promo", false);
|
||||
navigator.mozLoop.setLoopPref("contacts.gravatars.show", true);
|
||||
this.setState({ showMe: false });
|
||||
this.props.handleUse();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (!this.state.showMe) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let privacyUrl = navigator.mozLoop.getLoopPref("legal.privacy_url");
|
||||
let message = mozL10n.get("gravatars_promo_message", {
|
||||
"learn_more": React.renderToStaticMarkup(
|
||||
React.createElement("a", {href: privacyUrl, target: "_blank"},
|
||||
mozL10n.get("gravatars_promo_message_learnmore")
|
||||
)
|
||||
)
|
||||
});
|
||||
return (
|
||||
React.createElement("div", {className: "contacts-gravatar-promo"},
|
||||
React.createElement(Button, {additionalClass: "button-close", onClick: this.handleCloseButtonClick}),
|
||||
React.createElement("p", {dangerouslySetInnerHTML: {__html: message}}),
|
||||
React.createElement(ButtonGroup, null,
|
||||
React.createElement(Button, {caption: mozL10n.get("gravatars_promo_button_nothanks"),
|
||||
onClick: this.handleCloseButtonClick}),
|
||||
React.createElement(Button, {caption: mozL10n.get("gravatars_promo_button_use"),
|
||||
additionalClass: "button-accept",
|
||||
onClick: this.handleUseButtonClick})
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const ContactDropdown = React.createClass({displayName: "ContactDropdown",
|
||||
propTypes: {
|
||||
handleAction: React.PropTypes.func.isRequired,
|
||||
@ -232,7 +285,9 @@ loop.contacts = (function(_, mozL10n) {
|
||||
|
||||
return (
|
||||
React.createElement("li", {className: contactCSSClass, onMouseLeave: this.hideDropdownMenu},
|
||||
React.createElement("div", {className: "avatar"}),
|
||||
React.createElement("div", {className: "avatar"},
|
||||
React.createElement("img", {src: navigator.mozLoop.getUserAvatar(email.value)})
|
||||
),
|
||||
React.createElement("div", {className: "details"},
|
||||
React.createElement("div", {className: "username"}, React.createElement("strong", null, names.firstName), " ", names.lastName,
|
||||
React.createElement("i", {className: cx({"icon icon-google": this.props.contact.category[0] == "google"})}),
|
||||
@ -462,6 +517,12 @@ loop.contacts = (function(_, mozL10n) {
|
||||
}
|
||||
},
|
||||
|
||||
handleUseGravatar: function() {
|
||||
// We got permission to use Gravatar icons now, so we need to redraw the
|
||||
// list entirely to show them.
|
||||
this.refresh();
|
||||
},
|
||||
|
||||
sortContacts: function(contact1, contact2) {
|
||||
let comp = contact1.name[0].localeCompare(contact2.name[0]);
|
||||
if (comp !== 0) {
|
||||
@ -522,7 +583,8 @@ loop.contacts = (function(_, mozL10n) {
|
||||
React.createElement("input", {className: "contact-filter",
|
||||
placeholder: mozL10n.get("contacts_search_placesholder"),
|
||||
valueLink: this.linkState("filter")})
|
||||
: null
|
||||
: null,
|
||||
React.createElement(GravatarPromo, {handleUse: this.handleUseGravatar})
|
||||
),
|
||||
React.createElement("ul", {className: "contact-list"},
|
||||
shownContacts.available ?
|
||||
|
@ -81,6 +81,59 @@ loop.contacts = (function(_, mozL10n) {
|
||||
contact[field][0].value = value;
|
||||
};
|
||||
|
||||
const GravatarPromo = React.createClass({
|
||||
propTypes: {
|
||||
handleUse: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
showMe: navigator.mozLoop.getLoopPref("contacts.gravatars.promo") &&
|
||||
!navigator.mozLoop.getLoopPref("contacts.gravatars.show")
|
||||
};
|
||||
},
|
||||
|
||||
handleCloseButtonClick: function() {
|
||||
navigator.mozLoop.setLoopPref("contacts.gravatars.promo", false);
|
||||
this.setState({ showMe: false });
|
||||
},
|
||||
|
||||
handleUseButtonClick: function() {
|
||||
navigator.mozLoop.setLoopPref("contacts.gravatars.promo", false);
|
||||
navigator.mozLoop.setLoopPref("contacts.gravatars.show", true);
|
||||
this.setState({ showMe: false });
|
||||
this.props.handleUse();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (!this.state.showMe) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let privacyUrl = navigator.mozLoop.getLoopPref("legal.privacy_url");
|
||||
let message = mozL10n.get("gravatars_promo_message", {
|
||||
"learn_more": React.renderToStaticMarkup(
|
||||
<a href={privacyUrl} target="_blank">
|
||||
{mozL10n.get("gravatars_promo_message_learnmore")}
|
||||
</a>
|
||||
)
|
||||
});
|
||||
return (
|
||||
<div className="contacts-gravatar-promo">
|
||||
<Button additionalClass="button-close" onClick={this.handleCloseButtonClick}/>
|
||||
<p dangerouslySetInnerHTML={{__html: message}}></p>
|
||||
<ButtonGroup>
|
||||
<Button caption={mozL10n.get("gravatars_promo_button_nothanks")}
|
||||
onClick={this.handleCloseButtonClick}/>
|
||||
<Button caption={mozL10n.get("gravatars_promo_button_use")}
|
||||
additionalClass="button-accept"
|
||||
onClick={this.handleUseButtonClick}/>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const ContactDropdown = React.createClass({
|
||||
propTypes: {
|
||||
handleAction: React.PropTypes.func.isRequired,
|
||||
@ -232,7 +285,9 @@ loop.contacts = (function(_, mozL10n) {
|
||||
|
||||
return (
|
||||
<li className={contactCSSClass} onMouseLeave={this.hideDropdownMenu}>
|
||||
<div className="avatar" />
|
||||
<div className="avatar">
|
||||
<img src={navigator.mozLoop.getUserAvatar(email.value)} />
|
||||
</div>
|
||||
<div className="details">
|
||||
<div className="username"><strong>{names.firstName}</strong> {names.lastName}
|
||||
<i className={cx({"icon icon-google": this.props.contact.category[0] == "google"})} />
|
||||
@ -462,6 +517,12 @@ loop.contacts = (function(_, mozL10n) {
|
||||
}
|
||||
},
|
||||
|
||||
handleUseGravatar: function() {
|
||||
// We got permission to use Gravatar icons now, so we need to redraw the
|
||||
// list entirely to show them.
|
||||
this.refresh();
|
||||
},
|
||||
|
||||
sortContacts: function(contact1, contact2) {
|
||||
let comp = contact1.name[0].localeCompare(contact2.name[0]);
|
||||
if (comp !== 0) {
|
||||
@ -523,6 +584,7 @@ loop.contacts = (function(_, mozL10n) {
|
||||
placeholder={mozL10n.get("contacts_search_placesholder")}
|
||||
valueLink={this.linkState("filter")} />
|
||||
: null }
|
||||
<GravatarPromo handleUse={this.handleUseGravatar}/>
|
||||
</div>
|
||||
<ul className="contact-list">
|
||||
{shownContacts.available ?
|
||||
|
@ -40,6 +40,18 @@ loop.panel = (function(_, mozL10n) {
|
||||
if (tabChange) {
|
||||
this.props.mozLoop.notifyUITour("Loop:PanelTabChanged", nextState.selectedTab);
|
||||
}
|
||||
|
||||
if (!tabChange && nextProps.buttonsHidden) {
|
||||
if (nextProps.buttonsHidden.length !== this.props.buttonsHidden.length) {
|
||||
tabChange = true;
|
||||
} else {
|
||||
for (var i = 0, l = nextProps.buttonsHidden.length; i < l && !tabChange; ++i) {
|
||||
if (this.props.buttonsHidden.indexOf(nextProps.buttonsHidden[i]) === -1) {
|
||||
tabChange = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return tabChange;
|
||||
},
|
||||
|
||||
|
@ -40,6 +40,18 @@ loop.panel = (function(_, mozL10n) {
|
||||
if (tabChange) {
|
||||
this.props.mozLoop.notifyUITour("Loop:PanelTabChanged", nextState.selectedTab);
|
||||
}
|
||||
|
||||
if (!tabChange && nextProps.buttonsHidden) {
|
||||
if (nextProps.buttonsHidden.length !== this.props.buttonsHidden.length) {
|
||||
tabChange = true;
|
||||
} else {
|
||||
for (var i = 0, l = nextProps.buttonsHidden.length; i < l && !tabChange; ++i) {
|
||||
if (this.props.buttonsHidden.indexOf(nextProps.buttonsHidden[i]) === -1) {
|
||||
tabChange = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return tabChange;
|
||||
},
|
||||
|
||||
|
@ -72,7 +72,11 @@ loop.Dispatcher = (function() {
|
||||
}
|
||||
|
||||
registeredStores.forEach(function(store) {
|
||||
store[type](action);
|
||||
try {
|
||||
store[type](action);
|
||||
} catch (x) {
|
||||
console.error("[Dispatcher] Dispatching action caused an exception: ", x);
|
||||
}
|
||||
});
|
||||
|
||||
this._active = false;
|
||||
|
@ -14,9 +14,71 @@ describe("loop.contacts", function() {
|
||||
var fakeAddContactButtonText = "Fake Add Contact";
|
||||
var fakeEditContactButtonText = "Fake Edit Contact";
|
||||
var fakeDoneButtonText = "Fake Done";
|
||||
// The fake contacts array is copied each time mozLoop.contacts.getAll() is called.
|
||||
var fakeContacts = [{
|
||||
id: 1,
|
||||
_guid: 1,
|
||||
name: ["Ally Avocado"],
|
||||
email: [{
|
||||
"pref": true,
|
||||
"type": ["work"],
|
||||
"value": "ally@mail.com"
|
||||
}],
|
||||
tel: [{
|
||||
"pref": true,
|
||||
"type": ["mobile"],
|
||||
"value": "+31-6-12345678"
|
||||
}],
|
||||
category: ["google"],
|
||||
published: 1406798311748,
|
||||
updated: 1406798311748
|
||||
},{
|
||||
id: 2,
|
||||
_guid: 2,
|
||||
name: ["Bob Banana"],
|
||||
email: [{
|
||||
"pref": true,
|
||||
"type": ["work"],
|
||||
"value": "bob@gmail.com"
|
||||
}],
|
||||
tel: [{
|
||||
"pref": true,
|
||||
"type": ["mobile"],
|
||||
"value": "+1-214-5551234"
|
||||
}],
|
||||
category: ["local"],
|
||||
published: 1406798311748,
|
||||
updated: 1406798311748
|
||||
}, {
|
||||
id: 3,
|
||||
_guid: 3,
|
||||
name: ["Caitlin Cantaloupe"],
|
||||
email: [{
|
||||
"pref": true,
|
||||
"type": ["work"],
|
||||
"value": "caitlin.cant@hotmail.com"
|
||||
}],
|
||||
category: ["local"],
|
||||
published: 1406798311748,
|
||||
updated: 1406798311748
|
||||
}, {
|
||||
id: 4,
|
||||
_guid: 4,
|
||||
name: ["Dave Dragonfruit"],
|
||||
email: [{
|
||||
"pref": true,
|
||||
"type": ["work"],
|
||||
"value": "dd@dragons.net"
|
||||
}],
|
||||
category: ["google"],
|
||||
published: 1406798311748,
|
||||
updated: 1406798311748
|
||||
}];
|
||||
var sandbox;
|
||||
var fakeWindow;
|
||||
var notifications;
|
||||
var listView;
|
||||
var oldMozLoop = navigator.mozLoop;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@ -32,6 +94,27 @@ describe("loop.contacts", function() {
|
||||
}
|
||||
return JSON.stringify({textContent: textContentValue});
|
||||
},
|
||||
getLoopPref: function(pref) {
|
||||
if (pref == "contacts.gravatars.promo") {
|
||||
return true;
|
||||
} else if (pref == "contacts.gravatars.show") {
|
||||
return false;
|
||||
}
|
||||
return "";
|
||||
},
|
||||
setLoopPref: sandbox.stub(),
|
||||
getUserAvatar: function() {
|
||||
if (navigator.mozLoop.getLoopPref("contacts.gravatars.show")) {
|
||||
return "gravatarsEnabled";
|
||||
}
|
||||
return "gravatarsDisabled";
|
||||
},
|
||||
contacts: {
|
||||
getAll: function(callback) {
|
||||
callback(null, [].concat(fakeContacts));
|
||||
},
|
||||
on: sandbox.stub()
|
||||
}
|
||||
};
|
||||
|
||||
fakeWindow = {
|
||||
@ -39,18 +122,120 @@ describe("loop.contacts", function() {
|
||||
};
|
||||
loop.shared.mixins.setRootObject(fakeWindow);
|
||||
|
||||
notifications = new loop.shared.models.NotificationCollection();
|
||||
|
||||
document.mozL10n.initialize(navigator.mozLoop);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
listView = null;
|
||||
loop.shared.mixins.setRootObject(window);
|
||||
navigator.mozLoop = oldMozLoop;
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe("GravatarsPromo", function() {
|
||||
function checkGravatarContacts(enabled) {
|
||||
var node = listView.getDOMNode();
|
||||
|
||||
// When gravatars are enabled, contacts should be rendered with gravatars.
|
||||
var gravatars = node.querySelectorAll(".contact img[src=gravatarsEnabled]");
|
||||
expect(gravatars.length).to.equal(enabled ? fakeContacts.length : 0);
|
||||
// Sanity check the reverse:
|
||||
gravatars = node.querySelectorAll(".contact img[src=gravatarsDisabled]");
|
||||
expect(gravatars.length).to.equal(enabled ? 0 : fakeContacts.length);
|
||||
}
|
||||
|
||||
it("should show the gravatars promo box", function() {
|
||||
listView = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.contacts.ContactsList, {
|
||||
notifications: notifications
|
||||
}));
|
||||
|
||||
var promo = listView.getDOMNode().querySelector(".contacts-gravatar-promo");
|
||||
expect(promo).to.not.equal(null);
|
||||
|
||||
checkGravatarContacts(false);
|
||||
});
|
||||
|
||||
it("should not show the gravatars promo box when the 'contacts.gravatars.promo' pref is set", function() {
|
||||
navigator.mozLoop.getLoopPref = function(pref) {
|
||||
if (pref == "contacts.gravatars.promo") {
|
||||
return false;
|
||||
} else if (pref == "contacts.gravatars.show") {
|
||||
return true;
|
||||
}
|
||||
return "";
|
||||
};
|
||||
listView = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.contacts.ContactsList, {
|
||||
notifications: notifications
|
||||
}));
|
||||
|
||||
var promo = listView.getDOMNode().querySelector(".contacts-gravatar-promo");
|
||||
expect(promo).to.equal(null);
|
||||
|
||||
checkGravatarContacts(true);
|
||||
});
|
||||
|
||||
it("should hide the gravatars promo box when the 'use' button is clicked", function() {
|
||||
listView = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.contacts.ContactsList, {
|
||||
notifications: notifications
|
||||
}));
|
||||
|
||||
React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
|
||||
".contacts-gravatar-promo .button-accept"));
|
||||
|
||||
sinon.assert.calledTwice(navigator.mozLoop.setLoopPref);
|
||||
|
||||
var promo = listView.getDOMNode().querySelector(".contacts-gravatar-promo");
|
||||
expect(promo).to.equal(null);
|
||||
});
|
||||
|
||||
it("should should set the prefs correctly when the 'use' button is clicked", function() {
|
||||
listView = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.contacts.ContactsList, {
|
||||
notifications: notifications
|
||||
}));
|
||||
|
||||
React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
|
||||
".contacts-gravatar-promo .button-accept"));
|
||||
|
||||
sinon.assert.calledTwice(navigator.mozLoop.setLoopPref);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.setLoopPref, "contacts.gravatars.promo", false);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.setLoopPref, "contacts.gravatars.show", true);
|
||||
});
|
||||
|
||||
it("should hide the gravatars promo box when the 'close' button is clicked", function() {
|
||||
listView = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.contacts.ContactsList, {
|
||||
notifications: notifications
|
||||
}));
|
||||
|
||||
React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
|
||||
".contacts-gravatar-promo .button-close"));
|
||||
|
||||
var promo = listView.getDOMNode().querySelector(".contacts-gravatar-promo");
|
||||
expect(promo).to.equal(null);
|
||||
});
|
||||
|
||||
it("should set prefs correctly when the 'close' button is clicked", function() {
|
||||
listView = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.contacts.ContactsList, {
|
||||
notifications: notifications
|
||||
}));
|
||||
|
||||
React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
|
||||
".contacts-gravatar-promo .button-close"));
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.setLoopPref);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.setLoopPref,
|
||||
"contacts.gravatars.promo", false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("ContactsList", function () {
|
||||
var listView;
|
||||
|
||||
beforeEach(function() {
|
||||
navigator.mozLoop.calls = {
|
||||
startDirectCall: sandbox.stub(),
|
||||
@ -58,19 +243,12 @@ describe("loop.contacts", function() {
|
||||
};
|
||||
navigator.mozLoop.contacts = {getAll: sandbox.stub()};
|
||||
|
||||
notifications = new loop.shared.models.NotificationCollection();
|
||||
listView = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.contacts.ContactsList, {
|
||||
notifications: notifications
|
||||
}));
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
listView = null;
|
||||
delete navigator.mozLoop.calls;
|
||||
delete navigator.mozLoop.contacts;
|
||||
});
|
||||
|
||||
describe("#handleContactAction", function() {
|
||||
it("should call window.close when called with 'video-call' action",
|
||||
function() {
|
||||
|
@ -38,6 +38,8 @@ add_task(function* test_LoopUI_getters() {
|
||||
yield loadLoopPanel();
|
||||
Assert.ok(LoopUI.browser, "Browser element should be there");
|
||||
Assert.strictEqual(LoopUI.selectedTab, "rooms", "Initially the rooms tab should be selected");
|
||||
let panelTabs = LoopUI.browser.contentDocument.querySelectorAll(".tab-view > li");
|
||||
Assert.strictEqual(panelTabs.length, 1, "Only one tab, 'rooms', should be visible");
|
||||
|
||||
// Hide the panel.
|
||||
yield LoopUI.togglePanel();
|
||||
@ -48,6 +50,12 @@ add_task(function* test_LoopUI_getters() {
|
||||
MozLoopServiceInternal.fxAOAuthProfile = fxASampleProfile;
|
||||
yield MozLoopServiceInternal.notifyStatusChanged("login");
|
||||
|
||||
yield LoopUI.togglePanel();
|
||||
Assert.strictEqual(LoopUI.selectedTab, "rooms", "Rooms tab should still be selected");
|
||||
panelTabs = LoopUI.browser.contentDocument.querySelectorAll(".tab-view > li");
|
||||
Assert.strictEqual(panelTabs.length, 2, "Two tabs should be visible");
|
||||
yield LoopUI.togglePanel();
|
||||
|
||||
// Programmatically select the contacts tab.
|
||||
yield LoopUI.togglePanel(null, "contacts");
|
||||
Assert.strictEqual(LoopUI.selectedTab, "contacts", "Contacts tab should be selected now");
|
||||
|
@ -104,6 +104,31 @@ describe("loop.Dispatcher", function () {
|
||||
sinon.assert.calledOnce(getDataStore2.getWindowData);
|
||||
});
|
||||
|
||||
describe("Error handling", function() {
|
||||
beforeEach(function() {
|
||||
sandbox.stub(console, "error");
|
||||
});
|
||||
|
||||
it("should handle uncaught exceptions", function() {
|
||||
getDataStore1.getWindowData.throws("Uncaught Error");
|
||||
|
||||
dispatcher.dispatch(getDataAction);
|
||||
dispatcher.dispatch(cancelAction);
|
||||
|
||||
sinon.assert.calledOnce(getDataStore1.getWindowData);
|
||||
sinon.assert.calledOnce(getDataStore2.getWindowData);
|
||||
sinon.assert.calledOnce(cancelStore1.cancelCall);
|
||||
});
|
||||
|
||||
it("should log uncaught exceptions", function() {
|
||||
getDataStore1.getWindowData.throws("Uncaught Error");
|
||||
|
||||
dispatcher.dispatch(getDataAction);
|
||||
|
||||
sinon.assert.calledOnce(console.error);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Queued actions", function() {
|
||||
beforeEach(function() {
|
||||
// Restore the stub, so that we can easily add a function to be
|
||||
|
@ -43,6 +43,66 @@ var fakeRooms = [
|
||||
}
|
||||
];
|
||||
|
||||
var fakeContacts = [{
|
||||
id: 1,
|
||||
_guid: 1,
|
||||
name: ["Ally Avocado"],
|
||||
email: [{
|
||||
"pref": true,
|
||||
"type": ["work"],
|
||||
"value": "ally@mail.com"
|
||||
}],
|
||||
tel: [{
|
||||
"pref": true,
|
||||
"type": ["mobile"],
|
||||
"value": "+31-6-12345678"
|
||||
}],
|
||||
category: ["google"],
|
||||
published: 1406798311748,
|
||||
updated: 1406798311748
|
||||
},{
|
||||
id: 2,
|
||||
_guid: 2,
|
||||
name: ["Bob Banana"],
|
||||
email: [{
|
||||
"pref": true,
|
||||
"type": ["work"],
|
||||
"value": "bob@gmail.com"
|
||||
}],
|
||||
tel: [{
|
||||
"pref": true,
|
||||
"type": ["mobile"],
|
||||
"value": "+1-214-5551234"
|
||||
}],
|
||||
category: ["local"],
|
||||
published: 1406798311748,
|
||||
updated: 1406798311748
|
||||
}, {
|
||||
id: 3,
|
||||
_guid: 3,
|
||||
name: ["Caitlin Cantaloupe"],
|
||||
email: [{
|
||||
"pref": true,
|
||||
"type": ["work"],
|
||||
"value": "caitlin.cant@hotmail.com"
|
||||
}],
|
||||
category: ["local"],
|
||||
published: 1406798311748,
|
||||
updated: 1406798311748
|
||||
}, {
|
||||
id: 4,
|
||||
_guid: 4,
|
||||
name: ["Dave Dragonfruit"],
|
||||
email: [{
|
||||
"pref": true,
|
||||
"type": ["work"],
|
||||
"value": "dd@dragons.net"
|
||||
}],
|
||||
category: ["google"],
|
||||
published: 1406798311748,
|
||||
updated: 1406798311748
|
||||
}];
|
||||
|
||||
/**
|
||||
* Faking the mozLoop object which doesn't exist in regular web pages.
|
||||
* @type {Object}
|
||||
@ -54,21 +114,28 @@ navigator.mozLoop = {
|
||||
switch(pref) {
|
||||
// Ensure we skip FTE completely.
|
||||
case "gettingStarted.seen":
|
||||
case "contacts.gravatars.promo":
|
||||
return true;
|
||||
case "contacts.gravatars.show":
|
||||
return false;
|
||||
}
|
||||
},
|
||||
setLoopPref: function(){},
|
||||
releaseCallData: function() {},
|
||||
copyString: function() {},
|
||||
getUserAvatar: function(emailAddress) {
|
||||
return "http://www.gravatar.com/avatar/" + (Math.ceil(Math.random() * 3) === 2 ?
|
||||
"0a996f0fe2727ef1668bdb11897e4459" : "foo") + ".jpg?default=blank&s=40";
|
||||
},
|
||||
contacts: {
|
||||
getAll: function(callback) {
|
||||
callback(null, []);
|
||||
callback(null, [].concat(fakeContacts));
|
||||
},
|
||||
on: function() {}
|
||||
},
|
||||
rooms: {
|
||||
getAll: function(version, callback) {
|
||||
callback(null, fakeRooms);
|
||||
callback(null, [].concat(fakeRooms));
|
||||
},
|
||||
on: function() {}
|
||||
},
|
||||
|
@ -10,11 +10,22 @@ function checkRLState() {
|
||||
let sidebarBroadcaster = document.getElementById("readingListSidebar");
|
||||
let sidebarMenuitem = document.getElementById("menu_readingListSidebar");
|
||||
|
||||
let bookmarksMenubarItem = document.getElementById("menu_readingList");
|
||||
let bookmarksMenubarSeparator = document.getElementById("menu_readingListSeparator");
|
||||
|
||||
if (enabled) {
|
||||
Assert.notEqual(sidebarBroadcaster.getAttribute("hidden"), "true",
|
||||
"Sidebar broadcaster should not be hidden");
|
||||
Assert.notEqual(sidebarMenuitem.getAttribute("hidden"), "true",
|
||||
"Sidebar menuitem should be visible");
|
||||
|
||||
// Currently disabled on OSX.
|
||||
if (bookmarksMenubarItem) {
|
||||
Assert.notEqual(bookmarksMenubarItem.getAttribute("hidden"), "true",
|
||||
"RL bookmarks submenu in menubar should not be hidden");
|
||||
Assert.notEqual(sidebarMenuitem.getAttribute("hidden"), "true",
|
||||
"RL bookmarks separator in menubar should be visible");
|
||||
}
|
||||
} else {
|
||||
Assert.equal(sidebarBroadcaster.getAttribute("hidden"), "true",
|
||||
"Sidebar broadcaster should be hidden");
|
||||
@ -22,6 +33,14 @@ function checkRLState() {
|
||||
"Sidebar menuitem should be hidden");
|
||||
Assert.equal(ReadingListUI.isSidebarOpen, false,
|
||||
"ReadingListUI should not think sidebar is open");
|
||||
|
||||
// Currently disabled on OSX.
|
||||
if (bookmarksMenubarItem) {
|
||||
Assert.equal(bookmarksMenubarItem.getAttribute("hidden"), "true",
|
||||
"RL bookmarks submenu in menubar should not be hidden");
|
||||
Assert.equal(sidebarMenuitem.getAttribute("hidden"), "true",
|
||||
"RL bookmarks separator in menubar should be visible");
|
||||
}
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
|
@ -266,7 +266,10 @@ function mousedown (win, button) {
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, button, win);
|
||||
}
|
||||
|
||||
function* startRecording(panel, options={}) {
|
||||
function* startRecording(panel, options = {
|
||||
waitForOverview: true,
|
||||
waitForStateChanged: true
|
||||
}) {
|
||||
let win = panel.panelWin;
|
||||
let clicked = panel.panelWin.PerformanceView.once(win.EVENTS.UI_START_RECORDING);
|
||||
let willStart = panel.panelWin.PerformanceController.once(win.EVENTS.RECORDING_WILL_START);
|
||||
@ -287,10 +290,14 @@ function* startRecording(panel, options={}) {
|
||||
"The record button should be locked.");
|
||||
|
||||
yield willStart;
|
||||
let stateChanged = once(win.PerformanceView, win.EVENTS.UI_STATE_CHANGED);
|
||||
let stateChanged = options.waitForStateChanged
|
||||
? once(win.PerformanceView, win.EVENTS.UI_STATE_CHANGED)
|
||||
: Promise.resolve();
|
||||
|
||||
yield hasStarted;
|
||||
let overviewRendered = options.waitForOverview ? once(win.OverviewView, win.EVENTS.OVERVIEW_RENDERED) : Promise.resolve();
|
||||
let overviewRendered = options.waitForOverview
|
||||
? once(win.OverviewView, win.EVENTS.OVERVIEW_RENDERED)
|
||||
: Promise.resolve();
|
||||
|
||||
yield stateChanged;
|
||||
yield overviewRendered;
|
||||
@ -304,7 +311,10 @@ function* startRecording(panel, options={}) {
|
||||
"The record button should not be locked.");
|
||||
}
|
||||
|
||||
function* stopRecording(panel, options={}) {
|
||||
function* stopRecording(panel, options = {
|
||||
waitForOverview: true,
|
||||
waitForStateChanged: true
|
||||
}) {
|
||||
let win = panel.panelWin;
|
||||
let clicked = panel.panelWin.PerformanceView.once(win.EVENTS.UI_STOP_RECORDING);
|
||||
let willStop = panel.panelWin.PerformanceController.once(win.EVENTS.RECORDING_WILL_STOP);
|
||||
@ -325,10 +335,14 @@ function* stopRecording(panel, options={}) {
|
||||
"The record button should be locked.");
|
||||
|
||||
yield willStop;
|
||||
let stateChanged = once(win.PerformanceView, win.EVENTS.UI_STATE_CHANGED);
|
||||
let stateChanged = options.waitForStateChanged
|
||||
? once(win.PerformanceView, win.EVENTS.UI_STATE_CHANGED)
|
||||
: Promise.resolve();
|
||||
|
||||
yield hasStopped;
|
||||
let overviewRendered = options.waitForOverview ? once(win.OverviewView, win.EVENTS.OVERVIEW_RENDERED) : Promise.resolve();
|
||||
let overviewRendered = options.waitForOverview
|
||||
? once(win.OverviewView, win.EVENTS.OVERVIEW_RENDERED)
|
||||
: Promise.resolve();
|
||||
|
||||
yield stateChanged;
|
||||
yield overviewRendered;
|
||||
|
@ -34,11 +34,13 @@ let JsFlameGraphView = Heritage.extend(DetailsSubview, {
|
||||
/**
|
||||
* Unbinds events.
|
||||
*/
|
||||
destroy: function () {
|
||||
destroy: Task.async(function* () {
|
||||
DetailsSubview.destroy.call(this);
|
||||
|
||||
this.graph.off("selecting", this._onRangeChangeInGraph);
|
||||
},
|
||||
|
||||
yield this.graph.destroy();
|
||||
}),
|
||||
|
||||
/**
|
||||
* Method for handling all the set up for rendering a new flamegraph.
|
||||
|
@ -33,11 +33,13 @@ let MemoryFlameGraphView = Heritage.extend(DetailsSubview, {
|
||||
/**
|
||||
* Unbinds events.
|
||||
*/
|
||||
destroy: function () {
|
||||
destroy: Task.async(function* () {
|
||||
DetailsSubview.destroy.call(this);
|
||||
|
||||
this.graph.off("selecting", this._onRangeChangeInGraph);
|
||||
},
|
||||
|
||||
yield this.graph.destroy();
|
||||
}),
|
||||
|
||||
/**
|
||||
* Method for handling all the set up for rendering a new flamegraph.
|
||||
|
@ -54,15 +54,15 @@ let OverviewView = {
|
||||
/**
|
||||
* Unbinds events.
|
||||
*/
|
||||
destroy: function () {
|
||||
destroy: Task.async(function*() {
|
||||
if (this.markersOverview) {
|
||||
this.markersOverview.destroy();
|
||||
yield this.markersOverview.destroy();
|
||||
}
|
||||
if (this.memoryOverview) {
|
||||
this.memoryOverview.destroy();
|
||||
yield this.memoryOverview.destroy();
|
||||
}
|
||||
if (this.framerateGraph) {
|
||||
this.framerateGraph.destroy();
|
||||
yield this.framerateGraph.destroy();
|
||||
}
|
||||
|
||||
PerformanceController.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
|
||||
@ -71,7 +71,7 @@ let OverviewView = {
|
||||
PerformanceController.off(EVENTS.RECORDING_WILL_STOP, this._onRecordingWillStop);
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* Disabled in the event we're using a Timeline mock, so we'll have no
|
||||
|
@ -26,7 +26,7 @@ function* performTest() {
|
||||
|
||||
testGraph(host, graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ function* performTest() {
|
||||
yield graph.ready();
|
||||
testGraph(host, graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@ function* performTest() {
|
||||
|
||||
testGraph(graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ function* performTest() {
|
||||
|
||||
testGraph(graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ function* performTest() {
|
||||
|
||||
testGraph(graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ function* performTest() {
|
||||
|
||||
testGraph(host, graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ function* performTest() {
|
||||
testDataAndRegions(graph);
|
||||
testHighlights(graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ function* performTest() {
|
||||
yield testSelection(graph);
|
||||
yield testCursor(graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ function* performTest() {
|
||||
|
||||
testGraph(graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ function* performTest() {
|
||||
|
||||
testGraph(graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ function* performTest() {
|
||||
|
||||
testGraph(graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ function* performTest() {
|
||||
|
||||
testGraph(graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ function* performTest() {
|
||||
|
||||
testGraph(graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ function* performTest() {
|
||||
|
||||
testGraph(graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ function* performTest() {
|
||||
|
||||
yield testGraph(graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ function* performTest() {
|
||||
|
||||
yield testGraph(graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ function* performTest() {
|
||||
|
||||
yield testGraph(graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ function* performTest() {
|
||||
|
||||
yield testGraph(graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ function* performTest() {
|
||||
|
||||
yield testGraph(graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -48,5 +48,5 @@ function* testGraph (parent, options) {
|
||||
is(graph._avgGutterLine.hidden, options.avg === false,
|
||||
`The avg gutter should ${options.avg === false ? "not " : ""}be shown`);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ function* performTest() {
|
||||
|
||||
is(refreshCount, 2, "The graph should've been refreshed 2 times.");
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ function* performTest() {
|
||||
is(refreshCount, 0, "The graph shouldn't have been refreshed at all.");
|
||||
is(refreshCancelledCount, 2, "The graph should've had 2 refresh attempts.");
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ function* performTest() {
|
||||
|
||||
testGraph(graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@ function* performTest() {
|
||||
yield graph.once("ready");
|
||||
yield testGraph(graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -35,8 +35,8 @@ function* performTest() {
|
||||
|
||||
testGraphs(graph1, graph2);
|
||||
|
||||
graph1.destroy();
|
||||
graph2.destroy();
|
||||
yield graph1.destroy();
|
||||
yield graph2.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ function* performTest() {
|
||||
yield graph.ready();
|
||||
testGraph(host, graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ function* performTest() {
|
||||
|
||||
yield testGraph(graph);
|
||||
|
||||
graph.destroy();
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ const OVERVIEW_ROW_HEIGHT = 11; // px
|
||||
const OVERVIEW_SELECTION_LINE_COLOR = "#666";
|
||||
const OVERVIEW_CLIPHEAD_LINE_COLOR = "#555";
|
||||
|
||||
const FIND_OPTIMAL_TICK_INTERVAL_MAX_ITERS = 100;
|
||||
const OVERVIEW_HEADER_TICKS_MULTIPLE = 100; // ms
|
||||
const OVERVIEW_HEADER_TICKS_SPACING_MIN = 75; // px
|
||||
const OVERVIEW_HEADER_TEXT_FONT_SIZE = 9; // px
|
||||
@ -199,9 +200,18 @@ MarkersOverview.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
||||
_findOptimalTickInterval: function(dataScale) {
|
||||
let timingStep = OVERVIEW_HEADER_TICKS_MULTIPLE;
|
||||
let spacingMin = OVERVIEW_HEADER_TICKS_SPACING_MIN * this._pixelRatio;
|
||||
let maxIters = FIND_OPTIMAL_TICK_INTERVAL_MAX_ITERS;
|
||||
let numIters = 0;
|
||||
|
||||
if (dataScale > spacingMin) {
|
||||
return dataScale;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
let scaledStep = dataScale * timingStep;
|
||||
if (++numIters > maxIters) {
|
||||
return scaledStep;
|
||||
}
|
||||
if (scaledStep < spacingMin) {
|
||||
timingStep <<= 1;
|
||||
continue;
|
||||
|
@ -27,6 +27,7 @@ const WATERFALL_SIDEBAR_WIDTH = 150; // px
|
||||
const WATERFALL_IMMEDIATE_DRAW_MARKERS_COUNT = 30;
|
||||
const WATERFALL_FLUSH_OUTSTANDING_MARKERS_DELAY = 75; // ms
|
||||
|
||||
const FIND_OPTIMAL_TICK_INTERVAL_MAX_ITERS = 100;
|
||||
const WATERFALL_HEADER_TICKS_MULTIPLE = 5; // ms
|
||||
const WATERFALL_HEADER_TICKS_SPACING_MIN = 50; // px
|
||||
const WATERFALL_HEADER_TEXT_PADDING = 3; // px
|
||||
@ -575,9 +576,18 @@ Waterfall.prototype = {
|
||||
*/
|
||||
_findOptimalTickInterval: function({ ticksMultiple, ticksSpacingMin, dataScale }) {
|
||||
let timingStep = ticksMultiple;
|
||||
let maxIters = FIND_OPTIMAL_TICK_INTERVAL_MAX_ITERS;
|
||||
let numIters = 0;
|
||||
|
||||
if (dataScale > ticksSpacingMin) {
|
||||
return dataScale;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
let scaledStep = dataScale * timingStep;
|
||||
if (++numIters > maxIters) {
|
||||
return scaledStep;
|
||||
}
|
||||
if (scaledStep < ticksSpacingMin) {
|
||||
timingStep <<= 1;
|
||||
continue;
|
||||
|
@ -26,6 +26,7 @@ const GRAPH_WHEEL_ZOOM_SENSITIVITY = 0.00035;
|
||||
const GRAPH_WHEEL_SCROLL_SENSITIVITY = 0.5;
|
||||
const GRAPH_MIN_SELECTION_WIDTH = 0.001; // ms
|
||||
|
||||
const FIND_OPTIMAL_TICK_INTERVAL_MAX_ITERS = 100;
|
||||
const TIMELINE_TICKS_MULTIPLE = 5; // ms
|
||||
const TIMELINE_TICKS_SPACING_MIN = 75; // px
|
||||
|
||||
@ -180,7 +181,9 @@ FlameGraph.prototype = {
|
||||
/**
|
||||
* Destroys this graph.
|
||||
*/
|
||||
destroy: function() {
|
||||
destroy: Task.async(function*() {
|
||||
yield this.ready();
|
||||
|
||||
this._window.removeEventListener("mousemove", this._onMouseMove);
|
||||
this._window.removeEventListener("mousedown", this._onMouseDown);
|
||||
this._window.removeEventListener("mouseup", this._onMouseUp);
|
||||
@ -200,7 +203,7 @@ FlameGraph.prototype = {
|
||||
this._data = null;
|
||||
|
||||
this.emit("destroyed");
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* Rendering options. Subclasses should override these.
|
||||
@ -789,12 +792,17 @@ FlameGraph.prototype = {
|
||||
_findOptimalTickInterval: function(dataScale) {
|
||||
let timingStep = TIMELINE_TICKS_MULTIPLE;
|
||||
let spacingMin = TIMELINE_TICKS_SPACING_MIN * this._pixelRatio;
|
||||
let maxIters = FIND_OPTIMAL_TICK_INTERVAL_MAX_ITERS;
|
||||
let numIters = 0;
|
||||
|
||||
if (dataScale > spacingMin) {
|
||||
return dataScale;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (++numIters > maxIters) {
|
||||
return scaledStep;
|
||||
}
|
||||
let scaledStep = dataScale * timingStep;
|
||||
if (scaledStep < spacingMin) {
|
||||
timingStep <<= 1;
|
||||
|
@ -229,7 +229,9 @@ AbstractCanvasGraph.prototype = {
|
||||
/**
|
||||
* Destroys this graph.
|
||||
*/
|
||||
destroy: function() {
|
||||
destroy: Task.async(function *() {
|
||||
yield this.ready();
|
||||
|
||||
this._window.removeEventListener("mousemove", this._onMouseMove);
|
||||
this._window.removeEventListener("mousedown", this._onMouseDown);
|
||||
this._window.removeEventListener("mouseup", this._onMouseUp);
|
||||
@ -259,7 +261,7 @@ AbstractCanvasGraph.prototype = {
|
||||
gCachedStripePattern.clear();
|
||||
|
||||
this.emit("destroyed");
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* Rendering options. Subclasses should override these.
|
||||
|
@ -387,9 +387,7 @@ PreviewController.prototype = {
|
||||
this.onTabPaint(r);
|
||||
}
|
||||
}
|
||||
let preview = this.preview;
|
||||
if (preview.visible)
|
||||
preview.invalidate();
|
||||
this.preview.invalidate();
|
||||
break;
|
||||
case "TabAttrModified":
|
||||
this.updateTitleAndTooltip();
|
||||
|
@ -85,8 +85,7 @@ body {
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
#element-picker::before,
|
||||
#toggle-all::before {
|
||||
#element-picker::before {
|
||||
background-image: url("chrome://browser/skin/devtools/command-pick@2x.png");
|
||||
background-size: 64px;
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ public class CustomEditText extends ThemedEditText {
|
||||
super.setPrivateMode(isPrivate);
|
||||
|
||||
mHighlightColor = getContext().getResources().getColor(isPrivate
|
||||
? R.color.url_bar_text_highlight_pb : R.color.url_bar_text_highlight);
|
||||
? R.color.url_bar_text_highlight_pb : R.color.fennec_ui_orange);
|
||||
// android:textColorHighlight cannot support a ColorStateList.
|
||||
setHighlightColor(mHighlightColor);
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
<item android:id="@android:id/progress">
|
||||
<clip>
|
||||
<shape>
|
||||
<solid android:color="@color/highlight_orange"/>
|
||||
<solid android:color="@color/fennec_ui_orange"/>
|
||||
</shape>
|
||||
</clip>
|
||||
</item>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<shape>
|
||||
<gradient android:angle="90"
|
||||
android:startColor="#E66000"
|
||||
android:endColor="#FF9500"
|
||||
android:endColor="@color/fennec_ui_orange"
|
||||
android:type="linear"/>
|
||||
|
||||
<corners android:radius="4dp"/>
|
||||
|
@ -39,7 +39,7 @@
|
||||
gecko:state_recording="false">
|
||||
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#FFFF9500"/>
|
||||
<solid android:color="@color/fennec_ui_orange"/>
|
||||
<corners android:radius="3dp"/>
|
||||
</shape>
|
||||
|
||||
|
@ -52,7 +52,7 @@
|
||||
<View android:id="@+id/divider_doorhanger"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="#FFFF9500"
|
||||
android:background="@color/fennec_ui_orange"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</merge>
|
||||
|
@ -16,7 +16,7 @@
|
||||
android:inputType="text"
|
||||
android:paddingLeft="@dimen/find_in_page_text_padding_left"
|
||||
android:paddingRight="@dimen/find_in_page_text_padding_right"
|
||||
android:textColorHighlight="@color/url_bar_text_highlight"
|
||||
android:textColorHighlight="@color/fennec_ui_orange"
|
||||
android:imeOptions="actionSearch"
|
||||
android:selectAllOnFocus="true"
|
||||
android:gravity="center_vertical|left"/>
|
||||
|
@ -22,7 +22,7 @@
|
||||
android:layout_gravity="top"
|
||||
android:gravity="center_vertical"
|
||||
android:background="@color/firstrun_tabstrip"
|
||||
gecko:tabIndicatorColor="@color/text_color_highlight"
|
||||
gecko:tabIndicatorColor="@color/fennec_ui_orange"
|
||||
android:textColor="@color/android:white"/>
|
||||
</org.mozilla.gecko.firstrun.FirstrunPager>
|
||||
</org.mozilla.gecko.firstrun.FirstrunPane>
|
||||
|
@ -18,7 +18,7 @@
|
||||
android:layout_gravity="top"
|
||||
android:gravity="center_vertical"
|
||||
android:background="@color/background_light"
|
||||
gecko:tabIndicatorColor="@color/text_color_highlight"
|
||||
gecko:tabIndicatorColor="@color/fennec_ui_orange"
|
||||
android:textAppearance="@style/TextAppearance.Widget.HomePagerTabStrip"/>
|
||||
|
||||
</org.mozilla.gecko.home.HomePager>
|
||||
|
@ -23,7 +23,7 @@
|
||||
android:background="@drawable/url_bar_entry"
|
||||
android:textColor="@color/url_bar_title"
|
||||
android:textColorHint="@color/url_bar_title_hint"
|
||||
android:textColorHighlight="@color/url_bar_text_highlight"
|
||||
android:textColorHighlight="@color/fennec_ui_orange"
|
||||
android:textSelectHandle="@drawable/handle_middle"
|
||||
android:textSelectHandleLeft="@drawable/handle_start"
|
||||
android:textSelectHandleRight="@drawable/handle_end"
|
||||
|
@ -16,7 +16,7 @@
|
||||
android:textSize="@dimen/query_text_size"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:textColorHighlight="@color/highlight_orange"
|
||||
android:textColorHighlight="@color/fennec_ui_orange"
|
||||
android:textSelectHandle="@drawable/handle_middle"
|
||||
android:textSelectHandleLeft="@drawable/handle_start"
|
||||
android:textSelectHandleRight="@drawable/handle_end" />
|
||||
|
@ -4,6 +4,28 @@
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<resources>
|
||||
<!-- Fennec color palette (bug 1127517) -->
|
||||
<color name="fennec_ui_orange">#FF9500</color>
|
||||
<color name="action_orange">#E66000</color>
|
||||
<color name="action_orange_pressed">#DC5600</color>
|
||||
<color name="link_blue">#0096DD</color>
|
||||
<color name="private_browsing_purple">#CF68FF</color>
|
||||
|
||||
<color name="placeholder_active_grey">#222222</color>
|
||||
<color name="placeholder_grey">#777777</color>
|
||||
<color name="private_toolbar_grey">#292C29</color>
|
||||
<color name="text_and_tabs_tray_grey">#363B40</color>
|
||||
<color name="tabs_tray_grey_pressed">#45494E</color>
|
||||
<color name="toolbar_icon_grey">#5F6368</color>
|
||||
|
||||
<color name="tabs_tray_icon_grey">#AFB1B3</color>
|
||||
<color name="disabled_grey">#BFBFBF</color>
|
||||
<color name="toolbar_grey_pressed">#D7D7DC</color>
|
||||
<color name="toolbar_menu_dark_grey">#E1E1E6</color>
|
||||
<color name="toolbar_grey">#EBEBF0</color>
|
||||
<color name="about_page_header_grey">#F5F5F5</color>
|
||||
|
||||
<!-- Non-palette colors -->
|
||||
<color name="primary">#363B40</color>
|
||||
|
||||
<color name="background_light">#FFF5F5F5</color>
|
||||
@ -83,7 +105,6 @@
|
||||
<color name="text_color_hint_floating_focused">#33b5e5</color>
|
||||
|
||||
<!-- Highlight colors -->
|
||||
<color name="text_color_highlight">#FF9500</color>
|
||||
<color name="text_color_highlight_inverse">#D06BFF</color>
|
||||
|
||||
<!-- Link colors -->
|
||||
@ -103,7 +124,6 @@
|
||||
<color name="doorhanger_background_dark">#FFDDE4EA</color>
|
||||
|
||||
<color name="validation_message_text">#ffffff</color>
|
||||
<color name="url_bar_text_highlight">#FFFF9500</color>
|
||||
<color name="url_bar_text_highlight_pb">#FFD06BFF</color>
|
||||
<color name="suggestion_primary">#dddddd</color>
|
||||
<color name="suggestion_pressed">#bbbbbb</color>
|
||||
|
@ -6,8 +6,6 @@
|
||||
|
||||
<color name="global_background_color">#EBEBF0</color>
|
||||
|
||||
<color name="highlight_orange">#FF9500</color>
|
||||
|
||||
<color name="edit_text_default">#AFB1B3</color>
|
||||
|
||||
<!-- card colors -->
|
||||
|
@ -143,7 +143,7 @@
|
||||
</style>
|
||||
|
||||
<style name="Widget.ReadingListRow.ReadTime">
|
||||
<item name="android:textColor">@color/text_color_highlight</item>
|
||||
<item name="android:textColor">@color/fennec_ui_orange</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.BookmarkFolderView" parent="Widget.TwoLinePageRow.Title">
|
||||
@ -292,7 +292,7 @@
|
||||
-->
|
||||
<style name="TextAppearance">
|
||||
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||
<item name="android:textColorHighlight">@color/text_color_highlight</item>
|
||||
<item name="android:textColorHighlight">@color/fennec_ui_orange</item>
|
||||
<item name="android:textColorHint">?android:attr/textColorHint</item>
|
||||
<item name="android:textColorLink">?android:attr/textColorLink</item>
|
||||
<item name="android:textSize">@dimen/menu_item_textsize</item>
|
||||
@ -457,7 +457,7 @@
|
||||
<item name="android:textAppearance">@style/TextAppearance.UrlBar.Title</item>
|
||||
<item name="android:textColor">@color/url_bar_title</item>
|
||||
<item name="android:textColorHint">@color/url_bar_title_hint</item>
|
||||
<item name="android:textColorHighlight">@color/url_bar_text_highlight</item>
|
||||
<item name="android:textColorHighlight">@color/fennec_ui_orange</item>
|
||||
<item name="android:textSelectHandle">@drawable/handle_middle</item>
|
||||
<item name="android:textSelectHandleLeft">@drawable/handle_start</item>
|
||||
<item name="android:textSelectHandleRight">@drawable/handle_end</item>
|
||||
|
@ -49,7 +49,7 @@
|
||||
<item name="android:textColorHintInverse">@color/text_color_hint_inverse</item>
|
||||
|
||||
<!-- Highlight colors -->
|
||||
<item name="android:textColorHighlight">@color/text_color_highlight</item>
|
||||
<item name="android:textColorHighlight">@color/fennec_ui_orange</item>
|
||||
<item name="android:textColorHighlightInverse">@color/text_color_highlight_inverse</item>
|
||||
|
||||
<!-- Link colors -->
|
||||
|
@ -184,7 +184,7 @@ TaskbarPreview::GetActive(bool *active) {
|
||||
NS_IMETHODIMP
|
||||
TaskbarPreview::Invalidate() {
|
||||
if (!mVisible)
|
||||
return NS_ERROR_FAILURE;
|
||||
return NS_OK;
|
||||
|
||||
// DWM Composition is required for previews
|
||||
if (!nsUXThemeData::CheckForCompositor())
|
||||
|
Loading…
Reference in New Issue
Block a user