2014-09-20 10:20:41 +04:00
|
|
|
/* 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/MediaDevices.h"
|
2020-11-23 19:21:38 +03:00
|
|
|
|
|
|
|
#include "mozilla/dom/Document.h"
|
2014-09-20 10:20:41 +04:00
|
|
|
#include "mozilla/dom/MediaStreamBinding.h"
|
2016-02-17 00:55:33 +03:00
|
|
|
#include "mozilla/dom/MediaDeviceInfo.h"
|
2014-09-20 10:20:41 +04:00
|
|
|
#include "mozilla/dom/MediaDevicesBinding.h"
|
2018-07-28 07:40:26 +03:00
|
|
|
#include "mozilla/dom/NavigatorBinding.h"
|
2014-09-20 10:20:41 +04:00
|
|
|
#include "mozilla/dom/Promise.h"
|
2020-12-10 01:19:01 +03:00
|
|
|
#include "mozilla/dom/WindowContext.h"
|
2014-09-20 10:20:41 +04:00
|
|
|
#include "mozilla/MediaManager.h"
|
2015-07-02 21:21:49 +03:00
|
|
|
#include "MediaTrackConstraints.h"
|
2017-09-19 04:56:48 +03:00
|
|
|
#include "nsContentUtils.h"
|
2017-07-26 21:18:20 +03:00
|
|
|
#include "nsINamed.h"
|
2014-09-20 10:20:41 +04:00
|
|
|
#include "nsIScriptGlobalObject.h"
|
|
|
|
#include "nsPIDOMWindow.h"
|
2015-04-15 19:47:03 +03:00
|
|
|
#include "nsQueryObject.h"
|
2014-09-20 10:20:41 +04:00
|
|
|
|
2017-01-26 19:08:06 +03:00
|
|
|
#define DEVICECHANGE_HOLD_TIME_IN_MS 1000
|
|
|
|
|
2020-11-04 20:04:01 +03:00
|
|
|
namespace mozilla::dom {
|
2014-09-20 10:20:41 +04:00
|
|
|
|
2019-10-29 16:01:54 +03:00
|
|
|
MediaDevices::~MediaDevices() {
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
if (mFuzzTimer) {
|
|
|
|
mFuzzTimer->Cancel();
|
2017-07-26 21:18:20 +03:00
|
|
|
}
|
2019-10-29 16:01:54 +03:00
|
|
|
mDeviceChangeListener.DisconnectIfExists();
|
|
|
|
}
|
2016-11-21 09:59:51 +03:00
|
|
|
|
2014-09-20 10:20:41 +04:00
|
|
|
already_AddRefed<Promise> MediaDevices::GetUserMedia(
|
|
|
|
const MediaStreamConstraints& aConstraints, CallerType aCallerType,
|
|
|
|
ErrorResult& aRv) {
|
2020-11-17 07:48:32 +03:00
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
// Get the relevant global for the promise from the wrapper cache because
|
|
|
|
// DOMEventTargetHelper::GetOwner() returns null if the document is unloaded.
|
|
|
|
// We know the wrapper exists because it is being used for |this| from JS.
|
|
|
|
// See https://github.com/heycam/webidl/issues/932 for why the relevant
|
|
|
|
// global is used instead of the current global.
|
|
|
|
nsCOMPtr<nsIGlobalObject> global = xpc::NativeGlobal(GetWrapper());
|
|
|
|
// global is a window because MediaDevices is exposed only to Window.
|
|
|
|
nsCOMPtr<nsPIDOMWindowInner> owner = do_QueryInterface(global);
|
|
|
|
if (Document* doc = owner->GetExtantDoc()) {
|
|
|
|
if (!owner->IsSecureContext()) {
|
|
|
|
doc->SetUseCounter(eUseCounter_custom_GetUserMediaInsec);
|
|
|
|
}
|
|
|
|
Document* topDoc = doc->GetTopLevelContentDocument();
|
|
|
|
IgnoredErrorResult ignored;
|
|
|
|
if (topDoc && !topDoc->HasFocus(ignored)) {
|
|
|
|
doc->SetUseCounter(eUseCounter_custom_GetUserMediaUnfocused);
|
2019-05-04 02:50:22 +03:00
|
|
|
}
|
|
|
|
}
|
2020-11-17 07:48:32 +03:00
|
|
|
RefPtr<Promise> p = Promise::Create(global, aRv);
|
2018-11-30 08:13:58 +03:00
|
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2020-11-17 07:04:25 +03:00
|
|
|
/* If requestedMediaTypes is the empty set, return a promise rejected with a
|
|
|
|
* TypeError. */
|
|
|
|
if (!MediaManager::IsOn(aConstraints.mVideo) &&
|
|
|
|
!MediaManager::IsOn(aConstraints.mAudio)) {
|
|
|
|
p->MaybeRejectWithTypeError("audio and/or video is required");
|
|
|
|
return p.forget();
|
|
|
|
}
|
|
|
|
/* If the relevant settings object's responsible document is NOT fully
|
|
|
|
* active, return a promise rejected with a DOMException object whose name
|
|
|
|
* attribute has the value "InvalidStateError". */
|
|
|
|
if (!owner->IsFullyActive()) {
|
|
|
|
p->MaybeRejectWithInvalidStateError("The document is not fully active.");
|
|
|
|
return p.forget();
|
|
|
|
}
|
2018-11-30 08:13:58 +03:00
|
|
|
RefPtr<MediaDevices> self(this);
|
|
|
|
MediaManager::Get()
|
2020-11-17 07:48:32 +03:00
|
|
|
->GetUserMedia(owner, aConstraints, aCallerType)
|
2018-11-30 08:13:58 +03:00
|
|
|
->Then(
|
2020-06-23 08:05:36 +03:00
|
|
|
GetCurrentSerialEventTarget(), __func__,
|
2018-11-30 08:13:58 +03:00
|
|
|
[this, self, p](RefPtr<DOMMediaStream>&& aStream) {
|
2018-11-30 08:15:54 +03:00
|
|
|
if (!GetWindowIfCurrent()) {
|
2018-11-30 08:13:58 +03:00
|
|
|
return; // Leave Promise pending after navigation by design.
|
|
|
|
}
|
|
|
|
p->MaybeResolve(std::move(aStream));
|
|
|
|
},
|
2018-11-30 08:15:54 +03:00
|
|
|
[this, self, p](const RefPtr<MediaMgrError>& error) {
|
|
|
|
nsPIDOMWindowInner* window = GetWindowIfCurrent();
|
|
|
|
if (!window) {
|
2018-11-30 08:13:58 +03:00
|
|
|
return; // Leave Promise pending after navigation by design.
|
|
|
|
}
|
2020-11-12 03:07:15 +03:00
|
|
|
error->Reject(p);
|
2018-11-30 08:13:58 +03:00
|
|
|
});
|
2014-09-20 10:20:41 +04:00
|
|
|
return p.forget();
|
|
|
|
}
|
|
|
|
|
2017-09-18 04:52:06 +03:00
|
|
|
already_AddRefed<Promise> MediaDevices::EnumerateDevices(CallerType aCallerType,
|
|
|
|
ErrorResult& aRv) {
|
2018-11-30 08:14:05 +03:00
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
2020-11-17 07:48:32 +03:00
|
|
|
nsCOMPtr<nsIGlobalObject> global = xpc::NativeGlobal(GetWrapper());
|
|
|
|
nsCOMPtr<nsPIDOMWindowInner> owner = do_QueryInterface(global);
|
|
|
|
if (Document* doc = owner->GetExtantDoc()) {
|
|
|
|
if (!owner->IsSecureContext()) {
|
|
|
|
doc->SetUseCounter(eUseCounter_custom_EnumerateDevicesInsec);
|
|
|
|
}
|
|
|
|
Document* topDoc = doc->GetTopLevelContentDocument();
|
|
|
|
IgnoredErrorResult ignored;
|
|
|
|
if (topDoc && !topDoc->HasFocus(ignored)) {
|
|
|
|
doc->SetUseCounter(eUseCounter_custom_EnumerateDevicesUnfocused);
|
2019-05-04 02:50:22 +03:00
|
|
|
}
|
|
|
|
}
|
2020-11-17 07:48:32 +03:00
|
|
|
RefPtr<Promise> p = Promise::Create(global, aRv);
|
2018-11-30 08:14:05 +03:00
|
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
RefPtr<MediaDevices> self(this);
|
|
|
|
MediaManager::Get()
|
2020-11-17 07:48:32 +03:00
|
|
|
->EnumerateDevices(owner, aCallerType)
|
2018-11-30 08:14:05 +03:00
|
|
|
->Then(
|
2020-06-23 08:05:36 +03:00
|
|
|
GetCurrentSerialEventTarget(), __func__,
|
2018-11-30 08:14:05 +03:00
|
|
|
[this, self,
|
|
|
|
p](RefPtr<MediaManager::MediaDeviceSetRefCnt>&& aDevices) {
|
2018-11-30 08:15:54 +03:00
|
|
|
nsPIDOMWindowInner* window = GetWindowIfCurrent();
|
|
|
|
if (!window) {
|
2018-11-30 08:14:05 +03:00
|
|
|
return; // Leave Promise pending after navigation by design.
|
|
|
|
}
|
2018-11-30 08:15:54 +03:00
|
|
|
auto windowId = window->WindowID();
|
2018-11-30 08:14:05 +03:00
|
|
|
nsTArray<RefPtr<MediaDeviceInfo>> infos;
|
2019-09-05 20:04:48 +03:00
|
|
|
bool allowLabel =
|
|
|
|
aDevices->Length() == 0 ||
|
|
|
|
MediaManager::Get()->IsActivelyCapturingOrHasAPermission(
|
|
|
|
windowId);
|
2018-11-30 08:15:34 +03:00
|
|
|
for (auto& device : *aDevices) {
|
2018-11-30 08:14:05 +03:00
|
|
|
MOZ_ASSERT(device->mKind == dom::MediaDeviceKind::Audioinput ||
|
|
|
|
device->mKind == dom::MediaDeviceKind::Videoinput ||
|
|
|
|
device->mKind == dom::MediaDeviceKind::Audiooutput);
|
|
|
|
// Include name only if page currently has a gUM stream active
|
|
|
|
// or persistent permissions (audio or video) have been granted
|
|
|
|
nsString label;
|
2019-09-05 20:04:48 +03:00
|
|
|
if (allowLabel ||
|
2018-11-30 08:14:05 +03:00
|
|
|
Preferences::GetBool("media.navigator.permission.disabled",
|
|
|
|
false)) {
|
|
|
|
label = device->mName;
|
|
|
|
}
|
|
|
|
infos.AppendElement(MakeRefPtr<MediaDeviceInfo>(
|
2019-03-08 14:51:45 +03:00
|
|
|
device->mID, device->mKind, label, device->mGroupID));
|
2018-11-30 08:14:05 +03:00
|
|
|
}
|
|
|
|
p->MaybeResolve(std::move(infos));
|
|
|
|
},
|
2018-11-30 08:15:54 +03:00
|
|
|
[this, self, p](const RefPtr<MediaMgrError>& error) {
|
|
|
|
nsPIDOMWindowInner* window = GetWindowIfCurrent();
|
|
|
|
if (!window) {
|
2018-11-30 08:14:05 +03:00
|
|
|
return; // Leave Promise pending after navigation by design.
|
|
|
|
}
|
2020-11-12 03:07:15 +03:00
|
|
|
error->Reject(p);
|
2018-11-30 08:14:05 +03:00
|
|
|
});
|
2015-03-03 17:51:05 +03:00
|
|
|
return p.forget();
|
|
|
|
}
|
|
|
|
|
2018-12-28 06:12:57 +03:00
|
|
|
already_AddRefed<Promise> MediaDevices::GetDisplayMedia(
|
|
|
|
const DisplayMediaStreamConstraints& aConstraints, CallerType aCallerType,
|
|
|
|
ErrorResult& aRv) {
|
2020-11-17 07:48:32 +03:00
|
|
|
nsCOMPtr<nsIGlobalObject> global = xpc::NativeGlobal(GetWrapper());
|
|
|
|
RefPtr<Promise> p = Promise::Create(global, aRv);
|
2018-12-28 06:12:57 +03:00
|
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2020-11-17 07:48:32 +03:00
|
|
|
nsCOMPtr<nsPIDOMWindowInner> owner = do_QueryInterface(global);
|
2020-11-17 07:04:25 +03:00
|
|
|
/* If the relevant global object of this does not have transient activation,
|
|
|
|
* return a promise rejected with a DOMException object whose name attribute
|
|
|
|
* has the value InvalidStateError. */
|
2020-12-10 01:19:01 +03:00
|
|
|
WindowContext* wc = owner->GetWindowContext();
|
|
|
|
if (!wc || !wc->HasValidTransientUserGestureActivation()) {
|
2020-11-17 07:04:25 +03:00
|
|
|
p->MaybeRejectWithInvalidStateError(
|
|
|
|
"getDisplayMedia must be called from a user gesture handler.");
|
|
|
|
return p.forget();
|
|
|
|
}
|
|
|
|
/* If constraints.video is false, return a promise rejected with a newly
|
|
|
|
* created TypeError. */
|
|
|
|
if (!MediaManager::IsOn(aConstraints.mVideo)) {
|
|
|
|
p->MaybeRejectWithTypeError("video is required");
|
|
|
|
return p.forget();
|
|
|
|
}
|
|
|
|
MediaStreamConstraints c;
|
|
|
|
auto& vc = c.mVideo.SetAsMediaTrackConstraints();
|
|
|
|
|
|
|
|
if (aConstraints.mVideo.IsMediaTrackConstraints()) {
|
|
|
|
vc = aConstraints.mVideo.GetAsMediaTrackConstraints();
|
|
|
|
/* If CS contains a member named advanced, return a promise rejected with
|
|
|
|
* a newly created TypeError. */
|
|
|
|
if (vc.mAdvanced.WasPassed()) {
|
|
|
|
p->MaybeRejectWithTypeError("advanced not allowed");
|
|
|
|
return p.forget();
|
|
|
|
}
|
|
|
|
auto getCLR = [](const auto& aCon) -> const ConstrainLongRange& {
|
|
|
|
static ConstrainLongRange empty;
|
|
|
|
return (aCon.WasPassed() && !aCon.Value().IsLong())
|
|
|
|
? aCon.Value().GetAsConstrainLongRange()
|
|
|
|
: empty;
|
|
|
|
};
|
|
|
|
auto getCDR = [](auto&& aCon) -> const ConstrainDoubleRange& {
|
|
|
|
static ConstrainDoubleRange empty;
|
|
|
|
return (aCon.WasPassed() && !aCon.Value().IsDouble())
|
|
|
|
? aCon.Value().GetAsConstrainDoubleRange()
|
|
|
|
: empty;
|
|
|
|
};
|
|
|
|
const auto& w = getCLR(vc.mWidth);
|
|
|
|
const auto& h = getCLR(vc.mHeight);
|
|
|
|
const auto& f = getCDR(vc.mFrameRate);
|
|
|
|
/* If CS contains a member whose name specifies a constrainable property
|
|
|
|
* applicable to display surfaces, and whose value in turn is a dictionary
|
|
|
|
* containing a member named either min or exact, return a promise
|
|
|
|
* rejected with a newly created TypeError. */
|
|
|
|
if (w.mMin.WasPassed() || h.mMin.WasPassed() || f.mMin.WasPassed()) {
|
|
|
|
p->MaybeRejectWithTypeError("min not allowed");
|
|
|
|
return p.forget();
|
|
|
|
}
|
|
|
|
if (w.mExact.WasPassed() || h.mExact.WasPassed() || f.mExact.WasPassed()) {
|
|
|
|
p->MaybeRejectWithTypeError("exact not allowed");
|
|
|
|
return p.forget();
|
|
|
|
}
|
|
|
|
/* If CS contains a member whose name, failedConstraint specifies a
|
|
|
|
* constrainable property, constraint, applicable to display surfaces, and
|
|
|
|
* whose value in turn is a dictionary containing a member named max, and
|
|
|
|
* that member's value in turn is less than the constrainable property's
|
|
|
|
* floor value, then let failedConstraint be the name of the constraint,
|
|
|
|
* let message be either undefined or an informative human-readable
|
|
|
|
* message, and return a promise rejected with a new OverconstrainedError
|
|
|
|
* created by calling OverconstrainedError(failedConstraint, message). */
|
|
|
|
// We fail early without incurring a prompt, on known-to-fail constraint
|
|
|
|
// values that don't reveal anything about the user's system.
|
|
|
|
const char* badConstraint = nullptr;
|
|
|
|
if (w.mMax.WasPassed() && w.mMax.Value() < 1) {
|
|
|
|
badConstraint = "width";
|
|
|
|
}
|
|
|
|
if (h.mMax.WasPassed() && h.mMax.Value() < 1) {
|
|
|
|
badConstraint = "height";
|
|
|
|
}
|
|
|
|
if (f.mMax.WasPassed() && f.mMax.Value() < 1) {
|
|
|
|
badConstraint = "frameRate";
|
|
|
|
}
|
|
|
|
if (badConstraint) {
|
|
|
|
p->MaybeReject(MakeRefPtr<dom::MediaStreamError>(
|
|
|
|
owner, *MakeRefPtr<MediaMgrError>(
|
|
|
|
MediaMgrError::Name::OverconstrainedError, "",
|
|
|
|
NS_ConvertASCIItoUTF16(badConstraint))));
|
|
|
|
return p.forget();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* If the relevant settings object's responsible document is NOT fully
|
|
|
|
* active, return a promise rejected with a DOMException object whose name
|
|
|
|
* attribute has the value "InvalidStateError". */
|
|
|
|
if (!owner->IsFullyActive()) {
|
|
|
|
p->MaybeRejectWithInvalidStateError("The document is not fully active.");
|
|
|
|
return p.forget();
|
|
|
|
}
|
|
|
|
// We ask for "screen" sharing.
|
|
|
|
//
|
|
|
|
// If this is a privileged call or permission is disabled, this gives us full
|
|
|
|
// screen sharing by default, which is useful for internal testing.
|
|
|
|
//
|
|
|
|
// If this is a non-priviliged call, GetUserMedia() will change it to "window"
|
|
|
|
// for us.
|
|
|
|
vc.mMediaSource.Reset();
|
|
|
|
vc.mMediaSource.Construct().AssignASCII(
|
|
|
|
dom::MediaSourceEnumValues::GetString(MediaSourceEnum::Screen));
|
|
|
|
|
2018-12-28 06:12:57 +03:00
|
|
|
RefPtr<MediaDevices> self(this);
|
|
|
|
MediaManager::Get()
|
2020-11-17 07:04:25 +03:00
|
|
|
->GetUserMedia(owner, c, aCallerType)
|
2018-12-28 06:12:57 +03:00
|
|
|
->Then(
|
2020-06-23 08:05:36 +03:00
|
|
|
GetCurrentSerialEventTarget(), __func__,
|
2018-12-28 06:12:57 +03:00
|
|
|
[this, self, p](RefPtr<DOMMediaStream>&& aStream) {
|
|
|
|
if (!GetWindowIfCurrent()) {
|
|
|
|
return; // leave promise pending after navigation.
|
|
|
|
}
|
|
|
|
p->MaybeResolve(std::move(aStream));
|
|
|
|
},
|
|
|
|
[this, self, p](RefPtr<MediaMgrError>&& error) {
|
|
|
|
nsPIDOMWindowInner* window = GetWindowIfCurrent();
|
|
|
|
if (!window) {
|
|
|
|
return; // leave promise pending after navigation.
|
|
|
|
}
|
2020-11-12 03:07:15 +03:00
|
|
|
error->Reject(p);
|
2018-12-28 06:12:57 +03:00
|
|
|
});
|
|
|
|
return p.forget();
|
|
|
|
}
|
|
|
|
|
2014-09-20 10:20:41 +04:00
|
|
|
NS_IMPL_ADDREF_INHERITED(MediaDevices, DOMEventTargetHelper)
|
|
|
|
NS_IMPL_RELEASE_INHERITED(MediaDevices, DOMEventTargetHelper)
|
|
|
|
NS_INTERFACE_MAP_BEGIN(MediaDevices)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(MediaDevices)
|
|
|
|
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
|
|
|
|
2016-08-11 20:04:49 +03:00
|
|
|
void MediaDevices::OnDeviceChange() {
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
2020-02-19 17:07:36 +03:00
|
|
|
if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
|
|
|
|
// This is a ghost window, don't do anything.
|
2016-08-11 20:04:49 +03:00
|
|
|
return;
|
2017-01-26 19:08:06 +03:00
|
|
|
}
|
2016-08-11 20:04:49 +03:00
|
|
|
|
|
|
|
if (!(MediaManager::Get()->IsActivelyCapturingOrHasAPermission(
|
|
|
|
GetOwner()->WindowID()) ||
|
|
|
|
Preferences::GetBool("media.navigator.permission.disabled", false))) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-09-19 04:56:48 +03:00
|
|
|
// Do not fire event to content script when
|
|
|
|
// privacy.resistFingerprinting is true.
|
|
|
|
if (nsContentUtils::ShouldResistFingerprinting()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-10-29 16:01:54 +03:00
|
|
|
if (mFuzzTimer) {
|
|
|
|
// An event is already in flight.
|
|
|
|
return;
|
2017-01-26 19:08:06 +03:00
|
|
|
}
|
|
|
|
|
2019-10-29 16:01:54 +03:00
|
|
|
mFuzzTimer = NS_NewTimer();
|
|
|
|
|
2017-01-26 19:08:06 +03:00
|
|
|
if (!mFuzzTimer) {
|
|
|
|
MOZ_ASSERT(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-10-29 16:01:54 +03:00
|
|
|
mFuzzTimer->InitWithNamedFuncCallback(
|
|
|
|
[](nsITimer*, void* aClosure) {
|
|
|
|
MediaDevices* md = static_cast<MediaDevices*>(aClosure);
|
|
|
|
md->DispatchTrustedEvent(u"devicechange"_ns);
|
|
|
|
md->mFuzzTimer = nullptr;
|
|
|
|
},
|
|
|
|
this, DEVICECHANGE_HOLD_TIME_IN_MS, nsITimer::TYPE_ONE_SHOT,
|
|
|
|
"MediaDevices::mFuzzTimer Callback");
|
2016-08-11 20:04:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
mozilla::dom::EventHandlerNonNull* MediaDevices::GetOndevicechange() {
|
2018-07-25 01:15:19 +03:00
|
|
|
return GetEventHandler(nsGkAtoms::ondevicechange);
|
2016-08-11 20:04:49 +03:00
|
|
|
}
|
|
|
|
|
Bug 1582637 - Move internal DeviceChange events to higher order functions. r=jib,achronop
This does three major things:
1) Moves the DeviceChange events from manual callbacks/listeners to
MediaEventSource/MediaEventListener. This is the reason this patch is so
large, as it traverses a lot of files.
There are four layers (from low to high):
- CamerasChild for camera device list changes, and CubebDeviceEnumerator for
microphone and speaker device list changes
- MediaEngineWebRTC, which gathers these into a single listener
- MediaManager, which owns the MediaEngineWebRTC backend
- MediaDevices, where the events from MediaManager are exposed to js
2) Changes the fake event triggering from starting a 30-event burst on setting
the js event listener, to a toggle, so that while the pref is on the events
keep coming.
3) Moves the fake event generation from CamerasChild to MediaEngineWebRTC, since
that's the lowest level where we are aware of both video and audio events.
The fake event generation is also greatly simplified. From being a dedicated
thread with periodic runnables, it is now a periodic timer on main thread
that fires while fake events are enabled. MediaEventProducer gracefully
handles thread safety.
Differential Revision: https://phabricator.services.mozilla.com/D48516
--HG--
extra : moz-landing-system : lando
2019-10-29 16:01:43 +03:00
|
|
|
void MediaDevices::SetupDeviceChangeListener() {
|
|
|
|
if (mIsDeviceChangeListenerSetUp) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsPIDOMWindowInner* window = GetOwner();
|
|
|
|
if (!window) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsISerialEventTarget* mainThread =
|
|
|
|
window->EventTargetFor(TaskCategory::Other);
|
|
|
|
if (!mainThread) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mDeviceChangeListener = MediaManager::Get()->DeviceListChangeEvent().Connect(
|
|
|
|
mainThread, this, &MediaDevices::OnDeviceChange);
|
|
|
|
mIsDeviceChangeListenerSetUp = true;
|
|
|
|
}
|
|
|
|
|
2016-08-11 20:04:49 +03:00
|
|
|
void MediaDevices::SetOndevicechange(
|
|
|
|
mozilla::dom::EventHandlerNonNull* aCallback) {
|
2018-07-25 01:15:19 +03:00
|
|
|
SetEventHandler(nsGkAtoms::ondevicechange, aCallback);
|
Bug 1582637 - Move internal DeviceChange events to higher order functions. r=jib,achronop
This does three major things:
1) Moves the DeviceChange events from manual callbacks/listeners to
MediaEventSource/MediaEventListener. This is the reason this patch is so
large, as it traverses a lot of files.
There are four layers (from low to high):
- CamerasChild for camera device list changes, and CubebDeviceEnumerator for
microphone and speaker device list changes
- MediaEngineWebRTC, which gathers these into a single listener
- MediaManager, which owns the MediaEngineWebRTC backend
- MediaDevices, where the events from MediaManager are exposed to js
2) Changes the fake event triggering from starting a 30-event burst on setting
the js event listener, to a toggle, so that while the pref is on the events
keep coming.
3) Moves the fake event generation from CamerasChild to MediaEngineWebRTC, since
that's the lowest level where we are aware of both video and audio events.
The fake event generation is also greatly simplified. From being a dedicated
thread with periodic runnables, it is now a periodic timer on main thread
that fires while fake events are enabled. MediaEventProducer gracefully
handles thread safety.
Differential Revision: https://phabricator.services.mozilla.com/D48516
--HG--
extra : moz-landing-system : lando
2019-10-29 16:01:43 +03:00
|
|
|
SetupDeviceChangeListener();
|
2016-08-11 20:04:49 +03:00
|
|
|
}
|
|
|
|
|
2018-04-05 20:42:42 +03:00
|
|
|
void MediaDevices::EventListenerAdded(nsAtom* aType) {
|
|
|
|
DOMEventTargetHelper::EventListenerAdded(aType);
|
Bug 1582637 - Move internal DeviceChange events to higher order functions. r=jib,achronop
This does three major things:
1) Moves the DeviceChange events from manual callbacks/listeners to
MediaEventSource/MediaEventListener. This is the reason this patch is so
large, as it traverses a lot of files.
There are four layers (from low to high):
- CamerasChild for camera device list changes, and CubebDeviceEnumerator for
microphone and speaker device list changes
- MediaEngineWebRTC, which gathers these into a single listener
- MediaManager, which owns the MediaEngineWebRTC backend
- MediaDevices, where the events from MediaManager are exposed to js
2) Changes the fake event triggering from starting a 30-event burst on setting
the js event listener, to a toggle, so that while the pref is on the events
keep coming.
3) Moves the fake event generation from CamerasChild to MediaEngineWebRTC, since
that's the lowest level where we are aware of both video and audio events.
The fake event generation is also greatly simplified. From being a dedicated
thread with periodic runnables, it is now a periodic timer on main thread
that fires while fake events are enabled. MediaEventProducer gracefully
handles thread safety.
Differential Revision: https://phabricator.services.mozilla.com/D48516
--HG--
extra : moz-landing-system : lando
2019-10-29 16:01:43 +03:00
|
|
|
SetupDeviceChangeListener();
|
2016-08-11 20:04:49 +03:00
|
|
|
}
|
|
|
|
|
Bug 1117172 part 3. Change the wrappercached WrapObject methods to allow passing in aGivenProto. r=peterv
The only manual changes here are to BindingUtils.h, BindingUtils.cpp,
Codegen.py, Element.cpp, IDBFileRequest.cpp, IDBObjectStore.cpp,
dom/workers/Navigator.cpp, WorkerPrivate.cpp, DeviceStorageRequestChild.cpp,
Notification.cpp, nsGlobalWindow.cpp, MessagePort.cpp, nsJSEnvironment.cpp,
Sandbox.cpp, XPCConvert.cpp, ExportHelpers.cpp, and DataStoreService.cpp. The
rest of this diff was generated by running the following commands:
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObjectInternal\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObjectInternal\((?:aCx|cx|aContext|aCtx|js))\)/\1, aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapNode\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapNode\((?:aCx|cx|aContext|aCtx|js))\)/\1, aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObject\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(Binding(?:_workers)?::Wrap\((?:aCx|cx|aContext|aCtx|js), [^,)]+)\)/\1, aGivenProto)/g'
2015-03-19 17:13:33 +03:00
|
|
|
JSObject* MediaDevices::WrapObject(JSContext* aCx,
|
|
|
|
JS::Handle<JSObject*> aGivenProto) {
|
2018-06-26 00:20:54 +03:00
|
|
|
return MediaDevices_Binding::Wrap(aCx, this, aGivenProto);
|
2014-09-20 10:20:41 +04:00
|
|
|
}
|
|
|
|
|
2020-11-04 20:04:01 +03:00
|
|
|
} // namespace mozilla::dom
|