Merge m-c to inbound, a=merge
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="8999f0ba6326d815c8366e3c1155b7e4e9763b40"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f75a7e01912cee313fed92ff2089586f507b2ba5"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7938df689aa87769fad3f2cf9097fb4ecb106a43"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="8999f0ba6326d815c8366e3c1155b7e4e9763b40"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f75a7e01912cee313fed92ff2089586f507b2ba5"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7938df689aa87769fad3f2cf9097fb4ecb106a43"/>
|
||||
|
@ -135,7 +135,7 @@
|
|||
<project groups="invensense" name="platform/hardware/invensense" path="hardware/invensense" revision="e6d9ab28b4f4e7684f6c07874ee819c9ea0002a2"/>
|
||||
<project name="platform/hardware/ril" path="hardware/ril" revision="865ce3b4a2ba0b3a31421ca671f4d6c5595f8690"/>
|
||||
<project name="kernel/common" path="kernel" revision="b698c5aa4e9393dfd70d0ea7a0bf93b333b54750"/>
|
||||
<project name="platform/system/core" path="system/core" revision="4776448ebcd3f07d58b91503c478da9b54cb58a0"/>
|
||||
<project name="platform/system/core" path="system/core" revision="1b888e1ae54c358246f13e4fb915ce43cde00691"/>
|
||||
<project name="u-boot" path="u-boot" revision="f1502910977ac88f43da7bf9277c3523ad4b0b2f"/>
|
||||
<project name="vendor/sprd/gps" path="vendor/sprd/gps" revision="7d6e1269be7186b2073fa568958b357826692c4b"/>
|
||||
<project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="295ff253b74353751a99aafd687196a28c84a58e"/>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<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="8999f0ba6326d815c8366e3c1155b7e4e9763b40"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f75a7e01912cee313fed92ff2089586f507b2ba5"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7938df689aa87769fad3f2cf9097fb4ecb106a43"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="4ace9aaee0e048dfda11bb787646c59982a3dc80"/>
|
||||
|
|
|
@ -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="8999f0ba6326d815c8366e3c1155b7e4e9763b40"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f75a7e01912cee313fed92ff2089586f507b2ba5"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7938df689aa87769fad3f2cf9097fb4ecb106a43"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0c28789b9957913be975eb002a22323f93585d4c"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="8999f0ba6326d815c8366e3c1155b7e4e9763b40"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f75a7e01912cee313fed92ff2089586f507b2ba5"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7938df689aa87769fad3f2cf9097fb4ecb106a43"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<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="8999f0ba6326d815c8366e3c1155b7e4e9763b40"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f75a7e01912cee313fed92ff2089586f507b2ba5"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7938df689aa87769fad3f2cf9097fb4ecb106a43"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<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="8999f0ba6326d815c8366e3c1155b7e4e9763b40"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f75a7e01912cee313fed92ff2089586f507b2ba5"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7938df689aa87769fad3f2cf9097fb4ecb106a43"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="4ace9aaee0e048dfda11bb787646c59982a3dc80"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="8999f0ba6326d815c8366e3c1155b7e4e9763b40"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f75a7e01912cee313fed92ff2089586f507b2ba5"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7938df689aa87769fad3f2cf9097fb4ecb106a43"/>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"git": {
|
||||
"git_revision": "8999f0ba6326d815c8366e3c1155b7e4e9763b40",
|
||||
"git_revision": "f75a7e01912cee313fed92ff2089586f507b2ba5",
|
||||
"remote": "https://git.mozilla.org/releases/gaia.git",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "983b7ecb17c67b9fb511f400bb5b28b9069eea00",
|
||||
"revision": "f51f8770b9665f44e57e9e09faee2b967a80abf1",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="8999f0ba6326d815c8366e3c1155b7e4e9763b40"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f75a7e01912cee313fed92ff2089586f507b2ba5"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7938df689aa87769fad3f2cf9097fb4ecb106a43"/>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="8999f0ba6326d815c8366e3c1155b7e4e9763b40"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f75a7e01912cee313fed92ff2089586f507b2ba5"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7938df689aa87769fad3f2cf9097fb4ecb106a43"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0c28789b9957913be975eb002a22323f93585d4c"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<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="8999f0ba6326d815c8366e3c1155b7e4e9763b40"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f75a7e01912cee313fed92ff2089586f507b2ba5"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7938df689aa87769fad3f2cf9097fb4ecb106a43"/>
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
"dot-location": [2, "property"],
|
||||
"eol-last": 2,
|
||||
"eqeqeq": [2, "smart"],
|
||||
"jsx-quotes": [2, "prefer-double"],
|
||||
"key-spacing": [2, {"beforeColon": false, "afterColon": true }],
|
||||
"linebreak-style": [2, "unix"],
|
||||
"new-cap": 0, // TODO: set to 2
|
||||
|
@ -106,7 +107,6 @@
|
|||
"yoda": [2, "never"],
|
||||
// eslint-plugin-react rules. These are documented at
|
||||
// <https://github.com/yannickcr/eslint-plugin-react#list-of-supported-rules>
|
||||
"react/jsx-quotes": [2, "double", "avoid-escape"],
|
||||
"react/jsx-no-undef": 2,
|
||||
"react/jsx-sort-props": 2,
|
||||
"react/jsx-sort-prop-types": 2,
|
||||
|
|
|
@ -469,51 +469,6 @@ html, .fx-embedded, #main,
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
.room-conversation-wrapper header {
|
||||
background: #000;
|
||||
height: 50px;
|
||||
text-align: left;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .room-conversation-wrapper header {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.room-conversation-wrapper header h1 {
|
||||
font-size: 1.5em;
|
||||
color: #fff;
|
||||
line-height: 50px;
|
||||
text-indent: 40px;
|
||||
background-image: url("../img/firefox-logo.png");
|
||||
background-size: 30px;
|
||||
background-position: 0 center;
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .room-conversation-wrapper header h1 {
|
||||
background-position: 100% center;
|
||||
}
|
||||
|
||||
.room-conversation-wrapper header a {
|
||||
float: right;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .room-conversation-wrapper header a {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.room-conversation-wrapper header .icon-help {
|
||||
display: inline-block;
|
||||
background-size: contain;
|
||||
margin-top: 15px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: transparent url("../img/svg/glyph-help-16x16.svg") no-repeat;
|
||||
}
|
||||
|
||||
.room-invitation-overlay {
|
||||
position: absolute;
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
|
@ -676,12 +631,6 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
.standalone-room-wrapper > .media-layout {
|
||||
/* 50px is the header, 10px is the bottom margin. */
|
||||
height: calc(100% - 50px - 10px);
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.media-layout > .media-wrapper {
|
||||
display: flex;
|
||||
flex-flow: column wrap;
|
||||
|
@ -797,11 +746,6 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
|
|||
}
|
||||
|
||||
@media screen and (max-width:640px) {
|
||||
.standalone-room-wrapper > .media-layout {
|
||||
/* 50px is height of header, 10px is bottom margin. */
|
||||
height: calc(100% - 50px - 10px);
|
||||
}
|
||||
|
||||
.media-layout > .media-wrapper {
|
||||
flex-direction: row;
|
||||
margin: 0;
|
||||
|
@ -832,8 +776,8 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
|
|||
}
|
||||
|
||||
.media-wrapper > .focus-stream > .local ~ .conversation-toolbar {
|
||||
/* 22px are the margins, 120px is for the local video area. */
|
||||
max-width: calc(100% - 22px - 120px);
|
||||
/* 120px is for the local video area. */
|
||||
max-width: calc(100% - 120px);
|
||||
}
|
||||
|
||||
.media-wrapper > .focus-stream > .local {
|
||||
|
@ -938,7 +882,7 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
|
|||
}
|
||||
|
||||
.media-wrapper > .focus-stream > .local ~ .conversation-toolbar {
|
||||
max-width: calc(75% - 22px);
|
||||
max-width: 75%;
|
||||
}
|
||||
|
||||
.media-wrapper > .focus-stream > .local {
|
||||
|
@ -981,7 +925,7 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
|
|||
}
|
||||
|
||||
.media-wrapper:not(.showing-remote-streams) > .focus-stream > .local ~ .conversation-toolbar {
|
||||
max-width: calc(100% - 22px);
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.media-wrapper > .focus-stream {
|
||||
|
@ -1247,8 +1191,7 @@ html[dir="rtl"] .text-chat-entry.received .text-chat-arrow {
|
|||
left: 0;
|
||||
}
|
||||
.standalone .room-conversation-wrapper .video-layout-wrapper {
|
||||
/* 50px: header's height; 10px: bottom margin */
|
||||
height: calc(100% - 50px - 10px);
|
||||
height: 100%;
|
||||
}
|
||||
.standalone .room-conversation .video_wrapper.remote_wrapper {
|
||||
width: 100%;
|
||||
|
|
|
@ -1123,14 +1123,15 @@ loop.shared.views = (function(_, mozL10n) {
|
|||
srcMediaElement: this.props.remoteSrcMediaElement}),
|
||||
this.state.localMediaAboslutelyPositioned ?
|
||||
this.renderLocalVideo() : null,
|
||||
this.props.children
|
||||
this.props.displayScreenShare ? null : this.props.children
|
||||
),
|
||||
React.createElement("div", {className: screenShareStreamClasses},
|
||||
React.createElement(MediaView, {displayAvatar: false,
|
||||
isLoading: this.props.isScreenShareLoading,
|
||||
mediaType: "screen-share",
|
||||
posterUrl: this.props.screenSharePosterUrl,
|
||||
srcMediaElement: this.props.screenShareMediaElement})
|
||||
srcMediaElement: this.props.screenShareMediaElement}),
|
||||
this.props.displayScreenShare ? this.props.children : null
|
||||
),
|
||||
React.createElement(loop.shared.views.chat.TextChatView, {
|
||||
dispatcher: this.props.dispatcher,
|
||||
|
|
|
@ -1123,7 +1123,7 @@ loop.shared.views = (function(_, mozL10n) {
|
|||
srcMediaElement={this.props.remoteSrcMediaElement} />
|
||||
{ this.state.localMediaAboslutelyPositioned ?
|
||||
this.renderLocalVideo() : null }
|
||||
{ this.props.children }
|
||||
{ this.props.displayScreenShare ? null : this.props.children }
|
||||
</div>
|
||||
<div className={screenShareStreamClasses}>
|
||||
<MediaView displayAvatar={false}
|
||||
|
@ -1131,6 +1131,7 @@ loop.shared.views = (function(_, mozL10n) {
|
|||
mediaType="screen-share"
|
||||
posterUrl={this.props.screenSharePosterUrl}
|
||||
srcMediaElement={this.props.screenShareMediaElement} />
|
||||
{ this.props.displayScreenShare ? this.props.children : null }
|
||||
</div>
|
||||
<loop.shared.views.chat.TextChatView
|
||||
dispatcher={this.props.dispatcher}
|
||||
|
|
|
@ -46,47 +46,82 @@ body,
|
|||
height: 30px;
|
||||
}
|
||||
|
||||
.room-conversation-wrapper > .beta-logo {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
/* Standalone Overlay wrapper */
|
||||
.standalone-overlay-wrapper {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: transparent url(../shared/img/beta-ribbon.svg) no-repeat;
|
||||
background-size: 50px;
|
||||
z-index: 1000;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
margin: 1.2rem;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Mozilla Logo */
|
||||
.standalone-overlay-wrapper > .hello-logo {
|
||||
width: 128px;
|
||||
height: 21px;
|
||||
background-image: url("../shared/img/hello_logo.svg");
|
||||
background-size: contain;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.focus-stream > .standalone-moz-logo {
|
||||
html[dir="rtl"] .standalone-overlay-wrapper > .hello-logo {
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
.standalone-overlay-wrapper > .general-support-url {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .standalone-overlay-wrapper > .general-support-url {
|
||||
left: 0;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.standalone-overlay-wrapper .icon-help {
|
||||
background-size: contain;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: transparent url("../shared/img/svg/glyph-help-16x16.svg") no-repeat;
|
||||
}
|
||||
|
||||
.standalone-overlay-wrapper > .standalone-moz-logo {
|
||||
width: 50px;
|
||||
height: 13px;
|
||||
background-size: contain;
|
||||
background-image: url("../img/mozilla-logo.svg#logo-white");
|
||||
background-repeat: no-repeat;
|
||||
position: absolute;
|
||||
bottom: 1.2rem;
|
||||
right: 1.2rem;
|
||||
left: auto;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .focus-stream > .standalone-moz-logo {
|
||||
left: 1.2rem;
|
||||
html[dir="rtl"] .standalone-overlay-wrapper > .standalone-moz-logo {
|
||||
left: 0;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
@media screen and (max-width:640px) {
|
||||
.focus-stream > .standalone-moz-logo {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-large {
|
||||
font-size: 1rem;
|
||||
padding: .3em .5rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width:640px) {
|
||||
.standalone-overlay-wrapper > .standalone-moz-logo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.standalone-overlay-wrapper > .hello-logo {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsupported/expired views
|
||||
*/
|
||||
|
|
|
@ -390,32 +390,6 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||
}
|
||||
});
|
||||
|
||||
var StandaloneRoomHeader = React.createClass({displayName: "StandaloneRoomHeader",
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
|
||||
},
|
||||
|
||||
recordClick: function() {
|
||||
this.props.dispatcher.dispatch(new sharedActions.RecordClick({
|
||||
linkInfo: "Support link click"
|
||||
}));
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
React.createElement("header", null,
|
||||
React.createElement("h1", null, mozL10n.get("clientShortname2")),
|
||||
React.createElement("a", {href: loop.config.generalSupportUrl,
|
||||
onClick: this.recordClick,
|
||||
rel: "noreferrer",
|
||||
target: "_blank"},
|
||||
React.createElement("i", {className: "icon icon-help"})
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var StandaloneRoomView = React.createClass({displayName: "StandaloneRoomView",
|
||||
mixins: [
|
||||
Backbone.Events,
|
||||
|
@ -628,8 +602,6 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||
|
||||
return (
|
||||
React.createElement("div", {className: "room-conversation-wrapper standalone-room-wrapper"},
|
||||
React.createElement("div", {className: "beta-logo"}),
|
||||
React.createElement(StandaloneRoomHeader, {dispatcher: this.props.dispatcher}),
|
||||
React.createElement(sharedViews.MediaLayoutView, {
|
||||
dispatcher: this.props.dispatcher,
|
||||
displayScreenShare: displayScreenShare,
|
||||
|
@ -647,6 +619,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||
screenSharePosterUrl: this.props.screenSharePosterUrl,
|
||||
showContextRoomName: true,
|
||||
useDesktopPaths: false},
|
||||
React.createElement(StandaloneOverlayWrapper, {dispatcher: this.props.dispatcher}),
|
||||
React.createElement(StandaloneRoomInfoArea, {activeRoomStore: this.props.activeRoomStore,
|
||||
dispatcher: this.props.dispatcher,
|
||||
failureReason: this.state.failureReason,
|
||||
|
@ -662,22 +635,49 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||
publishStream: this.publishStream,
|
||||
show: true,
|
||||
video: {enabled: !this.state.videoMuted,
|
||||
visible: this._roomIsActive()}}),
|
||||
React.createElement(StandaloneMozLogo, {dispatcher: this.props.dispatcher})
|
||||
visible: this._roomIsActive()}})
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var StandaloneMozLogo = React.createClass({displayName: "StandaloneMozLogo",
|
||||
var StandaloneOverlayWrapper = React.createClass({displayName: "StandaloneOverlayWrapper",
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
React.createElement("div", {className: "standalone-moz-logo"})
|
||||
React.createElement("div", {className: "standalone-overlay-wrapper"},
|
||||
React.createElement("div", {className: "hello-logo"}),
|
||||
React.createElement(GeneralSupportURL, {dispatcher: this.props.dispatcher}),
|
||||
React.createElement("div", {className: "standalone-moz-logo"})
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var GeneralSupportURL = React.createClass({displayName: "GeneralSupportURL",
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
|
||||
},
|
||||
|
||||
generalSupportUrlClick: function() {
|
||||
this.props.dispatcher.dispatch(new sharedActions.RecordClick({
|
||||
linkInfo: "Support link click"
|
||||
}));
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
React.createElement("a", {className: "general-support-url",
|
||||
href: loop.config.generalSupportUrl,
|
||||
onClick: this.generalSupportUrlClick,
|
||||
rel: "noreferrer",
|
||||
target: "_blank"},
|
||||
React.createElement("div", {className: "icon icon-help"})
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -722,8 +722,8 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||
StandaloneHandleUserAgentView: StandaloneHandleUserAgentView,
|
||||
StandaloneRoomControllerView: StandaloneRoomControllerView,
|
||||
StandaloneRoomFailureView: StandaloneRoomFailureView,
|
||||
StandaloneRoomHeader: StandaloneRoomHeader,
|
||||
StandaloneRoomInfoArea: StandaloneRoomInfoArea,
|
||||
StandaloneOverlayWrapper: StandaloneOverlayWrapper,
|
||||
StandaloneRoomView: StandaloneRoomView,
|
||||
ToSView: ToSView
|
||||
};
|
||||
|
|
|
@ -390,32 +390,6 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||
}
|
||||
});
|
||||
|
||||
var StandaloneRoomHeader = React.createClass({
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
|
||||
},
|
||||
|
||||
recordClick: function() {
|
||||
this.props.dispatcher.dispatch(new sharedActions.RecordClick({
|
||||
linkInfo: "Support link click"
|
||||
}));
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<header>
|
||||
<h1>{mozL10n.get("clientShortname2")}</h1>
|
||||
<a href={loop.config.generalSupportUrl}
|
||||
onClick={this.recordClick}
|
||||
rel="noreferrer"
|
||||
target="_blank">
|
||||
<i className="icon icon-help"></i>
|
||||
</a>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var StandaloneRoomView = React.createClass({
|
||||
mixins: [
|
||||
Backbone.Events,
|
||||
|
@ -628,8 +602,6 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||
|
||||
return (
|
||||
<div className="room-conversation-wrapper standalone-room-wrapper">
|
||||
<div className="beta-logo" />
|
||||
<StandaloneRoomHeader dispatcher={this.props.dispatcher} />
|
||||
<sharedViews.MediaLayoutView
|
||||
dispatcher={this.props.dispatcher}
|
||||
displayScreenShare={displayScreenShare}
|
||||
|
@ -647,6 +619,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||
screenSharePosterUrl={this.props.screenSharePosterUrl}
|
||||
showContextRoomName={true}
|
||||
useDesktopPaths={false}>
|
||||
<StandaloneOverlayWrapper dispatcher={this.props.dispatcher} />
|
||||
<StandaloneRoomInfoArea activeRoomStore={this.props.activeRoomStore}
|
||||
dispatcher={this.props.dispatcher}
|
||||
failureReason={this.state.failureReason}
|
||||
|
@ -663,21 +636,48 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||
show={true}
|
||||
video={{enabled: !this.state.videoMuted,
|
||||
visible: this._roomIsActive()}} />
|
||||
<StandaloneMozLogo dispatcher={this.props.dispatcher}/>
|
||||
</sharedViews.MediaLayoutView>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var StandaloneMozLogo = React.createClass({
|
||||
var StandaloneOverlayWrapper = React.createClass({
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="standalone-moz-logo" />
|
||||
<div className="standalone-overlay-wrapper">
|
||||
<div className="hello-logo"></div>
|
||||
<GeneralSupportURL dispatcher={this.props.dispatcher} />
|
||||
<div className="standalone-moz-logo" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var GeneralSupportURL = React.createClass({
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
|
||||
},
|
||||
|
||||
generalSupportUrlClick: function() {
|
||||
this.props.dispatcher.dispatch(new sharedActions.RecordClick({
|
||||
linkInfo: "Support link click"
|
||||
}));
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<a className="general-support-url"
|
||||
href={loop.config.generalSupportUrl}
|
||||
onClick={this.generalSupportUrlClick}
|
||||
rel="noreferrer"
|
||||
target="_blank">
|
||||
<div className="icon icon-help"></div>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -722,8 +722,8 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||
StandaloneHandleUserAgentView: StandaloneHandleUserAgentView,
|
||||
StandaloneRoomControllerView: StandaloneRoomControllerView,
|
||||
StandaloneRoomFailureView: StandaloneRoomFailureView,
|
||||
StandaloneRoomHeader: StandaloneRoomHeader,
|
||||
StandaloneRoomInfoArea: StandaloneRoomInfoArea,
|
||||
StandaloneOverlayWrapper: StandaloneOverlayWrapper,
|
||||
StandaloneRoomView: StandaloneRoomView,
|
||||
ToSView: ToSView
|
||||
};
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"compression": "1.5.x",
|
||||
"eslint": "1.2.x",
|
||||
"eslint": "1.6.x",
|
||||
"eslint-plugin-mozilla": "../../../../testing/eslint-plugin-mozilla",
|
||||
"eslint-plugin-react": "3.2.x",
|
||||
"eslint-plugin-react": "3.5.x",
|
||||
"exports-loader": "0.6.x",
|
||||
"express": "4.x",
|
||||
"imports-loader": "0.6.x",
|
||||
|
|
|
@ -40,6 +40,7 @@ describe("loop.standaloneRoomViews", function() {
|
|||
close: sandbox.stub(),
|
||||
addEventListener: function() {},
|
||||
document: { addEventListener: function(){} },
|
||||
removeEventListener: function() {},
|
||||
setTimeout: function(callback) { callback(); }
|
||||
};
|
||||
loop.shared.mixins.setRootObject(fakeWindow);
|
||||
|
@ -211,7 +212,7 @@ describe("loop.standaloneRoomViews", function() {
|
|||
function mountTestComponent() {
|
||||
return TestUtils.renderIntoDocument(
|
||||
React.createElement(
|
||||
loop.standaloneRoomViews.StandaloneRoomHeader, {
|
||||
loop.standaloneRoomViews.StandaloneOverlayWrapper, {
|
||||
dispatcher: dispatcher
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -52,7 +52,6 @@
|
|||
#nav-bar[brighttext] {
|
||||
--toolbarbutton-hover-background: rgba(255,255,255,.25);
|
||||
--toolbarbutton-hover-bordercolor: rgba(255,255,255,.5);
|
||||
--toolbarbutton-hover-boxshadow: none;
|
||||
|
||||
--toolbarbutton-active-background: rgba(255,255,255,.4);
|
||||
--toolbarbutton-active-bordercolor: rgba(255,255,255,.7);
|
||||
|
@ -61,6 +60,17 @@
|
|||
--toolbarbutton-checkedhover-backgroundcolor: rgba(255,255,255,.3);
|
||||
}
|
||||
|
||||
#nav-bar:-moz-lwtheme {
|
||||
--toolbarbutton-hover-background: rgba(255,255,255,.25);
|
||||
--toolbarbutton-hover-bordercolor: rgba(0,0,0,.2);
|
||||
|
||||
--toolbarbutton-active-background: rgba(70%,70%,70%,.25);
|
||||
--toolbarbutton-active-bordercolor: rgba(0,0,0,.3);
|
||||
--toolbarbutton-active-boxshadow: 0 0 2px rgba(0,0,0,.6) inset;
|
||||
|
||||
--toolbarbutton-checkedhover-backgroundcolor: rgba(85%,85%,85%,.25);
|
||||
}
|
||||
|
||||
#menubar-items {
|
||||
-moz-box-orient: vertical; /* for flex hack */
|
||||
}
|
||||
|
@ -1136,10 +1146,6 @@ toolbar .toolbarbutton-1:-moz-any(@primaryToolbarButtons@) > :-moz-any(.toolbarb
|
|||
}
|
||||
|
||||
#nav-bar:-moz-lwtheme {
|
||||
--urlbar-border-color: rgba(0,0,0,.32);
|
||||
}
|
||||
|
||||
#nav-bar:-moz-lwtheme-brighttext {
|
||||
--urlbar-border-color: var(--toolbarbutton-hover-bordercolor);
|
||||
}
|
||||
|
||||
|
@ -1171,11 +1177,6 @@ toolbar .toolbarbutton-1:-moz-any(@primaryToolbarButtons@) > :-moz-any(.toolbarb
|
|||
border-color: var(--urlbar-border-color);
|
||||
}
|
||||
|
||||
#urlbar:-moz-lwtheme-brighttext,
|
||||
.searchbar-textbox:-moz-lwtheme-brighttext {
|
||||
background-clip: border-box;
|
||||
}
|
||||
|
||||
#urlbar:hover,
|
||||
.searchbar-textbox:hover {
|
||||
border-color: var(--urlbar-border-color-hover);
|
||||
|
|
|
@ -384,13 +384,6 @@ exports.dumpv = function(msg) {
|
|||
// loader, so define it on dumpn instead.
|
||||
exports.dumpv.wantVerbose = false;
|
||||
|
||||
exports.dbg_assert = function dbg_assert(cond, e) {
|
||||
if (!cond) {
|
||||
return e;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Utility function for updating an object with the properties of
|
||||
* other objects.
|
||||
|
@ -447,6 +440,50 @@ exports.defineLazyGetter = function defineLazyGetter(aObject, aName, aLambda) {
|
|||
});
|
||||
};
|
||||
|
||||
// DEPRECATED: use DevToolsUtils.assert(condition, message) instead!
|
||||
exports.dbg_assert = function dbg_assert(cond, e) {
|
||||
if (!cond) {
|
||||
return e;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* DevToolsUtils.assert(condition, message)
|
||||
*
|
||||
* @param Boolean condition
|
||||
* @param String message
|
||||
*
|
||||
* Assertions are enabled when any of the following are true:
|
||||
* - This is a DEBUG_JS_MODULES build
|
||||
* - This is a DEBUG build
|
||||
* - DevToolsUtils.testing is set to true
|
||||
*
|
||||
* If assertions are enabled, then `condition` is checked and if false-y, the
|
||||
* assertion failure is logged and then an error is thrown.
|
||||
*
|
||||
* If assertions are not enabled, then this function is a no-op.
|
||||
*
|
||||
* This is an improvement over `dbg_assert`, which doesn't actually cause any
|
||||
* fatal behavior, and is therefore much easier to accidentally ignore.
|
||||
*/
|
||||
exports.defineLazyGetter(exports, "assert", () => {
|
||||
function noop(condition, msg) { }
|
||||
|
||||
function assert(condition, message) {
|
||||
if (!condition) {
|
||||
const err = new Error("Assertion failure: " + message);
|
||||
exports.reportException("DevToolsUtils.assert", err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
const scope = {};
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm", scope);
|
||||
const { DEBUG, DEBUG_JS_MODULES } = scope.AppConstants;
|
||||
|
||||
return (DEBUG || DEBUG_JS_MODULES || exports.testing) ? assert : noop;
|
||||
});
|
||||
|
||||
/**
|
||||
* Defines a getter on a specified object for a module. The module will not
|
||||
* be imported until first use.
|
||||
|
|
|
@ -0,0 +1,324 @@
|
|||
/* 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/. */
|
||||
|
||||
/*** Visitor ****************************************************************/
|
||||
|
||||
/**
|
||||
* A Visitor visits each node and edge of a census report tree as the census
|
||||
* report is being traversed by `walk`.
|
||||
*/
|
||||
function Visitor() { };
|
||||
exports.Visitor = Visitor;
|
||||
|
||||
/**
|
||||
* The `enter` method is called when a new sub-report is entered in traversal.
|
||||
*
|
||||
* @param {Object} breakdown
|
||||
* The breakdown for the sub-report that is being entered by traversal.
|
||||
*
|
||||
* @param {any} edge
|
||||
* The edge leading to this sub-report. The edge is null if (but not iff!
|
||||
* eg, null allocation stack edges) we are entering the root report.
|
||||
*/
|
||||
Visitor.prototype.enter = function (breakdown, edge) { };
|
||||
|
||||
/**
|
||||
* The `exit` method is called when traversal of a sub-report has finished.
|
||||
*
|
||||
* @param {Object} breakdown
|
||||
* The breakdown for the sub-report whose traversal has finished.
|
||||
*/
|
||||
Visitor.prototype.exit = function (breakdown) { };
|
||||
|
||||
/**
|
||||
* The `count` method is called when leaf nodes (reports whose breakdown is
|
||||
* by: "count") in the report tree are encountered.
|
||||
*
|
||||
* @param {Object} breakdown
|
||||
* The count breakdown for this report.
|
||||
*
|
||||
* @param {Object} report
|
||||
* The report generated by a breakdown by "count".
|
||||
*
|
||||
* @param {any|null} edge
|
||||
* The edge leading to this count report. The edge is null if we are
|
||||
* entering the root report.
|
||||
*/
|
||||
Visitor.prototype.count = function (breakdown, report, edge) { }
|
||||
|
||||
/*** getReportEdges *********************************************************/
|
||||
|
||||
const EDGES = Object.create(null);
|
||||
|
||||
EDGES.count = function (breakdown, report) {
|
||||
return [];
|
||||
};
|
||||
|
||||
EDGES.internalType = function (breakdown, report) {
|
||||
return Object.keys(report).map(key => ({
|
||||
edge: key,
|
||||
referent: report[key],
|
||||
breakdown: breakdown.then
|
||||
}));
|
||||
};
|
||||
|
||||
EDGES.objectClass = function (breakdown, report) {
|
||||
return Object.keys(report).map(key => ({
|
||||
edge: key,
|
||||
referent: report[key],
|
||||
breakdown: key === "other" ? breakdown.other : breakdown.then
|
||||
}));
|
||||
};
|
||||
|
||||
EDGES.coarseType = function (breakdown, report) {
|
||||
return [
|
||||
{ edge: "objects", referent: report.objects, breakdown: breakdown.objects },
|
||||
{ edge: "scripts", referent: report.scripts, breakdown: breakdown.scripts },
|
||||
{ edge: "strings", referent: report.strings, breakdown: breakdown.strings },
|
||||
{ edge: "other", referent: report.other, breakdown: breakdown.other },
|
||||
];
|
||||
};
|
||||
|
||||
EDGES.allocationStack = function (breakdown, report) {
|
||||
const edges = [];
|
||||
report.forEach((value, key) => {
|
||||
edges.push({
|
||||
edge: key,
|
||||
referent: value,
|
||||
breakdown: key === "noStack" ? breakdown.noStack : breakdown.then
|
||||
})
|
||||
});
|
||||
return edges;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the set of outgoing edges from `report` as specified by the given
|
||||
* breakdown.
|
||||
*
|
||||
* @param {Object} breakdown
|
||||
* The census breakdown.
|
||||
*
|
||||
* @param {Object} report
|
||||
* The census report.
|
||||
*/
|
||||
function getReportEdges(breakdown, report) {
|
||||
return EDGES[breakdown.by](breakdown, report);
|
||||
}
|
||||
exports.getReportEdges = getReportEdges;
|
||||
|
||||
/*** walk *******************************************************************/
|
||||
|
||||
function recursiveWalk(breakdown, edge, report, visitor) {
|
||||
if (breakdown.by === "count") {
|
||||
visitor.enter(breakdown, edge);
|
||||
visitor.count(breakdown, report, edge);
|
||||
visitor.exit(breakdown, edge);
|
||||
} else {
|
||||
visitor.enter(breakdown, edge);
|
||||
for (let { edge, referent, breakdown } of getReportEdges(breakdown, report)) {
|
||||
recursiveWalk(breakdown, edge, referent, visitor);
|
||||
}
|
||||
visitor.exit(breakdown, edge);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Walk the given `report` that was generated by taking a census with the
|
||||
* specified `breakdown`.
|
||||
*
|
||||
* @param {Object} breakdown
|
||||
* The census breakdown.
|
||||
*
|
||||
* @param {Object} report
|
||||
* The census report.
|
||||
*
|
||||
* @param {Visitor} visitor
|
||||
* The Visitor instance to call into while traversing.
|
||||
*/
|
||||
function walk(breakdown, report, visitor) {
|
||||
recursiveWalk(breakdown, null, report, visitor);
|
||||
};
|
||||
exports.walk = walk;
|
||||
|
||||
/*** diff *******************************************************************/
|
||||
|
||||
/**
|
||||
* Return true if the object is a Map, false otherwise. Works with Map objects
|
||||
* from other globals, unlike `instanceof`.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
function isMap(obj) {
|
||||
return Object.prototype.toString.call(obj) === "[object Map]";
|
||||
}
|
||||
|
||||
/**
|
||||
* A Visitor for computing the difference between the census report being
|
||||
* traversed and the given other census.
|
||||
*
|
||||
* @param {Object} otherCensus
|
||||
* The other census report.
|
||||
*/
|
||||
function DiffVisitor(otherCensus) {
|
||||
// The other census we are comparing against.
|
||||
this._otherCensus = otherCensus;
|
||||
|
||||
// Stack maintaining the current corresponding sub-report for the other
|
||||
// census we are comparing against.
|
||||
this._otherCensusStack = [];
|
||||
|
||||
// Stack maintaining the set of edges visited at each sub-report.
|
||||
this._edgesVisited = [new Set()];
|
||||
|
||||
// The final delta census. Valid only after traversal.
|
||||
this._results = null;
|
||||
|
||||
// Stack maintaining the results corresponding to each sub-report we are
|
||||
// currently traversing.
|
||||
this._resultsStack = [];
|
||||
}
|
||||
|
||||
DiffVisitor.prototype = Object.create(Visitor.prototype);
|
||||
|
||||
/**
|
||||
* Given a report and an outgoing edge, get the edge's referent.
|
||||
*/
|
||||
DiffVisitor.prototype._get = function (report, edge) {
|
||||
if (!report) {
|
||||
return undefined;
|
||||
}
|
||||
return isMap(report) ? report.get(edge) : report[edge];
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a report, an outgoing edge, and a value, set the edge's referent to
|
||||
* the given value.
|
||||
*/
|
||||
DiffVisitor.prototype._set = function (report, edge, val) {
|
||||
if (isMap(report)) {
|
||||
report.set(edge, val);
|
||||
} else {
|
||||
report[edge] = val;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @overrides Visitor.prototype.enter
|
||||
*/
|
||||
DiffVisitor.prototype.enter = function (breakdown, edge) {
|
||||
const isFirstTimeEntering = this._results === null;
|
||||
|
||||
const newResults = breakdown.by === "allocationStack" ? new Map() : {};
|
||||
let newOther;
|
||||
|
||||
if (!this._results) {
|
||||
// This is the first time we have entered a sub-report.
|
||||
this._results = newResults;
|
||||
newOther = this._otherCensus;
|
||||
} else {
|
||||
const topResults = this._resultsStack[this._resultsStack.length - 1];
|
||||
this._set(topResults, edge, newResults);
|
||||
|
||||
const topOther = this._otherCensusStack[this._otherCensusStack.length - 1];
|
||||
newOther = this._get(topOther, edge);
|
||||
}
|
||||
|
||||
this._resultsStack.push(newResults);
|
||||
this._otherCensusStack.push(newOther);
|
||||
|
||||
const visited = this._edgesVisited[this._edgesVisited.length - 1];
|
||||
visited.add(edge);
|
||||
this._edgesVisited.push(new Set());
|
||||
};
|
||||
|
||||
/**
|
||||
* @overrides Visitor.prototype.exit
|
||||
*/
|
||||
DiffVisitor.prototype.exit = function (breakdown) {
|
||||
// Find all the edges in the other census report that were not traversed and
|
||||
// add them to the results directly.
|
||||
const other = this._otherCensusStack[this._otherCensusStack.length - 1];
|
||||
if (other) {
|
||||
const visited = this._edgesVisited[this._edgesVisited.length - 1];
|
||||
const unvisited = getReportEdges(breakdown, other)
|
||||
.map(e => e.edge)
|
||||
.filter(e => !visited.has(e));
|
||||
const results = this._resultsStack[this._resultsStack.length - 1];
|
||||
for (let edge of unvisited) {
|
||||
this._set(results, edge, this._get(other, edge));
|
||||
}
|
||||
}
|
||||
|
||||
this._otherCensusStack.pop();
|
||||
this._resultsStack.pop();
|
||||
this._edgesVisited.pop();
|
||||
};
|
||||
|
||||
/**
|
||||
* @overrides Visitor.prototype.count
|
||||
*/
|
||||
DiffVisitor.prototype.count = function (breakdown, report, edge) {
|
||||
const other = this._otherCensusStack[this._otherCensusStack.length - 1];
|
||||
const results = this._resultsStack[this._resultsStack.length - 1];
|
||||
|
||||
if (other) {
|
||||
if (breakdown.count) {
|
||||
results.count = other.count - report.count;
|
||||
}
|
||||
if (breakdown.bytes) {
|
||||
results.bytes = other.bytes - report.bytes;
|
||||
}
|
||||
} else {
|
||||
if (breakdown.count) {
|
||||
results.count = -report.count;
|
||||
}
|
||||
if (breakdown.bytes) {
|
||||
results.bytes = -report.bytes;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the resulting report of the difference between the traversed census
|
||||
* report and the other census report.
|
||||
*
|
||||
* @returns {Object}
|
||||
* The delta census report.
|
||||
*/
|
||||
DiffVisitor.prototype.results = function () {
|
||||
if (!this._results) {
|
||||
throw new Error("Attempt to get results before computing diff!");
|
||||
}
|
||||
|
||||
if (this._resultsStack.length) {
|
||||
throw new Error("Attempt to get results while still computing diff!");
|
||||
}
|
||||
|
||||
return this._results;
|
||||
};
|
||||
|
||||
/**
|
||||
* Take the difference between two censuses. The resulting delta report
|
||||
* contains the number/size of things that are in the `endCensus` that are not
|
||||
* in the `startCensus`.
|
||||
*
|
||||
* @param {Object} breakdown
|
||||
* The breakdown used to generate both census reports.
|
||||
*
|
||||
* @param {Object} startCensus
|
||||
* The first census report.
|
||||
*
|
||||
* @param {Object} endCensus
|
||||
* The second census report.
|
||||
*
|
||||
* @returns {Object}
|
||||
* A delta report mirroring the structure of the two census reports
|
||||
* (as specified by the given breakdown).
|
||||
*/
|
||||
function diff(breakdown, startCensus, endCensus) {
|
||||
const visitor = new DiffVisitor(endCensus);
|
||||
walk(breakdown, startCensus, visitor);
|
||||
return visitor.results();
|
||||
};
|
||||
exports.diff = diff
|
|
@ -76,7 +76,7 @@ HeapAnalysesClient.prototype.readHeapSnapshot = function (snapshotFilePath) {
|
|||
* if `asTreeNode` is true.
|
||||
*/
|
||||
HeapAnalysesClient.prototype.takeCensus = function (snapshotFilePath,
|
||||
censusOptions={},
|
||||
censusOptions,
|
||||
requestOptions={}) {
|
||||
return this._worker.performTask("takeCensus", {
|
||||
snapshotFilePath,
|
||||
|
@ -84,3 +84,42 @@ HeapAnalysesClient.prototype.takeCensus = function (snapshotFilePath,
|
|||
requestOptions,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Request that the worker take a census on the heap snapshots with the given
|
||||
* paths and then return the difference between them. Both heap snapshots must
|
||||
* have already been read into memory by the worker (see `readHeapSnapshot`).
|
||||
*
|
||||
* @param {String} firstSnapshotFilePath
|
||||
* The first snapshot file path.
|
||||
*
|
||||
* @param {String} secondSnapshotFilePath
|
||||
* The second snapshot file path.
|
||||
*
|
||||
* @param {Object} censusOptions
|
||||
* A structured-cloneable object specifying the requested census's
|
||||
* breakdown. See the "takeCensus" section of
|
||||
* `js/src/doc/Debugger/Debugger.Memory.md` for detailed documentation.
|
||||
*
|
||||
* @param {Object} requestOptions
|
||||
* An object specifying options for this request.
|
||||
* - {Boolean} asTreeNode
|
||||
* Whether the resulting delta report should be converted to a census
|
||||
* tree node before returned. Defaults to false.
|
||||
*
|
||||
* @returns Promise<delta report|CensusTreeNode>
|
||||
* The delta report generated by diffing the two census reports, or a
|
||||
* CensusTreeNode generated from the delta report if
|
||||
* `requestOptions.asTreeNode` was true.
|
||||
*/
|
||||
HeapAnalysesClient.prototype.takeCensusDiff = function (firstSnapshotFilePath,
|
||||
secondSnapshotFilePath,
|
||||
censusOptions,
|
||||
requestOptions = {}) {
|
||||
return this._worker.performTask("takeCensusDiff", {
|
||||
firstSnapshotFilePath,
|
||||
secondSnapshotFilePath,
|
||||
censusOptions,
|
||||
requestOptions
|
||||
});
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
importScripts("resource://gre/modules/workers/require.js");
|
||||
importScripts("resource://gre/modules/devtools/shared/worker/helper.js");
|
||||
const { CensusTreeNode } = require("resource://gre/modules/devtools/shared/heapsnapshot/census-tree-node.js");
|
||||
const CensusUtils = require("resource://gre/modules/devtools/shared/heapsnapshot/CensusUtils.js");
|
||||
|
||||
// The set of HeapSnapshot instances this worker has read into memory. Keyed by
|
||||
// snapshot file path.
|
||||
|
@ -36,5 +37,35 @@ workerHelper.createTask(self, "takeCensus", ({ snapshotFilePath, censusOptions,
|
|||
}
|
||||
|
||||
let report = snapshots[snapshotFilePath].takeCensus(censusOptions);
|
||||
return requestOptions.asTreeNode ? new CensusTreeNode(censusOptions.breakdown, report) : report;
|
||||
return requestOptions.asTreeNode
|
||||
? new CensusTreeNode(censusOptions.breakdown, report)
|
||||
: report;
|
||||
});
|
||||
|
||||
/**
|
||||
* @see HeapAnalysesClient.prototype.takeCensusDiff
|
||||
*/
|
||||
workerHelper.createTask(self, "takeCensusDiff", request => {
|
||||
const {
|
||||
firstSnapshotFilePath,
|
||||
secondSnapshotFilePath,
|
||||
censusOptions,
|
||||
requestOptions
|
||||
} = request;
|
||||
|
||||
if (!snapshots[firstSnapshotFilePath]) {
|
||||
throw new Error(`No known heap snapshot for '${firstSnapshotFilePath}'`);
|
||||
}
|
||||
|
||||
if (!snapshots[secondSnapshotFilePath]) {
|
||||
throw new Error(`No known heap snapshot for '${secondSnapshotFilePath}'`);
|
||||
}
|
||||
|
||||
const first = snapshots[firstSnapshotFilePath].takeCensus(censusOptions);
|
||||
const second = snapshots[secondSnapshotFilePath].takeCensus(censusOptions);
|
||||
const delta = CensusUtils.diff(censusOptions.breakdown, first, second);
|
||||
|
||||
return requestOptions.asTreeNode
|
||||
? new CensusTreeNode(censusOptions.breakdown, delta)
|
||||
: delta;
|
||||
});
|
||||
|
|
|
@ -202,6 +202,12 @@ HeapSnapshot::saveNode(const protobuf::Node& node)
|
|||
return false;
|
||||
NodeId id = node.id();
|
||||
|
||||
// NodeIds are derived from pointers (at most 48 bits) and we rely on them
|
||||
// fitting into JS numbers (IEEE 754 doubles, can precisely store 53 bit
|
||||
// integers) despite storing them on disk as 64 bit integers.
|
||||
if (NS_WARN_IF(!JS::Value::isNumberRepresentable(id)))
|
||||
return false;
|
||||
|
||||
// Should only deserialize each node once.
|
||||
if (NS_WARN_IF(nodes.has(id)))
|
||||
return false;
|
||||
|
|
|
@ -45,6 +45,7 @@ FINAL_LIBRARY = 'xul'
|
|||
|
||||
DevToolsModules(
|
||||
'census-tree-node.js',
|
||||
'CensusUtils.js',
|
||||
'HeapAnalysesClient.js',
|
||||
'HeapAnalysesWorker.js',
|
||||
'HeapSnapshotFileUtils.js',
|
||||
|
|
|
@ -21,6 +21,7 @@ const HeapAnalysesClient =
|
|||
require("devtools/shared/heapsnapshot/HeapAnalysesClient");
|
||||
const Services = require("Services");
|
||||
const { CensusTreeNode } = require("devtools/shared/heapsnapshot/census-tree-node");
|
||||
const CensusUtils = require("devtools/shared/heapsnapshot/CensusUtils");
|
||||
|
||||
// Always log packets when running tests. runxpcshelltests.py will throw
|
||||
// the output away anyway, unless you give it the --verbose flag.
|
||||
|
@ -149,7 +150,96 @@ function saveHeapSnapshotAndTakeCensus(dbg=null, censusOptions=undefined) {
|
|||
return snapshot.takeCensus(censusOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that creating a CensusTreeNode from the given `report` with the
|
||||
* specified `breakdown` creates the given `expected` CensusTreeNode.
|
||||
*
|
||||
* @param {Object} breakdown
|
||||
* The census breakdown.
|
||||
*
|
||||
* @param {Object} report
|
||||
* The census report.
|
||||
*
|
||||
* @param {Object} expected
|
||||
* The expected CensusTreeNode result.
|
||||
*
|
||||
* @param {String} assertion
|
||||
* The assertion message.
|
||||
*/
|
||||
function compareCensusViewData (breakdown, report, expected, assertion) {
|
||||
let data = new CensusTreeNode(breakdown, report);
|
||||
equal(JSON.stringify(data), JSON.stringify(expected), assertion);
|
||||
}
|
||||
|
||||
// Deep structural equivalence that can handle Map objects in addition to plain
|
||||
// objects.
|
||||
function assertStructurallyEquivalent(actual, expected, path="root") {
|
||||
equal(typeof actual, typeof expected, `${path}: typeof should be the same`);
|
||||
|
||||
if (actual && typeof actual === "object") {
|
||||
const actualProtoString = Object.prototype.toString.call(actual);
|
||||
const expectedProtoString = Object.prototype.toString.call(expected);
|
||||
equal(actualProtoString, expectedProtoString,
|
||||
`${path}: Object.prototype.toString.call() should be the same`);
|
||||
|
||||
if (actualProtoString === "[object Map]") {
|
||||
const expectedKeys = new Set([...expected.keys()]);
|
||||
|
||||
for (let key of actual.keys()) {
|
||||
ok(expectedKeys.has(key),
|
||||
`${path}: every key in actual should exist in expected: ${String(key).slice(0, 10)}`);
|
||||
expectedKeys.delete(key);
|
||||
|
||||
assertStructurallyEquivalent(actual.get(key), expected.get(key),
|
||||
path + ".get(" + String(key).slice(0, 20) + ")");
|
||||
}
|
||||
|
||||
equal(expectedKeys.size, 0,
|
||||
`${path}: every key in expected should also exist in actual`);
|
||||
} else {
|
||||
const expectedKeys = new Set(Object.keys(expected));
|
||||
|
||||
for (let key of Object.keys(actual)) {
|
||||
ok(expectedKeys.has(key),
|
||||
`${path}: every key in actual should exist in expected: ${key}`);
|
||||
expectedKeys.delete(key);
|
||||
|
||||
assertStructurallyEquivalent(actual[key], expected[key], path + "." + key);
|
||||
}
|
||||
|
||||
equal(expectedKeys.size, 0,
|
||||
`${path}: every key in expected should also exist in actual`);
|
||||
}
|
||||
} else {
|
||||
equal(actual, expected, `${path}: primitives should be equal`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that creating a diff of the `first` and `second` census reports
|
||||
* creates the `expected` delta-report.
|
||||
*
|
||||
* @param {Object} breakdown
|
||||
* The census breakdown.
|
||||
*
|
||||
* @param {Object} first
|
||||
* The first census report.
|
||||
*
|
||||
* @param {Object} second
|
||||
* The second census report.
|
||||
*
|
||||
* @param {Object} expected
|
||||
* The expected delta-report.
|
||||
*/
|
||||
function assertDiff(breakdown, first, second, expected) {
|
||||
dumpn("Diffing census reports:");
|
||||
dumpn("Breakdown: " + JSON.stringify(breakdown, null, 4));
|
||||
dumpn("First census report: " + JSON.stringify(first, null, 4));
|
||||
dumpn("Second census report: " + JSON.stringify(second, null, 4));
|
||||
dumpn("Expected delta-report: " + JSON.stringify(expected, null, 4));
|
||||
|
||||
const actual = CensusUtils.diff(breakdown, first, second);
|
||||
dumpn("Actual delta-report: " + JSON.stringify(actual, null, 4));
|
||||
|
||||
assertStructurallyEquivalent(actual, expected);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that the HeapAnalyses{Client,Worker} can take diffs between censuses.
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
const BREAKDOWN = {
|
||||
by: "objectClass",
|
||||
then: { by: "count", count: true, bytes: false },
|
||||
other: { by: "count", count: true, bytes: false },
|
||||
};
|
||||
|
||||
add_task(function* () {
|
||||
const client = new HeapAnalysesClient();
|
||||
|
||||
const markers = [allocationMarker()];
|
||||
|
||||
const firstSnapshotFilePath = saveNewHeapSnapshot();
|
||||
|
||||
// Allocate and hold an additional AllocationMarker object so we can see it in
|
||||
// the next heap snapshot.
|
||||
markers.push(allocationMarker());
|
||||
|
||||
const secondSnapshotFilePath = saveNewHeapSnapshot();
|
||||
|
||||
yield client.readHeapSnapshot(firstSnapshotFilePath);
|
||||
yield client.readHeapSnapshot(secondSnapshotFilePath);
|
||||
ok(true, "Should have read both heap snapshot files");
|
||||
|
||||
const delta = yield client.takeCensusDiff(firstSnapshotFilePath,
|
||||
secondSnapshotFilePath,
|
||||
{ breakdown: BREAKDOWN });
|
||||
|
||||
equal(delta.AllocationMarker.count, 1,
|
||||
"There exists one new AllocationMarker in the second heap snapshot");
|
||||
|
||||
const deltaTreeNode = yield client.takeCensusDiff(firstSnapshotFilePath,
|
||||
secondSnapshotFilePath,
|
||||
{ breakdown: BREAKDOWN },
|
||||
{ asTreeNode: true });
|
||||
|
||||
compareCensusViewData(BREAKDOWN, delta, deltaTreeNode,
|
||||
"Returning delta-census as a tree node represents same data as the report");
|
||||
|
||||
client.destroy();
|
||||
});
|
|
@ -0,0 +1,74 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test diffing census reports of breakdown by "internalType".
|
||||
|
||||
const BREAKDOWN = {
|
||||
by: "internalType",
|
||||
then: { by: "count", count: true, bytes: true }
|
||||
};
|
||||
|
||||
const REPORT1 = {
|
||||
"JSObject": {
|
||||
"count": 10,
|
||||
"bytes": 100,
|
||||
},
|
||||
"js::Shape": {
|
||||
"count": 50,
|
||||
"bytes": 500,
|
||||
},
|
||||
"JSString": {
|
||||
"count": 0,
|
||||
"bytes": 0,
|
||||
},
|
||||
"js::LazyScript": {
|
||||
"count": 1,
|
||||
"bytes": 10,
|
||||
},
|
||||
};
|
||||
|
||||
const REPORT2 = {
|
||||
"JSObject": {
|
||||
"count": 11,
|
||||
"bytes": 110,
|
||||
},
|
||||
"js::Shape": {
|
||||
"count": 51,
|
||||
"bytes": 510,
|
||||
},
|
||||
"JSString": {
|
||||
"count": 1,
|
||||
"bytes": 1,
|
||||
},
|
||||
"js::BaseShape": {
|
||||
"count": 1,
|
||||
"bytes": 42,
|
||||
},
|
||||
};
|
||||
|
||||
const EXPECTED = {
|
||||
"JSObject": {
|
||||
"count": 1,
|
||||
"bytes": 10,
|
||||
},
|
||||
"js::Shape": {
|
||||
"count": 1,
|
||||
"bytes": 10,
|
||||
},
|
||||
"JSString": {
|
||||
"count": 1,
|
||||
"bytes": 1,
|
||||
},
|
||||
"js::LazyScript": {
|
||||
"count": -1,
|
||||
"bytes": -10,
|
||||
},
|
||||
"js::BaseShape": {
|
||||
"count": 1,
|
||||
"bytes": 42,
|
||||
},
|
||||
};
|
||||
|
||||
function run_test() {
|
||||
assertDiff(BREAKDOWN, REPORT1, REPORT2, EXPECTED);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test diffing census reports of breakdown by "count".
|
||||
|
||||
const BREAKDOWN = { by: "count", count: true, bytes: true };
|
||||
|
||||
const REPORT1 = {
|
||||
"count": 10,
|
||||
"bytes": 100,
|
||||
};
|
||||
|
||||
const REPORT2 = {
|
||||
"count": 11,
|
||||
"bytes": 110,
|
||||
};
|
||||
|
||||
const EXPECTED = {
|
||||
"count": 1,
|
||||
"bytes": 10,
|
||||
};
|
||||
|
||||
function run_test() {
|
||||
assertDiff(BREAKDOWN, REPORT1, REPORT2, EXPECTED);
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test diffing census reports of breakdown by "coarseType".
|
||||
|
||||
const BREAKDOWN = {
|
||||
by: "coarseType",
|
||||
objects: { by: "count", count: true, bytes: true },
|
||||
scripts: { by: "count", count: true, bytes: true },
|
||||
strings: { by: "count", count: true, bytes: true },
|
||||
other: { by: "count", count: true, bytes: true },
|
||||
};
|
||||
|
||||
const REPORT1 = {
|
||||
objects: {
|
||||
count: 1,
|
||||
bytes: 10,
|
||||
},
|
||||
scripts: {
|
||||
count: 1,
|
||||
bytes: 10,
|
||||
},
|
||||
strings: {
|
||||
count: 1,
|
||||
bytes: 10,
|
||||
},
|
||||
other: {
|
||||
count: 3,
|
||||
bytes: 30,
|
||||
},
|
||||
};
|
||||
|
||||
const REPORT2 = {
|
||||
objects: {
|
||||
count: 1,
|
||||
bytes: 10,
|
||||
},
|
||||
scripts: {
|
||||
count: 0,
|
||||
bytes: 0,
|
||||
},
|
||||
strings: {
|
||||
count: 2,
|
||||
bytes: 20,
|
||||
},
|
||||
other: {
|
||||
count: 4,
|
||||
bytes: 40,
|
||||
},
|
||||
};
|
||||
|
||||
const EXPECTED = {
|
||||
objects: {
|
||||
count: 0,
|
||||
bytes: 0,
|
||||
},
|
||||
scripts: {
|
||||
count: -1,
|
||||
bytes: -10,
|
||||
},
|
||||
strings: {
|
||||
count: 1,
|
||||
bytes: 10,
|
||||
},
|
||||
other: {
|
||||
count: 1,
|
||||
bytes: 10,
|
||||
},
|
||||
};
|
||||
|
||||
function run_test() {
|
||||
assertDiff(BREAKDOWN, REPORT1, REPORT2, EXPECTED);
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test diffing census reports of breakdown by "objectClass".
|
||||
|
||||
const BREAKDOWN = {
|
||||
by: "objectClass",
|
||||
then: { by: "count", count: true, bytes: true },
|
||||
other: { by: "count", count: true, bytes: true },
|
||||
};
|
||||
|
||||
const REPORT1 = {
|
||||
"Array": {
|
||||
count: 1,
|
||||
bytes: 100,
|
||||
},
|
||||
"Function": {
|
||||
count: 10,
|
||||
bytes: 10,
|
||||
},
|
||||
"other": {
|
||||
count: 10,
|
||||
bytes: 100,
|
||||
}
|
||||
};
|
||||
|
||||
const REPORT2 = {
|
||||
"Object": {
|
||||
count: 1,
|
||||
bytes: 100,
|
||||
},
|
||||
"Function": {
|
||||
count: 20,
|
||||
bytes: 20,
|
||||
},
|
||||
"other": {
|
||||
count: 10,
|
||||
bytes: 100,
|
||||
}
|
||||
};
|
||||
|
||||
const EXPECTED = {
|
||||
"Array": {
|
||||
count: -1,
|
||||
bytes: -100,
|
||||
},
|
||||
"Function": {
|
||||
count: 10,
|
||||
bytes: 10,
|
||||
},
|
||||
"other": {
|
||||
count: 0,
|
||||
bytes: 0,
|
||||
},
|
||||
"Object": {
|
||||
count: 1,
|
||||
bytes: 100,
|
||||
},
|
||||
};
|
||||
|
||||
function run_test() {
|
||||
assertDiff(BREAKDOWN, REPORT1, REPORT2, EXPECTED);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test diffing census reports of breakdown by "allocationStack".
|
||||
|
||||
const BREAKDOWN = {
|
||||
by: "allocationStack",
|
||||
then: { by: "count", count: true, bytes: true },
|
||||
noStack: { by: "count", count: true, bytes: true },
|
||||
};
|
||||
|
||||
const stack1 = saveStack();
|
||||
const stack2 = saveStack();
|
||||
const stack3 = saveStack();
|
||||
|
||||
const REPORT1 = new Map([
|
||||
[stack1, { "count": 10, "bytes": 100 }],
|
||||
[stack2, { "count": 1, "bytes": 10 }],
|
||||
]);
|
||||
|
||||
const REPORT2 = new Map([
|
||||
[stack2, { "count": 10, "bytes": 100 }],
|
||||
[stack3, { "count": 1, "bytes": 10 }],
|
||||
]);
|
||||
|
||||
const EXPECTED = new Map([
|
||||
[stack1, { "count": -10, "bytes": -100 }],
|
||||
[stack2, { "count": 9, "bytes": 90 }],
|
||||
[stack3, { "count": 1, "bytes": 10 }],
|
||||
]);
|
||||
|
||||
function run_test() {
|
||||
assertDiff(BREAKDOWN, REPORT1, REPORT2, EXPECTED);
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test diffing census reports of a "complex" and "realistic" breakdown.
|
||||
|
||||
const BREAKDOWN = {
|
||||
by: "coarseType",
|
||||
objects: {
|
||||
by: "allocationStack",
|
||||
then: {
|
||||
by: "objectClass",
|
||||
then: { by: "count", count: false, bytes: true },
|
||||
other: { by: "count", count: false, bytes: true }
|
||||
},
|
||||
noStack: {
|
||||
by: "objectClass",
|
||||
then: { by: "count", count: false, bytes: true },
|
||||
other: { by: "count", count: false, bytes: true }
|
||||
}
|
||||
},
|
||||
strings: {
|
||||
by: "internalType",
|
||||
then: { by: "count", count: false, bytes: true }
|
||||
},
|
||||
scripts: {
|
||||
by: "internalType",
|
||||
then: { by: "count", count: false, bytes: true }
|
||||
},
|
||||
other: {
|
||||
by: "internalType",
|
||||
then: { by: "count", count: false, bytes: true }
|
||||
},
|
||||
};
|
||||
|
||||
const stack1 = saveStack();
|
||||
const stack2 = saveStack();
|
||||
const stack3 = saveStack();
|
||||
|
||||
const REPORT1 = {
|
||||
objects: new Map([
|
||||
[stack1, { Function: { bytes: 1 },
|
||||
Object: { bytes: 2 },
|
||||
other: { bytes: 0 },
|
||||
}],
|
||||
[stack2, { Array: { bytes: 3 },
|
||||
Date: { bytes: 4 },
|
||||
other: { bytes: 0 },
|
||||
}],
|
||||
["noStack", { Object: { bytes: 3 }}],
|
||||
]),
|
||||
strings: {
|
||||
JSAtom: { bytes: 10 },
|
||||
JSLinearString: { bytes: 5 },
|
||||
},
|
||||
scripts: {
|
||||
JSScript: { bytes: 1 },
|
||||
"js::jit::JitCode": { bytes: 2 },
|
||||
},
|
||||
other: {
|
||||
"mozilla::dom::Thing": { bytes: 1 },
|
||||
}
|
||||
};
|
||||
|
||||
const REPORT2 = {
|
||||
objects: new Map([
|
||||
[stack2, { Array: { bytes: 1 },
|
||||
Date: { bytes: 2 },
|
||||
other: { bytes: 3 },
|
||||
}],
|
||||
[stack3, { Function: { bytes: 1 },
|
||||
Object: { bytes: 2 },
|
||||
other: { bytes: 0 },
|
||||
}],
|
||||
["noStack", { Object: { bytes: 3 }}],
|
||||
]),
|
||||
strings: {
|
||||
JSAtom: { bytes: 5 },
|
||||
JSLinearString: { bytes: 10 },
|
||||
},
|
||||
scripts: {
|
||||
JSScript: { bytes: 2 },
|
||||
"js::LazyScript": { bytes: 42 },
|
||||
"js::jit::JitCode": { bytes: 1 },
|
||||
},
|
||||
other: {
|
||||
"mozilla::dom::OtherThing": { bytes: 1 },
|
||||
}
|
||||
};
|
||||
|
||||
const EXPECTED = {
|
||||
"objects": new Map([
|
||||
[stack1, { Function: { bytes: -1 },
|
||||
Object: { bytes: -2 },
|
||||
other: { bytes: 0 },
|
||||
}],
|
||||
[stack2, { Array: { bytes: -2 },
|
||||
Date: { bytes: -2 },
|
||||
other: { bytes: 3 },
|
||||
}],
|
||||
[stack3, { Function: { bytes: 1 },
|
||||
Object: { bytes: 2 },
|
||||
other: { bytes: 0 },
|
||||
}],
|
||||
["noStack", { Object: { bytes: 0 }}],
|
||||
]),
|
||||
"scripts": {
|
||||
"JSScript": {
|
||||
"bytes": 1
|
||||
},
|
||||
"js::jit::JitCode": {
|
||||
"bytes": -1
|
||||
},
|
||||
"js::LazyScript": {
|
||||
"bytes": 42
|
||||
}
|
||||
},
|
||||
"strings": {
|
||||
"JSAtom": {
|
||||
"bytes": -5
|
||||
},
|
||||
"JSLinearString": {
|
||||
"bytes": 5
|
||||
}
|
||||
},
|
||||
"other": {
|
||||
"mozilla::dom::Thing": {
|
||||
"bytes": -1
|
||||
},
|
||||
"mozilla::dom::OtherThing": {
|
||||
"bytes": 1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function run_test() {
|
||||
assertDiff(BREAKDOWN, REPORT1, REPORT2, EXPECTED);
|
||||
}
|
|
@ -9,10 +9,17 @@ support-files =
|
|||
heap-snapshot-worker.js
|
||||
Match.jsm
|
||||
|
||||
[test_census_diff_01.js]
|
||||
[test_census_diff_02.js]
|
||||
[test_census_diff_03.js]
|
||||
[test_census_diff_04.js]
|
||||
[test_census_diff_05.js]
|
||||
[test_census_diff_06.js]
|
||||
[test_census-tree-node-01.js]
|
||||
[test_census-tree-node-02.js]
|
||||
[test_census-tree-node-03.js]
|
||||
[test_HeapAnalyses_readHeapSnapshot_01.js]
|
||||
[test_HeapAnalyses_takeCensusDiff_01.js]
|
||||
[test_HeapAnalyses_takeCensus_01.js]
|
||||
[test_HeapAnalyses_takeCensus_02.js]
|
||||
[test_HeapAnalyses_takeCensus_03.js]
|
||||
|
|
|
@ -35,6 +35,8 @@ namespace {
|
|||
static BluetoothA2dpInterface* sBtA2dpInterface;
|
||||
} // namespace
|
||||
|
||||
const int BluetoothA2dpManager::MAX_NUM_CLIENTS = 1;
|
||||
|
||||
NS_IMETHODIMP
|
||||
BluetoothA2dpManager::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
|
@ -86,40 +88,52 @@ AvStatusToSinkString(BluetoothA2dpConnectionState aState, nsAString& aString)
|
|||
}
|
||||
}
|
||||
|
||||
class BluetoothA2dpManager::InitResultHandler final
|
||||
: public BluetoothA2dpResultHandler
|
||||
class BluetoothA2dpManager::RegisterModuleResultHandler final
|
||||
: public BluetoothSetupResultHandler
|
||||
{
|
||||
public:
|
||||
InitResultHandler(BluetoothProfileResultHandler* aRes)
|
||||
: mRes(aRes)
|
||||
RegisterModuleResultHandler(BluetoothA2dpInterface* aInterface,
|
||||
BluetoothProfileResultHandler* aRes)
|
||||
: mInterface(aInterface)
|
||||
, mRes(aRes)
|
||||
{ }
|
||||
|
||||
void OnError(BluetoothStatus aStatus) override
|
||||
{
|
||||
BT_WARNING("BluetoothA2dpInterface::Init failed: %d",
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
BT_WARNING("BluetoothSetupInterface::RegisterModule failed for A2DP: %d",
|
||||
(int)aStatus);
|
||||
|
||||
mInterface->SetNotificationHandler(nullptr);
|
||||
|
||||
if (mRes) {
|
||||
mRes->OnError(NS_ERROR_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void Init() override
|
||||
void RegisterModule() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
sBtA2dpInterface = mInterface;
|
||||
|
||||
if (mRes) {
|
||||
mRes->Init();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
BluetoothA2dpInterface* mInterface;
|
||||
nsRefPtr<BluetoothProfileResultHandler> mRes;
|
||||
};
|
||||
|
||||
class BluetoothA2dpManager::OnErrorProfileResultHandlerRunnable final
|
||||
class BluetoothA2dpManager::InitProfileResultHandlerRunnable final
|
||||
: public nsRunnable
|
||||
{
|
||||
public:
|
||||
OnErrorProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
|
||||
nsresult aRv)
|
||||
InitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
|
||||
nsresult aRv)
|
||||
: mRes(aRes)
|
||||
, mRv(aRv)
|
||||
{
|
||||
|
@ -128,7 +142,13 @@ public:
|
|||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
mRes->OnError(mRv);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (NS_SUCCEEDED(mRv)) {
|
||||
mRes->Init();
|
||||
} else {
|
||||
mRes->OnError(mRv);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -147,32 +167,64 @@ private:
|
|||
void
|
||||
BluetoothA2dpManager::InitA2dpInterface(BluetoothProfileResultHandler* aRes)
|
||||
{
|
||||
BluetoothInterface* btInf = BluetoothInterface::GetInstance();
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (sBtA2dpInterface) {
|
||||
BT_LOGR("Bluetooth A2DP interface is already initalized.");
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new InitProfileResultHandlerRunnable(aRes, NS_OK);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch A2DP Init runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto btInf = BluetoothInterface::GetInstance();
|
||||
|
||||
if (NS_WARN_IF(!btInf)) {
|
||||
// If there's no HFP interface, we dispatch a runnable
|
||||
// If there's no Bluetooth interface, we dispatch a runnable
|
||||
// that calls the profile result handler.
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new OnErrorProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch HFP OnError runnable");
|
||||
BT_LOGR("Failed to dispatch A2DP OnError runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sBtA2dpInterface = btInf->GetBluetoothA2dpInterface();
|
||||
if (NS_WARN_IF(!sBtA2dpInterface)) {
|
||||
// If there's no HFP interface, we dispatch a runnable
|
||||
auto setupInterface = btInf->GetBluetoothSetupInterface();
|
||||
|
||||
if (NS_WARN_IF(!setupInterface)) {
|
||||
// If there's no Setup interface, we dispatch a runnable
|
||||
// that calls the profile result handler.
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new OnErrorProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch HFP OnError runnable");
|
||||
BT_LOGR("Failed to dispatch A2DP OnError runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
BluetoothA2dpManager* a2dpManager = BluetoothA2dpManager::Get();
|
||||
sBtA2dpInterface->Init(a2dpManager, new InitResultHandler(aRes));
|
||||
auto a2dpInterface = btInf->GetBluetoothA2dpInterface();
|
||||
|
||||
if (NS_WARN_IF(!a2dpInterface)) {
|
||||
// If there's no A2DP interface, we dispatch a runnable
|
||||
// that calls the profile result handler.
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch A2DP OnError runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Set notification handler _before_ registering the module. It could
|
||||
// happen that we receive notifications, before the result handler runs.
|
||||
a2dpInterface->SetNotificationHandler(BluetoothA2dpManager::Get());
|
||||
|
||||
setupInterface->RegisterModule(
|
||||
SETUP_SERVICE_ID_A2DP, 0, MAX_NUM_CLIENTS,
|
||||
new RegisterModuleResultHandler(a2dpInterface, aRes));
|
||||
}
|
||||
|
||||
BluetoothA2dpManager::~BluetoothA2dpManager()
|
||||
|
@ -227,19 +279,22 @@ BluetoothA2dpManager::Get()
|
|||
return sBluetoothA2dpManager;
|
||||
}
|
||||
|
||||
class BluetoothA2dpManager::CleanupResultHandler final
|
||||
: public BluetoothA2dpResultHandler
|
||||
class BluetoothA2dpManager::UnregisterModuleResultHandler final
|
||||
: public BluetoothSetupResultHandler
|
||||
{
|
||||
public:
|
||||
CleanupResultHandler(BluetoothProfileResultHandler* aRes)
|
||||
UnregisterModuleResultHandler(BluetoothProfileResultHandler* aRes)
|
||||
: mRes(aRes)
|
||||
{ }
|
||||
|
||||
void OnError(BluetoothStatus aStatus) override
|
||||
{
|
||||
BT_WARNING("BluetoothA2dpInterface::Cleanup failed: %d",
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
BT_WARNING("BluetoothSetupInterface::UnregisterModule failed for A2DP: %d",
|
||||
(int)aStatus);
|
||||
|
||||
sBtA2dpInterface->SetNotificationHandler(nullptr);
|
||||
sBtA2dpInterface = nullptr;
|
||||
|
||||
if (mRes) {
|
||||
|
@ -247,8 +302,11 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void Cleanup() override
|
||||
void UnregisterModule() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
sBtA2dpInterface->SetNotificationHandler(nullptr);
|
||||
sBtA2dpInterface = nullptr;
|
||||
|
||||
if (mRes) {
|
||||
|
@ -260,26 +318,33 @@ private:
|
|||
nsRefPtr<BluetoothProfileResultHandler> mRes;
|
||||
};
|
||||
|
||||
class BluetoothA2dpManager::CleanupResultHandlerRunnable final
|
||||
class BluetoothA2dpManager::DeinitProfileResultHandlerRunnable final
|
||||
: public nsRunnable
|
||||
{
|
||||
public:
|
||||
CleanupResultHandlerRunnable(BluetoothProfileResultHandler* aRes)
|
||||
DeinitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
|
||||
nsresult aRv)
|
||||
: mRes(aRes)
|
||||
{ }
|
||||
, mRv(aRv)
|
||||
{
|
||||
MOZ_ASSERT(mRes);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
sBtA2dpInterface = nullptr;
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mRes) {
|
||||
if (NS_SUCCEEDED(mRv)) {
|
||||
mRes->Deinit();
|
||||
} else {
|
||||
mRes->OnError(mRv);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<BluetoothProfileResultHandler> mRes;
|
||||
nsresult mRv;
|
||||
};
|
||||
|
||||
// static
|
||||
|
@ -288,16 +353,45 @@ BluetoothA2dpManager::DeinitA2dpInterface(BluetoothProfileResultHandler* aRes)
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (sBtA2dpInterface) {
|
||||
sBtA2dpInterface->Cleanup(new CleanupResultHandler(aRes));
|
||||
} else if (aRes) {
|
||||
// We dispatch a runnable here to make the profile resource handler
|
||||
// behave as if A2DP was initialized.
|
||||
nsRefPtr<nsRunnable> r = new CleanupResultHandlerRunnable(aRes);
|
||||
if (!sBtA2dpInterface) {
|
||||
BT_LOGR("Bluetooth A2DP interface has not been initalized.");
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new DeinitProfileResultHandlerRunnable(aRes, NS_OK);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch cleanup-result-handler runnable");
|
||||
BT_LOGR("Failed to dispatch A2DP Deinit runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto btInf = BluetoothInterface::GetInstance();
|
||||
|
||||
if (NS_WARN_IF(!btInf)) {
|
||||
// If there's no backend interface, we dispatch a runnable
|
||||
// that calls the profile result handler.
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch A2DP OnError runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto setupInterface = btInf->GetBluetoothSetupInterface();
|
||||
|
||||
if (NS_WARN_IF(!setupInterface)) {
|
||||
// If there's no Setup interface, we dispatch a runnable
|
||||
// that calls the profile result handler.
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch A2DP OnError runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
setupInterface->UnregisterModule(
|
||||
SETUP_SERVICE_ID_A2DP,
|
||||
new UnregisterModuleResultHandler(aRes));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -17,6 +17,8 @@ class BluetoothA2dpManager : public BluetoothProfileManagerBase
|
|||
, public BluetoothA2dpNotificationHandler
|
||||
{
|
||||
public:
|
||||
static const int MAX_NUM_CLIENTS;
|
||||
|
||||
BT_DECL_PROFILE_MGR_BASE
|
||||
virtual void GetName(nsACString& aName)
|
||||
{
|
||||
|
@ -47,12 +49,12 @@ protected:
|
|||
virtual ~BluetoothA2dpManager();
|
||||
|
||||
private:
|
||||
class CleanupResultHandler;
|
||||
class CleanupResultHandlerRunnable;
|
||||
class ConnectResultHandler;
|
||||
class DeinitProfileResultHandlerRunnable;
|
||||
class DisconnectResultHandler;
|
||||
class InitResultHandler;
|
||||
class OnErrorProfileResultHandlerRunnable;
|
||||
class InitProfileResultHandlerRunnable;
|
||||
class RegisterModuleResultHandler;
|
||||
class UnregisterModuleResultHandler;
|
||||
|
||||
BluetoothA2dpManager();
|
||||
|
||||
|
|
|
@ -35,6 +35,8 @@ namespace {
|
|||
static BluetoothAvrcpInterface* sBtAvrcpInterface;
|
||||
} // namespace
|
||||
|
||||
const int BluetoothAvrcpManager::MAX_NUM_CLIENTS = 1;
|
||||
|
||||
/*
|
||||
* This function maps attribute id and returns corresponding values
|
||||
*/
|
||||
|
@ -118,18 +120,27 @@ BluetoothAvrcpManager::Reset()
|
|||
mPlayStatus = ControlPlayStatus::PLAYSTATUS_STOPPED;
|
||||
}
|
||||
|
||||
class BluetoothAvrcpManager::InitResultHandler final
|
||||
: public BluetoothAvrcpResultHandler
|
||||
class BluetoothAvrcpManager::RegisterModuleResultHandler final
|
||||
: public BluetoothSetupResultHandler
|
||||
{
|
||||
public:
|
||||
InitResultHandler(BluetoothProfileResultHandler* aRes)
|
||||
: mRes(aRes)
|
||||
{ }
|
||||
RegisterModuleResultHandler(BluetoothAvrcpInterface* aInterface,
|
||||
BluetoothProfileResultHandler* aRes)
|
||||
: mInterface(aInterface)
|
||||
, mRes(aRes)
|
||||
{
|
||||
MOZ_ASSERT(mInterface);
|
||||
}
|
||||
|
||||
void OnError(BluetoothStatus aStatus) override
|
||||
{
|
||||
BT_WARNING("BluetoothAvrcpInterface::Init failed: %d",
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
BT_WARNING("BluetoothSetupInterface::RegisterModule failed for AVRCP: %d",
|
||||
(int)aStatus);
|
||||
|
||||
mInterface->SetNotificationHandler(nullptr);
|
||||
|
||||
if (mRes) {
|
||||
if (aStatus == STATUS_UNSUPPORTED) {
|
||||
/* Not all versions of Bluedroid support AVRCP. So if the
|
||||
|
@ -143,23 +154,28 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void Init() override
|
||||
void RegisterModule() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
sBtAvrcpInterface = mInterface;
|
||||
|
||||
if (mRes) {
|
||||
mRes->Init();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
BluetoothAvrcpInterface* mInterface;
|
||||
nsRefPtr<BluetoothProfileResultHandler> mRes;
|
||||
};
|
||||
|
||||
class BluetoothAvrcpManager::OnErrorProfileResultHandlerRunnable final
|
||||
class BluetoothAvrcpManager::InitProfileResultHandlerRunnable final
|
||||
: public nsRunnable
|
||||
{
|
||||
public:
|
||||
OnErrorProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
|
||||
nsresult aRv)
|
||||
InitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
|
||||
nsresult aRv)
|
||||
: mRes(aRes)
|
||||
, mRv(aRv)
|
||||
{
|
||||
|
@ -168,7 +184,13 @@ public:
|
|||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
mRes->OnError(mRv);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (NS_SUCCEEDED(mRv)) {
|
||||
mRes->Init();
|
||||
} else {
|
||||
mRes->OnError(mRv);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -184,32 +206,64 @@ private:
|
|||
void
|
||||
BluetoothAvrcpManager::InitAvrcpInterface(BluetoothProfileResultHandler* aRes)
|
||||
{
|
||||
BluetoothInterface* btInf = BluetoothInterface::GetInstance();
|
||||
if (NS_WARN_IF(!btInf)) {
|
||||
// If there's no HFP interface, we dispatch a runnable
|
||||
// that calls the profile result handler.
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (sBtAvrcpInterface) {
|
||||
BT_LOGR("Bluetooth AVRCP interface is already initalized.");
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new OnErrorProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
new InitProfileResultHandlerRunnable(aRes, NS_OK);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch HFP OnError runnable");
|
||||
BT_LOGR("Failed to dispatch AVRCP Init runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sBtAvrcpInterface = btInf->GetBluetoothAvrcpInterface();
|
||||
if (NS_WARN_IF(!sBtAvrcpInterface)) {
|
||||
auto btInf = BluetoothInterface::GetInstance();
|
||||
|
||||
if (NS_WARN_IF(!btInf)) {
|
||||
// If there's no Bluetooth interface, we dispatch a runnable
|
||||
// that calls the profile result handler.
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch AVRCP OnError runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto setupInterface = btInf->GetBluetoothSetupInterface();
|
||||
|
||||
if (NS_WARN_IF(!setupInterface)) {
|
||||
// If there's no Setup interface, we dispatch a runnable
|
||||
// that calls the profile result handler.
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch AVRCP OnError runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto avrcpInterface = btInf->GetBluetoothAvrcpInterface();
|
||||
|
||||
if (NS_WARN_IF(!avrcpInterface)) {
|
||||
// If there's no AVRCP interface, we dispatch a runnable
|
||||
// that calls the profile result handler.
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new OnErrorProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch HFP OnError runnable");
|
||||
BT_LOGR("Failed to dispatch AVRCP OnError runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
BluetoothAvrcpManager* avrcpManager = BluetoothAvrcpManager::Get();
|
||||
sBtAvrcpInterface->Init(avrcpManager, new InitResultHandler(aRes));
|
||||
// Set notification handler _before_ registering the module. It could
|
||||
// happen that we receive notifications, before the result handler runs.
|
||||
avrcpInterface->SetNotificationHandler(BluetoothAvrcpManager::Get());
|
||||
|
||||
setupInterface->RegisterModule(
|
||||
SETUP_SERVICE_ID_AVRCP, 0, MAX_NUM_CLIENTS,
|
||||
new RegisterModuleResultHandler(avrcpInterface, aRes));
|
||||
}
|
||||
|
||||
BluetoothAvrcpManager::~BluetoothAvrcpManager()
|
||||
|
@ -245,37 +299,36 @@ BluetoothAvrcpManager::Get()
|
|||
return sBluetoothAvrcpManager;
|
||||
}
|
||||
|
||||
class BluetoothAvrcpManager::CleanupResultHandler final
|
||||
: public BluetoothAvrcpResultHandler
|
||||
class BluetoothAvrcpManager::UnregisterModuleResultHandler final
|
||||
: public BluetoothSetupResultHandler
|
||||
{
|
||||
public:
|
||||
CleanupResultHandler(BluetoothProfileResultHandler* aRes)
|
||||
UnregisterModuleResultHandler(BluetoothProfileResultHandler* aRes)
|
||||
: mRes(aRes)
|
||||
{ }
|
||||
|
||||
void OnError(BluetoothStatus aStatus) override
|
||||
{
|
||||
BT_WARNING("BluetoothAvrcpInterface::Cleanup failed: %d",
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
BT_WARNING("BluetoothSetupInterface::UnregisterModule failed for AVRCP: %d",
|
||||
(int)aStatus);
|
||||
|
||||
sBtAvrcpInterface->SetNotificationHandler(nullptr);
|
||||
sBtAvrcpInterface = nullptr;
|
||||
|
||||
if (mRes) {
|
||||
if (aStatus == STATUS_UNSUPPORTED) {
|
||||
/* Not all versions of Bluedroid support AVRCP. So if the
|
||||
* cleanup fails with STATUS_UNSUPPORTED, we still signal
|
||||
* success.
|
||||
*/
|
||||
mRes->Deinit();
|
||||
} else {
|
||||
mRes->OnError(NS_ERROR_FAILURE);
|
||||
}
|
||||
mRes->OnError(NS_ERROR_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void Cleanup() override
|
||||
void UnregisterModule() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
sBtAvrcpInterface->SetNotificationHandler(nullptr);
|
||||
sBtAvrcpInterface = nullptr;
|
||||
|
||||
if (mRes) {
|
||||
mRes->Deinit();
|
||||
}
|
||||
|
@ -285,25 +338,33 @@ private:
|
|||
nsRefPtr<BluetoothProfileResultHandler> mRes;
|
||||
};
|
||||
|
||||
class BluetoothAvrcpManager::CleanupResultHandlerRunnable final
|
||||
class BluetoothAvrcpManager::DeinitProfileResultHandlerRunnable final
|
||||
: public nsRunnable
|
||||
{
|
||||
public:
|
||||
CleanupResultHandlerRunnable(BluetoothProfileResultHandler* aRes)
|
||||
DeinitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
|
||||
nsresult aRv)
|
||||
: mRes(aRes)
|
||||
, mRv(aRv)
|
||||
{
|
||||
MOZ_ASSERT(mRes);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
mRes->Deinit();
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (NS_SUCCEEDED(mRv)) {
|
||||
mRes->Deinit();
|
||||
} else {
|
||||
mRes->OnError(mRv);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<BluetoothProfileResultHandler> mRes;
|
||||
nsresult mRv;
|
||||
};
|
||||
|
||||
// static
|
||||
|
@ -312,16 +373,45 @@ BluetoothAvrcpManager::DeinitAvrcpInterface(BluetoothProfileResultHandler* aRes)
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (sBtAvrcpInterface) {
|
||||
sBtAvrcpInterface->Cleanup(new CleanupResultHandler(aRes));
|
||||
} else if (aRes) {
|
||||
// We dispatch a runnable here to make the profile resource handler
|
||||
// behave as if AVRCP was initialized.
|
||||
nsRefPtr<nsRunnable> r = new CleanupResultHandlerRunnable(aRes);
|
||||
if (!sBtAvrcpInterface) {
|
||||
BT_LOGR("Bluetooth AVRCP interface has not been initalized.");
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new DeinitProfileResultHandlerRunnable(aRes, NS_OK);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch cleanup-result-handler runnable");
|
||||
BT_LOGR("Failed to dispatch AVRCP Deinit runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto btInf = BluetoothInterface::GetInstance();
|
||||
|
||||
if (NS_WARN_IF(!btInf)) {
|
||||
// If there's no backend interface, we dispatch a runnable
|
||||
// that calls the profile result handler.
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch AVRCP OnError runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto setupInterface = btInf->GetBluetoothSetupInterface();
|
||||
|
||||
if (NS_WARN_IF(!setupInterface)) {
|
||||
// If there's no Setup interface, we dispatch a runnable
|
||||
// that calls the profile result handler.
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch AVRCP OnError runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
setupInterface->UnregisterModule(
|
||||
SETUP_SERVICE_ID_AVRCP,
|
||||
new UnregisterModuleResultHandler(aRes));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -17,6 +17,8 @@ class BluetoothAvrcpManager : public BluetoothProfileManagerBase
|
|||
, public BluetoothAvrcpNotificationHandler
|
||||
{
|
||||
public:
|
||||
static const int MAX_NUM_CLIENTS;
|
||||
|
||||
BT_DECL_PROFILE_MGR_BASE
|
||||
virtual void GetName(nsACString& aName)
|
||||
{
|
||||
|
@ -60,12 +62,12 @@ protected:
|
|||
virtual ~BluetoothAvrcpManager();
|
||||
|
||||
private:
|
||||
class CleanupResultHandler;
|
||||
class CleanupResultHandlerRunnable;
|
||||
class ConnectRunnable;
|
||||
class DeinitProfileResultHandlerRunnable;
|
||||
class DisconnectRunnable;
|
||||
class InitResultHandler;
|
||||
class OnErrorProfileResultHandlerRunnable;
|
||||
class InitProfileResultHandlerRunnable;
|
||||
class RegisterModuleResultHandler;
|
||||
class UnregisterModuleResultHandler;
|
||||
|
||||
BluetoothAvrcpManager();
|
||||
|
||||
|
|
|
@ -15,8 +15,6 @@ using namespace mozilla::ipc;
|
|||
// A2DP module
|
||||
//
|
||||
|
||||
const int BluetoothDaemonA2dpModule::MAX_NUM_CLIENTS = 1;
|
||||
|
||||
BluetoothA2dpNotificationHandler*
|
||||
BluetoothDaemonA2dpModule::sNotificationHandler;
|
||||
|
||||
|
@ -235,109 +233,13 @@ BluetoothDaemonA2dpInterface::BluetoothDaemonA2dpInterface(
|
|||
BluetoothDaemonA2dpInterface::~BluetoothDaemonA2dpInterface()
|
||||
{ }
|
||||
|
||||
class BluetoothDaemonA2dpInterface::InitResultHandler final
|
||||
: public BluetoothSetupResultHandler
|
||||
{
|
||||
public:
|
||||
InitResultHandler(BluetoothA2dpResultHandler* aRes)
|
||||
: mRes(aRes)
|
||||
{
|
||||
MOZ_ASSERT(mRes);
|
||||
}
|
||||
|
||||
void OnError(BluetoothStatus aStatus) override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mRes->OnError(aStatus);
|
||||
}
|
||||
|
||||
void RegisterModule() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mRes->Init();
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<BluetoothA2dpResultHandler> mRes;
|
||||
};
|
||||
|
||||
void
|
||||
BluetoothDaemonA2dpInterface::Init(
|
||||
BluetoothA2dpNotificationHandler* aNotificationHandler,
|
||||
BluetoothA2dpResultHandler* aRes)
|
||||
BluetoothDaemonA2dpInterface::SetNotificationHandler(
|
||||
BluetoothA2dpNotificationHandler* aNotificationHandler)
|
||||
{
|
||||
// Set notification handler _before_ registering the module. It could
|
||||
// happen that we receive notifications, before the result handler runs.
|
||||
MOZ_ASSERT(mModule);
|
||||
|
||||
mModule->SetNotificationHandler(aNotificationHandler);
|
||||
|
||||
InitResultHandler* res;
|
||||
|
||||
if (aRes) {
|
||||
res = new InitResultHandler(aRes);
|
||||
} else {
|
||||
// We don't need a result handler if the caller is not interested.
|
||||
res = nullptr;
|
||||
}
|
||||
|
||||
nsresult rv = mModule->RegisterModule(BluetoothDaemonA2dpModule::SERVICE_ID,
|
||||
0x00, BluetoothDaemonA2dpModule::MAX_NUM_CLIENTS, res);
|
||||
if (NS_FAILED(rv) && aRes) {
|
||||
DispatchError(aRes, rv);
|
||||
}
|
||||
}
|
||||
|
||||
class BluetoothDaemonA2dpInterface::CleanupResultHandler final
|
||||
: public BluetoothSetupResultHandler
|
||||
{
|
||||
public:
|
||||
CleanupResultHandler(BluetoothDaemonA2dpModule* aModule,
|
||||
BluetoothA2dpResultHandler* aRes)
|
||||
: mModule(aModule)
|
||||
, mRes(aRes)
|
||||
{
|
||||
MOZ_ASSERT(mModule);
|
||||
}
|
||||
|
||||
void OnError(BluetoothStatus aStatus) override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mRes) {
|
||||
mRes->OnError(aStatus);
|
||||
}
|
||||
}
|
||||
|
||||
void UnregisterModule() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Clear notification handler _after_ module has been
|
||||
// unregistered. While unregistering the module, we might
|
||||
// still receive notifications.
|
||||
mModule->SetNotificationHandler(nullptr);
|
||||
|
||||
if (mRes) {
|
||||
mRes->Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
BluetoothDaemonA2dpModule* mModule;
|
||||
nsRefPtr<BluetoothA2dpResultHandler> mRes;
|
||||
};
|
||||
|
||||
void
|
||||
BluetoothDaemonA2dpInterface::Cleanup(
|
||||
BluetoothA2dpResultHandler* aRes)
|
||||
{
|
||||
nsresult rv = mModule->UnregisterModule(
|
||||
BluetoothDaemonA2dpModule::SERVICE_ID,
|
||||
new CleanupResultHandler(mModule, aRes));
|
||||
if (NS_FAILED(rv)) {
|
||||
DispatchError(aRes, rv);
|
||||
}
|
||||
}
|
||||
|
||||
/* Connect / Disconnect */
|
||||
|
|
|
@ -30,18 +30,9 @@ public:
|
|||
OPCODE_DISCONNECT = 0x02
|
||||
};
|
||||
|
||||
static const int MAX_NUM_CLIENTS;
|
||||
|
||||
virtual nsresult Send(DaemonSocketPDU* aPDU,
|
||||
DaemonSocketResultHandler* aRes) = 0;
|
||||
|
||||
virtual nsresult RegisterModule(uint8_t aId, uint8_t aMode,
|
||||
uint32_t aMaxNumClients,
|
||||
BluetoothSetupResultHandler* aRes) = 0;
|
||||
|
||||
virtual nsresult UnregisterModule(uint8_t aId,
|
||||
BluetoothSetupResultHandler* aRes) = 0;
|
||||
|
||||
void SetNotificationHandler(
|
||||
BluetoothA2dpNotificationHandler* aNotificationHandler);
|
||||
|
||||
|
@ -129,17 +120,12 @@ protected:
|
|||
class BluetoothDaemonA2dpInterface final
|
||||
: public BluetoothA2dpInterface
|
||||
{
|
||||
class CleanupResultHandler;
|
||||
class InitResultHandler;
|
||||
|
||||
public:
|
||||
BluetoothDaemonA2dpInterface(BluetoothDaemonA2dpModule* aModule);
|
||||
~BluetoothDaemonA2dpInterface();
|
||||
|
||||
void Init(
|
||||
BluetoothA2dpNotificationHandler* aNotificationHandler,
|
||||
BluetoothA2dpResultHandler* aRes) override;
|
||||
void Cleanup(BluetoothA2dpResultHandler* aRes) override;
|
||||
void SetNotificationHandler(
|
||||
BluetoothA2dpNotificationHandler* aNotificationHandler) override;
|
||||
|
||||
/* Connect / Disconnect */
|
||||
|
||||
|
|
|
@ -15,8 +15,6 @@ using namespace mozilla::ipc;
|
|||
// AVRCP module
|
||||
//
|
||||
|
||||
const int BluetoothDaemonAvrcpModule::MAX_NUM_CLIENTS = 1;
|
||||
|
||||
BluetoothAvrcpNotificationHandler*
|
||||
BluetoothDaemonAvrcpModule::sNotificationHandler;
|
||||
|
||||
|
@ -805,115 +803,13 @@ BluetoothDaemonAvrcpInterface::BluetoothDaemonAvrcpInterface(
|
|||
BluetoothDaemonAvrcpInterface::~BluetoothDaemonAvrcpInterface()
|
||||
{ }
|
||||
|
||||
class BluetoothDaemonAvrcpInterface::InitResultHandler final
|
||||
: public BluetoothSetupResultHandler
|
||||
{
|
||||
public:
|
||||
InitResultHandler(BluetoothAvrcpResultHandler* aRes)
|
||||
: mRes(aRes)
|
||||
{
|
||||
MOZ_ASSERT(mRes);
|
||||
}
|
||||
|
||||
void OnError(BluetoothStatus aStatus) override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mRes->OnError(aStatus);
|
||||
}
|
||||
|
||||
void RegisterModule() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mRes->Init();
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<BluetoothAvrcpResultHandler> mRes;
|
||||
};
|
||||
|
||||
void
|
||||
BluetoothDaemonAvrcpInterface::Init(
|
||||
BluetoothAvrcpNotificationHandler* aNotificationHandler,
|
||||
BluetoothAvrcpResultHandler* aRes)
|
||||
BluetoothDaemonAvrcpInterface::SetNotificationHandler(
|
||||
BluetoothAvrcpNotificationHandler* aNotificationHandler)
|
||||
{
|
||||
MOZ_ASSERT(mModule);
|
||||
|
||||
// Set notification handler _before_ registering the module. It could
|
||||
// happen that we receive notifications, before the result handler runs.
|
||||
mModule->SetNotificationHandler(aNotificationHandler);
|
||||
|
||||
InitResultHandler* res;
|
||||
|
||||
if (aRes) {
|
||||
res = new InitResultHandler(aRes);
|
||||
} else {
|
||||
// We don't need a result handler if the caller is not interested.
|
||||
res = nullptr;
|
||||
}
|
||||
|
||||
nsresult rv = mModule->RegisterModule(
|
||||
BluetoothDaemonAvrcpModule::SERVICE_ID,
|
||||
BluetoothDaemonAvrcpModule::MAX_NUM_CLIENTS, 0x00, res);
|
||||
|
||||
if (NS_FAILED(rv) && aRes) {
|
||||
DispatchError(aRes, rv);
|
||||
}
|
||||
}
|
||||
|
||||
class BluetoothDaemonAvrcpInterface::CleanupResultHandler final
|
||||
: public BluetoothSetupResultHandler
|
||||
{
|
||||
public:
|
||||
CleanupResultHandler(BluetoothDaemonAvrcpModule* aModule,
|
||||
BluetoothAvrcpResultHandler* aRes)
|
||||
: mModule(aModule)
|
||||
, mRes(aRes)
|
||||
{
|
||||
MOZ_ASSERT(mModule);
|
||||
}
|
||||
|
||||
void OnError(BluetoothStatus aStatus) override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mRes) {
|
||||
mRes->OnError(aStatus);
|
||||
}
|
||||
}
|
||||
|
||||
void UnregisterModule() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Clear notification handler _after_ module has been
|
||||
// unregistered. While unregistering the module, we might
|
||||
// still receive notifications.
|
||||
mModule->SetNotificationHandler(nullptr);
|
||||
|
||||
if (mRes) {
|
||||
mRes->Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
BluetoothDaemonAvrcpModule* mModule;
|
||||
nsRefPtr<BluetoothAvrcpResultHandler> mRes;
|
||||
};
|
||||
|
||||
void
|
||||
BluetoothDaemonAvrcpInterface::Cleanup(
|
||||
BluetoothAvrcpResultHandler* aRes)
|
||||
{
|
||||
MOZ_ASSERT(mModule);
|
||||
|
||||
nsresult rv = mModule->UnregisterModule(
|
||||
BluetoothDaemonAvrcpModule::SERVICE_ID,
|
||||
new CleanupResultHandler(mModule, aRes));
|
||||
if (NS_FAILED(rv)) {
|
||||
DispatchError(aRes, rv);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -62,18 +62,9 @@ public:
|
|||
#endif
|
||||
};
|
||||
|
||||
static const int MAX_NUM_CLIENTS;
|
||||
|
||||
virtual nsresult Send(DaemonSocketPDU* aPDU,
|
||||
DaemonSocketResultHandler* aRes) = 0;
|
||||
|
||||
virtual nsresult RegisterModule(uint8_t aId, uint8_t aMode,
|
||||
uint32_t aMaxNumClients,
|
||||
BluetoothSetupResultHandler* aRes) = 0;
|
||||
|
||||
virtual nsresult UnregisterModule(uint8_t aId,
|
||||
BluetoothSetupResultHandler* aRes) = 0;
|
||||
|
||||
void SetNotificationHandler(
|
||||
BluetoothAvrcpNotificationHandler* aNotificationHandler);
|
||||
|
||||
|
@ -305,10 +296,8 @@ public:
|
|||
BluetoothDaemonAvrcpInterface(BluetoothDaemonAvrcpModule* aModule);
|
||||
~BluetoothDaemonAvrcpInterface();
|
||||
|
||||
void Init(BluetoothAvrcpNotificationHandler* aNotificationHandler,
|
||||
BluetoothAvrcpResultHandler* aRes) override;
|
||||
|
||||
void Cleanup(BluetoothAvrcpResultHandler* aRes) override;
|
||||
void SetNotificationHandler(
|
||||
BluetoothAvrcpNotificationHandler* aNotificationHandler) override;
|
||||
|
||||
void GetPlayStatusRsp(ControlPlayStatus aPlayStatus,
|
||||
uint32_t aSongLen, uint32_t aSongPos,
|
||||
|
|
|
@ -15,8 +15,6 @@ using namespace mozilla::ipc;
|
|||
// GATT module
|
||||
//
|
||||
|
||||
const int BluetoothDaemonGattModule::MAX_NUM_CLIENTS = 1;
|
||||
|
||||
BluetoothGattNotificationHandler*
|
||||
BluetoothDaemonGattModule::sNotificationHandler;
|
||||
|
||||
|
@ -2010,6 +2008,7 @@ BluetoothDaemonGattInterface::BluetoothDaemonGattInterface(
|
|||
BluetoothDaemonGattInterface::~BluetoothDaemonGattInterface()
|
||||
{ }
|
||||
|
||||
#if 0
|
||||
class BluetoothDaemonGattInterface::InitResultHandler final
|
||||
: public BluetoothSetupResultHandler
|
||||
{
|
||||
|
@ -2057,8 +2056,8 @@ BluetoothDaemonGattInterface::Init(
|
|||
}
|
||||
|
||||
nsresult rv = mModule->RegisterModule(
|
||||
BluetoothDaemonGattModule::SERVICE_ID, 0x00,
|
||||
BluetoothDaemonGattModule::MAX_NUM_CLIENTS, res);
|
||||
SETUP_SERVICE_ID_GATT, 0x00, BluetoothDaemonGattModule::MAX_NUM_CLIENTS,
|
||||
res);
|
||||
|
||||
if (NS_FAILED(rv) && aRes) {
|
||||
DispatchError(aRes, rv);
|
||||
|
@ -2110,12 +2109,21 @@ BluetoothDaemonGattInterface::Cleanup(
|
|||
BluetoothGattResultHandler* aRes)
|
||||
{
|
||||
nsresult rv = mModule->UnregisterModule(
|
||||
BluetoothDaemonGattModule::SERVICE_ID,
|
||||
new CleanupResultHandler(mModule, aRes));
|
||||
SETUP_SERVICE_ID_GATT, new CleanupResultHandler(mModule, aRes));
|
||||
if (NS_FAILED(rv)) {
|
||||
DispatchError(aRes, rv);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
BluetoothDaemonGattInterface::SetNotificationHandler(
|
||||
BluetoothGattNotificationHandler* aNotificationHandler)
|
||||
{
|
||||
MOZ_ASSERT(mModule);
|
||||
|
||||
mModule->SetNotificationHandler(aNotificationHandler);
|
||||
}
|
||||
|
||||
/* Register / Unregister */
|
||||
void
|
||||
|
|
|
@ -64,18 +64,9 @@ public:
|
|||
// TODO: Add L support
|
||||
};
|
||||
|
||||
static const int MAX_NUM_CLIENTS;
|
||||
|
||||
virtual nsresult Send(DaemonSocketPDU* aPDU,
|
||||
DaemonSocketResultHandler* aRes) = 0;
|
||||
|
||||
virtual nsresult RegisterModule(uint8_t aId, uint8_t aMode,
|
||||
uint32_t aMaxNumClients,
|
||||
BluetoothSetupResultHandler* aRes) = 0;
|
||||
|
||||
virtual nsresult UnregisterModule(uint8_t aId,
|
||||
BluetoothSetupResultHandler* aRes) = 0;
|
||||
|
||||
void SetNotificationHandler(
|
||||
BluetoothGattNotificationHandler* aNotificationHandler);
|
||||
|
||||
|
@ -798,9 +789,8 @@ public:
|
|||
BluetoothDaemonGattInterface(BluetoothDaemonGattModule* aModule);
|
||||
~BluetoothDaemonGattInterface();
|
||||
|
||||
void Init(BluetoothGattNotificationHandler* aNotificationHandler,
|
||||
BluetoothGattResultHandler* aRes) override;
|
||||
void Cleanup(BluetoothGattResultHandler* aRes) override;
|
||||
void SetNotificationHandler(
|
||||
BluetoothGattNotificationHandler* aNotificationHandler) override;
|
||||
|
||||
/* Register / Unregister */
|
||||
void RegisterClient(const BluetoothUuid& aUuid,
|
||||
|
|
|
@ -1409,115 +1409,12 @@ BluetoothDaemonHandsfreeInterface::BluetoothDaemonHandsfreeInterface(
|
|||
BluetoothDaemonHandsfreeInterface::~BluetoothDaemonHandsfreeInterface()
|
||||
{ }
|
||||
|
||||
class BluetoothDaemonHandsfreeInterface::InitResultHandler final
|
||||
: public BluetoothSetupResultHandler
|
||||
void BluetoothDaemonHandsfreeInterface::SetNotificationHandler(
|
||||
BluetoothHandsfreeNotificationHandler* aNotificationHandler)
|
||||
{
|
||||
public:
|
||||
InitResultHandler(BluetoothHandsfreeResultHandler* aRes)
|
||||
: mRes(aRes)
|
||||
{
|
||||
MOZ_ASSERT(mRes);
|
||||
}
|
||||
MOZ_ASSERT(mModule);
|
||||
|
||||
void OnError(BluetoothStatus aStatus) override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mRes->OnError(aStatus);
|
||||
}
|
||||
|
||||
void RegisterModule() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mRes->Init();
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<BluetoothHandsfreeResultHandler> mRes;
|
||||
};
|
||||
|
||||
void
|
||||
BluetoothDaemonHandsfreeInterface::Init(
|
||||
BluetoothHandsfreeNotificationHandler* aNotificationHandler,
|
||||
int aMaxNumClients, BluetoothHandsfreeResultHandler* aRes)
|
||||
{
|
||||
// Set notification handler _before_ registering the module. It could
|
||||
// happen that we receive notifications, before the result handler runs.
|
||||
mModule->SetNotificationHandler(aNotificationHandler);
|
||||
|
||||
InitResultHandler* res;
|
||||
|
||||
if (aRes) {
|
||||
res = new InitResultHandler(aRes);
|
||||
} else {
|
||||
// We don't need a result handler if the caller is not interested.
|
||||
res = nullptr;
|
||||
}
|
||||
|
||||
nsresult rv = mModule->RegisterModule(
|
||||
BluetoothDaemonHandsfreeModule::SERVICE_ID, MODE_NARROWBAND_SPEECH,
|
||||
aMaxNumClients, res);
|
||||
|
||||
if (NS_FAILED(rv) && aRes) {
|
||||
DispatchError(aRes, rv);
|
||||
}
|
||||
}
|
||||
|
||||
class BluetoothDaemonHandsfreeInterface::CleanupResultHandler final
|
||||
: public BluetoothSetupResultHandler
|
||||
{
|
||||
public:
|
||||
CleanupResultHandler(BluetoothDaemonHandsfreeModule* aModule,
|
||||
BluetoothHandsfreeResultHandler* aRes)
|
||||
: mModule(aModule)
|
||||
, mRes(aRes)
|
||||
{
|
||||
MOZ_ASSERT(mModule);
|
||||
}
|
||||
|
||||
void OnError(BluetoothStatus aStatus) override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
BT_LOGR("%s:%d", __func__, __LINE__);
|
||||
if (mRes) {
|
||||
mRes->OnError(aStatus);
|
||||
}
|
||||
}
|
||||
|
||||
void UnregisterModule() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
BT_LOGR("%s:%d", __func__, __LINE__);
|
||||
// Clear notification handler _after_ module has been
|
||||
// unregistered. While unregistering the module, we might
|
||||
// still receive notifications.
|
||||
mModule->SetNotificationHandler(nullptr);
|
||||
|
||||
if (mRes) {
|
||||
mRes->Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
BluetoothDaemonHandsfreeModule* mModule;
|
||||
nsRefPtr<BluetoothHandsfreeResultHandler> mRes;
|
||||
};
|
||||
|
||||
void
|
||||
BluetoothDaemonHandsfreeInterface::Cleanup(
|
||||
BluetoothHandsfreeResultHandler* aRes)
|
||||
{
|
||||
BT_LOGR("%s:%d", __func__, __LINE__);
|
||||
nsresult rv = mModule->UnregisterModule(
|
||||
BluetoothDaemonHandsfreeModule::SERVICE_ID,
|
||||
new CleanupResultHandler(mModule, aRes));
|
||||
BT_LOGR("%s:%d", __func__, __LINE__);
|
||||
if (NS_FAILED(rv)) {
|
||||
DispatchError(aRes, rv);
|
||||
}
|
||||
}
|
||||
|
||||
/* Connect / Disconnect */
|
||||
|
|
|
@ -46,13 +46,6 @@ public:
|
|||
virtual nsresult Send(DaemonSocketPDU* aPDU,
|
||||
DaemonSocketResultHandler* aRes) = 0;
|
||||
|
||||
virtual nsresult RegisterModule(uint8_t aId, uint8_t aMode,
|
||||
uint32_t aMaxNumClients,
|
||||
BluetoothSetupResultHandler* aRes) = 0;
|
||||
|
||||
virtual nsresult UnregisterModule(uint8_t aId,
|
||||
BluetoothSetupResultHandler* aRes) = 0;
|
||||
|
||||
void SetNotificationHandler(
|
||||
BluetoothHandsfreeNotificationHandler* aNotificationHandler);
|
||||
|
||||
|
@ -400,23 +393,12 @@ protected:
|
|||
class BluetoothDaemonHandsfreeInterface final
|
||||
: public BluetoothHandsfreeInterface
|
||||
{
|
||||
class CleanupResultHandler;
|
||||
class InitResultHandler;
|
||||
|
||||
enum {
|
||||
MODE_HEADSET = 0x00,
|
||||
MODE_NARROWBAND_SPEECH = 0x01,
|
||||
MODE_NARRAWBAND_WIDEBAND_SPEECH = 0x02
|
||||
};
|
||||
|
||||
public:
|
||||
BluetoothDaemonHandsfreeInterface(BluetoothDaemonHandsfreeModule* aModule);
|
||||
~BluetoothDaemonHandsfreeInterface();
|
||||
|
||||
void Init(
|
||||
BluetoothHandsfreeNotificationHandler* aNotificationHandler,
|
||||
int aMaxNumClients, BluetoothHandsfreeResultHandler* aRes) override;
|
||||
void Cleanup(BluetoothHandsfreeResultHandler* aRes) override;
|
||||
void SetNotificationHandler(
|
||||
BluetoothHandsfreeNotificationHandler* aNotificationHandler) override;
|
||||
|
||||
/* Connect / Disconnect */
|
||||
|
||||
|
|
|
@ -841,6 +841,35 @@ Convert(BluetoothScanMode aIn, int32_t& aOut)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Convert(BluetoothSetupServiceId aIn, uint8_t& aOut)
|
||||
{
|
||||
static const uint8_t sServiceId[] = {
|
||||
[SETUP_SERVICE_ID_SETUP] = 0x00,
|
||||
[SETUP_SERVICE_ID_CORE] = 0x01,
|
||||
[SETUP_SERVICE_ID_SOCKET] = 0x02,
|
||||
[SETUP_SERVICE_ID_HID] = 0x03,
|
||||
[SETUP_SERVICE_ID_PAN] = 0x04,
|
||||
[SETUP_SERVICE_ID_HANDSFREE] = 0x05,
|
||||
[SETUP_SERVICE_ID_A2DP] = 0x06,
|
||||
[SETUP_SERVICE_ID_HEALTH] = 0x07,
|
||||
[SETUP_SERVICE_ID_AVRCP] = 0x08,
|
||||
[SETUP_SERVICE_ID_GATT] = 0x09,
|
||||
[SETUP_SERVICE_ID_HANDSFREE_CLIENT] = 0x0a,
|
||||
[SETUP_SERVICE_ID_MAP_CLIENT] = 0x0b,
|
||||
[SETUP_SERVICE_ID_AVRCP_CONTROLLER] = 0x0c,
|
||||
[SETUP_SERVICE_ID_A2DP_SINK] = 0x0d
|
||||
};
|
||||
if (MOZ_HAL_IPC_CONVERT_WARN_IF(
|
||||
aIn >= MOZ_ARRAY_LENGTH(sServiceId),
|
||||
BluetoothServiceSetupId, uint8_t)) {
|
||||
aOut = 0; // silences compiler warning
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
aOut = sServiceId[aIn];
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Convert(BluetoothSspVariant aIn, uint8_t& aOut)
|
||||
{
|
||||
|
@ -1267,6 +1296,12 @@ PackPDU(BluetoothScanMode aIn, DaemonSocketPDU& aPDU)
|
|||
return PackPDU(PackConversion<BluetoothScanMode, int32_t>(aIn), aPDU);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PackPDU(BluetoothSetupServiceId aIn, DaemonSocketPDU& aPDU)
|
||||
{
|
||||
return PackPDU(PackConversion<BluetoothSetupServiceId, uint8_t>(aIn), aPDU);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PackPDU(const BluetoothServiceName& aIn, DaemonSocketPDU& aPDU)
|
||||
{
|
||||
|
|
|
@ -97,12 +97,6 @@ struct BluetoothAvrcpEventParamPair {
|
|||
const BluetoothAvrcpNotificationParam& mParam;
|
||||
};
|
||||
|
||||
struct BluetoothConfigurationParameter {
|
||||
uint8_t mType;
|
||||
uint16_t mLength;
|
||||
nsAutoArrayPtr<uint8_t> mValue;
|
||||
};
|
||||
|
||||
//
|
||||
// Conversion
|
||||
//
|
||||
|
@ -236,6 +230,9 @@ Convert(BluetoothPropertyType aIn, uint8_t& aOut);
|
|||
nsresult
|
||||
Convert(BluetoothScanMode aIn, uint8_t& aOut);
|
||||
|
||||
nsresult
|
||||
Convert(BluetoothSetupServiceId aIn, uint8_t& aOut);
|
||||
|
||||
nsresult
|
||||
Convert(BluetoothSocketType aIn, uint8_t& aOut);
|
||||
|
||||
|
@ -335,6 +332,9 @@ PackPDU(BluetoothPropertyType aIn, DaemonSocketPDU& aPDU);
|
|||
nsresult
|
||||
PackPDU(const BluetoothServiceName& aIn, DaemonSocketPDU& aPDU);
|
||||
|
||||
nsresult
|
||||
PackPDU(BluetoothSetupServiceId aIn, DaemonSocketPDU& aPDU);
|
||||
|
||||
nsresult
|
||||
PackPDU(BluetoothSocketType aIn, DaemonSocketPDU& aPDU);
|
||||
|
||||
|
|
|
@ -39,24 +39,14 @@ static const int sRetryInterval = 100; // ms
|
|||
//
|
||||
// Each |BluetoothDaemon*Module| class implements an individual
|
||||
// module of the HAL protocol. Each class contains the abstract
|
||||
// methods
|
||||
// method
|
||||
//
|
||||
// - |Send|,
|
||||
// - |RegisterModule|, and
|
||||
// - |UnregisterModule|.
|
||||
// - |Send|.
|
||||
//
|
||||
// Module classes use |Send| to send out command PDUs. The socket
|
||||
// in |BluetoothDaemonProtocol| is required for sending. The abstract
|
||||
// method hides all these internal details from the modules.
|
||||
//
|
||||
// |RegisterModule| is required during module initialization, when
|
||||
// modules must register themselves at the daemon. The register command
|
||||
// is not part of the module itself, but contained in the Setup module
|
||||
// (id of 0x00). The abstract method |RegisterModule| allows modules to
|
||||
// call into the Setup module for generating the register command.
|
||||
//
|
||||
// |UnregisterModule| works like |RegisterModule|, but for cleanups.
|
||||
//
|
||||
// |BluetoothDaemonProtocol| also handles PDU receiving. It implements
|
||||
// the method |Handle| from |DaemonSocketIOConsumer|. The socket
|
||||
// connections of type |DaemonSocket| invoke this method
|
||||
|
@ -66,11 +56,9 @@ static const int sRetryInterval = 100; // ms
|
|||
// |HandleSvc|. Further PDU processing is module-dependent.
|
||||
//
|
||||
// To summarize the interface between |BluetoothDaemonProtocol| and
|
||||
// modules; the former implements the abstract methods
|
||||
// modules; the former implements the abstract method
|
||||
//
|
||||
// - |Send|,
|
||||
// - |RegisterModule|, and
|
||||
// - |UnregisterModule|,
|
||||
//
|
||||
// which allow modules to send out data. Each module implements the
|
||||
// method
|
||||
|
@ -95,12 +83,6 @@ public:
|
|||
|
||||
void SetConnection(DaemonSocket* aConnection);
|
||||
|
||||
nsresult RegisterModule(uint8_t aId, uint8_t aMode, uint32_t aMaxNumClients,
|
||||
BluetoothSetupResultHandler* aRes) override;
|
||||
|
||||
nsresult UnregisterModule(uint8_t aId,
|
||||
BluetoothSetupResultHandler* aRes) override;
|
||||
|
||||
// Outgoing PDUs
|
||||
//
|
||||
|
||||
|
@ -153,22 +135,6 @@ BluetoothDaemonProtocol::SetConnection(DaemonSocket* aConnection)
|
|||
mConnection = aConnection;
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothDaemonProtocol::RegisterModule(uint8_t aId, uint8_t aMode,
|
||||
uint32_t aMaxNumClients,
|
||||
BluetoothSetupResultHandler* aRes)
|
||||
{
|
||||
return BluetoothDaemonSetupModule::RegisterModuleCmd(aId, aMode,
|
||||
aMaxNumClients, aRes);
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothDaemonProtocol::UnregisterModule(uint8_t aId,
|
||||
BluetoothSetupResultHandler* aRes)
|
||||
{
|
||||
return BluetoothDaemonSetupModule::UnregisterModuleCmd(aId, aRes);
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothDaemonProtocol::Send(DaemonSocketPDU* aPDU,
|
||||
DaemonSocketResultHandler* aRes)
|
||||
|
@ -422,7 +388,9 @@ public:
|
|||
if (!mRegisteredSocketModule) {
|
||||
mRegisteredSocketModule = true;
|
||||
// Init, step 5: Register Socket module
|
||||
mInterface->mProtocol->RegisterModuleCmd(0x02, 0x00,
|
||||
mInterface->mProtocol->RegisterModuleCmd(
|
||||
SETUP_SERVICE_ID_SOCKET,
|
||||
0x00,
|
||||
BluetoothDaemonSocketModule::MAX_NUM_CLIENTS, this);
|
||||
} else if (mRes) {
|
||||
// Init, step 6: Signal success to caller
|
||||
|
@ -556,7 +524,7 @@ private:
|
|||
if (!mUnregisteredCoreModule) {
|
||||
mUnregisteredCoreModule = true;
|
||||
// Cleanup, step 2: Unregister Core module
|
||||
mInterface->mProtocol->UnregisterModuleCmd(0x01, this);
|
||||
mInterface->mProtocol->UnregisterModuleCmd(SETUP_SERVICE_ID_CORE, this);
|
||||
} else {
|
||||
// Cleanup, step 3: Close command channel
|
||||
mInterface->mCmdChannel->Close();
|
||||
|
@ -600,7 +568,7 @@ BluetoothDaemonInterface::Cleanup(BluetoothResultHandler* aRes)
|
|||
|
||||
// Cleanup, step 1: Unregister Socket module
|
||||
nsresult rv = mProtocol->UnregisterModuleCmd(
|
||||
0x02, new CleanupResultHandler(this));
|
||||
SETUP_SERVICE_ID_SOCKET, new CleanupResultHandler(this));
|
||||
if (NS_FAILED(rv)) {
|
||||
DispatchError(aRes, rv);
|
||||
return;
|
||||
|
@ -889,9 +857,21 @@ BluetoothDaemonInterface::DispatchError(BluetoothResultHandler* aRes,
|
|||
DispatchError(aRes, status);
|
||||
}
|
||||
|
||||
// Profile Interfaces
|
||||
// Service Interfaces
|
||||
//
|
||||
|
||||
BluetoothSetupInterface*
|
||||
BluetoothDaemonInterface::GetBluetoothSetupInterface()
|
||||
{
|
||||
if (mSetupInterface) {
|
||||
return mSetupInterface;
|
||||
}
|
||||
|
||||
mSetupInterface = new BluetoothDaemonSetupInterface(mProtocol);
|
||||
|
||||
return mSetupInterface;
|
||||
}
|
||||
|
||||
BluetoothSocketInterface*
|
||||
BluetoothDaemonInterface::GetBluetoothSocketInterface()
|
||||
{
|
||||
|
@ -1002,7 +982,9 @@ BluetoothDaemonInterface::OnConnectSuccess(int aIndex)
|
|||
|
||||
// Init, step 4: Register Core module
|
||||
nsresult rv = mProtocol->RegisterModuleCmd(
|
||||
0x01, 0x00, BluetoothDaemonCoreModule::MAX_NUM_CLIENTS,
|
||||
SETUP_SERVICE_ID_CORE,
|
||||
0x00,
|
||||
BluetoothDaemonCoreModule::MAX_NUM_CLIENTS,
|
||||
new InitResultHandler(this, res));
|
||||
if (NS_FAILED(rv) && res) {
|
||||
DispatchError(res, STATUS_FAIL);
|
||||
|
|
|
@ -27,6 +27,7 @@ class BluetoothDaemonAvrcpInterface;
|
|||
class BluetoothDaemonGattInterface;
|
||||
class BluetoothDaemonHandsfreeInterface;
|
||||
class BluetoothDaemonProtocol;
|
||||
class BluetoothDaemonSetupInterface;
|
||||
class BluetoothDaemonSocketInterface;
|
||||
|
||||
class BluetoothDaemonInterface final
|
||||
|
@ -125,8 +126,9 @@ public:
|
|||
|
||||
void ReadEnergyInfo(BluetoothResultHandler* aRes) override;
|
||||
|
||||
/* Profile Interfaces */
|
||||
/* Service Interfaces */
|
||||
|
||||
BluetoothSetupInterface* GetBluetoothSetupInterface() override;
|
||||
BluetoothSocketInterface* GetBluetoothSocketInterface() override;
|
||||
BluetoothHandsfreeInterface* GetBluetoothHandsfreeInterface() override;
|
||||
BluetoothA2dpInterface* GetBluetoothA2dpInterface() override;
|
||||
|
@ -162,6 +164,7 @@ private:
|
|||
|
||||
nsTArray<nsRefPtr<BluetoothResultHandler> > mResultHandlerQ;
|
||||
|
||||
nsAutoPtr<BluetoothDaemonSetupInterface> mSetupInterface;
|
||||
nsAutoPtr<BluetoothDaemonSocketInterface> mSocketInterface;
|
||||
nsAutoPtr<BluetoothDaemonHandsfreeInterface> mHandsfreeInterface;
|
||||
nsAutoPtr<BluetoothDaemonA2dpInterface> mA2dpInterface;
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
BEGIN_BLUETOOTH_NAMESPACE
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
|
||||
//
|
||||
// Setup module
|
||||
//
|
||||
|
@ -54,7 +56,7 @@ BluetoothDaemonSetupModule::HandleSvc(const DaemonSocketPDUHeader& aHeader,
|
|||
|
||||
nsresult
|
||||
BluetoothDaemonSetupModule::RegisterModuleCmd(
|
||||
uint8_t aId, uint8_t aMode, uint32_t aMaxNumClients,
|
||||
BluetoothSetupServiceId aId, uint8_t aMode, uint32_t aMaxNumClients,
|
||||
BluetoothSetupResultHandler* aRes)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
@ -81,7 +83,7 @@ BluetoothDaemonSetupModule::RegisterModuleCmd(
|
|||
|
||||
nsresult
|
||||
BluetoothDaemonSetupModule::UnregisterModuleCmd(
|
||||
uint8_t aId, BluetoothSetupResultHandler* aRes)
|
||||
BluetoothSetupServiceId aId, BluetoothSetupResultHandler* aRes)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
|
@ -167,4 +169,77 @@ BluetoothDaemonSetupModule::ConfigurationRsp(
|
|||
UnpackPDUInitOp(aPDU));
|
||||
}
|
||||
|
||||
//
|
||||
// Setup interface
|
||||
//
|
||||
|
||||
BluetoothDaemonSetupInterface::BluetoothDaemonSetupInterface(
|
||||
BluetoothDaemonSetupModule* aModule)
|
||||
: mModule(aModule)
|
||||
{ }
|
||||
|
||||
BluetoothDaemonSetupInterface::~BluetoothDaemonSetupInterface()
|
||||
{ }
|
||||
|
||||
void
|
||||
BluetoothDaemonSetupInterface::RegisterModule(
|
||||
BluetoothSetupServiceId aId, uint8_t aMode, uint32_t aMaxNumClients,
|
||||
BluetoothSetupResultHandler* aRes)
|
||||
{
|
||||
MOZ_ASSERT(mModule);
|
||||
|
||||
nsresult rv = mModule->RegisterModuleCmd(aId, aMode, aMaxNumClients, aRes);
|
||||
if (NS_FAILED(rv)) {
|
||||
DispatchError(aRes, rv);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothDaemonSetupInterface::UnregisterModule(
|
||||
BluetoothSetupServiceId aId,
|
||||
BluetoothSetupResultHandler* aRes)
|
||||
{
|
||||
MOZ_ASSERT(mModule);
|
||||
|
||||
nsresult rv = mModule->UnregisterModuleCmd(aId, aRes);
|
||||
if (NS_FAILED(rv)) {
|
||||
DispatchError(aRes, rv);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothDaemonSetupInterface::Configuration(
|
||||
const BluetoothConfigurationParameter* aParam, uint8_t aLen,
|
||||
BluetoothSetupResultHandler* aRes)
|
||||
{
|
||||
MOZ_ASSERT(mModule);
|
||||
|
||||
nsresult rv = mModule->ConfigurationCmd(aParam, aLen, aRes);
|
||||
if (NS_FAILED(rv)) {
|
||||
DispatchError(aRes, rv);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothDaemonSetupInterface::DispatchError(
|
||||
BluetoothSetupResultHandler* aRes, BluetoothStatus aStatus)
|
||||
{
|
||||
DaemonResultRunnable1<BluetoothSetupResultHandler, void,
|
||||
BluetoothStatus, BluetoothStatus>::Dispatch(
|
||||
aRes, &BluetoothSetupResultHandler::OnError,
|
||||
ConstantInitOp1<BluetoothStatus>(aStatus));
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothDaemonSetupInterface::DispatchError(
|
||||
BluetoothSetupResultHandler* aRes, nsresult aRv)
|
||||
{
|
||||
BluetoothStatus status;
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(Convert(aRv, status)))) {
|
||||
status = STATUS_FAIL;
|
||||
}
|
||||
DispatchError(aRes, status);
|
||||
}
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
|
|
@ -37,11 +37,11 @@ public:
|
|||
// Commands
|
||||
//
|
||||
|
||||
nsresult RegisterModuleCmd(uint8_t aId, uint8_t aMode,
|
||||
nsresult RegisterModuleCmd(BluetoothSetupServiceId aId, uint8_t aMode,
|
||||
uint32_t aMaxNumClients,
|
||||
BluetoothSetupResultHandler* aRes);
|
||||
|
||||
nsresult UnregisterModuleCmd(uint8_t aId,
|
||||
nsresult UnregisterModuleCmd(BluetoothSetupServiceId aId,
|
||||
BluetoothSetupResultHandler* aRes);
|
||||
|
||||
nsresult ConfigurationCmd(const BluetoothConfigurationParameter* aParam,
|
||||
|
@ -86,6 +86,32 @@ private:
|
|||
BluetoothSetupResultHandler* aRes);
|
||||
};
|
||||
|
||||
class BluetoothDaemonSetupInterface final
|
||||
: public BluetoothSetupInterface
|
||||
{
|
||||
public:
|
||||
BluetoothDaemonSetupInterface(BluetoothDaemonSetupModule* aModule);
|
||||
~BluetoothDaemonSetupInterface();
|
||||
|
||||
void RegisterModule(BluetoothSetupServiceId aId, uint8_t aMode,
|
||||
uint32_t aMaxNumClients,
|
||||
BluetoothSetupResultHandler* aRes) override;
|
||||
|
||||
void UnregisterModule(BluetoothSetupServiceId aId,
|
||||
BluetoothSetupResultHandler* aRes) override;
|
||||
|
||||
void Configuration(const BluetoothConfigurationParameter* aParam,
|
||||
uint8_t aLen,
|
||||
BluetoothSetupResultHandler* aRes) override;
|
||||
|
||||
private:
|
||||
void DispatchError(BluetoothSetupResultHandler* aRes,
|
||||
BluetoothStatus aStatus);
|
||||
void DispatchError(BluetoothSetupResultHandler* aRes, nsresult aRv);
|
||||
|
||||
BluetoothDaemonSetupModule* mModule;
|
||||
};
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
||||
#endif // mozilla_dom_bluetooth_bluedroid_BluetoothDaemonSetupInterface_h
|
||||
|
|
|
@ -37,6 +37,8 @@ namespace {
|
|||
static BluetoothGattInterface* sBluetoothGattInterface;
|
||||
} // namespace
|
||||
|
||||
const int BluetoothGattManager::MAX_NUM_CLIENTS = 1;
|
||||
|
||||
bool BluetoothGattManager::mInShutdown = false;
|
||||
|
||||
static StaticAutoPtr<nsTArray<nsRefPtr<BluetoothGattClient> > > sClients;
|
||||
|
@ -376,52 +378,126 @@ BluetoothGattManager::Get()
|
|||
return sBluetoothGattManager;
|
||||
}
|
||||
|
||||
class BluetoothGattManager::InitGattResultHandler final
|
||||
: public BluetoothGattResultHandler
|
||||
class BluetoothGattManager::RegisterModuleResultHandler final
|
||||
: public BluetoothSetupResultHandler
|
||||
{
|
||||
public:
|
||||
InitGattResultHandler(BluetoothProfileResultHandler* aRes)
|
||||
: mRes(aRes)
|
||||
RegisterModuleResultHandler(BluetoothGattInterface* aInterface,
|
||||
BluetoothProfileResultHandler* aRes)
|
||||
: mInterface(aInterface)
|
||||
, mRes(aRes)
|
||||
{ }
|
||||
|
||||
void OnError(BluetoothStatus aStatus) override
|
||||
{
|
||||
BT_WARNING("BluetoothGattInterface::Init failed: %d",
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
BT_WARNING("BluetoothSetupInterface::RegisterModule failed for GATT: %d",
|
||||
(int)aStatus);
|
||||
|
||||
mInterface->SetNotificationHandler(nullptr);
|
||||
|
||||
if (mRes) {
|
||||
mRes->OnError(NS_ERROR_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void Init() override
|
||||
void RegisterModule() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
sBluetoothGattInterface = mInterface;
|
||||
|
||||
if (mRes) {
|
||||
mRes->Init();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
BluetoothGattInterface* mInterface;
|
||||
nsRefPtr<BluetoothProfileResultHandler> mRes;
|
||||
};
|
||||
|
||||
class BluetoothGattManager::InitProfileResultHandlerRunnable final
|
||||
: public nsRunnable
|
||||
{
|
||||
public:
|
||||
InitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
|
||||
nsresult aRv)
|
||||
: mRes(aRes)
|
||||
, mRv(aRv)
|
||||
{
|
||||
MOZ_ASSERT(mRes);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (NS_SUCCEEDED(mRv)) {
|
||||
mRes->Init();
|
||||
} else {
|
||||
mRes->OnError(mRv);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<BluetoothProfileResultHandler> mRes;
|
||||
nsresult mRv;
|
||||
};
|
||||
|
||||
// static
|
||||
void
|
||||
BluetoothGattManager::InitGattInterface(BluetoothProfileResultHandler* aRes)
|
||||
{
|
||||
BluetoothInterface* btInf = BluetoothInterface::GetInstance();
|
||||
if (!btInf) {
|
||||
BT_LOGR("Error: Bluetooth interface not available");
|
||||
if (aRes) {
|
||||
aRes->OnError(NS_ERROR_FAILURE);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (sBluetoothGattInterface) {
|
||||
BT_LOGR("Bluetooth GATT interface is already initalized.");
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new InitProfileResultHandlerRunnable(aRes, NS_OK);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch GATT Init runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sBluetoothGattInterface = btInf->GetBluetoothGattInterface();
|
||||
if (!sBluetoothGattInterface) {
|
||||
BT_LOGR("Error: Bluetooth GATT interface not available");
|
||||
if (aRes) {
|
||||
aRes->OnError(NS_ERROR_FAILURE);
|
||||
auto btInf = BluetoothInterface::GetInstance();
|
||||
|
||||
if (NS_WARN_IF(!btInf)) {
|
||||
// If there's no Bluetooth interface, we dispatch a runnable
|
||||
// that calls the profile result handler.
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch GATT OnError runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto setupInterface = btInf->GetBluetoothSetupInterface();
|
||||
|
||||
if (NS_WARN_IF(!setupInterface)) {
|
||||
// If there's no Setup interface, we dispatch a runnable
|
||||
// that calls the profile result handler.
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch GATT OnError runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto gattInterface = btInf->GetBluetoothGattInterface();
|
||||
|
||||
if (NS_WARN_IF(!gattInterface)) {
|
||||
// If there's no GATT interface, we dispatch a runnable
|
||||
// that calls the profile result handler.
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch GATT OnError runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -434,30 +510,43 @@ BluetoothGattManager::InitGattInterface(BluetoothProfileResultHandler* aRes)
|
|||
sServers = new nsTArray<nsRefPtr<BluetoothGattServer> >;
|
||||
}
|
||||
|
||||
BluetoothGattManager* gattManager = BluetoothGattManager::Get();
|
||||
sBluetoothGattInterface->Init(gattManager,
|
||||
new InitGattResultHandler(aRes));
|
||||
// Set notification handler _before_ registering the module. It could
|
||||
// happen that we receive notifications, before the result handler runs.
|
||||
gattInterface->SetNotificationHandler(BluetoothGattManager::Get());
|
||||
|
||||
setupInterface->RegisterModule(
|
||||
SETUP_SERVICE_ID_GATT, 0, MAX_NUM_CLIENTS,
|
||||
new RegisterModuleResultHandler(gattInterface, aRes));
|
||||
}
|
||||
|
||||
class BluetoothGattManager::CleanupResultHandler final
|
||||
: public BluetoothGattResultHandler
|
||||
class BluetoothGattManager::UnregisterModuleResultHandler final
|
||||
: public BluetoothSetupResultHandler
|
||||
{
|
||||
public:
|
||||
CleanupResultHandler(BluetoothProfileResultHandler* aRes)
|
||||
: mRes(aRes)
|
||||
UnregisterModuleResultHandler(BluetoothProfileResultHandler* aRes)
|
||||
: mRes(aRes)
|
||||
{ }
|
||||
|
||||
void OnError(BluetoothStatus aStatus) override
|
||||
{
|
||||
BT_WARNING("BluetoothGattInterface::Cleanup failed: %d",
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
BT_WARNING("BluetoothSetupInterface::UnregisterModule failed for GATT: %d",
|
||||
(int)aStatus);
|
||||
|
||||
sBluetoothGattInterface->SetNotificationHandler(nullptr);
|
||||
sBluetoothGattInterface = nullptr;
|
||||
|
||||
if (mRes) {
|
||||
mRes->OnError(NS_ERROR_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void Cleanup() override
|
||||
void UnregisterModule() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
sBluetoothGattInterface->SetNotificationHandler(nullptr);
|
||||
sBluetoothGattInterface = nullptr;
|
||||
sClients = nullptr;
|
||||
sServers = nullptr;
|
||||
|
@ -471,24 +560,33 @@ private:
|
|||
nsRefPtr<BluetoothProfileResultHandler> mRes;
|
||||
};
|
||||
|
||||
class BluetoothGattManager::CleanupResultHandlerRunnable final
|
||||
class BluetoothGattManager::DeinitProfileResultHandlerRunnable final
|
||||
: public nsRunnable
|
||||
{
|
||||
public:
|
||||
CleanupResultHandlerRunnable(BluetoothProfileResultHandler* aRes)
|
||||
: mRes(aRes)
|
||||
DeinitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
|
||||
nsresult aRv)
|
||||
: mRes(aRes)
|
||||
, mRv(aRv)
|
||||
{
|
||||
MOZ_ASSERT(mRes);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
mRes->Deinit();
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (NS_SUCCEEDED(mRv)) {
|
||||
mRes->Deinit();
|
||||
} else {
|
||||
mRes->OnError(mRv);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<BluetoothProfileResultHandler> mRes;
|
||||
nsresult mRv;
|
||||
};
|
||||
|
||||
// static
|
||||
|
@ -497,16 +595,45 @@ BluetoothGattManager::DeinitGattInterface(BluetoothProfileResultHandler* aRes)
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (sBluetoothGattInterface) {
|
||||
sBluetoothGattInterface->Cleanup(new CleanupResultHandler(aRes));
|
||||
} else if (aRes) {
|
||||
// We dispatch a runnable here to make the profile resource handler
|
||||
// behave as if GATT was initialized.
|
||||
nsRefPtr<nsRunnable> r = new CleanupResultHandlerRunnable(aRes);
|
||||
if (!sBluetoothGattInterface) {
|
||||
BT_LOGR("Bluetooth GATT interface has not been initalized.");
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new DeinitProfileResultHandlerRunnable(aRes, NS_OK);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch cleanup-result-handler runnable");
|
||||
BT_LOGR("Failed to dispatch GATT Deinit runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto btInf = BluetoothInterface::GetInstance();
|
||||
|
||||
if (NS_WARN_IF(!btInf)) {
|
||||
// If there's no backend interface, we dispatch a runnable
|
||||
// that calls the profile result handler.
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch GATT OnError runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto setupInterface = btInf->GetBluetoothSetupInterface();
|
||||
|
||||
if (NS_WARN_IF(!setupInterface)) {
|
||||
// If there's no Setup interface, we dispatch a runnable
|
||||
// that calls the profile result handler.
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch GATT OnError runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
setupInterface->UnregisterModule(
|
||||
SETUP_SERVICE_ID_GATT,
|
||||
new UnregisterModuleResultHandler(aRes));
|
||||
}
|
||||
|
||||
class BluetoothGattManager::RegisterClientResultHandler final
|
||||
|
|
|
@ -20,6 +20,8 @@ class BluetoothGattManager final : public nsIObserver
|
|||
, public BluetoothGattNotificationHandler
|
||||
{
|
||||
public:
|
||||
static const int MAX_NUM_CLIENTS;
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
|
@ -165,9 +167,16 @@ public:
|
|||
private:
|
||||
~BluetoothGattManager();
|
||||
|
||||
#if 0
|
||||
class CleanupResultHandler;
|
||||
class CleanupResultHandlerRunnable;
|
||||
class InitGattResultHandler;
|
||||
#endif
|
||||
class DeinitProfileResultHandlerRunnable;
|
||||
class InitProfileResultHandlerRunnable;
|
||||
class RegisterModuleResultHandler;
|
||||
class UnregisterModuleResultHandler;
|
||||
|
||||
class RegisterClientResultHandler;
|
||||
class UnregisterClientResultHandler;
|
||||
class StartLeScanResultHandler;
|
||||
|
|
|
@ -276,12 +276,12 @@ BluetoothHfpManager::Init()
|
|||
return true;
|
||||
}
|
||||
|
||||
class BluetoothHfpManager::CleanupInitResultHandler final
|
||||
: public BluetoothHandsfreeResultHandler
|
||||
class BluetoothHfpManager::RegisterModuleResultHandler final
|
||||
: public BluetoothSetupResultHandler
|
||||
{
|
||||
public:
|
||||
CleanupInitResultHandler(BluetoothHandsfreeInterface* aInterface,
|
||||
BluetoothProfileResultHandler* aRes)
|
||||
RegisterModuleResultHandler(BluetoothHandsfreeInterface* aInterface,
|
||||
BluetoothProfileResultHandler* aRes)
|
||||
: mInterface(aInterface)
|
||||
, mRes(aRes)
|
||||
{
|
||||
|
@ -290,68 +290,40 @@ public:
|
|||
|
||||
void OnError(BluetoothStatus aStatus) override
|
||||
{
|
||||
BT_WARNING("BluetoothHandsfreeInterface::Init failed: %d", (int)aStatus);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
BT_WARNING("BluetoothSetupInterface::RegisterModule failed for HFP: %d",
|
||||
(int)aStatus);
|
||||
|
||||
mInterface->SetNotificationHandler(nullptr);
|
||||
|
||||
if (mRes) {
|
||||
mRes->OnError(NS_ERROR_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void Init() override
|
||||
void RegisterModule() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
sBluetoothHfpInterface = mInterface;
|
||||
|
||||
if (mRes) {
|
||||
mRes->Init();
|
||||
}
|
||||
}
|
||||
|
||||
void Cleanup() override
|
||||
{
|
||||
sBluetoothHfpInterface = nullptr;
|
||||
/* During re-initialization, a previouly initialized
|
||||
* |BluetoothHandsfreeInterface| has now been cleaned
|
||||
* up, so we start initialization.
|
||||
*/
|
||||
RunInit();
|
||||
}
|
||||
|
||||
void RunInit()
|
||||
{
|
||||
BluetoothHfpManager* hfpManager = BluetoothHfpManager::Get();
|
||||
|
||||
mInterface->Init(hfpManager, BluetoothHfpManager::MAX_NUM_CLIENTS, this);
|
||||
}
|
||||
|
||||
private:
|
||||
BluetoothHandsfreeInterface* mInterface;
|
||||
nsRefPtr<BluetoothProfileResultHandler> mRes;
|
||||
};
|
||||
|
||||
class BluetoothHfpManager::InitResultHandlerRunnable final
|
||||
class BluetoothHfpManager::InitProfileResultHandlerRunnable final
|
||||
: public nsRunnable
|
||||
{
|
||||
public:
|
||||
InitResultHandlerRunnable(CleanupInitResultHandler* aRes)
|
||||
: mRes(aRes)
|
||||
{
|
||||
MOZ_ASSERT(mRes);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
mRes->RunInit();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<CleanupInitResultHandler> mRes;
|
||||
};
|
||||
|
||||
class BluetoothHfpManager::OnErrorProfileResultHandlerRunnable final
|
||||
: public nsRunnable
|
||||
{
|
||||
public:
|
||||
OnErrorProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
|
||||
nsresult aRv)
|
||||
InitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
|
||||
nsresult aRv)
|
||||
: mRes(aRes)
|
||||
, mRv(aRv)
|
||||
{
|
||||
|
@ -360,7 +332,13 @@ public:
|
|||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
mRes->OnError(mRv);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (NS_SUCCEEDED(mRv)) {
|
||||
mRes->Init();
|
||||
} else {
|
||||
mRes->OnError(mRv);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -373,45 +351,64 @@ private:
|
|||
void
|
||||
BluetoothHfpManager::InitHfpInterface(BluetoothProfileResultHandler* aRes)
|
||||
{
|
||||
BluetoothInterface* btInf = BluetoothInterface::GetInstance();
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (sBluetoothHfpInterface) {
|
||||
BT_LOGR("Bluetooth Handsfree interface is already initalized.");
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new InitProfileResultHandlerRunnable(aRes, NS_OK);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch HFP Init runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto btInf = BluetoothInterface::GetInstance();
|
||||
|
||||
if (NS_WARN_IF(!btInf)) {
|
||||
// If there's no backend interface, we dispatch a runnable
|
||||
// that calls the profile result handler.
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new OnErrorProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch HFP OnError runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
BluetoothHandsfreeInterface *interface =
|
||||
btInf->GetBluetoothHandsfreeInterface();
|
||||
auto setupInterface = btInf->GetBluetoothSetupInterface();
|
||||
|
||||
if (NS_WARN_IF(!setupInterface)) {
|
||||
// If there's no Setup interface, we dispatch a runnable
|
||||
// that calls the profile result handler.
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch HFP OnError runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto interface = btInf->GetBluetoothHandsfreeInterface();
|
||||
|
||||
if (NS_WARN_IF(!interface)) {
|
||||
// If there's no HFP interface, we dispatch a runnable
|
||||
// that calls the profile result handler.
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new OnErrorProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch HFP OnError runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<CleanupInitResultHandler> res =
|
||||
new CleanupInitResultHandler(interface, aRes);
|
||||
// Set notification handler _before_ registering the module. It could
|
||||
// happen that we receive notifications, before the result handler runs.
|
||||
interface->SetNotificationHandler(BluetoothHfpManager::Get());
|
||||
|
||||
if (sBluetoothHfpInterface) {
|
||||
// Cleanup an initialized HFP before initializing again.
|
||||
sBluetoothHfpInterface->Cleanup(res);
|
||||
} else {
|
||||
// If there's no HFP interface to cleanup first, we dispatch
|
||||
// a runnable that calls the profile result handler.
|
||||
nsRefPtr<nsRunnable> r = new InitResultHandlerRunnable(res);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch HFP init runnable");
|
||||
}
|
||||
}
|
||||
setupInterface->RegisterModule(
|
||||
SETUP_SERVICE_ID_HANDSFREE, MODE_NARROWBAND_SPEECH, MAX_NUM_CLIENTS,
|
||||
new RegisterModuleResultHandler(interface, aRes));
|
||||
}
|
||||
|
||||
BluetoothHfpManager::~BluetoothHfpManager()
|
||||
|
@ -432,18 +429,22 @@ BluetoothHfpManager::~BluetoothHfpManager()
|
|||
hal::UnregisterBatteryObserver(this);
|
||||
}
|
||||
|
||||
class BluetoothHfpManager::CleanupResultHandler final
|
||||
: public BluetoothHandsfreeResultHandler
|
||||
class BluetoothHfpManager::UnregisterModuleResultHandler final
|
||||
: public BluetoothSetupResultHandler
|
||||
{
|
||||
public:
|
||||
CleanupResultHandler(BluetoothProfileResultHandler* aRes)
|
||||
UnregisterModuleResultHandler(BluetoothProfileResultHandler* aRes)
|
||||
: mRes(aRes)
|
||||
{ }
|
||||
|
||||
void OnError(BluetoothStatus aStatus) override
|
||||
{
|
||||
BT_WARNING("BluetoothHandsfreeInterface::Cleanup failed: %d", (int)aStatus);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
BT_WARNING("BluetoothSetupInterface::UnregisterModule failed for HFP: %d",
|
||||
(int)aStatus);
|
||||
|
||||
sBluetoothHfpInterface->SetNotificationHandler(nullptr);
|
||||
sBluetoothHfpInterface = nullptr;
|
||||
|
||||
if (mRes) {
|
||||
|
@ -451,9 +452,13 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void Cleanup() override
|
||||
void UnregisterModule() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
sBluetoothHfpInterface->SetNotificationHandler(nullptr);
|
||||
sBluetoothHfpInterface = nullptr;
|
||||
|
||||
if (mRes) {
|
||||
mRes->Deinit();
|
||||
}
|
||||
|
@ -463,40 +468,80 @@ private:
|
|||
nsRefPtr<BluetoothProfileResultHandler> mRes;
|
||||
};
|
||||
|
||||
class BluetoothHfpManager::DeinitResultHandlerRunnable final
|
||||
class BluetoothHfpManager::DeinitProfileResultHandlerRunnable final
|
||||
: public nsRunnable
|
||||
{
|
||||
public:
|
||||
DeinitResultHandlerRunnable(BluetoothProfileResultHandler* aRes)
|
||||
DeinitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
|
||||
nsresult aRv)
|
||||
: mRes(aRes)
|
||||
, mRv(aRv)
|
||||
{
|
||||
MOZ_ASSERT(mRes);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
mRes->Deinit();
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (NS_SUCCEEDED(mRv)) {
|
||||
mRes->Deinit();
|
||||
} else {
|
||||
mRes->OnError(mRv);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<BluetoothProfileResultHandler> mRes;
|
||||
nsresult mRv;
|
||||
};
|
||||
|
||||
// static
|
||||
void
|
||||
BluetoothHfpManager::DeinitHfpInterface(BluetoothProfileResultHandler* aRes)
|
||||
{
|
||||
if (sBluetoothHfpInterface) {
|
||||
sBluetoothHfpInterface->Cleanup(new CleanupResultHandler(aRes));
|
||||
} else if (aRes) {
|
||||
// We dispatch a runnable here to make the profile resource handler
|
||||
// behave as if HFP was initialized.
|
||||
nsRefPtr<nsRunnable> r = new DeinitResultHandlerRunnable(aRes);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!sBluetoothHfpInterface) {
|
||||
BT_LOGR("Bluetooth Handsfree interface has not been initialized.");
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new DeinitProfileResultHandlerRunnable(aRes, NS_OK);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch cleanup-result-handler runnable");
|
||||
BT_LOGR("Failed to dispatch HFP Deinit runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto btInf = BluetoothInterface::GetInstance();
|
||||
|
||||
if (NS_WARN_IF(!btInf)) {
|
||||
// If there's no backend interface, we dispatch a runnable
|
||||
// that calls the profile result handler.
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch HFP OnError runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto setupInterface = btInf->GetBluetoothSetupInterface();
|
||||
|
||||
if (NS_WARN_IF(!setupInterface)) {
|
||||
// If there's no Setup interface, we dispatch a runnable
|
||||
// that calls the profile result handler.
|
||||
nsRefPtr<nsRunnable> r =
|
||||
new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(r))) {
|
||||
BT_LOGR("Failed to dispatch HFP OnError runnable");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
setupInterface->UnregisterModule(
|
||||
SETUP_SERVICE_ID_HANDSFREE,
|
||||
new UnregisterModuleResultHandler(aRes));
|
||||
}
|
||||
|
||||
//static
|
||||
|
|
|
@ -73,6 +73,12 @@ class BluetoothHfpManager : public BluetoothHfpManagerBase
|
|||
, public BluetoothHandsfreeNotificationHandler
|
||||
, public BatteryObserver
|
||||
{
|
||||
enum {
|
||||
MODE_HEADSET = 0x00,
|
||||
MODE_NARROWBAND_SPEECH = 0x01,
|
||||
MODE_NARRAWBAND_WIDEBAND_SPEECH = 0x02
|
||||
};
|
||||
|
||||
public:
|
||||
BT_DECL_HFP_MGR_BASE
|
||||
|
||||
|
@ -151,21 +157,20 @@ private:
|
|||
class ConnectResultHandler;
|
||||
class CopsResponseResultHandler;
|
||||
class ClccResponseResultHandler;
|
||||
class CleanupInitResultHandler;
|
||||
class CleanupResultHandler;
|
||||
class CloseScoRunnable;
|
||||
class CloseScoTask;
|
||||
class DeinitResultHandlerRunnable;
|
||||
class DeinitProfileResultHandlerRunnable;
|
||||
class DeviceStatusNotificationResultHandler;
|
||||
class DisconnectAudioResultHandler;
|
||||
class DisconnectResultHandler;
|
||||
class FormattedAtResponseResultHandler;
|
||||
class GetVolumeTask;
|
||||
class InitResultHandlerRunnable;
|
||||
class InitProfileResultHandlerRunnable;
|
||||
class MainThreadTask;
|
||||
class OnErrorProfileResultHandlerRunnable;
|
||||
class PhoneStateChangeResultHandler;
|
||||
class RegisterModuleResultHandler;
|
||||
class RespondToBLDNTask;
|
||||
class UnregisterModuleResultHandler;
|
||||
class VolumeControlResultHandler;
|
||||
|
||||
friend class BluetoothHfpManagerObserver;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "mozilla/Compiler.h"
|
||||
#include "mozilla/Observer.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
@ -325,6 +326,23 @@ enum BluetoothBondState {
|
|||
BOND_STATE_BONDED
|
||||
};
|
||||
|
||||
enum BluetoothSetupServiceId {
|
||||
SETUP_SERVICE_ID_SETUP,
|
||||
SETUP_SERVICE_ID_CORE,
|
||||
SETUP_SERVICE_ID_SOCKET,
|
||||
SETUP_SERVICE_ID_HID,
|
||||
SETUP_SERVICE_ID_PAN,
|
||||
SETUP_SERVICE_ID_HANDSFREE,
|
||||
SETUP_SERVICE_ID_A2DP,
|
||||
SETUP_SERVICE_ID_HEALTH,
|
||||
SETUP_SERVICE_ID_AVRCP,
|
||||
SETUP_SERVICE_ID_GATT,
|
||||
SETUP_SERVICE_ID_HANDSFREE_CLIENT,
|
||||
SETUP_SERVICE_ID_MAP_CLIENT,
|
||||
SETUP_SERVICE_ID_AVRCP_CONTROLLER,
|
||||
SETUP_SERVICE_ID_A2DP_SINK
|
||||
};
|
||||
|
||||
/* Physical transport for GATT connections to remote dual-mode devices */
|
||||
enum BluetoothTransport {
|
||||
TRANSPORT_AUTO, /* No preference of physical transport */
|
||||
|
@ -480,6 +498,12 @@ struct BluetoothAddress {
|
|||
|
||||
};
|
||||
|
||||
struct BluetoothConfigurationParameter {
|
||||
uint8_t mType;
|
||||
uint16_t mLength;
|
||||
nsAutoArrayPtr<uint8_t> mValue;
|
||||
};
|
||||
|
||||
struct BluetoothUuid {
|
||||
uint8_t mUuid[16];
|
||||
|
||||
|
|
|
@ -39,6 +39,12 @@ void
|
|||
BluetoothSetupResultHandler::Configuration()
|
||||
{ }
|
||||
|
||||
// Interface
|
||||
//
|
||||
|
||||
BluetoothSetupInterface::~BluetoothSetupInterface()
|
||||
{ }
|
||||
|
||||
//
|
||||
// Socket Interface
|
||||
//
|
||||
|
@ -181,14 +187,6 @@ BluetoothHandsfreeResultHandler::OnError(BluetoothStatus aStatus)
|
|||
BT_WARNING("Received error code %d", (int)aStatus);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothHandsfreeResultHandler::Init()
|
||||
{ }
|
||||
|
||||
void
|
||||
BluetoothHandsfreeResultHandler::Cleanup()
|
||||
{ }
|
||||
|
||||
void
|
||||
BluetoothHandsfreeResultHandler::Connect()
|
||||
{ }
|
||||
|
@ -295,14 +293,6 @@ BluetoothA2dpResultHandler::OnError(BluetoothStatus aStatus)
|
|||
BT_WARNING("Received error code %d", (int)aStatus);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothA2dpResultHandler::Init()
|
||||
{ }
|
||||
|
||||
void
|
||||
BluetoothA2dpResultHandler::Cleanup()
|
||||
{ }
|
||||
|
||||
void
|
||||
BluetoothA2dpResultHandler::Connect()
|
||||
{ }
|
||||
|
@ -400,14 +390,6 @@ BluetoothAvrcpResultHandler::OnError(BluetoothStatus aStatus)
|
|||
BT_WARNING("Received error code %d", (int)aStatus);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothAvrcpResultHandler::Init()
|
||||
{ }
|
||||
|
||||
void
|
||||
BluetoothAvrcpResultHandler::Cleanup()
|
||||
{ }
|
||||
|
||||
void
|
||||
BluetoothAvrcpResultHandler::GetPlayStatusRsp()
|
||||
{ }
|
||||
|
@ -679,14 +661,6 @@ BluetoothGattResultHandler::OnError(BluetoothStatus aStatus)
|
|||
BT_WARNING("Received error code %d", (int)aStatus);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothGattResultHandler::Init()
|
||||
{ }
|
||||
|
||||
void
|
||||
BluetoothGattResultHandler::Cleanup()
|
||||
{ }
|
||||
|
||||
void
|
||||
BluetoothGattResultHandler::RegisterClient()
|
||||
{ }
|
||||
|
|
|
@ -30,6 +30,25 @@ protected:
|
|||
virtual ~BluetoothSetupResultHandler() { }
|
||||
};
|
||||
|
||||
class BluetoothSetupInterface
|
||||
{
|
||||
public:
|
||||
virtual void RegisterModule(BluetoothSetupServiceId aId,
|
||||
uint8_t aMode,
|
||||
uint32_t aMaxNumClients,
|
||||
BluetoothSetupResultHandler* aRes) = 0;
|
||||
|
||||
virtual void UnregisterModule(BluetoothSetupServiceId aId,
|
||||
BluetoothSetupResultHandler* aRes) = 0;
|
||||
|
||||
virtual void Configuration(const BluetoothConfigurationParameter* aParam,
|
||||
uint8_t aLen,
|
||||
BluetoothSetupResultHandler* aRes) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~BluetoothSetupInterface();
|
||||
};
|
||||
|
||||
//
|
||||
// Socket Interface
|
||||
//
|
||||
|
@ -155,9 +174,6 @@ class BluetoothHandsfreeResultHandler
|
|||
public:
|
||||
virtual void OnError(BluetoothStatus aStatus);
|
||||
|
||||
virtual void Init();
|
||||
virtual void Cleanup();
|
||||
|
||||
virtual void Connect();
|
||||
virtual void Disconnect();
|
||||
virtual void ConnectAudio();
|
||||
|
@ -186,10 +202,8 @@ protected:
|
|||
class BluetoothHandsfreeInterface
|
||||
{
|
||||
public:
|
||||
virtual void Init(
|
||||
BluetoothHandsfreeNotificationHandler* aNotificationHandler,
|
||||
int aMaxNumClients, BluetoothHandsfreeResultHandler* aRes) = 0;
|
||||
virtual void Cleanup(BluetoothHandsfreeResultHandler* aRes) = 0;
|
||||
virtual void SetNotificationHandler(
|
||||
BluetoothHandsfreeNotificationHandler* aNotificationHandler) = 0;
|
||||
|
||||
/* Connect / Disconnect */
|
||||
|
||||
|
@ -295,8 +309,6 @@ class BluetoothA2dpResultHandler
|
|||
public:
|
||||
virtual void OnError(BluetoothStatus aStatus);
|
||||
|
||||
virtual void Init();
|
||||
virtual void Cleanup();
|
||||
virtual void Connect();
|
||||
virtual void Disconnect();
|
||||
|
||||
|
@ -307,9 +319,8 @@ protected:
|
|||
class BluetoothA2dpInterface
|
||||
{
|
||||
public:
|
||||
virtual void Init(BluetoothA2dpNotificationHandler* aNotificationHandler,
|
||||
BluetoothA2dpResultHandler* aRes) = 0;
|
||||
virtual void Cleanup(BluetoothA2dpResultHandler* aRes) = 0;
|
||||
virtual void SetNotificationHandler(
|
||||
BluetoothA2dpNotificationHandler* aNotificationHandler) = 0;
|
||||
|
||||
virtual void Connect(const BluetoothAddress& aBdAddr,
|
||||
BluetoothA2dpResultHandler* aRes) = 0;
|
||||
|
@ -382,9 +393,6 @@ class BluetoothAvrcpResultHandler
|
|||
public:
|
||||
virtual void OnError(BluetoothStatus aStatus);
|
||||
|
||||
virtual void Init();
|
||||
virtual void Cleanup();
|
||||
|
||||
virtual void GetPlayStatusRsp();
|
||||
|
||||
virtual void ListPlayerAppAttrRsp();
|
||||
|
@ -409,9 +417,8 @@ protected:
|
|||
class BluetoothAvrcpInterface
|
||||
{
|
||||
public:
|
||||
virtual void Init(BluetoothAvrcpNotificationHandler* aNotificationHandler,
|
||||
BluetoothAvrcpResultHandler* aRes) = 0;
|
||||
virtual void Cleanup(BluetoothAvrcpResultHandler* aRes) = 0;
|
||||
virtual void SetNotificationHandler(
|
||||
BluetoothAvrcpNotificationHandler* aNotificationHandler) = 0;
|
||||
|
||||
virtual void GetPlayStatusRsp(ControlPlayStatus aPlayStatus,
|
||||
uint32_t aSongLen, uint32_t aSongPos,
|
||||
|
@ -663,9 +670,6 @@ class BluetoothGattResultHandler
|
|||
public:
|
||||
virtual void OnError(BluetoothStatus aStatus);
|
||||
|
||||
virtual void Init();
|
||||
virtual void Cleanup();
|
||||
|
||||
virtual void RegisterClient();
|
||||
virtual void UnregisterClient();
|
||||
|
||||
|
@ -723,9 +727,8 @@ protected:
|
|||
class BluetoothGattInterface
|
||||
{
|
||||
public:
|
||||
virtual void Init(BluetoothGattNotificationHandler* aNotificationHandler,
|
||||
BluetoothGattResultHandler* aRes) = 0;
|
||||
virtual void Cleanup(BluetoothGattResultHandler* aRes) = 0;
|
||||
virtual void SetNotificationHandler(
|
||||
BluetoothGattNotificationHandler* aNotificationHandler) = 0;
|
||||
|
||||
/* Register / Unregister */
|
||||
virtual void RegisterClient(const BluetoothUuid& aUuid,
|
||||
|
@ -1111,6 +1114,7 @@ public:
|
|||
|
||||
/* Profile Interfaces */
|
||||
|
||||
virtual BluetoothSetupInterface* GetBluetoothSetupInterface() = 0;
|
||||
virtual BluetoothSocketInterface* GetBluetoothSocketInterface() = 0;
|
||||
virtual BluetoothHandsfreeInterface* GetBluetoothHandsfreeInterface() = 0;
|
||||
virtual BluetoothA2dpInterface* GetBluetoothA2dpInterface() = 0;
|
||||
|
|
|
@ -998,7 +998,7 @@ BrowserElementParent.prototype = {
|
|||
try {
|
||||
let nfcContentHelper =
|
||||
Cc["@mozilla.org/nfc/content-helper;1"].getService(Ci.nsINfcBrowserAPI);
|
||||
nfcContentHelper.setFocusApp(tabId, isFocus);
|
||||
nfcContentHelper.setFocusTab(tabId, isFocus);
|
||||
} catch(e) {
|
||||
// Not all platforms support NFC
|
||||
}
|
||||
|
|
|
@ -97,8 +97,8 @@ NfcContentHelper.prototype = {
|
|||
return cpmm.sendSyncMessage("NFC:QueryInfo")[0].rfState;
|
||||
},
|
||||
|
||||
setFocusApp: function setFocusApp(tabId, isFocus) {
|
||||
cpmm.sendAsyncMessage("NFC:SetFocusApp", {
|
||||
setFocusTab: function setFocusTab(tabId, isFocus) {
|
||||
cpmm.sendAsyncMessage("NFC:SetFocusTab", {
|
||||
tabId: tabId,
|
||||
isFocus: isFocus
|
||||
});
|
||||
|
|
|
@ -79,7 +79,7 @@ const NFC_IPC_MSG_ENTRIES = [
|
|||
"NFC:NotifyUserAcceptedP2P",
|
||||
"NFC:NotifySendFileStatus",
|
||||
"NFC:ChangeRFState",
|
||||
"NFC:SetFocusApp"] }
|
||||
"NFC:SetFocusTab"] }
|
||||
];
|
||||
|
||||
// Should be consistent with NfcRequestType defined in NfcOptions.webidl.
|
||||
|
@ -148,7 +148,7 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
|
|||
|
||||
eventListeners: {},
|
||||
|
||||
focusApp: NFC.SYSTEM_APP_ID,
|
||||
focusId: NFC.SYSTEM_APP_ID,
|
||||
|
||||
init: function init(nfc) {
|
||||
this.nfc = nfc;
|
||||
|
@ -214,7 +214,7 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
|
|||
});
|
||||
},
|
||||
|
||||
notifyFocusApp: function notifyFocusApp(options) {
|
||||
notifyFocusTab: function notifyFocusTab(options) {
|
||||
let tabId = this.getFocusTabId();
|
||||
options.tabId = tabId;
|
||||
|
||||
|
@ -231,29 +231,29 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
|
|||
},
|
||||
|
||||
getFocusTabId: function getFocusTabId() {
|
||||
return this.eventListeners[this.focusApp] ? this.focusApp
|
||||
: NFC.SYSTEM_APP_ID;
|
||||
return this.eventListeners[this.focusId] ? this.focusId
|
||||
: NFC.SYSTEM_APP_ID;
|
||||
},
|
||||
|
||||
setFocusApp: function setFocusApp(id, isFocus) {
|
||||
setFocusTab: function setFocusTab(id, isFocus) {
|
||||
// if calling setNFCFocus(true) on the browser-element which is already
|
||||
// focused, or calling setNFCFocus(false) on the browser-element which has
|
||||
// lost focus already, ignore.
|
||||
if (isFocus == (id == this.focusApp)) {
|
||||
if (isFocus == (id == this.focusId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.focusApp != NFC.SYSTEM_APP_ID) {
|
||||
this.onFocusChanged(this.focusApp, false);
|
||||
if (this.focusId != NFC.SYSTEM_APP_ID) {
|
||||
this.onFocusChanged(this.focusId, false);
|
||||
}
|
||||
|
||||
if (isFocus) {
|
||||
// Now we only support one focus app.
|
||||
this.focusApp = id;
|
||||
this.onFocusChanged(this.focusApp, true);
|
||||
} else if (this.focusApp == id){
|
||||
// Set focusApp to SystemApp means currently there is no foreground app.
|
||||
this.focusApp = NFC.SYSTEM_APP_ID;
|
||||
this.focusId = id;
|
||||
this.onFocusChanged(this.focusId, true);
|
||||
} else if (this.focusId == id){
|
||||
// Set focusId to SystemApp means currently there is no foreground app.
|
||||
this.focusId = NFC.SYSTEM_APP_ID;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -325,17 +325,17 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
|
|||
|
||||
onTagFound: function onTagFound(message) {
|
||||
message.event = NFC.TAG_EVENT_FOUND;
|
||||
this.notifyFocusApp(message);
|
||||
this.notifyFocusTab(message);
|
||||
delete message.event;
|
||||
},
|
||||
|
||||
onTagLost: function onTagLost(sessionToken) {
|
||||
this.notifyFocusApp({ event: NFC.TAG_EVENT_LOST,
|
||||
this.notifyFocusTab({ event: NFC.TAG_EVENT_LOST,
|
||||
sessionToken: sessionToken });
|
||||
},
|
||||
|
||||
onPeerEvent: function onPeerEvent(eventType, sessionToken) {
|
||||
this.notifyFocusApp({ event: eventType,
|
||||
this.notifyFocusTab({ event: eventType,
|
||||
sessionToken: sessionToken });
|
||||
},
|
||||
|
||||
|
@ -348,13 +348,13 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
|
|||
}
|
||||
},
|
||||
|
||||
onFocusChanged: function onFocusChanged(focusApp, focus) {
|
||||
let target = this.eventListeners[focusApp];
|
||||
onFocusChanged: function onFocusChanged(focusId, focus) {
|
||||
let target = this.eventListeners[focusId];
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.notifyDOMEvent(target, { tabId: this.focusApp,
|
||||
this.notifyDOMEvent(target, { tabId: this.focusId,
|
||||
event: NFC.FOCUS_CHANGED,
|
||||
focus: focus });
|
||||
},
|
||||
|
@ -386,8 +386,8 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
|
|||
}
|
||||
|
||||
switch (message.name) {
|
||||
case "NFC:SetFocusApp":
|
||||
this.setFocusApp(message.data.tabId, message.data.isFocus);
|
||||
case "NFC:SetFocusTab":
|
||||
this.setFocusTab(message.data.tabId, message.data.isFocus);
|
||||
return null;
|
||||
case "NFC:AddEventListener":
|
||||
this.addEventListener(message.target, message.data.tabId);
|
||||
|
|
|
@ -115,12 +115,12 @@ interface nsINfcRequestCallback : nsISupports
|
|||
void notifyError(in DOMString errorMsg);
|
||||
};
|
||||
|
||||
[scriptable, uuid(2dbc73d4-ba16-4c89-bce5-3c22cee6b50a)]
|
||||
[scriptable, uuid(9f86c799-6959-4ad2-bdd6-6fbf49b52d1c)]
|
||||
interface nsINfcBrowserAPI : nsISupports
|
||||
{
|
||||
const int32_t SYSTEM_APP_ID = 0;
|
||||
|
||||
void setFocusApp(in uint64_t tabId,
|
||||
void setFocusTab(in uint64_t tabId,
|
||||
in boolean isFocus);
|
||||
};
|
||||
|
||||
|
|
|
@ -243,7 +243,7 @@ class BaseStackFrame {
|
|||
|
||||
// Get a unique identifier for this StackFrame. The identifier is not valid
|
||||
// across garbage collections.
|
||||
virtual uint64_t identifier() const { return reinterpret_cast<uint64_t>(ptr); }
|
||||
virtual uint64_t identifier() const { return uint64_t(uintptr_t(ptr)); }
|
||||
|
||||
// Get this frame's parent frame.
|
||||
virtual StackFrame parent() const = 0;
|
||||
|
@ -418,7 +418,11 @@ class StackFrame : public JS::Traceable {
|
|||
// Methods that forward to virtual calls through BaseStackFrame.
|
||||
|
||||
void trace(JSTracer* trc) { base()->trace(trc); }
|
||||
uint64_t identifier() const { return base()->identifier(); }
|
||||
uint64_t identifier() const {
|
||||
auto id = base()->identifier();
|
||||
MOZ_ASSERT(JS::Value::isNumberRepresentable(id));
|
||||
return id;
|
||||
}
|
||||
uint32_t line() const { return base()->line(); }
|
||||
uint32_t column() const { return base()->column(); }
|
||||
AtomOrTwoByteChars source() const { return base()->source(); }
|
||||
|
@ -564,7 +568,7 @@ class Base {
|
|||
// caveats about multiple objects allocated at the same address for
|
||||
// 'ubi::Node::operator=='.)
|
||||
using Id = uint64_t;
|
||||
virtual Id identifier() const { return reinterpret_cast<Id>(ptr); }
|
||||
virtual Id identifier() const { return Id(uintptr_t(ptr)); }
|
||||
|
||||
// Returns true if this node is pointing to something on the live heap, as
|
||||
// opposed to something from a deserialized core dump. Returns false,
|
||||
|
@ -790,7 +794,11 @@ class Node {
|
|||
}
|
||||
|
||||
using Id = Base::Id;
|
||||
Id identifier() const { return base()->identifier(); }
|
||||
Id identifier() const {
|
||||
auto id = base()->identifier();
|
||||
MOZ_ASSERT(JS::Value::isNumberRepresentable(id));
|
||||
return id;
|
||||
}
|
||||
|
||||
// A hash policy for ubi::Nodes.
|
||||
// This simply uses the stock PointerHasher on the ubi::Node's pointer.
|
||||
|
|
Двоичные данные
mobile/android/base/resources/drawable-hdpi/menu_item_check.png
До Ширина: | Высота: | Размер: 785 B После Ширина: | Высота: | Размер: 502 B |
До Ширина: | Высота: | Размер: 90 B После Ширина: | Высота: | Размер: 254 B |
Двоичные данные
mobile/android/base/resources/drawable-xhdpi/menu_item_check.png
До Ширина: | Высота: | Размер: 904 B После Ширина: | Высота: | Размер: 555 B |
До Ширина: | Высота: | Размер: 108 B После Ширина: | Высота: | Размер: 315 B |
После Ширина: | Высота: | Размер: 688 B |
После Ширина: | Высота: | Размер: 391 B |
|
@ -732,7 +732,7 @@ nsHttpHandler::InitUserAgentComponents()
|
|||
#endif
|
||||
|
||||
|
||||
#if defined(ANDROID) || defined(FXOS_SIMULATOR)
|
||||
#ifdef ANDROID
|
||||
nsCOMPtr<nsIPropertyBag2> infoService = do_GetService("@mozilla.org/system-info;1");
|
||||
MOZ_ASSERT(infoService, "Could not find a system info service");
|
||||
nsresult rv;
|
||||
|
@ -753,15 +753,35 @@ nsHttpHandler::InitUserAgentComponents()
|
|||
}
|
||||
}
|
||||
#endif
|
||||
// Add the `Mobile` or `Tablet` token when running on device or in the
|
||||
// b2g desktop simulator.
|
||||
// Add the `Mobile` or `Tablet` or `TV` token when running on device.
|
||||
bool isTablet;
|
||||
rv = infoService->GetPropertyAsBool(NS_LITERAL_STRING("tablet"), &isTablet);
|
||||
if (NS_SUCCEEDED(rv) && isTablet)
|
||||
if (NS_SUCCEEDED(rv) && isTablet) {
|
||||
mCompatDevice.AssignLiteral("Tablet");
|
||||
else
|
||||
mCompatDevice.AssignLiteral("Mobile");
|
||||
#endif
|
||||
} else {
|
||||
bool isTV;
|
||||
rv = infoService->GetPropertyAsBool(NS_LITERAL_STRING("tv"), &isTV);
|
||||
if (NS_SUCCEEDED(rv) && isTV) {
|
||||
mCompatDevice.AssignLiteral("TV");
|
||||
} else {
|
||||
mCompatDevice.AssignLiteral("Mobile");
|
||||
}
|
||||
}
|
||||
#endif // ANDROID
|
||||
|
||||
#ifdef FXOS_SIMULATOR
|
||||
{
|
||||
// Add the `Mobile` or `Tablet` or `TV` token when running in the b2g
|
||||
// desktop simulator via preference.
|
||||
nsCString deviceType;
|
||||
nsresult rv = Preferences::GetCString("devtools.useragent.device_type", &deviceType);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mCompatDevice.Assign(deviceType);
|
||||
} else {
|
||||
mCompatDevice.AssignLiteral("Mobile");
|
||||
}
|
||||
}
|
||||
#endif // FXOS_SIMULATOR
|
||||
|
||||
#if defined(MOZ_WIDGET_GONK)
|
||||
// Device model identifier should be a simple token, which can be composed
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
.Python
|
||||
bin/
|
||||
include/
|
||||
lib/
|
||||
talos.egg-info
|
||||
talos/tests/tp5n.zip
|
||||
talos/tests/tp5n
|
||||
talos/tests/devtools/damp.manifest.develop
|
|
@ -358,6 +358,7 @@ class damp(PageloaderTest):
|
|||
sps_profile_entries = 1000000
|
||||
win_counters = w7_counters = linux_counters = mac_counters = None
|
||||
filters = filter.ignore_first.prepare(1) + filter.median.prepare()
|
||||
preferences = {'devtools.memory.enabled': True}
|
||||
|
||||
|
||||
@register_test()
|
||||
|
|
|
@ -16,6 +16,7 @@ var defaultConfig = {
|
|||
styleEditorOpen: true,
|
||||
performanceOpen: true,
|
||||
netmonitorOpen: true,
|
||||
saveAndReadHeapSnapshot: true,
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -25,7 +26,8 @@ var testsInfo = {
|
|||
debuggerOpen: "Measure open/close toolbox on debugger panel",
|
||||
styleEditorOpen: "Measure open/close toolbox on style editor panel",
|
||||
performanceOpen: "Measure open/close toolbox on performance panel",
|
||||
netmonitorOpen: "Measure open/close toolbox on network monitor panel"
|
||||
netmonitorOpen: "Measure open/close toolbox on network monitor panel",
|
||||
saveAndReadHeapSnapshot: "Measure open/close toolbox on memory panel and save/read heap snapshot",
|
||||
};
|
||||
|
||||
function updateConfig() {
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
|
||||
Components.utils.import("resource:///modules/devtools/client/framework/gDevTools.jsm");
|
||||
const {devtools} =
|
||||
Components.utils.import("resource://gre/modules/devtools/shared/Loader.jsm", {});
|
||||
const { getActiveTab } = devtools.require("sdk/tabs/utils");
|
||||
const { getMostRecentBrowserWindow } = devtools.require("sdk/window/utils");
|
||||
const ThreadSafeChromeUtils = devtools.require("ThreadSafeChromeUtils");
|
||||
|
||||
const SIMPLE_URL = "chrome://damp/content/pages/simple.html";
|
||||
const COMPLICATED_URL = "http://localhost/tests/tp5n/bild.de/www.bild.de/index.html";
|
||||
|
||||
function Damp() {
|
||||
// Path to the temp file where the heap snapshot file is saved. Set by
|
||||
// saveHeapSnapshot and read by readHeapSnapshot.
|
||||
this._heapSnapshotFilePath = null;
|
||||
// HeapSnapshot instance. Set by readHeapSnapshot, used by takeCensus.
|
||||
this._snapshot = null;
|
||||
}
|
||||
|
||||
Damp.prototype = {
|
||||
|
@ -69,6 +74,71 @@ Damp.prototype = {
|
|||
});
|
||||
},
|
||||
|
||||
saveHeapSnapshot: function(label) {
|
||||
let tab = getActiveTab(getMostRecentBrowserWindow());
|
||||
let target = devtools.TargetFactory.forTab(tab);
|
||||
let toolbox = gDevTools.getToolbox(target);
|
||||
let panel = toolbox.getCurrentPanel();
|
||||
let memoryFront = panel.panelWin.gFront;
|
||||
|
||||
let start = performance.now();
|
||||
return memoryFront.saveHeapSnapshot().then(filePath => {
|
||||
this._heapSnapshotFilePath = filePath;
|
||||
let end = performance.now();
|
||||
this._results.push({
|
||||
name: label + ".saveHeapSnapshot",
|
||||
value: end - start
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
readHeapSnapshot: function(label) {
|
||||
let start = performance.now();
|
||||
this._snapshot = ThreadSafeChromeUtils.readHeapSnapshot(this._heapSnapshotFilePath);
|
||||
let end = performance.now();
|
||||
this._results.push({
|
||||
name: label + ".readHeapSnapshot",
|
||||
value: end - start
|
||||
});
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
takeCensus: function(label) {
|
||||
let start = performance.now();
|
||||
|
||||
this._snapshot.takeCensus({
|
||||
breakdown: {
|
||||
by: "coarseType",
|
||||
objects: {
|
||||
by: "objectClass",
|
||||
then: { by: "count", bytes: true, count: true },
|
||||
other: { by: "count", bytes: true, count: true }
|
||||
},
|
||||
strings: {
|
||||
by: "internalType",
|
||||
then: { by: "count", bytes: true, count: true }
|
||||
},
|
||||
scripts: {
|
||||
by: "internalType",
|
||||
then: { by: "count", bytes: true, count: true }
|
||||
},
|
||||
other: {
|
||||
by: "internalType",
|
||||
then: { by: "count", bytes: true, count: true }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let end = performance.now();
|
||||
|
||||
this._results.push({
|
||||
name: label + ".takeCensus",
|
||||
value: end - start
|
||||
});
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
_startTest: function() {
|
||||
|
||||
var self = this;
|
||||
|
@ -76,6 +146,9 @@ Damp.prototype = {
|
|||
var closeToolbox = this.closeToolbox.bind(this);
|
||||
var reloadPage = this.reloadPage.bind(this);
|
||||
var next = this._nextCommand.bind(this);
|
||||
var saveHeapSnapshot = this.saveHeapSnapshot.bind(this);
|
||||
var readHeapSnapshot = this.readHeapSnapshot.bind(this);
|
||||
var takeCensus = this.takeCensus.bind(this);
|
||||
var config = this._config;
|
||||
var rest = config.rest; // How long to wait in between opening the tab and starting the test.
|
||||
|
||||
|
@ -135,6 +208,14 @@ Damp.prototype = {
|
|||
() => { closeToolbox(label + ".netmonitor").then(next); },
|
||||
],
|
||||
|
||||
saveAndReadHeapSnapshot: [
|
||||
() => { openToolbox(label + ".memory", "memory").then(next); },
|
||||
() => { reloadPage(label + ".memory").then(next); },
|
||||
() => { saveHeapSnapshot(label).then(next); },
|
||||
() => { readHeapSnapshot(label).then(next); },
|
||||
() => { takeCensus(label).then(next); },
|
||||
() => { closeToolbox(label + ".memory").then(next); },
|
||||
]
|
||||
};
|
||||
|
||||
// Construct the sequence array: config.repeat times config.subtests,
|
||||
|
|
|
@ -186,6 +186,8 @@ tests:
|
|||
allowed_build_tasks:
|
||||
tasks/builds/b2g_emulator_ics_opt.yml:
|
||||
task: tasks/tests/b2g_emulator_crashtest.yml
|
||||
tasks/builds/b2g_emulator_x86_kk_opt.yml:
|
||||
task: tasks/tests/b2g_emulator_crashtest.yml
|
||||
gaia-build:
|
||||
allowed_build_tasks:
|
||||
tasks/builds/b2g_desktop_opt.yml:
|
||||
|
|
|
@ -32,7 +32,9 @@ task:
|
|||
extra:
|
||||
chunks:
|
||||
total: 5
|
||||
|
||||
treeherderEnv:
|
||||
- production
|
||||
- staging
|
||||
treeherder:
|
||||
groupName: Reftest
|
||||
groupSymbol: tc-R
|
||||
|
|
|
@ -13,6 +13,8 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "devtools",
|
||||
"resource://gre/modules/devtools/shared/Loader.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerContent",
|
||||
"resource://gre/modules/LoginManagerContent.jsm");
|
||||
|
||||
Object.defineProperty(this, "WebConsoleUtils", {
|
||||
get: function() {
|
||||
|
@ -47,50 +49,6 @@ this.InsecurePasswordUtils = {
|
|||
Services.console.logMessage(consoleMsg);
|
||||
},
|
||||
|
||||
/*
|
||||
* Checks whether the passed uri is secure
|
||||
* Check Protocol Flags to determine if scheme is secure:
|
||||
* URI_DOES_NOT_RETURN_DATA - e.g.
|
||||
* "mailto"
|
||||
* URI_IS_LOCAL_RESOURCE - e.g.
|
||||
* "data",
|
||||
* "resource",
|
||||
* "moz-icon"
|
||||
* URI_INHERITS_SECURITY_CONTEXT - e.g.
|
||||
* "javascript"
|
||||
* URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT - e.g.
|
||||
* "https",
|
||||
* "moz-safe-about"
|
||||
*
|
||||
* The use of this logic comes directly from nsMixedContentBlocker.cpp
|
||||
* At the time it was decided to include these protocols since a secure
|
||||
* uri for mixed content blocker means that the resource can't be
|
||||
* easily tampered with because 1) it is sent over an encrypted channel or
|
||||
* 2) it is a local resource that never hits the network
|
||||
* or 3) it is a request sent without any response that could alter
|
||||
* the behavior of the page. It was decided to include the same logic
|
||||
* here both to be consistent with MCB and to make sure we cover all
|
||||
* "safe" protocols. Eventually, the code here and the code in MCB
|
||||
* will be moved to a common location that will be referenced from
|
||||
* both places. Look at
|
||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=899099 for more info.
|
||||
*/
|
||||
_checkIfURIisSecure : function(uri) {
|
||||
let isSafe = false;
|
||||
let netutil = Cc["@mozilla.org/network/util;1"].getService(Ci.nsINetUtil);
|
||||
let ph = Ci.nsIProtocolHandler;
|
||||
|
||||
if (netutil.URIChainHasFlags(uri, ph.URI_IS_LOCAL_RESOURCE) ||
|
||||
netutil.URIChainHasFlags(uri, ph.URI_DOES_NOT_RETURN_DATA) ||
|
||||
netutil.URIChainHasFlags(uri, ph.URI_INHERITS_SECURITY_CONTEXT) ||
|
||||
netutil.URIChainHasFlags(uri, ph.URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT)) {
|
||||
|
||||
isSafe = true;
|
||||
}
|
||||
|
||||
return isSafe;
|
||||
},
|
||||
|
||||
/*
|
||||
* Checks whether the passed nested document is insecure
|
||||
* or is inside an insecure parent document.
|
||||
|
@ -109,7 +67,7 @@ this.InsecurePasswordUtils = {
|
|||
// We are at the top, nothing to check here
|
||||
return false;
|
||||
}
|
||||
if (!this._checkIfURIisSecure(uri)) {
|
||||
if (!LoginManagerContent.checkIfURIisSecure(uri)) {
|
||||
// We are insecure
|
||||
return true;
|
||||
}
|
||||
|
@ -127,7 +85,7 @@ this.InsecurePasswordUtils = {
|
|||
checkForInsecurePasswords : function (aForm) {
|
||||
var domDoc = aForm.ownerDocument;
|
||||
let pageURI = domDoc.defaultView.top.document.documentURIObject;
|
||||
let isSafePage = this._checkIfURIisSecure(pageURI);
|
||||
let isSafePage = LoginManagerContent.checkIfURIisSecure(pageURI);
|
||||
|
||||
if (!isSafePage) {
|
||||
this._sendWebConsoleMessage("InsecurePasswordsPresentOnPage", domDoc);
|
||||
|
|
|
@ -414,6 +414,18 @@ var LoginManagerContent = {
|
|||
return null;
|
||||
};
|
||||
|
||||
// Returns true if this window or any subframes have insecure login forms.
|
||||
let hasInsecureLoginForms = (thisWindow, parentIsInsecure) => {
|
||||
let doc = thisWindow.document;
|
||||
let isInsecure =
|
||||
parentIsInsecure ||
|
||||
!this.checkIfURIisSecure(doc.documentURIObject);
|
||||
let hasLoginForm = !!this.stateForDocument(doc).loginForm;
|
||||
return (hasLoginForm && isInsecure) ||
|
||||
Array.some(thisWindow.frames,
|
||||
frame => hasInsecureLoginForms(frame, isInsecure));
|
||||
};
|
||||
|
||||
// Store the actual form to use on the state for the top-level document.
|
||||
let topState = this.stateForDocument(topWindow.document);
|
||||
topState.loginFormForFill = getFirstLoginForm(topWindow);
|
||||
|
@ -423,6 +435,7 @@ var LoginManagerContent = {
|
|||
messageManager.sendAsyncMessage("RemoteLogins:updateLoginFormPresence", {
|
||||
loginFormOrigin,
|
||||
loginFormPresent: !!topState.loginFormForFill,
|
||||
hasInsecureLoginForms: hasInsecureLoginForms(topWindow, false),
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -1089,6 +1102,49 @@ var LoginManagerContent = {
|
|||
};
|
||||
},
|
||||
|
||||
/*
|
||||
* Checks whether the passed uri is secure
|
||||
* Check Protocol Flags to determine if scheme is secure:
|
||||
* URI_DOES_NOT_RETURN_DATA - e.g.
|
||||
* "mailto"
|
||||
* URI_IS_LOCAL_RESOURCE - e.g.
|
||||
* "data",
|
||||
* "resource",
|
||||
* "moz-icon"
|
||||
* URI_INHERITS_SECURITY_CONTEXT - e.g.
|
||||
* "javascript"
|
||||
* URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT - e.g.
|
||||
* "https",
|
||||
* "moz-safe-about"
|
||||
*
|
||||
* The use of this logic comes directly from nsMixedContentBlocker.cpp
|
||||
* At the time it was decided to include these protocols since a secure
|
||||
* uri for mixed content blocker means that the resource can't be
|
||||
* easily tampered with because 1) it is sent over an encrypted channel or
|
||||
* 2) it is a local resource that never hits the network
|
||||
* or 3) it is a request sent without any response that could alter
|
||||
* the behavior of the page. It was decided to include the same logic
|
||||
* here both to be consistent with MCB and to make sure we cover all
|
||||
* "safe" protocols. Eventually, the code here and the code in MCB
|
||||
* will be moved to a common location that will be referenced from
|
||||
* both places. Look at
|
||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=899099 for more info.
|
||||
*/
|
||||
checkIfURIisSecure : function(uri) {
|
||||
let isSafe = false;
|
||||
let netutil = Cc["@mozilla.org/network/util;1"].getService(Ci.nsINetUtil);
|
||||
let ph = Ci.nsIProtocolHandler;
|
||||
|
||||
if (netutil.URIChainHasFlags(uri, ph.URI_IS_LOCAL_RESOURCE) ||
|
||||
netutil.URIChainHasFlags(uri, ph.URI_DOES_NOT_RETURN_DATA) ||
|
||||
netutil.URIChainHasFlags(uri, ph.URI_INHERITS_SECURITY_CONTEXT) ||
|
||||
netutil.URIChainHasFlags(uri, ph.URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT)) {
|
||||
|
||||
isSafe = true;
|
||||
}
|
||||
|
||||
return isSafe;
|
||||
},
|
||||
};
|
||||
|
||||
var LoginUtils = {
|
||||
|
|
|
@ -569,12 +569,23 @@ var LoginManagerParent = {
|
|||
return loginFormState;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if the page currently loaded in the given browser element has
|
||||
* insecure login forms. This state may be updated asynchronously, in which
|
||||
* case a custom event named InsecureLoginFormsStateChange will be dispatched
|
||||
* on the browser element.
|
||||
*/
|
||||
hasInsecureLoginForms(browser) {
|
||||
return !!this.stateForBrowser(browser).hasInsecureLoginForms;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called to indicate whether a login form on the currently loaded page is
|
||||
* present or not. This is one of the factors used to control the visibility
|
||||
* of the password fill doorhanger.
|
||||
*/
|
||||
updateLoginFormPresence(browser, { loginFormOrigin, loginFormPresent }) {
|
||||
updateLoginFormPresence(browser, { loginFormOrigin, loginFormPresent,
|
||||
hasInsecureLoginForms }) {
|
||||
const ANCHOR_DELAY_MS = 200;
|
||||
|
||||
let state = this.stateForBrowser(browser);
|
||||
|
@ -583,8 +594,13 @@ var LoginManagerParent = {
|
|||
// processed in order, this will always be the latest version to use.
|
||||
state.loginFormOrigin = loginFormOrigin;
|
||||
state.loginFormPresent = loginFormPresent;
|
||||
state.hasInsecureLoginForms = hasInsecureLoginForms;
|
||||
|
||||
// Apply the data to the currently displayed icon later.
|
||||
// Report the insecure login form state immediately.
|
||||
browser.dispatchEvent(new browser.ownerDocument.defaultView
|
||||
.CustomEvent("InsecureLoginFormsStateChange"));
|
||||
|
||||
// Apply the data to the currently displayed login fill icon later.
|
||||
if (!state.anchorDeferredTask) {
|
||||
state.anchorDeferredTask = new DeferredTask(
|
||||
() => this.updateLoginAnchor(browser),
|
||||
|
|
|
@ -2,11 +2,14 @@
|
|||
support-files =
|
||||
authenticate.sjs
|
||||
form_basic.html
|
||||
insecure_test.html
|
||||
insecure_test_subframe.html
|
||||
multiple_forms.html
|
||||
|
||||
[browser_DOMFormHasPassword.js]
|
||||
[browser_DOMInputPasswordAdded.js]
|
||||
[browser_filldoorhanger.js]
|
||||
[browser_hasInsecureLoginForms.js]
|
||||
[browser_notifications.js]
|
||||
skip-if = true # Intermittent failures: Bug 1182296, bug 1148771
|
||||
[browser_passwordmgr_editing.js]
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource://gre/modules/LoginManagerParent.jsm", this);
|
||||
|
||||
const testUrlPath =
|
||||
"://example.com/browser/toolkit/components/passwordmgr/test/browser/";
|
||||
|
||||
/**
|
||||
* Waits for the given number of occurrences of InsecureLoginFormsStateChange
|
||||
* on the given browser element.
|
||||
*/
|
||||
function waitForInsecureLoginFormsStateChange(browser, count) {
|
||||
return BrowserTestUtils.waitForEvent(browser, "InsecureLoginFormsStateChange",
|
||||
false, () => --count == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that hasInsecureLoginForms is true for a simple HTTP page and false
|
||||
* for a simple HTTPS page.
|
||||
*/
|
||||
add_task(function* test_simple() {
|
||||
for (let scheme of ["http", "https"]) {
|
||||
let tab = gBrowser.addTab(scheme + testUrlPath + "form_basic.html");
|
||||
let browser = tab.linkedBrowser;
|
||||
yield Promise.all([
|
||||
BrowserTestUtils.switchTab(gBrowser, tab),
|
||||
BrowserTestUtils.browserLoaded(browser),
|
||||
// One event is triggered by pageshow and one by DOMFormHasPassword.
|
||||
waitForInsecureLoginFormsStateChange(browser, 2),
|
||||
]);
|
||||
|
||||
Assert.equal(LoginManagerParent.hasInsecureLoginForms(browser),
|
||||
scheme == "http");
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Checks that hasInsecureLoginForms is true if a password field is present in
|
||||
* an HTTP page loaded as a subframe of a top-level HTTPS page, when mixed
|
||||
* active content blocking is disabled.
|
||||
*
|
||||
* When the subframe is navigated to an HTTPS page, hasInsecureLoginForms should
|
||||
* be set to false.
|
||||
*
|
||||
* Moving back in history should set hasInsecureLoginForms to true again.
|
||||
*/
|
||||
add_task(function* test_subframe_navigation() {
|
||||
yield new Promise(resolve => SpecialPowers.pushPrefEnv({
|
||||
"set": [["security.mixed_content.block_active_content", false]],
|
||||
}, resolve));
|
||||
|
||||
// Load the page with the subframe in a new tab.
|
||||
let tab = gBrowser.addTab("https" + testUrlPath + "insecure_test.html");
|
||||
let browser = tab.linkedBrowser;
|
||||
yield Promise.all([
|
||||
BrowserTestUtils.switchTab(gBrowser, tab),
|
||||
BrowserTestUtils.browserLoaded(browser),
|
||||
// Two events are triggered by pageshow and one by DOMFormHasPassword.
|
||||
waitForInsecureLoginFormsStateChange(browser, 3),
|
||||
]);
|
||||
|
||||
Assert.ok(LoginManagerParent.hasInsecureLoginForms(browser));
|
||||
|
||||
// Navigate the subframe to a secure page.
|
||||
let promiseSubframeReady = Promise.all([
|
||||
BrowserTestUtils.browserLoaded(browser, true),
|
||||
// One event is triggered by pageshow and one by DOMFormHasPassword.
|
||||
waitForInsecureLoginFormsStateChange(browser, 2),
|
||||
]);
|
||||
yield ContentTask.spawn(browser, null, function* () {
|
||||
content.document.getElementById("test-iframe")
|
||||
.contentDocument.getElementById("test-link").click();
|
||||
});
|
||||
yield promiseSubframeReady;
|
||||
|
||||
Assert.ok(!LoginManagerParent.hasInsecureLoginForms(browser));
|
||||
|
||||
// Navigate back to the insecure page. We only have to wait for the
|
||||
// InsecureLoginFormsStateChange event that is triggered by pageshow.
|
||||
let promise = waitForInsecureLoginFormsStateChange(browser, 1);
|
||||
yield ContentTask.spawn(browser, null, function* () {
|
||||
content.document.getElementById("test-iframe")
|
||||
.contentWindow.history.back();
|
||||
});
|
||||
yield promise;
|
||||
|
||||
Assert.ok(LoginManagerParent.hasInsecureLoginForms(browser));
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
|
||||
<!-- This frame is initially loaded over HTTP. -->
|
||||
<iframe id="test-iframe"
|
||||
src="http://example.org/browser/toolkit/components/passwordmgr/test/browser/insecure_test_subframe.html"/>
|
||||
|
||||
</body></html>
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
|
||||
<form>
|
||||
<input name="password" type="password">
|
||||
</form>
|
||||
|
||||
<!-- Link to reload this page over HTTPS. -->
|
||||
<a id="test-link"
|
||||
href="https://example.org/browser/toolkit/components/passwordmgr/test/browser/insecure_test_subframe.html">HTTPS</a>
|
||||
|
||||
</body></html>
|
|
@ -810,11 +810,13 @@ var StackRenderer = {
|
|||
}
|
||||
};
|
||||
|
||||
function SymbolicationRequest(aPrefix, aRenderHeader, aMemoryMap, aStacks) {
|
||||
function SymbolicationRequest(aPrefix, aRenderHeader,
|
||||
aMemoryMap, aStacks, aDurations = null) {
|
||||
this.prefix = aPrefix;
|
||||
this.renderHeader = aRenderHeader;
|
||||
this.memoryMap = aMemoryMap;
|
||||
this.stacks = aStacks;
|
||||
this.durations = aDurations;
|
||||
}
|
||||
/**
|
||||
* A callback for onreadystatechange. It replaces the numeric stack with
|
||||
|
@ -848,7 +850,7 @@ function SymbolicationRequest_handleSymbolResponse() {
|
|||
|
||||
for (let i = 0; i < jsonResponse.length; ++i) {
|
||||
let stack = jsonResponse[i];
|
||||
this.renderHeader(i);
|
||||
this.renderHeader(i, this.durations);
|
||||
|
||||
for (let symbol of stack) {
|
||||
div.appendChild(document.createTextNode(symbol));
|
||||
|
@ -894,14 +896,14 @@ var ChromeHangs = {
|
|||
|
||||
let stacks = hangs.stacks;
|
||||
let memoryMap = hangs.memoryMap;
|
||||
let durations = hangs.durations;
|
||||
|
||||
StackRenderer.renderStacks("chrome-hangs", stacks, memoryMap,
|
||||
(index) => this.renderHangHeader(aPing, index));
|
||||
(index) => this.renderHangHeader(index, durations));
|
||||
},
|
||||
|
||||
renderHangHeader: function ChromeHangs_renderHangHeader(aPing, aIndex) {
|
||||
let durations = aPing.payload.chromeHangs.durations;
|
||||
StackRenderer.renderHeader("chrome-hangs", [aIndex + 1, durations[aIndex]]);
|
||||
renderHangHeader: function ChromeHangs_renderHangHeader(aIndex, aDurations) {
|
||||
StackRenderer.renderHeader("chrome-hangs", [aIndex + 1, aDurations[aIndex]]);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1391,7 +1393,9 @@ function setupListeners() {
|
|||
let hangs = gPingData.payload.chromeHangs;
|
||||
let req = new SymbolicationRequest("chrome-hangs",
|
||||
ChromeHangs.renderHangHeader,
|
||||
hangs.memoryMap, hangs.stacks);
|
||||
hangs.memoryMap,
|
||||
hangs.stacks,
|
||||
hangs.durations);
|
||||
req.fetchSymbols();
|
||||
}, false);
|
||||
|
||||
|
|
|
@ -743,6 +743,8 @@ nsSystemInfo::Init()
|
|||
if (__system_property_get("ro.build.characteristics", characteristics)) {
|
||||
if (!strcmp(characteristics, "tablet")) {
|
||||
SetPropertyAsBool(NS_LITERAL_STRING("tablet"), true);
|
||||
} else if (!strcmp(characteristics, "tv")) {
|
||||
SetPropertyAsBool(NS_LITERAL_STRING("tv"), true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|