Bug 1201598 - Add a midir-based implementation for WebMIDI r=padenot

Differential Revision: https://phabricator.services.mozilla.com/D131351
This commit is contained in:
Gabriele Svelto 2021-12-21 11:34:52 +00:00
Родитель 2793748f11
Коммит 97a5174c27
14 изменённых файлов: 633 добавлений и 6 удалений

11
Cargo.lock сгенерированный
Просмотреть файл

@ -2015,6 +2015,7 @@ dependencies = [
"mapped_hyph",
"mdns_service",
"midir",
"midir_impl",
"mozurl",
"mp4parse_capi",
"neqo_glue",
@ -3074,6 +3075,16 @@ dependencies = [
"winapi",
]
[[package]]
name = "midir_impl"
version = "0.1.0"
dependencies = [
"midir",
"nsstring",
"thin-vec",
"uuid",
]
[[package]]
name = "mime"
version = "0.3.16"

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

@ -101,6 +101,7 @@ chardetng = { git = "https://github.com/hsivonen/chardetng", rev="302c995f91f44c
chardetng_c = { git = "https://github.com/hsivonen/chardetng_c", rev="ed8a4c6f900a90d4dbc1d64b856e61490a1c3570" }
libudev-sys = { path = "dom/webauthn/libudev-sys" }
packed_simd = { git = "https://github.com/hsivonen/packed_simd", rev="8b4bd7d8229660a749dbe419a57ea01df9de5453" }
midir = { git = "https://github.com/mozilla/midir.git", rev = "dc87afbd4361ae5ec192e1fab0a6409dd13d4011" }
minidump_writer_linux = { git = "https://github.com/msirringhaus/minidump_writer_linux.git", rev = "029ac0d54b237f27dc7d8d4e51bc0fb076e5e852" }
# Patch mio 0.6 to use winapi 0.3 and miow 0.3, getting rid of winapi 0.2.

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

@ -7,6 +7,9 @@
#include "MIDIPlatformService.h"
#include "MIDIMessageQueue.h"
#include "TestMIDIPlatformService.h"
#ifndef MOZ_WIDGET_ANDROID
# include "midirMIDIPlatformService.h"
#endif // MOZ_WIDGET_ANDROID
#include "mozilla/ErrorResult.h"
#include "mozilla/StaticPrefs_midi.h"
#include "mozilla/StaticPtr.h"
@ -179,10 +182,14 @@ MIDIPlatformService* MIDIPlatformService::Get() {
MOZ_ASSERT(XRE_IsParentProcess());
::mozilla::ipc::AssertIsOnBackgroundThread();
if (!IsRunning()) {
// Uncomment once we have an actual platform library to test.
//
// if (StaticPrefs::midi_testing()) {
gMIDIPlatformService = new TestMIDIPlatformService();
if (StaticPrefs::midi_testing()) {
gMIDIPlatformService = new TestMIDIPlatformService();
}
#ifndef MOZ_WIDGET_ANDROID
else {
gMIDIPlatformService = new midirMIDIPlatformService();
}
#endif // MOZ_WIDGET_ANDROID
gMIDIPlatformService->Init();
}
return gMIDIPlatformService;

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

@ -0,0 +1,152 @@
/* 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 "midirMIDIPlatformService.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/dom/MIDIPort.h"
#include "mozilla/dom/MIDITypes.h"
#include "mozilla/dom/MIDIPortInterface.h"
#include "mozilla/dom/MIDIPortParent.h"
#include "mozilla/dom/MIDIPlatformRunnables.h"
#include "mozilla/dom/MIDIUtils.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/Unused.h"
#include "nsIThread.h"
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::ipc;
static_assert(sizeof(TimeStamp) == sizeof(GeckoTimeStamp));
/**
* Runnable used for to send messages asynchronously on the I/O thread.
*/
class SendRunnable : public MIDIBackgroundRunnable {
public:
explicit SendRunnable(const nsAString& aPortID)
: MIDIBackgroundRunnable("SendRunnable"), mPortID(aPortID) {}
~SendRunnable() = default;
virtual void RunInternal() {
AssertIsOnBackgroundThread();
midirMIDIPlatformService* srv =
static_cast<midirMIDIPlatformService*>(MIDIPlatformService::Get());
srv->SendMessages(mPortID);
}
private:
nsString mPortID;
nsTArray<MIDIMessage> mMsgs;
};
// static
StaticMutex midirMIDIPlatformService::gBackgroundThreadMutex;
// static
nsCOMPtr<nsIThread> midirMIDIPlatformService::gBackgroundThread;
midirMIDIPlatformService::midirMIDIPlatformService()
: mIsInitialized(false), mImplementation(nullptr) {
StaticMutexAutoLock lock(gBackgroundThreadMutex);
gBackgroundThread = NS_GetCurrentThread();
}
midirMIDIPlatformService::~midirMIDIPlatformService() {
midir_impl_shutdown(mImplementation);
StaticMutexAutoLock lock(gBackgroundThreadMutex);
gBackgroundThread = nullptr;
}
// static
void midirMIDIPlatformService::AddPort(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()->AddPortInfo(port);
}
void midirMIDIPlatformService::Init() {
if (mIsInitialized) {
return;
}
mImplementation = midir_impl_init();
if (mImplementation) {
mIsInitialized = true;
midir_impl_enum_ports(mImplementation, AddPort);
MIDIPlatformService::Get()->SendPortList();
}
}
// static
void midirMIDIPlatformService::CheckAndReceive(const nsString* aId,
const uint8_t* aData,
size_t aLength,
const GeckoTimeStamp* aTimeStamp,
uint64_t aMicros) {
nsTArray<uint8_t> data;
for (size_t i = 0; i < aLength; i++) {
data.AppendElement(aData[i]);
}
const TimeStamp* openTime = reinterpret_cast<const TimeStamp*>(aTimeStamp);
TimeStamp timestamp =
*openTime + TimeDuration::FromMicroseconds(static_cast<double>(aMicros));
MIDIMessage message(data, timestamp);
nsTArray<MIDIMessage> messages;
messages.AppendElement(message);
nsCOMPtr<nsIRunnable> r(new ReceiveRunnable(*aId, messages));
StaticMutexAutoLock lock(gBackgroundThreadMutex);
if (gBackgroundThread) {
gBackgroundThread->Dispatch(r, NS_DISPATCH_NORMAL);
}
}
void midirMIDIPlatformService::Open(MIDIPortParent* aPort) {
MOZ_ASSERT(aPort);
nsString id = aPort->MIDIPortInterface::Id();
TimeStamp openTimeStamp = TimeStamp::Now();
if (midir_impl_open_port(mImplementation, &id,
reinterpret_cast<GeckoTimeStamp*>(&openTimeStamp),
CheckAndReceive)) {
nsCOMPtr<nsIRunnable> r(new SetStatusRunnable(
aPort->MIDIPortInterface::Id(), aPort->DeviceState(),
MIDIPortConnectionState::Open));
NS_DispatchToCurrentThread(r);
}
}
void midirMIDIPlatformService::Stop() {
// Nothing to do here AFAIK
}
void midirMIDIPlatformService::ScheduleSend(const nsAString& aPortId) {
nsCOMPtr<nsIRunnable> r(new SendRunnable(aPortId));
StaticMutexAutoLock lock(gBackgroundThreadMutex);
if (gBackgroundThread) {
gBackgroundThread->Dispatch(r, NS_DISPATCH_NORMAL);
}
}
void midirMIDIPlatformService::ScheduleClose(MIDIPortParent* aPort) {
MOZ_ASSERT(aPort);
nsString id = aPort->MIDIPortInterface::Id();
if (aPort->ConnectionState() == MIDIPortConnectionState::Open) {
midir_impl_close_port(mImplementation, &id);
nsCOMPtr<nsIRunnable> r(new SetStatusRunnable(
aPort->MIDIPortInterface::Id(), aPort->DeviceState(),
MIDIPortConnectionState::Closed));
NS_DispatchToCurrentThread(r);
}
}
void midirMIDIPlatformService::SendMessages(const nsAString& aPortId) {
nsTArray<MIDIMessage> messages;
GetMessages(aPortId, messages);
for (const auto& message : messages) {
midir_impl_send(mImplementation, &aPortId, &message.data());
}
}

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

@ -0,0 +1,59 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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_midirMIDIPlatformService_h
#define mozilla_dom_midirMIDIPlatformService_h
#include "mozilla/StaticMutex.h"
#include "mozilla/dom/MIDIPlatformService.h"
#include "mozilla/dom/MIDITypes.h"
#include "mozilla/dom/midi/midir_impl_ffi_generated.h"
class nsIThread;
struct MidirWrapper;
namespace mozilla::dom {
class MIDIPortInterface;
/**
* Platform service implementation using the midir crate.
*/
class midirMIDIPlatformService : public MIDIPlatformService {
public:
midirMIDIPlatformService();
virtual void Init() override;
virtual void Open(MIDIPortParent* aPort) override;
virtual void Stop() override;
virtual void ScheduleSend(const nsAString& aPort) override;
virtual void ScheduleClose(MIDIPortParent* aPort) override;
void SendMessages(const nsAString& aPort);
private:
virtual ~midirMIDIPlatformService();
static void AddPort(const nsString* aId, const nsString* aName, bool aInput);
static void CheckAndReceive(const nsString* aId, const uint8_t* aData,
size_t aLength, const GeckoTimeStamp* aTimeStamp,
uint64_t aMicros);
// True if server has been brought up already.
bool mIsInitialized;
// Wrapper around the midir Rust implementation.
MidirWrapper* mImplementation;
// midir has its own internal threads and we can't execute jobs directly on
// them, instead we forward them to the background thread the service was
// created in.
static StaticMutex gBackgroundThreadMutex;
static nsCOMPtr<nsIThread> gBackgroundThread;
};
} // namespace mozilla::dom
#endif // mozilla_dom_midirMIDIPlatformService_h

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

@ -0,0 +1,13 @@
[package]
name = "midir_impl"
version = "0.1.0"
authors = ["Gabriele Svelto"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
midir = "0.7.0"
nsstring = { path = "../../../xpcom/rust/nsstring/" }
uuid = { version = "0.8", features = ["v4"] }
thin-vec = { version = "0.2.1", features = ["gecko-ffi"] }

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

@ -0,0 +1,18 @@
header = """/* 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/. */"""
autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen. See RunCbindgen.py */
"""
include_version = true
braces = "SameLine"
line_length = 100
tab_width = 2
language = "C++"
include_guard = "midir_impl_ffi_generated_h"
includes = ["nsStringFwd.h", "nsTArrayForwardDeclare.h"]
[defines]
"target_os = windows" = "XP_WIN"
[export.rename]
"ThinVec" = "nsTArray"

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

@ -0,0 +1,17 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
if CONFIG["COMPILE_ENVIRONMENT"]:
# This tells mach to run cbindgen and that this header-file should be created
CbindgenHeader(
"midir_impl_ffi_generated.h",
inputs=["/dom/midi/midir_impl"],
)
# This tells mach to copy that generated file to obj/dist/includes/mozilla/dom/midi
EXPORTS.mozilla.dom.midi += [
"!midir_impl_ffi_generated.h",
]

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

@ -0,0 +1,334 @@
extern crate thin_vec;
use midir::{
InitError, MidiInput, MidiInputConnection, MidiInputPort, MidiOutput, MidiOutputConnection,
MidiOutputPort,
};
use nsstring::{nsAString, nsString};
use std::boxed::Box;
use std::ptr;
use thin_vec::ThinVec;
use uuid::Uuid;
/* 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/. */
extern crate midir;
#[cfg(target_os = "windows")]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct GeckoTimeStamp {
gtc: u64,
qpc: u64,
is_null: u8,
has_qpc: u8,
}
#[cfg(not(target_os = "windows"))]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct GeckoTimeStamp {
value: u64,
}
enum MidiConnection {
Input(MidiInputConnection<CallbackData>),
Output(MidiOutputConnection),
}
struct MidiConnectionWrapper {
id: String,
connection: MidiConnection,
}
enum MidiPort {
Input(MidiInputPort),
Output(MidiOutputPort),
}
struct MidiPortWrapper {
id: String,
name: String,
port: MidiPort,
}
pub struct MidirWrapper {
ports: Vec<MidiPortWrapper>,
connections: Vec<MidiConnectionWrapper>,
}
struct CallbackData {
nsid: nsString,
open_timestamp: GeckoTimeStamp,
}
impl MidirWrapper {
fn open_port(
self: &mut MidirWrapper,
nsid: &nsString,
timestamp: GeckoTimeStamp,
callback: unsafe extern "C" fn(
id: &nsString,
data: *const u8,
length: usize,
timestamp: &GeckoTimeStamp,
micros: u64,
),
) -> Result<(), ()> {
let id = nsid.to_string();
let ports = &self.ports;
let connections = &mut self.connections;
let port = ports.iter().find(|e| e.id.eq(&id));
if let Some(port) = port {
match &port.port {
MidiPort::Input(port) => {
let input = MidiInput::new("WebMIDI input").map_err(|_err| ())?;
let data = CallbackData {
nsid: nsid.clone(),
open_timestamp: timestamp,
};
let connection = input
.connect(
port,
"Input connection",
move |stamp, message, data| unsafe {
callback(
&data.nsid,
message.as_ptr(),
message.len(),
&data.open_timestamp,
stamp,
);
},
data,
)
.map_err(|_err| ())?;
let connection_wrapper = MidiConnectionWrapper {
id: id.clone(),
connection: MidiConnection::Input(connection),
};
connections.push(connection_wrapper);
return Ok(());
}
MidiPort::Output(port) => {
let output = MidiOutput::new("WebMIDI output").map_err(|_err| ())?;
let connection = output
.connect(port, "Output connection")
.map_err(|_err| ())?;
let connection_wrapper = MidiConnectionWrapper {
connection: MidiConnection::Output(connection),
id: id.clone(),
};
connections.push(connection_wrapper);
return Ok(());
}
}
}
Err(())
}
fn close_port(self: &mut MidirWrapper, id: &str) {
let connections = &mut self.connections;
let index = connections.iter().position(|e| e.id.eq(id)).unwrap();
let connection_wrapper = connections.remove(index);
match connection_wrapper.connection {
MidiConnection::Input(connection) => {
connection.close();
}
MidiConnection::Output(connection) => {
connection.close();
}
}
}
fn send(self: &mut MidirWrapper, id: &str, data: &[u8]) -> Result<(), ()> {
let connections = &mut self.connections;
let index = connections.iter().position(|e| e.id.eq(id)).unwrap();
let connection_wrapper = connections.get_mut(index).unwrap();
match &mut connection_wrapper.connection {
MidiConnection::Output(connection) => {
connection.send(data).map_err(|_err| ())?;
}
_ => {
panic!("Sending on an input port!");
}
}
Ok(())
}
}
impl MidirWrapper {
fn new() -> Result<MidirWrapper, InitError> {
let input = MidiInput::new("WebMIDI input")?;
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();
Ok(MidirWrapper { ports, connections })
}
}
/// Create the C++ wrapper that will be used to talk with midir.
///
/// This function will be exposed to C++
///
/// # Safety
///
/// This function deliberately leaks the wrapper because ownership is
/// transfered to the C++ code. Use [midir_impl_shutdown()] to free it.
#[no_mangle]
pub unsafe extern "C" fn midir_impl_init() -> *mut MidirWrapper {
if let Ok(midir_impl) = MidirWrapper::new() {
let midir_box = Box::new(midir_impl);
// Leak the object as it will be owned by the C++ code from now on
Box::leak(midir_box) as *mut _
} else {
ptr::null_mut()
}
}
/// Shutdown midir and free the C++ wrapper.
///
/// This function will be exposed to C++
///
/// # Safety
///
/// `wrapper` must be the pointer returned by [midir_impl_init()]. After this
/// has been called the wrapper object will be destoyed and cannot be accessed
/// anymore.
#[no_mangle]
pub unsafe extern "C" fn midir_impl_shutdown(wrapper: *mut MidirWrapper) {
// The MidirImpl object will be automatically destroyed when the contents
// of this box are automatically dropped at the end of the function
let _midir_box = Box::from_raw(wrapper);
}
/// Enumerate the available MIDI 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_enum_ports(
wrapper: *mut MidirWrapper,
callback: unsafe extern "C" fn(id: &nsString, name: &nsString, input: bool),
) {
iterate_ports(&(*wrapper).ports, callback);
}
/// Open a MIDI port.
///
/// 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_open_port(
wrapper: *mut MidirWrapper,
nsid: *mut nsString,
timestamp: *mut GeckoTimeStamp,
callback: unsafe extern "C" fn(
id: &nsString,
data: *const u8,
length: usize,
timestamp: &GeckoTimeStamp,
micros: u64,
),
) -> bool {
(*wrapper)
.open_port(nsid.as_ref().unwrap(), *timestamp, callback)
.is_ok()
}
/// Close a MIDI port.
///
/// 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_close_port(wrapper: *mut MidirWrapper, id: *mut nsString) {
(*wrapper).close_port(&(*id).to_string());
}
/// Send a message over a MIDI output port.
///
/// 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_send(
wrapper: *mut MidirWrapper,
id: *const nsAString,
data: *const ThinVec<u8>,
) -> bool {
(*wrapper)
.send(&(*id).to_string(), (*data).as_slice())
.is_ok()
}
fn collect_input_ports(input: &MidiInput, wrappers: &mut Vec<MidiPortWrapper>) {
let ports = input.ports();
for port in ports {
let id = Uuid::new_v4()
.to_hyphenated()
.encode_lower(&mut Uuid::encode_buffer())
.to_owned();
let name = input
.port_name(&port)
.unwrap_or_else(|_| "unknown input port".to_string());
let port = MidiPortWrapper {
id,
name,
port: MidiPort::Input(port),
};
wrappers.push(port);
}
}
fn collect_output_ports(output: &MidiOutput, wrappers: &mut Vec<MidiPortWrapper>) {
let ports = output.ports();
for port in ports {
let id = Uuid::new_v4()
.to_hyphenated()
.encode_lower(&mut Uuid::encode_buffer())
.to_owned();
let name = output
.port_name(&port)
.unwrap_or_else(|_| "unknown input port".to_string());
let port = MidiPortWrapper {
id,
name,
port: MidiPort::Output(port),
};
wrappers.push(port);
}
}
unsafe fn iterate_ports(
ports: &[MidiPortWrapper],
callback: unsafe extern "C" fn(id: &nsString, name: &nsString, input: bool),
) {
for port in ports {
let id = nsString::from(&port.id);
let name = nsString::from(&port.name);
let input = match port.port {
MidiPort::Input(_) => true,
MidiPort::Output(_) => false,
};
callback(&id, &name, input);
}
}

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

@ -4,6 +4,9 @@
# 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/.
if CONFIG["OS_TARGET"] != "Android":
DIRS += ["midir_impl"]
IPDL_SOURCES += [
"MIDITypes.ipdlh",
"PMIDIManager.ipdl",
@ -31,7 +34,7 @@ EXPORTS.mozilla.dom += [
"MIDIUtils.h",
]
UNIFIED_SOURCES = [
UNIFIED_SOURCES += [
"MIDIAccess.cpp",
"MIDIAccessManager.cpp",
"MIDIInput.cpp",
@ -56,8 +59,15 @@ UNIFIED_SOURCES = [
include("/ipc/chromium/chromium-config.mozbuild")
if CONFIG["OS_TARGET"] == "Linux":
OS_LIBS += ["asound"] # Required by midir
UNIFIED_SOURCES += ["AlsaCompatibility.cpp"]
if CONFIG["OS_TARGET"] != "Android":
UNIFIED_SOURCES += [
"midirMIDIPlatformService.cpp",
]
FINAL_LIBRARY = "xul"
LOCAL_INCLUDES += [
"/dom/base",

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

@ -1270,7 +1270,7 @@ cocoa)
LDFLAGS="$LDFLAGS -framework Cocoa"
# Use -Wl as a trick to avoid -framework and framework names from
# being separated by AC_SUBST_LIST.
TK_LIBS='-Wl,-framework,Foundation -Wl,-framework,CoreFoundation -Wl,-framework,CoreLocation -Wl,-framework,QuartzCore -Wl,-framework,Carbon -Wl,-framework,CoreAudio -Wl,-framework,CoreVideo -Wl,-framework,AudioToolbox -Wl,-framework,AudioUnit -Wl,-framework,AddressBook -Wl,-framework,OpenGL -Wl,-framework,Security -Wl,-framework,ServiceManagement -Wl,-framework,CoreServices -Wl,-framework,ApplicationServices -Wl,-framework,AppKit'
TK_LIBS='-Wl,-framework,Foundation -Wl,-framework,CoreFoundation -Wl,-framework,CoreLocation -Wl,-framework,QuartzCore -Wl,-framework,Carbon -Wl,-framework,CoreAudio -Wl,-framework,CoreVideo -Wl,-framework,AudioToolbox -Wl,-framework,AudioUnit -Wl,-framework,AddressBook -Wl,-framework,OpenGL -Wl,-framework,Security -Wl,-framework,ServiceManagement -Wl,-framework,CoreServices -Wl,-framework,ApplicationServices -Wl,-framework,AppKit -Wl,-framework,CoreMIDI'
TK_CFLAGS=""
CFLAGS="$CFLAGS $TK_CFLAGS"
CXXFLAGS="$CXXFLAGS $TK_CFLAGS"

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

@ -73,6 +73,7 @@ qcms = { path = "../../../../gfx/qcms", features = ["c_bindings", "neon"], defau
viaduct = { git = "https://github.com/mozilla/application-services", rev = "c51b63595a27a6ef45161012323e0261475c10c9"}
webext_storage_bridge = { path = "../../../components/extensions/storage/webext_storage_bridge" }
midir = { git = "https://github.com/mozilla/midir.git", rev = "dc87afbd4361ae5ec192e1fab0a6409dd13d4011" }
midir_impl = { path = "../../../../dom/midi/midir_impl" }
# https://github.com/mozilla/audioipc-2/commits/master
[target.'cfg(not(target_os = "macos"))'.dependencies]

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

@ -82,6 +82,9 @@ extern crate viaduct;
#[cfg(not(target_os = "android"))]
extern crate midir;
#[cfg(not(target_os = "android"))]
extern crate midir_impl;
extern crate gecko_logger;
#[cfg(feature = "oxidized_breakpad")]

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

@ -3,6 +3,7 @@ clippy:
description: Lint rust
include:
- build/workspace-hack/
- dom/midi/midir_impl/
- dom/media/gtest/
- dom/webauthn/libudev-sys/
- gfx/webrender_bindings/