diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index f49eca5d65c9..8316d58fcc00 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -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/"); diff --git a/browser/base/content/browser-menubar.inc b/browser/base/content/browser-menubar.inc index 39464556b8b4..14410a689e3c 100644 --- a/browser/base/content/browser-menubar.inc +++ b/browser/base/content/browser-menubar.inc @@ -446,7 +446,9 @@ #ifndef XP_MACOSX # Disabled on Mac because we can't fill native menupopups asynchronously - + + + .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; +} diff --git a/browser/components/loop/content/css/panel.css b/browser/components/loop/content/css/panel.css index df4095c8eb4e..7c8c0dd35a58 100644 --- a/browser/components/loop/content/css/panel.css +++ b/browser/components/loop/content/css/panel.css @@ -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 { diff --git a/browser/components/loop/content/js/contacts.js b/browser/components/loop/content/js/contacts.js index bdfca3103a8e..aca6461b835f 100644 --- a/browser/components/loop/content/js/contacts.js +++ b/browser/components/loop/content/js/contacts.js @@ -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 ? diff --git a/browser/components/loop/content/js/contacts.jsx b/browser/components/loop/content/js/contacts.jsx index af20771bfecd..ce3c2f2db9cf 100644 --- a/browser/components/loop/content/js/contacts.jsx +++ b/browser/components/loop/content/js/contacts.jsx @@ -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( + + {mozL10n.get("gravatars_promo_message_learnmore")} + + ) + }); + return ( +
+
+ ); + } + }); + const ContactDropdown = React.createClass({ propTypes: { handleAction: React.PropTypes.func.isRequired, @@ -232,7 +285,9 @@ loop.contacts = (function(_, mozL10n) { return (
  • -
    +
    + +
    {names.firstName} {names.lastName} @@ -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 } +
      {shownContacts.available ? diff --git a/browser/components/loop/content/js/panel.js b/browser/components/loop/content/js/panel.js index c0310b514ba1..6005a20b7d35 100644 --- a/browser/components/loop/content/js/panel.js +++ b/browser/components/loop/content/js/panel.js @@ -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; }, diff --git a/browser/components/loop/content/js/panel.jsx b/browser/components/loop/content/js/panel.jsx index 5d140a3ac913..171544bae78e 100644 --- a/browser/components/loop/content/js/panel.jsx +++ b/browser/components/loop/content/js/panel.jsx @@ -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; }, diff --git a/browser/components/loop/content/shared/js/dispatcher.js b/browser/components/loop/content/shared/js/dispatcher.js index 408f0bf6de57..768164c4f665 100644 --- a/browser/components/loop/content/shared/js/dispatcher.js +++ b/browser/components/loop/content/shared/js/dispatcher.js @@ -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; diff --git a/browser/components/loop/test/desktop-local/contacts_test.js b/browser/components/loop/test/desktop-local/contacts_test.js index 4131db6fdbd4..f68bc19e90ab 100644 --- a/browser/components/loop/test/desktop-local/contacts_test.js +++ b/browser/components/loop/test/desktop-local/contacts_test.js @@ -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() { diff --git a/browser/components/loop/test/mochitest/browser_toolbarbutton.js b/browser/components/loop/test/mochitest/browser_toolbarbutton.js index f5dc1a9cd268..95b8ed32560e 100644 --- a/browser/components/loop/test/mochitest/browser_toolbarbutton.js +++ b/browser/components/loop/test/mochitest/browser_toolbarbutton.js @@ -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"); diff --git a/browser/components/loop/test/shared/dispatcher_test.js b/browser/components/loop/test/shared/dispatcher_test.js index 9193b1da0e5c..a84b08c59773 100644 --- a/browser/components/loop/test/shared/dispatcher_test.js +++ b/browser/components/loop/test/shared/dispatcher_test.js @@ -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 diff --git a/browser/components/loop/ui/fake-mozLoop.js b/browser/components/loop/ui/fake-mozLoop.js index 4031bffb8fc3..8e1f31493a58 100644 --- a/browser/components/loop/ui/fake-mozLoop.js +++ b/browser/components/loop/ui/fake-mozLoop.js @@ -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() {} }, diff --git a/browser/components/readinglist/test/browser/browser_ui_enable_disable.js b/browser/components/readinglist/test/browser/browser_ui_enable_disable.js index 797286e8154f..2610bbd88556 100644 --- a/browser/components/readinglist/test/browser/browser_ui_enable_disable.js +++ b/browser/components/readinglist/test/browser/browser_ui_enable_disable.js @@ -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) { diff --git a/browser/devtools/performance/test/head.js b/browser/devtools/performance/test/head.js index 7e5b7d2f3efc..76392e0c1db8 100644 --- a/browser/devtools/performance/test/head.js +++ b/browser/devtools/performance/test/head.js @@ -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; diff --git a/browser/devtools/performance/views/details-js-flamegraph.js b/browser/devtools/performance/views/details-js-flamegraph.js index 6924a1fd955c..c4ee13e16077 100644 --- a/browser/devtools/performance/views/details-js-flamegraph.js +++ b/browser/devtools/performance/views/details-js-flamegraph.js @@ -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. diff --git a/browser/devtools/performance/views/details-memory-flamegraph.js b/browser/devtools/performance/views/details-memory-flamegraph.js index f0f4f7ac711b..10a3173e3ba7 100644 --- a/browser/devtools/performance/views/details-memory-flamegraph.js +++ b/browser/devtools/performance/views/details-memory-flamegraph.js @@ -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. diff --git a/browser/devtools/performance/views/overview.js b/browser/devtools/performance/views/overview.js index 301d317a12a1..9d766a988d0a 100644 --- a/browser/devtools/performance/views/overview.js +++ b/browser/devtools/performance/views/overview.js @@ -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 diff --git a/browser/devtools/shared/test/browser_flame-graph-01.js b/browser/devtools/shared/test/browser_flame-graph-01.js index 733841b9e89e..44013e2d783d 100644 --- a/browser/devtools/shared/test/browser_flame-graph-01.js +++ b/browser/devtools/shared/test/browser_flame-graph-01.js @@ -26,7 +26,7 @@ function* performTest() { testGraph(host, graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_flame-graph-02.js b/browser/devtools/shared/test/browser_flame-graph-02.js index ce01e43548fb..3f776f6f38f8 100644 --- a/browser/devtools/shared/test/browser_flame-graph-02.js +++ b/browser/devtools/shared/test/browser_flame-graph-02.js @@ -23,7 +23,7 @@ function* performTest() { yield graph.ready(); testGraph(host, graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_flame-graph-03a.js b/browser/devtools/shared/test/browser_flame-graph-03a.js index 2913f60dd255..f42cf2ff8db1 100644 --- a/browser/devtools/shared/test/browser_flame-graph-03a.js +++ b/browser/devtools/shared/test/browser_flame-graph-03a.js @@ -29,7 +29,7 @@ function* performTest() { testGraph(graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_flame-graph-03b.js b/browser/devtools/shared/test/browser_flame-graph-03b.js index 707ed9ec2acd..ab04fa18f3a7 100644 --- a/browser/devtools/shared/test/browser_flame-graph-03b.js +++ b/browser/devtools/shared/test/browser_flame-graph-03b.js @@ -30,7 +30,7 @@ function* performTest() { testGraph(graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_flame-graph-04.js b/browser/devtools/shared/test/browser_flame-graph-04.js index 44cdf4e0329c..9dc698cbf7c0 100644 --- a/browser/devtools/shared/test/browser_flame-graph-04.js +++ b/browser/devtools/shared/test/browser_flame-graph-04.js @@ -25,7 +25,7 @@ function* performTest() { testGraph(graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_graphs-01.js b/browser/devtools/shared/test/browser_graphs-01.js index 3a4555d1cdd4..f9cf66cde035 100644 --- a/browser/devtools/shared/test/browser_graphs-01.js +++ b/browser/devtools/shared/test/browser_graphs-01.js @@ -27,7 +27,7 @@ function* performTest() { testGraph(host, graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_graphs-02.js b/browser/devtools/shared/test/browser_graphs-02.js index 907cb5701d8d..5c56ac93bc5b 100644 --- a/browser/devtools/shared/test/browser_graphs-02.js +++ b/browser/devtools/shared/test/browser_graphs-02.js @@ -22,7 +22,7 @@ function* performTest() { testDataAndRegions(graph); testHighlights(graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_graphs-03.js b/browser/devtools/shared/test/browser_graphs-03.js index b97acbffc633..6aa6b8aae1aa 100644 --- a/browser/devtools/shared/test/browser_graphs-03.js +++ b/browser/devtools/shared/test/browser_graphs-03.js @@ -21,7 +21,7 @@ function* performTest() { yield testSelection(graph); yield testCursor(graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_graphs-04.js b/browser/devtools/shared/test/browser_graphs-04.js index 2d90b09bfadd..c75cf3c1852e 100644 --- a/browser/devtools/shared/test/browser_graphs-04.js +++ b/browser/devtools/shared/test/browser_graphs-04.js @@ -19,7 +19,7 @@ function* performTest() { testGraph(graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_graphs-05.js b/browser/devtools/shared/test/browser_graphs-05.js index 78fdfbf06f73..15d9c48c9948 100644 --- a/browser/devtools/shared/test/browser_graphs-05.js +++ b/browser/devtools/shared/test/browser_graphs-05.js @@ -21,7 +21,7 @@ function* performTest() { testGraph(graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_graphs-06.js b/browser/devtools/shared/test/browser_graphs-06.js index 781b0ed9cf01..fee5a14b8d96 100644 --- a/browser/devtools/shared/test/browser_graphs-06.js +++ b/browser/devtools/shared/test/browser_graphs-06.js @@ -21,7 +21,7 @@ function* performTest() { testGraph(graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_graphs-07a.js b/browser/devtools/shared/test/browser_graphs-07a.js index d0828b2ee5b2..c55d75f5387f 100644 --- a/browser/devtools/shared/test/browser_graphs-07a.js +++ b/browser/devtools/shared/test/browser_graphs-07a.js @@ -20,7 +20,7 @@ function* performTest() { testGraph(graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_graphs-07b.js b/browser/devtools/shared/test/browser_graphs-07b.js index fe24cf64fde1..b5beb8fbd6a1 100644 --- a/browser/devtools/shared/test/browser_graphs-07b.js +++ b/browser/devtools/shared/test/browser_graphs-07b.js @@ -20,7 +20,7 @@ function* performTest() { testGraph(graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_graphs-08.js b/browser/devtools/shared/test/browser_graphs-08.js index 158e12823ce2..eae8fa1da0fd 100644 --- a/browser/devtools/shared/test/browser_graphs-08.js +++ b/browser/devtools/shared/test/browser_graphs-08.js @@ -20,7 +20,7 @@ function* performTest() { testGraph(graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_graphs-09a.js b/browser/devtools/shared/test/browser_graphs-09a.js index bd5c9d9dc6f8..3325139a5fea 100644 --- a/browser/devtools/shared/test/browser_graphs-09a.js +++ b/browser/devtools/shared/test/browser_graphs-09a.js @@ -19,7 +19,7 @@ function* performTest() { yield testGraph(graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_graphs-09b.js b/browser/devtools/shared/test/browser_graphs-09b.js index 0cae0d46797f..d5171ccd4654 100644 --- a/browser/devtools/shared/test/browser_graphs-09b.js +++ b/browser/devtools/shared/test/browser_graphs-09b.js @@ -21,7 +21,7 @@ function* performTest() { yield testGraph(graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_graphs-09c.js b/browser/devtools/shared/test/browser_graphs-09c.js index 97e4dd7e57ef..7007f516b6de 100644 --- a/browser/devtools/shared/test/browser_graphs-09c.js +++ b/browser/devtools/shared/test/browser_graphs-09c.js @@ -19,7 +19,7 @@ function* performTest() { yield testGraph(graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_graphs-09d.js b/browser/devtools/shared/test/browser_graphs-09d.js index c56163aa6bed..6138b91f538b 100644 --- a/browser/devtools/shared/test/browser_graphs-09d.js +++ b/browser/devtools/shared/test/browser_graphs-09d.js @@ -20,7 +20,7 @@ function* performTest() { yield testGraph(graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_graphs-09e.js b/browser/devtools/shared/test/browser_graphs-09e.js index 2f75f30996e9..afa7a9065ff1 100644 --- a/browser/devtools/shared/test/browser_graphs-09e.js +++ b/browser/devtools/shared/test/browser_graphs-09e.js @@ -22,7 +22,7 @@ function* performTest() { yield testGraph(graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_graphs-09f.js b/browser/devtools/shared/test/browser_graphs-09f.js index 7b43a4c412e7..10723acc35b5 100644 --- a/browser/devtools/shared/test/browser_graphs-09f.js +++ b/browser/devtools/shared/test/browser_graphs-09f.js @@ -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(); } diff --git a/browser/devtools/shared/test/browser_graphs-10a.js b/browser/devtools/shared/test/browser_graphs-10a.js index c5f2f8d948d4..bd4a61a0d23a 100644 --- a/browser/devtools/shared/test/browser_graphs-10a.js +++ b/browser/devtools/shared/test/browser_graphs-10a.js @@ -27,7 +27,7 @@ function* performTest() { is(refreshCount, 2, "The graph should've been refreshed 2 times."); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_graphs-10b.js b/browser/devtools/shared/test/browser_graphs-10b.js index ddb0f5a0f16a..2796765b2786 100644 --- a/browser/devtools/shared/test/browser_graphs-10b.js +++ b/browser/devtools/shared/test/browser_graphs-10b.js @@ -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(); } diff --git a/browser/devtools/shared/test/browser_graphs-11a.js b/browser/devtools/shared/test/browser_graphs-11a.js index 51f8b5a02351..cb07d91f0f67 100644 --- a/browser/devtools/shared/test/browser_graphs-11a.js +++ b/browser/devtools/shared/test/browser_graphs-11a.js @@ -25,7 +25,7 @@ function* performTest() { testGraph(graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_graphs-11b.js b/browser/devtools/shared/test/browser_graphs-11b.js index bf283e2d3c12..509026592d2e 100644 --- a/browser/devtools/shared/test/browser_graphs-11b.js +++ b/browser/devtools/shared/test/browser_graphs-11b.js @@ -29,7 +29,7 @@ function* performTest() { yield graph.once("ready"); yield testGraph(graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_graphs-12.js b/browser/devtools/shared/test/browser_graphs-12.js index 1517f3efb20b..8cab4fb34c86 100644 --- a/browser/devtools/shared/test/browser_graphs-12.js +++ b/browser/devtools/shared/test/browser_graphs-12.js @@ -35,8 +35,8 @@ function* performTest() { testGraphs(graph1, graph2); - graph1.destroy(); - graph2.destroy(); + yield graph1.destroy(); + yield graph2.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_graphs-13.js b/browser/devtools/shared/test/browser_graphs-13.js index f0f09203c72c..b7b6b729d0c7 100644 --- a/browser/devtools/shared/test/browser_graphs-13.js +++ b/browser/devtools/shared/test/browser_graphs-13.js @@ -23,7 +23,7 @@ function* performTest() { yield graph.ready(); testGraph(host, graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/test/browser_graphs-14.js b/browser/devtools/shared/test/browser_graphs-14.js index ce7b96a5d73f..4f3123618a6a 100644 --- a/browser/devtools/shared/test/browser_graphs-14.js +++ b/browser/devtools/shared/test/browser_graphs-14.js @@ -19,7 +19,7 @@ function* performTest() { yield testGraph(graph); - graph.destroy(); + yield graph.destroy(); host.destroy(); } diff --git a/browser/devtools/shared/timeline/markers-overview.js b/browser/devtools/shared/timeline/markers-overview.js index 5ad805eaa787..a333f54b625e 100644 --- a/browser/devtools/shared/timeline/markers-overview.js +++ b/browser/devtools/shared/timeline/markers-overview.js @@ -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; diff --git a/browser/devtools/shared/timeline/waterfall.js b/browser/devtools/shared/timeline/waterfall.js index 8093d3088586..a74569a97932 100644 --- a/browser/devtools/shared/timeline/waterfall.js +++ b/browser/devtools/shared/timeline/waterfall.js @@ -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; diff --git a/browser/devtools/shared/widgets/FlameGraph.jsm b/browser/devtools/shared/widgets/FlameGraph.jsm index 208e2e3d2fc4..f54746e537db 100644 --- a/browser/devtools/shared/widgets/FlameGraph.jsm +++ b/browser/devtools/shared/widgets/FlameGraph.jsm @@ -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; diff --git a/browser/devtools/shared/widgets/Graphs.jsm b/browser/devtools/shared/widgets/Graphs.jsm index ac27bd10eb40..2dbc791a91c4 100644 --- a/browser/devtools/shared/widgets/Graphs.jsm +++ b/browser/devtools/shared/widgets/Graphs.jsm @@ -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. diff --git a/browser/modules/WindowsPreviewPerTab.jsm b/browser/modules/WindowsPreviewPerTab.jsm index 1d6431cc4d40..61836f5a6664 100644 --- a/browser/modules/WindowsPreviewPerTab.jsm +++ b/browser/modules/WindowsPreviewPerTab.jsm @@ -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(); diff --git a/browser/themes/shared/devtools/animationinspector.css b/browser/themes/shared/devtools/animationinspector.css index 5c519c558bab..c632d91296b3 100644 --- a/browser/themes/shared/devtools/animationinspector.css +++ b/browser/themes/shared/devtools/animationinspector.css @@ -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; } diff --git a/mobile/android/base/CustomEditText.java b/mobile/android/base/CustomEditText.java index 1a4401892bab..2ea7d6d02ff9 100644 --- a/mobile/android/base/CustomEditText.java +++ b/mobile/android/base/CustomEditText.java @@ -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); } diff --git a/mobile/android/base/resources/drawable/progressbar.xml b/mobile/android/base/resources/drawable/progressbar.xml index e66a23f5defa..627484a1f3a6 100644 --- a/mobile/android/base/resources/drawable/progressbar.xml +++ b/mobile/android/base/resources/drawable/progressbar.xml @@ -2,7 +2,7 @@ - + diff --git a/mobile/android/base/resources/drawable/suggestion_selector.xml b/mobile/android/base/resources/drawable/suggestion_selector.xml index b1196210cef7..be88d11377c4 100644 --- a/mobile/android/base/resources/drawable/suggestion_selector.xml +++ b/mobile/android/base/resources/drawable/suggestion_selector.xml @@ -5,7 +5,7 @@ diff --git a/mobile/android/base/resources/drawable/tab_thumbnail.xml b/mobile/android/base/resources/drawable/tab_thumbnail.xml index d886dc06127d..056568dd1292 100644 --- a/mobile/android/base/resources/drawable/tab_thumbnail.xml +++ b/mobile/android/base/resources/drawable/tab_thumbnail.xml @@ -39,7 +39,7 @@ gecko:state_recording="false"> - + diff --git a/mobile/android/base/resources/layout/doorhanger.xml b/mobile/android/base/resources/layout/doorhanger.xml index 76dfe272de7d..33a8b5aa3279 100644 --- a/mobile/android/base/resources/layout/doorhanger.xml +++ b/mobile/android/base/resources/layout/doorhanger.xml @@ -52,7 +52,7 @@ diff --git a/mobile/android/base/resources/layout/find_in_page_content.xml b/mobile/android/base/resources/layout/find_in_page_content.xml index 5a84e17f5302..8ce74db051af 100644 --- a/mobile/android/base/resources/layout/find_in_page_content.xml +++ b/mobile/android/base/resources/layout/find_in_page_content.xml @@ -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"/> diff --git a/mobile/android/base/resources/layout/firstrun_pane.xml b/mobile/android/base/resources/layout/firstrun_pane.xml index a2c10203eca3..88d726e682ba 100644 --- a/mobile/android/base/resources/layout/firstrun_pane.xml +++ b/mobile/android/base/resources/layout/firstrun_pane.xml @@ -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"/> diff --git a/mobile/android/base/resources/layout/home_pager.xml b/mobile/android/base/resources/layout/home_pager.xml index 595c8866e45e..da2002194f60 100644 --- a/mobile/android/base/resources/layout/home_pager.xml +++ b/mobile/android/base/resources/layout/home_pager.xml @@ -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"/> diff --git a/mobile/android/base/resources/layout/pin_site_dialog.xml b/mobile/android/base/resources/layout/pin_site_dialog.xml index baeb21a132ed..7a4f03ed183a 100644 --- a/mobile/android/base/resources/layout/pin_site_dialog.xml +++ b/mobile/android/base/resources/layout/pin_site_dialog.xml @@ -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" diff --git a/mobile/android/base/resources/layout/search_bar.xml b/mobile/android/base/resources/layout/search_bar.xml index e4637a244818..69232ff07f70 100644 --- a/mobile/android/base/resources/layout/search_bar.xml +++ b/mobile/android/base/resources/layout/search_bar.xml @@ -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" /> diff --git a/mobile/android/base/resources/values/colors.xml b/mobile/android/base/resources/values/colors.xml index a0e28fffd707..6ab160d7c9af 100644 --- a/mobile/android/base/resources/values/colors.xml +++ b/mobile/android/base/resources/values/colors.xml @@ -4,6 +4,28 @@ - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + + #FF9500 + #E66000 + #DC5600 + #0096DD + #CF68FF + + #222222 + #777777 + #292C29 + #363B40 + #45494E + #5F6368 + + #AFB1B3 + #BFBFBF + #D7D7DC + #E1E1E6 + #EBEBF0 + #F5F5F5 + + #363B40 #FFF5F5F5 @@ -83,7 +105,6 @@ #33b5e5 - #FF9500 #D06BFF @@ -103,7 +124,6 @@ #FFDDE4EA #ffffff - #FFFF9500 #FFD06BFF #dddddd #bbbbbb diff --git a/mobile/android/base/resources/values/search_colors.xml b/mobile/android/base/resources/values/search_colors.xml index b71c200b80f7..921709d97930 100644 --- a/mobile/android/base/resources/values/search_colors.xml +++ b/mobile/android/base/resources/values/search_colors.xml @@ -6,8 +6,6 @@ #EBEBF0 - #FF9500 - #AFB1B3 diff --git a/mobile/android/base/resources/values/styles.xml b/mobile/android/base/resources/values/styles.xml index e7bb643afe17..d2cf8200b28b 100644 --- a/mobile/android/base/resources/values/styles.xml +++ b/mobile/android/base/resources/values/styles.xml @@ -143,7 +143,7 @@