Merge mozilla-central to mozilla-inbound

--HG--
extra : rebase_source : ce63e63e1b27245eddc3f65e784abd96580c47f7
This commit is contained in:
Carsten "Tomcat" Book 2015-09-08 15:55:01 +02:00
Родитель 9157524ebc 06682809ee
Коммит 61766bf62f
62 изменённых файлов: 1809 добавлений и 227 удалений

Просмотреть файл

@ -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";
}

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e935894ef5f27e2f04b9e929a45a958e6288a223">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="891798f1e345bc2b69e71de42bd524a90b1745c4"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="b81185d30e548f782770b852473ffb53c641a490"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e935894ef5f27e2f04b9e929a45a958e6288a223">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="891798f1e345bc2b69e71de42bd524a90b1745c4"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="b81185d30e548f782770b852473ffb53c641a490"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

Просмотреть файл

@ -19,11 +19,11 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="891798f1e345bc2b69e71de42bd524a90b1745c4"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b81185d30e548f782770b852473ffb53c641a490"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="2d58f4b9206b50b8fda0d5036da6f0c62608db7c"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="d70e4bfdcb65e7514de0f9315b74aea1c811678d"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="27eb2f04e149fc2c9976d881b1b5984bbe7ee089"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="067c08fb3e5744b42b68d1f861245f7d507109bc"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
<!-- Stock Android things -->
@ -98,7 +98,7 @@
<project name="platform/system/vold" path="system/vold" revision="919829940468066a32f403980b43f6ebfee5d314"/>
<!-- Emulator specific things -->
<project name="android-development" path="development" remote="b2g" revision="2bdf22305b523af644e1891b4ddfd9229336d0ce"/>
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="0d5c43228006bae775c4cb57a6d3908484d41718"/>
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="fc5f390fa314385e2a84629ea88284a60b40f7c4"/>
<project name="platform/external/iproute2" path="external/iproute2" revision="c66c5716d5335e450f7a7b71ccc6a604fb2f41d2"/>
<project name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="d2685281e2e54ca14d1df304867aa82c37b27162"/>
<project name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="627f9b20fc518937b93747a7ff1ed4f5ed46e06f"/>

Просмотреть файл

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="891798f1e345bc2b69e71de42bd524a90b1745c4"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="b81185d30e548f782770b852473ffb53c641a490"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="c6cace53426b5be7e56c0fd202118009689bc707"/>

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e935894ef5f27e2f04b9e929a45a958e6288a223">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="891798f1e345bc2b69e71de42bd524a90b1745c4"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="b81185d30e548f782770b852473ffb53c641a490"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

Просмотреть файл

@ -12,10 +12,10 @@
<!--original fetch url was https://git.mozilla.org/releases-->
<remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
<!-- B2G specific things. -->
<project name="platform_build" path="build" remote="b2g" revision="05a36844c1046a1eb07d5b1325f85ed741f961ea">
<project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="891798f1e345bc2b69e71de42bd524a90b1745c4"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="b81185d30e548f782770b852473ffb53c641a490"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
@ -141,7 +141,7 @@
<default remote="caf" revision="refs/tags/android-5.1.0_r1" sync-j="4"/>
<!-- Emulator specific things -->
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="fe7df1bc8dd0fd71571505d7be1c31a4ad1e40fb"/>
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="48132ec0b0dfe9fc29c7c3f0e799066be8999198"/>
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="dc8c7896562bf63190befb3e6b21310a4b7144fa"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="59e434cbecc02653f44cedeb2ef5cc88dc8bb61b"/>
<project name="platform_external_wpa_supplicant_8" path="external/wpa_supplicant_8" remote="b2g" revision="cbda29a58abc4ea1f7f4611fe354ab67b606219d"/>
<project name="platform/development" path="development" revision="0c51f6e0aa2ee57fcb75ec3b2ff6bf754cece63e"/>

Просмотреть файл

@ -19,11 +19,11 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="891798f1e345bc2b69e71de42bd524a90b1745c4"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b81185d30e548f782770b852473ffb53c641a490"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="2d58f4b9206b50b8fda0d5036da6f0c62608db7c"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="d70e4bfdcb65e7514de0f9315b74aea1c811678d"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="27eb2f04e149fc2c9976d881b1b5984bbe7ee089"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="067c08fb3e5744b42b68d1f861245f7d507109bc"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
<!-- Stock Android things -->
@ -98,7 +98,7 @@
<project name="platform/system/vold" path="system/vold" revision="919829940468066a32f403980b43f6ebfee5d314"/>
<!-- Emulator specific things -->
<project name="android-development" path="development" remote="b2g" revision="2bdf22305b523af644e1891b4ddfd9229336d0ce"/>
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="0d5c43228006bae775c4cb57a6d3908484d41718"/>
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="fc5f390fa314385e2a84629ea88284a60b40f7c4"/>
<project name="platform/external/iproute2" path="external/iproute2" revision="c66c5716d5335e450f7a7b71ccc6a604fb2f41d2"/>
<project name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="d2685281e2e54ca14d1df304867aa82c37b27162"/>
<project name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="627f9b20fc518937b93747a7ff1ed4f5ed46e06f"/>

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e935894ef5f27e2f04b9e929a45a958e6288a223">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="891798f1e345bc2b69e71de42bd524a90b1745c4"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="b81185d30e548f782770b852473ffb53c641a490"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

Просмотреть файл

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "891798f1e345bc2b69e71de42bd524a90b1745c4",
"git_revision": "b81185d30e548f782770b852473ffb53c641a490",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "8c6b450b2f9f4606f164c33513a023e59271e1e4",
"revision": "d0a1f4d22e1a40cb89f9d592334e7506ee1317bd",
"repo_path": "integration/gaia-central"
}

Просмотреть файл

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="891798f1e345bc2b69e71de42bd524a90b1745c4"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="b81185d30e548f782770b852473ffb53c641a490"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="c6cace53426b5be7e56c0fd202118009689bc707"/>

Просмотреть файл

@ -12,10 +12,10 @@
<!--original fetch url was https://git.mozilla.org/releases-->
<remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
<!-- B2G specific things. -->
<project name="platform_build" path="build" remote="b2g" revision="05a36844c1046a1eb07d5b1325f85ed741f961ea">
<project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="891798f1e345bc2b69e71de42bd524a90b1745c4"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="b81185d30e548f782770b852473ffb53c641a490"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

Просмотреть файл

@ -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,94 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "base/basictypes.h"
#include "BluetoothMapFolder.h"
BEGIN_BLUETOOTH_NAMESPACE
BluetoothMapFolder::~BluetoothMapFolder()
{ }
BluetoothMapFolder::BluetoothMapFolder(const nsAString& aFolderName,
BluetoothMapFolder* aParent)
: mName(aFolderName)
, mParent(aParent)
{
}
BluetoothMapFolder*
BluetoothMapFolder::AddSubFolder(const nsAString& aFolderName)
{
nsRefPtr<BluetoothMapFolder> folder = new BluetoothMapFolder(aFolderName,
this);
mSubFolders.Put(nsString(aFolderName), folder);
return folder;
}
BluetoothMapFolder*
BluetoothMapFolder::GetSubFolder(const nsAString& aFolderName)
{
BluetoothMapFolder* subfolder;
mSubFolders.Get(aFolderName, &subfolder);
return subfolder;
}
BluetoothMapFolder*
BluetoothMapFolder::GetParentFolder()
{
return mParent;
}
int
BluetoothMapFolder::GetSubFolderCount()
{
return mSubFolders.Count();
}
void
BluetoothMapFolder::GetFolderListingObjectString(nsAString& aString,
uint16_t aMaxListCount,
uint16_t aStartOffset)
{
const char* folderListingPrefix =
"<?xml version=\"1.0\"?>\n"
"<!DOCTYPE folder-listing SYSTEM \" obex-folder-listing.dtd\">\n"
"<folder-listing version=\"1.0\">\n";
const char* folderListingSuffix = "</folder-listing>";
// Based on Element Specification, 9.1.1, IrObex 1.2
nsAutoCString folderListingObejct(folderListingPrefix);
int count = 0;
for (auto iter = mSubFolders.Iter(); !iter.Done(); iter.Next()) {
if (count < aStartOffset) {
continue;
}
if (count > aMaxListCount) {
break;
}
const nsAString& key = iter.Key();
folderListingObejct.Append("<folder name=\"");
folderListingObejct.Append(NS_ConvertUTF16toUTF8(key).get());
folderListingObejct.Append("\">");
count++;
}
folderListingObejct.Append(folderListingSuffix);
aString = NS_ConvertUTF8toUTF16(folderListingObejct);
}
void
BluetoothMapFolder::DumpFolderInfo()
{
BT_LOGR("Folder name: %s, subfolder counts: %d",
NS_ConvertUTF16toUTF8(mName).get(), mSubFolders.Count());
}
END_BLUETOOTH_NAMESPACE

Просмотреть файл

@ -0,0 +1,41 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef mozilla_dom_bluetooth_bluedroid_BluetoothMapFolder_h
#define mozilla_dom_bluetooth_bluedroid_BluetoothMapFolder_h
#include "BluetoothCommon.h"
#include "nsAutoPtr.h"
#include "nsRefPtrHashtable.h"
#include "nsTArray.h"
BEGIN_BLUETOOTH_NAMESPACE
/* This class maps MAP virtual folder structures */
class BluetoothMapFolder
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BluetoothMapFolder)
BluetoothMapFolder(const nsAString& aFolderName, BluetoothMapFolder* aParent);
// Add virtual folder to subfolders
BluetoothMapFolder* AddSubFolder(const nsAString& aFolderName);
BluetoothMapFolder* GetSubFolder(const nsAString& aFolderName);
BluetoothMapFolder* GetParentFolder();
int GetSubFolderCount();
// Format folder listing object string
void GetFolderListingObjectString(nsAString& aString, uint16_t aMaxListCount,
uint16_t aStartOffset);
void DumpFolderInfo();
private:
~BluetoothMapFolder();
nsString mName;
nsRefPtr<BluetoothMapFolder> mParent;
nsRefPtrHashtable<nsStringHashKey, BluetoothMapFolder> mSubFolders;
};
END_BLUETOOTH_NAMESPACE
#endif //mozilla_dom_bluetooth_bluedroid_BluetoothMapFolder_h

Просмотреть файл

@ -0,0 +1,915 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "base/basictypes.h"
#include "BluetoothMapSmsManager.h"
#include "BluetoothService.h"
#include "BluetoothSocket.h"
#include "BluetoothUuid.h"
#include "ObexBase.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "nsAutoPtr.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
USING_BLUETOOTH_NAMESPACE
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::ipc;
namespace {
// UUID of Map Mas
static const BluetoothUuid kMapMas = {
{
0x00, 0x00, 0x11, 0x32, 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
}
};
// UUID of Map Mns
static const BluetoothUuid kMapMns = {
{
0x00, 0x00, 0x11, 0x33, 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
}
};
// UUID used in Map OBEX MAS target header
static const BluetoothUuid kMapMasObexTarget = {
{
0xBB, 0x58, 0x2B, 0x40, 0x42, 0x0C, 0x11, 0xDB,
0xB0, 0xDE, 0x08, 0x00, 0x20, 0x0C, 0x9A, 0x66
}
};
// UUID used in Map OBEX MNS target header
static const BluetoothUuid kMapMnsObexTarget = {
{
0xBB, 0x58, 0x2B, 0x41, 0x42, 0x0C, 0x11, 0xDB,
0xB0, 0xDE, 0x08, 0x00, 0x20, 0x0C, 0x9A, 0x66
}
};
StaticRefPtr<BluetoothMapSmsManager> sMapSmsManager;
static bool sInShutdown = false;
}
BEGIN_BLUETOOTH_NAMESPACE
NS_IMETHODIMP
BluetoothMapSmsManager::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData)
{
MOZ_ASSERT(sMapSmsManager);
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
HandleShutdown();
return NS_OK;
}
MOZ_ASSERT(false, "MapSmsManager got unexpected topic!");
return NS_ERROR_UNEXPECTED;
}
void
BluetoothMapSmsManager::HandleShutdown()
{
MOZ_ASSERT(NS_IsMainThread());
sInShutdown = true;
Disconnect(nullptr);
sMapSmsManager = nullptr;
}
BluetoothMapSmsManager::BluetoothMapSmsManager() : mMasConnected(false),
mMnsConnected(false),
mNtfRequired(false)
{
mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
BuildDefaultFolderStructure();
}
BluetoothMapSmsManager::~BluetoothMapSmsManager()
{
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (NS_WARN_IF(!obs)) {
return;
}
NS_WARN_IF(NS_FAILED(
obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID)));
}
bool
BluetoothMapSmsManager::Init()
{
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (NS_WARN_IF(!obs)) {
return false;
}
if (NS_WARN_IF(NS_FAILED(
obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false)))) {
return false;
}
/**
* We don't start listening here as BluetoothServiceBluedroid calls Listen()
* immediately when BT stops.
*
* If we start listening here, the listening fails when device boots up since
* Listen() is called again and restarts server socket. The restart causes
* absence of read events when device boots up.
*/
return true;
}
//static
BluetoothMapSmsManager*
BluetoothMapSmsManager::Get()
{
MOZ_ASSERT(NS_IsMainThread());
// Exit early if sMapSmsManager already exists
if (sMapSmsManager) {
return sMapSmsManager;
}
// Do not create a new instance if we're in shutdown
if (NS_WARN_IF(sInShutdown)) {
return nullptr;
}
// Create a new instance, register, and return
BluetoothMapSmsManager *manager = new BluetoothMapSmsManager();
if (NS_WARN_IF(!manager->Init())) {
return nullptr;
}
sMapSmsManager = manager;
return sMapSmsManager;
}
bool
BluetoothMapSmsManager::Listen()
{
MOZ_ASSERT(NS_IsMainThread());
// Fail to listen if |mMasSocket| already exists
if (NS_WARN_IF(mMasSocket)) {
return false;
}
/**
* Restart server socket since its underlying fd becomes invalid when
* BT stops; otherwise no more read events would be received even if
* BT restarts.
*/
if (mMasServerSocket) {
mMasServerSocket->Close();
mMasServerSocket = nullptr;
}
mMasServerSocket = new BluetoothSocket(this);
nsString sdpString;
#if ANDROID_VERSION >= 21
/**
* The way bluedroid handles MAP SDP record is very hacky.
* In Lollipop version, SDP string format would be instanceId + msg type
* + msg name. See add_maps_sdp in btif/src/btif_sock_sdp.c
*/
// MAS instance id
sdpString.AppendPrintf("%02x", SDP_SMS_MMS_INSTANCE_ID);
// Supported message type
sdpString.AppendPrintf("%02x", SDP_MESSAGE_TYPE_SMS_GSM |
SDP_MESSAGE_TYPE_SMS_CDMA |
SDP_MESSAGE_TYPE_MMS);
#endif
/**
* SDP service name, we don't assign RFCOMM channel directly
* bluedroid automatically assign channel number randomly.
*/
sdpString.AppendLiteral("SMS/MMS Message Access");
nsresult rv = mMasServerSocket->Listen(sdpString, kMapMas,
BluetoothSocketType::RFCOMM, -1, false,
true);
if (NS_FAILED(rv)) {
mMasServerSocket = nullptr;
return false;
}
return true;
}
void
BluetoothMapSmsManager::MnsDataHandler(UnixSocketBuffer* aMessage)
{
// Ensure valid access to data[0], i.e., opCode
int receivedLength = aMessage->GetSize();
if (receivedLength < 1) {
BT_LOGR("Receive empty response packet");
return;
}
const uint8_t* data = aMessage->GetData();
uint8_t opCode = data[0];
if (opCode != ObexResponseCode::Success) {
BT_LOGR("Unexpected OpCode: %x", opCode);
if (mLastCommand == ObexRequestCode::Put ||
mLastCommand == ObexRequestCode::Abort ||
mLastCommand == ObexRequestCode::PutFinal) {
SendMnsDisconnectRequest();
}
}
}
void
BluetoothMapSmsManager::MasDataHandler(UnixSocketBuffer* aMessage)
{
/**
* Ensure
* - valid access to data[0], i.e., opCode
* - received packet length smaller than max packet length
*/
int receivedLength = aMessage->GetSize();
if (receivedLength < 1 || receivedLength > MAX_PACKET_LENGTH) {
ReplyError(ObexResponseCode::BadRequest);
return;
}
const uint8_t* data = aMessage->GetData();
uint8_t opCode = data[0];
ObexHeaderSet pktHeaders;
nsString type;
switch (opCode) {
case ObexRequestCode::Connect:
// Section 3.3.1 "Connect", IrOBEX 1.2
// [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
// [Headers:var]
if (receivedLength < 7 ||
!ParseHeaders(&data[7], receivedLength - 7, &pktHeaders)) {
ReplyError(ObexResponseCode::BadRequest);
return;
}
// "Establishing an OBEX Session"
// The OBEX header target shall equal to MAS obex target UUID.
if (!CompareHeaderTarget(pktHeaders)) {
ReplyError(ObexResponseCode::BadRequest);
return;
}
mRemoteMaxPacketLength = ((static_cast<int>(data[5]) << 8) | data[6]);
if (mRemoteMaxPacketLength < 255) {
BT_LOGR("Remote maximum packet length %d", mRemoteMaxPacketLength);
mRemoteMaxPacketLength = 0;
ReplyError(ObexResponseCode::BadRequest);
return;
}
ReplyToConnect();
AfterMapSmsConnected();
break;
case ObexRequestCode::Disconnect:
case ObexRequestCode::Abort:
// Section 3.3.2 "Disconnect" and Section 3.3.5 "Abort", IrOBEX 1.2
// The format of request packet of "Disconnect" and "Abort" are the same
// [opcode:1][length:2][Headers:var]
if (receivedLength < 3 ||
!ParseHeaders(&data[3], receivedLength - 3, &pktHeaders)) {
ReplyError(ObexResponseCode::BadRequest);
return;
}
ReplyToDisconnectOrAbort();
AfterMapSmsDisconnected();
break;
case ObexRequestCode::SetPath: {
// Section 3.3.6 "SetPath", IrOBEX 1.2
// [opcode:1][length:2][flags:1][contants:1][Headers:var]
if (receivedLength < 5 ||
!ParseHeaders(&data[5], receivedLength - 5, &pktHeaders)) {
ReplyError(ObexResponseCode::BadRequest);
return;
}
uint8_t response = SetPath(data[3], pktHeaders);
if (response != ObexResponseCode::Success) {
ReplyError(response);
return;
}
ReplyToSetPath();
}
break;
case ObexRequestCode::Put:
case ObexRequestCode::PutFinal:
// Section 3.3.3 "Put", IrOBEX 1.2
// [opcode:1][length:2][Headers:var]
if (receivedLength < 3 ||
!ParseHeaders(&data[3], receivedLength - 3, &pktHeaders)) {
ReplyError(ObexResponseCode::BadRequest);
return;
}
if (pktHeaders.Has(ObexHeaderId::Type)) {
pktHeaders.GetContentType(type);
BT_LOGR("Type: %s", NS_ConvertUTF16toUTF8(type).get());
ReplyToPut();
if (type.EqualsLiteral("x-bt/MAP-NotificationRegistration")) {
HandleNotificationRegistration(pktHeaders);
} else if (type.EqualsLiteral("x-bt/MAP-event-report")) {
HandleEventReport(pktHeaders);
} else if (type.EqualsLiteral("x-bt/messageStatus")) {
HandleMessageStatus(pktHeaders);
}
}
break;
case ObexRequestCode::Get:
case ObexRequestCode::GetFinal: {
// [opcode:1][length:2][Headers:var]
if (receivedLength < 3 ||
!ParseHeaders(&data[3], receivedLength - 3, &pktHeaders)) {
ReplyError(ObexResponseCode::BadRequest);
return;
}
pktHeaders.GetContentType(type);
if (type.EqualsLiteral("x-obex/folder-listing")) {
HandleSmsMmsFolderListing(pktHeaders);
} else if (type.EqualsLiteral("x-bt/MAP-msg-listing")) {
// TODO: Implement this feature in Bug 1166675
} else if (type.EqualsLiteral("x-bt/message")) {
// TODO: Implement this feature in Bug 1166679
} else {
BT_LOGR("Unknown MAP request type: %s",
NS_ConvertUTF16toUTF8(type).get());
}
}
break;
default:
ReplyError(ObexResponseCode::NotImplemented);
BT_LOGR("Unrecognized ObexRequestCode %x", opCode);
break;
}
}
// Virtual function of class SocketConsumer
void
BluetoothMapSmsManager::ReceiveSocketData(BluetoothSocket* aSocket,
nsAutoPtr<UnixSocketBuffer>& aMessage)
{
MOZ_ASSERT(NS_IsMainThread());
if (aSocket == mMnsSocket) {
MnsDataHandler(aMessage);
} else {
MasDataHandler(aMessage);
}
}
bool
BluetoothMapSmsManager::CompareHeaderTarget(const ObexHeaderSet& aHeader)
{
if (!aHeader.Has(ObexHeaderId::Target)) {
BT_LOGR("No ObexHeaderId::Target in header");
return false;
}
uint8_t* targetPtr;
int targetLength;
aHeader.GetTarget(&targetPtr, &targetLength);
if (targetLength != sizeof(BluetoothUuid)) {
BT_LOGR("Length mismatch: %d != 16", targetLength);
return false;
}
for (uint8_t i = 0; i < sizeof(BluetoothUuid); i++) {
if (targetPtr[i] != kMapMasObexTarget.mUuid[i]) {
BT_LOGR("UUID mismatch: received target[%d]=0x%x != 0x%x",
i, targetPtr[i], kMapMasObexTarget.mUuid[i]);
return false;
}
}
return true;
}
uint8_t
BluetoothMapSmsManager::SetPath(uint8_t flags,
const ObexHeaderSet& aHeader)
{
// Section 5.2 "SetPath Function", MapSms 1.2
// flags bit 1 must be 1 and bit 2~7 be 0
if ((flags >> 1) != 1) {
BT_LOGR("Illegal flags [0x%x]: bits 1~7 must be 0x01", flags);
return ObexResponseCode::BadRequest;
}
/**
* Three cases:
* 1) Go up 1 level - flags bit 0 is 1
* 2) Go back to root - flags bit 0 is 0 AND name header is empty
* 3) Go down 1 level - flags bit 0 is 0 AND name header is not empty,
* where name header is the name of child folder
*/
if (flags & 1) {
// Go up 1 level
BluetoothMapFolder* parent = mCurrentFolder->GetParentFolder();
if (!parent) {
mCurrentFolder = parent;
BT_LOGR("MAS SetPath Go up 1 level");
}
} else {
MOZ_ASSERT(aHeader.Has(ObexHeaderId::Name));
nsString childFolderName;
aHeader.GetName(childFolderName);
if (childFolderName.IsEmpty()) {
// Go back to root
mCurrentFolder = mRootFolder;
BT_LOGR("MAS SetPath Go back to root");
} else {
// Go down 1 level
BluetoothMapFolder* child = mCurrentFolder->GetSubFolder(childFolderName);
if (!child) {
BT_LOGR("Illegal sub-folder name [%s]",
NS_ConvertUTF16toUTF8(childFolderName).get());
return ObexResponseCode::NotFound;
}
mCurrentFolder = child;
BT_LOGR("MAS SetPath Go down to 1 level");
}
}
mCurrentFolder->DumpFolderInfo();
return ObexResponseCode::Success;
}
void
BluetoothMapSmsManager::AfterMapSmsConnected()
{
mMasConnected = true;
}
void
BluetoothMapSmsManager::AfterMapSmsDisconnected()
{
mMasConnected = false;
// To ensure we close MNS connection
DestroyMnsObexConnection();
}
bool
BluetoothMapSmsManager::IsConnected()
{
return mMasConnected;
}
void
BluetoothMapSmsManager::GetAddress(nsAString& aDeviceAddress)
{
return mMasSocket->GetAddress(aDeviceAddress);
}
void
BluetoothMapSmsManager::ReplyToConnect()
{
if (mMasConnected) {
return;
}
// Section 3.3.1 "Connect", IrOBEX 1.2
// [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
// [Headers:var]
uint8_t req[255];
int index = 7;
req[3] = 0x10; // version=1.0
req[4] = 0x00; // flag=0x00
req[5] = BluetoothMapSmsManager::MAX_PACKET_LENGTH >> 8;
req[6] = (uint8_t)BluetoothMapSmsManager::MAX_PACKET_LENGTH;
// Section 6.4 "Establishing an OBEX Session", MapSms 1.2
// Headers: [Who:16][Connection ID]
index += AppendHeaderWho(&req[index], 255, kMapMasObexTarget.mUuid,
sizeof(BluetoothUuid));
index += AppendHeaderConnectionId(&req[index], 0x01);
SendMasObexData(req, ObexResponseCode::Success, index);
}
void
BluetoothMapSmsManager::ReplyToDisconnectOrAbort()
{
if (!mMasConnected) {
return;
}
// Section 3.3.2 "Disconnect" and Section 3.3.5 "Abort", IrOBEX 1.2
// The format of response packet of "Disconnect" and "Abort" are the same
// [opcode:1][length:2][Headers:var]
uint8_t req[255];
int index = 3;
SendMasObexData(req, ObexResponseCode::Success, index);
}
void
BluetoothMapSmsManager::ReplyToSetPath()
{
if (!mMasConnected) {
return;
}
// Section 3.3.6 "SetPath", IrOBEX 1.2
// [opcode:1][length:2][Headers:var]
uint8_t req[255];
int index = 3;
SendMasObexData(req, ObexResponseCode::Success, index);
}
void
BluetoothMapSmsManager::ReplyToPut()
{
if (!mMasConnected) {
return;
}
// Section 3.3.3.2 "PutResponse", IrOBEX 1.2
// [opcode:1][length:2][Headers:var]
uint8_t req[255];
int index = 3;
SendMasObexData(req, ObexResponseCode::Success, index);
}
void
BluetoothMapSmsManager::CreateMnsObexConnection()
{
if (mMnsSocket) {
return;
}
mMnsSocket = new BluetoothSocket(this);
// Already encrypted in previous session
mMnsSocket->Connect(mDeviceAddress, kMapMns,
BluetoothSocketType::RFCOMM, -1, false, false);
}
void
BluetoothMapSmsManager::DestroyMnsObexConnection()
{
if (!mMnsSocket) {
return;
}
mMnsSocket->Close();
mMnsSocket = nullptr;
mNtfRequired = false;
}
void
BluetoothMapSmsManager::SendMnsConnectRequest()
{
MOZ_ASSERT(mMnsSocket);
// Section 3.3.1 "Connect", IrOBEX 1.2
// [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
// [Headers:var]
uint8_t req[255];
int index = 7;
req[3] = 0x10; // version=1.0
req[4] = 0x00; // flag=0x00
req[5] = BluetoothMapSmsManager::MAX_PACKET_LENGTH >> 8;
req[6] = (uint8_t)BluetoothMapSmsManager::MAX_PACKET_LENGTH;
index += AppendHeaderTarget(&req[index], 255, kMapMnsObexTarget.mUuid,
sizeof(BluetoothUuid));
SendMnsObexData(req, ObexRequestCode::Connect, index);
}
void
BluetoothMapSmsManager::SendMnsDisconnectRequest()
{
MOZ_ASSERT(mMnsSocket);
if (!mMasConnected) {
return;
}
// Section 3.3.2 "Disconnect", IrOBEX 1.2
// [opcode:1][length:2][Headers:var]
uint8_t req[255];
int index = 3;
SendMnsObexData(req, ObexRequestCode::Disconnect, index);
}
void
BluetoothMapSmsManager::HandleSmsMmsFolderListing(const ObexHeaderSet& aHeader)
{
MOZ_ASSERT(NS_IsMainThread());
uint8_t buf[64];
uint16_t maxListCount = 0;
if (aHeader.GetAppParameter(Map::AppParametersTagId::MaxListCount,
buf, 64)) {
maxListCount = *((uint16_t *)buf);
// convert big endian to little endian
maxListCount = (maxListCount >> 8) | (maxListCount << 8);
}
uint16_t startOffset = 0;
if (aHeader.GetAppParameter(Map::AppParametersTagId::StartOffset,
buf, 64)) {
startOffset = *((uint16_t *)buf);
// convert big endian to little endian
startOffset = (startOffset >> 8) | (startOffset << 8);
}
// Folder listing size
int foldersize = mCurrentFolder->GetSubFolderCount();
// Convert little endian to big endian
uint8_t folderListingSizeValue[2];
folderListingSizeValue[0] = (foldersize & 0xFF00) >> 8;
folderListingSizeValue[1] = (foldersize & 0x00FF);
// Section 3.3.4 "GetResponse", IrOBEX 1.2
// [opcode:1][length:2][FolderListingSize:4][Headers:var] where
// Application Parameter [FolderListingSize:4] = [tagId:1][length:1][value: 2]
uint8_t appParameter[4];
AppendAppParameter(appParameter, sizeof(appParameter),
(uint8_t)Map::AppParametersTagId::FolderListingSize,
folderListingSizeValue, sizeof(folderListingSizeValue));
uint8_t resp[255];
int index = 3;
index += AppendHeaderAppParameters(&resp[index], 255, appParameter,
sizeof(appParameter));
/*
* MCE wants to query sub-folder size FolderListingSize AppParameter shall
* be used in the response if the value of MaxListCount in the request is 0.
* If MaxListCount = 0, the MSE shall ignore all other applications
* parameters that may be presented in the request. The response shall
* contain any Body header.
*/
if (maxListCount) {
nsString output;
mCurrentFolder->GetFolderListingObjectString(output, maxListCount,
startOffset);
index += AppendHeaderBody(&resp[index],
mRemoteMaxPacketLength - index,
reinterpret_cast<const uint8_t*>(
NS_ConvertUTF16toUTF8(output).get()),
NS_ConvertUTF16toUTF8(output).Length());
index += AppendHeaderEndOfBody(&resp[index]);
}
SendMasObexData(resp, ObexResponseCode::Success, index);
}
void
BluetoothMapSmsManager::BuildDefaultFolderStructure()
{
/* MAP specification defines virtual folders structure
* /
* /telecom
* /telecom/msg
* /telecom/msg/inbox
* /telecom/msg/draft
* /telecom/msg/outbox
* /telecom/msg/sent
* /telecom/msg/deleted
*/
mRootFolder = new BluetoothMapFolder(NS_LITERAL_STRING("root"), nullptr);
BluetoothMapFolder* folder =
mRootFolder->AddSubFolder(NS_LITERAL_STRING("telecom"));
folder = folder->AddSubFolder(NS_LITERAL_STRING("msg"));
// Add mandatory folders
folder->AddSubFolder(NS_LITERAL_STRING("inbox"));
folder->AddSubFolder(NS_LITERAL_STRING("sent"));
folder->AddSubFolder(NS_LITERAL_STRING("deleted"));
folder->AddSubFolder(NS_LITERAL_STRING("outbox"));
folder->AddSubFolder(NS_LITERAL_STRING("draft"));
mCurrentFolder = mRootFolder;
}
void
BluetoothMapSmsManager::HandleNotificationRegistration(
const ObexHeaderSet& aHeader)
{
MOZ_ASSERT(NS_IsMainThread());
uint8_t buf[64];
if (!aHeader.GetAppParameter(Map::AppParametersTagId::NotificationStatus,
buf, 64)) {
return;
}
bool ntfRequired = static_cast<bool>(buf[0]);
if (mNtfRequired == ntfRequired) {
// Ignore request
return;
}
mNtfRequired = ntfRequired;
/*
* Initialization sequence for a MAP session that uses both the Messsage
* Access service and the Message Notification service. The MNS connection
* shall be established by the first SetNotificationRegistration set to ON
* during MAP session. Only one MNS connection per device pair.
* Section 6.4.2, MAP
* If the Message Access connection is disconnected after Message Notification
* connection establishment, this will automatically indicate a MAS
* Notification-Deregistration for this MAS instance.
*/
if (mNtfRequired) {
CreateMnsObexConnection();
} else {
/*
* TODO: we shall check multiple MAS instances unregister notification to
* drop MNS connection, but now we only support SMS/MMS, so drop connection
* directly.
*/
DestroyMnsObexConnection();
}
}
void
BluetoothMapSmsManager::HandleEventReport(const ObexHeaderSet& aHeader)
{
// TODO: Handle event report in Bug 1166666
}
void
BluetoothMapSmsManager::HandleMessageStatus(const ObexHeaderSet& aHeader)
{
// TODO: Handle MessageStatus update in Bug 1186836
}
void
BluetoothMapSmsManager::ReplyError(uint8_t aError)
{
BT_LOGR("[0x%x]", aError);
// Section 3.2 "Response Format", IrOBEX 1.2
// [opcode:1][length:2][Headers:var]
uint8_t req[255];
int index = 3;
SendMasObexData(req, aError, index);
}
void
BluetoothMapSmsManager::SendMasObexData(uint8_t* aData, uint8_t aOpcode,
int aSize)
{
SetObexPacketInfo(aData, aOpcode, aSize);
mMasSocket->SendSocketData(new UnixSocketRawData(aData, aSize));
}
void
BluetoothMapSmsManager::SendMnsObexData(uint8_t* aData, uint8_t aOpcode,
int aSize)
{
mLastCommand = aOpcode;
SetObexPacketInfo(aData, aOpcode, aSize);
mMnsSocket->SendSocketData(new UnixSocketRawData(aData, aSize));
}
void
BluetoothMapSmsManager::OnSocketConnectSuccess(BluetoothSocket* aSocket)
{
MOZ_ASSERT(aSocket);
// MNS socket is connected
if (aSocket == mMnsSocket) {
mMnsConnected = true;
SendMnsConnectRequest();
return;
}
// MAS socket is connected
// Close server socket as only one session is allowed at a time
mMasServerSocket.swap(mMasSocket);
// Cache device address since we can't get socket address when a remote
// device disconnect with us.
mMasSocket->GetAddress(mDeviceAddress);
}
void
BluetoothMapSmsManager::OnSocketConnectError(BluetoothSocket* aSocket)
{
// MNS socket connection error
if (aSocket == mMnsSocket) {
mMnsConnected = false;
mMnsSocket = nullptr;
return;
}
// MAS socket connection error
mMasServerSocket = nullptr;
mMasSocket = nullptr;
}
void
BluetoothMapSmsManager::OnSocketDisconnect(BluetoothSocket* aSocket)
{
MOZ_ASSERT(aSocket);
// MNS socket is disconnected
if (aSocket == mMnsSocket) {
mMnsConnected = false;
mMnsSocket = nullptr;
BT_LOGR("MNS socket disconnected");
return;
}
// MAS server socket is closed
if (aSocket != mMasSocket) {
// Do nothing when a listening server socket is closed.
return;
}
// MAS socket is disconnected
AfterMapSmsDisconnected();
mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
mMasSocket = nullptr;
Listen();
}
void
BluetoothMapSmsManager::Disconnect(BluetoothProfileController* aController)
{
if (!mMasSocket) {
BT_WARNING("%s: No ongoing connection to disconnect", __FUNCTION__);
return;
}
mMasSocket->Close();
}
NS_IMPL_ISUPPORTS(BluetoothMapSmsManager, nsIObserver)
void
BluetoothMapSmsManager::Connect(const nsAString& aDeviceAddress,
BluetoothProfileController* aController)
{
MOZ_ASSERT(false);
}
void
BluetoothMapSmsManager::OnGetServiceChannel(const nsAString& aDeviceAddress,
const nsAString& aServiceUuid,
int aChannel)
{
MOZ_ASSERT(false);
}
void
BluetoothMapSmsManager::OnUpdateSdpRecords(const nsAString& aDeviceAddress)
{
MOZ_ASSERT(false);
}
void
BluetoothMapSmsManager::OnConnect(const nsAString& aErrorStr)
{
MOZ_ASSERT(false);
}
void
BluetoothMapSmsManager::OnDisconnect(const nsAString& aErrorStr)
{
MOZ_ASSERT(false);
}
void
BluetoothMapSmsManager::Reset()
{
MOZ_ASSERT(false);
}
END_BLUETOOTH_NAMESPACE

Просмотреть файл

@ -0,0 +1,150 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef mozilla_dom_bluetooth_bluedroid_BluetoothMapSmsManager_h
#define mozilla_dom_bluetooth_bluedroid_BluetoothMapSmsManager_h
#include "BluetoothCommon.h"
#include "BluetoothMapFolder.h"
#include "BluetoothProfileManagerBase.h"
#include "BluetoothSocketObserver.h"
#include "mozilla/ipc/SocketBase.h"
BEGIN_BLUETOOTH_NAMESPACE
struct Map {
enum AppParametersTagId {
MaxListCount = 0x1,
StartOffset = 0x2,
FilterMessageType = 0x3,
FilterPeriodBegin = 0x4,
FilterPeriodEnd = 0x5,
FilterReadStatus = 0x6,
FilterRecipient = 0x7,
FilterOriginator = 0x8,
FilterPriority = 0x9,
Attachment = 0x0A,
Transparent = 0x0B,
Retry = 0x0C,
NewMessage = 0x0D,
NotificationStatus = 0x0E,
MASInstanceId = 0x0F,
ParameterMask = 0x10,
FolderListingSize = 0x11,
MessagesListingSize = 0x12,
SubjectLength = 0x13,
Charset = 0x14,
FractionRequest = 0x15,
FractionDeliver = 0x16,
StatusIndicator = 0x17,
StatusValue = 0x18,
MSETime = 0x19
};
};
class BluetoothNamedValue;
class BluetoothSocket;
class ObexHeaderSet;
/*
* BluetoothMapSmsManager acts as Message Server Equipment (MSE) and runs both
* MAS server and MNS client to exchange SMS/MMS message.
*/
class BluetoothMapSmsManager : public BluetoothSocketObserver
, public BluetoothProfileManagerBase
{
public:
BT_DECL_PROFILE_MGR_BASE
BT_DECL_SOCKET_OBSERVER
virtual void GetName(nsACString& aName)
{
aName.AssignLiteral("MapSms");
}
static const int MAX_PACKET_LENGTH = 0xFFFE;
static const int MAX_INSTANCE_ID = 255;
// SDP record for SupportedMessageTypes
static const int SDP_MESSAGE_TYPE_EMAIL = 0x01;
static const int SDP_MESSAGE_TYPE_SMS_GSM = 0x02;
static const int SDP_MESSAGE_TYPE_SMS_CDMA = 0x04;
static const int SDP_MESSAGE_TYPE_MMS = 0x08;
// By defualt SMS/MMS is default supported
static const int SDP_SMS_MMS_INSTANCE_ID = 0;
static BluetoothMapSmsManager* Get();
bool Listen();
protected:
virtual ~BluetoothMapSmsManager();
private:
BluetoothMapSmsManager();
bool Init();
void HandleShutdown();
void ReplyToConnect();
void ReplyToDisconnectOrAbort();
void ReplyToSetPath();
void ReplyToPut();
void ReplyError(uint8_t aError);
void HandleNotificationRegistration(const ObexHeaderSet& aHeader);
void HandleEventReport(const ObexHeaderSet& aHeader);
void HandleMessageStatus(const ObexHeaderSet& aHeader);
void HandleSmsMmsFolderListing(const ObexHeaderSet& aHeader);
void SendMasObexData(uint8_t* aData, uint8_t aOpcode, int aSize);
void SendMnsObexData(uint8_t* aData, uint8_t aOpcode, int aSize);
uint8_t SetPath(uint8_t flags, const ObexHeaderSet& aHeader);
bool CompareHeaderTarget(const ObexHeaderSet& aHeader);
void AfterMapSmsConnected();
void AfterMapSmsDisconnected();
void CreateMnsObexConnection();
void DestroyMnsObexConnection();
void SendMnsConnectRequest();
void SendMnsDisconnectRequest();
void MnsDataHandler(mozilla::ipc::UnixSocketBuffer* aMessage);
void MasDataHandler(mozilla::ipc::UnixSocketBuffer* aMessage);
/*
* Build mandatory folders
*/
void BuildDefaultFolderStructure();
/**
* Current virtual folder path
*/
BluetoothMapFolder* mCurrentFolder;
nsRefPtr<BluetoothMapFolder> mRootFolder;
/*
* Record the last command
*/
int mLastCommand;
// MAS OBEX session status. Set when MAS OBEX session is established.
bool mMasConnected;
// MNS OBEX session status. Set when MNS OBEX session is established.
bool mMnsConnected;
bool mNtfRequired;
nsString mDeviceAddress;
unsigned int mRemoteMaxPacketLength;
// If a connection has been established, mMasSocket will be the socket
// communicating with the remote socket. We maintain the invariant that if
// mMasSocket is non-null, mServerSocket must be null (and vice versa).
nsRefPtr<BluetoothSocket> mMasSocket;
// Server socket. Once an inbound connection is established, it will hand
// over the ownership to mMasSocket, and get a new server socket while Listen()
// is called.
nsRefPtr<BluetoothSocket> mMasServerSocket;
// Message notification service client socket
nsRefPtr<BluetoothSocket> mMnsSocket;
};
END_BLUETOOTH_NAMESPACE
#endif //mozilla_dom_bluetooth_bluedroid_BluetoothMapSmsManager_h

Просмотреть файл

@ -380,7 +380,6 @@ BluetoothOppManager::StartSendingNextFile()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!IsConnected());
MOZ_ASSERT(!mBatches.IsEmpty());
MOZ_ASSERT((int)mBatches[0].mBlobs.Length() > mCurrentBlobIndex + 1);
@ -473,6 +472,7 @@ bool
BluetoothOppManager::ProcessNextBatch()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!IsConnected());
// Remove the processed batch.
// A batch is processed if we've incremented mCurrentBlobIndex for it.
@ -605,9 +605,11 @@ BluetoothOppManager::AfterOppDisconnected()
mOutputStream = nullptr;
}
if (mReadFileThread) {
mReadFileThread->Shutdown();
mReadFileThread = nullptr;
// Store local pointer of |mReadFileThread| to avoid shutdown reentry crash
// See bug 1191715 comment 19 for more details.
nsCOMPtr<nsIThread> thread = mReadFileThread.forget();
if (thread) {
thread->Shutdown();
}
// Release the mount lock if file transfer completed
@ -790,7 +792,7 @@ BluetoothOppManager::RetrieveSentFileName()
}
/**
* We try our best to get the file extention to avoid interoperability issues.
* We try our best to get the file extension to avoid interoperability issues.
* However, once we found that we are unable to get suitable extension or
* information about the content type, sending a pre-defined file name without
* extension would be fine.

Просмотреть файл

@ -23,6 +23,7 @@
#include "BluetoothGattManager.h"
#include "BluetoothHfpManager.h"
#include "BluetoothHidManager.h"
#include "BluetoothMapSmsManager.h"
#include "BluetoothOppManager.h"
#include "BluetoothPbapManager.h"
#include "BluetoothProfileController.h"
@ -301,6 +302,7 @@ BluetoothServiceBluedroid::StopInternal(BluetoothReplyRunnable* aRunnable)
BluetoothA2dpManager::Get(),
BluetoothOppManager::Get(),
BluetoothPbapManager::Get(),
BluetoothMapSmsManager::Get(),
BluetoothHidManager::Get()
};
@ -1520,6 +1522,11 @@ BluetoothServiceBluedroid::AdapterStateChangedNotification(bool aState)
if (!pbap || !pbap->Listen()) {
BT_LOGR("Fail to start BluetoothPbapManager listening");
}
BluetoothMapSmsManager* map = BluetoothMapSmsManager::Get();
if (!map || !map->Listen()) {
BT_LOGR("Fail to start BluetoothMapSmsManager listening");
}
}
// Resolve promise if existed

Просмотреть файл

@ -367,7 +367,6 @@ BluetoothOppManager::StartSendingNextFile()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!IsConnected());
MOZ_ASSERT(!mBatches.IsEmpty());
MOZ_ASSERT((int)mBatches[0].mBlobs.Length() > mCurrentBlobIndex + 1);
@ -460,6 +459,7 @@ bool
BluetoothOppManager::ProcessNextBatch()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!IsConnected());
// Remove the processed batch.
// A batch is processed if we've incremented mCurrentBlobIndex for it.
@ -592,9 +592,11 @@ BluetoothOppManager::AfterOppDisconnected()
mOutputStream = nullptr;
}
if (mReadFileThread) {
mReadFileThread->Shutdown();
mReadFileThread = nullptr;
// Store local pointer of |mReadFileThread| to avoid shutdown reentry crash
// See bug 1191715 comment 19 for more details.
nsCOMPtr<nsIThread> thread = mReadFileThread.forget();
if (thread) {
thread->Shutdown();
}
// Release the mount lock if file transfer completed
@ -777,7 +779,7 @@ BluetoothOppManager::RetrieveSentFileName()
}
/**
* We try our best to get the file extention to avoid interoperability issues.
* We try our best to get the file extension to avoid interoperability issues.
* However, once we found that we are unable to get suitable extension or
* information about the content type, sending a pre-defined file name without
* extension would be fine.

Просмотреть файл

@ -65,6 +65,14 @@ AppendHeaderBody(uint8_t* aRetBuf, int aBufferSize, const uint8_t* aBody,
aBody, aLength);
}
int
AppendHeaderTarget(uint8_t* aRetBuf, int aBufferSize, const uint8_t* aTarget,
int aLength)
{
return AppendHeader(ObexHeaderId::Target, aRetBuf, aBufferSize,
aTarget, aLength);
}
int
AppendHeaderWho(uint8_t* aRetBuf, int aBufferSize, const uint8_t* aWho,
int aLength)

Просмотреть файл

@ -343,6 +343,8 @@ int AppendHeaderName(uint8_t* aRetBuf, int aBufferSize, const uint8_t* aName,
int aLength);
int AppendHeaderBody(uint8_t* aRetBuf, int aBufferSize, const uint8_t* aBody,
int aLength);
int AppendHeaderTarget(uint8_t* aRetBuf, int aBufferSize, const uint8_t* aTarget,
int aLength);
int AppendHeaderWho(uint8_t* aRetBuf, int aBufferSize, const uint8_t* aWho,
int aLength);
int AppendHeaderAppParameters(uint8_t* aRetBuf, int aBufferSize,

Просмотреть файл

@ -81,6 +81,8 @@ if CONFIG['MOZ_B2G_BT']:
'bluedroid/BluetoothDaemonSetupInterface.cpp',
'bluedroid/BluetoothDaemonSocketInterface.cpp',
'bluedroid/BluetoothGattManager.cpp',
'bluedroid/BluetoothMapFolder.cpp',
'bluedroid/BluetoothMapSmsManager.cpp',
'bluedroid/BluetoothOppManager.cpp',
'bluedroid/BluetoothPbapManager.cpp',
'bluedroid/BluetoothServiceBluedroid.cpp',

Просмотреть файл

@ -381,6 +381,7 @@ SettingsManager.prototype = {
debug("WARNING: MORE THAN " + kObserverSoftLimit + " OBSERVERS FOR " +
aName + ": " + length + " FROM" + (new Error).stack);
#ifdef DEBUG
debug("JS STOPS EXECUTING AT THIS POINT IN DEBUG BUILDS!");
throw Components.results.NS_ERROR_ABORT;
#endif
}

Просмотреть файл

@ -776,7 +776,12 @@ let gTestSuite = (function() {
.then(() => runEmulatorShellSafe(['hostapd', '-B', configFileName]))
.then(function (reply) {
// It may fail at the first time due to the previous ungracefully terminated one.
if (reply[0] === 'bind(PF_UNIX): Address already in use') {
if (reply.length === 0) {
// The hostapd starts successfully
return;
}
if (reply[0].indexOf('bind(PF_UNIX): Address already in use') !== -1) {
return startOneHostapd(aIndex);
}
});

Просмотреть файл

@ -14,6 +14,8 @@ const STATIC_IP_CONFIG = {
dns2: "8.8.4.4",
};
const TESTING_HOSTAPD = [{ ssid: 'ap0' }];
function testAssociateWithStaticIp(aNetwork, aStaticIpConfig) {
return gTestSuite.setStaticIpMode(aNetwork, aStaticIpConfig)
.then(() => gTestSuite.testAssociate(aNetwork))
@ -32,10 +34,32 @@ function testAssociateWithStaticIp(aNetwork, aStaticIpConfig) {
});
}
function findDesireNetwork(aNetworks) {
let i = gTestSuite.getFirstIndexBySsid(TESTING_HOSTAPD[0].ssid, aNetworks);
if (-1 !== i) {
return aNetworks[i];
}
return aNetworks[0];
}
// Start test.
gTestSuite.doTest(function() {
gTestSuite.doTestWithoutStockAp(function() {
return gTestSuite.ensureWifiEnabled(true)
// Start custom hostapd for testing.
.then(() => gTestSuite.startHostapds(TESTING_HOSTAPD))
.then(() => gTestSuite.verifyNumOfProcesses('hostapd',
TESTING_HOSTAPD.length))
// Perform a wifi scan, and then run the static ip test
.then(() => gTestSuite.requestWifiScan())
.then((aNetworks) => testAssociateWithStaticIp(aNetworks[0],
STATIC_IP_CONFIG));
.then((aNetworks) => findDesireNetwork(aNetworks))
.then((aNetwork) => testAssociateWithStaticIp(aNetwork,
STATIC_IP_CONFIG))
// Kill running hostapd.
.then(gTestSuite.killAllHostapd)
.then(() => gTestSuite.verifyNumOfProcesses('hostapd', 0));
});

Просмотреть файл

@ -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({