зеркало из https://github.com/mozilla/gecko-dev.git
merge fx-team to mozilla-central a=merge
This commit is contained in:
Коммит
cd7ed331d3
|
@ -32,5 +32,5 @@ exports.locale = function locale() {
|
|||
// Returns the short locale code: ja, en, fr
|
||||
exports.language = function language() {
|
||||
return bestMatchingLocale ? bestMatchingLocale.split("-")[0].toLowerCase()
|
||||
: null;
|
||||
: "en";
|
||||
}
|
||||
|
|
|
@ -103,9 +103,11 @@ let gSyncUI = {
|
|||
// We want to treat "account needs verification" as "needs setup". So
|
||||
// "reach in" to Weave.Status._authManager to check whether we the signed-in
|
||||
// user is verified.
|
||||
// Referencing Weave.Status spins a nested event loop to initialize the
|
||||
// authManager, so this should always return a value directly.
|
||||
// This only applies to fxAccounts-based Sync.
|
||||
// NOTE: We used to have this _authManager hack to avoid a nested
|
||||
// event-loop from querying Weave.Status.checkSetup() - while that's no
|
||||
// longer true, we do still have the FxA-specific requirement of checking
|
||||
// the verified state - so the hack remains. We should consider refactoring
|
||||
// Sync's "check setup" capabilities to take this into account at some point...
|
||||
if (Weave.Status._authManager._signedInUser !== undefined) {
|
||||
// If we have a signed in user already, and that user is not verified,
|
||||
// revert to the "needs setup" state.
|
||||
|
|
|
@ -340,7 +340,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|||
let engine = Services.search.getEngineByName(action.params.engineName);
|
||||
let query = action.params.searchSuggestion ||
|
||||
action.params.searchQuery;
|
||||
let submission = engine.getSubmission(query);
|
||||
let submission = engine.getSubmission(query, null, "keyword");
|
||||
|
||||
url = submission.uri.spec;
|
||||
postData = submission.postData;
|
||||
|
@ -1390,7 +1390,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|||
let engine = Services.search.getEngineByName(action.params.engineName);
|
||||
let query = action.params.searchSuggestion ||
|
||||
action.params.searchQuery;
|
||||
let submission = engine.getSubmission(query);
|
||||
let submission = engine.getSubmission(query, null, "keyword");
|
||||
url = submission.uri.spec;
|
||||
options.postData = submission.postData;
|
||||
break;
|
||||
|
|
|
@ -293,6 +293,15 @@ html[dir="rtl"] .tab-view li:nth-child(2).selected ~ .slide-bar {
|
|||
font-size: 2.2rem;
|
||||
}
|
||||
|
||||
.room-list-loading {
|
||||
margin: 5rem 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.room-list-loading > img {
|
||||
width: 66px;
|
||||
}
|
||||
|
||||
|
||||
/* Rooms */
|
||||
.rooms {
|
||||
|
@ -321,10 +330,10 @@ html[dir="rtl"] .tab-view li:nth-child(2).selected ~ .slide-bar {
|
|||
}
|
||||
|
||||
.new-room-view > .context {
|
||||
border-top: 1px solid #ebebeb;
|
||||
flex: 1;
|
||||
border-radius: 3px 3px 0 0;
|
||||
margin: 1rem 0 .5rem;
|
||||
padding: 1rem 15px;
|
||||
margin: .5rem 0;
|
||||
padding: .5rem 15px 1rem;
|
||||
}
|
||||
|
||||
.new-room-view > .context > .context-enabled {
|
||||
|
|
|
@ -809,6 +809,21 @@ loop.panel = (function(_, mozL10n) {
|
|||
mozL10n.get("display_name_guest");
|
||||
},
|
||||
|
||||
/**
|
||||
* Let the user know we're loading rooms
|
||||
* @returns {Object} React render
|
||||
*/
|
||||
_renderLoadingRoomsView: function() {
|
||||
return (
|
||||
React.createElement("div", {className: "room-list"},
|
||||
React.createElement("div", {className: "room-list-loading"},
|
||||
React.createElement("img", {src: "loop/shared/img/animated-spinner.svg"})
|
||||
),
|
||||
this._renderNewRoomButton()
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
_renderNoRoomsView: function() {
|
||||
return (
|
||||
React.createElement("div", {className: "room-list"},
|
||||
|
@ -841,6 +856,10 @@ loop.panel = (function(_, mozL10n) {
|
|||
console.error("RoomList error", this.state.error);
|
||||
}
|
||||
|
||||
if (this.state.pendingInitialRetrieval) {
|
||||
return this._renderLoadingRoomsView();
|
||||
}
|
||||
|
||||
if (!this.state.rooms.length) {
|
||||
return this._renderNoRoomsView();
|
||||
}
|
||||
|
|
|
@ -809,6 +809,21 @@ loop.panel = (function(_, mozL10n) {
|
|||
mozL10n.get("display_name_guest");
|
||||
},
|
||||
|
||||
/**
|
||||
* Let the user know we're loading rooms
|
||||
* @returns {Object} React render
|
||||
*/
|
||||
_renderLoadingRoomsView: function() {
|
||||
return (
|
||||
<div className="room-list">
|
||||
<div className="room-list-loading">
|
||||
<img src="loop/shared/img/animated-spinner.svg" />
|
||||
</div>
|
||||
{this._renderNewRoomButton()}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
_renderNoRoomsView: function() {
|
||||
return (
|
||||
<div className="room-list">
|
||||
|
@ -841,6 +856,10 @@ loop.panel = (function(_, mozL10n) {
|
|||
console.error("RoomList error", this.state.error);
|
||||
}
|
||||
|
||||
if (this.state.pendingInitialRetrieval) {
|
||||
return this._renderLoadingRoomsView();
|
||||
}
|
||||
|
||||
if (!this.state.rooms.length) {
|
||||
return this._renderNoRoomsView();
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ loop.store = loop.store || {};
|
|||
activeRoom: this.activeRoomStore ? this.activeRoomStore.getStoreState() : {},
|
||||
error: null,
|
||||
pendingCreation: false,
|
||||
pendingInitialRetrieval: false,
|
||||
pendingInitialRetrieval: true,
|
||||
rooms: [],
|
||||
savingContext: false
|
||||
};
|
||||
|
|
|
@ -593,8 +593,20 @@ loop.roomViews = (function(mozL10n) {
|
|||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if the invitation controls should be shown.
|
||||
*
|
||||
* @return {Boolean} True if there's no guests.
|
||||
*/
|
||||
_shouldRenderInvitationOverlay: function() {
|
||||
return (this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS);
|
||||
var hasGuests = typeof this.state.participants === "object" &&
|
||||
this.state.participants.filter(function(participant) {
|
||||
return !participant.owner;
|
||||
}).length > 0;
|
||||
|
||||
// Don't show if the room has participants whether from the room state or
|
||||
// there being non-owner guests in the participants array.
|
||||
return this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS && !hasGuests;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -593,8 +593,20 @@ loop.roomViews = (function(mozL10n) {
|
|||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if the invitation controls should be shown.
|
||||
*
|
||||
* @return {Boolean} True if there's no guests.
|
||||
*/
|
||||
_shouldRenderInvitationOverlay: function() {
|
||||
return (this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS);
|
||||
var hasGuests = typeof this.state.participants === "object" &&
|
||||
this.state.participants.filter(function(participant) {
|
||||
return !participant.owner;
|
||||
}).length > 0;
|
||||
|
||||
// Don't show if the room has participants whether from the room state or
|
||||
// there being non-owner guests in the participants array.
|
||||
return this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS && !hasGuests;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g fill="#0095FF"><path opacity=".15" d="M4.9 11.8c.2-.4.7-.5 1.1-.3.4.2.5.7.3 1.1l-1.2 2.1c-.2.3-.7.5-1.1.2-.4-.2-.5-.7-.3-1.1l1.2-2z"><animate attributeType="CSS" attributeName="opacity" from="1" to=".15" dur="0.96s" repeatCount="indefinite"/></path><path opacity=".2" d="M3.4 9.7c.4-.2.9-.1 1.1.3.2.4.1.9-.3 1.1l-2.1 1.2c-.4.2-.9.1-1.1-.3-.2-.4-.1-.9.3-1.1l2.1-1.2z"><animate attributeType="CSS" attributeName="opacity" from="1" to=".15" dur="0.96s" repeatCount="indefinite" begin="0.08s"/></path><path opacity=".25" d="M3.2 7.2c.4 0 .8.4.8.8s-.4.8-.8.8H.8C.4 8.8 0 8.4 0 8s.4-.8.8-.8h2.4z"><animate attributeType="CSS" attributeName="opacity" from="1" to=".15" dur="0.96s" repeatCount="indefinite" begin="0.16s"/></path><path opacity=".35" d="M1.4 5.1C1 4.9.8 4.4 1.1 4c.2-.4.7-.5 1.1-.3l2.1 1.2c.3.2.5.7.2 1.1-.2.4-.7.5-1.1.3l-2-1.2z"><animate attributeType="CSS" attributeName="opacity" from="1" to=".15" dur="0.96s" repeatCount="indefinite" begin="0.24s"/></path><path opacity=".45" d="M3.7 2.2c-.2-.4-.1-.9.3-1.1.4-.2.9-.1 1.1.3l1.2 2.1c.2.3.1.8-.3 1-.4.3-.9.1-1.1-.3l-1.2-2z"><animate attributeType="CSS" attributeName="opacity" from="1" to=".15" dur="0.96s" repeatCount="indefinite" begin="0.32s"/></path><path opacity=".5" d="M8.8 3.2c0 .4-.4.8-.8.8s-.8-.4-.8-.8V.8c0-.4.4-.8.8-.8s.8.4.8.8v2.4z"><animate attributeType="CSS" attributeName="opacity" from="1" to=".15" dur="0.96s" repeatCount="indefinite" begin="0.40s"/></path><path opacity=".55" d="M10.9 1.4c.2-.4.7-.6 1.1-.3.4.2.5.7.3 1.1l-1.2 2.1c-.2.4-.7.5-1.1.3-.4-.3-.5-.8-.3-1.2l1.2-2z"><animate attributeType="CSS" attributeName="opacity" from="1" to=".15" dur="0.96s" repeatCount="indefinite" begin="0.48s"/></path><path opacity=".6" d="M13.8 3.7c.4-.2.9-.1 1.1.3.2.4.1.9-.3 1.1l-2.1 1.2c-.4.2-.9.1-1.1-.3-.2-.4-.1-.9.3-1.1l2.1-1.2z"><animate attributeType="CSS" attributeName="opacity" from="1" to=".15" dur="0.96s" repeatCount="indefinite" begin="0.56s"/></path><path opacity=".65" d="M15.2 7.2c.4 0 .8.4.8.8s-.4.8-.8.8h-2.4c-.4 0-.8-.4-.8-.8s.4-.8.8-.8h2.4z"><animate attributeType="CSS" attributeName="opacity" from="1" to=".15" dur="0.96s" repeatCount="indefinite" begin="0.64s"/></path><path opacity=".7" d="M11.8 11.1c-.4-.2-.5-.7-.3-1.1.2-.4.7-.5 1.1-.3l2.1 1.2c.4.2.5.7.3 1.1-.2.4-.7.5-1.1.3l-2.1-1.2z"><animate attributeType="CSS" attributeName="opacity" from="1" to=".15" dur="0.96s" repeatCount="indefinite" begin="0.70s"/></path><path opacity=".8" d="M9.7 12.6c-.2-.4-.1-.9.3-1.1.4-.2.9-.1 1.1.3l1.2 2.1c.2.4.1.9-.3 1.1-.4.2-.9.1-1.1-.3l-1.2-2.1z"><animate attributeType="CSS" attributeName="opacity" from="1" to=".15" dur="0.96s" repeatCount="indefinite" begin="0.78s"/></path><path d="M8.8 15.2c0 .4-.4.8-.8.8s-.8-.4-.8-.8v-2.4c0-.4.4-.8.8-.8s.8.4.8.8v2.4z"><animate attributeType="CSS" attributeName="opacity" from="1" to=".15" dur="0.96s" repeatCount="indefinite" begin="0.86s"/></path></g><path fill-rule="evenodd" clip-rule="evenodd" fill="#0095FF" d="M8 8.7c-1.5 0-2.6-.5-2.6-.5S5.9 10 8 10s2.6-1.9 2.6-1.9-1.1.6-2.6.6zM9.3 6c.4 0 .7.3.7.7s-.3.7-.7.7-.7-.3-.7-.7.3-.7.7-.7zM6.7 6c.4 0 .7.3.7.7s-.3.7-.7.7-.7-.3-.7-.7.3-.7.7-.7z"/></svg>
|
После Ширина: | Высота: | Размер: 3.1 KiB |
|
@ -51,6 +51,7 @@ loop.store.ActiveRoomStore = (function() {
|
|||
var OPTIONAL_ROOMINFO_FIELDS = {
|
||||
urls: "roomContextUrls",
|
||||
description: "roomDescription",
|
||||
participants: "participants",
|
||||
roomInfoFailure: "roomInfoFailure",
|
||||
roomName: "roomName",
|
||||
roomState: "roomState"
|
||||
|
@ -296,6 +297,7 @@ loop.store.ActiveRoomStore = (function() {
|
|||
}
|
||||
|
||||
this.dispatchAction(new sharedActions.SetupRoomInfo({
|
||||
participants: roomData.participants,
|
||||
roomToken: actionData.roomToken,
|
||||
roomContextUrls: roomData.decryptedContext.urls,
|
||||
roomDescription: roomData.decryptedContext.description,
|
||||
|
@ -418,6 +420,7 @@ loop.store.ActiveRoomStore = (function() {
|
|||
}
|
||||
|
||||
this.setStoreState({
|
||||
participants: actionData.participants,
|
||||
roomContextUrls: actionData.roomContextUrls,
|
||||
roomDescription: actionData.roomDescription,
|
||||
roomName: actionData.roomName,
|
||||
|
@ -449,7 +452,7 @@ loop.store.ActiveRoomStore = (function() {
|
|||
// Iterate over the optional fields that _may_ be present on the actionData
|
||||
// object.
|
||||
Object.keys(OPTIONAL_ROOMINFO_FIELDS).forEach(function(field) {
|
||||
if (actionData[field]) {
|
||||
if (actionData[field] !== undefined) {
|
||||
newState[OPTIONAL_ROOMINFO_FIELDS[field]] = actionData[field];
|
||||
}
|
||||
});
|
||||
|
@ -478,6 +481,7 @@ loop.store.ActiveRoomStore = (function() {
|
|||
this.dispatchAction(new sharedActions.UpdateRoomInfo({
|
||||
urls: roomData.decryptedContext.urls,
|
||||
description: roomData.decryptedContext.description,
|
||||
participants: roomData.participants,
|
||||
roomName: roomData.decryptedContext.roomName,
|
||||
roomUrl: roomData.roomUrl
|
||||
}));
|
||||
|
@ -792,7 +796,16 @@ loop.store.ActiveRoomStore = (function() {
|
|||
* one participantleaves.
|
||||
*/
|
||||
remotePeerDisconnected: function() {
|
||||
// Update the participants to just the owner.
|
||||
var participants = this.getStoreState("participants");
|
||||
if (participants) {
|
||||
participants = participants.filter(function(participant) {
|
||||
return participant.owner;
|
||||
});
|
||||
}
|
||||
|
||||
this.setStoreState({
|
||||
participants: participants,
|
||||
roomState: ROOM_STATES.SESSION_CONNECTED,
|
||||
remoteSrcVideoObject: null
|
||||
});
|
||||
|
|
|
@ -94,6 +94,7 @@ browser.jar:
|
|||
content/browser/loop/shared/img/empty_contacts.svg (content/shared/img/empty_contacts.svg)
|
||||
content/browser/loop/shared/img/empty_conversations.svg (content/shared/img/empty_conversations.svg)
|
||||
content/browser/loop/shared/img/empty_search.svg (content/shared/img/empty_search.svg)
|
||||
content/browser/loop/shared/img/animated-spinner.svg (content/shared/img/animated-spinner.svg)
|
||||
content/browser/loop/shared/img/avatars.svg (content/shared/img/avatars.svg)
|
||||
content/browser/loop/shared/img/firefox-avatar.svg (content/shared/img/firefox-avatar.svg)
|
||||
|
||||
|
|
|
@ -850,6 +850,13 @@ describe("loop.panel", function() {
|
|||
sinon.assert.calledWithExactly(document.mozL10n.get,
|
||||
"no_conversations_message_heading");
|
||||
});
|
||||
|
||||
it("should display a loading animation when rooms are pending", function() {
|
||||
var view = createTestComponent();
|
||||
roomStore.setStoreState({pendingInitialRetrieval: true});
|
||||
|
||||
expect(view.getDOMNode().querySelectorAll(".room-list-loading").length).to.eql(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("loop.panel.NewRoomView", function() {
|
||||
|
|
|
@ -69,7 +69,7 @@ describe("loop.store.RoomStore", function () {
|
|||
var defaultStoreState = {
|
||||
error: undefined,
|
||||
pendingCreation: false,
|
||||
pendingInitialRetrieval: false,
|
||||
pendingInitialRetrieval: true,
|
||||
rooms: [],
|
||||
activeRoom: {}
|
||||
};
|
||||
|
|
|
@ -470,8 +470,51 @@ describe("loop.roomViews", function () {
|
|||
|
||||
view = mountTestComponent();
|
||||
|
||||
expect(TestUtils.findRenderedComponentWithType(view,
|
||||
loop.roomViews.DesktopRoomInvitationView).getDOMNode()).to.not.eql(null);
|
||||
});
|
||||
|
||||
it("should render the DesktopRoomInvitationView if roomState is `JOINED` with just owner",
|
||||
function() {
|
||||
activeRoomStore.setStoreState({
|
||||
participants: [{owner: true}],
|
||||
roomState: ROOM_STATES.JOINED
|
||||
});
|
||||
|
||||
view = mountTestComponent();
|
||||
|
||||
expect(TestUtils.findRenderedComponentWithType(view,
|
||||
loop.roomViews.DesktopRoomInvitationView).getDOMNode()).to.not.eql(null);
|
||||
});
|
||||
|
||||
it("should render the DesktopRoomConversationView if roomState is `JOINED` with remote participant",
|
||||
function() {
|
||||
activeRoomStore.setStoreState({
|
||||
participants: [{}],
|
||||
roomState: ROOM_STATES.JOINED
|
||||
});
|
||||
|
||||
view = mountTestComponent();
|
||||
|
||||
TestUtils.findRenderedComponentWithType(view,
|
||||
loop.roomViews.DesktopRoomInvitationView);
|
||||
loop.roomViews.DesktopRoomConversationView);
|
||||
expect(TestUtils.findRenderedComponentWithType(view,
|
||||
loop.roomViews.DesktopRoomInvitationView).getDOMNode()).to.eql(null);
|
||||
});
|
||||
|
||||
it("should render the DesktopRoomConversationView if roomState is `JOINED` with participants",
|
||||
function() {
|
||||
activeRoomStore.setStoreState({
|
||||
participants: [{owner: true}, {}],
|
||||
roomState: ROOM_STATES.JOINED
|
||||
});
|
||||
|
||||
view = mountTestComponent();
|
||||
|
||||
TestUtils.findRenderedComponentWithType(view,
|
||||
loop.roomViews.DesktopRoomConversationView);
|
||||
expect(TestUtils.findRenderedComponentWithType(view,
|
||||
loop.roomViews.DesktopRoomInvitationView).getDOMNode()).to.eql(null);
|
||||
});
|
||||
|
||||
it("should render the DesktopRoomConversationView if roomState is `HAS_PARTICIPANTS`",
|
||||
|
@ -482,6 +525,8 @@ describe("loop.roomViews", function () {
|
|||
|
||||
TestUtils.findRenderedComponentWithType(view,
|
||||
loop.roomViews.DesktopRoomConversationView);
|
||||
expect(TestUtils.findRenderedComponentWithType(view,
|
||||
loop.roomViews.DesktopRoomInvitationView).getDOMNode()).to.eql(null);
|
||||
});
|
||||
|
||||
it("should call onCallTerminated when the call ended", function() {
|
||||
|
|
|
@ -310,6 +310,7 @@ describe("loop.store.ActiveRoomStore", function () {
|
|||
decryptedContext: {
|
||||
roomName: "Monkeys"
|
||||
},
|
||||
participants: [],
|
||||
roomUrl: "http://invalid"
|
||||
};
|
||||
|
||||
|
@ -350,6 +351,7 @@ describe("loop.store.ActiveRoomStore", function () {
|
|||
new sharedActions.SetupRoomInfo({
|
||||
roomContextUrls: undefined,
|
||||
roomDescription: undefined,
|
||||
participants: [],
|
||||
roomToken: fakeToken,
|
||||
roomName: fakeRoomData.decryptedContext.roomName,
|
||||
roomUrl: fakeRoomData.roomUrl,
|
||||
|
@ -1277,6 +1279,30 @@ describe("loop.store.ActiveRoomStore", function () {
|
|||
|
||||
expect(store.getStoreState().remoteSrcVideoObject).eql(null);
|
||||
});
|
||||
|
||||
it("should remove non-owner participants", function() {
|
||||
store.setStoreState({
|
||||
participants: [{owner: true}, {}]
|
||||
});
|
||||
|
||||
store.remotePeerDisconnected();
|
||||
|
||||
var participants = store.getStoreState().participants;
|
||||
expect(participants).to.have.length.of(1);
|
||||
expect(participants[0].owner).eql(true);
|
||||
});
|
||||
|
||||
it("should keep the owner participant", function() {
|
||||
store.setStoreState({
|
||||
participants: [{owner: true}]
|
||||
});
|
||||
|
||||
store.remotePeerDisconnected();
|
||||
|
||||
var participants = store.getStoreState().participants;
|
||||
expect(participants).to.have.length.of(1);
|
||||
expect(participants[0].owner).eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#connectionStatus", function() {
|
||||
|
@ -1518,6 +1544,7 @@ describe("loop.store.ActiveRoomStore", function () {
|
|||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.UpdateRoomInfo({
|
||||
description: "fakeDescription",
|
||||
participants: undefined,
|
||||
roomName: fakeRoomData.decryptedContext.roomName,
|
||||
roomUrl: fakeRoomData.roomUrl,
|
||||
urls: {
|
||||
|
|
|
@ -186,6 +186,17 @@ let gSyncPane = {
|
|||
}
|
||||
},
|
||||
|
||||
_closeSyncStatusMessageBox: function() {
|
||||
document.getElementById("syncStatusMessage").removeAttribute("message-type");
|
||||
document.getElementById("syncStatusMessageTitle").textContent = "";
|
||||
document.getElementById("syncStatusMessageDescription").textContent = "";
|
||||
let learnMoreLink = document.getElementById("learnMoreLink");
|
||||
if (learnMoreLink) {
|
||||
learnMoreLink.parentNode.removeChild(learnMoreLink);
|
||||
}
|
||||
document.getElementById("sync-migration-buttons-deck").hidden = true;
|
||||
},
|
||||
|
||||
_setupEventListeners: function() {
|
||||
function setEventListener(aId, aEventType, aCallback)
|
||||
{
|
||||
|
@ -193,6 +204,9 @@ let gSyncPane = {
|
|||
.addEventListener(aEventType, aCallback.bind(gSyncPane));
|
||||
}
|
||||
|
||||
setEventListener("syncStatusMessageClose", "command", function () {
|
||||
gSyncPane._closeSyncStatusMessageBox();
|
||||
});
|
||||
setEventListener("noAccountSetup", "click", function (aEvent) {
|
||||
aEvent.stopPropagation();
|
||||
gSyncPane.openSetup(null);
|
||||
|
@ -431,16 +445,17 @@ let gSyncPane = {
|
|||
},
|
||||
|
||||
updateMigrationState: function(subject, state) {
|
||||
this._closeSyncStatusMessageBox();
|
||||
let selIndex;
|
||||
let sb = this._accountsStringBundle;
|
||||
switch (state) {
|
||||
case fxaMigrator.STATE_USER_FXA: {
|
||||
let sb = this._accountsStringBundle;
|
||||
// There are 2 cases here - no email address means it is an offer on
|
||||
// the first device (so the user is prompted to create an account).
|
||||
// If there is an email address it is the "join the party" flow, so the
|
||||
// user is prompted to sign in with the address they previously used.
|
||||
let email = subject ? subject.QueryInterface(Components.interfaces.nsISupportsString).data : null;
|
||||
let elt = document.getElementById("sync-migrate-upgrade-description");
|
||||
let elt = document.getElementById("syncStatusMessageDescription");
|
||||
elt.textContent = email ?
|
||||
sb.formatStringFromName("signInAfterUpgradeOnOtherDevice.description",
|
||||
[email], 1) :
|
||||
|
@ -449,11 +464,12 @@ let gSyncPane = {
|
|||
// The "Learn more" link.
|
||||
if (!email) {
|
||||
let learnMoreLink = document.createElement("label");
|
||||
learnMoreLink.id = "learnMoreLink";
|
||||
learnMoreLink.className = "text-link";
|
||||
let { text, href } = fxaMigrator.learnMoreLink;
|
||||
learnMoreLink.setAttribute("value", text);
|
||||
learnMoreLink.href = href;
|
||||
elt.appendChild(learnMoreLink);
|
||||
elt.parentNode.insertBefore(learnMoreLink, elt.nextSibling);
|
||||
}
|
||||
|
||||
// The "upgrade" button.
|
||||
|
@ -481,7 +497,7 @@ let gSyncPane = {
|
|||
let sb = this._accountsStringBundle;
|
||||
let email = subject.QueryInterface(Components.interfaces.nsISupportsString).data;
|
||||
let label = sb.formatStringFromName("needVerifiedUserLong", [email], 1);
|
||||
let elt = document.getElementById("sync-migrate-verify-label");
|
||||
let elt = document.getElementById("syncStatusMessageDescription");
|
||||
elt.setAttribute("value", label);
|
||||
// The "resend" button.
|
||||
let button = document.getElementById("sync-migrate-resend");
|
||||
|
@ -501,8 +517,8 @@ let gSyncPane = {
|
|||
document.getElementById("sync-migration").hidden = true;
|
||||
return;
|
||||
}
|
||||
document.getElementById("sync-migration").hidden = false;
|
||||
document.getElementById("sync-migration-deck").selectedIndex = selIndex;
|
||||
document.getElementById("sync-migration-buttons-deck").selectedIndex = selIndex;
|
||||
document.getElementById("syncStatusMessage").setAttribute("message-type", "migration");
|
||||
},
|
||||
|
||||
// Called whenever one of the sync engine preferences is changed.
|
||||
|
@ -678,23 +694,39 @@ let gSyncPane = {
|
|||
},
|
||||
|
||||
verifyFirefoxAccount: function() {
|
||||
fxAccounts.resendVerificationEmail().then(() => {
|
||||
fxAccounts.getSignedInUser().then(data => {
|
||||
let sb = this._accountsStringBundle;
|
||||
let title = sb.GetStringFromName("verificationSentTitle");
|
||||
let heading = sb.formatStringFromName("verificationSentHeading",
|
||||
[data.email], 1);
|
||||
let description = sb.GetStringFromName("verificationSentDescription");
|
||||
this._closeSyncStatusMessageBox();
|
||||
let changesyncStatusMessage = (data) => {
|
||||
let isError = !data;
|
||||
let syncStatusMessage = document.getElementById("syncStatusMessage");
|
||||
let syncStatusMessageTitle = document.getElementById("syncStatusMessageTitle");
|
||||
let syncStatusMessageDescription = document.getElementById("syncStatusMessageDescription");
|
||||
let maybeNot = isError ? "Not" : "";
|
||||
let sb = this._accountsStringBundle;
|
||||
let title = sb.GetStringFromName("verification" + maybeNot + "SentTitle");
|
||||
let email = !isError && data ? data.email : "";
|
||||
let description = sb.formatStringFromName("verification" + maybeNot + "SentFull", [email], 1)
|
||||
|
||||
let factory = Cc["@mozilla.org/prompter;1"]
|
||||
.getService(Ci.nsIPromptFactory);
|
||||
let prompt = factory.getPrompt(window, Ci.nsIPrompt);
|
||||
let bag = prompt.QueryInterface(Ci.nsIWritablePropertyBag2);
|
||||
bag.setPropertyAsBool("allowTabModal", true);
|
||||
syncStatusMessageTitle.textContent = title;
|
||||
syncStatusMessageDescription.textContent = description;
|
||||
let messageType = isError ? "verify-error" : "verify-success";
|
||||
syncStatusMessage.setAttribute("message-type", messageType);
|
||||
}
|
||||
|
||||
prompt.alert(title, heading + "\n\n" + description);
|
||||
});
|
||||
});
|
||||
let onError = () => {
|
||||
changesyncStatusMessage();
|
||||
};
|
||||
|
||||
let onSuccess = data => {
|
||||
if (data) {
|
||||
changesyncStatusMessage(data);
|
||||
} else {
|
||||
onError();
|
||||
}
|
||||
};
|
||||
|
||||
fxAccounts.resendVerificationEmail()
|
||||
.then(fxAccounts.getSignedInUser, onError)
|
||||
.then(onSuccess, onError);
|
||||
},
|
||||
|
||||
openOldSyncSupportPage: function() {
|
||||
|
|
|
@ -38,31 +38,29 @@
|
|||
<label class="header-name">&paneSync.title;</label>
|
||||
</hbox>
|
||||
|
||||
<hbox id="sync-migration-container"
|
||||
data-category="paneSync"
|
||||
hidden="true">
|
||||
|
||||
<vbox id="sync-migration" flex="1" hidden="true">
|
||||
|
||||
<deck id="sync-migration-deck">
|
||||
<!-- When we are in the "need FxA user" state -->
|
||||
<hbox align="center">
|
||||
<description id="sync-migrate-upgrade-description" flex="1"/>
|
||||
<spacer flex="1"/>
|
||||
<button id="sync-migrate-unlink"/>
|
||||
<button id="sync-migrate-upgrade"/>
|
||||
</hbox>
|
||||
|
||||
<!-- When we are in the "need the user to be verified" state -->
|
||||
<hbox align="center">
|
||||
<label id="sync-migrate-verify-label"/>
|
||||
<spacer flex="1"/>
|
||||
<button id="sync-migrate-forget"/>
|
||||
<button id="sync-migrate-resend"/>
|
||||
</hbox>
|
||||
</deck>
|
||||
</vbox>
|
||||
</hbox>
|
||||
<vbox id="syncStatusMessage-container" data-category="paneSync" hidden="true">
|
||||
<hbox id="syncStatusMessage">
|
||||
<vbox id="syncStatusMessageWrapper">
|
||||
<label id="syncStatusMessageTitle"></label>
|
||||
<description id="syncStatusMessageDescription"></description>
|
||||
<deck id="sync-migration-buttons-deck">
|
||||
<!-- When we are in the "need FxA user" state -->
|
||||
<hbox>
|
||||
<button id="sync-migrate-unlink"/>
|
||||
<button id="sync-migrate-upgrade"/>
|
||||
</hbox>
|
||||
<!-- When we are in the "need the user to be verified" state -->
|
||||
<hbox>
|
||||
<button id="sync-migrate-forget"/>
|
||||
<button id="sync-migrate-resend"/>
|
||||
</hbox>
|
||||
</deck>
|
||||
</vbox>
|
||||
<vbox>
|
||||
<button id="syncStatusMessageClose" class="close-icon"/>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
||||
<deck id="weavePrefsDeck" data-category="paneSync" hidden="true">
|
||||
<!-- These panels are for the "legacy" sync provider -->
|
||||
|
|
|
@ -19,10 +19,11 @@ function checkElements(expectedPane) {
|
|||
continue;
|
||||
}
|
||||
let attributeValue = element.getAttribute("data-category");
|
||||
let suffix = " (id=" + element.id + ")";
|
||||
if (attributeValue == "pane" + expectedPane) {
|
||||
is_element_visible(element, expectedPane + " elements should be visible");
|
||||
is_element_visible(element, expectedPane + " elements should be visible" + suffix);
|
||||
} else {
|
||||
is_element_hidden(element, "Elements not in " + expectedPane + " should be hidden");
|
||||
is_element_hidden(element, "Elements not in " + expectedPane + " should be hidden" + suffix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ loader.lazyRequireGetter(this, "AnimationsFront",
|
|||
|
||||
const STRINGS_URI = "chrome://browser/locale/devtools/animationinspector.properties";
|
||||
const L10N = new ViewHelpers.L10N(STRINGS_URI);
|
||||
const V3_UI_PREF = "devtools.inspector.animationInspectorV3";
|
||||
|
||||
// Global toolbox/inspector, set when startup is called.
|
||||
let gToolbox, gInspector;
|
||||
|
@ -76,19 +77,20 @@ function destroy() {
|
|||
* @return {Object} An object with boolean properties.
|
||||
*/
|
||||
let getServerTraits = Task.async(function*(target) {
|
||||
let config = [{
|
||||
name: "hasToggleAll", actor: "animations", method: "toggleAll"
|
||||
}, {
|
||||
name: "hasSetCurrentTime", actor: "animationplayer", method: "setCurrentTime"
|
||||
}, {
|
||||
name: "hasMutationEvents", actor: "animations", method: "stopAnimationPlayerUpdates"
|
||||
}, {
|
||||
name: "hasSetPlaybackRate", actor: "animationplayer", method: "setPlaybackRate"
|
||||
}, {
|
||||
name: "hasTargetNode", actor: "domwalker", method: "getNodeFromActor"
|
||||
}, {
|
||||
name: "hasSetCurrentTimes", actor: "animations", method: "setCurrentTimes"
|
||||
}];
|
||||
let config = [
|
||||
{ name: "hasToggleAll", actor: "animations",
|
||||
method: "toggleAll" },
|
||||
{ name: "hasSetCurrentTime", actor: "animationplayer",
|
||||
method: "setCurrentTime" },
|
||||
{ name: "hasMutationEvents", actor: "animations",
|
||||
method: "stopAnimationPlayerUpdates" },
|
||||
{ name: "hasSetPlaybackRate", actor: "animationplayer",
|
||||
method: "setPlaybackRate" },
|
||||
{ name: "hasTargetNode", actor: "domwalker",
|
||||
method: "getNodeFromActor" },
|
||||
{ name: "hasSetCurrentTimes", actor: "animations",
|
||||
method: "setCurrentTimes" }
|
||||
];
|
||||
|
||||
let traits = {};
|
||||
for (let {name, actor, method} of config) {
|
||||
|
@ -96,7 +98,7 @@ let getServerTraits = Task.async(function*(target) {
|
|||
}
|
||||
|
||||
// Special pref-based UI trait.
|
||||
traits.isNewUI = Services.prefs.getBoolPref("devtools.inspector.animationInspectorV3");
|
||||
traits.isNewUI = Services.prefs.getBoolPref(V3_UI_PREF);
|
||||
|
||||
return traits;
|
||||
});
|
||||
|
@ -114,7 +116,8 @@ let getServerTraits = Task.async(function*(target) {
|
|||
*
|
||||
* Usage example:
|
||||
*
|
||||
* AnimationsController.on(AnimationsController.PLAYERS_UPDATED_EVENT, onPlayers);
|
||||
* AnimationsController.on(AnimationsController.PLAYERS_UPDATED_EVENT,
|
||||
* onPlayers);
|
||||
* function onPlayers() {
|
||||
* for (let player of AnimationsController.animationPlayers) {
|
||||
* // do something with player
|
||||
|
@ -126,7 +129,8 @@ let AnimationsController = {
|
|||
|
||||
initialize: Task.async(function*() {
|
||||
if (this.initialized) {
|
||||
return this.initialized.promise;
|
||||
yield this.initialized.promise;
|
||||
return;
|
||||
}
|
||||
this.initialized = promise.defer();
|
||||
|
||||
|
@ -157,7 +161,8 @@ let AnimationsController = {
|
|||
}
|
||||
|
||||
if (this.destroyed) {
|
||||
return this.destroyed.promise;
|
||||
yield this.destroyed.promise;
|
||||
return;
|
||||
}
|
||||
this.destroyed = promise.defer();
|
||||
|
||||
|
@ -272,7 +277,8 @@ let AnimationsController = {
|
|||
refreshAnimationPlayers: Task.async(function*(nodeFront) {
|
||||
yield this.destroyAnimationPlayers();
|
||||
|
||||
this.animationPlayers = yield this.animationsFront.getAnimationPlayersForNode(nodeFront);
|
||||
this.animationPlayers = yield this.animationsFront
|
||||
.getAnimationPlayersForNode(nodeFront);
|
||||
this.startAllAutoRefresh();
|
||||
|
||||
// Start listening for animation mutations only after the first method call
|
||||
|
@ -308,6 +314,25 @@ let AnimationsController = {
|
|||
this.emit(this.PLAYERS_UPDATED_EVENT, this.animationPlayers);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get the latest known current time of document.timeline.
|
||||
* This value is sent along with all AnimationPlayerActors' states, but it
|
||||
* isn't updated after that, so this function loops over all know animations
|
||||
* to find the highest value.
|
||||
* @return {Number|Boolean} False is returned if this server version doesn't
|
||||
* provide document's current time.
|
||||
*/
|
||||
get documentCurrentTime() {
|
||||
let time = 0;
|
||||
for (let {state} of this.animationPlayers) {
|
||||
if (!state.documentCurrentTime) {
|
||||
return false;
|
||||
}
|
||||
time = Math.max(time, state.documentCurrentTime);
|
||||
}
|
||||
return time;
|
||||
},
|
||||
|
||||
startAllAutoRefresh: function() {
|
||||
if (this.traits.isNewUI) {
|
||||
return;
|
||||
|
|
|
@ -30,7 +30,8 @@ let AnimationsPanel = {
|
|||
return;
|
||||
}
|
||||
if (this.initialized) {
|
||||
return this.initialized.promise;
|
||||
yield this.initialized.promise;
|
||||
return;
|
||||
}
|
||||
this.initialized = promise.defer();
|
||||
|
||||
|
@ -74,7 +75,8 @@ let AnimationsPanel = {
|
|||
}
|
||||
|
||||
if (this.destroyed) {
|
||||
return this.destroyed.promise;
|
||||
yield this.destroyed.promise;
|
||||
return;
|
||||
}
|
||||
this.destroyed = promise.defer();
|
||||
|
||||
|
@ -157,7 +159,8 @@ let AnimationsPanel = {
|
|||
currentWidgetStateChange.push(btnClass.contains("paused")
|
||||
? widget.play() : widget.pause());
|
||||
}
|
||||
yield promise.all(currentWidgetStateChange).catch(e => console.error(e));
|
||||
yield promise.all(currentWidgetStateChange)
|
||||
.catch(error => console.error(error));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,7 +173,8 @@ let AnimationsPanel = {
|
|||
},
|
||||
|
||||
onTimelineTimeChanged: function(e, time) {
|
||||
AnimationsController.setCurrentTimeAll(time, true).catch(e => console.error(e));
|
||||
AnimationsController.setCurrentTimeAll(time, true)
|
||||
.catch(error => console.error(error));
|
||||
},
|
||||
|
||||
refreshAnimations: Task.async(function*() {
|
||||
|
@ -183,7 +187,8 @@ let AnimationsPanel = {
|
|||
// Re-render the timeline component.
|
||||
if (this.animationsTimelineComponent) {
|
||||
this.animationsTimelineComponent.render(
|
||||
AnimationsController.animationPlayers);
|
||||
AnimationsController.animationPlayers,
|
||||
AnimationsController.documentCurrentTime);
|
||||
}
|
||||
|
||||
// If there are no players to show, show the error message instead and
|
||||
|
|
|
@ -567,7 +567,12 @@ let TimeScale = {
|
|||
addAnimation: function(state) {
|
||||
let {startTime, delay, duration, iterationCount, playbackRate} = state;
|
||||
|
||||
this.minStartTime = Math.min(this.minStartTime, startTime);
|
||||
// Negative-delayed animations have their startTimes set such that we would
|
||||
// be displaying the delay outside the time window if we didn't take it into
|
||||
// account here.
|
||||
let relevantDelay = delay < 0 ? delay / playbackRate : 0;
|
||||
|
||||
this.minStartTime = Math.min(this.minStartTime, startTime + relevantDelay);
|
||||
let length = (delay / playbackRate) +
|
||||
((duration / playbackRate) *
|
||||
(!iterationCount ? 1 : iterationCount));
|
||||
|
@ -791,7 +796,7 @@ AnimationsTimeline.prototype = {
|
|||
this.emit("current-time-changed", time);
|
||||
},
|
||||
|
||||
render: function(animations) {
|
||||
render: function(animations, documentCurrentTime) {
|
||||
this.unrender();
|
||||
|
||||
this.animations = animations;
|
||||
|
@ -849,12 +854,11 @@ AnimationsTimeline.prototype = {
|
|||
// doesn't provide it, hide the scrubber entirely).
|
||||
// Note that because the currentTime was sent via the protocol, some time
|
||||
// may have gone by since then, and so the scrubber might be a bit late.
|
||||
let time = this.animations[0].state.documentCurrentTime;
|
||||
if (!time) {
|
||||
if (!documentCurrentTime) {
|
||||
this.scrubberEl.style.display = "none";
|
||||
} else {
|
||||
this.scrubberEl.style.display = "block";
|
||||
this.startAnimatingScrubber(time);
|
||||
this.startAnimatingScrubber(documentCurrentTime);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -953,23 +957,31 @@ AnimationsTimeline.prototype = {
|
|||
});
|
||||
|
||||
// The animation name is displayed over the iterations.
|
||||
// Note that in case of negative delay, we push the name towards the right
|
||||
// so the delay can be shown.
|
||||
createNode({
|
||||
parent: iterations,
|
||||
attributes: {
|
||||
"class": "name",
|
||||
"title": this.getAnimationTooltipText(state)
|
||||
"title": this.getAnimationTooltipText(state),
|
||||
"style": delay < 0
|
||||
? "margin-left:" +
|
||||
TimeScale.durationToDistance(Math.abs(delay), width) + "px"
|
||||
: ""
|
||||
},
|
||||
textContent: state.name
|
||||
});
|
||||
|
||||
// Delay.
|
||||
if (delay) {
|
||||
let w = TimeScale.durationToDistance(delay / rate, width);
|
||||
// Negative delays need to start at 0.
|
||||
let x = TimeScale.durationToDistance((delay < 0 ? 0 : delay) / rate, width);
|
||||
let w = TimeScale.durationToDistance(Math.abs(delay) / rate, width);
|
||||
createNode({
|
||||
parent: iterations,
|
||||
attributes: {
|
||||
"class": "delay",
|
||||
"style": `left:-${w}px;
|
||||
"style": `left:-${x}px;
|
||||
width:${w}px;`
|
||||
}
|
||||
});
|
||||
|
|
|
@ -22,18 +22,23 @@ add_task(function*() {
|
|||
let timeBlocks = timelineEl.querySelectorAll(".time-block");
|
||||
is(timeBlocks.length, 2, "2 animations are displayed");
|
||||
|
||||
info("The first animation has its rate to 1, let's measure it");
|
||||
info("The first animation has its rate set to 1, let's measure it");
|
||||
|
||||
let el = timeBlocks[0];
|
||||
let duration = el.querySelector(".iterations").getBoundingClientRect().width;
|
||||
let delay = el.querySelector(".delay").getBoundingClientRect().width;
|
||||
let duration = parseInt(el.querySelector(".iterations").style.width, 10);
|
||||
let delay = parseInt(el.querySelector(".delay").style.width, 10);
|
||||
|
||||
info("The second animation has its rate set to 2, so should be shorter");
|
||||
|
||||
let el2 = timeBlocks[1];
|
||||
let duration2 = el2.querySelector(".iterations").getBoundingClientRect().width;
|
||||
let delay2 = el2.querySelector(".delay").getBoundingClientRect().width;
|
||||
let duration2 = parseInt(el2.querySelector(".iterations").style.width, 10);
|
||||
let delay2 = parseInt(el2.querySelector(".delay").style.width, 10);
|
||||
|
||||
is(duration, 2 * duration2, "The duration width is correct");
|
||||
is(delay, 2 * delay2, "The delay width is correct");
|
||||
// The width are calculated by the animation-inspector dynamically depending
|
||||
// on the size of the panel, and therefore depends on the test machine/OS.
|
||||
// Let's not try to be too precise here and compare numbers.
|
||||
let durationDelta = (2 * duration2) - duration;
|
||||
ok(durationDelta <= 1, "The duration width is correct");
|
||||
let delayDelta = (2 * delay2) - delay;
|
||||
ok(delayDelta <= 1, "The delay width is correct");
|
||||
});
|
||||
|
|
|
@ -46,7 +46,10 @@ verificationSentTitle = Verification Sent
|
|||
# LOCALIZATION NOTE (verificationSentHeading) - %S = Email address of user's Firefox Account
|
||||
verificationSentHeading = A verification link has been sent to %S
|
||||
verificationSentDescription = Please check your email and click the link to begin syncing.
|
||||
# LOCALIZATION NOTE (verificationSentFull) - %S = Email address of user's Firefox Account
|
||||
verificationSentFull = A verification link has been sent to %S. Please check your email and click the link to begin syncing.
|
||||
|
||||
verificationNotSentTitle = Unable to Send Verification
|
||||
verificationNotSentHeading = We are unable to send a verification mail at this time
|
||||
verificationNotSentDescription = Please try again later.
|
||||
verificationNotSentFull = We are unable to send a verification mail at this time, please try again later.
|
||||
|
|
|
@ -1614,7 +1614,6 @@ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-ba
|
|||
}
|
||||
|
||||
#urlbar {
|
||||
-moz-padding-end: 4px;
|
||||
border-radius: @toolbarbuttonCornerRadius@;
|
||||
}
|
||||
|
||||
|
@ -1906,6 +1905,7 @@ richlistitem[type~="action"][actiontype="switchtab"][selected="true"] > .ac-url-
|
|||
var(--urlbar-separator-color) 15%,
|
||||
var(--urlbar-separator-color) 85%,
|
||||
transparent 85%);
|
||||
border-image-slice: 1;
|
||||
}
|
||||
|
||||
#urlbar-go-button {
|
||||
|
|
|
@ -238,7 +238,7 @@ body {
|
|||
border: 1px solid var(--timelime-border-color);
|
||||
|
||||
/* The background color is set independently */
|
||||
background: var(--timeline-background-color);
|
||||
background-color: var(--timeline-background-color);
|
||||
}
|
||||
|
||||
.animation-timeline .animation .cssanimation {
|
||||
|
@ -287,25 +287,16 @@ body {
|
|||
.animation-timeline .animation .delay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
/* Make sure the delay covers up the animation border */
|
||||
transform: translate(-1px, -1px);
|
||||
height: 100%;
|
||||
background-image: linear-gradient(to bottom,
|
||||
transparent,
|
||||
transparent 9px,
|
||||
var(--timelime-border-color) 9px,
|
||||
var(--timelime-border-color) 11px,
|
||||
transparent 11px,
|
||||
transparent);
|
||||
}
|
||||
|
||||
.animation-timeline .animation .delay::before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
left: 0;
|
||||
width: 2px;
|
||||
height: 8px;
|
||||
top: 50%;
|
||||
margin-top: -4px;
|
||||
background: var(--timelime-border-color);
|
||||
background-image: repeating-linear-gradient(45deg,
|
||||
transparent,
|
||||
transparent 1px,
|
||||
var(--theme-selection-color) 1px,
|
||||
var(--theme-selection-color) 4px);
|
||||
background-color: var(--timelime-border-color);
|
||||
border: 1px solid var(--timelime-border-color);
|
||||
}
|
||||
|
||||
/* Animation target node gutter, contains a preview of the dom node */
|
||||
|
|
|
@ -415,6 +415,101 @@ description > html|a {
|
|||
-moz-box-align: start;
|
||||
}
|
||||
|
||||
#syncStatusMessage {
|
||||
visibility: collapse;
|
||||
opacity: 0;
|
||||
transition: opacity 1s linear;
|
||||
padding: 14px 8px 14px 14px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
#syncStatusMessage[message-type] {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#syncStatusMessage[message-type="verify-success"] {
|
||||
background-color: #74BF43;
|
||||
}
|
||||
|
||||
#syncStatusMessage[message-type="verify-error"] {
|
||||
background-color: #D74345;
|
||||
}
|
||||
|
||||
#syncStatusMessage[message-type="migration"] {
|
||||
background-color: #FF9500;
|
||||
}
|
||||
|
||||
#sync-migration-buttons-deck {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
#learnMoreLink {
|
||||
margin: 0;
|
||||
color: #FBFBFB;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#syncStatusMessage[message-type="migration"] #sync-migration-buttons-deck {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
#sync-migration-buttons-deck {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
#sync-migration-buttons-deck button {
|
||||
margin: 0 10px 0 0;
|
||||
border: 0;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
#sync-migrate-upgrade,
|
||||
#sync-migrate-resend {
|
||||
background-color: #0095DD;
|
||||
color: #FBFBFB;
|
||||
}
|
||||
|
||||
#sync-migrate-upgrade:hover,
|
||||
#sync-migrate-resend:hover {
|
||||
background-color: #008ACB;
|
||||
}
|
||||
|
||||
#sync-migrate-upgrade:hover:active,
|
||||
#sync-migrate-resend:hover:active {
|
||||
background-color: #006B9D;
|
||||
}
|
||||
|
||||
#syncStatusMessageWrapper {
|
||||
-moz-box-flex: 1;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
#syncStatusMessageTitle, #syncStatusMessageDescription {
|
||||
color: #FBFBFB;
|
||||
}
|
||||
|
||||
#syncStatusMessage[message-type="migration"] #syncStatusMessageTitle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#syncStatusMessageTitle {
|
||||
font-weight: bold !important;
|
||||
font-size: 16px;
|
||||
line-height: 157%;
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
|
||||
#syncStatusMessageDescription {
|
||||
font-size: 14px;
|
||||
line-height: 158%;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
#syncStatusMessageClose {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
#fxaSyncEngines > vbox:first-child {
|
||||
margin-right: 80px;
|
||||
}
|
||||
|
|
|
@ -1230,10 +1230,6 @@ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-ba
|
|||
border: 1px solid ThreeDShadow;
|
||||
}
|
||||
|
||||
#urlbar {
|
||||
-moz-padding-end: 2px;
|
||||
}
|
||||
|
||||
/* overlap the urlbar's border */
|
||||
#PopupAutoCompleteRichResult {
|
||||
margin-top: -1px;
|
||||
|
@ -1267,7 +1263,6 @@ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-ba
|
|||
.searchbar-textbox:not(:-moz-lwtheme) {
|
||||
border-color: hsl(0,0%,90%);
|
||||
padding: 1px;
|
||||
-moz-padding-end: 3px;
|
||||
transition-property: border-color, box-shadow;
|
||||
transition-duration: .1s;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<resources>
|
||||
<dimen name="tab_panel_column_width">156dip</dimen>
|
||||
<dimen name="tab_thumbnail_height">110dip</dimen>
|
||||
<dimen name="tab_thumbnail_width">148dip</dimen>
|
||||
</resources>
|
|
@ -4,7 +4,7 @@
|
|||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<resources>
|
||||
<dimen name="tab_panel_column_width">174dip</dimen>
|
||||
<dimen name="tab_panel_column_width">176dip</dimen>
|
||||
<dimen name="tab_thumbnail_height">120dip</dimen>
|
||||
<dimen name="tab_thumbnail_width">168dip</dimen>
|
||||
</resources>
|
||||
|
|
|
@ -104,12 +104,6 @@ this.BrowserIDManager.prototype = {
|
|||
// we don't consider the lack of a keybundle as a failure state.
|
||||
_shouldHaveSyncKeyBundle: false,
|
||||
|
||||
get readyToAuthenticate() {
|
||||
// We are finished initializing when we *should* have a sync key bundle,
|
||||
// although we might not actually have one due to auth failures etc.
|
||||
return this._shouldHaveSyncKeyBundle;
|
||||
},
|
||||
|
||||
get needsCustomization() {
|
||||
try {
|
||||
return Services.prefs.getBoolPref(PREF_SYNC_SHOW_CUSTOMIZATION);
|
||||
|
@ -122,7 +116,19 @@ this.BrowserIDManager.prototype = {
|
|||
for (let topic of OBSERVER_TOPICS) {
|
||||
Services.obs.addObserver(this, topic, false);
|
||||
}
|
||||
return this.initializeWithCurrentIdentity();
|
||||
// and a background fetch of account data just so we can set this.account,
|
||||
// so we have a username available before we've actually done a login.
|
||||
// XXX - this is actually a hack just for tests and really shouldn't be
|
||||
// necessary. Also, you'd think it would be safe to allow this.account to
|
||||
// be set to null when there's no user logged in, but argue with the test
|
||||
// suite, not with me :)
|
||||
this._fxaService.getSignedInUser().then(accountData => {
|
||||
if (accountData) {
|
||||
this.account = accountData.email;
|
||||
}
|
||||
}).catch(err => {
|
||||
// As above, this is only for tests so it is safe to ignore.
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -130,7 +136,7 @@ this.BrowserIDManager.prototype = {
|
|||
* the user is logged in, or is rejected if the login attempt has failed.
|
||||
*/
|
||||
ensureLoggedIn: function() {
|
||||
if (!this._shouldHaveSyncKeyBundle) {
|
||||
if (!this._shouldHaveSyncKeyBundle && this.whenReadyToAuthenticate) {
|
||||
// We are already in the process of logging in.
|
||||
return this.whenReadyToAuthenticate.promise;
|
||||
}
|
||||
|
@ -160,7 +166,6 @@ this.BrowserIDManager.prototype = {
|
|||
}
|
||||
this.resetCredentials();
|
||||
this._signedInUser = null;
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
offerSyncOptions: function () {
|
||||
|
@ -294,7 +299,8 @@ this.BrowserIDManager.prototype = {
|
|||
// reauth with the server - in that case we will also get here, but
|
||||
// should have the same identity.
|
||||
// initializeWithCurrentIdentity will throw and log if these constraints
|
||||
// aren't met, so just go ahead and do the init.
|
||||
// aren't met (indirectly, via _updateSignedInUser()), so just go ahead
|
||||
// and do the init.
|
||||
this.initializeWithCurrentIdentity(true);
|
||||
break;
|
||||
|
||||
|
@ -636,7 +642,6 @@ this.BrowserIDManager.prototype = {
|
|||
// that there is no authentication dance still under way.
|
||||
this._shouldHaveSyncKeyBundle = true;
|
||||
Weave.Status.login = this._authFailureReason;
|
||||
Services.obs.notifyObservers(null, "weave:ui:login:error", null);
|
||||
throw err;
|
||||
});
|
||||
},
|
||||
|
|
|
@ -122,7 +122,6 @@ LOGIN_FAILED_NETWORK_ERROR: "error.login.reason.network",
|
|||
LOGIN_FAILED_SERVER_ERROR: "error.login.reason.server",
|
||||
LOGIN_FAILED_INVALID_PASSPHRASE: "error.login.reason.recoverykey",
|
||||
LOGIN_FAILED_LOGIN_REJECTED: "error.login.reason.account",
|
||||
LOGIN_FAILED_NOT_READY: "error.login.reason.initializing",
|
||||
|
||||
// sync failure status codes
|
||||
METARECORD_DOWNLOAD_FAIL: "error.sync.reason.metarecord_download_fail",
|
||||
|
|
|
@ -85,18 +85,14 @@ IdentityManager.prototype = {
|
|||
_syncKeyBundle: null,
|
||||
|
||||
/**
|
||||
* Initialize the identity provider. Returns a promise that is resolved
|
||||
* when initialization is complete and the provider can be queried for
|
||||
* its state
|
||||
* Initialize the identity provider.
|
||||
*/
|
||||
initialize: function() {
|
||||
// Nothing to do for this identity provider.
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
finalize: function() {
|
||||
// Nothing to do for this identity provider.
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -115,14 +111,6 @@ IdentityManager.prototype = {
|
|||
return Promise.resolve();
|
||||
},
|
||||
|
||||
/**
|
||||
* Indicates if the identity manager is still initializing
|
||||
*/
|
||||
get readyToAuthenticate() {
|
||||
// We initialize in a fully sync manner, so we are always finished.
|
||||
return true;
|
||||
},
|
||||
|
||||
get account() {
|
||||
return Svc.Prefs.get("account", this.username);
|
||||
},
|
||||
|
|
|
@ -690,13 +690,6 @@ Sync11Service.prototype = {
|
|||
},
|
||||
|
||||
verifyLogin: function verifyLogin(allow40XRecovery = true) {
|
||||
// If the identity isn't ready it might not know the username...
|
||||
if (!this.identity.readyToAuthenticate) {
|
||||
this._log.info("Not ready to authenticate in verifyLogin.");
|
||||
this.status.login = LOGIN_FAILED_NOT_READY;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.identity.username) {
|
||||
this._log.warn("No username in verifyLogin.");
|
||||
this.status.login = LOGIN_FAILED_NO_USERNAME;
|
||||
|
@ -943,25 +936,22 @@ Sync11Service.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
this.identity.finalize().then(
|
||||
() => {
|
||||
// an observer so the FxA migration code can take some action before
|
||||
// the new identity is created.
|
||||
Svc.Obs.notify("weave:service:start-over:init-identity");
|
||||
this.identity.username = "";
|
||||
this.status.__authManager = null;
|
||||
this.identity = Status._authManager;
|
||||
this._clusterManager = this.identity.createClusterManager(this);
|
||||
Svc.Obs.notify("weave:service:start-over:finish");
|
||||
}
|
||||
).then(null,
|
||||
err => {
|
||||
this._log.error("startOver failed to re-initialize the identity manager: " + err);
|
||||
// Still send the observer notification so the current state is
|
||||
// reflected in the UI.
|
||||
Svc.Obs.notify("weave:service:start-over:finish");
|
||||
}
|
||||
);
|
||||
try {
|
||||
this.identity.finalize();
|
||||
// an observer so the FxA migration code can take some action before
|
||||
// the new identity is created.
|
||||
Svc.Obs.notify("weave:service:start-over:init-identity");
|
||||
this.identity.username = "";
|
||||
this.status.__authManager = null;
|
||||
this.identity = Status._authManager;
|
||||
this._clusterManager = this.identity.createClusterManager(this);
|
||||
Svc.Obs.notify("weave:service:start-over:finish");
|
||||
} catch (err) {
|
||||
this._log.error("startOver failed to re-initialize the identity manager: " + err);
|
||||
// Still send the observer notification so the current state is
|
||||
// reflected in the UI.
|
||||
Svc.Obs.notify("weave:service:start-over:finish");
|
||||
}
|
||||
},
|
||||
|
||||
persistLogin: function persistLogin() {
|
||||
|
|
|
@ -30,10 +30,7 @@ this.Status = {
|
|||
.wrappedJSObject;
|
||||
let idClass = service.fxAccountsEnabled ? BrowserIDManager : IdentityManager;
|
||||
this.__authManager = new idClass();
|
||||
// .initialize returns a promise, so we need to spin until it resolves.
|
||||
let cb = Async.makeSpinningCallback();
|
||||
this.__authManager.initialize().then(cb, cb);
|
||||
cb.wait();
|
||||
this.__authManager.initialize();
|
||||
return this.__authManager;
|
||||
},
|
||||
|
||||
|
|
|
@ -652,8 +652,8 @@ Livemark.prototype = {
|
|||
let nodes = this._nodes.get(container);
|
||||
for (let node of nodes) {
|
||||
// Workaround for bug 449811.
|
||||
localObserver = observer;
|
||||
localNode = node;
|
||||
let localObserver = observer;
|
||||
let localNode = node;
|
||||
if (!aURI || node.uri == aURI.spec) {
|
||||
Services.tm.mainThread.dispatch(() => {
|
||||
localObserver.nodeHistoryDetailsChanged(localNode, 0, aVisitedStatus);
|
||||
|
|
|
@ -75,12 +75,13 @@ const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12
|
|||
* This is thrown by |TelemetryStorage.loadPingFile| when reading the ping
|
||||
* from the disk fails.
|
||||
*/
|
||||
function PingReadError(message="Error reading the ping file") {
|
||||
function PingReadError(message="Error reading the ping file", becauseNoSuchFile = false) {
|
||||
Error.call(this, message);
|
||||
let error = new Error();
|
||||
this.name = "PingReadError";
|
||||
this.message = message;
|
||||
this.stack = error.stack;
|
||||
this.becauseNoSuchFile = becauseNoSuchFile;
|
||||
}
|
||||
PingReadError.prototype = Object.create(Error.prototype);
|
||||
PingReadError.prototype.constructor = PingReadError;
|
||||
|
@ -1445,7 +1446,7 @@ let TelemetryStorageImpl = {
|
|||
array = yield OS.File.read(aFilePath, options);
|
||||
} catch(e) {
|
||||
this._log.trace("loadPingfile - unreadable ping " + aFilePath, e);
|
||||
throw new PingReadError(e.message);
|
||||
throw new PingReadError(e.message, e.becauseNoSuchFile);
|
||||
}
|
||||
|
||||
let decoder = new TextDecoder();
|
||||
|
|
|
@ -324,7 +324,14 @@ let AnimationPlayerActor = ActorClass({
|
|||
*/
|
||||
onAnimationMutation: function(mutations) {
|
||||
let hasChanged = false;
|
||||
for (let {changedAnimations} of mutations) {
|
||||
for (let {removedAnimations, changedAnimations} of mutations) {
|
||||
if (removedAnimations.length) {
|
||||
// Reset the local copy of the state on removal, since the animation can
|
||||
// be kept on the client and re-added, its state needs to be sent in
|
||||
// full.
|
||||
this.currentState = null;
|
||||
}
|
||||
|
||||
if (!changedAnimations.length) {
|
||||
return;
|
||||
}
|
||||
|
@ -689,11 +696,14 @@ let AnimationsActor = exports.AnimationsActor = ActorClass({
|
|||
// already have, it means it's a transition that's re-starting. So send
|
||||
// a "removed" event for the one we already have.
|
||||
let index = this.actors.findIndex(a => {
|
||||
return a.player.constructor === player.constructor &&
|
||||
((a.isAnimation() &&
|
||||
a.player.animationName === player.animationName) ||
|
||||
(a.isTransition() &&
|
||||
a.player.transitionProperty === player.transitionProperty));
|
||||
let isSameType = a.player.constructor === player.constructor;
|
||||
let isSameName = (a.isAnimation() &&
|
||||
a.player.animationName === player.animationName) ||
|
||||
(a.isTransition() &&
|
||||
a.player.transitionProperty === player.transitionProperty);
|
||||
let isSameNode = a.player.effect.target === player.effect.target;
|
||||
|
||||
return isSameType && isSameNode && isSameName;
|
||||
});
|
||||
if (index !== -1) {
|
||||
eventData.push({
|
||||
|
|
Загрузка…
Ссылка в новой задаче