tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

MIDIPort.cpp (7473B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/dom/MIDIPort.h"
      8 
      9 #include "MIDILog.h"
     10 #include "mozilla/dom/Document.h"
     11 #include "mozilla/dom/MIDIAccess.h"
     12 #include "mozilla/dom/MIDIConnectionEvent.h"
     13 #include "mozilla/dom/MIDIPortChild.h"
     14 #include "mozilla/dom/MIDITypes.h"
     15 #include "mozilla/dom/Promise.h"
     16 #include "mozilla/ipc/BackgroundChild.h"
     17 #include "mozilla/ipc/Endpoint.h"
     18 #include "mozilla/ipc/PBackgroundChild.h"
     19 #include "nsContentUtils.h"
     20 #include "nsISupportsImpl.h"  // for MOZ_COUNT_CTOR, MOZ_COUNT_DTOR
     21 
     22 using namespace mozilla::ipc;
     23 
     24 namespace mozilla::dom {
     25 
     26 NS_IMPL_CYCLE_COLLECTION_INHERITED(MIDIPort, DOMEventTargetHelper,
     27                                   mMIDIAccessParent, mOpeningPromise,
     28                                   mClosingPromise)
     29 
     30 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(MIDIPort, DOMEventTargetHelper)
     31 NS_IMPL_CYCLE_COLLECTION_TRACE_END
     32 
     33 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MIDIPort)
     34  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     35 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
     36 
     37 NS_IMPL_ADDREF_INHERITED(MIDIPort, DOMEventTargetHelper)
     38 NS_IMPL_RELEASE_INHERITED(MIDIPort, DOMEventTargetHelper)
     39 
     40 MIDIPort::MIDIPort(nsPIDOMWindowInner* aWindow)
     41    : DOMEventTargetHelper(aWindow), mKeepAlive(false) {
     42  MOZ_ASSERT(aWindow);
     43 
     44  if (Document* aDoc = aWindow->GetExtantDoc()) {
     45    aDoc->DisallowBFCaching();
     46  }
     47 }
     48 
     49 MIDIPort::~MIDIPort() {
     50  if (Port()) {
     51    // If the IPC port channel is still alive at this point, it means we're
     52    // probably CC'ing this port object. Send the shutdown message to also clean
     53    // up the IPC channel.
     54    Port()->SendShutdown();
     55  }
     56 }
     57 
     58 bool MIDIPort::Initialize(const MIDIPortInfo& aPortInfo, bool aSysexEnabled,
     59                          MIDIAccess* aMIDIAccessParent) {
     60  MOZ_ASSERT(aMIDIAccessParent);
     61  nsCOMPtr<Document> document = GetDocumentIfCurrent();
     62  if (!document) {
     63    return false;
     64  }
     65 
     66  nsCOMPtr<nsIURI> uri = document->GetDocumentURI();
     67  if (!uri) {
     68    return false;
     69  }
     70 
     71  nsAutoCString origin;
     72  nsresult rv = nsContentUtils::GetWebExposedOriginSerialization(uri, origin);
     73  if (NS_FAILED(rv)) {
     74    return false;
     75  }
     76  RefPtr<MIDIPortChild> port =
     77      new MIDIPortChild(aPortInfo, aSysexEnabled, this);
     78  if (NS_FAILED(port->GenerateStableId(origin))) {
     79    return false;
     80  }
     81  PBackgroundChild* b = BackgroundChild::GetForCurrentThread();
     82  MOZ_ASSERT(b,
     83             "Should always have a valid BackgroundChild when creating a port "
     84             "object!");
     85 
     86  // Create the endpoints and bind the one on the child side.
     87  Endpoint<PMIDIPortParent> parentEndpoint;
     88  Endpoint<PMIDIPortChild> childEndpoint;
     89  MOZ_ALWAYS_SUCCEEDS(
     90      PMIDIPort::CreateEndpoints(&parentEndpoint, &childEndpoint));
     91  MOZ_ALWAYS_TRUE(childEndpoint.Bind(port));
     92 
     93  if (!b->SendCreateMIDIPort(std::move(parentEndpoint), aPortInfo,
     94                             aSysexEnabled)) {
     95    return false;
     96  }
     97 
     98  mMIDIAccessParent = aMIDIAccessParent;
     99  mPortHolder.Init(port.forget());
    100  LOG("MIDIPort::Initialize (%s, %s)",
    101      NS_ConvertUTF16toUTF8(Port()->Name()).get(),
    102      GetEnumString(Port()->Type()).get());
    103  return true;
    104 }
    105 
    106 void MIDIPort::UnsetIPCPort() {
    107  LOG("MIDIPort::UnsetIPCPort (%s, %s)",
    108      NS_ConvertUTF16toUTF8(Port()->Name()).get(),
    109      GetEnumString(Port()->Type()).get());
    110  mPortHolder.Clear();
    111 }
    112 
    113 void MIDIPort::GetId(nsString& aRetVal) const {
    114  MOZ_ASSERT(Port());
    115  aRetVal = Port()->StableId();
    116 }
    117 
    118 void MIDIPort::GetManufacturer(nsString& aRetVal) const {
    119  MOZ_ASSERT(Port());
    120  aRetVal = Port()->Manufacturer();
    121 }
    122 
    123 void MIDIPort::GetName(nsString& aRetVal) const {
    124  MOZ_ASSERT(Port());
    125  aRetVal = Port()->Name();
    126 }
    127 
    128 void MIDIPort::GetVersion(nsString& aRetVal) const {
    129  MOZ_ASSERT(Port());
    130  aRetVal = Port()->Version();
    131 }
    132 
    133 MIDIPortType MIDIPort::Type() const {
    134  MOZ_ASSERT(Port());
    135  return Port()->Type();
    136 }
    137 
    138 MIDIPortConnectionState MIDIPort::Connection() const {
    139  MOZ_ASSERT(Port());
    140  return Port()->ConnectionState();
    141 }
    142 
    143 MIDIPortDeviceState MIDIPort::State() const {
    144  MOZ_ASSERT(Port());
    145  return Port()->DeviceState();
    146 }
    147 
    148 bool MIDIPort::SysexEnabled() const {
    149  MOZ_ASSERT(Port());
    150  return Port()->SysexEnabled();
    151 }
    152 
    153 already_AddRefed<Promise> MIDIPort::Open(ErrorResult& aError) {
    154  LOG("MIDIPort::Open");
    155  MOZ_ASSERT(Port());
    156  if (mOpeningPromise) {
    157    return do_AddRef(mOpeningPromise);
    158  }
    159  RefPtr<Promise> p = Promise::Create(GetOwnerGlobal(), aError);
    160  if (aError.Failed()) {
    161    return nullptr;
    162  }
    163  mOpeningPromise = p;
    164  Port()->SendOpen();
    165  return p.forget();
    166 }
    167 
    168 already_AddRefed<Promise> MIDIPort::Close(ErrorResult& aError) {
    169  LOG("MIDIPort::Close");
    170  MOZ_ASSERT(Port());
    171  if (mClosingPromise) {
    172    return do_AddRef(mClosingPromise);
    173  }
    174  RefPtr<Promise> p = Promise::Create(GetOwnerGlobal(), aError);
    175  if (aError.Failed()) {
    176    return nullptr;
    177  }
    178  mClosingPromise = p;
    179  Port()->SendClose();
    180  return p.forget();
    181 }
    182 
    183 void MIDIPort::FireStateChangeEvent() {
    184  if (!GetOwnerWindow()) {
    185    return;  // Ignore changes once we've been disconnected from the owner
    186  }
    187 
    188  StateChange();
    189 
    190  MOZ_ASSERT(Port());
    191  if (Port()->ConnectionState() == MIDIPortConnectionState::Open ||
    192      Port()->ConnectionState() == MIDIPortConnectionState::Pending) {
    193    if (mOpeningPromise) {
    194      mOpeningPromise->MaybeResolve(this);
    195      mOpeningPromise = nullptr;
    196    }
    197  } else if (Port()->ConnectionState() == MIDIPortConnectionState::Closed) {
    198    if (mOpeningPromise) {
    199      mOpeningPromise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
    200      mOpeningPromise = nullptr;
    201    }
    202    if (mClosingPromise) {
    203      mClosingPromise->MaybeResolve(this);
    204      mClosingPromise = nullptr;
    205    }
    206  }
    207 
    208  if (Port()->DeviceState() == MIDIPortDeviceState::Connected &&
    209      Port()->ConnectionState() == MIDIPortConnectionState::Pending) {
    210    Port()->SendOpen();
    211  }
    212 
    213  if (Port()->ConnectionState() == MIDIPortConnectionState::Open ||
    214      (Port()->DeviceState() == MIDIPortDeviceState::Connected &&
    215       Port()->ConnectionState() == MIDIPortConnectionState::Pending)) {
    216    KeepAliveOnStatechange();
    217  } else {
    218    DontKeepAliveOnStatechange();
    219  }
    220 
    221  // Fire MIDIAccess events first so that the port is no longer in the port
    222  // maps.
    223  if (mMIDIAccessParent) {
    224    mMIDIAccessParent->FireConnectionEvent(this);
    225  }
    226 
    227  MIDIConnectionEventInit init;
    228  init.mPort = this;
    229  RefPtr<MIDIConnectionEvent> event(
    230      MIDIConnectionEvent::Constructor(this, u"statechange"_ns, init));
    231  DispatchTrustedEvent(event);
    232 }
    233 
    234 void MIDIPort::StateChange() {}
    235 
    236 void MIDIPort::Receive(const nsTArray<MIDIMessage>& aMsg) {
    237  MOZ_CRASH("We should never get here!");
    238 }
    239 
    240 void MIDIPort::DisconnectFromOwner() {
    241  if (Port()) {
    242    Port()->SendClose();
    243  }
    244  DontKeepAliveOnStatechange();
    245 
    246  DOMEventTargetHelper::DisconnectFromOwner();
    247 }
    248 
    249 void MIDIPort::KeepAliveOnStatechange() {
    250  if (!mKeepAlive) {
    251    mKeepAlive = true;
    252    KeepAliveIfHasListenersFor(nsGkAtoms::onstatechange);
    253  }
    254 }
    255 
    256 void MIDIPort::DontKeepAliveOnStatechange() {
    257  if (mKeepAlive) {
    258    IgnoreKeepAliveIfHasListenersFor(nsGkAtoms::onstatechange);
    259    mKeepAlive = false;
    260  }
    261 }
    262 
    263 const nsString& MIDIPort::StableId() { return Port()->StableId(); }
    264 
    265 }  // namespace mozilla::dom