зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1769009
- Refresh the list of MIDI devices both when navigating to a new page and away from an old one r=padenot
Differential Revision: https://phabricator.services.mozilla.com/D146564
This commit is contained in:
Родитель
1f362db13a
Коммит
874e321393
|
@ -205,6 +205,11 @@ void MIDIAccess::MaybeCreateMIDIPort(const MIDIPortInfo& aInfo,
|
||||||
// request removal from MIDIAccess's maps.
|
// request removal from MIDIAccess's maps.
|
||||||
void MIDIAccess::Notify(const MIDIPortList& aEvent) {
|
void MIDIAccess::Notify(const MIDIPortList& aEvent) {
|
||||||
LOG("MIDIAcess::Notify");
|
LOG("MIDIAcess::Notify");
|
||||||
|
if (!GetOwner()) {
|
||||||
|
// Do nothing if we've already been disconnected from the document.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& port : aEvent.ports()) {
|
for (const auto& port : aEvent.ports()) {
|
||||||
// Something went very wrong. Warn and return.
|
// Something went very wrong. Warn and return.
|
||||||
ErrorResult rv;
|
ErrorResult rv;
|
||||||
|
@ -237,6 +242,7 @@ void MIDIAccess::DisconnectFromOwner() {
|
||||||
IgnoreKeepAliveIfHasListenersFor(nsGkAtoms::onstatechange);
|
IgnoreKeepAliveIfHasListenersFor(nsGkAtoms::onstatechange);
|
||||||
|
|
||||||
DOMEventTargetHelper::DisconnectFromOwner();
|
DOMEventTargetHelper::DisconnectFromOwner();
|
||||||
|
MIDIAccessManager::Get()->SendRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mozilla::dom
|
} // namespace mozilla::dom
|
||||||
|
|
|
@ -98,6 +98,8 @@ bool MIDIAccessManager::AddObserver(Observer<MIDIPortList>* aObserver) {
|
||||||
// Add a ref to mChild here, that will be deref'd by
|
// Add a ref to mChild here, that will be deref'd by
|
||||||
// BackgroundChildImpl::DeallocPMIDIManagerChild on IPC cleanup.
|
// BackgroundChildImpl::DeallocPMIDIManagerChild on IPC cleanup.
|
||||||
mChild->SetActorAlive();
|
mChild->SetActorAlive();
|
||||||
|
} else {
|
||||||
|
mChild->SendRefresh();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -115,6 +117,12 @@ void MIDIAccessManager::RemoveObserver(Observer<MIDIPortList>* aObserver) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MIDIAccessManager::SendRefresh() {
|
||||||
|
if (mChild) {
|
||||||
|
mChild->SendRefresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MIDIAccessManager::CreateMIDIAccess(nsPIDOMWindowInner* aWindow,
|
void MIDIAccessManager::CreateMIDIAccess(nsPIDOMWindowInner* aWindow,
|
||||||
bool aNeedsSysex, Promise* aPromise) {
|
bool aNeedsSysex, Promise* aPromise) {
|
||||||
MOZ_ASSERT(aWindow);
|
MOZ_ASSERT(aWindow);
|
||||||
|
|
|
@ -50,6 +50,8 @@ class MIDIAccessManager final {
|
||||||
bool AddObserver(Observer<MIDIPortList>* aObserver);
|
bool AddObserver(Observer<MIDIPortList>* aObserver);
|
||||||
// Removes a device update observer (usually a MIDIAccess object)
|
// Removes a device update observer (usually a MIDIAccess object)
|
||||||
void RemoveObserver(Observer<MIDIPortList>* aObserver);
|
void RemoveObserver(Observer<MIDIPortList>* aObserver);
|
||||||
|
// Requests the service to update the list of devices
|
||||||
|
void SendRefresh();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MIDIAccessManager();
|
MIDIAccessManager();
|
||||||
|
|
|
@ -17,6 +17,11 @@ void MIDIManagerParent::Teardown() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult MIDIManagerParent::RecvRefresh() {
|
||||||
|
MIDIPlatformService::Get()->Refresh();
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
mozilla::ipc::IPCResult MIDIManagerParent::RecvShutdown() {
|
mozilla::ipc::IPCResult MIDIManagerParent::RecvShutdown() {
|
||||||
Teardown();
|
Teardown();
|
||||||
Unused << Send__delete__(this);
|
Unused << Send__delete__(this);
|
||||||
|
|
|
@ -21,6 +21,7 @@ class MIDIManagerParent final : public PMIDIManagerParent {
|
||||||
public:
|
public:
|
||||||
NS_INLINE_DECL_REFCOUNTING(MIDIManagerParent);
|
NS_INLINE_DECL_REFCOUNTING(MIDIManagerParent);
|
||||||
MIDIManagerParent() = default;
|
MIDIManagerParent() = default;
|
||||||
|
mozilla::ipc::IPCResult RecvRefresh();
|
||||||
mozilla::ipc::IPCResult RecvShutdown();
|
mozilla::ipc::IPCResult RecvShutdown();
|
||||||
void Teardown();
|
void Teardown();
|
||||||
void ActorDestroy(ActorDestroyReason aWhy) override;
|
void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||||
|
|
|
@ -54,6 +54,9 @@ class MIDIPlatformService {
|
||||||
// Platform specific init function.
|
// Platform specific init function.
|
||||||
virtual void Init() = 0;
|
virtual void Init() = 0;
|
||||||
|
|
||||||
|
// Forces the implementation to refresh the port list.
|
||||||
|
virtual void Refresh() = 0;
|
||||||
|
|
||||||
// Platform specific MIDI port opening function.
|
// Platform specific MIDI port opening function.
|
||||||
virtual void Open(MIDIPortParent* aPort) = 0;
|
virtual void Open(MIDIPortParent* aPort) = 0;
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ async protocol PMIDIManager
|
||||||
{
|
{
|
||||||
manager PBackground;
|
manager PBackground;
|
||||||
parent:
|
parent:
|
||||||
|
async Refresh();
|
||||||
async Shutdown();
|
async Shutdown();
|
||||||
child:
|
child:
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -114,6 +114,8 @@ void TestMIDIPlatformService::Init() {
|
||||||
NS_DispatchToCurrentThread(r);
|
NS_DispatchToCurrentThread(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestMIDIPlatformService::Refresh() { AddPortInfo(mStateTestInputPort); }
|
||||||
|
|
||||||
void TestMIDIPlatformService::Open(MIDIPortParent* aPort) {
|
void TestMIDIPlatformService::Open(MIDIPortParent* aPort) {
|
||||||
MOZ_ASSERT(aPort);
|
MOZ_ASSERT(aPort);
|
||||||
MIDIPortConnectionState s = MIDIPortConnectionState::Open;
|
MIDIPortConnectionState s = MIDIPortConnectionState::Open;
|
||||||
|
|
|
@ -26,6 +26,7 @@ class TestMIDIPlatformService : public MIDIPlatformService {
|
||||||
public:
|
public:
|
||||||
TestMIDIPlatformService();
|
TestMIDIPlatformService();
|
||||||
virtual void Init() override;
|
virtual void Init() override;
|
||||||
|
virtual void Refresh() override;
|
||||||
virtual void Open(MIDIPortParent* aPort) override;
|
virtual void Open(MIDIPortParent* aPort) override;
|
||||||
virtual void Stop() override;
|
virtual void Stop() override;
|
||||||
virtual void ScheduleSend(const nsAString& aPort) override;
|
virtual void ScheduleSend(const nsAString& aPort) override;
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "mozilla/dom/MIDIPortParent.h"
|
#include "mozilla/dom/MIDIPortParent.h"
|
||||||
#include "mozilla/dom/MIDIPlatformRunnables.h"
|
#include "mozilla/dom/MIDIPlatformRunnables.h"
|
||||||
#include "mozilla/dom/MIDIUtils.h"
|
#include "mozilla/dom/MIDIUtils.h"
|
||||||
|
#include "mozilla/dom/midi/midir_impl_ffi_generated.h"
|
||||||
#include "mozilla/ipc/BackgroundParent.h"
|
#include "mozilla/ipc/BackgroundParent.h"
|
||||||
#include "mozilla/Unused.h"
|
#include "mozilla/Unused.h"
|
||||||
#include "nsIThread.h"
|
#include "nsIThread.h"
|
||||||
|
@ -78,6 +79,14 @@ void midirMIDIPlatformService::AddPort(const nsString* aId,
|
||||||
MIDIPlatformService::Get()->AddPortInfo(port);
|
MIDIPlatformService::Get()->AddPortInfo(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void midirMIDIPlatformService::RemovePort(const nsString* aId,
|
||||||
|
const nsString* aName, bool aInput) {
|
||||||
|
MIDIPortType type = aInput ? MIDIPortType::Input : MIDIPortType::Output;
|
||||||
|
MIDIPortInfo port(*aId, *aName, u""_ns, u""_ns, static_cast<uint32_t>(type));
|
||||||
|
MIDIPlatformService::Get()->RemovePortInfo(port);
|
||||||
|
}
|
||||||
|
|
||||||
void midirMIDIPlatformService::Init() {
|
void midirMIDIPlatformService::Init() {
|
||||||
if (mImplementation) {
|
if (mImplementation) {
|
||||||
return;
|
return;
|
||||||
|
@ -117,6 +126,10 @@ void midirMIDIPlatformService::CheckAndReceive(const nsString* aId,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void midirMIDIPlatformService::Refresh() {
|
||||||
|
midir_impl_refresh(mImplementation, AddPort, RemovePort);
|
||||||
|
}
|
||||||
|
|
||||||
void midirMIDIPlatformService::Open(MIDIPortParent* aPort) {
|
void midirMIDIPlatformService::Open(MIDIPortParent* aPort) {
|
||||||
MOZ_ASSERT(aPort);
|
MOZ_ASSERT(aPort);
|
||||||
nsString id = aPort->MIDIPortInterface::Id();
|
nsString id = aPort->MIDIPortInterface::Id();
|
||||||
|
|
|
@ -26,6 +26,7 @@ class midirMIDIPlatformService : public MIDIPlatformService {
|
||||||
public:
|
public:
|
||||||
midirMIDIPlatformService();
|
midirMIDIPlatformService();
|
||||||
virtual void Init() override;
|
virtual void Init() override;
|
||||||
|
virtual void Refresh() override;
|
||||||
virtual void Open(MIDIPortParent* aPort) override;
|
virtual void Open(MIDIPortParent* aPort) override;
|
||||||
virtual void Stop() override;
|
virtual void Stop() override;
|
||||||
virtual void ScheduleSend(const nsAString& aPort) override;
|
virtual void ScheduleSend(const nsAString& aPort) override;
|
||||||
|
@ -37,6 +38,8 @@ class midirMIDIPlatformService : public MIDIPlatformService {
|
||||||
virtual ~midirMIDIPlatformService();
|
virtual ~midirMIDIPlatformService();
|
||||||
|
|
||||||
static void AddPort(const nsString* aId, const nsString* aName, bool aInput);
|
static void AddPort(const nsString* aId, const nsString* aName, bool aInput);
|
||||||
|
static void RemovePort(const nsString* aId, const nsString* aName,
|
||||||
|
bool aInput);
|
||||||
static void CheckAndReceive(const nsString* aId, const uint8_t* aData,
|
static void CheckAndReceive(const nsString* aId, const uint8_t* aData,
|
||||||
size_t aLength, const GeckoTimeStamp* aTimeStamp,
|
size_t aLength, const GeckoTimeStamp* aTimeStamp,
|
||||||
uint64_t aMicros);
|
uint64_t aMicros);
|
||||||
|
|
|
@ -55,6 +55,15 @@ struct MidiPortWrapper {
|
||||||
open_count: u32,
|
open_count: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MidiPortWrapper {
|
||||||
|
fn input(self: &MidiPortWrapper) -> bool {
|
||||||
|
match self.port {
|
||||||
|
MidiPort::Input(_) => true,
|
||||||
|
MidiPort::Output(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct MidirWrapper {
|
pub struct MidirWrapper {
|
||||||
ports: Vec<MidiPortWrapper>,
|
ports: Vec<MidiPortWrapper>,
|
||||||
connections: Vec<MidiConnectionWrapper>,
|
connections: Vec<MidiConnectionWrapper>,
|
||||||
|
@ -66,6 +75,43 @@ struct CallbackData {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MidirWrapper {
|
impl MidirWrapper {
|
||||||
|
fn refresh(
|
||||||
|
self: &mut MidirWrapper,
|
||||||
|
add_callback: unsafe extern "C" fn(id: &nsString, name: &nsString, input: bool),
|
||||||
|
remove_callback: unsafe extern "C" fn(id: &nsString, name: &nsString, input: bool),
|
||||||
|
) {
|
||||||
|
if let Ok(ports) = collect_ports() {
|
||||||
|
let old_ports = &mut self.ports;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < old_ports.len() {
|
||||||
|
if !ports
|
||||||
|
.iter()
|
||||||
|
.any(|p| p.name == old_ports[i].name && p.input() == old_ports[i].input())
|
||||||
|
{
|
||||||
|
let port = old_ports.remove(i);
|
||||||
|
let id = nsString::from(&port.id);
|
||||||
|
let name = nsString::from(&port.name);
|
||||||
|
unsafe { remove_callback(&id, &name, port.input()) };
|
||||||
|
} else {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for port in ports {
|
||||||
|
if !self
|
||||||
|
.ports
|
||||||
|
.iter()
|
||||||
|
.any(|p| p.name == port.name && p.input() == port.input())
|
||||||
|
{
|
||||||
|
let id = nsString::from(&port.id);
|
||||||
|
let name = nsString::from(&port.name);
|
||||||
|
unsafe { add_callback(&id, &name, port.input()) };
|
||||||
|
self.ports.push(port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn open_port(
|
fn open_port(
|
||||||
self: &mut MidirWrapper,
|
self: &mut MidirWrapper,
|
||||||
nsid: &nsString,
|
nsid: &nsString,
|
||||||
|
@ -106,22 +152,20 @@ impl MidirWrapper {
|
||||||
data,
|
data,
|
||||||
)
|
)
|
||||||
.map_err(|_err| ())?;
|
.map_err(|_err| ())?;
|
||||||
let connection_wrapper = MidiConnectionWrapper {
|
MidiConnectionWrapper {
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
connection: MidiConnection::Input(connection),
|
connection: MidiConnection::Input(connection),
|
||||||
};
|
}
|
||||||
connection_wrapper
|
|
||||||
}
|
}
|
||||||
MidiPort::Output(port) => {
|
MidiPort::Output(port) => {
|
||||||
let output = MidiOutput::new("WebMIDI output").map_err(|_err| ())?;
|
let output = MidiOutput::new("WebMIDI output").map_err(|_err| ())?;
|
||||||
let connection = output
|
let connection = output
|
||||||
.connect(port, "Output connection")
|
.connect(port, "Output connection")
|
||||||
.map_err(|_err| ())?;
|
.map_err(|_err| ())?;
|
||||||
let connection_wrapper = MidiConnectionWrapper {
|
MidiConnectionWrapper {
|
||||||
connection: MidiConnection::Output(connection),
|
connection: MidiConnection::Output(connection),
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
};
|
}
|
||||||
connection_wrapper
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -175,13 +219,18 @@ impl MidirWrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn collect_ports() -> Result<Vec<MidiPortWrapper>, InitError> {
|
||||||
|
let input = MidiInput::new("WebMIDI input")?;
|
||||||
|
let output = MidiOutput::new("WebMIDI output")?;
|
||||||
|
let mut ports = Vec::<MidiPortWrapper>::new();
|
||||||
|
collect_input_ports(&input, &mut ports);
|
||||||
|
collect_output_ports(&output, &mut ports);
|
||||||
|
Ok(ports)
|
||||||
|
}
|
||||||
|
|
||||||
impl MidirWrapper {
|
impl MidirWrapper {
|
||||||
fn new() -> Result<MidirWrapper, InitError> {
|
fn new() -> Result<MidirWrapper, InitError> {
|
||||||
let input = MidiInput::new("WebMIDI input")?;
|
let ports = collect_ports()?;
|
||||||
let output = MidiOutput::new("WebMIDI output")?;
|
|
||||||
let mut ports: Vec<MidiPortWrapper> = Vec::new();
|
|
||||||
collect_input_ports(&input, &mut ports);
|
|
||||||
collect_output_ports(&output, &mut ports);
|
|
||||||
let connections: Vec<MidiConnectionWrapper> = Vec::new();
|
let connections: Vec<MidiConnectionWrapper> = Vec::new();
|
||||||
Ok(MidirWrapper { ports, connections })
|
Ok(MidirWrapper { ports, connections })
|
||||||
}
|
}
|
||||||
|
@ -210,6 +259,22 @@ pub unsafe extern "C" fn midir_impl_init(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Refresh the list of ports.
|
||||||
|
///
|
||||||
|
/// This function will be exposed to C++
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `wrapper` must be the pointer returned by [midir_impl_init()].
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn midir_impl_refresh(
|
||||||
|
wrapper: *mut MidirWrapper,
|
||||||
|
add_callback: unsafe extern "C" fn(id: &nsString, name: &nsString, input: bool),
|
||||||
|
remove_callback: unsafe extern "C" fn(id: &nsString, name: &nsString, input: bool),
|
||||||
|
) {
|
||||||
|
(*wrapper).refresh(add_callback, remove_callback)
|
||||||
|
}
|
||||||
|
|
||||||
/// Shutdown midir and free the C++ wrapper.
|
/// Shutdown midir and free the C++ wrapper.
|
||||||
///
|
///
|
||||||
/// This function will be exposed to C++
|
/// This function will be exposed to C++
|
||||||
|
|
|
@ -10,3 +10,8 @@ run-if = (os != 'android')
|
||||||
support-files =
|
support-files =
|
||||||
port_ids_page_1.html
|
port_ids_page_1.html
|
||||||
port_ids_page_2.html
|
port_ids_page_2.html
|
||||||
|
|
||||||
|
[browser_refresh_port_list.js]
|
||||||
|
run-if = (os != 'android')
|
||||||
|
support-files =
|
||||||
|
refresh_port_list.html
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
const EXAMPLE_ORG_URL = "https://example.org/browser/dom/midi/tests/";
|
||||||
|
const PAGE = "refresh_port_list.html";
|
||||||
|
|
||||||
|
async function get_port_num(browser) {
|
||||||
|
return SpecialPowers.spawn(gBrowser.selectedBrowser, [""], function() {
|
||||||
|
return content.wrappedJSObject.get_num_ports();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
add_task(async function() {
|
||||||
|
gBrowser.selectedTab = BrowserTestUtils.addTab(
|
||||||
|
gBrowser,
|
||||||
|
EXAMPLE_ORG_URL + PAGE
|
||||||
|
);
|
||||||
|
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||||
|
|
||||||
|
let ports_num = await get_port_num(gBrowser.selectedBrowser);
|
||||||
|
Assert.equal(ports_num, 4, "There are four ports");
|
||||||
|
|
||||||
|
const finished = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||||
|
gBrowser.reloadTab(gBrowser.selectedTab);
|
||||||
|
await finished;
|
||||||
|
|
||||||
|
let refreshed_ports_num = await get_port_num(gBrowser.selectedBrowser);
|
||||||
|
Assert.equal(refreshed_ports_num, 5, "There are five ports");
|
||||||
|
|
||||||
|
gBrowser.removeTab(gBrowser.selectedTab);
|
||||||
|
});
|
|
@ -0,0 +1,15 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Refresh MIDI port list test</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"></meta>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
async function get_num_ports() {
|
||||||
|
let access = await navigator.requestMIDIAccess({ sysex: false });
|
||||||
|
return access.inputs.size + access.outputs.size;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Загрузка…
Ссылка в новой задаче