mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central to b2g-inbound
This commit is contained in:
commit
64ebf55ec9
@ -2058,7 +2058,7 @@ DocAccessible::ValidateARIAOwned()
|
|||||||
for (uint32_t idx = 0; idx < childEls->Length(); idx++) {
|
for (uint32_t idx = 0; idx < childEls->Length(); idx++) {
|
||||||
nsIContent* childEl = childEls->ElementAt(idx);
|
nsIContent* childEl = childEls->ElementAt(idx);
|
||||||
Accessible* child = GetAccessible(childEl);
|
Accessible* child = GetAccessible(childEl);
|
||||||
if (child && !child->GetFrame()) {
|
if (child && child->IsInDocument() && !child->GetFrame()) {
|
||||||
UpdateTreeOnRemoval(child->Parent(), childEl);
|
UpdateTreeOnRemoval(child->Parent(), childEl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,6 @@ pref("browser.cache.memory_limit", 2048); // 2 MB
|
|||||||
|
|
||||||
/* image cache prefs */
|
/* image cache prefs */
|
||||||
pref("image.cache.size", 1048576); // bytes
|
pref("image.cache.size", 1048576); // bytes
|
||||||
pref("image.high_quality_downscaling.enabled", false);
|
|
||||||
pref("canvas.image.cache.limit", 20971520); // 20 MB
|
pref("canvas.image.cache.limit", 20971520); // 20 MB
|
||||||
|
|
||||||
/* offline cache prefs */
|
/* offline cache prefs */
|
||||||
|
@ -679,11 +679,6 @@
|
|||||||
@RESPATH@/components/ActivityWrapper.js
|
@RESPATH@/components/ActivityWrapper.js
|
||||||
@RESPATH@/components/ActivityMessageConfigurator.js
|
@RESPATH@/components/ActivityMessageConfigurator.js
|
||||||
|
|
||||||
@RESPATH@/components/TCPSocket.js
|
|
||||||
@RESPATH@/components/TCPServerSocket.js
|
|
||||||
@RESPATH@/components/TCPSocketParentIntermediary.js
|
|
||||||
@RESPATH@/components/TCPSocket.manifest
|
|
||||||
|
|
||||||
@RESPATH@/components/Payment.js
|
@RESPATH@/components/Payment.js
|
||||||
@RESPATH@/components/PaymentFlowInfo.js
|
@RESPATH@/components/PaymentFlowInfo.js
|
||||||
@RESPATH@/components/PaymentProvider.js
|
@RESPATH@/components/PaymentProvider.js
|
||||||
|
@ -1693,6 +1693,7 @@ pref("image.mem.max_decoded_image_kb", 256000);
|
|||||||
pref("loop.enabled", true);
|
pref("loop.enabled", true);
|
||||||
pref("loop.textChat.enabled", true);
|
pref("loop.textChat.enabled", true);
|
||||||
pref("loop.server", "https://loop.services.mozilla.com/v0");
|
pref("loop.server", "https://loop.services.mozilla.com/v0");
|
||||||
|
pref("loop.linkClicker.url", "https://hello.firefox.com/");
|
||||||
pref("loop.gettingStarted.seen", false);
|
pref("loop.gettingStarted.seen", false);
|
||||||
pref("loop.gettingStarted.url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/hello/start/");
|
pref("loop.gettingStarted.url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/hello/start/");
|
||||||
pref("loop.gettingStarted.resumeOnFirstJoin", false);
|
pref("loop.gettingStarted.resumeOnFirstJoin", false);
|
||||||
|
@ -214,10 +214,6 @@ var gFxAccounts = {
|
|||||||
this.showDoorhanger("sync-start-panel");
|
this.showDoorhanger("sync-start-panel");
|
||||||
},
|
},
|
||||||
|
|
||||||
showSyncFailedDoorhanger: function () {
|
|
||||||
this.showDoorhanger("sync-error-panel");
|
|
||||||
},
|
|
||||||
|
|
||||||
updateUI: function () {
|
updateUI: function () {
|
||||||
this.updateAppMenuItem();
|
this.updateAppMenuItem();
|
||||||
this.updateMigrationNotification();
|
this.updateMigrationNotification();
|
||||||
|
@ -449,28 +449,6 @@
|
|||||||
</hbox>
|
</hbox>
|
||||||
</panel>
|
</panel>
|
||||||
|
|
||||||
<!-- Sync Error Panel -->
|
|
||||||
<panel id="sync-error-panel" class="sync-panel" type="arrow" hidden="true"
|
|
||||||
noautofocus="true" onclick="this.hidePopup();"
|
|
||||||
flip="slide">
|
|
||||||
<hbox class="sync-panel-outer">
|
|
||||||
<image class="sync-panel-icon"/>
|
|
||||||
<vbox class="sync-panel-inner">
|
|
||||||
<description id="sync-error-panel-title"
|
|
||||||
value="&syncErrorPanel.heading;"/>
|
|
||||||
<description id="sync-error-panel-subtitle"
|
|
||||||
value="&syncErrorPanel.subTitle;"/>
|
|
||||||
<hbox class="sync-panel-button-box">
|
|
||||||
<spacer flex="1"/>
|
|
||||||
<button class="sync-panel-button"
|
|
||||||
label="&syncErrorPanel.signInButton.label;"
|
|
||||||
accesskey="&syncErrorPanel.signInButton.accesskey;"
|
|
||||||
onclick="gFxAccounts.openSignInAgainPage();"/>
|
|
||||||
</hbox>
|
|
||||||
</vbox>
|
|
||||||
</hbox>
|
|
||||||
</panel>
|
|
||||||
|
|
||||||
<!-- Bookmarks and history tooltip -->
|
<!-- Bookmarks and history tooltip -->
|
||||||
<tooltip id="bhTooltip"/>
|
<tooltip id="bhTooltip"/>
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@
|
|||||||
"SocialShare": false,
|
"SocialShare": false,
|
||||||
"Task": false,
|
"Task": false,
|
||||||
"UITour": false,
|
"UITour": false,
|
||||||
|
"WebChannel": false,
|
||||||
"XPCOMUtils": false,
|
"XPCOMUtils": false,
|
||||||
"uuidgen": true,
|
"uuidgen": true,
|
||||||
// Test Related
|
// Test Related
|
||||||
|
@ -607,89 +607,6 @@ html[dir="rtl"] .room-entry-context-actions > .dropdown-menu {
|
|||||||
width: 16px;
|
width: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Buttons */
|
|
||||||
|
|
||||||
.button-group {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
width: 100%;
|
|
||||||
padding-top: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-group > .button {
|
|
||||||
flex: 1;
|
|
||||||
margin: 0 5px;
|
|
||||||
min-height: 3rem;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
line-height: 1rem;
|
|
||||||
font-weight: 300;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-group > .button:first-child {
|
|
||||||
-moz-margin-start: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-group > .button:last-child {
|
|
||||||
-moz-margin-end: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
padding: 2px 5px;
|
|
||||||
background-color: #fbfbfb;
|
|
||||||
color: #333;
|
|
||||||
border-radius: 2px;
|
|
||||||
min-height: 26px;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
line-height: 1.2rem;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button:hover {
|
|
||||||
background-color: #ebebeb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button:active {
|
|
||||||
background-color: #ccc;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button.button-accept {
|
|
||||||
background-color: #00a9dc;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button.button-accept:hover,
|
|
||||||
.button.button-accept:hover:active {
|
|
||||||
background-color: #5cccee;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button.button-cancel {
|
|
||||||
background-color: #ebebeb;
|
|
||||||
border: 0;
|
|
||||||
color: #000;
|
|
||||||
width: 105px; /* based on fixed width of Cancel button from mockup */
|
|
||||||
flex: 0 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button.button-cancel:hover,
|
|
||||||
.button.button-cancel:hover:active {
|
|
||||||
background-color: #dcd6d6;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button.button-cancel:disabled {
|
|
||||||
background-color: #ebebeb;
|
|
||||||
color: #c3c3c3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button.button-accept:active {
|
|
||||||
background-color: #3aa689;
|
|
||||||
border-color: #3aa689;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-close {
|
.button-close {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
background-image: url(../shared/img/icons-10x10.svg#close);
|
background-image: url(../shared/img/icons-10x10.svg#close);
|
||||||
|
@ -719,6 +719,7 @@ loop.conversationViews = (function(mozL10n) {
|
|||||||
mozLoop: this.props.mozLoop,
|
mozLoop: this.props.mozLoop,
|
||||||
publishStream: this.publishStream,
|
publishStream: this.publishStream,
|
||||||
settingsMenuItems: settingsMenuItems,
|
settingsMenuItems: settingsMenuItems,
|
||||||
|
show: true,
|
||||||
video: this.props.video})
|
video: this.props.video})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -719,6 +719,7 @@ loop.conversationViews = (function(mozL10n) {
|
|||||||
mozLoop={this.props.mozLoop}
|
mozLoop={this.props.mozLoop}
|
||||||
publishStream={this.publishStream}
|
publishStream={this.publishStream}
|
||||||
settingsMenuItems={settingsMenuItems}
|
settingsMenuItems={settingsMenuItems}
|
||||||
|
show={true}
|
||||||
video={this.props.video} />
|
video={this.props.video} />
|
||||||
</sharedViews.MediaLayoutView>
|
</sharedViews.MediaLayoutView>
|
||||||
</div>
|
</div>
|
||||||
|
@ -975,7 +975,7 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
React.createElement("div", {className: "new-room-view"},
|
React.createElement("div", {className: "new-room-view"},
|
||||||
React.createElement("div", {className: contextClasses},
|
React.createElement("div", {className: contextClasses},
|
||||||
React.createElement(Checkbox, {checked: this.state.checked,
|
React.createElement(Checkbox, {checked: this.state.checked,
|
||||||
label: mozL10n.get("context_inroom_label"),
|
label: mozL10n.get("context_inroom_label2"),
|
||||||
onChange: this.onCheckboxChange}),
|
onChange: this.onCheckboxChange}),
|
||||||
React.createElement(sharedViews.ContextUrlView, {
|
React.createElement(sharedViews.ContextUrlView, {
|
||||||
allowClick: false,
|
allowClick: false,
|
||||||
|
@ -975,7 +975,7 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
<div className="new-room-view">
|
<div className="new-room-view">
|
||||||
<div className={contextClasses}>
|
<div className={contextClasses}>
|
||||||
<Checkbox checked={this.state.checked}
|
<Checkbox checked={this.state.checked}
|
||||||
label={mozL10n.get("context_inroom_label")}
|
label={mozL10n.get("context_inroom_label2")}
|
||||||
onChange={this.onCheckboxChange} />
|
onChange={this.onCheckboxChange} />
|
||||||
<sharedViews.ContextUrlView
|
<sharedViews.ContextUrlView
|
||||||
allowClick={false}
|
allowClick={false}
|
||||||
|
@ -449,25 +449,6 @@ loop.roomViews = (function(mozL10n) {
|
|||||||
mozLoop.telemetryAddValue("LOOP_ROOM_CONTEXT_CLICK", 1);
|
mozLoop.telemetryAddValue("LOOP_ROOM_CONTEXT_CLICK", 1);
|
||||||
},
|
},
|
||||||
|
|
||||||
handleCheckboxChange: function(state) {
|
|
||||||
if (state.checked) {
|
|
||||||
// The checkbox was checked, prefill the fields with the values available
|
|
||||||
// in `availableContext`.
|
|
||||||
var context = this.state.availableContext;
|
|
||||||
this.setState({
|
|
||||||
newRoomURL: context.url,
|
|
||||||
newRoomDescription: context.description,
|
|
||||||
newRoomThumbnail: context.previewImage
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
newRoomURL: "",
|
|
||||||
newRoomDescription: "",
|
|
||||||
newRoomThumbnail: ""
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleFormSubmit: function(event) {
|
handleFormSubmit: function(event) {
|
||||||
event && event.preventDefault();
|
event && event.preventDefault();
|
||||||
|
|
||||||
@ -516,27 +497,13 @@ loop.roomViews = (function(mozL10n) {
|
|||||||
|
|
||||||
var cx = React.addons.classSet;
|
var cx = React.addons.classSet;
|
||||||
var availableContext = this.state.availableContext;
|
var availableContext = this.state.availableContext;
|
||||||
// The checkbox shows as checked when there's already context data
|
|
||||||
// attached to this room.
|
|
||||||
var checked = !!urlDescription;
|
|
||||||
var checkboxLabel = urlDescription || (availableContext && availableContext.url ?
|
|
||||||
availableContext.description : "");
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
React.createElement("div", {className: "room-context"},
|
React.createElement("div", {className: "room-context"},
|
||||||
React.createElement("p", {className: cx({"error": !!this.props.error,
|
React.createElement("p", {className: cx({"error": !!this.props.error,
|
||||||
"error-display-area": true})},
|
"error-display-area": true})},
|
||||||
mozL10n.get("rooms_change_failed_label")
|
mozL10n.get("rooms_change_failed_label")
|
||||||
),
|
),
|
||||||
React.createElement("div", {className: "room-context-label"}, mozL10n.get("context_inroom_label")),
|
React.createElement("h2", {className: "room-context-header"}, mozL10n.get("context_inroom_header")),
|
||||||
React.createElement(sharedViews.Checkbox, {
|
|
||||||
additionalClass: cx({ hide: !checkboxLabel }),
|
|
||||||
checked: checked,
|
|
||||||
disabled: checked,
|
|
||||||
label: checkboxLabel,
|
|
||||||
onChange: this.handleCheckboxChange,
|
|
||||||
useEllipsis: true,
|
|
||||||
value: location}),
|
|
||||||
React.createElement("form", {onSubmit: this.handleFormSubmit},
|
React.createElement("form", {onSubmit: this.handleFormSubmit},
|
||||||
React.createElement("input", {className: "room-context-name",
|
React.createElement("input", {className: "room-context-name",
|
||||||
maxLength: this.maxRoomNameLength,
|
maxLength: this.maxRoomNameLength,
|
||||||
@ -554,16 +521,17 @@ loop.roomViews = (function(mozL10n) {
|
|||||||
onKeyDown: this.handleTextareaKeyDown,
|
onKeyDown: this.handleTextareaKeyDown,
|
||||||
placeholder: mozL10n.get("context_edit_comments_placeholder"),
|
placeholder: mozL10n.get("context_edit_comments_placeholder"),
|
||||||
rows: "2", type: "text",
|
rows: "2", type: "text",
|
||||||
valueLink: this.linkState("newRoomDescription")})
|
valueLink: this.linkState("newRoomDescription")}),
|
||||||
),
|
React.createElement(sharedViews.ButtonGroup, null,
|
||||||
React.createElement("button", {className: "btn btn-info",
|
React.createElement(sharedViews.Button, {additionalClass: "button-cancel",
|
||||||
disabled: this.props.savingContext,
|
caption: mozL10n.get("context_cancel_label"),
|
||||||
onClick: this.handleFormSubmit},
|
onClick: this.handleCloseClick}),
|
||||||
mozL10n.get("context_save_label2")
|
React.createElement(sharedViews.Button, {additionalClass: "button-accept",
|
||||||
),
|
caption: mozL10n.get("context_done_label"),
|
||||||
React.createElement("button", {className: "room-context-btn-close",
|
disabled: this.props.savingContext,
|
||||||
onClick: this.handleCloseClick,
|
onClick: this.handleFormSubmit})
|
||||||
title: mozL10n.get("cancel_button")})
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -819,6 +787,7 @@ loop.roomViews = (function(mozL10n) {
|
|||||||
publishStream: this.publishStream,
|
publishStream: this.publishStream,
|
||||||
screenShare: screenShareData,
|
screenShare: screenShareData,
|
||||||
settingsMenuItems: settingsMenuItems,
|
settingsMenuItems: settingsMenuItems,
|
||||||
|
show: !shouldRenderEditContextView,
|
||||||
video: {enabled: !this.state.videoMuted, visible: true}}),
|
video: {enabled: !this.state.videoMuted, visible: true}}),
|
||||||
React.createElement(DesktopRoomInvitationView, {
|
React.createElement(DesktopRoomInvitationView, {
|
||||||
dispatcher: this.props.dispatcher,
|
dispatcher: this.props.dispatcher,
|
||||||
|
@ -449,25 +449,6 @@ loop.roomViews = (function(mozL10n) {
|
|||||||
mozLoop.telemetryAddValue("LOOP_ROOM_CONTEXT_CLICK", 1);
|
mozLoop.telemetryAddValue("LOOP_ROOM_CONTEXT_CLICK", 1);
|
||||||
},
|
},
|
||||||
|
|
||||||
handleCheckboxChange: function(state) {
|
|
||||||
if (state.checked) {
|
|
||||||
// The checkbox was checked, prefill the fields with the values available
|
|
||||||
// in `availableContext`.
|
|
||||||
var context = this.state.availableContext;
|
|
||||||
this.setState({
|
|
||||||
newRoomURL: context.url,
|
|
||||||
newRoomDescription: context.description,
|
|
||||||
newRoomThumbnail: context.previewImage
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
newRoomURL: "",
|
|
||||||
newRoomDescription: "",
|
|
||||||
newRoomThumbnail: ""
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleFormSubmit: function(event) {
|
handleFormSubmit: function(event) {
|
||||||
event && event.preventDefault();
|
event && event.preventDefault();
|
||||||
|
|
||||||
@ -516,27 +497,13 @@ loop.roomViews = (function(mozL10n) {
|
|||||||
|
|
||||||
var cx = React.addons.classSet;
|
var cx = React.addons.classSet;
|
||||||
var availableContext = this.state.availableContext;
|
var availableContext = this.state.availableContext;
|
||||||
// The checkbox shows as checked when there's already context data
|
|
||||||
// attached to this room.
|
|
||||||
var checked = !!urlDescription;
|
|
||||||
var checkboxLabel = urlDescription || (availableContext && availableContext.url ?
|
|
||||||
availableContext.description : "");
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="room-context">
|
<div className="room-context">
|
||||||
<p className={cx({"error": !!this.props.error,
|
<p className={cx({"error": !!this.props.error,
|
||||||
"error-display-area": true})}>
|
"error-display-area": true})}>
|
||||||
{mozL10n.get("rooms_change_failed_label")}
|
{mozL10n.get("rooms_change_failed_label")}
|
||||||
</p>
|
</p>
|
||||||
<div className="room-context-label">{mozL10n.get("context_inroom_label")}</div>
|
<h2 className="room-context-header">{mozL10n.get("context_inroom_header")}</h2>
|
||||||
<sharedViews.Checkbox
|
|
||||||
additionalClass={cx({ hide: !checkboxLabel })}
|
|
||||||
checked={checked}
|
|
||||||
disabled={checked}
|
|
||||||
label={checkboxLabel}
|
|
||||||
onChange={this.handleCheckboxChange}
|
|
||||||
useEllipsis={true}
|
|
||||||
value={location} />
|
|
||||||
<form onSubmit={this.handleFormSubmit}>
|
<form onSubmit={this.handleFormSubmit}>
|
||||||
<input className="room-context-name"
|
<input className="room-context-name"
|
||||||
maxLength={this.maxRoomNameLength}
|
maxLength={this.maxRoomNameLength}
|
||||||
@ -555,15 +522,16 @@ loop.roomViews = (function(mozL10n) {
|
|||||||
placeholder={mozL10n.get("context_edit_comments_placeholder")}
|
placeholder={mozL10n.get("context_edit_comments_placeholder")}
|
||||||
rows="2" type="text"
|
rows="2" type="text"
|
||||||
valueLink={this.linkState("newRoomDescription")} />
|
valueLink={this.linkState("newRoomDescription")} />
|
||||||
|
<sharedViews.ButtonGroup>
|
||||||
|
<sharedViews.Button additionalClass="button-cancel"
|
||||||
|
caption={mozL10n.get("context_cancel_label")}
|
||||||
|
onClick={this.handleCloseClick} />
|
||||||
|
<sharedViews.Button additionalClass="button-accept"
|
||||||
|
caption={mozL10n.get("context_done_label")}
|
||||||
|
disabled={this.props.savingContext}
|
||||||
|
onClick={this.handleFormSubmit} />
|
||||||
|
</sharedViews.ButtonGroup>
|
||||||
</form>
|
</form>
|
||||||
<button className="btn btn-info"
|
|
||||||
disabled={this.props.savingContext}
|
|
||||||
onClick={this.handleFormSubmit}>
|
|
||||||
{mozL10n.get("context_save_label2")}
|
|
||||||
</button>
|
|
||||||
<button className="room-context-btn-close"
|
|
||||||
onClick={this.handleCloseClick}
|
|
||||||
title={mozL10n.get("cancel_button")}/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -819,6 +787,7 @@ loop.roomViews = (function(mozL10n) {
|
|||||||
publishStream={this.publishStream}
|
publishStream={this.publishStream}
|
||||||
screenShare={screenShareData}
|
screenShare={screenShareData}
|
||||||
settingsMenuItems={settingsMenuItems}
|
settingsMenuItems={settingsMenuItems}
|
||||||
|
show={!shouldRenderEditContextView}
|
||||||
video={{enabled: !this.state.videoMuted, visible: true}} />
|
video={{enabled: !this.state.videoMuted, visible: true}} />
|
||||||
<DesktopRoomInvitationView
|
<DesktopRoomInvitationView
|
||||||
dispatcher={this.props.dispatcher}
|
dispatcher={this.props.dispatcher}
|
||||||
|
@ -75,6 +75,87 @@ p {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Buttons */
|
||||||
|
|
||||||
|
.button-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
width: 100%;
|
||||||
|
padding-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group > .button {
|
||||||
|
flex: 1;
|
||||||
|
margin: 0 5px;
|
||||||
|
min-height: 3rem;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
line-height: 1rem;
|
||||||
|
font-weight: 300;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group > .button:first-child {
|
||||||
|
-moz-margin-start: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group > .button:last-child {
|
||||||
|
-moz-margin-end: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
padding: 2px 5px;
|
||||||
|
background-color: #fbfbfb;
|
||||||
|
color: #333;
|
||||||
|
border-radius: 2px;
|
||||||
|
min-height: 26px;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
line-height: 1.2rem;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:hover {
|
||||||
|
background-color: #ebebeb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:active {
|
||||||
|
background-color: #ccc;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.button-accept {
|
||||||
|
background-color: #00a9dc;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.button-accept:hover,
|
||||||
|
.button.button-accept:hover:active {
|
||||||
|
background-color: #5cccee;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.button-cancel {
|
||||||
|
background-color: #ebebeb;
|
||||||
|
border: 0;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.button-cancel:hover,
|
||||||
|
.button.button-cancel:hover:active {
|
||||||
|
background-color: #dcd6d6;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.button-cancel:disabled {
|
||||||
|
background-color: #ebebeb;
|
||||||
|
color: #c3c3c3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.button-accept:active {
|
||||||
|
background-color: #3aa689;
|
||||||
|
border-color: #3aa689;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
/* A reset for all button-appearing elements, with the lowest-common
|
/* A reset for all button-appearing elements, with the lowest-common
|
||||||
* denominator of the needed rules. Intended to be used as a base class
|
* denominator of the needed rules. Intended to be used as a base class
|
||||||
* together with .btn-*
|
* together with .btn-*
|
||||||
|
@ -915,13 +915,6 @@ html[dir="rtl"] .room-conversation-wrapper header a {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.room-invitation-overlay input[type="text"] {
|
|
||||||
display: block;
|
|
||||||
background-color: rgba(0,0,0,.5);
|
|
||||||
border-radius: 3px;
|
|
||||||
padding: .5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.room-invitation-overlay .btn-group {
|
.room-invitation-overlay .btn-group {
|
||||||
padding: 0 0 5rem 0;
|
padding: 0 0 5rem 0;
|
||||||
}
|
}
|
||||||
@ -999,10 +992,9 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.room-context {
|
.room-context {
|
||||||
background: rgba(0,0,0,.8);
|
background: #fff;
|
||||||
border-top: 2px solid #444;
|
border-top: 2px solid #444;
|
||||||
border-bottom: 2px solid #444;
|
border-bottom: 2px solid #444;
|
||||||
padding: .5rem;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@ -1049,13 +1041,11 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.room-context-label {
|
.room-context-header {
|
||||||
margin-bottom: 1em;
|
color: #333;
|
||||||
}
|
font-size: 1.2rem;
|
||||||
|
font-weight: bold;
|
||||||
.room-context-label,
|
margin: 1rem auto;
|
||||||
.room-context > .checkbox-wrapper > label {
|
|
||||||
color: #fff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.room-context-comment {
|
.room-context-comment {
|
||||||
@ -1079,71 +1069,40 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.room-context > form {
|
.room-context > form {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
padding: .5rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.room-context > form > textarea,
|
.room-context > form > textarea,
|
||||||
.room-context > form > input[type="text"] {
|
.room-context > form > input[type="text"] {
|
||||||
display: block;
|
display: block;
|
||||||
background: rgba(0,0,0,.5);
|
|
||||||
font-family: "Helvetica Neue", Arial, sans;
|
|
||||||
border: 1px solid rgba(255,255,255,.2);
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: .5em;
|
outline: none;
|
||||||
border-radius: 3px;
|
border-radius: 4px;
|
||||||
resize: none;
|
margin: 10px 0;
|
||||||
color: #fff;
|
border: 1px solid #c3c3c3;
|
||||||
|
height: 2.6rem;
|
||||||
|
padding: 6px;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: #4a4a4a;
|
||||||
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.room-context > form > textarea {
|
.room-context > form > textarea {
|
||||||
font-size: 1em;
|
font-family: inherit;
|
||||||
|
height: 5.2rem;
|
||||||
|
resize: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.room-context > form > input:not([disabled]).room-context-url {
|
.room-context > form > textarea::-moz-placeholder,
|
||||||
color: #0095dd;
|
.room-context > form > input::-moz-placeholder {
|
||||||
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
.room-context > form > input[disabled] {
|
.room-context > form > textarea:focus,
|
||||||
background-color: rgba(255,255,255,.2);
|
.room-context > form > input:focus {
|
||||||
color: rgba(255,255,255,.4);
|
border: 0.1rem solid #5cccee;
|
||||||
}
|
|
||||||
|
|
||||||
.room-context > form > textarea:not(:last-of-type),
|
|
||||||
.room-context > form > input[type="text"] {
|
|
||||||
margin: 0 0 .5em 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.room-context > .btn {
|
|
||||||
margin: .5em 0 0;
|
|
||||||
font-size: 1.1em;
|
|
||||||
padding: 0 .5em;
|
|
||||||
align-self: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.room-context-btn-close {
|
|
||||||
position: absolute;
|
|
||||||
right: 8px;
|
|
||||||
/* 8px offset + 2px border-top */
|
|
||||||
top: 10px;
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
background-color: transparent;
|
|
||||||
background-image: url("../img/icons-10x10.svg#close-darkergrey");
|
|
||||||
background-size: 8px 8px;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
border: 0;
|
|
||||||
padding: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.room-context-btn-close:hover,
|
|
||||||
.room-context-btn-close:hover:active {
|
|
||||||
background-image: url("../img/icons-10x10.svg#close-active");
|
|
||||||
}
|
|
||||||
|
|
||||||
html[dir="rtl"] .room-context-btn-close {
|
|
||||||
right: auto;
|
|
||||||
left: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.media-layout {
|
.media-layout {
|
||||||
|
@ -359,6 +359,7 @@ loop.shared.views = (function(_, mozL10n) {
|
|||||||
publishStream: React.PropTypes.func.isRequired,
|
publishStream: React.PropTypes.func.isRequired,
|
||||||
screenShare: React.PropTypes.object,
|
screenShare: React.PropTypes.object,
|
||||||
settingsMenuItems: React.PropTypes.array,
|
settingsMenuItems: React.PropTypes.array,
|
||||||
|
show: React.PropTypes.bool.isRequired,
|
||||||
video: React.PropTypes.object.isRequired
|
video: React.PropTypes.object.isRequired
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -440,6 +441,10 @@ loop.shared.views = (function(_, mozL10n) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
if (!this.props.show) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var cx = React.addons.classSet;
|
var cx = React.addons.classSet;
|
||||||
var conversationToolbarCssClasses = cx({
|
var conversationToolbarCssClasses = cx({
|
||||||
"conversation-toolbar": true,
|
"conversation-toolbar": true,
|
||||||
@ -798,7 +803,7 @@ loop.shared.views = (function(_, mozL10n) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return React.createElement("p", null, mozL10n.get("context_inroom_label"));
|
return React.createElement("p", null, mozL10n.get("context_inroom_label2"));
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
@ -359,6 +359,7 @@ loop.shared.views = (function(_, mozL10n) {
|
|||||||
publishStream: React.PropTypes.func.isRequired,
|
publishStream: React.PropTypes.func.isRequired,
|
||||||
screenShare: React.PropTypes.object,
|
screenShare: React.PropTypes.object,
|
||||||
settingsMenuItems: React.PropTypes.array,
|
settingsMenuItems: React.PropTypes.array,
|
||||||
|
show: React.PropTypes.bool.isRequired,
|
||||||
video: React.PropTypes.object.isRequired
|
video: React.PropTypes.object.isRequired
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -440,6 +441,10 @@ loop.shared.views = (function(_, mozL10n) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
if (!this.props.show) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var cx = React.addons.classSet;
|
var cx = React.addons.classSet;
|
||||||
var conversationToolbarCssClasses = cx({
|
var conversationToolbarCssClasses = cx({
|
||||||
"conversation-toolbar": true,
|
"conversation-toolbar": true,
|
||||||
@ -798,7 +803,7 @@ loop.shared.views = (function(_, mozL10n) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <p>{mozL10n.get("context_inroom_label")}</p>;
|
return <p>{mozL10n.get("context_inroom_label2")}</p>;
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
@ -15,6 +15,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
|||||||
"resource://gre/modules/Promise.jsm");
|
"resource://gre/modules/Promise.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
|
XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
|
||||||
"resource://services-common/utils.js");
|
"resource://services-common/utils.js");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "WebChannel",
|
||||||
|
"resource://gre/modules/WebChannel.jsm");
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(this, "eventEmitter", function() {
|
XPCOMUtils.defineLazyGetter(this, "eventEmitter", function() {
|
||||||
const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js", {});
|
const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js", {});
|
||||||
return new EventEmitter();
|
return new EventEmitter();
|
||||||
@ -45,6 +48,9 @@ const MAX_TIME_BEFORE_ENCRYPTION = 30 * 60 * 1000;
|
|||||||
// Wait time between individual re-encryption cycles (1 second).
|
// Wait time between individual re-encryption cycles (1 second).
|
||||||
const TIME_BETWEEN_ENCRYPTIONS = 1000;
|
const TIME_BETWEEN_ENCRYPTIONS = 1000;
|
||||||
|
|
||||||
|
// This is the pref name for the url of the standalone pages.
|
||||||
|
const LINKCLICKER_URL_PREFNAME = "loop.linkClicker.url";
|
||||||
|
|
||||||
const roomsPushNotification = function(version, channelID) {
|
const roomsPushNotification = function(version, channelID) {
|
||||||
return LoopRoomsInternal.onNotification(version, channelID);
|
return LoopRoomsInternal.onNotification(version, channelID);
|
||||||
};
|
};
|
||||||
@ -58,6 +64,8 @@ var gDirty = true;
|
|||||||
var gCurrentUser = null;
|
var gCurrentUser = null;
|
||||||
// Global variable that keeps track of the room cache.
|
// Global variable that keeps track of the room cache.
|
||||||
var gRoomsCache = null;
|
var gRoomsCache = null;
|
||||||
|
// Global variable that keeps track of the link clicker channel.
|
||||||
|
var gLinkClickerChannel = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extend a `target` object with the properties defined in `source`.
|
* Extend a `target` object with the properties defined in `source`.
|
||||||
@ -177,6 +185,40 @@ var LoopRoomsInternal = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialises the rooms, sets up the link clicker listener.
|
||||||
|
*/
|
||||||
|
init: function() {
|
||||||
|
Services.prefs.addObserver(LINKCLICKER_URL_PREFNAME,
|
||||||
|
this.setupLinkClickerListener.bind(this), false);
|
||||||
|
|
||||||
|
this.setupLinkClickerListener();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up a WebChannel listener for the link clicker so that we can open
|
||||||
|
* rooms in the Firefox UI.
|
||||||
|
*/
|
||||||
|
setupLinkClickerListener: function() {
|
||||||
|
// Ensure any existing channel is tidied up.
|
||||||
|
if (gLinkClickerChannel) {
|
||||||
|
gLinkClickerChannel.stopListening();
|
||||||
|
gLinkClickerChannel = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let linkClickerUrl = Services.prefs.getCharPref(LINKCLICKER_URL_PREFNAME);
|
||||||
|
|
||||||
|
// Don't do anything if there's no url.
|
||||||
|
if (!linkClickerUrl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let uri = Services.io.newURI(linkClickerUrl, null, null);
|
||||||
|
|
||||||
|
gLinkClickerChannel = new WebChannel("loop-link-clicker", uri);
|
||||||
|
gLinkClickerChannel.listen(this._handleLinkClickerMessage.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var {String} sessionType The type of user session. May be 'FXA' or 'GUEST'.
|
* @var {String} sessionType The type of user session. May be 'FXA' or 'GUEST'.
|
||||||
*/
|
*/
|
||||||
@ -905,6 +947,42 @@ var LoopRoomsInternal = {
|
|||||||
eventEmitter.emit("refresh");
|
eventEmitter.emit("refresh");
|
||||||
this.getAll(null, () => {});
|
this.getAll(null, () => {});
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a message received from the content channel.
|
||||||
|
*
|
||||||
|
* @param {String} id The channel id.
|
||||||
|
* @param {Object} message The message received.
|
||||||
|
* @param {Object} sendingContext The context for the sending location.
|
||||||
|
*/
|
||||||
|
_handleLinkClickerMessage: function(id, message, sendingContext) {
|
||||||
|
if (!message) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sendResponse = response => {
|
||||||
|
gLinkClickerChannel.send({
|
||||||
|
response: response
|
||||||
|
}, sendingContext);
|
||||||
|
};
|
||||||
|
|
||||||
|
let hasRoom = this.rooms.has(message.roomToken);
|
||||||
|
|
||||||
|
switch (message.command) {
|
||||||
|
case "checkWillOpenRoom":
|
||||||
|
sendResponse(hasRoom);
|
||||||
|
break;
|
||||||
|
case "openRoom":
|
||||||
|
if (hasRoom) {
|
||||||
|
this.open(message.roomToken);
|
||||||
|
}
|
||||||
|
sendResponse(hasRoom);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sendResponse(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Object.freeze(LoopRoomsInternal);
|
Object.freeze(LoopRoomsInternal);
|
||||||
@ -926,6 +1004,10 @@ Object.freeze(LoopRoomsInternal);
|
|||||||
* See the internal code for the API documentation.
|
* See the internal code for the API documentation.
|
||||||
*/
|
*/
|
||||||
this.LoopRooms = {
|
this.LoopRooms = {
|
||||||
|
init: function() {
|
||||||
|
LoopRoomsInternal.init();
|
||||||
|
},
|
||||||
|
|
||||||
get participantsCount() {
|
get participantsCount() {
|
||||||
return LoopRoomsInternal.participantsCount;
|
return LoopRoomsInternal.participantsCount;
|
||||||
},
|
},
|
||||||
@ -1016,6 +1098,24 @@ this.LoopRooms = {
|
|||||||
|
|
||||||
once: (...params) => eventEmitter.once(...params),
|
once: (...params) => eventEmitter.once(...params),
|
||||||
|
|
||||||
off: (...params) => eventEmitter.off(...params)
|
off: (...params) => eventEmitter.off(...params),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expose the internal rooms map for testing purposes only. This avoids
|
||||||
|
* needing to mock the server interfaces.
|
||||||
|
*
|
||||||
|
* @param {Map} roomsCache The new cache data to set for testing purposes. If
|
||||||
|
* not specified, it will reset the cache.
|
||||||
|
*/
|
||||||
|
_setRoomsCache: function(roomsCache) {
|
||||||
|
LoopRoomsInternal.rooms.clear();
|
||||||
|
|
||||||
|
if (roomsCache) {
|
||||||
|
// Need a clone as the internal map is read-only.
|
||||||
|
for (let [key, value] of roomsCache) {
|
||||||
|
LoopRoomsInternal.rooms.set(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Object.freeze(this.LoopRooms);
|
Object.freeze(this.LoopRooms);
|
||||||
|
@ -1211,6 +1211,9 @@ this.MozLoopService = {
|
|||||||
// stub out API functions for unit testing
|
// stub out API functions for unit testing
|
||||||
Object.freeze(this);
|
Object.freeze(this);
|
||||||
|
|
||||||
|
// Initialise anything that needs it in rooms.
|
||||||
|
LoopRooms.init();
|
||||||
|
|
||||||
// Don't do anything if loop is not enabled.
|
// Don't do anything if loop is not enabled.
|
||||||
if (!Services.prefs.getBoolPref("loop.enabled")) {
|
if (!Services.prefs.getBoolPref("loop.enabled")) {
|
||||||
return Promise.reject(new Error("loop is not enabled"));
|
return Promise.reject(new Error("loop is not enabled"));
|
||||||
|
@ -46,6 +46,7 @@ TESTS="
|
|||||||
${LOOPDIR}/test/mochitest
|
${LOOPDIR}/test/mochitest
|
||||||
browser/components/uitour/test/browser_UITour_loop.js
|
browser/components/uitour/test/browser_UITour_loop.js
|
||||||
browser/base/content/test/general/browser_devices_get_user_media_about_urls.js
|
browser/base/content/test/general/browser_devices_get_user_media_about_urls.js
|
||||||
|
browser/base/content/test/general/browser_parsable_css.js
|
||||||
"
|
"
|
||||||
|
|
||||||
./mach mochitest $TESTS
|
./mach mochitest $TESTS
|
||||||
@ -53,9 +54,3 @@ TESTS="
|
|||||||
if [ "$1" != "--skip-e10s" ]; then
|
if [ "$1" != "--skip-e10s" ]; then
|
||||||
./mach mochitest --e10s $TESTS
|
./mach mochitest --e10s $TESTS
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# This is currently disabled because the test itself is busted. Once bug
|
|
||||||
# 1062821 is landed, we should see if things work again, and then re-enable it.
|
|
||||||
# The re-enabling is tracked in bug 1113350.
|
|
||||||
#
|
|
||||||
# browser/base/content/test/general/browser_parsable_css.js \
|
|
||||||
|
@ -582,6 +582,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
hangup: this.leaveRoom,
|
hangup: this.leaveRoom,
|
||||||
hangupButtonLabel: mozL10n.get("rooms_leave_button_label"),
|
hangupButtonLabel: mozL10n.get("rooms_leave_button_label"),
|
||||||
publishStream: this.publishStream,
|
publishStream: this.publishStream,
|
||||||
|
show: true,
|
||||||
video: {enabled: !this.state.videoMuted,
|
video: {enabled: !this.state.videoMuted,
|
||||||
visible: this._roomIsActive()}})
|
visible: this._roomIsActive()}})
|
||||||
),
|
),
|
||||||
|
@ -582,6 +582,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
hangup={this.leaveRoom}
|
hangup={this.leaveRoom}
|
||||||
hangupButtonLabel={mozL10n.get("rooms_leave_button_label")}
|
hangupButtonLabel={mozL10n.get("rooms_leave_button_label")}
|
||||||
publishStream={this.publishStream}
|
publishStream={this.publishStream}
|
||||||
|
show={true}
|
||||||
video={{enabled: !this.state.videoMuted,
|
video={{enabled: !this.state.videoMuted,
|
||||||
visible: this._roomIsActive()}} />
|
visible: this._roomIsActive()}} />
|
||||||
</sharedViews.MediaLayoutView>
|
</sharedViews.MediaLayoutView>
|
||||||
|
@ -84,9 +84,9 @@ status_error=Something went wrong
|
|||||||
# Text chat strings
|
# Text chat strings
|
||||||
|
|
||||||
chat_textbox_placeholder=Type here…
|
chat_textbox_placeholder=Type here…
|
||||||
# LOCALIZATION NOTE (context_inroom_label): this string is followed by the
|
# LOCALIZATION NOTE (context_inroom_label2): this string is followed by the
|
||||||
# title/URL of the website you are having a conversation about, displayed on a
|
# title/URL of the website you are having a conversation about, displayed on a
|
||||||
# separate line. If this structure doesn't work for your locale, you might want
|
# separate line. If this structure doesn't work for your locale, you might want
|
||||||
# to consider this as a stand-alone title. See example screenshot:
|
# to consider this as a stand-alone title. See example screenshot:
|
||||||
# https://bug1084991.bugzilla.mozilla.org/attachment.cgi?id=8614721
|
# https://bug1084991.bugzilla.mozilla.org/attachment.cgi?id=8614721
|
||||||
context_inroom_label=Let's talk about:
|
context_inroom_label2=Let's Talk About:
|
||||||
|
@ -708,16 +708,13 @@ describe("loop.roomViews", function () {
|
|||||||
expect(view.getDOMNode().querySelector(".room-context")).to.not.eql(null);
|
expect(view.getDOMNode().querySelector(".room-context")).to.not.eql(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should hide the form when the edit button is clicked again", function() {
|
it("should not have a settings menu when the edit button is clicked", function() {
|
||||||
view = mountTestComponent();
|
view = mountTestComponent();
|
||||||
|
|
||||||
var editButton = view.getDOMNode().querySelector(".settings-menu > li.entry-settings-edit");
|
var editButton = view.getDOMNode().querySelector(".settings-menu > li.entry-settings-edit");
|
||||||
React.addons.TestUtils.Simulate.click(editButton);
|
React.addons.TestUtils.Simulate.click(editButton);
|
||||||
|
|
||||||
// Click again.
|
expect(view.getDOMNode().querySelector(".settings-menu")).to.eql(null);
|
||||||
React.addons.TestUtils.Simulate.click(editButton);
|
|
||||||
|
|
||||||
expect(view.getDOMNode().querySelector(".room-context")).to.eql(null);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -840,12 +837,12 @@ describe("loop.roomViews", function () {
|
|||||||
expect(view.getDOMNode()).to.eql(null);
|
expect(view.getDOMNode()).to.eql(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should close the view when the close button is clicked", function() {
|
it("should close the view when the cancel button is clicked", function() {
|
||||||
view = mountTestComponent({
|
view = mountTestComponent({
|
||||||
roomData: { roomContextUrls: [fakeContextURL] }
|
roomData: { roomContextUrls: [fakeContextURL] }
|
||||||
});
|
});
|
||||||
|
|
||||||
var closeBtn = view.getDOMNode().querySelector(".room-context-btn-close");
|
var closeBtn = view.getDOMNode().querySelector(".button-cancel");
|
||||||
React.addons.TestUtils.Simulate.click(closeBtn);
|
React.addons.TestUtils.Simulate.click(closeBtn);
|
||||||
expect(view.getDOMNode()).to.eql(null);
|
expect(view.getDOMNode()).to.eql(null);
|
||||||
});
|
});
|
||||||
@ -866,35 +863,6 @@ describe("loop.roomViews", function () {
|
|||||||
expect(node.querySelector(".room-context-url").value).to.eql(fakeContextURL.location);
|
expect(node.querySelector(".room-context-url").value).to.eql(fakeContextURL.location);
|
||||||
expect(node.querySelector(".room-context-comments").value).to.eql(fakeContextURL.description);
|
expect(node.querySelector(".room-context-comments").value).to.eql(fakeContextURL.description);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should show the checkbox as disabled when context is already set", function() {
|
|
||||||
view = mountTestComponent({
|
|
||||||
roomData: {
|
|
||||||
roomToken: "fakeToken",
|
|
||||||
roomName: "fakeName",
|
|
||||||
roomContextUrls: [fakeContextURL]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var checkbox = view.getDOMNode().querySelector(".checkbox");
|
|
||||||
expect(checkbox.classList.contains("disabled")).to.eql(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should hide the checkbox when no context data is stored or available", function() {
|
|
||||||
view = mountTestComponent({
|
|
||||||
roomData: {
|
|
||||||
roomToken: "fakeToken",
|
|
||||||
roomName: "Hello, is it me you're looking for?"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// First check if availableContext is set correctly.
|
|
||||||
expect(view.state.availableContext).to.not.eql(null);
|
|
||||||
expect(view.state.availableContext.previewImage).to.eql(favicon);
|
|
||||||
|
|
||||||
var node = view.getDOMNode();
|
|
||||||
expect(node.querySelector(".checkbox-wrapper").classList.contains("hide")).to.eql(true);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Update Room", function() {
|
describe("Update Room", function() {
|
||||||
@ -919,7 +887,7 @@ describe("loop.roomViews", function () {
|
|||||||
value: "reallyFake"
|
value: "reallyFake"
|
||||||
}});
|
}});
|
||||||
|
|
||||||
React.addons.TestUtils.Simulate.click(view.getDOMNode().querySelector(".btn-info"));
|
React.addons.TestUtils.Simulate.click(view.getDOMNode().querySelector(".button-accept"));
|
||||||
|
|
||||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||||
@ -955,7 +923,7 @@ describe("loop.roomViews", function () {
|
|||||||
view.setProps({ savingContext: true }, function() {
|
view.setProps({ savingContext: true }, function() {
|
||||||
var node = view.getDOMNode();
|
var node = view.getDOMNode();
|
||||||
// The button should show up as disabled.
|
// The button should show up as disabled.
|
||||||
expect(node.querySelector(".btn-info").hasAttribute("disabled")).to.eql(true);
|
expect(node.querySelector(".button-accept").hasAttribute("disabled")).to.eql(true);
|
||||||
|
|
||||||
// Now simulate a successful save.
|
// Now simulate a successful save.
|
||||||
view.setProps({ savingContext: false }, function() {
|
view.setProps({ savingContext: false }, function() {
|
||||||
@ -967,45 +935,6 @@ describe("loop.roomViews", function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#handleCheckboxChange", function() {
|
|
||||||
var node, checkbox;
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
fakeMozLoop.getSelectedTabMetadata = sinon.stub().callsArgWith(0, {
|
|
||||||
favicon: fakeContextURL.thumbnail,
|
|
||||||
title: fakeContextURL.description,
|
|
||||||
url: fakeContextURL.location
|
|
||||||
});
|
|
||||||
view = mountTestComponent({
|
|
||||||
roomData: {
|
|
||||||
roomToken: "fakeToken",
|
|
||||||
roomName: "fakeName"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
node = view.getDOMNode();
|
|
||||||
checkbox = node.querySelector(".checkbox");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should prefill the form with available context data when clicked", function() {
|
|
||||||
React.addons.TestUtils.Simulate.click(checkbox);
|
|
||||||
|
|
||||||
expect(node.querySelector(".room-context-name").value).to.eql("fakeName");
|
|
||||||
expect(node.querySelector(".room-context-url").value).to.eql(fakeContextURL.location);
|
|
||||||
expect(node.querySelector(".room-context-comments").value).to.eql(fakeContextURL.description);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should undo prefill when clicking the checkbox again", function() {
|
|
||||||
React.addons.TestUtils.Simulate.click(checkbox);
|
|
||||||
// Twice.
|
|
||||||
React.addons.TestUtils.Simulate.click(checkbox);
|
|
||||||
|
|
||||||
expect(node.querySelector(".room-context-name").value).to.eql("fakeName");
|
|
||||||
expect(node.querySelector(".room-context-url").value).to.eql("");
|
|
||||||
expect(node.querySelector(".room-context-comments").value).to.eql("");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#handleContextClick", function() {
|
describe("#handleContextClick", function() {
|
||||||
var fakeEvent;
|
var fakeEvent;
|
||||||
|
|
||||||
|
@ -32,5 +32,8 @@
|
|||||||
"MozLoopServiceInternal": true,
|
"MozLoopServiceInternal": true,
|
||||||
"LoopRoomsInternal": true,
|
"LoopRoomsInternal": true,
|
||||||
"LoopUI": false,
|
"LoopUI": false,
|
||||||
|
// Other items
|
||||||
|
"Chat": true,
|
||||||
|
"WebChannel": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ support-files =
|
|||||||
google_service.sjs
|
google_service.sjs
|
||||||
head.js
|
head.js
|
||||||
loop_fxa.sjs
|
loop_fxa.sjs
|
||||||
|
test_loopLinkClicker_channel.html
|
||||||
../../../../base/content/test/general/browser_fxa_oauth_with_keys.html
|
../../../../base/content/test/general/browser_fxa_oauth_with_keys.html
|
||||||
|
|
||||||
[browser_CardDavImporter.js]
|
[browser_CardDavImporter.js]
|
||||||
@ -15,6 +16,8 @@ support-files =
|
|||||||
skip-if = e10s
|
skip-if = e10s
|
||||||
[browser_loop_fxa_server.js]
|
[browser_loop_fxa_server.js]
|
||||||
[browser_LoopContacts.js]
|
[browser_LoopContacts.js]
|
||||||
|
[browser_LoopRooms_channel.js]
|
||||||
|
skip-if = asan && e10s # Bug 1206457
|
||||||
[browser_mozLoop_appVersionInfo.js]
|
[browser_mozLoop_appVersionInfo.js]
|
||||||
[browser_mozLoop_context.js]
|
[browser_mozLoop_context.js]
|
||||||
[browser_mozLoop_prefs.js]
|
[browser_mozLoop_prefs.js]
|
||||||
|
@ -0,0 +1,108 @@
|
|||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file contains tests for checking the channel from the standalone to
|
||||||
|
* LoopRooms works for checking if rooms can be opened within the conversation
|
||||||
|
* window.
|
||||||
|
*/
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var {WebChannel} = Cu.import("resource://gre/modules/WebChannel.jsm", {});
|
||||||
|
var {Chat} = Cu.import("resource:///modules/Chat.jsm", {});
|
||||||
|
|
||||||
|
const TEST_URI =
|
||||||
|
"example.com/browser/browser/components/loop/test/mochitest/test_loopLinkClicker_channel.html";
|
||||||
|
const TEST_URI_GOOD = Services.io.newURI("https://" + TEST_URI, null, null);
|
||||||
|
const TEST_URI_BAD = Services.io.newURI("http://" + TEST_URI, null, null);
|
||||||
|
|
||||||
|
const ROOM_TOKEN = "fake1234";
|
||||||
|
const LINKCLICKER_URL_PREFNAME = "loop.linkClicker.url";
|
||||||
|
|
||||||
|
var openChatOrig = Chat.open;
|
||||||
|
|
||||||
|
var fakeRoomList = new Map([[ ROOM_TOKEN, { roomToken: ROOM_TOKEN } ]]);
|
||||||
|
|
||||||
|
// Loads the specified URI in a new tab and waits for it to send us data on our
|
||||||
|
// test web-channel and resolves with that data.
|
||||||
|
function promiseNewChannelResponse(uri, hash) {
|
||||||
|
let waitForChannelPromise = new Promise((resolve, reject) => {
|
||||||
|
let channel = new WebChannel("test-loop-link-clicker-backchannel", uri);
|
||||||
|
channel.listen((id, data, target) => {
|
||||||
|
channel.stopListening();
|
||||||
|
resolve(data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return BrowserTestUtils.withNewTab({
|
||||||
|
gBrowser: gBrowser,
|
||||||
|
url: uri.spec + "#" + hash
|
||||||
|
}, () => waitForChannelPromise);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_task(function* test_loopRooms_webChannel_permissions() {
|
||||||
|
// We haven't set the allowed web page yet - so even the "good" URI should fail.
|
||||||
|
let got = yield promiseNewChannelResponse(TEST_URI_GOOD, "checkWillOpenRoom");
|
||||||
|
// Should have no data.
|
||||||
|
Assert.ok(got.message === undefined, "should have failed to get any data");
|
||||||
|
|
||||||
|
// Add a permission manager entry for our URI.
|
||||||
|
Services.prefs.setCharPref(LINKCLICKER_URL_PREFNAME, TEST_URI_GOOD.spec);
|
||||||
|
registerCleanupFunction(() => {
|
||||||
|
Services.prefs.clearUserPref(LINKCLICKER_URL_PREFNAME);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Try again - now we are expecting a response with actual data.
|
||||||
|
got = yield promiseNewChannelResponse(TEST_URI_GOOD, "checkWillOpenRoom");
|
||||||
|
|
||||||
|
// The room doesn't exist, so we should get a negative response.
|
||||||
|
Assert.equal(got.message.response, false, "should have got a response of false");
|
||||||
|
|
||||||
|
// Now a http:// URI - should get nothing even with the permission setup.
|
||||||
|
got = yield promiseNewChannelResponse(TEST_URI_BAD, "checkWillOpenRoom");
|
||||||
|
Assert.ok(got.message === undefined, "should have failed to get any data");
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function* test_loopRooms_webchannel_checkWillOpenRoom() {
|
||||||
|
// We've already tested if the room doesn't exist above, so here we add the
|
||||||
|
// room and check the result.
|
||||||
|
LoopRooms._setRoomsCache(fakeRoomList);
|
||||||
|
|
||||||
|
let got = yield promiseNewChannelResponse(TEST_URI_GOOD, "checkWillOpenRoom");
|
||||||
|
|
||||||
|
Assert.equal(got.message.response, true, "should have got a response of true");
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function* test_loopRooms_webchannel_openRoom() {
|
||||||
|
let openedUrl;
|
||||||
|
Chat.open = function(contentWindow, origin, title, url) {
|
||||||
|
openedUrl = url;
|
||||||
|
};
|
||||||
|
registerCleanupFunction(() => {
|
||||||
|
Chat.open = openChatOrig;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test when the room doesn't exist
|
||||||
|
LoopRooms._setRoomsCache();
|
||||||
|
|
||||||
|
let got = yield promiseNewChannelResponse(TEST_URI_GOOD, "openRoom");
|
||||||
|
|
||||||
|
Assert.ok(!openedUrl, "should not open a chat window");
|
||||||
|
Assert.equal(got.message.response, false, "should have got a response of false");
|
||||||
|
|
||||||
|
// Now add a room & check it.
|
||||||
|
LoopRooms._setRoomsCache(fakeRoomList);
|
||||||
|
|
||||||
|
got = yield promiseNewChannelResponse(TEST_URI_GOOD, "openRoom");
|
||||||
|
|
||||||
|
// Check the room was opened.
|
||||||
|
Assert.ok(openedUrl, "should open a chat window");
|
||||||
|
|
||||||
|
let windowId = openedUrl.match(/about:loopconversation\#(\w+)$/)[1];
|
||||||
|
let windowData = MozLoopService.getConversationWindowData(windowId);
|
||||||
|
|
||||||
|
Assert.equal(windowData.type, "room", "window data should contain room as the type");
|
||||||
|
Assert.equal(windowData.roomToken, ROOM_TOKEN, "window data should have the roomToken");
|
||||||
|
|
||||||
|
Assert.equal(got.message.response, true, "should have got a response of true");
|
||||||
|
});
|
@ -0,0 +1,46 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<script>
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Add a listener for responses to our remote requests.
|
||||||
|
window.addEventListener("WebChannelMessageToContent", function (event) {
|
||||||
|
if (event.detail.id == "loop-link-clicker") {
|
||||||
|
// Send what we got back to the test.
|
||||||
|
var backEvent = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||||
|
detail: {
|
||||||
|
id: "test-loop-link-clicker-backchannel",
|
||||||
|
message: {
|
||||||
|
message: event.detail.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
window.dispatchEvent(backEvent);
|
||||||
|
// and stick it in our DOM just for good measure/diagnostics.
|
||||||
|
document.getElementById("troubleshooting").textContent =
|
||||||
|
JSON.stringify(event.detail.message, null, 2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send a message on load requesting that the room is opened.
|
||||||
|
window.onload = function() {
|
||||||
|
var hash = window.location.hash;
|
||||||
|
|
||||||
|
var event = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||||
|
detail: {
|
||||||
|
id: "loop-link-clicker",
|
||||||
|
message: {
|
||||||
|
command: hash.substring(1, hash.length),
|
||||||
|
roomToken: "fake1234"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
window.dispatchEvent(event);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<pre id="troubleshooting"/>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -429,7 +429,8 @@ describe("loop.shared.views", function() {
|
|||||||
function mountTestComponent(props) {
|
function mountTestComponent(props) {
|
||||||
props = _.extend({
|
props = _.extend({
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
mozLoop: {}
|
mozLoop: {},
|
||||||
|
show: true
|
||||||
}, props || {});
|
}, props || {});
|
||||||
return TestUtils.renderIntoDocument(
|
return TestUtils.renderIntoDocument(
|
||||||
React.createElement(sharedViews.ConversationToolbar, props));
|
React.createElement(sharedViews.ConversationToolbar, props));
|
||||||
@ -445,6 +446,16 @@ describe("loop.shared.views", function() {
|
|||||||
clock.restore();
|
clock.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should not render the component when 'show' is false", function() {
|
||||||
|
var comp = mountTestComponent({
|
||||||
|
hangup: hangup,
|
||||||
|
publishStream: publishStream,
|
||||||
|
show: false
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(comp.getDOMNode()).to.eql(null);
|
||||||
|
});
|
||||||
|
|
||||||
it("should start no idle", function() {
|
it("should start no idle", function() {
|
||||||
var comp = mountTestComponent({
|
var comp = mountTestComponent({
|
||||||
hangupButtonLabel: "foo",
|
hangupButtonLabel: "foo",
|
||||||
|
@ -1118,6 +1118,7 @@
|
|||||||
publishStream: noop,
|
publishStream: noop,
|
||||||
screenShare: { state: SCREEN_SHARE_STATES.INACTIVE, visible: true},
|
screenShare: { state: SCREEN_SHARE_STATES.INACTIVE, visible: true},
|
||||||
settingsMenuItems: [{ id: "feedback" }],
|
settingsMenuItems: [{ id: "feedback" }],
|
||||||
|
show: true,
|
||||||
video: { enabled: true, visible: true}})
|
video: { enabled: true, visible: true}})
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@ -1132,6 +1133,7 @@
|
|||||||
publishStream: noop,
|
publishStream: noop,
|
||||||
screenShare: { state: SCREEN_SHARE_STATES.PENDING, visible: true},
|
screenShare: { state: SCREEN_SHARE_STATES.PENDING, visible: true},
|
||||||
settingsMenuItems: [{ id: "feedback" }],
|
settingsMenuItems: [{ id: "feedback" }],
|
||||||
|
show: true,
|
||||||
video: { enabled: false, visible: true}})
|
video: { enabled: false, visible: true}})
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@ -1146,6 +1148,7 @@
|
|||||||
publishStream: noop,
|
publishStream: noop,
|
||||||
screenShare: { state: SCREEN_SHARE_STATES.ACTIVE, visible: true},
|
screenShare: { state: SCREEN_SHARE_STATES.ACTIVE, visible: true},
|
||||||
settingsMenuItems: [{ id: "feedback" }],
|
settingsMenuItems: [{ id: "feedback" }],
|
||||||
|
show: true,
|
||||||
video: { enabled: true, visible: true}})
|
video: { enabled: true, visible: true}})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -1118,6 +1118,7 @@
|
|||||||
publishStream={noop}
|
publishStream={noop}
|
||||||
screenShare={{ state: SCREEN_SHARE_STATES.INACTIVE, visible: true }}
|
screenShare={{ state: SCREEN_SHARE_STATES.INACTIVE, visible: true }}
|
||||||
settingsMenuItems={[{ id: "feedback" }]}
|
settingsMenuItems={[{ id: "feedback" }]}
|
||||||
|
show={true}
|
||||||
video={{ enabled: true, visible: true }} />
|
video={{ enabled: true, visible: true }} />
|
||||||
</div>
|
</div>
|
||||||
</FramedExample>
|
</FramedExample>
|
||||||
@ -1132,6 +1133,7 @@
|
|||||||
publishStream={noop}
|
publishStream={noop}
|
||||||
screenShare={{ state: SCREEN_SHARE_STATES.PENDING, visible: true }}
|
screenShare={{ state: SCREEN_SHARE_STATES.PENDING, visible: true }}
|
||||||
settingsMenuItems={[{ id: "feedback" }]}
|
settingsMenuItems={[{ id: "feedback" }]}
|
||||||
|
show={true}
|
||||||
video={{ enabled: false, visible: true }} />
|
video={{ enabled: false, visible: true }} />
|
||||||
</div>
|
</div>
|
||||||
</FramedExample>
|
</FramedExample>
|
||||||
@ -1146,6 +1148,7 @@
|
|||||||
publishStream={noop}
|
publishStream={noop}
|
||||||
screenShare={{ state: SCREEN_SHARE_STATES.ACTIVE, visible: true }}
|
screenShare={{ state: SCREEN_SHARE_STATES.ACTIVE, visible: true }}
|
||||||
settingsMenuItems={[{ id: "feedback" }]}
|
settingsMenuItems={[{ id: "feedback" }]}
|
||||||
|
show={true}
|
||||||
video={{ enabled: true, visible: true }} />
|
video={{ enabled: true, visible: true }} />
|
||||||
</div>
|
</div>
|
||||||
</FramedExample>
|
</FramedExample>
|
||||||
|
@ -70,11 +70,11 @@
|
|||||||
|
|
||||||
<!-- Passwords -->
|
<!-- Passwords -->
|
||||||
<groupbox id="passwordsGroup" orient="vertical" data-category="paneSecurity" hidden="true">
|
<groupbox id="passwordsGroup" orient="vertical" data-category="paneSecurity" hidden="true">
|
||||||
<caption><label>&passwords.label;</label></caption>
|
<caption><label>&logins.label;</label></caption>
|
||||||
|
|
||||||
<hbox id="savePasswordsBox">
|
<hbox id="savePasswordsBox">
|
||||||
<checkbox id="savePasswords"
|
<checkbox id="savePasswords"
|
||||||
label="&rememberPasswords.label;" accesskey="&rememberPasswords.accesskey;"
|
label="&rememberLogins.label;" accesskey="&rememberLogins.accesskey;"
|
||||||
preference="signon.rememberSignons"
|
preference="signon.rememberSignons"
|
||||||
onsyncfrompreference="return gSecurityPane.readSavePasswords();"/>
|
onsyncfrompreference="return gSecurityPane.readSavePasswords();"/>
|
||||||
<spacer flex="1"/>
|
<spacer flex="1"/>
|
||||||
@ -103,7 +103,7 @@
|
|||||||
<row id="showPasswordRow">
|
<row id="showPasswordRow">
|
||||||
<hbox id="showPasswordsBox"/>
|
<hbox id="showPasswordsBox"/>
|
||||||
<button id="showPasswords"
|
<button id="showPasswords"
|
||||||
label="&savedPasswords.label;" accesskey="&savedPasswords.accesskey;"
|
label="&savedLogins.label;" accesskey="&savedLogins.accesskey;"
|
||||||
preference="pref.privacy.disable_button.view_passwords"/>
|
preference="pref.privacy.disable_button.view_passwords"/>
|
||||||
</row>
|
</row>
|
||||||
</rows>
|
</rows>
|
||||||
|
@ -22,7 +22,3 @@ BROWSER_CHROME_MANIFESTS += [
|
|||||||
XPCSHELL_TESTS_MANIFESTS += [
|
XPCSHELL_TESTS_MANIFESTS += [
|
||||||
'test/unit/xpcshell.ini'
|
'test/unit/xpcshell.ini'
|
||||||
]
|
]
|
||||||
|
|
||||||
EXTRA_PP_COMPONENTS += [
|
|
||||||
'translation.manifest',
|
|
||||||
]
|
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
#ifdef MOZ_SERVICES_HEALTHREPORT
|
|
||||||
category healthreport-js-provider-default TranslationProvider resource:///modules/translation/Translation.jsm
|
|
||||||
#endif
|
|
@ -684,8 +684,11 @@ AnimationsTimeline.prototype = {
|
|||||||
let getTime = time => L10N.getFormatStr("player.timeLabel",
|
let getTime = time => L10N.getFormatStr("player.timeLabel",
|
||||||
L10N.numberWithDecimals(time / 1000, 2));
|
L10N.numberWithDecimals(time / 1000, 2));
|
||||||
|
|
||||||
let title = L10N.getFormatStr("timeline." + state.type + ".nameLabel",
|
// The type isn't always available, older servers don't send it.
|
||||||
state.name);
|
let title =
|
||||||
|
state.type
|
||||||
|
? L10N.getFormatStr("timeline." + state.type + ".nameLabel", state.name)
|
||||||
|
: state.name;
|
||||||
let delay = L10N.getStr("player.animationDelayLabel") + " " +
|
let delay = L10N.getStr("player.animationDelayLabel") + " " +
|
||||||
getTime(state.delay);
|
getTime(state.delay);
|
||||||
let duration = L10N.getStr("player.animationDurationLabel") + " " +
|
let duration = L10N.getStr("player.animationDurationLabel") + " " +
|
||||||
|
@ -43,7 +43,7 @@ label,
|
|||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-header[hidden] {
|
.panel-header[hidden], .panel-item[hidden] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,7 +386,6 @@
|
|||||||
@RESPATH@/browser/components/webideComponents.manifest
|
@RESPATH@/browser/components/webideComponents.manifest
|
||||||
@RESPATH@/browser/components/Experiments.manifest
|
@RESPATH@/browser/components/Experiments.manifest
|
||||||
@RESPATH@/browser/components/ExperimentsService.js
|
@RESPATH@/browser/components/ExperimentsService.js
|
||||||
@RESPATH@/browser/components/translation.manifest
|
|
||||||
@RESPATH@/components/Downloads.manifest
|
@RESPATH@/components/Downloads.manifest
|
||||||
@RESPATH@/components/DownloadLegacy.js
|
@RESPATH@/components/DownloadLegacy.js
|
||||||
@RESPATH@/components/BrowserPageThumbs.manifest
|
@RESPATH@/components/BrowserPageThumbs.manifest
|
||||||
@ -567,11 +566,6 @@
|
|||||||
@RESPATH@/components/InterAppMessagePort.js
|
@RESPATH@/components/InterAppMessagePort.js
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@RESPATH@/components/TCPSocket.js
|
|
||||||
@RESPATH@/components/TCPServerSocket.js
|
|
||||||
@RESPATH@/components/TCPSocketParentIntermediary.js
|
|
||||||
@RESPATH@/components/TCPSocket.manifest
|
|
||||||
|
|
||||||
#ifdef MOZ_ACTIVITIES
|
#ifdef MOZ_ACTIVITIES
|
||||||
@RESPATH@/components/SystemMessageCache.js
|
@RESPATH@/components/SystemMessageCache.js
|
||||||
@RESPATH@/components/SystemMessageInternal.js
|
@RESPATH@/components/SystemMessageInternal.js
|
||||||
|
@ -115,10 +115,6 @@ These should match what Safari and other Apple applications use on OS X Lion. --
|
|||||||
<!ENTITY fxaUnverified.label "Verify Your Account">
|
<!ENTITY fxaUnverified.label "Verify Your Account">
|
||||||
<!ENTITY syncStartPanel2.heading "&syncBrand.shortName.label; enabled">
|
<!ENTITY syncStartPanel2.heading "&syncBrand.shortName.label; enabled">
|
||||||
<!ENTITY syncStartPanel2.subTitle "&brandShortName; will begin syncing momentarily.">
|
<!ENTITY syncStartPanel2.subTitle "&brandShortName; will begin syncing momentarily.">
|
||||||
<!ENTITY syncErrorPanel.heading "Cannot connect to &syncBrand.shortName.label;">
|
|
||||||
<!ENTITY syncErrorPanel.subTitle "Please sign in to resume syncing.">
|
|
||||||
<!ENTITY syncErrorPanel.signInButton.label "Sign In">
|
|
||||||
<!ENTITY syncErrorPanel.signInButton.accesskey "S">
|
|
||||||
|
|
||||||
|
|
||||||
<!ENTITY fullScreenMinimize.tooltip "Minimize">
|
<!ENTITY fullScreenMinimize.tooltip "Minimize">
|
||||||
|
@ -31,6 +31,15 @@ first_time_experience_button_label=Get Started
|
|||||||
first_time_experience_subheading=Join the conversation
|
first_time_experience_subheading=Join the conversation
|
||||||
|
|
||||||
invite_header_text=Invite someone to join you.
|
invite_header_text=Invite someone to join you.
|
||||||
|
invite_header_text2=Invite a friend to join you
|
||||||
|
invite_facebook_button=share on Facebook
|
||||||
|
invite_facebook_triggered=shared!
|
||||||
|
invite_contacts_button=share with contacts
|
||||||
|
invite_contacts_triggered=shared!
|
||||||
|
invite_copy_button=copy link
|
||||||
|
invite_copy_triggered=copied!
|
||||||
|
invite_email_button=email link
|
||||||
|
invite_email_triggered=emailed!
|
||||||
|
|
||||||
# Status text
|
# Status text
|
||||||
display_name_guest=Guest
|
display_name_guest=Guest
|
||||||
@ -343,12 +352,16 @@ infobar_menuitem_dontshowagain_accesskey=D
|
|||||||
|
|
||||||
# Context in conversation strings
|
# Context in conversation strings
|
||||||
|
|
||||||
# LOCALIZATION NOTE (context_inroom_label): this string is followed by the
|
# LOCALIZATION NOTE (context_inroom_header): this string is displayed in the
|
||||||
# title/URL of the website you are having a conversation about, displayed on a
|
# conversation window when the user edits context. It is a header to the edit
|
||||||
|
# section.
|
||||||
|
context_inroom_header=Let's Talk About…
|
||||||
|
# LOCALIZATION NOTE (context_inroom_label2): this string is followed by the
|
||||||
|
# title and domain of the website you are having a conversation about, displayed on a
|
||||||
# separate line. If this structure doesn't work for your locale, you might want
|
# separate line. If this structure doesn't work for your locale, you might want
|
||||||
# to consider this as a stand-alone title. See example screenshot:
|
# to consider this as a stand-alone title. See example screenshot:
|
||||||
# https://bug1115342.bugzilla.mozilla.org/attachment.cgi?id=8563677
|
# https://bug1115342.bugzilla.mozilla.org/attachment.cgi?id=8563677
|
||||||
context_inroom_label=Let's talk about:
|
context_inroom_label2=Let's Talk About:
|
||||||
## LOCALIZATION_NOTE (context_edit_activate_label): {{title}} will be replaced
|
## LOCALIZATION_NOTE (context_edit_activate_label): {{title}} will be replaced
|
||||||
## by the title of the active tab, also known as the title of an HTML document.
|
## by the title of the active tab, also known as the title of an HTML document.
|
||||||
## The quotes around the title are intentional.
|
## The quotes around the title are intentional.
|
||||||
@ -357,7 +370,8 @@ context_edit_name_placeholder=Conversation Name
|
|||||||
context_edit_comments_placeholder=Comments
|
context_edit_comments_placeholder=Comments
|
||||||
context_add_some_label=Add some context
|
context_add_some_label=Add some context
|
||||||
context_show_tooltip=Show Context
|
context_show_tooltip=Show Context
|
||||||
context_save_label2=Save
|
context_cancel_label=Cancel
|
||||||
|
context_done_label=Done
|
||||||
context_link_modified=This link was modified.
|
context_link_modified=This link was modified.
|
||||||
context_learn_more_link_label=Learn more.
|
context_learn_more_link_label=Learn more.
|
||||||
conversation_settings_menu_edit_context=Edit Context
|
conversation_settings_menu_edit_context=Edit Context
|
||||||
|
@ -24,10 +24,10 @@
|
|||||||
<!ENTITY addonExceptions.accesskey "E">
|
<!ENTITY addonExceptions.accesskey "E">
|
||||||
|
|
||||||
|
|
||||||
<!ENTITY passwords.label "Passwords">
|
<!ENTITY logins.label "Logins">
|
||||||
|
|
||||||
<!ENTITY rememberPasswords.label "Remember passwords for sites">
|
<!ENTITY rememberLogins.label "Remember logins for sites">
|
||||||
<!ENTITY rememberPasswords.accesskey "R">
|
<!ENTITY rememberLogins.accesskey "R">
|
||||||
<!ENTITY passwordExceptions.label "Exceptions…">
|
<!ENTITY passwordExceptions.label "Exceptions…">
|
||||||
<!ENTITY passwordExceptions.accesskey "x">
|
<!ENTITY passwordExceptions.accesskey "x">
|
||||||
|
|
||||||
@ -36,5 +36,5 @@
|
|||||||
<!ENTITY changeMasterPassword.label "Change Master Password…">
|
<!ENTITY changeMasterPassword.label "Change Master Password…">
|
||||||
<!ENTITY changeMasterPassword.accesskey "M">
|
<!ENTITY changeMasterPassword.accesskey "M">
|
||||||
|
|
||||||
<!ENTITY savedPasswords.label "Saved Passwords…">
|
<!ENTITY savedLogins.label "Saved Logins…">
|
||||||
<!ENTITY savedPasswords.accesskey "P">
|
<!ENTITY savedLogins.accesskey "L">
|
||||||
|
@ -1725,15 +1725,13 @@ toolbarbutton.chevron > .toolbarbutton-icon {
|
|||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sync-error-panel-title,
|
|
||||||
#sync-start-panel-title {
|
#sync-start-panel-title {
|
||||||
font-size: 120%;
|
font-size: 120%;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sync-start-panel-subtitle,
|
#sync-start-panel-subtitle {
|
||||||
#sync-error-panel-subtitle {
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3275,15 +3275,13 @@ notification[value="loop-sharing-notification"] .messageImage {
|
|||||||
@hudButtonFocused@
|
@hudButtonFocused@
|
||||||
}
|
}
|
||||||
|
|
||||||
#sync-error-panel-title,
|
|
||||||
#sync-start-panel-title {
|
#sync-start-panel-title {
|
||||||
font-size: 120%;
|
font-size: 120%;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sync-start-panel-subtitle,
|
#sync-start-panel-subtitle {
|
||||||
#sync-error-panel-subtitle {
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#PopupAutoCompleteRichResult > hbox[anonid="search-suggestions-notification"] {
|
#PopupAutoCompleteRichResult > hbox[anonid="search-suggestions-notification"] {
|
||||||
border-bottom: 1px solid hsla(210, 4%, 10%, 0.14);
|
border-bottom: 1px solid hsla(210, 4%, 10%, 0.14);
|
||||||
color: -moz-FieldText;
|
|
||||||
background-color: hsla(210, 4%, 10%, 0.07);
|
background-color: hsla(210, 4%, 10%, 0.07);
|
||||||
padding: 6px 0;
|
padding: 6px 0;
|
||||||
-moz-padding-start: 44px;
|
-moz-padding-start: 44px;
|
||||||
|
@ -2473,15 +2473,13 @@ notification[value="loop-sharing-notification"] .messageImage {
|
|||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sync-error-panel-title,
|
|
||||||
#sync-start-panel-title {
|
#sync-start-panel-title {
|
||||||
font-size: 120%;
|
font-size: 120%;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sync-start-panel-subtitle,
|
#sync-start-panel-subtitle {
|
||||||
#sync-error-panel-subtitle {
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,6 +173,9 @@ nsNullPrincipal::Read(nsIObjectInputStream* aStream)
|
|||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsNullPrincipal::Write(nsIObjectOutputStream* aStream)
|
nsNullPrincipal::Write(nsIObjectOutputStream* aStream)
|
||||||
{
|
{
|
||||||
|
NS_ENSURE_TRUE(mOriginAttributes.mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
|
||||||
|
NS_ERROR_INVALID_ARG);
|
||||||
|
|
||||||
nsAutoCString suffix;
|
nsAutoCString suffix;
|
||||||
OriginAttributesRef().CreateSuffix(suffix);
|
OriginAttributesRef().CreateSuffix(suffix);
|
||||||
|
|
||||||
|
@ -427,6 +427,8 @@ NS_IMETHODIMP
|
|||||||
nsPrincipal::Write(nsIObjectOutputStream* aStream)
|
nsPrincipal::Write(nsIObjectOutputStream* aStream)
|
||||||
{
|
{
|
||||||
NS_ENSURE_STATE(mCodebase);
|
NS_ENSURE_STATE(mCodebase);
|
||||||
|
NS_ENSURE_TRUE(mOriginAttributes.mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
|
||||||
|
NS_ERROR_INVALID_ARG);
|
||||||
|
|
||||||
nsresult rv = NS_WriteOptionalCompoundObject(aStream, mCodebase, NS_GET_IID(nsIURI),
|
nsresult rv = NS_WriteOptionalCompoundObject(aStream, mCodebase, NS_GET_IID(nsIURI),
|
||||||
true);
|
true);
|
||||||
|
@ -103,6 +103,20 @@ function run_test() {
|
|||||||
var simplePrin = ssm.getSimpleCodebasePrincipal(makeURI('http://example.com'));
|
var simplePrin = ssm.getSimpleCodebasePrincipal(makeURI('http://example.com'));
|
||||||
try { simplePrin.origin; do_check_true(false); } catch (e) { do_check_true(true); }
|
try { simplePrin.origin; do_check_true(false); } catch (e) { do_check_true(true); }
|
||||||
|
|
||||||
|
// Make sure we don't crash when serializing them either.
|
||||||
|
try {
|
||||||
|
let binaryStream = Cc["@mozilla.org/binaryoutputstream;1"].
|
||||||
|
createInstance(Ci.nsIObjectOutputStream);
|
||||||
|
let pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
|
||||||
|
pipe.init(false, false, 0, 0xffffffff, null);
|
||||||
|
binaryStream.setOutputStream(pipe.outputStream);
|
||||||
|
binaryStream.writeCompoundObject(simplePrin, Ci.nsISupports, true);
|
||||||
|
binaryStream.close();
|
||||||
|
} catch (e) {
|
||||||
|
do_check_true(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Just userContext.
|
// Just userContext.
|
||||||
var exampleOrg_userContext = ssm.createCodebasePrincipal(makeURI('http://example.org'), {userContextId: 42});
|
var exampleOrg_userContext = ssm.createCodebasePrincipal(makeURI('http://example.org'), {userContextId: 42});
|
||||||
checkOriginAttributes(exampleOrg_userContext, { userContextId: 42 }, '^userContextId=42');
|
checkOriginAttributes(exampleOrg_userContext, { userContextId: 42 }, '^userContextId=42');
|
||||||
|
@ -168,7 +168,9 @@ const mozilla::Module::ContractIDEntry kDocShellContracts[] = {
|
|||||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "neterror", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "neterror", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
||||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "networking", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "networking", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
||||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "newaddon", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "newaddon", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
||||||
|
#ifdef NIGHTLY_BUILD
|
||||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "performance", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "performance", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
||||||
|
#endif
|
||||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "plugins", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "plugins", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
||||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "serviceworkers", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "serviceworkers", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
||||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "srcdoc", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "srcdoc", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
#include "mozilla/dom/Permissions.h"
|
#include "mozilla/dom/Permissions.h"
|
||||||
#include "mozilla/dom/Presentation.h"
|
#include "mozilla/dom/Presentation.h"
|
||||||
#include "mozilla/dom/ServiceWorkerContainer.h"
|
#include "mozilla/dom/ServiceWorkerContainer.h"
|
||||||
|
#include "mozilla/dom/TCPSocket.h"
|
||||||
#include "mozilla/dom/Telephony.h"
|
#include "mozilla/dom/Telephony.h"
|
||||||
#include "mozilla/dom/Voicemail.h"
|
#include "mozilla/dom/Voicemail.h"
|
||||||
#include "mozilla/dom/TVManager.h"
|
#include "mozilla/dom/TVManager.h"
|
||||||
@ -1805,6 +1806,13 @@ Navigator::GetInputPortManager(ErrorResult& aRv)
|
|||||||
return mInputPortManager;
|
return mInputPortManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
already_AddRefed<LegacyMozTCPSocket>
|
||||||
|
Navigator::MozTCPSocket()
|
||||||
|
{
|
||||||
|
nsRefPtr<LegacyMozTCPSocket> socket = new LegacyMozTCPSocket(GetWindow());
|
||||||
|
return socket.forget();
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef MOZ_B2G
|
#ifdef MOZ_B2G
|
||||||
already_AddRefed<Promise>
|
already_AddRefed<Promise>
|
||||||
Navigator::GetMobileIdAssertion(const MobileIdOptions& aOptions,
|
Navigator::GetMobileIdAssertion(const MobileIdOptions& aOptions,
|
||||||
|
@ -99,6 +99,7 @@ class TVManager;
|
|||||||
class InputPortManager;
|
class InputPortManager;
|
||||||
class DeviceStorageAreaListener;
|
class DeviceStorageAreaListener;
|
||||||
class Presentation;
|
class Presentation;
|
||||||
|
class LegacyMozTCPSocket;
|
||||||
|
|
||||||
namespace time {
|
namespace time {
|
||||||
class TimeManager;
|
class TimeManager;
|
||||||
@ -242,6 +243,7 @@ public:
|
|||||||
Voicemail* GetMozVoicemail(ErrorResult& aRv);
|
Voicemail* GetMozVoicemail(ErrorResult& aRv);
|
||||||
TVManager* GetTv();
|
TVManager* GetTv();
|
||||||
InputPortManager* GetInputPortManager(ErrorResult& aRv);
|
InputPortManager* GetInputPortManager(ErrorResult& aRv);
|
||||||
|
already_AddRefed<LegacyMozTCPSocket> MozTCPSocket();
|
||||||
network::Connection* GetConnection(ErrorResult& aRv);
|
network::Connection* GetConnection(ErrorResult& aRv);
|
||||||
nsDOMCameraManager* GetMozCameras(ErrorResult& aRv);
|
nsDOMCameraManager* GetMozCameras(ErrorResult& aRv);
|
||||||
MediaDevices* GetMediaDevices(ErrorResult& aRv);
|
MediaDevices* GetMediaDevices(ErrorResult& aRv);
|
||||||
|
@ -753,6 +753,7 @@ GK_ATOM(onDOMNodeInsertedIntoDocument, "onDOMNodeInsertedIntoDocument")
|
|||||||
GK_ATOM(onDOMNodeRemoved, "onDOMNodeRemoved")
|
GK_ATOM(onDOMNodeRemoved, "onDOMNodeRemoved")
|
||||||
GK_ATOM(onDOMNodeRemovedFromDocument, "onDOMNodeRemovedFromDocument")
|
GK_ATOM(onDOMNodeRemovedFromDocument, "onDOMNodeRemovedFromDocument")
|
||||||
GK_ATOM(onDOMSubtreeModified, "onDOMSubtreeModified")
|
GK_ATOM(onDOMSubtreeModified, "onDOMSubtreeModified")
|
||||||
|
GK_ATOM(ondata, "ondata")
|
||||||
GK_ATOM(ondrag, "ondrag")
|
GK_ATOM(ondrag, "ondrag")
|
||||||
GK_ATOM(ondragdrop, "ondragdrop")
|
GK_ATOM(ondragdrop, "ondragdrop")
|
||||||
GK_ATOM(ondragend, "ondragend")
|
GK_ATOM(ondragend, "ondragend")
|
||||||
@ -762,6 +763,7 @@ GK_ATOM(ondraggesture, "ondraggesture")
|
|||||||
GK_ATOM(ondragleave, "ondragleave")
|
GK_ATOM(ondragleave, "ondragleave")
|
||||||
GK_ATOM(ondragover, "ondragover")
|
GK_ATOM(ondragover, "ondragover")
|
||||||
GK_ATOM(ondragstart, "ondragstart")
|
GK_ATOM(ondragstart, "ondragstart")
|
||||||
|
GK_ATOM(ondrain, "ondrain")
|
||||||
GK_ATOM(ondrop, "ondrop")
|
GK_ATOM(ondrop, "ondrop")
|
||||||
GK_ATOM(oneitbroadcasted, "oneitbroadcasted")
|
GK_ATOM(oneitbroadcasted, "oneitbroadcasted")
|
||||||
GK_ATOM(onenabled, "onenabled")
|
GK_ATOM(onenabled, "onenabled")
|
||||||
|
@ -696,6 +696,11 @@ DOMInterfaces = {
|
|||||||
'concrete': False
|
'concrete': False
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'LegacyMozTCPSocket': {
|
||||||
|
'headerFile': 'TCPSocket.h',
|
||||||
|
'wrapperCache': False,
|
||||||
|
},
|
||||||
|
|
||||||
'LocalMediaStream': {
|
'LocalMediaStream': {
|
||||||
'headerFile': 'DOMMediaStream.h',
|
'headerFile': 'DOMMediaStream.h',
|
||||||
'nativeType': 'mozilla::DOMLocalMediaStream'
|
'nativeType': 'mozilla::DOMLocalMediaStream'
|
||||||
@ -1308,6 +1313,10 @@ DOMInterfaces = {
|
|||||||
'wrapperCache': False
|
'wrapperCache': False
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'TCPSocket': {
|
||||||
|
'implicitJSContext': ['send']
|
||||||
|
},
|
||||||
|
|
||||||
'ThreadSafeChromeUtils': {
|
'ThreadSafeChromeUtils': {
|
||||||
# The codegen is dumb, and doesn't understand that this interface is only a
|
# The codegen is dumb, and doesn't understand that this interface is only a
|
||||||
# collection of static methods, so we have this `concrete: False` hack.
|
# collection of static methods, so we have this `concrete: False` hack.
|
||||||
|
@ -205,11 +205,7 @@ function beginTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var prefs = [
|
beginTest();
|
||||||
[ "canvas.capturestream.enabled", true ],
|
|
||||||
];
|
|
||||||
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</pre>
|
</pre>
|
||||||
</body>
|
</body>
|
||||||
|
@ -211,13 +211,8 @@ function beginTest() {
|
|||||||
document.manager.runTests(corsTests, startTest);
|
document.manager.runTests(corsTests, startTest);
|
||||||
}
|
}
|
||||||
|
|
||||||
var prefs = [
|
|
||||||
[ "canvas.capturestream.enabled", true ],
|
|
||||||
];
|
|
||||||
|
|
||||||
SimpleTest.waitForExplicitFinish();
|
SimpleTest.waitForExplicitFinish();
|
||||||
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
|
beginTest();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</pre>
|
</pre>
|
||||||
</body>
|
</body>
|
||||||
|
@ -20,7 +20,7 @@ pref(webgl.force-layers-readback,true) == webgl-clear-test.html?readback wrappe
|
|||||||
== webgl-resize-test.html wrapper.html?green.png
|
== webgl-resize-test.html wrapper.html?green.png
|
||||||
|
|
||||||
# Check that captureStream() displays in a local video element
|
# Check that captureStream() displays in a local video element
|
||||||
pref(canvas.capturestream.enabled,true) skip-if(winWidget&&layersGPUAccelerated&&d2d) == webgl-capturestream-test.html?preserve wrapper.html?green.png
|
skip-if(winWidget&&layersGPUAccelerated&&d2d) == webgl-capturestream-test.html?preserve wrapper.html?green.png
|
||||||
|
|
||||||
# Some of the failure conditions are a little crazy. I'm (jgilbert) setting these based on
|
# Some of the failure conditions are a little crazy. I'm (jgilbert) setting these based on
|
||||||
# failures encountered when running on Try, and then targetting the Try config by
|
# failures encountered when running on Try, and then targetting the Try config by
|
||||||
@ -157,6 +157,6 @@ pref(canvas.focusring.enabled,true) skip-if(B2G) skip-if(Android&&AndroidVersion
|
|||||||
pref(canvas.customfocusring.enabled,true) skip-if(B2G) skip-if(Android&&AndroidVersion<15,8,500) skip-if(winWidget) needs-focus == drawCustomFocusRing.html drawCustomFocusRing-ref.html
|
pref(canvas.customfocusring.enabled,true) skip-if(B2G) skip-if(Android&&AndroidVersion<15,8,500) skip-if(winWidget) needs-focus == drawCustomFocusRing.html drawCustomFocusRing-ref.html
|
||||||
|
|
||||||
# Check that captureStream() displays in a local video element
|
# Check that captureStream() displays in a local video element
|
||||||
pref(canvas.capturestream.enabled,true) skip-if(winWidget&&layersGPUAccelerated&&d2d) == capturestream.html wrapper.html?green.png
|
skip-if(winWidget&&layersGPUAccelerated&&d2d) == capturestream.html wrapper.html?green.png
|
||||||
|
|
||||||
fuzzy-if(Android,3,40) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),1,1) == 1177726-text-stroke-bounds.html 1177726-text-stroke-bounds-ref.html
|
fuzzy-if(Android,3,40) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),1,1) == 1177726-text-stroke-bounds.html 1177726-text-stroke-bounds-ref.html
|
||||||
|
@ -107,9 +107,6 @@ function beginTest() {
|
|||||||
|
|
||||||
SimpleTest.waitForExplicitFinish();
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
|
||||||
var prefs = [
|
beginTest();
|
||||||
[ "canvas.capturestream.enabled", true ],
|
|
||||||
];
|
|
||||||
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -171,9 +171,6 @@ function beginTest() {
|
|||||||
|
|
||||||
SimpleTest.waitForExplicitFinish();
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
|
||||||
var prefs = [
|
beginTest();
|
||||||
[ "canvas.capturestream.enabled", true ],
|
|
||||||
];
|
|
||||||
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -249,6 +249,7 @@ public:
|
|||||||
bool CanBeFormatted();
|
bool CanBeFormatted();
|
||||||
bool CanBeShared();
|
bool CanBeShared();
|
||||||
bool IsRemovable();
|
bool IsRemovable();
|
||||||
|
bool LowDiskSpace();
|
||||||
bool Default();
|
bool Default();
|
||||||
void GetStorageName(nsAString& aStorageName);
|
void GetStorageName(nsAString& aStorageName);
|
||||||
|
|
||||||
|
@ -72,6 +72,7 @@ DeviceStorageStatics::InitializeDirs()
|
|||||||
DeviceStorageStatics::DeviceStorageStatics()
|
DeviceStorageStatics::DeviceStorageStatics()
|
||||||
: mInitialized(false)
|
: mInitialized(false)
|
||||||
, mPromptTesting(false)
|
, mPromptTesting(false)
|
||||||
|
, mLowDiskSpace(false)
|
||||||
{
|
{
|
||||||
DS_LOG_INFO("");
|
DS_LOG_INFO("");
|
||||||
}
|
}
|
||||||
@ -358,6 +359,16 @@ DeviceStorageStatics::IsPromptTesting()
|
|||||||
return sInstance->mPromptTesting;
|
return sInstance->mPromptTesting;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
DeviceStorageStatics::LowDiskSpace()
|
||||||
|
{
|
||||||
|
StaticMutexAutoLock lock(sMutex);
|
||||||
|
if (NS_WARN_IF(!sInstance)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return sInstance->mLowDiskSpace;
|
||||||
|
}
|
||||||
|
|
||||||
/* static */ void
|
/* static */ void
|
||||||
DeviceStorageStatics::GetWritableName(nsString& aName)
|
DeviceStorageStatics::GetWritableName(nsString& aName)
|
||||||
{
|
{
|
||||||
@ -605,27 +616,29 @@ DeviceStorageStatics::Observe(nsISupports* aSubject,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp(aTopic, kDiskSpaceWatcher)) {
|
if (!strcmp(aTopic, kDiskSpaceWatcher)) {
|
||||||
// 'disk-space-watcher' notifications are sent when there is a modification
|
|
||||||
// of a file in a specific location while a low device storage situation
|
|
||||||
// exists or after recovery of a low storage situation. For Firefox OS,
|
|
||||||
// these notifications are specific for apps storage.
|
|
||||||
bool lowDiskSpace = false;
|
|
||||||
if (!NS_strcmp(aData, MOZ_UTF16("full"))) {
|
|
||||||
lowDiskSpace = true;
|
|
||||||
} else if (NS_strcmp(aData, MOZ_UTF16("free"))) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
StaticMutexAutoLock lock(sMutex);
|
StaticMutexAutoLock lock(sMutex);
|
||||||
if (NS_WARN_IF(!sInstance)) {
|
if (NS_WARN_IF(!sInstance)) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 'disk-space-watcher' notifications are sent when there is a modification
|
||||||
|
// of a file in a specific location while a low device storage situation
|
||||||
|
// exists or after recovery of a low storage situation. For Firefox OS,
|
||||||
|
// these notifications are specific for apps storage.
|
||||||
|
if (!NS_strcmp(aData, MOZ_UTF16("full"))) {
|
||||||
|
sInstance->mLowDiskSpace = true;
|
||||||
|
} else if (!NS_strcmp(aData, MOZ_UTF16("free"))) {
|
||||||
|
sInstance->mLowDiskSpace = false;
|
||||||
|
} else {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
uint32_t i = mListeners.Length();
|
uint32_t i = mListeners.Length();
|
||||||
DS_LOG_INFO("disk space %d (%u)", lowDiskSpace, i);
|
DS_LOG_INFO("disk space %d (%u)", sInstance->mLowDiskSpace, i);
|
||||||
while (i > 0) {
|
while (i > 0) {
|
||||||
--i;
|
--i;
|
||||||
mListeners[i]->OnDiskSpaceWatcher(lowDiskSpace);
|
mListeners[i]->OnDiskSpaceWatcher(sInstance->mLowDiskSpace);
|
||||||
}
|
}
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ public:
|
|||||||
static void AddListener(nsDOMDeviceStorage* aListener);
|
static void AddListener(nsDOMDeviceStorage* aListener);
|
||||||
static void RemoveListener(nsDOMDeviceStorage* aListener);
|
static void RemoveListener(nsDOMDeviceStorage* aListener);
|
||||||
|
|
||||||
|
static bool LowDiskSpace();
|
||||||
static bool IsPromptTesting();
|
static bool IsPromptTesting();
|
||||||
static void GetWritableName(nsString& aName);
|
static void GetWritableName(nsString& aName);
|
||||||
static void SetWritableName(const nsAString& aName);
|
static void SetWritableName(const nsAString& aName);
|
||||||
@ -92,6 +93,7 @@ private:
|
|||||||
|
|
||||||
bool mInitialized;
|
bool mInitialized;
|
||||||
bool mPromptTesting;
|
bool mPromptTesting;
|
||||||
|
bool mLowDiskSpace;
|
||||||
nsString mWritableName;
|
nsString mWritableName;
|
||||||
|
|
||||||
static StaticRefPtr<DeviceStorageStatics> sInstance;
|
static StaticRefPtr<DeviceStorageStatics> sInstance;
|
||||||
|
@ -3374,6 +3374,12 @@ nsDOMDeviceStorage::IsRemovable()
|
|||||||
return mIsRemovable;
|
return mIsRemovable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
nsDOMDeviceStorage::LowDiskSpace()
|
||||||
|
{
|
||||||
|
return DeviceStorageStatics::LowDiskSpace();
|
||||||
|
}
|
||||||
|
|
||||||
already_AddRefed<Promise>
|
already_AddRefed<Promise>
|
||||||
nsDOMDeviceStorage::GetRoot(ErrorResult& aRv)
|
nsDOMDeviceStorage::GetRoot(ErrorResult& aRv)
|
||||||
{
|
{
|
||||||
|
@ -484,6 +484,18 @@ const kEventConstructors = {
|
|||||||
return e;
|
return e;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
TCPSocketErrorEvent: { create: function(aName, aProps) {
|
||||||
|
return new TCPSocketErrorEvent(aName, aProps);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TCPSocketEvent: { create: function(aName, aProps) {
|
||||||
|
return new TCPSocketEvent(aName, aProps);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TCPServerSocketEvent: { create: function(aName, aProps) {
|
||||||
|
return new TCPServerSocketEvent(aName, aProps);
|
||||||
|
},
|
||||||
|
},
|
||||||
TimeEvent: { create: function (aName, aProps) {
|
TimeEvent: { create: function (aName, aProps) {
|
||||||
var e = document.createEvent("timeevent");
|
var e = document.createEvent("timeevent");
|
||||||
e.initTimeEvent(aName, aProps.view, aProps.detail);
|
e.initTimeEvent(aName, aProps.view, aProps.detail);
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
#include "WorkerRunnable.h"
|
#include "WorkerRunnable.h"
|
||||||
#include "WorkerScope.h"
|
#include "WorkerScope.h"
|
||||||
#include "Workers.h"
|
#include "Workers.h"
|
||||||
|
#include "FetchUtil.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace dom {
|
namespace dom {
|
||||||
@ -909,53 +910,6 @@ ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUS
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class StreamDecoder final
|
|
||||||
{
|
|
||||||
nsCOMPtr<nsIUnicodeDecoder> mDecoder;
|
|
||||||
nsString mDecoded;
|
|
||||||
|
|
||||||
public:
|
|
||||||
StreamDecoder()
|
|
||||||
: mDecoder(EncodingUtils::DecoderForEncoding("UTF-8"))
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(mDecoder);
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
AppendText(const char* aSrcBuffer, uint32_t aSrcBufferLen)
|
|
||||||
{
|
|
||||||
int32_t destBufferLen;
|
|
||||||
nsresult rv =
|
|
||||||
mDecoder->GetMaxLength(aSrcBuffer, aSrcBufferLen, &destBufferLen);
|
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mDecoded.SetCapacity(mDecoded.Length() + destBufferLen, fallible)) {
|
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
char16_t* destBuffer = mDecoded.BeginWriting() + mDecoded.Length();
|
|
||||||
int32_t totalChars = mDecoded.Length();
|
|
||||||
|
|
||||||
int32_t srcLen = (int32_t) aSrcBufferLen;
|
|
||||||
int32_t outLen = destBufferLen;
|
|
||||||
rv = mDecoder->Convert(aSrcBuffer, &srcLen, destBuffer, &outLen);
|
|
||||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
||||||
|
|
||||||
totalChars += outLen;
|
|
||||||
mDecoded.SetLength(totalChars);
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsString&
|
|
||||||
GetText()
|
|
||||||
{
|
|
||||||
return mDecoded;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Called on successfully reading the complete stream.
|
* Called on successfully reading the complete stream.
|
||||||
*/
|
*/
|
||||||
@ -1436,128 +1390,81 @@ FetchBody<Derived>::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength
|
|||||||
jsapi.Init(DerivedClass()->GetParentObject());
|
jsapi.Init(DerivedClass()->GetParentObject());
|
||||||
JSContext* cx = jsapi.cx();
|
JSContext* cx = jsapi.cx();
|
||||||
|
|
||||||
|
ErrorResult error;
|
||||||
|
|
||||||
switch (mConsumeType) {
|
switch (mConsumeType) {
|
||||||
case CONSUME_ARRAYBUFFER: {
|
case CONSUME_ARRAYBUFFER: {
|
||||||
JS::Rooted<JSObject*> arrayBuffer(cx);
|
JS::Rooted<JSObject*> arrayBuffer(cx);
|
||||||
arrayBuffer = JS_NewArrayBufferWithContents(cx, aResultLength, reinterpret_cast<void *>(aResult));
|
FetchUtil::ConsumeArrayBuffer(cx, &arrayBuffer, aResultLength, aResult,
|
||||||
if (!arrayBuffer) {
|
error);
|
||||||
JS_ClearPendingException(cx);
|
|
||||||
localPromise->MaybeReject(NS_ERROR_DOM_UNKNOWN_ERR);
|
|
||||||
NS_WARNING("OUT OF MEMORY");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
JS::Rooted<JS::Value> val(cx);
|
error.WouldReportJSException();
|
||||||
val.setObjectOrNull(arrayBuffer);
|
if (!error.Failed()) {
|
||||||
localPromise->MaybeResolve(cx, val);
|
JS::Rooted<JS::Value> val(cx);
|
||||||
// ArrayBuffer takes over ownership.
|
val.setObjectOrNull(arrayBuffer);
|
||||||
autoFree.Reset();
|
|
||||||
return;
|
localPromise->MaybeResolve(cx, val);
|
||||||
|
// ArrayBuffer takes over ownership.
|
||||||
|
autoFree.Reset();
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case CONSUME_BLOB: {
|
case CONSUME_BLOB: {
|
||||||
nsRefPtr<dom::Blob> blob =
|
nsRefPtr<dom::Blob> blob = FetchUtil::ConsumeBlob(
|
||||||
Blob::CreateMemoryBlob(DerivedClass()->GetParentObject(),
|
DerivedClass()->GetParentObject(), NS_ConvertUTF8toUTF16(mMimeType),
|
||||||
reinterpret_cast<void *>(aResult), aResultLength,
|
aResultLength, aResult, error);
|
||||||
NS_ConvertUTF8toUTF16(mMimeType));
|
error.WouldReportJSException();
|
||||||
|
if (!error.Failed()) {
|
||||||
if (!blob) {
|
localPromise->MaybeResolve(blob);
|
||||||
localPromise->MaybeReject(NS_ERROR_DOM_UNKNOWN_ERR);
|
// File takes over ownership.
|
||||||
return;
|
autoFree.Reset();
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
localPromise->MaybeResolve(blob);
|
|
||||||
// File takes over ownership.
|
|
||||||
autoFree.Reset();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
case CONSUME_FORMDATA: {
|
case CONSUME_FORMDATA: {
|
||||||
nsCString data;
|
nsCString data;
|
||||||
data.Adopt(reinterpret_cast<char*>(aResult), aResultLength);
|
data.Adopt(reinterpret_cast<char*>(aResult), aResultLength);
|
||||||
autoFree.Reset();
|
autoFree.Reset();
|
||||||
|
|
||||||
NS_NAMED_LITERAL_CSTRING(formDataMimeType, "multipart/form-data");
|
nsRefPtr<nsFormData> fd = FetchUtil::ConsumeFormData(
|
||||||
|
DerivedClass()->GetParentObject(),
|
||||||
// Allow semicolon separated boundary/encoding suffix like multipart/form-data; boundary=
|
mMimeType, data, error);
|
||||||
// but disallow multipart/form-datafoobar.
|
if (!error.Failed()) {
|
||||||
bool isValidFormDataMimeType = StringBeginsWith(mMimeType, formDataMimeType);
|
|
||||||
|
|
||||||
if (isValidFormDataMimeType && mMimeType.Length() > formDataMimeType.Length()) {
|
|
||||||
isValidFormDataMimeType = mMimeType[formDataMimeType.Length()] == ';';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isValidFormDataMimeType) {
|
|
||||||
FormDataParser parser(mMimeType, data, DerivedClass()->GetParentObject());
|
|
||||||
if (!parser.Parse()) {
|
|
||||||
ErrorResult result;
|
|
||||||
result.ThrowTypeError(MSG_BAD_FORMDATA);
|
|
||||||
localPromise->MaybeReject(result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsRefPtr<nsFormData> fd = parser.FormData();
|
|
||||||
MOZ_ASSERT(fd);
|
|
||||||
localPromise->MaybeResolve(fd);
|
localPromise->MaybeResolve(fd);
|
||||||
} else {
|
|
||||||
NS_NAMED_LITERAL_CSTRING(urlDataMimeType, "application/x-www-form-urlencoded");
|
|
||||||
bool isValidUrlEncodedMimeType = StringBeginsWith(mMimeType, urlDataMimeType);
|
|
||||||
|
|
||||||
if (isValidUrlEncodedMimeType && mMimeType.Length() > urlDataMimeType.Length()) {
|
|
||||||
isValidUrlEncodedMimeType = mMimeType[urlDataMimeType.Length()] == ';';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isValidUrlEncodedMimeType) {
|
|
||||||
URLParams params;
|
|
||||||
params.ParseInput(data);
|
|
||||||
|
|
||||||
nsRefPtr<nsFormData> fd = new nsFormData(DerivedClass()->GetParentObject());
|
|
||||||
FillFormIterator iterator(fd);
|
|
||||||
DebugOnly<bool> status = params.ForEach(iterator);
|
|
||||||
MOZ_ASSERT(status);
|
|
||||||
|
|
||||||
localPromise->MaybeResolve(fd);
|
|
||||||
} else {
|
|
||||||
ErrorResult result;
|
|
||||||
result.ThrowTypeError(MSG_BAD_FORMDATA);
|
|
||||||
localPromise->MaybeReject(result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
case CONSUME_TEXT:
|
case CONSUME_TEXT:
|
||||||
// fall through handles early exit.
|
// fall through handles early exit.
|
||||||
case CONSUME_JSON: {
|
case CONSUME_JSON: {
|
||||||
StreamDecoder decoder;
|
nsString decoded;
|
||||||
decoder.AppendText(reinterpret_cast<char*>(aResult), aResultLength);
|
if (NS_SUCCEEDED(FetchUtil::ConsumeText(aResultLength, aResult, decoded))) {
|
||||||
|
if (mConsumeType == CONSUME_TEXT) {
|
||||||
nsString& decoded = decoder.GetText();
|
localPromise->MaybeResolve(decoded);
|
||||||
if (mConsumeType == CONSUME_TEXT) {
|
} else {
|
||||||
localPromise->MaybeResolve(decoded);
|
JS::Rooted<JS::Value> json(cx);
|
||||||
return;
|
FetchUtil::ConsumeJson(cx, &json, decoded, error);
|
||||||
}
|
if (!error.Failed()) {
|
||||||
|
localPromise->MaybeResolve(cx, json);
|
||||||
AutoForceSetExceptionOnContext forceExn(cx);
|
}
|
||||||
JS::Rooted<JS::Value> json(cx);
|
|
||||||
if (!JS_ParseJSON(cx, decoded.get(), decoded.Length(), &json)) {
|
|
||||||
if (!JS_IsExceptionPending(cx)) {
|
|
||||||
localPromise->MaybeReject(NS_ERROR_DOM_UNKNOWN_ERR);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
JS::Rooted<JS::Value> exn(cx);
|
break;
|
||||||
DebugOnly<bool> gotException = JS_GetPendingException(cx, &exn);
|
|
||||||
MOZ_ASSERT(gotException);
|
|
||||||
|
|
||||||
JS_ClearPendingException(cx);
|
|
||||||
localPromise->MaybeReject(cx, exn);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
localPromise->MaybeResolve(cx, json);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
NS_NOTREACHED("Unexpected consume body type");
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_NOTREACHED("Unexpected consume body type");
|
error.WouldReportJSException();
|
||||||
|
if (error.Failed()) {
|
||||||
|
if (error.IsJSException()) {
|
||||||
|
JS::Rooted<JS::Value> exn(cx);
|
||||||
|
error.StealJSException(cx, &exn);
|
||||||
|
localPromise->MaybeReject(cx, exn);
|
||||||
|
} else {
|
||||||
|
localPromise->MaybeReject(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Derived>
|
template <class Derived>
|
||||||
@ -1622,5 +1529,6 @@ FetchBody<Request>::SetMimeType();
|
|||||||
template
|
template
|
||||||
void
|
void
|
||||||
FetchBody<Response>::SetMimeType();
|
FetchBody<Response>::SetMimeType();
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
@ -1,10 +1,63 @@
|
|||||||
#include "FetchUtil.h"
|
#include "FetchUtil.h"
|
||||||
|
|
||||||
#include "nsError.h"
|
#include "nsError.h"
|
||||||
|
#include "nsIUnicodeDecoder.h"
|
||||||
#include "nsString.h"
|
#include "nsString.h"
|
||||||
|
|
||||||
|
#include "mozilla/dom/EncodingUtils.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
class StreamDecoder final
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsIUnicodeDecoder> mDecoder;
|
||||||
|
nsString mDecoded;
|
||||||
|
|
||||||
|
public:
|
||||||
|
StreamDecoder()
|
||||||
|
: mDecoder(EncodingUtils::DecoderForEncoding("UTF-8"))
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mDecoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
AppendText(const char* aSrcBuffer, uint32_t aSrcBufferLen)
|
||||||
|
{
|
||||||
|
int32_t destBufferLen;
|
||||||
|
nsresult rv =
|
||||||
|
mDecoder->GetMaxLength(aSrcBuffer, aSrcBufferLen, &destBufferLen);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mDecoded.SetCapacity(mDecoded.Length() + destBufferLen, fallible)) {
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
char16_t* destBuffer = mDecoded.BeginWriting() + mDecoded.Length();
|
||||||
|
int32_t totalChars = mDecoded.Length();
|
||||||
|
|
||||||
|
int32_t srcLen = (int32_t) aSrcBufferLen;
|
||||||
|
int32_t outLen = destBufferLen;
|
||||||
|
rv = mDecoder->Convert(aSrcBuffer, &srcLen, destBuffer, &outLen);
|
||||||
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||||
|
|
||||||
|
totalChars += outLen;
|
||||||
|
mDecoded.SetLength(totalChars);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsString&
|
||||||
|
GetText()
|
||||||
|
{
|
||||||
|
return mDecoded;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
nsresult
|
nsresult
|
||||||
FetchUtil::GetValidRequestMethod(const nsACString& aMethod, nsCString& outMethod)
|
FetchUtil::GetValidRequestMethod(const nsACString& aMethod, nsCString& outMethod)
|
||||||
@ -33,5 +86,133 @@ FetchUtil::GetValidRequestMethod(const nsACString& aMethod, nsCString& outMethod
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void
|
||||||
|
FetchUtil::ConsumeArrayBuffer(JSContext* aCx,
|
||||||
|
JS::MutableHandle<JSObject*> aValue,
|
||||||
|
uint32_t aInputLength, uint8_t* aInput,
|
||||||
|
ErrorResult& aRv)
|
||||||
|
{
|
||||||
|
JS::Rooted<JSObject*> arrayBuffer(aCx);
|
||||||
|
arrayBuffer = JS_NewArrayBufferWithContents(aCx, aInputLength,
|
||||||
|
reinterpret_cast<void *>(aInput));
|
||||||
|
if (!arrayBuffer) {
|
||||||
|
JS_ClearPendingException(aCx);
|
||||||
|
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
aValue.set(arrayBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
already_AddRefed<Blob>
|
||||||
|
FetchUtil::ConsumeBlob(nsISupports* aParent, const nsString& aMimeType,
|
||||||
|
uint32_t aInputLength, uint8_t* aInput,
|
||||||
|
ErrorResult& aRv)
|
||||||
|
{
|
||||||
|
nsRefPtr<Blob> blob =
|
||||||
|
Blob::CreateMemoryBlob(aParent,
|
||||||
|
reinterpret_cast<void *>(aInput), aInputLength,
|
||||||
|
aMimeType);
|
||||||
|
|
||||||
|
if (!blob) {
|
||||||
|
aRv.Throw(NS_ERROR_DOM_UNKNOWN_ERR);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return blob.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
already_AddRefed<nsFormData>
|
||||||
|
FetchUtil::ConsumeFormData(nsIGlobalObject* aParent, const nsCString& aMimeType,
|
||||||
|
const nsCString& aStr, ErrorResult& aRv)
|
||||||
|
{
|
||||||
|
NS_NAMED_LITERAL_CSTRING(formDataMimeType, "multipart/form-data");
|
||||||
|
|
||||||
|
// Allow semicolon separated boundary/encoding suffix like multipart/form-data; boundary=
|
||||||
|
// but disallow multipart/form-datafoobar.
|
||||||
|
bool isValidFormDataMimeType = StringBeginsWith(aMimeType, formDataMimeType);
|
||||||
|
|
||||||
|
if (isValidFormDataMimeType && aMimeType.Length() > formDataMimeType.Length()) {
|
||||||
|
isValidFormDataMimeType = aMimeType[formDataMimeType.Length()] == ';';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isValidFormDataMimeType) {
|
||||||
|
FormDataParser parser(aMimeType, aStr, aParent);
|
||||||
|
if (!parser.Parse()) {
|
||||||
|
aRv.ThrowTypeError(MSG_BAD_FORMDATA);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsRefPtr<nsFormData> fd = parser.FormData();
|
||||||
|
MOZ_ASSERT(fd);
|
||||||
|
return fd.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_NAMED_LITERAL_CSTRING(urlDataMimeType, "application/x-www-form-urlencoded");
|
||||||
|
bool isValidUrlEncodedMimeType = StringBeginsWith(aMimeType, urlDataMimeType);
|
||||||
|
|
||||||
|
if (isValidUrlEncodedMimeType && aMimeType.Length() > urlDataMimeType.Length()) {
|
||||||
|
isValidUrlEncodedMimeType = aMimeType[urlDataMimeType.Length()] == ';';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isValidUrlEncodedMimeType) {
|
||||||
|
URLParams params;
|
||||||
|
params.ParseInput(aStr);
|
||||||
|
|
||||||
|
nsRefPtr<nsFormData> fd = new nsFormData(aParent);
|
||||||
|
FillFormIterator iterator(fd);
|
||||||
|
DebugOnly<bool> status = params.ForEach(iterator);
|
||||||
|
MOZ_ASSERT(status);
|
||||||
|
|
||||||
|
return fd.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
aRv.ThrowTypeError(MSG_BAD_FORMDATA);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
nsresult
|
||||||
|
FetchUtil::ConsumeText(uint32_t aInputLength, uint8_t* aInput,
|
||||||
|
nsString& aText)
|
||||||
|
{
|
||||||
|
StreamDecoder decoder;
|
||||||
|
nsresult rv = decoder.AppendText(reinterpret_cast<char*>(aInput),
|
||||||
|
aInputLength);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
aText = decoder.GetText();
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void
|
||||||
|
FetchUtil::ConsumeJson(JSContext* aCx, JS::MutableHandle<JS::Value> aValue,
|
||||||
|
const nsString& aStr, ErrorResult& aRv)
|
||||||
|
{
|
||||||
|
aRv.MightThrowJSException();
|
||||||
|
|
||||||
|
AutoForceSetExceptionOnContext forceExn(aCx);
|
||||||
|
JS::Rooted<JS::Value> json(aCx);
|
||||||
|
if (!JS_ParseJSON(aCx, aStr.get(), aStr.Length(), &json)) {
|
||||||
|
if (!JS_IsExceptionPending(aCx)) {
|
||||||
|
aRv.Throw(NS_ERROR_DOM_UNKNOWN_ERR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JS::Rooted<JS::Value> exn(aCx);
|
||||||
|
DebugOnly<bool> gotException = JS_GetPendingException(aCx, &exn);
|
||||||
|
MOZ_ASSERT(gotException);
|
||||||
|
|
||||||
|
JS_ClearPendingException(aCx);
|
||||||
|
aRv.ThrowJSException(aCx, exn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
aValue.set(json);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
@ -3,6 +3,10 @@
|
|||||||
|
|
||||||
#include "nsString.h"
|
#include "nsString.h"
|
||||||
#include "nsError.h"
|
#include "nsError.h"
|
||||||
|
#include "nsFormData.h"
|
||||||
|
|
||||||
|
#include "mozilla/ErrorResult.h"
|
||||||
|
#include "mozilla/dom/File.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace dom {
|
namespace dom {
|
||||||
@ -21,6 +25,46 @@ public:
|
|||||||
*/
|
*/
|
||||||
static nsresult
|
static nsresult
|
||||||
GetValidRequestMethod(const nsACString& aMethod, nsCString& outMethod);
|
GetValidRequestMethod(const nsACString& aMethod, nsCString& outMethod);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an array buffer from an array, assigning the result to |aValue|.
|
||||||
|
* The array buffer takes ownership of |aInput|, which must be allocated
|
||||||
|
* by |malloc|.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
ConsumeArrayBuffer(JSContext* aCx, JS::MutableHandle<JSObject*> aValue,
|
||||||
|
uint32_t aInputLength, uint8_t* aInput, ErrorResult& aRv);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an in-memory blob from an array. The blob takes ownership of
|
||||||
|
* |aInput|, which must be allocated by |malloc|.
|
||||||
|
*/
|
||||||
|
static already_AddRefed<Blob>
|
||||||
|
ConsumeBlob(nsISupports* aParent, const nsString& aMimeType,
|
||||||
|
uint32_t aInputLength, uint8_t* aInput, ErrorResult& aRv);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a form data object from a UTF-8 encoded |aStr|. Returns |nullptr|
|
||||||
|
* and sets |aRv| to MSG_BAD_FORMDATA if |aStr| contains invalid data.
|
||||||
|
*/
|
||||||
|
static already_AddRefed<nsFormData>
|
||||||
|
ConsumeFormData(nsIGlobalObject* aParent, const nsCString& aMimeType,
|
||||||
|
const nsCString& aStr, ErrorResult& aRv);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UTF-8 decodes |aInput| into |aText|. The caller may free |aInput|
|
||||||
|
* once this method returns.
|
||||||
|
*/
|
||||||
|
static nsresult
|
||||||
|
ConsumeText(uint32_t aInputLength, uint8_t* aInput, nsString& aText);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a UTF-8 encoded |aStr| as JSON, assigning the result to |aValue|.
|
||||||
|
* Sets |aRv| to a syntax error if |aStr| contains invalid data.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
ConsumeJson(JSContext* aCx, JS::MutableHandle<JS::Value> aValue,
|
||||||
|
const nsString& aStr, ErrorResult& aRv);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
|
@ -82,6 +82,16 @@ public:
|
|||||||
aURL.Assign(mURL);
|
aURL.Assign(mURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
GetUnfilteredUrl(nsCString& aURL) const
|
||||||
|
{
|
||||||
|
if (mWrappedResponse) {
|
||||||
|
return mWrappedResponse->GetUrl(aURL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetUrl(aURL);
|
||||||
|
}
|
||||||
|
|
||||||
// SetUrl should only be called when the fragment has alredy been stripped
|
// SetUrl should only be called when the fragment has alredy been stripped
|
||||||
void
|
void
|
||||||
SetUrl(const nsACString& aURL)
|
SetUrl(const nsACString& aURL)
|
||||||
|
@ -34,7 +34,7 @@ interface nsIServiceWorkerInfo : nsISupports
|
|||||||
readonly attribute DOMString waitingCacheName;
|
readonly attribute DOMString waitingCacheName;
|
||||||
};
|
};
|
||||||
|
|
||||||
[scriptable, builtinclass, uuid(8d80dd18-597b-4378-b41e-768bfe48dd4f)]
|
[scriptable, builtinclass, uuid(471b2d5d-64c3-4dea-bde1-219853dcaac8)]
|
||||||
interface nsIServiceWorkerManager : nsISupports
|
interface nsIServiceWorkerManager : nsISupports
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@ -141,9 +141,10 @@ interface nsIServiceWorkerManager : nsISupports
|
|||||||
in AString aIcon,
|
in AString aIcon,
|
||||||
in AString aData,
|
in AString aData,
|
||||||
in AString aBehavior);
|
in AString aBehavior);
|
||||||
void sendPushEvent(in ACString aOriginAttributes,
|
[optional_argc] void sendPushEvent(in ACString aOriginAttributes,
|
||||||
in ACString aScope,
|
in ACString aScope,
|
||||||
in DOMString aData);
|
[optional] in uint32_t aDataLength,
|
||||||
|
[optional, array, size_is(aDataLength)] in uint8_t aDataBytes);
|
||||||
void sendPushSubscriptionChangeEvent(in ACString aOriginAttributes,
|
void sendPushSubscriptionChangeEvent(in ACString aOriginAttributes,
|
||||||
in ACString scope);
|
in ACString scope);
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
29 = Unbalanced curly brace.
|
29 = Unbalanced curly brace.
|
||||||
30 = Creating an element with an invalid QName.
|
30 = Creating an element with an invalid QName.
|
||||||
31 = Variable binding shadows variable binding within the same template.
|
31 = Variable binding shadows variable binding within the same template.
|
||||||
|
32 = Call to the key function not allowed.
|
||||||
|
|
||||||
LoadingError = Error loading stylesheet: %S
|
LoadingError = Error loading stylesheet: %S
|
||||||
TransformError = Error during XSLT transformation: %S
|
TransformError = Error during XSLT transformation: %S
|
||||||
|
@ -358,6 +358,14 @@ DOMMediaStream::StopTrack(TrackID aTrackID)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
already_AddRefed<Promise>
|
||||||
|
DOMMediaStream::ApplyConstraintsToTrack(TrackID aTrackID,
|
||||||
|
const MediaTrackConstraints& aConstraints,
|
||||||
|
ErrorResult &aRv)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
DOMMediaStream::CombineWithPrincipal(nsIPrincipal* aPrincipal)
|
DOMMediaStream::CombineWithPrincipal(nsIPrincipal* aPrincipal)
|
||||||
{
|
{
|
||||||
|
@ -48,6 +48,7 @@ class VideoTrack;
|
|||||||
class AudioTrackList;
|
class AudioTrackList;
|
||||||
class VideoTrackList;
|
class VideoTrackList;
|
||||||
class MediaTrackListListener;
|
class MediaTrackListListener;
|
||||||
|
struct MediaTrackConstraints;
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
|
|
||||||
namespace layers {
|
namespace layers {
|
||||||
@ -77,6 +78,7 @@ class DOMMediaStream : public DOMEventTargetHelper
|
|||||||
typedef dom::MediaTrackListListener MediaTrackListListener;
|
typedef dom::MediaTrackListListener MediaTrackListListener;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
typedef dom::MediaTrackConstraints MediaTrackConstraints;
|
||||||
typedef uint8_t TrackTypeHints;
|
typedef uint8_t TrackTypeHints;
|
||||||
|
|
||||||
DOMMediaStream();
|
DOMMediaStream();
|
||||||
@ -121,6 +123,11 @@ public:
|
|||||||
|
|
||||||
virtual void StopTrack(TrackID aTrackID);
|
virtual void StopTrack(TrackID aTrackID);
|
||||||
|
|
||||||
|
virtual already_AddRefed<dom::Promise>
|
||||||
|
ApplyConstraintsToTrack(TrackID aTrackID,
|
||||||
|
const MediaTrackConstraints& aConstraints,
|
||||||
|
ErrorResult &aRv);
|
||||||
|
|
||||||
virtual DOMLocalMediaStream* AsDOMLocalMediaStream() { return nullptr; }
|
virtual DOMLocalMediaStream* AsDOMLocalMediaStream() { return nullptr; }
|
||||||
virtual DOMHwMediaStream* AsDOMHwMediaStream() { return nullptr; }
|
virtual DOMHwMediaStream* AsDOMHwMediaStream() { return nullptr; }
|
||||||
|
|
||||||
|
@ -79,8 +79,6 @@
|
|||||||
#include "mozilla/WindowsVersion.h"
|
#include "mozilla/WindowsVersion.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
|
// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
|
||||||
// GetTickCount() and conflicts with MediaStream::GetCurrentTime.
|
// GetTickCount() and conflicts with MediaStream::GetCurrentTime.
|
||||||
#ifdef GetCurrentTime
|
#ifdef GetCurrentTime
|
||||||
@ -119,6 +117,7 @@ using dom::File;
|
|||||||
using dom::MediaStreamConstraints;
|
using dom::MediaStreamConstraints;
|
||||||
using dom::MediaTrackConstraintSet;
|
using dom::MediaTrackConstraintSet;
|
||||||
using dom::MediaTrackConstraints;
|
using dom::MediaTrackConstraints;
|
||||||
|
using dom::MediaStreamTrack;
|
||||||
using dom::MediaStreamError;
|
using dom::MediaStreamError;
|
||||||
using dom::GetUserMediaRequest;
|
using dom::GetUserMediaRequest;
|
||||||
using dom::Sequence;
|
using dom::Sequence;
|
||||||
@ -214,6 +213,154 @@ HostHasPermission(nsIURI &docURI)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generic class for running long media operations like Start off the main
|
||||||
|
// thread, and then (because nsDOMMediaStreams aren't threadsafe),
|
||||||
|
// ProxyReleases mStream since it's cycle collected.
|
||||||
|
class MediaOperationTask : public Task
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// so we can send Stop without AddRef()ing from the MSG thread
|
||||||
|
MediaOperationTask(MediaOperation aType,
|
||||||
|
GetUserMediaCallbackMediaStreamListener* aListener,
|
||||||
|
DOMMediaStream* aStream,
|
||||||
|
DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback,
|
||||||
|
AudioDevice* aAudioDevice,
|
||||||
|
VideoDevice* aVideoDevice,
|
||||||
|
bool aBool,
|
||||||
|
uint64_t aWindowID,
|
||||||
|
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
|
||||||
|
const dom::MediaTrackConstraints& aConstraints = dom::MediaTrackConstraints())
|
||||||
|
: mType(aType)
|
||||||
|
, mStream(aStream)
|
||||||
|
, mOnTracksAvailableCallback(aOnTracksAvailableCallback)
|
||||||
|
, mAudioDevice(aAudioDevice)
|
||||||
|
, mVideoDevice(aVideoDevice)
|
||||||
|
, mListener(aListener)
|
||||||
|
, mBool(aBool)
|
||||||
|
, mWindowID(aWindowID)
|
||||||
|
, mOnFailure(aError)
|
||||||
|
, mConstraints(aConstraints)
|
||||||
|
{}
|
||||||
|
|
||||||
|
~MediaOperationTask()
|
||||||
|
{
|
||||||
|
// MediaStreams can be released on any thread.
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ReturnCallbackError(nsresult rv, const char* errorLog);
|
||||||
|
|
||||||
|
void
|
||||||
|
Run()
|
||||||
|
{
|
||||||
|
SourceMediaStream *source = mListener->GetSourceStream();
|
||||||
|
// No locking between these is required as all the callbacks for the
|
||||||
|
// same MediaStream will occur on the same thread.
|
||||||
|
if (!source) // means the stream was never Activated()
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (mType) {
|
||||||
|
case MEDIA_START:
|
||||||
|
{
|
||||||
|
NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
|
||||||
|
nsresult rv;
|
||||||
|
|
||||||
|
if (mAudioDevice) {
|
||||||
|
rv = mAudioDevice->GetSource()->Start(source, kAudioTrack);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
ReturnCallbackError(rv, "Starting audio failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mVideoDevice) {
|
||||||
|
rv = mVideoDevice->GetSource()->Start(source, kVideoTrack);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
ReturnCallbackError(rv, "Starting video failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Start() queued the tracks to be added synchronously to avoid races
|
||||||
|
source->FinishAddTracks();
|
||||||
|
|
||||||
|
source->SetPullEnabled(true);
|
||||||
|
source->AdvanceKnownTracksTime(STREAM_TIME_MAX);
|
||||||
|
|
||||||
|
MM_LOG(("started all sources"));
|
||||||
|
// Forward mOnTracksAvailableCallback to GetUserMediaNotificationEvent,
|
||||||
|
// because mOnTracksAvailableCallback needs to be added to mStream
|
||||||
|
// on the main thread.
|
||||||
|
nsIRunnable *event =
|
||||||
|
new GetUserMediaNotificationEvent(GetUserMediaNotificationEvent::STARTING,
|
||||||
|
mStream.forget(),
|
||||||
|
mOnTracksAvailableCallback.forget(),
|
||||||
|
mAudioDevice != nullptr,
|
||||||
|
mVideoDevice != nullptr,
|
||||||
|
mWindowID, mOnFailure.forget());
|
||||||
|
// event must always be released on mainthread due to the JS callbacks
|
||||||
|
// in the TracksAvailableCallback
|
||||||
|
NS_DispatchToMainThread(event);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MEDIA_STOP:
|
||||||
|
case MEDIA_STOP_TRACK:
|
||||||
|
{
|
||||||
|
NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
|
||||||
|
if (mAudioDevice) {
|
||||||
|
mAudioDevice->GetSource()->Stop(source, kAudioTrack);
|
||||||
|
mAudioDevice->GetSource()->Deallocate();
|
||||||
|
}
|
||||||
|
if (mVideoDevice) {
|
||||||
|
mVideoDevice->GetSource()->Stop(source, kVideoTrack);
|
||||||
|
mVideoDevice->GetSource()->Deallocate();
|
||||||
|
}
|
||||||
|
// Do this after stopping all tracks with EndTrack()
|
||||||
|
if (mBool) {
|
||||||
|
source->Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
nsIRunnable *event =
|
||||||
|
new GetUserMediaNotificationEvent(mListener,
|
||||||
|
mType == MEDIA_STOP ?
|
||||||
|
GetUserMediaNotificationEvent::STOPPING :
|
||||||
|
GetUserMediaNotificationEvent::STOPPED_TRACK,
|
||||||
|
mAudioDevice != nullptr,
|
||||||
|
mVideoDevice != nullptr,
|
||||||
|
mWindowID);
|
||||||
|
// event must always be released on mainthread due to the JS callbacks
|
||||||
|
// in the TracksAvailableCallback
|
||||||
|
NS_DispatchToMainThread(event);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MEDIA_DIRECT_LISTENERS:
|
||||||
|
{
|
||||||
|
NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
|
||||||
|
if (mVideoDevice) {
|
||||||
|
mVideoDevice->GetSource()->SetDirectListeners(mBool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
MOZ_ASSERT(false,"invalid MediaManager operation");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
MediaOperation mType;
|
||||||
|
nsRefPtr<DOMMediaStream> mStream;
|
||||||
|
nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
|
||||||
|
nsRefPtr<AudioDevice> mAudioDevice; // threadsafe
|
||||||
|
nsRefPtr<VideoDevice> mVideoDevice; // threadsafe
|
||||||
|
nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
|
||||||
|
bool mBool;
|
||||||
|
uint64_t mWindowID;
|
||||||
|
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
|
||||||
|
dom::MediaTrackConstraints mConstraints;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send an error back to content.
|
* Send an error back to content.
|
||||||
* Do this only on the main thread. The onSuccess callback is also passed here
|
* Do this only on the main thread. The onSuccess callback is also passed here
|
||||||
@ -470,6 +617,16 @@ nsresult AudioDevice::Allocate(const dom::MediaTrackConstraints &aConstraints,
|
|||||||
return GetSource()->Allocate(aConstraints, aPrefs, mID);
|
return GetSource()->Allocate(aConstraints, aPrefs, mID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult VideoDevice::Restart(const dom::MediaTrackConstraints &aConstraints,
|
||||||
|
const MediaEnginePrefs &aPrefs) {
|
||||||
|
return GetSource()->Restart(aConstraints, aPrefs, mID);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult AudioDevice::Restart(const dom::MediaTrackConstraints &aConstraints,
|
||||||
|
const MediaEnginePrefs &aPrefs) {
|
||||||
|
return GetSource()->Restart(aConstraints, aPrefs, mID);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A subclass that we only use to stash internal pointers to MediaStreamGraph objects
|
* A subclass that we only use to stash internal pointers to MediaStreamGraph objects
|
||||||
* that need to be cleaned up.
|
* that need to be cleaned up.
|
||||||
@ -480,23 +637,23 @@ public:
|
|||||||
static already_AddRefed<nsDOMUserMediaStream>
|
static already_AddRefed<nsDOMUserMediaStream>
|
||||||
CreateTrackUnionStream(nsIDOMWindow* aWindow,
|
CreateTrackUnionStream(nsIDOMWindow* aWindow,
|
||||||
GetUserMediaCallbackMediaStreamListener* aListener,
|
GetUserMediaCallbackMediaStreamListener* aListener,
|
||||||
MediaEngineSource* aAudioSource,
|
AudioDevice* aAudioDevice,
|
||||||
MediaEngineSource* aVideoSource,
|
VideoDevice* aVideoDevice,
|
||||||
MediaStreamGraph* aMSG)
|
MediaStreamGraph* aMSG)
|
||||||
{
|
{
|
||||||
nsRefPtr<nsDOMUserMediaStream> stream = new nsDOMUserMediaStream(aListener,
|
nsRefPtr<nsDOMUserMediaStream> stream = new nsDOMUserMediaStream(aListener,
|
||||||
aAudioSource,
|
aAudioDevice,
|
||||||
aVideoSource);
|
aVideoDevice);
|
||||||
stream->InitTrackUnionStream(aWindow, aMSG);
|
stream->InitTrackUnionStream(aWindow, aMSG);
|
||||||
return stream.forget();
|
return stream.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
nsDOMUserMediaStream(GetUserMediaCallbackMediaStreamListener* aListener,
|
nsDOMUserMediaStream(GetUserMediaCallbackMediaStreamListener* aListener,
|
||||||
MediaEngineSource *aAudioSource,
|
AudioDevice *aAudioDevice,
|
||||||
MediaEngineSource *aVideoSource) :
|
VideoDevice *aVideoDevice) :
|
||||||
mListener(aListener),
|
mListener(aListener),
|
||||||
mAudioSource(aAudioSource),
|
mAudioDevice(aAudioDevice),
|
||||||
mVideoSource(aVideoSource),
|
mVideoDevice(aVideoDevice),
|
||||||
mEchoOn(true),
|
mEchoOn(true),
|
||||||
mAgcOn(false),
|
mAgcOn(false),
|
||||||
mNoiseOn(true),
|
mNoiseOn(true),
|
||||||
@ -543,13 +700,60 @@ public:
|
|||||||
// risky to do late in a release since that will affect all track ends, and not
|
// risky to do late in a release since that will affect all track ends, and not
|
||||||
// just StopTrack()s.
|
// just StopTrack()s.
|
||||||
if (GetDOMTrackFor(aTrackID)) {
|
if (GetDOMTrackFor(aTrackID)) {
|
||||||
mListener->StopTrack(aTrackID, !!GetDOMTrackFor(aTrackID)->AsAudioStreamTrack());
|
mListener->StopTrack(aTrackID,
|
||||||
|
!!GetDOMTrackFor(aTrackID)->AsAudioStreamTrack());
|
||||||
} else {
|
} else {
|
||||||
LOG(("StopTrack(%d) on non-existant track", aTrackID));
|
LOG(("StopTrack(%d) on non-existent track", aTrackID));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual already_AddRefed<Promise>
|
||||||
|
ApplyConstraintsToTrack(TrackID aTrackID,
|
||||||
|
const MediaTrackConstraints& aConstraints,
|
||||||
|
ErrorResult &aRv) override
|
||||||
|
{
|
||||||
|
nsPIDOMWindow* window = static_cast<nsPIDOMWindow*>(mWindow.get());
|
||||||
|
nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(window);
|
||||||
|
nsRefPtr<Promise> promise = Promise::Create(go, aRv);
|
||||||
|
|
||||||
|
if (sInShutdown) {
|
||||||
|
nsRefPtr<MediaStreamError> error = new MediaStreamError(window,
|
||||||
|
NS_LITERAL_STRING("AbortError"),
|
||||||
|
NS_LITERAL_STRING("In shutdown"));
|
||||||
|
promise->MaybeReject(error);
|
||||||
|
return promise.forget();
|
||||||
|
}
|
||||||
|
if (!mSourceStream) {
|
||||||
|
nsRefPtr<MediaStreamError> error = new MediaStreamError(window,
|
||||||
|
NS_LITERAL_STRING("InternalError"),
|
||||||
|
NS_LITERAL_STRING("No stream."));
|
||||||
|
promise->MaybeReject(error);
|
||||||
|
return promise.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
nsRefPtr<dom::MediaStreamTrack> track = GetDOMTrackFor(aTrackID);
|
||||||
|
if (!track) {
|
||||||
|
LOG(("ApplyConstraintsToTrack(%d) on non-existent track", aTrackID));
|
||||||
|
nsRefPtr<MediaStreamError> error = new MediaStreamError(window,
|
||||||
|
NS_LITERAL_STRING("InternalError"),
|
||||||
|
NS_LITERAL_STRING("No track."));
|
||||||
|
promise->MaybeReject(error);
|
||||||
|
return promise.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef media::Pledge<bool, MediaStreamError*> PledgeVoid;
|
||||||
|
|
||||||
|
nsRefPtr<PledgeVoid> p = mListener->ApplyConstraintsToTrack(window,
|
||||||
|
aTrackID, !!track->AsAudioStreamTrack(), aConstraints);
|
||||||
|
p->Then([promise](bool& aDummy) mutable {
|
||||||
|
promise->MaybeResolve(false);
|
||||||
|
}, [promise](MediaStreamError*& reason) mutable {
|
||||||
|
promise->MaybeReject(reason);
|
||||||
|
});
|
||||||
|
return promise.forget();
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
virtual void NotifyMediaStreamTrackEnded(dom::MediaStreamTrack* aTrack)
|
virtual void NotifyMediaStreamTrackEnded(dom::MediaStreamTrack* aTrack)
|
||||||
{
|
{
|
||||||
@ -598,14 +802,14 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// let us intervene for direct listeners when someone does track.enabled = false
|
// let us intervene for direct listeners when someone does track.enabled = false
|
||||||
virtual void SetTrackEnabled(TrackID aID, bool aEnabled) override
|
virtual void SetTrackEnabled(TrackID aTrackID, bool aEnabled) override
|
||||||
{
|
{
|
||||||
// We encapsulate the SourceMediaStream and TrackUnion into one entity, so
|
// We encapsulate the SourceMediaStream and TrackUnion into one entity, so
|
||||||
// we can handle the disabling at the SourceMediaStream
|
// we can handle the disabling at the SourceMediaStream
|
||||||
|
|
||||||
// We need to find the input track ID for output ID aID, so we let the TrackUnion
|
// We need to find the input track ID for output ID aTrackID, so we let the TrackUnion
|
||||||
// forward the request to the source and translate the ID
|
// forward the request to the source and translate the ID
|
||||||
GetStream()->AsProcessedStream()->ForwardTrackEnabled(aID, aEnabled);
|
GetStream()->AsProcessedStream()->ForwardTrackEnabled(aTrackID, aEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual DOMLocalMediaStream* AsDOMLocalMediaStream() override
|
virtual DOMLocalMediaStream* AsDOMLocalMediaStream() override
|
||||||
@ -618,10 +822,10 @@ public:
|
|||||||
// MediaEngine supports only one video and on video track now and TrackID is
|
// MediaEngine supports only one video and on video track now and TrackID is
|
||||||
// fixed in MediaEngine.
|
// fixed in MediaEngine.
|
||||||
if (aTrackID == kVideoTrack) {
|
if (aTrackID == kVideoTrack) {
|
||||||
return mVideoSource;
|
return mVideoDevice ? mVideoDevice->GetSource() : nullptr;
|
||||||
}
|
}
|
||||||
else if (aTrackID == kAudioTrack) {
|
else if (aTrackID == kAudioTrack) {
|
||||||
return mAudioSource;
|
return mAudioDevice ? mAudioDevice->GetSource() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -632,8 +836,8 @@ public:
|
|||||||
nsRefPtr<SourceMediaStream> mSourceStream;
|
nsRefPtr<SourceMediaStream> mSourceStream;
|
||||||
nsRefPtr<MediaInputPort> mPort;
|
nsRefPtr<MediaInputPort> mPort;
|
||||||
nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
|
nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
|
||||||
nsRefPtr<MediaEngineSource> mAudioSource; // so we can turn on AEC
|
nsRefPtr<AudioDevice> mAudioDevice; // so we can turn on AEC
|
||||||
nsRefPtr<MediaEngineSource> mVideoSource;
|
nsRefPtr<VideoDevice> mVideoDevice;
|
||||||
bool mEchoOn;
|
bool mEchoOn;
|
||||||
bool mAgcOn;
|
bool mAgcOn;
|
||||||
bool mNoiseOn;
|
bool mNoiseOn;
|
||||||
@ -686,11 +890,11 @@ public:
|
|||||||
uint64_t aWindowID,
|
uint64_t aWindowID,
|
||||||
GetUserMediaCallbackMediaStreamListener* aListener,
|
GetUserMediaCallbackMediaStreamListener* aListener,
|
||||||
const nsCString& aOrigin,
|
const nsCString& aOrigin,
|
||||||
MediaEngineSource* aAudioSource,
|
AudioDevice* aAudioDevice,
|
||||||
MediaEngineSource* aVideoSource,
|
VideoDevice* aVideoDevice,
|
||||||
PeerIdentity* aPeerIdentity)
|
PeerIdentity* aPeerIdentity)
|
||||||
: mAudioSource(aAudioSource)
|
: mAudioDevice(aAudioDevice)
|
||||||
, mVideoSource(aVideoSource)
|
, mVideoDevice(aVideoDevice)
|
||||||
, mWindowID(aWindowID)
|
, mWindowID(aWindowID)
|
||||||
, mListener(aListener)
|
, mListener(aListener)
|
||||||
, mOrigin(aOrigin)
|
, mOrigin(aOrigin)
|
||||||
@ -787,7 +991,7 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
MediaStreamGraph::GraphDriverType graphDriverType =
|
MediaStreamGraph::GraphDriverType graphDriverType =
|
||||||
mAudioSource ? MediaStreamGraph::AUDIO_THREAD_DRIVER
|
mAudioDevice ? MediaStreamGraph::AUDIO_THREAD_DRIVER
|
||||||
: MediaStreamGraph::SYSTEM_THREAD_DRIVER;
|
: MediaStreamGraph::SYSTEM_THREAD_DRIVER;
|
||||||
MediaStreamGraph* msg =
|
MediaStreamGraph* msg =
|
||||||
MediaStreamGraph::GetInstance(graphDriverType,
|
MediaStreamGraph::GetInstance(graphDriverType,
|
||||||
@ -800,8 +1004,8 @@ public:
|
|||||||
// using the audio source and the SourceMediaStream, which acts as
|
// using the audio source and the SourceMediaStream, which acts as
|
||||||
// placeholders. We re-route a number of stream internaly in the MSG and mix
|
// placeholders. We re-route a number of stream internaly in the MSG and mix
|
||||||
// them down instead.
|
// them down instead.
|
||||||
if (mAudioSource &&
|
if (mAudioDevice &&
|
||||||
mAudioSource->GetMediaSource() == dom::MediaSourceEnum::AudioCapture) {
|
mAudioDevice->GetMediaSource() == dom::MediaSourceEnum::AudioCapture) {
|
||||||
domStream = DOMLocalMediaStream::CreateAudioCaptureStream(window, msg);
|
domStream = DOMLocalMediaStream::CreateAudioCaptureStream(window, msg);
|
||||||
// It should be possible to pipe the capture stream to anything. CORS is
|
// It should be possible to pipe the capture stream to anything. CORS is
|
||||||
// not a problem here, we got explicit user content.
|
// not a problem here, we got explicit user content.
|
||||||
@ -814,7 +1018,7 @@ public:
|
|||||||
// avoid us blocking
|
// avoid us blocking
|
||||||
nsRefPtr<nsDOMUserMediaStream> trackunion =
|
nsRefPtr<nsDOMUserMediaStream> trackunion =
|
||||||
nsDOMUserMediaStream::CreateTrackUnionStream(window, mListener,
|
nsDOMUserMediaStream::CreateTrackUnionStream(window, mListener,
|
||||||
mAudioSource, mVideoSource,
|
mAudioDevice, mVideoDevice,
|
||||||
msg);
|
msg);
|
||||||
trackunion->GetStream()->AsProcessedStream()->SetAutofinish(true);
|
trackunion->GetStream()->AsProcessedStream()->SetAutofinish(true);
|
||||||
nsRefPtr<MediaInputPort> port = trackunion->GetStream()->AsProcessedStream()->
|
nsRefPtr<MediaInputPort> port = trackunion->GetStream()->AsProcessedStream()->
|
||||||
@ -859,7 +1063,7 @@ public:
|
|||||||
// Activate our listener. We'll call Start() on the source when get a callback
|
// Activate our listener. We'll call Start() on the source when get a callback
|
||||||
// that the MediaStream has started consuming. The listener is freed
|
// that the MediaStream has started consuming. The listener is freed
|
||||||
// when the page is invalidated (on navigation or close).
|
// when the page is invalidated (on navigation or close).
|
||||||
mListener->Activate(stream.forget(), mAudioSource, mVideoSource);
|
mListener->Activate(stream.forget(), mAudioDevice, mVideoDevice);
|
||||||
|
|
||||||
// Note: includes JS callbacks; must be released on MainThread
|
// Note: includes JS callbacks; must be released on MainThread
|
||||||
TracksAvailableCallback* tracksAvailableCallback =
|
TracksAvailableCallback* tracksAvailableCallback =
|
||||||
@ -874,11 +1078,11 @@ public:
|
|||||||
// because that can take a while.
|
// because that can take a while.
|
||||||
// Pass ownership of trackunion to the MediaOperationTask
|
// Pass ownership of trackunion to the MediaOperationTask
|
||||||
// to ensure it's kept alive until the MediaOperationTask runs (at least).
|
// to ensure it's kept alive until the MediaOperationTask runs (at least).
|
||||||
MediaManager::PostTask(
|
MediaManager::PostTask(FROM_HERE,
|
||||||
FROM_HERE, new MediaOperationTask(MEDIA_START, mListener, domStream,
|
new MediaOperationTask(MEDIA_START, mListener, domStream,
|
||||||
tracksAvailableCallback, mAudioSource,
|
tracksAvailableCallback,
|
||||||
mVideoSource, false, mWindowID,
|
mAudioDevice, mVideoDevice,
|
||||||
mOnFailure.forget()));
|
false, mWindowID, mOnFailure.forget()));
|
||||||
// We won't need mOnFailure now.
|
// We won't need mOnFailure now.
|
||||||
mOnFailure = nullptr;
|
mOnFailure = nullptr;
|
||||||
|
|
||||||
@ -893,8 +1097,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mOnSuccess;
|
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mOnSuccess;
|
||||||
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
|
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
|
||||||
nsRefPtr<MediaEngineSource> mAudioSource;
|
nsRefPtr<AudioDevice> mAudioDevice;
|
||||||
nsRefPtr<MediaEngineSource> mVideoSource;
|
nsRefPtr<VideoDevice> mVideoDevice;
|
||||||
uint64_t mWindowID;
|
uint64_t mWindowID;
|
||||||
nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
|
nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
|
||||||
nsCString mOrigin;
|
nsCString mOrigin;
|
||||||
@ -949,74 +1153,14 @@ GetSources(MediaEngine *engine, dom::MediaSourceEnum aSrcType,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply constrains to a supplied list of sources (removes items from the list)
|
static const char*
|
||||||
|
SelectSettings(MediaStreamConstraints &aConstraints,
|
||||||
template<class DeviceType>
|
nsTArray<nsRefPtr<MediaDevice>>& aSources)
|
||||||
static void
|
|
||||||
ApplyConstraints(const MediaTrackConstraints &aConstraints,
|
|
||||||
nsTArray<nsRefPtr<DeviceType>>& aSources)
|
|
||||||
{
|
|
||||||
auto& c = aConstraints;
|
|
||||||
|
|
||||||
// First apply top-level constraints.
|
|
||||||
|
|
||||||
// Stack constraintSets that pass, starting with the required one, because the
|
|
||||||
// whole stack must be re-satisfied each time a capability-set is ruled out
|
|
||||||
// (this avoids storing state or pushing algorithm into the lower-level code).
|
|
||||||
nsTArray<const MediaTrackConstraintSet*> aggregateConstraints;
|
|
||||||
aggregateConstraints.AppendElement(&c);
|
|
||||||
|
|
||||||
std::multimap<uint32_t, nsRefPtr<DeviceType>> ordered;
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < aSources.Length();) {
|
|
||||||
uint32_t distance = aSources[i]->GetBestFitnessDistance(aggregateConstraints);
|
|
||||||
if (distance == UINT32_MAX) {
|
|
||||||
aSources.RemoveElementAt(i);
|
|
||||||
} else {
|
|
||||||
ordered.insert(std::pair<uint32_t, nsRefPtr<DeviceType>>(distance,
|
|
||||||
aSources[i]));
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Order devices by shortest distance
|
|
||||||
for (auto& ordinal : ordered) {
|
|
||||||
aSources.RemoveElement(ordinal.second);
|
|
||||||
aSources.AppendElement(ordinal.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then apply advanced constraints.
|
|
||||||
|
|
||||||
if (c.mAdvanced.WasPassed()) {
|
|
||||||
auto &array = c.mAdvanced.Value();
|
|
||||||
|
|
||||||
for (int i = 0; i < int(array.Length()); i++) {
|
|
||||||
aggregateConstraints.AppendElement(&array[i]);
|
|
||||||
nsTArray<nsRefPtr<DeviceType>> rejects;
|
|
||||||
for (uint32_t j = 0; j < aSources.Length();) {
|
|
||||||
if (aSources[j]->GetBestFitnessDistance(aggregateConstraints) == UINT32_MAX) {
|
|
||||||
rejects.AppendElement(aSources[j]);
|
|
||||||
aSources.RemoveElementAt(j);
|
|
||||||
} else {
|
|
||||||
++j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!aSources.Length()) {
|
|
||||||
aSources.AppendElements(Move(rejects));
|
|
||||||
aggregateConstraints.RemoveElementAt(aggregateConstraints.Length() - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
ApplyConstraints(MediaStreamConstraints &aConstraints,
|
|
||||||
nsTArray<nsRefPtr<MediaDevice>>& aSources)
|
|
||||||
{
|
{
|
||||||
// Since the advanced part of the constraints algorithm needs to know when
|
// Since the advanced part of the constraints algorithm needs to know when
|
||||||
// a candidate set is overconstrained (zero members), we must split up the
|
// a candidate set is overconstrained (zero members), we must split up the
|
||||||
// list into videos and audios, and put it back together again at the end.
|
// list into videos and audios, and put it back together again at the end.
|
||||||
|
|
||||||
bool overconstrained = false;
|
|
||||||
nsTArray<nsRefPtr<VideoDevice>> videos;
|
nsTArray<nsRefPtr<VideoDevice>> videos;
|
||||||
nsTArray<nsRefPtr<AudioDevice>> audios;
|
nsTArray<nsRefPtr<AudioDevice>> audios;
|
||||||
|
|
||||||
@ -1032,25 +1176,23 @@ ApplyConstraints(MediaStreamConstraints &aConstraints,
|
|||||||
aSources.Clear();
|
aSources.Clear();
|
||||||
MOZ_ASSERT(!aSources.Length());
|
MOZ_ASSERT(!aSources.Length());
|
||||||
|
|
||||||
|
const char* badConstraint = nullptr;
|
||||||
|
|
||||||
if (IsOn(aConstraints.mVideo)) {
|
if (IsOn(aConstraints.mVideo)) {
|
||||||
ApplyConstraints(GetInvariant(aConstraints.mVideo), videos);
|
badConstraint = MediaConstraintsHelper::SelectSettings(
|
||||||
if (!videos.Length()) {
|
GetInvariant(aConstraints.mVideo), videos);
|
||||||
overconstrained = true;
|
|
||||||
}
|
|
||||||
for (auto& video : videos) {
|
for (auto& video : videos) {
|
||||||
aSources.AppendElement(video);
|
aSources.AppendElement(video);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (IsOn(aConstraints.mAudio)) {
|
if (audios.Length() && IsOn(aConstraints.mAudio)) {
|
||||||
ApplyConstraints(GetInvariant(aConstraints.mAudio), audios);
|
badConstraint = MediaConstraintsHelper::SelectSettings(
|
||||||
if (!audios.Length()) {
|
GetInvariant(aConstraints.mAudio), audios);
|
||||||
overconstrained = true;
|
|
||||||
}
|
|
||||||
for (auto& audio : audios) {
|
for (auto& audio : audios) {
|
||||||
aSources.AppendElement(audio);
|
aSources.AppendElement(audio);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return !overconstrained;
|
return badConstraint;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1143,13 +1285,10 @@ public:
|
|||||||
peerIdentity = new PeerIdentity(mConstraints.mPeerIdentity);
|
peerIdentity = new PeerIdentity(mConstraints.mPeerIdentity);
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_DispatchToMainThread(do_AddRef(new GetUserMediaStreamRunnable(
|
NS_DispatchToMainThread(do_AddRef(
|
||||||
mOnSuccess, mOnFailure, mWindowID, mListener, mOrigin,
|
new GetUserMediaStreamRunnable(mOnSuccess, mOnFailure, mWindowID,
|
||||||
(mAudioDevice? mAudioDevice->GetSource() : nullptr),
|
mListener, mOrigin, mAudioDevice,
|
||||||
(mVideoDevice? mVideoDevice->GetSource() : nullptr),
|
mVideoDevice, peerIdentity)));
|
||||||
peerIdentity
|
|
||||||
)));
|
|
||||||
|
|
||||||
MOZ_ASSERT(!mOnSuccess);
|
MOZ_ASSERT(!mOnSuccess);
|
||||||
MOZ_ASSERT(!mOnFailure);
|
MOZ_ASSERT(!mOnFailure);
|
||||||
}
|
}
|
||||||
@ -1697,7 +1836,7 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow,
|
|||||||
auto& vc = c.mVideo.GetAsMediaTrackConstraints();
|
auto& vc = c.mVideo.GetAsMediaTrackConstraints();
|
||||||
videoType = StringToEnum(dom::MediaSourceEnumValues::strings,
|
videoType = StringToEnum(dom::MediaSourceEnumValues::strings,
|
||||||
vc.mMediaSource,
|
vc.mMediaSource,
|
||||||
videoType);
|
dom::MediaSourceEnum::Other);
|
||||||
switch (videoType) {
|
switch (videoType) {
|
||||||
case dom::MediaSourceEnum::Camera:
|
case dom::MediaSourceEnum::Camera:
|
||||||
break;
|
break;
|
||||||
@ -1740,7 +1879,10 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow,
|
|||||||
case dom::MediaSourceEnum::Other:
|
case dom::MediaSourceEnum::Other:
|
||||||
default: {
|
default: {
|
||||||
nsRefPtr<MediaStreamError> error =
|
nsRefPtr<MediaStreamError> error =
|
||||||
new MediaStreamError(aWindow, NS_LITERAL_STRING("NotFoundError"));
|
new MediaStreamError(aWindow,
|
||||||
|
NS_LITERAL_STRING("OverconstrainedError"),
|
||||||
|
NS_LITERAL_STRING(""),
|
||||||
|
NS_LITERAL_STRING("mediaSource"));
|
||||||
onFailure->OnError(error);
|
onFailure->OnError(error);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
@ -1785,7 +1927,7 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow,
|
|||||||
auto& ac = c.mAudio.GetAsMediaTrackConstraints();
|
auto& ac = c.mAudio.GetAsMediaTrackConstraints();
|
||||||
audioType = StringToEnum(dom::MediaSourceEnumValues::strings,
|
audioType = StringToEnum(dom::MediaSourceEnumValues::strings,
|
||||||
ac.mMediaSource,
|
ac.mMediaSource,
|
||||||
audioType);
|
dom::MediaSourceEnum::Other);
|
||||||
// Work around WebIDL default since spec uses same dictionary w/audio & video.
|
// Work around WebIDL default since spec uses same dictionary w/audio & video.
|
||||||
if (audioType == dom::MediaSourceEnum::Camera) {
|
if (audioType == dom::MediaSourceEnum::Camera) {
|
||||||
audioType = dom::MediaSourceEnum::Microphone;
|
audioType = dom::MediaSourceEnum::Microphone;
|
||||||
@ -1812,7 +1954,10 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow,
|
|||||||
case dom::MediaSourceEnum::Other:
|
case dom::MediaSourceEnum::Other:
|
||||||
default: {
|
default: {
|
||||||
nsRefPtr<MediaStreamError> error =
|
nsRefPtr<MediaStreamError> error =
|
||||||
new MediaStreamError(aWindow, NS_LITERAL_STRING("NotFoundError"));
|
new MediaStreamError(aWindow,
|
||||||
|
NS_LITERAL_STRING("OverconstrainedError"),
|
||||||
|
NS_LITERAL_STRING(""),
|
||||||
|
NS_LITERAL_STRING("mediaSource"));
|
||||||
onFailure->OnError(error);
|
onFailure->OnError(error);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
@ -1905,8 +2050,19 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply any constraints. This modifies the list.
|
// Apply any constraints. This modifies the list.
|
||||||
|
const char* badConstraint = SelectSettings(c, *devices);
|
||||||
if (!ApplyConstraints(c, *devices)) {
|
if (badConstraint) {
|
||||||
|
nsString constraint;
|
||||||
|
constraint.AssignASCII(badConstraint);
|
||||||
|
nsRefPtr<MediaStreamError> error =
|
||||||
|
new MediaStreamError(window,
|
||||||
|
NS_LITERAL_STRING("OverconstrainedError"),
|
||||||
|
NS_LITERAL_STRING(""),
|
||||||
|
constraint);
|
||||||
|
onFailure->OnError(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!devices->Length()) {
|
||||||
nsRefPtr<MediaStreamError> error =
|
nsRefPtr<MediaStreamError> error =
|
||||||
new MediaStreamError(window, NS_LITERAL_STRING("NotFoundError"));
|
new MediaStreamError(window, NS_LITERAL_STRING("NotFoundError"));
|
||||||
onFailure->OnError(error);
|
onFailure->OnError(error);
|
||||||
@ -1957,8 +2113,8 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow,
|
|||||||
#ifdef MOZ_WEBRTC
|
#ifdef MOZ_WEBRTC
|
||||||
EnableWebRtcLog();
|
EnableWebRtcLog();
|
||||||
#endif
|
#endif
|
||||||
}, [onFailure](MediaStreamError& reason) mutable {
|
}, [onFailure](MediaStreamError*& reason) mutable {
|
||||||
onFailure->OnError(&reason);
|
onFailure->OnError(reason);
|
||||||
});
|
});
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
@ -2138,10 +2294,10 @@ MediaManager::EnumerateDevices(nsPIDOMWindow* aWindow,
|
|||||||
mgr->RemoveFromWindowList(windowId, listener);
|
mgr->RemoveFromWindowList(windowId, listener);
|
||||||
nsCOMPtr<nsIWritableVariant> array = MediaManager_ToJSArray(*devices);
|
nsCOMPtr<nsIWritableVariant> array = MediaManager_ToJSArray(*devices);
|
||||||
onSuccess->OnSuccess(array);
|
onSuccess->OnSuccess(array);
|
||||||
}, [onFailure, windowId, listener](MediaStreamError& reason) mutable {
|
}, [onFailure, windowId, listener](MediaStreamError*& reason) mutable {
|
||||||
nsRefPtr<MediaManager> mgr = MediaManager_GetInstance();
|
nsRefPtr<MediaManager> mgr = MediaManager_GetInstance();
|
||||||
mgr->RemoveFromWindowList(windowId, listener);
|
mgr->RemoveFromWindowList(windowId, listener);
|
||||||
onFailure->OnError(&reason);
|
onFailure->OnError(reason);
|
||||||
});
|
});
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
@ -2853,10 +3009,10 @@ GetUserMediaCallbackMediaStreamListener::AudioConfig(bool aEchoOn,
|
|||||||
bool aNoiseOn, uint32_t aNoise,
|
bool aNoiseOn, uint32_t aNoise,
|
||||||
int32_t aPlayoutDelay)
|
int32_t aPlayoutDelay)
|
||||||
{
|
{
|
||||||
if (mAudioSource) {
|
if (mAudioDevice) {
|
||||||
#ifdef MOZ_WEBRTC
|
#ifdef MOZ_WEBRTC
|
||||||
MediaManager::PostTask(FROM_HERE,
|
MediaManager::PostTask(FROM_HERE,
|
||||||
NewRunnableMethod(mAudioSource.get(), &MediaEngineSource::Config,
|
NewRunnableMethod(mAudioDevice->GetSource(), &MediaEngineSource::Config,
|
||||||
aEchoOn, aEcho, aAgcOn, aAGC, aNoiseOn,
|
aEchoOn, aEcho, aAgcOn, aAGC, aNoiseOn,
|
||||||
aNoise, aPlayoutDelay));
|
aNoise, aPlayoutDelay));
|
||||||
#endif
|
#endif
|
||||||
@ -2874,7 +3030,7 @@ GetUserMediaCallbackMediaStreamListener::Invalidate()
|
|||||||
MediaManager::PostTask(FROM_HERE,
|
MediaManager::PostTask(FROM_HERE,
|
||||||
new MediaOperationTask(MEDIA_STOP,
|
new MediaOperationTask(MEDIA_STOP,
|
||||||
this, nullptr, nullptr,
|
this, nullptr, nullptr,
|
||||||
mAudioSource, mVideoSource,
|
mAudioDevice, mVideoDevice,
|
||||||
mFinished, mWindowID, nullptr));
|
mFinished, mWindowID, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2884,18 +3040,18 @@ void
|
|||||||
GetUserMediaCallbackMediaStreamListener::StopSharing()
|
GetUserMediaCallbackMediaStreamListener::StopSharing()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||||
if (mVideoSource && !mStopped &&
|
if (mVideoDevice && !mStopped &&
|
||||||
(mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Screen ||
|
(mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Screen ||
|
||||||
mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Application ||
|
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Application ||
|
||||||
mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Window)) {
|
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Window)) {
|
||||||
// Stop the whole stream if there's no audio; just the video track if we have both
|
// Stop the whole stream if there's no audio; just the video track if we have both
|
||||||
MediaManager::PostTask(FROM_HERE,
|
MediaManager::PostTask(FROM_HERE,
|
||||||
new MediaOperationTask(mAudioSource ? MEDIA_STOP_TRACK : MEDIA_STOP,
|
new MediaOperationTask(mAudioDevice ? MEDIA_STOP_TRACK : MEDIA_STOP,
|
||||||
this, nullptr, nullptr,
|
this, nullptr, nullptr,
|
||||||
nullptr, mVideoSource,
|
nullptr, mVideoDevice,
|
||||||
mFinished, mWindowID, nullptr));
|
mFinished, mWindowID, nullptr));
|
||||||
} else if (mAudioSource &&
|
} else if (mAudioDevice &&
|
||||||
mAudioSource->GetMediaSource() == dom::MediaSourceEnum::AudioCapture) {
|
mAudioDevice->GetMediaSource() == dom::MediaSourceEnum::AudioCapture) {
|
||||||
nsCOMPtr<nsPIDOMWindow> window = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
|
nsCOMPtr<nsPIDOMWindow> window = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
|
||||||
MOZ_ASSERT(window);
|
MOZ_ASSERT(window);
|
||||||
window->SetAudioCapture(false);
|
window->SetAudioCapture(false);
|
||||||
@ -2907,25 +3063,119 @@ GetUserMediaCallbackMediaStreamListener::StopSharing()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApplyConstraints for track
|
||||||
|
|
||||||
|
already_AddRefed<GetUserMediaCallbackMediaStreamListener::PledgeVoid>
|
||||||
|
GetUserMediaCallbackMediaStreamListener::ApplyConstraintsToTrack(
|
||||||
|
nsPIDOMWindow* aWindow,
|
||||||
|
TrackID aTrackID,
|
||||||
|
bool aIsAudio,
|
||||||
|
const MediaTrackConstraints& aConstraints)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
nsRefPtr<PledgeVoid> p = new PledgeVoid();
|
||||||
|
|
||||||
|
if (!(((aIsAudio && mAudioDevice) ||
|
||||||
|
(!aIsAudio && mVideoDevice)) && !mStopped))
|
||||||
|
{
|
||||||
|
LOG(("gUM track %d applyConstraints, but we don't have type %s",
|
||||||
|
aTrackID, aIsAudio ? "audio" : "video"));
|
||||||
|
p->Resolve(false);
|
||||||
|
return p.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX to support multiple tracks of a type in a stream, this should key off
|
||||||
|
// the TrackID and not just the type
|
||||||
|
nsRefPtr<AudioDevice> audioDevice = aIsAudio ? mAudioDevice.get() : nullptr;
|
||||||
|
nsRefPtr<VideoDevice> videoDevice = !aIsAudio ? mVideoDevice.get() : nullptr;
|
||||||
|
|
||||||
|
nsRefPtr<MediaManager> mgr = MediaManager::GetInstance();
|
||||||
|
uint32_t id = mgr->mOutstandingVoidPledges.Append(*p);
|
||||||
|
uint64_t windowId = aWindow->WindowID();
|
||||||
|
|
||||||
|
MediaManager::PostTask(FROM_HERE, NewTaskFrom([id, windowId,
|
||||||
|
audioDevice, videoDevice,
|
||||||
|
aConstraints]() mutable {
|
||||||
|
MOZ_ASSERT(MediaManager::IsInMediaThread());
|
||||||
|
nsRefPtr<MediaManager> mgr = MediaManager::GetInstance();
|
||||||
|
const char* badConstraint = nullptr;
|
||||||
|
nsresult rv = NS_OK;
|
||||||
|
|
||||||
|
if (audioDevice) {
|
||||||
|
rv = audioDevice->Restart(aConstraints, mgr->mPrefs);
|
||||||
|
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||||
|
nsTArray<nsRefPtr<AudioDevice>> audios;
|
||||||
|
audios.AppendElement(audioDevice);
|
||||||
|
badConstraint = MediaConstraintsHelper::SelectSettings(aConstraints,
|
||||||
|
audios);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rv = videoDevice->Restart(aConstraints, mgr->mPrefs);
|
||||||
|
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||||
|
nsTArray<nsRefPtr<VideoDevice>> videos;
|
||||||
|
videos.AppendElement(videoDevice);
|
||||||
|
badConstraint = MediaConstraintsHelper::SelectSettings(aConstraints,
|
||||||
|
videos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NS_DispatchToMainThread(do_AddRef(NewRunnableFrom([id, windowId, rv,
|
||||||
|
badConstraint]() mutable {
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
nsRefPtr<MediaManager> mgr = MediaManager_GetInstance();
|
||||||
|
if (!mgr) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
nsRefPtr<PledgeVoid> p = mgr->mOutstandingVoidPledges.Remove(id);
|
||||||
|
if (p) {
|
||||||
|
if (NS_SUCCEEDED(rv)) {
|
||||||
|
p->Resolve(false);
|
||||||
|
} else {
|
||||||
|
nsPIDOMWindow *window = static_cast<nsPIDOMWindow*>
|
||||||
|
(nsGlobalWindow::GetInnerWindowWithId(windowId));
|
||||||
|
if (window) {
|
||||||
|
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||||
|
nsString constraint;
|
||||||
|
constraint.AssignASCII(badConstraint);
|
||||||
|
nsRefPtr<MediaStreamError> error =
|
||||||
|
new MediaStreamError(window,
|
||||||
|
NS_LITERAL_STRING("OverconstrainedError"),
|
||||||
|
NS_LITERAL_STRING(""),
|
||||||
|
constraint);
|
||||||
|
p->Reject(error);
|
||||||
|
} else {
|
||||||
|
nsRefPtr<MediaStreamError> error =
|
||||||
|
new MediaStreamError(window,
|
||||||
|
NS_LITERAL_STRING("InternalError"));
|
||||||
|
p->Reject(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NS_OK;
|
||||||
|
})));
|
||||||
|
}));
|
||||||
|
return p.forget();
|
||||||
|
}
|
||||||
|
|
||||||
// Stop backend for track
|
// Stop backend for track
|
||||||
|
|
||||||
void
|
void
|
||||||
GetUserMediaCallbackMediaStreamListener::StopTrack(TrackID aID, bool aIsAudio)
|
GetUserMediaCallbackMediaStreamListener::StopTrack(TrackID aTrackID, bool aIsAudio)
|
||||||
{
|
{
|
||||||
if (((aIsAudio && mAudioSource) ||
|
if (((aIsAudio && mAudioDevice) ||
|
||||||
(!aIsAudio && mVideoSource)) && !mStopped)
|
(!aIsAudio && mVideoDevice)) && !mStopped)
|
||||||
{
|
{
|
||||||
// XXX to support multiple tracks of a type in a stream, this should key off
|
// XXX to support multiple tracks of a type in a stream, this should key off
|
||||||
// the TrackID and not just the type
|
// the TrackID and not just the type
|
||||||
MediaManager::PostTask(FROM_HERE,
|
MediaManager::PostTask(FROM_HERE,
|
||||||
new MediaOperationTask(MEDIA_STOP_TRACK,
|
new MediaOperationTask(MEDIA_STOP_TRACK,
|
||||||
this, nullptr, nullptr,
|
this, nullptr, nullptr,
|
||||||
aIsAudio ? mAudioSource.get() : nullptr,
|
aIsAudio ? mAudioDevice.get() : nullptr,
|
||||||
!aIsAudio ? mVideoSource.get() : nullptr,
|
!aIsAudio ? mVideoDevice.get() : nullptr,
|
||||||
mFinished, mWindowID, nullptr));
|
mFinished, mWindowID, nullptr));
|
||||||
} else {
|
} else {
|
||||||
LOG(("gUM track %d ended, but we don't have type %s",
|
LOG(("gUM track %d ended, but we don't have type %s",
|
||||||
aID, aIsAudio ? "audio" : "video"));
|
aTrackID, aIsAudio ? "audio" : "video"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2946,7 +3196,7 @@ GetUserMediaCallbackMediaStreamListener::NotifyDirectListeners(MediaStreamGraph*
|
|||||||
MediaManager::PostTask(FROM_HERE,
|
MediaManager::PostTask(FROM_HERE,
|
||||||
new MediaOperationTask(MEDIA_DIRECT_LISTENERS,
|
new MediaOperationTask(MEDIA_DIRECT_LISTENERS,
|
||||||
this, nullptr, nullptr,
|
this, nullptr, nullptr,
|
||||||
mAudioSource, mVideoSource,
|
mAudioDevice, mVideoDevice,
|
||||||
aHasListeners, mWindowID, nullptr));
|
aHasListeners, mWindowID, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,12 +47,72 @@
|
|||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace dom {
|
namespace dom {
|
||||||
struct MediaStreamConstraints;
|
struct MediaStreamConstraints;
|
||||||
|
struct MediaTrackConstraints;
|
||||||
struct MediaTrackConstraintSet;
|
struct MediaTrackConstraintSet;
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
|
|
||||||
extern PRLogModuleInfo* GetMediaManagerLog();
|
extern PRLogModuleInfo* GetMediaManagerLog();
|
||||||
#define MM_LOG(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Debug, msg)
|
#define MM_LOG(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Debug, msg)
|
||||||
|
|
||||||
|
class MediaDevice : public nsIMediaDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NS_DECL_THREADSAFE_ISUPPORTS
|
||||||
|
NS_DECL_NSIMEDIADEVICE
|
||||||
|
|
||||||
|
void SetId(const nsAString& aID);
|
||||||
|
virtual uint32_t GetBestFitnessDistance(
|
||||||
|
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets);
|
||||||
|
protected:
|
||||||
|
virtual ~MediaDevice() {}
|
||||||
|
explicit MediaDevice(MediaEngineSource* aSource, bool aIsVideo);
|
||||||
|
static uint32_t FitnessDistance(nsString aN,
|
||||||
|
const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters& aConstraint);
|
||||||
|
private:
|
||||||
|
static bool StringsContain(const dom::OwningStringOrStringSequence& aStrings,
|
||||||
|
nsString aN);
|
||||||
|
static uint32_t FitnessDistance(nsString aN,
|
||||||
|
const dom::ConstrainDOMStringParameters& aParams);
|
||||||
|
protected:
|
||||||
|
nsString mName;
|
||||||
|
nsString mID;
|
||||||
|
dom::MediaSourceEnum mMediaSource;
|
||||||
|
nsRefPtr<MediaEngineSource> mSource;
|
||||||
|
public:
|
||||||
|
dom::MediaSourceEnum GetMediaSource() {
|
||||||
|
return mMediaSource;
|
||||||
|
}
|
||||||
|
bool mIsVideo;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VideoDevice : public MediaDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef MediaEngineVideoSource Source;
|
||||||
|
|
||||||
|
explicit VideoDevice(Source* aSource);
|
||||||
|
NS_IMETHOD GetType(nsAString& aType);
|
||||||
|
Source* GetSource();
|
||||||
|
nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
|
||||||
|
const MediaEnginePrefs &aPrefs);
|
||||||
|
nsresult Restart(const dom::MediaTrackConstraints &aConstraints,
|
||||||
|
const MediaEnginePrefs &aPrefs);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AudioDevice : public MediaDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef MediaEngineAudioSource Source;
|
||||||
|
|
||||||
|
explicit AudioDevice(Source* aSource);
|
||||||
|
NS_IMETHOD GetType(nsAString& aType);
|
||||||
|
Source* GetSource();
|
||||||
|
nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
|
||||||
|
const MediaEnginePrefs &aPrefs);
|
||||||
|
nsresult Restart(const dom::MediaTrackConstraints &aConstraints,
|
||||||
|
const MediaEnginePrefs &aPrefs);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is an implementation of MediaStreamListener. This is used
|
* This class is an implementation of MediaStreamListener. This is used
|
||||||
* to Start() and Stop() the underlying MediaEngineSource when MediaStreams
|
* to Start() and Stop() the underlying MediaEngineSource when MediaStreams
|
||||||
@ -79,13 +139,13 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Activate(already_AddRefed<SourceMediaStream> aStream,
|
void Activate(already_AddRefed<SourceMediaStream> aStream,
|
||||||
MediaEngineSource* aAudioSource,
|
AudioDevice* aAudioDevice,
|
||||||
MediaEngineSource* aVideoSource)
|
VideoDevice* aVideoDevice)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||||
mStream = aStream;
|
mStream = aStream;
|
||||||
mAudioSource = aAudioSource;
|
mAudioDevice = aAudioDevice;
|
||||||
mVideoSource = aVideoSource;
|
mVideoDevice = aVideoDevice;
|
||||||
|
|
||||||
mStream->AddListener(this);
|
mStream->AddListener(this);
|
||||||
}
|
}
|
||||||
@ -107,46 +167,57 @@ public:
|
|||||||
|
|
||||||
void StopTrack(TrackID aID, bool aIsAudio);
|
void StopTrack(TrackID aID, bool aIsAudio);
|
||||||
|
|
||||||
// mVideo/AudioSource are set by Activate(), so we assume they're capturing
|
typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
|
||||||
|
|
||||||
|
already_AddRefed<PledgeVoid>
|
||||||
|
ApplyConstraintsToTrack(nsPIDOMWindow* aWindow,
|
||||||
|
TrackID aID, bool aIsAudio,
|
||||||
|
const dom::MediaTrackConstraints& aConstraints);
|
||||||
|
|
||||||
|
// mVideo/AudioDevice are set by Activate(), so we assume they're capturing
|
||||||
// if set and represent a real capture device.
|
// if set and represent a real capture device.
|
||||||
bool CapturingVideo()
|
bool CapturingVideo()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||||
return mVideoSource && !mStopped &&
|
return mVideoDevice && !mStopped &&
|
||||||
mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Camera &&
|
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Camera &&
|
||||||
(!mVideoSource->IsFake() ||
|
(!mVideoDevice->GetSource()->IsFake() ||
|
||||||
Preferences::GetBool("media.navigator.permission.fake"));
|
Preferences::GetBool("media.navigator.permission.fake"));
|
||||||
}
|
}
|
||||||
bool CapturingAudio()
|
bool CapturingAudio()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||||
return mAudioSource && !mStopped &&
|
return mAudioDevice && !mStopped &&
|
||||||
(!mAudioSource->IsFake() ||
|
(!mAudioDevice->GetSource()->IsFake() ||
|
||||||
Preferences::GetBool("media.navigator.permission.fake"));
|
Preferences::GetBool("media.navigator.permission.fake"));
|
||||||
}
|
}
|
||||||
bool CapturingScreen()
|
bool CapturingScreen()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||||
return mVideoSource && !mStopped && !mVideoSource->IsAvailable() &&
|
return mVideoDevice && !mStopped &&
|
||||||
mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Screen;
|
!mVideoDevice->GetSource()->IsAvailable() &&
|
||||||
|
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Screen;
|
||||||
}
|
}
|
||||||
bool CapturingWindow()
|
bool CapturingWindow()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||||
return mVideoSource && !mStopped && !mVideoSource->IsAvailable() &&
|
return mVideoDevice && !mStopped &&
|
||||||
mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Window;
|
!mVideoDevice->GetSource()->IsAvailable() &&
|
||||||
|
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Window;
|
||||||
}
|
}
|
||||||
bool CapturingApplication()
|
bool CapturingApplication()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||||
return mVideoSource && !mStopped && !mVideoSource->IsAvailable() &&
|
return mVideoDevice && !mStopped &&
|
||||||
mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Application;
|
!mVideoDevice->GetSource()->IsAvailable() &&
|
||||||
|
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Application;
|
||||||
}
|
}
|
||||||
bool CapturingBrowser()
|
bool CapturingBrowser()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||||
return mVideoSource && !mStopped && mVideoSource->IsAvailable() &&
|
return mVideoDevice && !mStopped &&
|
||||||
mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Browser;
|
mVideoDevice->GetSource()->IsAvailable() &&
|
||||||
|
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Browser;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetStopped()
|
void SetStopped()
|
||||||
@ -187,11 +258,13 @@ public:
|
|||||||
{
|
{
|
||||||
// Currently audio sources ignore NotifyPull, but they could
|
// Currently audio sources ignore NotifyPull, but they could
|
||||||
// watch it especially for fake audio.
|
// watch it especially for fake audio.
|
||||||
if (mAudioSource) {
|
if (mAudioDevice) {
|
||||||
mAudioSource->NotifyPull(aGraph, mStream, kAudioTrack, aDesiredTime);
|
mAudioDevice->GetSource()->NotifyPull(aGraph, mStream, kAudioTrack,
|
||||||
|
aDesiredTime);
|
||||||
}
|
}
|
||||||
if (mVideoSource) {
|
if (mVideoDevice) {
|
||||||
mVideoSource->NotifyPull(aGraph, mStream, kVideoTrack, aDesiredTime);
|
mVideoDevice->GetSource()->NotifyPull(aGraph, mStream, kVideoTrack,
|
||||||
|
aDesiredTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,8 +310,8 @@ private:
|
|||||||
|
|
||||||
// Accessed from MediaStreamGraph thread, MediaManager thread, and MainThread
|
// Accessed from MediaStreamGraph thread, MediaManager thread, and MainThread
|
||||||
// No locking needed as they're only addrefed except on the MediaManager thread
|
// No locking needed as they're only addrefed except on the MediaManager thread
|
||||||
nsRefPtr<MediaEngineSource> mAudioSource; // threadsafe refcnt
|
nsRefPtr<AudioDevice> mAudioDevice; // threadsafe refcnt
|
||||||
nsRefPtr<MediaEngineSource> mVideoSource; // threadsafe refcnt
|
nsRefPtr<VideoDevice> mVideoDevice; // threadsafe refcnt
|
||||||
nsRefPtr<SourceMediaStream> mStream; // threadsafe refcnt
|
nsRefPtr<SourceMediaStream> mStream; // threadsafe refcnt
|
||||||
bool mFinished;
|
bool mFinished;
|
||||||
|
|
||||||
@ -253,7 +326,7 @@ class GetUserMediaNotificationEvent: public nsRunnable
|
|||||||
enum GetUserMediaStatus {
|
enum GetUserMediaStatus {
|
||||||
STARTING,
|
STARTING,
|
||||||
STOPPING,
|
STOPPING,
|
||||||
STOPPED_TRACK
|
STOPPED_TRACK,
|
||||||
};
|
};
|
||||||
GetUserMediaNotificationEvent(GetUserMediaCallbackMediaStreamListener* aListener,
|
GetUserMediaNotificationEvent(GetUserMediaCallbackMediaStreamListener* aListener,
|
||||||
GetUserMediaStatus aStatus,
|
GetUserMediaStatus aStatus,
|
||||||
@ -291,7 +364,7 @@ typedef enum {
|
|||||||
MEDIA_START,
|
MEDIA_START,
|
||||||
MEDIA_STOP,
|
MEDIA_STOP,
|
||||||
MEDIA_STOP_TRACK,
|
MEDIA_STOP_TRACK,
|
||||||
MEDIA_DIRECT_LISTENERS
|
MEDIA_DIRECT_LISTENERS,
|
||||||
} MediaOperation;
|
} MediaOperation;
|
||||||
|
|
||||||
class MediaManager;
|
class MediaManager;
|
||||||
@ -310,206 +383,9 @@ private:
|
|||||||
nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
|
nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generic class for running long media operations like Start off the main
|
|
||||||
// thread, and then (because nsDOMMediaStreams aren't threadsafe),
|
|
||||||
// ProxyReleases mStream since it's cycle collected.
|
|
||||||
class MediaOperationTask : public Task
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// so we can send Stop without AddRef()ing from the MSG thread
|
|
||||||
MediaOperationTask(MediaOperation aType,
|
|
||||||
GetUserMediaCallbackMediaStreamListener* aListener,
|
|
||||||
DOMMediaStream* aStream,
|
|
||||||
DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback,
|
|
||||||
MediaEngineSource* aAudioSource,
|
|
||||||
MediaEngineSource* aVideoSource,
|
|
||||||
bool aBool,
|
|
||||||
uint64_t aWindowID,
|
|
||||||
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
|
|
||||||
: mType(aType)
|
|
||||||
, mStream(aStream)
|
|
||||||
, mOnTracksAvailableCallback(aOnTracksAvailableCallback)
|
|
||||||
, mAudioSource(aAudioSource)
|
|
||||||
, mVideoSource(aVideoSource)
|
|
||||||
, mListener(aListener)
|
|
||||||
, mBool(aBool)
|
|
||||||
, mWindowID(aWindowID)
|
|
||||||
, mOnFailure(aError)
|
|
||||||
{}
|
|
||||||
|
|
||||||
~MediaOperationTask()
|
|
||||||
{
|
|
||||||
// MediaStreams can be released on any thread.
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ReturnCallbackError(nsresult rv, const char* errorLog);
|
|
||||||
|
|
||||||
void
|
|
||||||
Run()
|
|
||||||
{
|
|
||||||
SourceMediaStream *source = mListener->GetSourceStream();
|
|
||||||
// No locking between these is required as all the callbacks for the
|
|
||||||
// same MediaStream will occur on the same thread.
|
|
||||||
if (!source) // means the stream was never Activated()
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch (mType) {
|
|
||||||
case MEDIA_START:
|
|
||||||
{
|
|
||||||
NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
|
|
||||||
nsresult rv;
|
|
||||||
|
|
||||||
if (mAudioSource) {
|
|
||||||
rv = mAudioSource->Start(source, kAudioTrack);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
ReturnCallbackError(rv, "Starting audio failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mVideoSource) {
|
|
||||||
rv = mVideoSource->Start(source, kVideoTrack);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
ReturnCallbackError(rv, "Starting video failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Start() queued the tracks to be added synchronously to avoid races
|
|
||||||
source->FinishAddTracks();
|
|
||||||
|
|
||||||
source->SetPullEnabled(true);
|
|
||||||
source->AdvanceKnownTracksTime(STREAM_TIME_MAX);
|
|
||||||
|
|
||||||
MM_LOG(("started all sources"));
|
|
||||||
// Forward mOnTracksAvailableCallback to GetUserMediaNotificationEvent,
|
|
||||||
// because mOnTracksAvailableCallback needs to be added to mStream
|
|
||||||
// on the main thread.
|
|
||||||
nsIRunnable *event =
|
|
||||||
new GetUserMediaNotificationEvent(GetUserMediaNotificationEvent::STARTING,
|
|
||||||
mStream.forget(),
|
|
||||||
mOnTracksAvailableCallback.forget(),
|
|
||||||
mAudioSource != nullptr,
|
|
||||||
mVideoSource != nullptr,
|
|
||||||
mWindowID, mOnFailure.forget());
|
|
||||||
// event must always be released on mainthread due to the JS callbacks
|
|
||||||
// in the TracksAvailableCallback
|
|
||||||
NS_DispatchToMainThread(event);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MEDIA_STOP:
|
|
||||||
case MEDIA_STOP_TRACK:
|
|
||||||
{
|
|
||||||
NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
|
|
||||||
if (mAudioSource) {
|
|
||||||
mAudioSource->Stop(source, kAudioTrack);
|
|
||||||
mAudioSource->Deallocate();
|
|
||||||
}
|
|
||||||
if (mVideoSource) {
|
|
||||||
mVideoSource->Stop(source, kVideoTrack);
|
|
||||||
mVideoSource->Deallocate();
|
|
||||||
}
|
|
||||||
// Do this after stopping all tracks with EndTrack()
|
|
||||||
if (mBool) {
|
|
||||||
source->Finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
nsIRunnable *event =
|
|
||||||
new GetUserMediaNotificationEvent(mListener,
|
|
||||||
mType == MEDIA_STOP ?
|
|
||||||
GetUserMediaNotificationEvent::STOPPING :
|
|
||||||
GetUserMediaNotificationEvent::STOPPED_TRACK,
|
|
||||||
mAudioSource != nullptr,
|
|
||||||
mVideoSource != nullptr,
|
|
||||||
mWindowID);
|
|
||||||
// event must always be released on mainthread due to the JS callbacks
|
|
||||||
// in the TracksAvailableCallback
|
|
||||||
NS_DispatchToMainThread(event);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MEDIA_DIRECT_LISTENERS:
|
|
||||||
{
|
|
||||||
NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
|
|
||||||
if (mVideoSource) {
|
|
||||||
mVideoSource->SetDirectListeners(mBool);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
MOZ_ASSERT(false,"invalid MediaManager operation");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
MediaOperation mType;
|
|
||||||
nsRefPtr<DOMMediaStream> mStream;
|
|
||||||
nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
|
|
||||||
nsRefPtr<MediaEngineSource> mAudioSource; // threadsafe
|
|
||||||
nsRefPtr<MediaEngineSource> mVideoSource; // threadsafe
|
|
||||||
nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
|
|
||||||
bool mBool;
|
|
||||||
uint64_t mWindowID;
|
|
||||||
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef nsTArray<nsRefPtr<GetUserMediaCallbackMediaStreamListener> > StreamListeners;
|
typedef nsTArray<nsRefPtr<GetUserMediaCallbackMediaStreamListener> > StreamListeners;
|
||||||
typedef nsClassHashtable<nsUint64HashKey, StreamListeners> WindowTable;
|
typedef nsClassHashtable<nsUint64HashKey, StreamListeners> WindowTable;
|
||||||
|
|
||||||
class MediaDevice : public nsIMediaDevice
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_DECL_THREADSAFE_ISUPPORTS
|
|
||||||
NS_DECL_NSIMEDIADEVICE
|
|
||||||
|
|
||||||
void SetId(const nsAString& aID);
|
|
||||||
virtual uint32_t GetBestFitnessDistance(
|
|
||||||
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets);
|
|
||||||
protected:
|
|
||||||
virtual ~MediaDevice() {}
|
|
||||||
explicit MediaDevice(MediaEngineSource* aSource, bool aIsVideo);
|
|
||||||
static uint32_t FitnessDistance(nsString aN,
|
|
||||||
const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters& aConstraint);
|
|
||||||
private:
|
|
||||||
static bool StringsContain(const dom::OwningStringOrStringSequence& aStrings,
|
|
||||||
nsString aN);
|
|
||||||
static uint32_t FitnessDistance(nsString aN,
|
|
||||||
const dom::ConstrainDOMStringParameters& aParams);
|
|
||||||
protected:
|
|
||||||
nsString mName;
|
|
||||||
nsString mID;
|
|
||||||
dom::MediaSourceEnum mMediaSource;
|
|
||||||
nsRefPtr<MediaEngineSource> mSource;
|
|
||||||
public:
|
|
||||||
bool mIsVideo;
|
|
||||||
};
|
|
||||||
|
|
||||||
class VideoDevice : public MediaDevice
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef MediaEngineVideoSource Source;
|
|
||||||
|
|
||||||
explicit VideoDevice(Source* aSource);
|
|
||||||
NS_IMETHOD GetType(nsAString& aType);
|
|
||||||
Source* GetSource();
|
|
||||||
nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
|
|
||||||
const MediaEnginePrefs &aPrefs);
|
|
||||||
};
|
|
||||||
|
|
||||||
class AudioDevice : public MediaDevice
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef MediaEngineAudioSource Source;
|
|
||||||
|
|
||||||
explicit AudioDevice(Source* aSource);
|
|
||||||
NS_IMETHOD GetType(nsAString& aType);
|
|
||||||
Source* GetSource();
|
|
||||||
nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
|
|
||||||
const MediaEnginePrefs &aPrefs);
|
|
||||||
};
|
|
||||||
|
|
||||||
// we could add MediaManager if needed
|
// we could add MediaManager if needed
|
||||||
typedef void (*WindowListenerCallback)(MediaManager *aThis,
|
typedef void (*WindowListenerCallback)(MediaManager *aThis,
|
||||||
uint64_t aWindowID,
|
uint64_t aWindowID,
|
||||||
@ -519,6 +395,7 @@ typedef void (*WindowListenerCallback)(MediaManager *aThis,
|
|||||||
class MediaManager final : public nsIMediaManagerService,
|
class MediaManager final : public nsIMediaManagerService,
|
||||||
public nsIObserver
|
public nsIObserver
|
||||||
{
|
{
|
||||||
|
friend GetUserMediaCallbackMediaStreamListener;
|
||||||
public:
|
public:
|
||||||
static already_AddRefed<MediaManager> GetInstance();
|
static already_AddRefed<MediaManager> GetInstance();
|
||||||
|
|
||||||
@ -586,7 +463,7 @@ public:
|
|||||||
typedef nsTArray<nsRefPtr<MediaDevice>> SourceSet;
|
typedef nsTArray<nsRefPtr<MediaDevice>> SourceSet;
|
||||||
static bool IsPrivateBrowsing(nsPIDOMWindow *window);
|
static bool IsPrivateBrowsing(nsPIDOMWindow *window);
|
||||||
private:
|
private:
|
||||||
typedef media::Pledge<SourceSet*, dom::MediaStreamError> PledgeSourceSet;
|
typedef media::Pledge<SourceSet*, dom::MediaStreamError*> PledgeSourceSet;
|
||||||
|
|
||||||
static bool IsPrivileged();
|
static bool IsPrivileged();
|
||||||
static bool IsLoop(nsIURI* aDocURI);
|
static bool IsLoop(nsIURI* aDocURI);
|
||||||
@ -646,6 +523,7 @@ private:
|
|||||||
static StaticRefPtr<MediaManager> sSingleton;
|
static StaticRefPtr<MediaManager> sSingleton;
|
||||||
|
|
||||||
media::CoatCheck<PledgeSourceSet> mOutstandingPledges;
|
media::CoatCheck<PledgeSourceSet> mOutstandingPledges;
|
||||||
|
media::CoatCheck<GetUserMediaCallbackMediaStreamListener::PledgeVoid> mOutstandingVoidPledges;
|
||||||
#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
|
#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
|
||||||
nsRefPtr<nsDOMCameraManager> mCameraManager;
|
nsRefPtr<nsDOMCameraManager> mCameraManager;
|
||||||
#endif
|
#endif
|
||||||
|
@ -12,10 +12,10 @@ namespace mozilla {
|
|||||||
|
|
||||||
BaseMediaMgrError::BaseMediaMgrError(const nsAString& aName,
|
BaseMediaMgrError::BaseMediaMgrError(const nsAString& aName,
|
||||||
const nsAString& aMessage,
|
const nsAString& aMessage,
|
||||||
const nsAString& aConstraintName)
|
const nsAString& aConstraint)
|
||||||
: mName(aName)
|
: mName(aName)
|
||||||
, mMessage(aMessage)
|
, mMessage(aMessage)
|
||||||
, mConstraintName(aConstraintName)
|
, mConstraint(aConstraint)
|
||||||
{
|
{
|
||||||
if (mMessage.IsEmpty()) {
|
if (mMessage.IsEmpty()) {
|
||||||
if (mName.EqualsLiteral("NotFoundError")) {
|
if (mName.EqualsLiteral("NotFoundError")) {
|
||||||
@ -28,8 +28,9 @@ BaseMediaMgrError::BaseMediaMgrError(const nsAString& aName,
|
|||||||
} else if (mName.EqualsLiteral("InternalError")) {
|
} else if (mName.EqualsLiteral("InternalError")) {
|
||||||
mMessage.AssignLiteral("Internal error.");
|
mMessage.AssignLiteral("Internal error.");
|
||||||
} else if (mName.EqualsLiteral("NotSupportedError")) {
|
} else if (mName.EqualsLiteral("NotSupportedError")) {
|
||||||
mMessage.AssignLiteral("Constraints with no audio or video in it are not "
|
mMessage.AssignLiteral("The operation is not supported.");
|
||||||
"supported");
|
} else if (mName.EqualsLiteral("OverconstrainedError")) {
|
||||||
|
mMessage.AssignLiteral("Constraints could be not satisfied.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -43,8 +44,8 @@ MediaStreamError::MediaStreamError(
|
|||||||
nsPIDOMWindow* aParent,
|
nsPIDOMWindow* aParent,
|
||||||
const nsAString& aName,
|
const nsAString& aName,
|
||||||
const nsAString& aMessage,
|
const nsAString& aMessage,
|
||||||
const nsAString& aConstraintName)
|
const nsAString& aConstraint)
|
||||||
: BaseMediaMgrError(aName, aMessage, aConstraintName)
|
: BaseMediaMgrError(aName, aMessage, aConstraint)
|
||||||
, mParent(aParent) {}
|
, mParent(aParent) {}
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MediaStreamError, mParent)
|
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MediaStreamError, mParent)
|
||||||
@ -75,9 +76,9 @@ MediaStreamError::GetMessage(nsAString& aMessage) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MediaStreamError::GetConstraintName(nsAString& aConstraintName) const
|
MediaStreamError::GetConstraint(nsAString& aConstraint) const
|
||||||
{
|
{
|
||||||
aConstraintName = mConstraintName;
|
aConstraint = mConstraint;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
|
@ -34,10 +34,10 @@ class BaseMediaMgrError
|
|||||||
protected:
|
protected:
|
||||||
BaseMediaMgrError(const nsAString& aName,
|
BaseMediaMgrError(const nsAString& aName,
|
||||||
const nsAString& aMessage,
|
const nsAString& aMessage,
|
||||||
const nsAString& aConstraintName);
|
const nsAString& aConstraint);
|
||||||
const nsString mName;
|
const nsString mName;
|
||||||
nsString mMessage;
|
nsString mMessage;
|
||||||
const nsString mConstraintName;
|
const nsString mConstraint;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MediaMgrError final : public nsISupports,
|
class MediaMgrError final : public nsISupports,
|
||||||
@ -46,8 +46,8 @@ class MediaMgrError final : public nsISupports,
|
|||||||
public:
|
public:
|
||||||
explicit MediaMgrError(const nsAString& aName,
|
explicit MediaMgrError(const nsAString& aName,
|
||||||
const nsAString& aMessage = EmptyString(),
|
const nsAString& aMessage = EmptyString(),
|
||||||
const nsAString& aConstraintName = EmptyString())
|
const nsAString& aConstraint = EmptyString())
|
||||||
: BaseMediaMgrError(aName, aMessage, aConstraintName) {}
|
: BaseMediaMgrError(aName, aMessage, aConstraint) {}
|
||||||
|
|
||||||
NS_DECL_THREADSAFE_ISUPPORTS
|
NS_DECL_THREADSAFE_ISUPPORTS
|
||||||
|
|
||||||
@ -64,11 +64,11 @@ public:
|
|||||||
MediaStreamError(nsPIDOMWindow* aParent,
|
MediaStreamError(nsPIDOMWindow* aParent,
|
||||||
const nsAString& aName,
|
const nsAString& aName,
|
||||||
const nsAString& aMessage = EmptyString(),
|
const nsAString& aMessage = EmptyString(),
|
||||||
const nsAString& aConstraintName = EmptyString());
|
const nsAString& aConstraint = EmptyString());
|
||||||
|
|
||||||
MediaStreamError(nsPIDOMWindow* aParent,
|
MediaStreamError(nsPIDOMWindow* aParent,
|
||||||
const BaseMediaMgrError& aOther)
|
const BaseMediaMgrError& aOther)
|
||||||
: BaseMediaMgrError(aOther.mName, aOther.mMessage, aOther.mConstraintName)
|
: BaseMediaMgrError(aOther.mName, aOther.mMessage, aOther.mConstraint)
|
||||||
, mParent(aParent) {}
|
, mParent(aParent) {}
|
||||||
|
|
||||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||||
@ -83,7 +83,7 @@ public:
|
|||||||
}
|
}
|
||||||
void GetName(nsAString& aName) const;
|
void GetName(nsAString& aName) const;
|
||||||
void GetMessage(nsAString& aMessage) const;
|
void GetMessage(nsAString& aMessage) const;
|
||||||
void GetConstraintName(nsAString& aConstraintName) const;
|
void GetConstraint(nsAString& aConstraint) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual ~MediaStreamError() {}
|
virtual ~MediaStreamError() {}
|
||||||
|
@ -62,5 +62,12 @@ MediaStreamTrack::Stop()
|
|||||||
mStream->StopTrack(mTrackID);
|
mStream->StopTrack(mTrackID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
already_AddRefed<Promise>
|
||||||
|
MediaStreamTrack::ApplyConstraints(const MediaTrackConstraints& aConstraints,
|
||||||
|
ErrorResult &aRv)
|
||||||
|
{
|
||||||
|
return mStream->ApplyConstraintsToTrack(mTrackID, aConstraints, aRv);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "mozilla/DOMEventTargetHelper.h"
|
#include "mozilla/DOMEventTargetHelper.h"
|
||||||
#include "nsID.h"
|
#include "nsID.h"
|
||||||
#include "StreamBuffer.h"
|
#include "StreamBuffer.h"
|
||||||
|
#include "MediaTrackConstraints.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
@ -49,6 +50,8 @@ public:
|
|||||||
bool Enabled() { return mEnabled; }
|
bool Enabled() { return mEnabled; }
|
||||||
void SetEnabled(bool aEnabled);
|
void SetEnabled(bool aEnabled);
|
||||||
void Stop();
|
void Stop();
|
||||||
|
already_AddRefed<Promise>
|
||||||
|
ApplyConstraints(const dom::MediaTrackConstraints& aConstraints, ErrorResult &aRv);
|
||||||
|
|
||||||
// Notifications from the MediaStreamGraph
|
// Notifications from the MediaStreamGraph
|
||||||
void NotifyEnded() { mEnded = true; }
|
void NotifyEnded() { mEnded = true; }
|
||||||
|
@ -63,7 +63,7 @@ class Pledge : public PledgeBase
|
|||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Pledge() : mDone(false), mError(nullptr) {}
|
explicit Pledge() : mDone(false), mRejected(false) {}
|
||||||
Pledge(const Pledge& aOther) = delete;
|
Pledge(const Pledge& aOther) = delete;
|
||||||
Pledge& operator = (const Pledge&) = delete;
|
Pledge& operator = (const Pledge&) = delete;
|
||||||
|
|
||||||
@ -97,10 +97,10 @@ public:
|
|||||||
mFunctors = new Functors(aOnSuccess, aOnFailure);
|
mFunctors = new Functors(aOnSuccess, aOnFailure);
|
||||||
|
|
||||||
if (mDone) {
|
if (mDone) {
|
||||||
if (!mError) {
|
if (!mRejected) {
|
||||||
mFunctors->Succeed(mValue);
|
mFunctors->Succeed(mValue);
|
||||||
} else {
|
} else {
|
||||||
mFunctors->Fail(*mError);
|
mFunctors->Fail(mError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,22 +110,11 @@ public:
|
|||||||
mValue = aValue;
|
mValue = aValue;
|
||||||
Resolve();
|
Resolve();
|
||||||
}
|
}
|
||||||
protected:
|
|
||||||
void Resolve()
|
|
||||||
{
|
|
||||||
if (!mDone) {
|
|
||||||
mDone = true;
|
|
||||||
MOZ_ASSERT(!mError);
|
|
||||||
if (mFunctors) {
|
|
||||||
mFunctors->Succeed(mValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Reject(ErrorType rv)
|
void Reject(ErrorType rv)
|
||||||
{
|
{
|
||||||
if (!mDone) {
|
if (!mDone) {
|
||||||
mDone = true;
|
mDone = mRejected = true;
|
||||||
mError = rv;
|
mError = rv;
|
||||||
if (mFunctors) {
|
if (mFunctors) {
|
||||||
mFunctors->Fail(mError);
|
mFunctors->Fail(mError);
|
||||||
@ -133,104 +122,24 @@ protected:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueType mValue;
|
|
||||||
private:
|
|
||||||
~Pledge() {};
|
|
||||||
bool mDone;
|
|
||||||
nsRefPtr<ErrorType> mError;
|
|
||||||
ScopedDeletePtr<FunctorsBase> mFunctors;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename ValueType>
|
|
||||||
class Pledge<ValueType, nsresult> : public PledgeBase
|
|
||||||
{
|
|
||||||
// TODO: Remove workaround once mozilla allows std::function from <functional>
|
|
||||||
// wo/std::function support, do template + virtual trick to accept lambdas
|
|
||||||
class FunctorsBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FunctorsBase() {}
|
|
||||||
virtual void Succeed(ValueType& result) = 0;
|
|
||||||
virtual void Fail(nsresult error) = 0;
|
|
||||||
virtual ~FunctorsBase() {};
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit Pledge() : mDone(false), mError(NS_OK) {}
|
|
||||||
Pledge(const Pledge& aOther) = delete;
|
|
||||||
Pledge& operator = (const Pledge&) = delete;
|
|
||||||
|
|
||||||
template<typename OnSuccessType>
|
|
||||||
void Then(OnSuccessType aOnSuccess)
|
|
||||||
{
|
|
||||||
Then(aOnSuccess, [](nsresult){});
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename OnSuccessType, typename OnFailureType>
|
|
||||||
void Then(OnSuccessType aOnSuccess, OnFailureType aOnFailure)
|
|
||||||
{
|
|
||||||
class Functors : public FunctorsBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Functors(OnSuccessType& aOnSuccess, OnFailureType& aOnFailure)
|
|
||||||
: mOnSuccess(aOnSuccess), mOnFailure(aOnFailure) {}
|
|
||||||
|
|
||||||
void Succeed(ValueType& result)
|
|
||||||
{
|
|
||||||
mOnSuccess(result);
|
|
||||||
}
|
|
||||||
void Fail(nsresult rv)
|
|
||||||
{
|
|
||||||
mOnFailure(rv);
|
|
||||||
};
|
|
||||||
|
|
||||||
OnSuccessType mOnSuccess;
|
|
||||||
OnFailureType mOnFailure;
|
|
||||||
};
|
|
||||||
mFunctors = new Functors(aOnSuccess, aOnFailure);
|
|
||||||
|
|
||||||
if (mDone) {
|
|
||||||
if (mError == NS_OK) {
|
|
||||||
mFunctors->Succeed(mValue);
|
|
||||||
} else {
|
|
||||||
mFunctors->Fail(mError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Resolve(const ValueType& aValue)
|
|
||||||
{
|
|
||||||
mValue = aValue;
|
|
||||||
Resolve();
|
|
||||||
}
|
|
||||||
protected:
|
protected:
|
||||||
void Resolve()
|
void Resolve()
|
||||||
{
|
{
|
||||||
if (!mDone) {
|
if (!mDone) {
|
||||||
mDone = true;
|
mDone = true;
|
||||||
MOZ_ASSERT(mError == NS_OK);
|
MOZ_ASSERT(!mRejected);
|
||||||
if (mFunctors) {
|
if (mFunctors) {
|
||||||
mFunctors->Succeed(mValue);
|
mFunctors->Succeed(mValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reject(nsresult error)
|
|
||||||
{
|
|
||||||
if (!mDone) {
|
|
||||||
mDone = true;
|
|
||||||
mError = error;
|
|
||||||
if (mFunctors) {
|
|
||||||
mFunctors->Fail(mError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ValueType mValue;
|
ValueType mValue;
|
||||||
private:
|
private:
|
||||||
~Pledge() {};
|
~Pledge() {};
|
||||||
bool mDone;
|
bool mDone;
|
||||||
nsresult mError;
|
bool mRejected;
|
||||||
|
ErrorType mError;
|
||||||
ScopedDeletePtr<FunctorsBase> mFunctors;
|
ScopedDeletePtr<FunctorsBase> mFunctors;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -234,7 +234,6 @@ function setupEnvironment() {
|
|||||||
window.finish = () => SimpleTest.finish();
|
window.finish = () => SimpleTest.finish();
|
||||||
SpecialPowers.pushPrefEnv({
|
SpecialPowers.pushPrefEnv({
|
||||||
'set': [
|
'set': [
|
||||||
['canvas.capturestream.enabled', true],
|
|
||||||
['media.peerconnection.enabled', true],
|
['media.peerconnection.enabled', true],
|
||||||
['media.peerconnection.identity.enabled', true],
|
['media.peerconnection.identity.enabled', true],
|
||||||
['media.peerconnection.identity.timeout', 120000],
|
['media.peerconnection.identity.timeout', 120000],
|
||||||
|
@ -16,9 +16,13 @@ function mustSucceed(msg, f) {
|
|||||||
e => is(e.name, null, msg + " must succeed: " + e.message));
|
e => is(e.name, null, msg + " must succeed: " + e.message));
|
||||||
}
|
}
|
||||||
|
|
||||||
function mustFailWith(msg, reason, f) {
|
function mustFailWith(msg, reason, constraint, f) {
|
||||||
return f().then(() => ok(false, msg + " must fail"),
|
return f().then(() => ok(false, msg + " must fail"), e => {
|
||||||
e => is(e.name, reason, msg + " must fail: " + e.message));
|
is(e.name, reason, msg + " must fail: " + e.message);
|
||||||
|
if (constraint) {
|
||||||
|
is(e.constraint, constraint, msg + " must fail w/correct constraint.");
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var pushPrefs = (...p) => new Promise(r => SpecialPowers.pushPrefEnv({set: p}, r));
|
var pushPrefs = (...p) => new Promise(r => SpecialPowers.pushPrefEnv({set: p}, r));
|
||||||
@ -49,13 +53,15 @@ runTest(() =>
|
|||||||
audio: { deviceId: "unknown9qHr8B0JIbcHlbl9xR+jMbZZ8WyoPfpCXPfc=" },
|
audio: { deviceId: "unknown9qHr8B0JIbcHlbl9xR+jMbZZ8WyoPfpCXPfc=" },
|
||||||
fake: true,
|
fake: true,
|
||||||
})))
|
})))
|
||||||
.then(() => mustFailWith("unknown exact deviceId on video", "NotFoundError",
|
.then(() => mustFailWith("unknown exact deviceId on video",
|
||||||
() => navigator.mediaDevices.getUserMedia({
|
"OverconstrainedError", "deviceId",
|
||||||
|
() => navigator.mediaDevices.getUserMedia({
|
||||||
video: { deviceId: { exact: "unknown9qHr8B0JIbcHlbl9xR+jMbZZ8WyoPfpCXPfc=" } },
|
video: { deviceId: { exact: "unknown9qHr8B0JIbcHlbl9xR+jMbZZ8WyoPfpCXPfc=" } },
|
||||||
fake: true,
|
fake: true,
|
||||||
})))
|
})))
|
||||||
.then(() => mustFailWith("unknown exact deviceId on audio", "NotFoundError",
|
.then(() => mustFailWith("unknown exact deviceId on audio",
|
||||||
() => navigator.mediaDevices.getUserMedia({
|
"OverconstrainedError", "deviceId",
|
||||||
|
() => navigator.mediaDevices.getUserMedia({
|
||||||
audio: { deviceId: { exact: "unknown9qHr8B0JIbcHlbl9xR+jMbZZ8WyoPfpCXPfc=" } },
|
audio: { deviceId: { exact: "unknown9qHr8B0JIbcHlbl9xR+jMbZZ8WyoPfpCXPfc=" } },
|
||||||
fake: true,
|
fake: true,
|
||||||
})))
|
})))
|
||||||
|
@ -39,9 +39,14 @@ var tests = [
|
|||||||
{ message: "browser screensharing requires permission",
|
{ message: "browser screensharing requires permission",
|
||||||
constraints: { video: { mediaSource: 'browser' } },
|
constraints: { video: { mediaSource: 'browser' } },
|
||||||
error: "PermissionDeniedError" },
|
error: "PermissionDeniedError" },
|
||||||
{ message: "unknown mediaSource fails",
|
{ message: "unknown mediaSource in video fails",
|
||||||
constraints: { video: { mediaSource: 'uncle' } },
|
constraints: { video: { mediaSource: 'uncle' } },
|
||||||
error: "NotFoundError" },
|
error: "OverconstrainedError",
|
||||||
|
constraint: "mediaSource" },
|
||||||
|
{ message: "unknown mediaSource in audio fails",
|
||||||
|
constraints: { audio: { mediaSource: 'uncle' } },
|
||||||
|
error: "OverconstrainedError",
|
||||||
|
constraint: "mediaSource" },
|
||||||
{ message: "emtpy constraint fails",
|
{ message: "emtpy constraint fails",
|
||||||
constraints: { },
|
constraints: { },
|
||||||
error: "NotSupportedError" },
|
error: "NotSupportedError" },
|
||||||
@ -106,8 +111,18 @@ runTest(function() {
|
|||||||
|
|
||||||
return tests.reduce((p, test) =>
|
return tests.reduce((p, test) =>
|
||||||
p.then(() => navigator.mediaDevices.getUserMedia(test.constraints))
|
p.then(() => navigator.mediaDevices.getUserMedia(test.constraints))
|
||||||
.then(() => is(null, test.error, test.message),
|
.then(() => is(null, test.error, test.message), e => {
|
||||||
e => is(e.name, test.error, test.message + ": " + e.message)), p);
|
is(e.name, test.error, test.message + ": " + e.message);
|
||||||
|
if (test.constraint) {
|
||||||
|
is(e.constraint, test.constraint,
|
||||||
|
test.message + " w/correct constraint.");
|
||||||
|
}
|
||||||
|
}), p)
|
||||||
|
.then(() => navigator.mediaDevices.getUserMedia({video: true, audio: true}))
|
||||||
|
.then(stream => stream.getVideoTracks()[0].applyConstraints({ width: 320 })
|
||||||
|
.then(() => stream.getAudioTracks()[0].applyConstraints({ })))
|
||||||
|
.then(() => ok(true, "applyConstraints code exercised"))
|
||||||
|
// TODO: Test outcome once fake devices support constraints (Bug 1088621)
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -111,6 +111,11 @@ public:
|
|||||||
/* Stop the device and release the corresponding MediaStream */
|
/* Stop the device and release the corresponding MediaStream */
|
||||||
virtual nsresult Stop(SourceMediaStream *aSource, TrackID aID) = 0;
|
virtual nsresult Stop(SourceMediaStream *aSource, TrackID aID) = 0;
|
||||||
|
|
||||||
|
/* Restart with new capability */
|
||||||
|
virtual nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||||
|
const MediaEnginePrefs &aPrefs,
|
||||||
|
const nsString& aDeviceId) = 0;
|
||||||
|
|
||||||
/* Change device configuration. */
|
/* Change device configuration. */
|
||||||
virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
|
virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
|
||||||
bool aAgcOn, uint32_t aAGC,
|
bool aAgcOn, uint32_t aAGC,
|
||||||
|
@ -201,6 +201,14 @@ MediaEngineDefaultVideoSource::Stop(SourceMediaStream *aSource, TrackID aID)
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
MediaEngineDefaultVideoSource::Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||||
|
const MediaEnginePrefs &aPrefs,
|
||||||
|
const nsString& aDeviceId)
|
||||||
|
{
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
MediaEngineDefaultVideoSource::Notify(nsITimer* aTimer)
|
MediaEngineDefaultVideoSource::Notify(nsITimer* aTimer)
|
||||||
{
|
{
|
||||||
@ -470,6 +478,14 @@ MediaEngineDefaultAudioSource::Stop(SourceMediaStream *aSource, TrackID aID)
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
MediaEngineDefaultAudioSource::Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||||
|
const MediaEnginePrefs &aPrefs,
|
||||||
|
const nsString& aDeviceId)
|
||||||
|
{
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
MediaEngineDefaultAudioSource::Notify(nsITimer* aTimer)
|
MediaEngineDefaultAudioSource::Notify(nsITimer* aTimer)
|
||||||
{
|
{
|
||||||
|
@ -49,6 +49,9 @@ public:
|
|||||||
virtual nsresult Deallocate() override;
|
virtual nsresult Deallocate() override;
|
||||||
virtual nsresult Start(SourceMediaStream*, TrackID) override;
|
virtual nsresult Start(SourceMediaStream*, TrackID) override;
|
||||||
virtual nsresult Stop(SourceMediaStream*, TrackID) override;
|
virtual nsresult Stop(SourceMediaStream*, TrackID) override;
|
||||||
|
virtual nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||||
|
const MediaEnginePrefs &aPrefs,
|
||||||
|
const nsString& aDeviceId) override;
|
||||||
virtual void SetDirectListeners(bool aHasDirectListeners) override {};
|
virtual void SetDirectListeners(bool aHasDirectListeners) override {};
|
||||||
virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
|
virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
|
||||||
bool aAgcOn, uint32_t aAGC,
|
bool aAgcOn, uint32_t aAGC,
|
||||||
@ -119,6 +122,9 @@ public:
|
|||||||
virtual nsresult Deallocate() override;
|
virtual nsresult Deallocate() override;
|
||||||
virtual nsresult Start(SourceMediaStream*, TrackID) override;
|
virtual nsresult Start(SourceMediaStream*, TrackID) override;
|
||||||
virtual nsresult Stop(SourceMediaStream*, TrackID) override;
|
virtual nsresult Stop(SourceMediaStream*, TrackID) override;
|
||||||
|
virtual nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||||
|
const MediaEnginePrefs &aPrefs,
|
||||||
|
const nsString& aDeviceId) override;
|
||||||
virtual void SetDirectListeners(bool aHasDirectListeners) override {};
|
virtual void SetDirectListeners(bool aHasDirectListeners) override {};
|
||||||
virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
|
virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
|
||||||
bool aAgcOn, uint32_t aAGC,
|
bool aAgcOn, uint32_t aAGC,
|
||||||
|
@ -328,6 +328,14 @@ MediaEngineGonkVideoSource::Stop(SourceMediaStream* aSource, TrackID aID)
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
MediaEngineGonkVideoSource::Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||||
|
const MediaEnginePrefs& aPrefs,
|
||||||
|
const nsString& aDeviceId)
|
||||||
|
{
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialization and Shutdown functions for the video source, called by the
|
* Initialization and Shutdown functions for the video source, called by the
|
||||||
* constructor and destructor respectively.
|
* constructor and destructor respectively.
|
||||||
|
@ -66,6 +66,9 @@ public:
|
|||||||
virtual nsresult Deallocate() override;
|
virtual nsresult Deallocate() override;
|
||||||
virtual nsresult Start(SourceMediaStream* aStream, TrackID aID) override;
|
virtual nsresult Start(SourceMediaStream* aStream, TrackID aID) override;
|
||||||
virtual nsresult Stop(SourceMediaStream* aSource, TrackID aID) override;
|
virtual nsresult Stop(SourceMediaStream* aSource, TrackID aID) override;
|
||||||
|
virtual nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||||
|
const MediaEnginePrefs &aPrefs,
|
||||||
|
const nsString& aDeviceId) override;
|
||||||
virtual void NotifyPull(MediaStreamGraph* aGraph,
|
virtual void NotifyPull(MediaStreamGraph* aGraph,
|
||||||
SourceMediaStream* aSource,
|
SourceMediaStream* aSource,
|
||||||
TrackID aId,
|
TrackID aId,
|
||||||
|
@ -212,6 +212,31 @@ MediaEngineRemoteVideoSource::Stop(mozilla::SourceMediaStream* aSource,
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
MediaEngineRemoteVideoSource::Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||||
|
const MediaEnginePrefs& aPrefs,
|
||||||
|
const nsString& aDeviceId)
|
||||||
|
{
|
||||||
|
if (!mInitDone) {
|
||||||
|
LOG(("Init not done"));
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
if (!ChooseCapability(aConstraints, aPrefs, aDeviceId)) {
|
||||||
|
return NS_ERROR_NOT_AVAILABLE;
|
||||||
|
}
|
||||||
|
if (mState != kStarted) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
mozilla::camera::StopCapture(mCapEngine, mCaptureIndex);
|
||||||
|
if (mozilla::camera::StartCapture(mCapEngine,
|
||||||
|
mCaptureIndex, mCapability, this)) {
|
||||||
|
LOG(("StartCapture failed"));
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MediaEngineRemoteVideoSource::NotifyPull(MediaStreamGraph* aGraph,
|
MediaEngineRemoteVideoSource::NotifyPull(MediaStreamGraph* aGraph,
|
||||||
SourceMediaStream* aSource,
|
SourceMediaStream* aSource,
|
||||||
|
@ -71,6 +71,9 @@ public:
|
|||||||
virtual nsresult Deallocate() override;;
|
virtual nsresult Deallocate() override;;
|
||||||
virtual nsresult Start(SourceMediaStream*, TrackID) override;
|
virtual nsresult Start(SourceMediaStream*, TrackID) override;
|
||||||
virtual nsresult Stop(SourceMediaStream*, TrackID) override;
|
virtual nsresult Stop(SourceMediaStream*, TrackID) override;
|
||||||
|
virtual nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||||
|
const MediaEnginePrefs &aPrefs,
|
||||||
|
const nsString& aDeviceId) override;
|
||||||
virtual void NotifyPull(MediaStreamGraph* aGraph,
|
virtual void NotifyPull(MediaStreamGraph* aGraph,
|
||||||
SourceMediaStream* aSource,
|
SourceMediaStream* aSource,
|
||||||
TrackID aId,
|
TrackID aId,
|
||||||
|
@ -291,6 +291,15 @@ MediaEngineTabVideoSource::Stop(mozilla::SourceMediaStream*, mozilla::TrackID)
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
MediaEngineTabVideoSource::Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||||
|
const mozilla::MediaEnginePrefs& aPrefs,
|
||||||
|
const nsString& aDeviceId)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
MediaEngineTabVideoSource::Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t)
|
MediaEngineTabVideoSource::Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t)
|
||||||
{
|
{
|
||||||
|
@ -29,6 +29,9 @@ class MediaEngineTabVideoSource : public MediaEngineVideoSource, nsIDOMEventList
|
|||||||
virtual void SetDirectListeners(bool aHasDirectListeners) override {};
|
virtual void SetDirectListeners(bool aHasDirectListeners) override {};
|
||||||
virtual void NotifyPull(mozilla::MediaStreamGraph*, mozilla::SourceMediaStream*, mozilla::TrackID, mozilla::StreamTime) override;
|
virtual void NotifyPull(mozilla::MediaStreamGraph*, mozilla::SourceMediaStream*, mozilla::TrackID, mozilla::StreamTime) override;
|
||||||
virtual nsresult Stop(mozilla::SourceMediaStream*, mozilla::TrackID) override;
|
virtual nsresult Stop(mozilla::SourceMediaStream*, mozilla::TrackID) override;
|
||||||
|
virtual nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||||
|
const mozilla::MediaEnginePrefs& aPrefs,
|
||||||
|
const nsString& aDeviceId) override;
|
||||||
virtual nsresult Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t) override;
|
virtual nsresult Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t) override;
|
||||||
virtual bool IsFake() override;
|
virtual bool IsFake() override;
|
||||||
virtual const dom::MediaSourceEnum GetMediaSource() override {
|
virtual const dom::MediaSourceEnum GetMediaSource() override {
|
||||||
|
@ -84,6 +84,9 @@ public:
|
|||||||
}
|
}
|
||||||
nsresult Start(SourceMediaStream* aMediaStream, TrackID aId) override;
|
nsresult Start(SourceMediaStream* aMediaStream, TrackID aId) override;
|
||||||
nsresult Stop(SourceMediaStream* aMediaStream, TrackID aId) override;
|
nsresult Stop(SourceMediaStream* aMediaStream, TrackID aId) override;
|
||||||
|
nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||||
|
const MediaEnginePrefs &aPrefs,
|
||||||
|
const nsString& aDeviceId) override;
|
||||||
void SetDirectListeners(bool aDirect) override
|
void SetDirectListeners(bool aDirect) override
|
||||||
{}
|
{}
|
||||||
nsresult Config(bool aEchoOn, uint32_t aEcho, bool aAgcOn,
|
nsresult Config(bool aEchoOn, uint32_t aEcho, bool aAgcOn,
|
||||||
@ -155,6 +158,9 @@ public:
|
|||||||
virtual nsresult Deallocate() override;
|
virtual nsresult Deallocate() override;
|
||||||
virtual nsresult Start(SourceMediaStream* aStream, TrackID aID) override;
|
virtual nsresult Start(SourceMediaStream* aStream, TrackID aID) override;
|
||||||
virtual nsresult Stop(SourceMediaStream* aSource, TrackID aID) override;
|
virtual nsresult Stop(SourceMediaStream* aSource, TrackID aID) override;
|
||||||
|
virtual nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||||
|
const MediaEnginePrefs &aPrefs,
|
||||||
|
const nsString& aDeviceId) override;
|
||||||
virtual void SetDirectListeners(bool aHasDirectListeners) override {};
|
virtual void SetDirectListeners(bool aHasDirectListeners) override {};
|
||||||
virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
|
virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
|
||||||
bool aAgcOn, uint32_t aAGC,
|
bool aAgcOn, uint32_t aAGC,
|
||||||
|
@ -422,6 +422,14 @@ MediaEngineWebRTCMicrophoneSource::Stop(SourceMediaStream *aSource, TrackID aID)
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
MediaEngineWebRTCMicrophoneSource::Restart(const dom::MediaTrackConstraints& aConstraints,
|
||||||
|
const MediaEnginePrefs &aPrefs,
|
||||||
|
const nsString& aDeviceId)
|
||||||
|
{
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MediaEngineWebRTCMicrophoneSource::NotifyPull(MediaStreamGraph *aGraph,
|
MediaEngineWebRTCMicrophoneSource::NotifyPull(MediaStreamGraph *aGraph,
|
||||||
SourceMediaStream *aSource,
|
SourceMediaStream *aSource,
|
||||||
@ -664,6 +672,15 @@ MediaEngineWebRTCAudioCaptureSource::Stop(SourceMediaStream *aMediaStream,
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
MediaEngineWebRTCAudioCaptureSource::Restart(
|
||||||
|
const dom::MediaTrackConstraints& aConstraints,
|
||||||
|
const MediaEnginePrefs &aPrefs,
|
||||||
|
const nsString& aDeviceId)
|
||||||
|
{
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t
|
uint32_t
|
||||||
MediaEngineWebRTCAudioCaptureSource::GetBestFitnessDistance(
|
MediaEngineWebRTCAudioCaptureSource::GetBestFitnessDistance(
|
||||||
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets,
|
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets,
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
#include "mozilla/dom/MediaTrackConstraintSetBinding.h"
|
#include "mozilla/dom/MediaTrackConstraintSetBinding.h"
|
||||||
#include "mozilla/dom/MediaTrackSupportedConstraintsBinding.h"
|
#include "mozilla/dom/MediaTrackSupportedConstraintsBinding.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
template<class EnumValuesStrings, class Enum>
|
template<class EnumValuesStrings, class Enum>
|
||||||
@ -104,6 +106,128 @@ protected:
|
|||||||
GetMinimumFitnessDistance(const dom::MediaTrackConstraintSet &aConstraints,
|
GetMinimumFitnessDistance(const dom::MediaTrackConstraintSet &aConstraints,
|
||||||
bool aAdvanced,
|
bool aAdvanced,
|
||||||
const nsString& aDeviceId);
|
const nsString& aDeviceId);
|
||||||
|
|
||||||
|
template<class DeviceType>
|
||||||
|
static bool
|
||||||
|
AreUnfitSettings(const dom::MediaTrackConstraints &aConstraints,
|
||||||
|
nsTArray<nsRefPtr<DeviceType>>& aSources)
|
||||||
|
{
|
||||||
|
nsTArray<const dom::MediaTrackConstraintSet*> aggregateConstraints;
|
||||||
|
aggregateConstraints.AppendElement(&aConstraints);
|
||||||
|
|
||||||
|
for (auto& source : aSources) {
|
||||||
|
if (source->GetBestFitnessDistance(aggregateConstraints) != UINT32_MAX) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Apply constrains to a supplied list of sources (removes items from the list)
|
||||||
|
|
||||||
|
template<class DeviceType>
|
||||||
|
static const char*
|
||||||
|
SelectSettings(const dom::MediaTrackConstraints &aConstraints,
|
||||||
|
nsTArray<nsRefPtr<DeviceType>>& aSources)
|
||||||
|
{
|
||||||
|
auto& c = aConstraints;
|
||||||
|
|
||||||
|
// First apply top-level constraints.
|
||||||
|
|
||||||
|
// Stack constraintSets that pass, starting with the required one, because the
|
||||||
|
// whole stack must be re-satisfied each time a capability-set is ruled out
|
||||||
|
// (this avoids storing state or pushing algorithm into the lower-level code).
|
||||||
|
nsTArray<nsRefPtr<DeviceType>> unsatisfactory;
|
||||||
|
nsTArray<const dom::MediaTrackConstraintSet*> aggregateConstraints;
|
||||||
|
aggregateConstraints.AppendElement(&c);
|
||||||
|
|
||||||
|
std::multimap<uint32_t, nsRefPtr<DeviceType>> ordered;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < aSources.Length();) {
|
||||||
|
uint32_t distance = aSources[i]->GetBestFitnessDistance(aggregateConstraints);
|
||||||
|
if (distance == UINT32_MAX) {
|
||||||
|
unsatisfactory.AppendElement(aSources[i]);
|
||||||
|
aSources.RemoveElementAt(i);
|
||||||
|
} else {
|
||||||
|
ordered.insert(std::pair<uint32_t, nsRefPtr<DeviceType>>(distance,
|
||||||
|
aSources[i]));
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!aSources.Length()) {
|
||||||
|
// None selected. The spec says to report a constraint that satisfies NONE
|
||||||
|
// of the sources. Unfortunately, this is a bit laborious to find out, and
|
||||||
|
// requires updating as new constraints are added!
|
||||||
|
|
||||||
|
if (c.mDeviceId.IsConstrainDOMStringParameters()) {
|
||||||
|
dom::MediaTrackConstraints fresh;
|
||||||
|
fresh.mDeviceId = c.mDeviceId;
|
||||||
|
if (AreUnfitSettings(fresh, unsatisfactory)) {
|
||||||
|
return "deviceId";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (c.mWidth.IsConstrainLongRange()) {
|
||||||
|
dom::MediaTrackConstraints fresh;
|
||||||
|
fresh.mWidth = c.mWidth;
|
||||||
|
if (AreUnfitSettings(fresh, unsatisfactory)) {
|
||||||
|
return "width";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (c.mHeight.IsConstrainLongRange()) {
|
||||||
|
dom::MediaTrackConstraints fresh;
|
||||||
|
fresh.mHeight = c.mHeight;
|
||||||
|
if (AreUnfitSettings(fresh, unsatisfactory)) {
|
||||||
|
return "height";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (c.mFrameRate.IsConstrainDoubleRange()) {
|
||||||
|
dom::MediaTrackConstraints fresh;
|
||||||
|
fresh.mFrameRate = c.mFrameRate;
|
||||||
|
if (AreUnfitSettings(fresh, unsatisfactory)) {
|
||||||
|
return "frameRate";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (c.mFacingMode.IsConstrainDOMStringParameters()) {
|
||||||
|
dom::MediaTrackConstraints fresh;
|
||||||
|
fresh.mFacingMode = c.mFacingMode;
|
||||||
|
if (AreUnfitSettings(fresh, unsatisfactory)) {
|
||||||
|
return "facingMode";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Order devices by shortest distance
|
||||||
|
for (auto& ordinal : ordered) {
|
||||||
|
aSources.RemoveElement(ordinal.second);
|
||||||
|
aSources.AppendElement(ordinal.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then apply advanced constraints.
|
||||||
|
|
||||||
|
if (c.mAdvanced.WasPassed()) {
|
||||||
|
auto &array = c.mAdvanced.Value();
|
||||||
|
|
||||||
|
for (int i = 0; i < int(array.Length()); i++) {
|
||||||
|
aggregateConstraints.AppendElement(&array[i]);
|
||||||
|
nsTArray<nsRefPtr<DeviceType>> rejects;
|
||||||
|
for (uint32_t j = 0; j < aSources.Length();) {
|
||||||
|
if (aSources[j]->GetBestFitnessDistance(aggregateConstraints) == UINT32_MAX) {
|
||||||
|
rejects.AppendElement(aSources[j]);
|
||||||
|
aSources.RemoveElementAt(j);
|
||||||
|
} else {
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!aSources.Length()) {
|
||||||
|
aSources.AppendElements(Move(rejects));
|
||||||
|
aggregateConstraints.RemoveElementAt(aggregateConstraints.Length() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
@ -24,8 +24,6 @@ parent:
|
|||||||
|
|
||||||
child:
|
child:
|
||||||
CallbackAccept(PTCPSocket socket);
|
CallbackAccept(PTCPSocket socket);
|
||||||
CallbackError(nsString message, nsString filename,
|
|
||||||
uint32_t lineNumber, uint32_t columnNumber);
|
|
||||||
__delete__();
|
__delete__();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -13,11 +13,12 @@ using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
|
|||||||
|
|
||||||
struct TCPError {
|
struct TCPError {
|
||||||
nsString name;
|
nsString name;
|
||||||
|
nsString message;
|
||||||
};
|
};
|
||||||
|
|
||||||
union SendableData {
|
union SendableData {
|
||||||
uint8_t[];
|
uint8_t[];
|
||||||
nsString;
|
nsCString;
|
||||||
};
|
};
|
||||||
|
|
||||||
union CallbackData {
|
union CallbackData {
|
||||||
@ -38,7 +39,13 @@ parent:
|
|||||||
// Forward calling to child's open() method to parent, expect TCPOptions
|
// Forward calling to child's open() method to parent, expect TCPOptions
|
||||||
// is expanded to |useSSL| (from TCPOptions.useSecureTransport) and
|
// is expanded to |useSSL| (from TCPOptions.useSecureTransport) and
|
||||||
// |binaryType| (from TCPOption.binaryType).
|
// |binaryType| (from TCPOption.binaryType).
|
||||||
Open(nsString host, uint16_t port, bool useSSL, nsString binaryType);
|
Open(nsString host, uint16_t port, bool useSSL, bool useArrayBuffers);
|
||||||
|
|
||||||
|
// Ask parent to open a socket and bind the newly-opened socket to a local
|
||||||
|
// address specified in |localAddr| and |localPort|.
|
||||||
|
OpenBind(nsCString host, uint16_t port,
|
||||||
|
nsCString localAddr, uint16_t localPort,
|
||||||
|
bool useSSL, bool aUseArrayBuffers);
|
||||||
|
|
||||||
// When child's send() is called, this message requrests parent to send
|
// When child's send() is called, this message requrests parent to send
|
||||||
// data and update it's trackingNumber.
|
// data and update it's trackingNumber.
|
||||||
@ -58,7 +65,7 @@ parent:
|
|||||||
|
|
||||||
child:
|
child:
|
||||||
// Forward events that are dispatched by parent.
|
// Forward events that are dispatched by parent.
|
||||||
Callback(nsString type, CallbackData data, nsString readyState);
|
Callback(nsString type, CallbackData data, uint32_t readyState);
|
||||||
|
|
||||||
// Update child's bufferedAmount when parent's bufferedAmount is updated.
|
// Update child's bufferedAmount when parent's bufferedAmount is updated.
|
||||||
// trackingNumber is also passed back to child to ensure the bufferedAmount
|
// trackingNumber is also passed back to child to ensure the bufferedAmount
|
||||||
|
196
dom/network/TCPServerSocket.cpp
Normal file
196
dom/network/TCPServerSocket.cpp
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "mozilla/dom/TCPServerSocketBinding.h"
|
||||||
|
#include "mozilla/dom/TCPServerSocketEvent.h"
|
||||||
|
#include "mozilla/dom/TCPSocketBinding.h"
|
||||||
|
#include "TCPServerSocketParent.h"
|
||||||
|
#include "TCPServerSocketChild.h"
|
||||||
|
#include "mozilla/dom/Event.h"
|
||||||
|
#include "mozilla/ErrorResult.h"
|
||||||
|
#include "TCPServerSocket.h"
|
||||||
|
#include "TCPSocket.h"
|
||||||
|
|
||||||
|
using namespace mozilla::dom;
|
||||||
|
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_CLASS(TCPServerSocket)
|
||||||
|
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(TCPServerSocket,
|
||||||
|
DOMEventTargetHelper)
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||||
|
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TCPServerSocket,
|
||||||
|
DOMEventTargetHelper)
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServerSocket)
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServerBridgeChild)
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServerBridgeParent)
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||||
|
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TCPServerSocket,
|
||||||
|
DOMEventTargetHelper)
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mServerSocket)
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mServerBridgeChild)
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mServerBridgeParent)
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||||
|
|
||||||
|
NS_IMPL_ADDREF_INHERITED(TCPServerSocket, DOMEventTargetHelper)
|
||||||
|
NS_IMPL_RELEASE_INHERITED(TCPServerSocket, DOMEventTargetHelper)
|
||||||
|
|
||||||
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TCPServerSocket)
|
||||||
|
NS_INTERFACE_MAP_ENTRY(nsIServerSocketListener)
|
||||||
|
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||||
|
|
||||||
|
TCPServerSocket::TCPServerSocket(nsIGlobalObject* aGlobal, uint16_t aPort,
|
||||||
|
bool aUseArrayBuffers, uint16_t aBacklog)
|
||||||
|
: DOMEventTargetHelper(aGlobal)
|
||||||
|
, mPort(aPort)
|
||||||
|
, mBacklog(aBacklog)
|
||||||
|
, mUseArrayBuffers(aUseArrayBuffers)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TCPServerSocket::~TCPServerSocket()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
TCPServerSocket::Init()
|
||||||
|
{
|
||||||
|
if (mServerSocket || mServerBridgeChild) {
|
||||||
|
NS_WARNING("Child TCPServerSocket is already listening.");
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
||||||
|
mServerBridgeChild = new TCPServerSocketChild(this, mPort, mBacklog, mUseArrayBuffers);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult rv;
|
||||||
|
mServerSocket = do_CreateInstance("@mozilla.org/network/server-socket;1", &rv);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
rv = mServerSocket->Init(mPort, false, mBacklog);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
rv = mServerSocket->GetPort(&mPort);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
rv = mServerSocket->AsyncListen(this);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
already_AddRefed<TCPServerSocket>
|
||||||
|
TCPServerSocket::Constructor(const GlobalObject& aGlobal,
|
||||||
|
uint16_t aPort,
|
||||||
|
const ServerSocketOptions& aOptions,
|
||||||
|
uint16_t aBacklog,
|
||||||
|
mozilla::ErrorResult& aRv)
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||||
|
if (!global) {
|
||||||
|
aRv = NS_ERROR_FAILURE;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
bool useArrayBuffers = aOptions.mBinaryType == TCPSocketBinaryType::Arraybuffer;
|
||||||
|
nsRefPtr<TCPServerSocket> socket = new TCPServerSocket(global, aPort, useArrayBuffers, aBacklog);
|
||||||
|
nsresult rv = socket->Init();
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
aRv = NS_ERROR_FAILURE;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return socket.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t
|
||||||
|
TCPServerSocket::LocalPort()
|
||||||
|
{
|
||||||
|
return mPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TCPServerSocket::Close()
|
||||||
|
{
|
||||||
|
if (mServerBridgeChild) {
|
||||||
|
mServerBridgeChild->Close();
|
||||||
|
}
|
||||||
|
if (mServerSocket) {
|
||||||
|
mServerSocket->Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TCPServerSocket::FireEvent(const nsAString& aType, TCPSocket* aSocket)
|
||||||
|
{
|
||||||
|
AutoJSAPI api;
|
||||||
|
api.Init(GetOwner());
|
||||||
|
|
||||||
|
TCPServerSocketEventInit init;
|
||||||
|
init.mBubbles = false;
|
||||||
|
init.mCancelable = false;
|
||||||
|
init.mSocket = aSocket;
|
||||||
|
|
||||||
|
nsRefPtr<TCPServerSocketEvent> event =
|
||||||
|
TCPServerSocketEvent::Constructor(this, aType, init);
|
||||||
|
event->SetTrusted(true);
|
||||||
|
bool dummy;
|
||||||
|
DispatchEvent(event, &dummy);
|
||||||
|
|
||||||
|
if (mServerBridgeParent) {
|
||||||
|
mServerBridgeParent->OnConnect(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
TCPServerSocket::OnSocketAccepted(nsIServerSocket* aServer, nsISocketTransport* aTransport)
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
|
||||||
|
nsRefPtr<TCPSocket> socket = TCPSocket::CreateAcceptedSocket(global, aTransport, mUseArrayBuffers);
|
||||||
|
if (mServerBridgeParent) {
|
||||||
|
socket->SetAppIdAndBrowser(mServerBridgeParent->GetAppId(),
|
||||||
|
mServerBridgeParent->GetInBrowser());
|
||||||
|
}
|
||||||
|
FireEvent(NS_LITERAL_STRING("connect"), socket);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
TCPServerSocket::OnStopListening(nsIServerSocket* aServer, nsresult aStatus)
|
||||||
|
{
|
||||||
|
if (aStatus != NS_BINDING_ABORTED) {
|
||||||
|
nsRefPtr<Event> event = new Event(GetOwner());
|
||||||
|
nsresult rv = event->InitEvent(NS_LITERAL_STRING("error"), false, false);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
event->SetTrusted(true);
|
||||||
|
bool dummy;
|
||||||
|
DispatchEvent(event, &dummy);
|
||||||
|
|
||||||
|
NS_WARNING("Server socket was closed by unexpected reason.");
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
mServerSocket = nullptr;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
TCPServerSocket::AcceptChildSocket(TCPSocketChild* aSocketChild)
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
|
||||||
|
NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
|
||||||
|
nsRefPtr<TCPSocket> socket = TCPSocket::CreateAcceptedSocket(global, aSocketChild, mUseArrayBuffers);
|
||||||
|
NS_ENSURE_TRUE(socket, NS_ERROR_FAILURE);
|
||||||
|
FireEvent(NS_LITERAL_STRING("connect"), socket);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TCPServerSocket::SetServerBridgeParent(TCPServerSocketParent* aBridgeParent)
|
||||||
|
{
|
||||||
|
mServerBridgeParent = aBridgeParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSObject*
|
||||||
|
TCPServerSocket::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||||
|
{
|
||||||
|
return TCPServerSocketBinding::Wrap(aCx, this, aGivenProto);
|
||||||
|
}
|
83
dom/network/TCPServerSocket.h
Normal file
83
dom/network/TCPServerSocket.h
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_TCPServerSocket_h
|
||||||
|
#define mozilla_dom_TCPServerSocket_h
|
||||||
|
|
||||||
|
#include "mozilla/DOMEventTargetHelper.h"
|
||||||
|
#include "nsIServerSocket.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
class ErrorResult;
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
struct ServerSocketOptions;
|
||||||
|
class GlobalObject;
|
||||||
|
class TCPSocket;
|
||||||
|
class TCPSocketChild;
|
||||||
|
class TCPServerSocketChild;
|
||||||
|
class TCPServerSocketParent;
|
||||||
|
|
||||||
|
class TCPServerSocket final : public DOMEventTargetHelper
|
||||||
|
, public nsIServerSocketListener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TCPServerSocket(nsIGlobalObject* aGlobal, uint16_t aPort, bool aUseArrayBuffers,
|
||||||
|
uint16_t aBacklog);
|
||||||
|
|
||||||
|
NS_DECL_ISUPPORTS_INHERITED
|
||||||
|
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(TCPServerSocket, DOMEventTargetHelper)
|
||||||
|
NS_DECL_NSISERVERSOCKETLISTENER
|
||||||
|
|
||||||
|
nsPIDOMWindow* GetParentObject() const
|
||||||
|
{
|
||||||
|
return GetOwner();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||||
|
|
||||||
|
nsresult Init();
|
||||||
|
|
||||||
|
uint16_t LocalPort();
|
||||||
|
void Close();
|
||||||
|
|
||||||
|
static already_AddRefed<TCPServerSocket>
|
||||||
|
Constructor(const GlobalObject& aGlobal,
|
||||||
|
uint16_t aPort,
|
||||||
|
const ServerSocketOptions& aOptions,
|
||||||
|
uint16_t aBacklog,
|
||||||
|
mozilla::ErrorResult& aRv);
|
||||||
|
|
||||||
|
IMPL_EVENT_HANDLER(connect);
|
||||||
|
IMPL_EVENT_HANDLER(error);
|
||||||
|
|
||||||
|
// Relay an accepted socket notification from the parent process and
|
||||||
|
// initialize this object with an existing child actor for the new socket.
|
||||||
|
nsresult AcceptChildSocket(TCPSocketChild* aSocketChild);
|
||||||
|
// Associate this object with an IPC actor in the parent process to relay
|
||||||
|
// notifications to content processes.
|
||||||
|
void SetServerBridgeParent(TCPServerSocketParent* aBridgeParent);
|
||||||
|
|
||||||
|
private:
|
||||||
|
~TCPServerSocket();
|
||||||
|
// Dispatch a TCPServerSocketEvent event of a given type at this object.
|
||||||
|
void FireEvent(const nsAString& aType, TCPSocket* aSocket);
|
||||||
|
|
||||||
|
// The server socket associated with this object.
|
||||||
|
nsCOMPtr<nsIServerSocket> mServerSocket;
|
||||||
|
// The IPC actor in the content process.
|
||||||
|
nsRefPtr<TCPServerSocketChild> mServerBridgeChild;
|
||||||
|
// The IPC actor in the parent process.
|
||||||
|
nsRefPtr<TCPServerSocketParent> mServerBridgeParent;
|
||||||
|
int32_t mPort;
|
||||||
|
uint16_t mBacklog;
|
||||||
|
// True if any accepted sockets should use array buffers for received messages.
|
||||||
|
bool mUseArrayBuffers;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_dom_TCPServerSocket_h
|
@ -1,187 +0,0 @@
|
|||||||
/* 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/. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const Cc = Components.classes;
|
|
||||||
const Ci = Components.interfaces;
|
|
||||||
const Cu = Components.utils;
|
|
||||||
const Cr = Components.results;
|
|
||||||
const CC = Components.Constructor;
|
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
||||||
|
|
||||||
const ServerSocket = CC(
|
|
||||||
'@mozilla.org/network/server-socket;1', 'nsIServerSocket', 'init'),
|
|
||||||
TCPSocketInternal = Cc[
|
|
||||||
'@mozilla.org/tcp-socket;1'].createInstance(Ci.nsITCPSocketInternal);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Debug logging function
|
|
||||||
*/
|
|
||||||
|
|
||||||
var debug = true;
|
|
||||||
function LOG(msg) {
|
|
||||||
if (debug) {
|
|
||||||
dump("TCPServerSocket: " + msg + "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* nsIDOMTCPServerSocket object
|
|
||||||
*/
|
|
||||||
|
|
||||||
function TCPServerSocket() {
|
|
||||||
this._localPort = 0;
|
|
||||||
this._binaryType = null;
|
|
||||||
|
|
||||||
this._onconnect = null;
|
|
||||||
this._onerror = null;
|
|
||||||
|
|
||||||
this._inChild = false;
|
|
||||||
this._neckoTCPServerSocket = null;
|
|
||||||
this._serverBridge = null;
|
|
||||||
this.useWin = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// When this API moves to WebIDL and these __exposedProps__ go away, remove
|
|
||||||
// this call here and remove the API from XPConnect.
|
|
||||||
Cu.skipCOWCallableChecks();
|
|
||||||
|
|
||||||
TCPServerSocket.prototype = {
|
|
||||||
__exposedProps__: {
|
|
||||||
localPort: 'r',
|
|
||||||
onconnect: 'rw',
|
|
||||||
onerror: 'rw'
|
|
||||||
},
|
|
||||||
get localPort() {
|
|
||||||
return this._localPort;
|
|
||||||
},
|
|
||||||
get onconnect() {
|
|
||||||
return this._onconnect;
|
|
||||||
},
|
|
||||||
set onconnect(f) {
|
|
||||||
this._onconnect = f;
|
|
||||||
},
|
|
||||||
get onerror() {
|
|
||||||
return this._onerror;
|
|
||||||
},
|
|
||||||
set onerror(f) {
|
|
||||||
this._onerror = f;
|
|
||||||
},
|
|
||||||
|
|
||||||
_callListenerAcceptCommon: function tss_callListenerAcceptCommon(socket) {
|
|
||||||
if (this._onconnect) {
|
|
||||||
try {
|
|
||||||
this["onconnect"].call(null, socket);
|
|
||||||
} catch (e) {
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
socket.close();
|
|
||||||
dump("Received unexpected connection!");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
init: function tss_init(aWindowObj) {
|
|
||||||
this.useWin = aWindowObj;
|
|
||||||
},
|
|
||||||
|
|
||||||
/* nsITCPServerSocketInternal method */
|
|
||||||
listen: function tss_listen(localPort, options, backlog) {
|
|
||||||
this._inChild = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
|
|
||||||
.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
|
|
||||||
this._binaryType = options.binaryType;
|
|
||||||
|
|
||||||
if (this._inChild) {
|
|
||||||
if (this._serverBridge == null) {
|
|
||||||
this._serverBridge = Cc["@mozilla.org/tcp-server-socket-child;1"]
|
|
||||||
.createInstance(Ci.nsITCPServerSocketChild);
|
|
||||||
this._serverBridge.listen(this, localPort, backlog, options.binaryType);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new Error("Child TCPServerSocket has already listening. \n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (this._neckoTCPServerSocket == null) {
|
|
||||||
this._neckoTCPServerSocket = new ServerSocket(localPort, false, backlog);
|
|
||||||
this._localPort = this._neckoTCPServerSocket.port;
|
|
||||||
this._neckoTCPServerSocket.asyncListen(this);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new Error("Parent TCPServerSocket has already listening. \n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
callListenerAccept: function tss_callListenerSocket(socketChild) {
|
|
||||||
// this method is called at child process when the socket is accepted at parent process.
|
|
||||||
let socket = TCPSocketInternal.createAcceptedChild(socketChild, this._binaryType, this.useWin);
|
|
||||||
this._callListenerAcceptCommon(socket);
|
|
||||||
},
|
|
||||||
|
|
||||||
callListenerError: function tss_callListenerError(message, filename, lineNumber, columnNumber) {
|
|
||||||
if (this._onerror) {
|
|
||||||
var type = "error";
|
|
||||||
var error = new Error(message, filename, lineNumber, columnNumber);
|
|
||||||
|
|
||||||
this["onerror"].call(null, new TCPSocketEvent(type, this, error));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/* end nsITCPServerSocketInternal method */
|
|
||||||
|
|
||||||
close: function tss_close() {
|
|
||||||
if (this._inChild) {
|
|
||||||
this._serverBridge.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Close ServerSocket */
|
|
||||||
if (this._neckoTCPServerSocket) {
|
|
||||||
this._neckoTCPServerSocket.close();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// nsIServerSocketListener (Triggered by _neckoTCPServerSocket.asyncListen)
|
|
||||||
onSocketAccepted: function tss_onSocketAccepted(server, trans) {
|
|
||||||
// precondition: this._inChild == false
|
|
||||||
try {
|
|
||||||
let that = TCPSocketInternal.createAcceptedParent(trans, this._binaryType,
|
|
||||||
this.useWin);
|
|
||||||
this._callListenerAcceptCommon(that);
|
|
||||||
}
|
|
||||||
catch(e) {
|
|
||||||
trans.close(Cr.NS_BINDING_ABORTED);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// nsIServerSocketListener (Triggered by _neckoTCPServerSocket.asyncListen)
|
|
||||||
onStopListening: function tss_onStopListening(server, status) {
|
|
||||||
if (status != Cr.NS_BINDING_ABORTED) {
|
|
||||||
throw new Error("Server socket was closed by unexpected reason.");
|
|
||||||
}
|
|
||||||
this._neckoTCPServerSocket = null;
|
|
||||||
},
|
|
||||||
|
|
||||||
classID: Components.ID("{73065eae-27dc-11e2-895a-000c29987aa2}"),
|
|
||||||
|
|
||||||
classInfo: XPCOMUtils.generateCI({
|
|
||||||
classID: Components.ID("{73065eae-27dc-11e2-895a-000c29987aa2}"),
|
|
||||||
classDescription: "Server TCP Socket",
|
|
||||||
interfaces: [
|
|
||||||
Ci.nsIDOMTCPServerSocket,
|
|
||||||
Ci.nsISupportsWeakReference
|
|
||||||
],
|
|
||||||
flags: Ci.nsIClassInfo.DOM_OBJECT,
|
|
||||||
}),
|
|
||||||
|
|
||||||
QueryInterface: XPCOMUtils.generateQI([
|
|
||||||
Ci.nsIDOMTCPServerSocket,
|
|
||||||
Ci.nsITCPServerSocketInternal,
|
|
||||||
Ci.nsISupportsWeakReference
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TCPServerSocket]);
|
|
@ -6,10 +6,10 @@
|
|||||||
|
|
||||||
#include "TCPServerSocketChild.h"
|
#include "TCPServerSocketChild.h"
|
||||||
#include "TCPSocketChild.h"
|
#include "TCPSocketChild.h"
|
||||||
|
#include "TCPServerSocket.h"
|
||||||
#include "mozilla/net/NeckoChild.h"
|
#include "mozilla/net/NeckoChild.h"
|
||||||
#include "mozilla/dom/PBrowserChild.h"
|
#include "mozilla/dom/PBrowserChild.h"
|
||||||
#include "mozilla/dom/TabChild.h"
|
#include "mozilla/dom/TabChild.h"
|
||||||
#include "nsIDOMTCPSocket.h"
|
|
||||||
#include "nsJSUtils.h"
|
#include "nsJSUtils.h"
|
||||||
#include "jsfriendapi.h"
|
#include "jsfriendapi.h"
|
||||||
|
|
||||||
@ -23,7 +23,6 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(TCPServerSocketChildBase)
|
|||||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(TCPServerSocketChildBase)
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(TCPServerSocketChildBase)
|
||||||
|
|
||||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPServerSocketChildBase)
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPServerSocketChildBase)
|
||||||
NS_INTERFACE_MAP_ENTRY(nsITCPServerSocketChild)
|
|
||||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||||
NS_INTERFACE_MAP_END
|
NS_INTERFACE_MAP_END
|
||||||
|
|
||||||
@ -46,18 +45,12 @@ NS_IMETHODIMP_(MozExternalRefCountType) TCPServerSocketChild::Release(void)
|
|||||||
return refcnt;
|
return refcnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
TCPServerSocketChild::TCPServerSocketChild()
|
TCPServerSocketChild::TCPServerSocketChild(TCPServerSocket* aServerSocket, uint16_t aLocalPort,
|
||||||
{
|
uint16_t aBacklog, bool aUseArrayBuffers)
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
TCPServerSocketChild::Listen(nsITCPServerSocketInternal* aServerSocket, uint16_t aLocalPort,
|
|
||||||
uint16_t aBacklog, const nsAString & aBinaryType, JSContext* aCx)
|
|
||||||
{
|
{
|
||||||
mServerSocket = aServerSocket;
|
mServerSocket = aServerSocket;
|
||||||
AddIPDLReference();
|
AddIPDLReference();
|
||||||
gNeckoChild->SendPTCPServerSocketConstructor(this, aLocalPort, aBacklog, nsString(aBinaryType));
|
gNeckoChild->SendPTCPServerSocketConstructor(this, aLocalPort, aBacklog, aUseArrayBuffers);
|
||||||
return NS_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -83,34 +76,16 @@ TCPServerSocketChild::~TCPServerSocketChild()
|
|||||||
bool
|
bool
|
||||||
TCPServerSocketChild::RecvCallbackAccept(PTCPSocketChild *psocket)
|
TCPServerSocketChild::RecvCallbackAccept(PTCPSocketChild *psocket)
|
||||||
{
|
{
|
||||||
TCPSocketChild* socket = static_cast<TCPSocketChild*>(psocket);
|
nsRefPtr<TCPSocketChild> socket = static_cast<TCPSocketChild*>(psocket);
|
||||||
|
nsresult rv = mServerSocket->AcceptChildSocket(socket);
|
||||||
nsresult rv = mServerSocket->CallListenerAccept(static_cast<nsITCPSocketChild*>(socket));
|
NS_ENSURE_SUCCESS(rv, true);
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
NS_WARNING("CallListenerAccept threw exception.");
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
TCPServerSocketChild::RecvCallbackError(const nsString& aMessage,
|
|
||||||
const nsString& aFilename,
|
|
||||||
const uint32_t& aLineNumber,
|
|
||||||
const uint32_t& aColumnNumber)
|
|
||||||
{
|
|
||||||
nsresult rv = mServerSocket->CallListenerError(aMessage, aFilename,
|
|
||||||
aLineNumber, aColumnNumber);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
NS_WARNING("CallListenerError threw exception.");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
TCPServerSocketChild::Close()
|
TCPServerSocketChild::Close()
|
||||||
{
|
{
|
||||||
SendClose();
|
SendClose();
|
||||||
return NS_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
|
@ -4,8 +4,10 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
* 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/. */
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_TCPServerSocketChild_h
|
||||||
|
#define mozilla_dom_TCPServerSocketChild_h
|
||||||
|
|
||||||
#include "mozilla/net/PTCPServerSocketChild.h"
|
#include "mozilla/net/PTCPServerSocketChild.h"
|
||||||
#include "nsITCPServerSocketChild.h"
|
|
||||||
#include "nsCycleCollectionParticipant.h"
|
#include "nsCycleCollectionParticipant.h"
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
|
|
||||||
@ -17,7 +19,9 @@ class nsITCPServerSocketInternal;
|
|||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
|
||||||
class TCPServerSocketChildBase : public nsITCPServerSocketChild {
|
class TCPServerSocket;
|
||||||
|
|
||||||
|
class TCPServerSocketChildBase : public nsISupports {
|
||||||
public:
|
public:
|
||||||
NS_DECL_CYCLE_COLLECTION_CLASS(TCPServerSocketChildBase)
|
NS_DECL_CYCLE_COLLECTION_CLASS(TCPServerSocketChildBase)
|
||||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||||
@ -29,7 +33,7 @@ protected:
|
|||||||
TCPServerSocketChildBase();
|
TCPServerSocketChildBase();
|
||||||
virtual ~TCPServerSocketChildBase();
|
virtual ~TCPServerSocketChildBase();
|
||||||
|
|
||||||
nsCOMPtr<nsITCPServerSocketInternal> mServerSocket;
|
nsRefPtr<TCPServerSocket> mServerSocket;
|
||||||
bool mIPCOpen;
|
bool mIPCOpen;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -37,18 +41,18 @@ class TCPServerSocketChild : public mozilla::net::PTCPServerSocketChild
|
|||||||
, public TCPServerSocketChildBase
|
, public TCPServerSocketChildBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NS_DECL_NSITCPSERVERSOCKETCHILD
|
|
||||||
NS_IMETHOD_(MozExternalRefCountType) Release() override;
|
NS_IMETHOD_(MozExternalRefCountType) Release() override;
|
||||||
|
|
||||||
TCPServerSocketChild();
|
TCPServerSocketChild(TCPServerSocket* aServerSocket, uint16_t aLocalPort,
|
||||||
|
uint16_t aBacklog, bool aUseArrayBuffers);
|
||||||
~TCPServerSocketChild();
|
~TCPServerSocketChild();
|
||||||
|
|
||||||
|
void Close();
|
||||||
|
|
||||||
virtual bool RecvCallbackAccept(PTCPSocketChild *socket) override;
|
virtual bool RecvCallbackAccept(PTCPSocketChild *socket) override;
|
||||||
virtual bool RecvCallbackError(const nsString& aMessage,
|
|
||||||
const nsString& aFilename,
|
|
||||||
const uint32_t& aLineNumber,
|
|
||||||
const uint32_t& aColumnNumber) override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_dom_TCPServerSocketChild_h
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user