15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)/* 25267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles) * Copyright (C) 2013 Google Inc. All rights reserved. 35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * 45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Redistribution and use in source and binary forms, with or without 55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * modification, are permitted provided that the following conditions are 65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * met: 75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * 85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * * Redistributions of source code must retain the above copyright 95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * notice, this list of conditions and the following disclaimer. 105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * * Redistributions in binary form must reproduce the above 115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * copyright notice, this list of conditions and the following disclaimer 125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * in the documentation and/or other materials provided with the 135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * distribution. 145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * * Neither the name of Google Inc. nor the names of its 155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * contributors may be used to endorse or promote products derived from 165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * this software without specific prior written permission. 175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * 185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) */ 305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "config.h" 325267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles)#include "modules/webmidi/MIDIAccess.h" 335267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles) 34591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch#include "core/dom/DOMError.h" 35e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch#include "core/dom/Document.h" 36e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch#include "core/loader/DocumentLoadTiming.h" 37e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch#include "core/loader/DocumentLoader.h" 38591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch#include "modules/webmidi/MIDIAccessPromise.h" 395267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles)#include "modules/webmidi/MIDIConnectionEvent.h" 40e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch#include "modules/webmidi/MIDIController.h" 415267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles)#include "modules/webmidi/MIDIInput.h" 425267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles)#include "modules/webmidi/MIDIOutput.h" 435267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles)#include "modules/webmidi/MIDIPort.h" 445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)namespace WebCore { 465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 47521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)PassRefPtr<MIDIAccess> MIDIAccess::create(ScriptExecutionContext* context, MIDIAccessPromise* promise) 485267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles){ 49521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles) RefPtr<MIDIAccess> midiAccess(adoptRef(new MIDIAccess(context, promise))); 505267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles) midiAccess->suspendIfNeeded(); 51e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch midiAccess->startRequest(); 525267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles) return midiAccess.release(); 535267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles)} 54926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 555267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles)MIDIAccess::~MIDIAccess() 565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 575267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles) stop(); 58926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)} 59926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 60521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)MIDIAccess::MIDIAccess(ScriptExecutionContext* context, MIDIAccessPromise* promise) 615267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles) : ActiveDOMObject(context) 62521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles) , m_promise(promise) 63591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch , m_hasAccess(false) 6402772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch , m_sysExEnabled(false) 65e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch , m_requesting(false) 66926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles){ 675267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles) ScriptWrappable::init(this); 68591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch m_accessor = MIDIAccessor::create(this); 69e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch} 70e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch 7102772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdochvoid MIDIAccess::setSysExEnabled(bool enable) 72e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch{ 73e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch m_requesting = false; 7402772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch m_sysExEnabled = enable; 75e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch if (enable) 76e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch m_accessor->startSession(); 77e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch else 78e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch permissionDenied(); 79591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch} 80591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch 81591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdochvoid MIDIAccess::didAddInputPort(const String& id, const String& manufacturer, const String& name, const String& version) 82591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch{ 83591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch ASSERT(isMainThread()); 84591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch 8502772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch // FIXME: Pass in |this| to create() method so we can filter system exclusive messages correctly. 86591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch m_inputs.append(MIDIInput::create(scriptExecutionContext(), id, manufacturer, name, version)); 87591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch} 88591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch 89591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdochvoid MIDIAccess::didAddOutputPort(const String& id, const String& manufacturer, const String& name, const String& version) 90591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch{ 91591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch ASSERT(isMainThread()); 92591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch 9302772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch unsigned portIndex = m_outputs.size(); 9402772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch m_outputs.append(MIDIOutput::create(this, portIndex, scriptExecutionContext(), id, manufacturer, name, version)); 95591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch} 96591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch 97e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdochvoid MIDIAccess::didStartSession() 98591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch{ 99591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch ASSERT(isMainThread()); 100591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch 101591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch m_hasAccess = true; 102591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch m_promise->fulfill(); 103591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch} 104591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch 105e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdochvoid MIDIAccess::didReceiveMIDIData(unsigned portIndex, const unsigned char* data, size_t length, double timeStamp) 106591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch{ 107591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch ASSERT(isMainThread()); 108591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch 109e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch if (m_hasAccess && portIndex < m_inputs.size()) { 110e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch // Convert from time in seconds which is based on the time coordinate system of monotonicallyIncreasingTime() 111e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch // into time in milliseconds (a DOMHighResTimeStamp) according to the same time coordinate system as performance.now(). 112e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch // This is how timestamps are defined in the Web MIDI spec. 113e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch Document* document = toDocument(scriptExecutionContext()); 114e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch ASSERT(document); 115e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch 116e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch double timeStampInMilliseconds = 1000 * document->loader()->timing()->monotonicTimeToZeroBasedDocumentTime(timeStamp); 117e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch 118e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch m_inputs[portIndex]->didReceiveMIDIData(portIndex, data, length, timeStampInMilliseconds); 119e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch } 120591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch} 121591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch 12202772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdochvoid MIDIAccess::sendMIDIData(unsigned portIndex, const unsigned char* data, size_t length, double timeStampInMilliseconds) 12302772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch{ 12402772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch if (m_hasAccess && portIndex < m_outputs.size() && data && length > 1) { 12502772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch // Convert from a time in milliseconds (a DOMHighResTimeStamp) according to the same time coordinate system as performance.now() 12602772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch // into a time in seconds which is based on the time coordinate system of monotonicallyIncreasingTime(). 12702772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch double timeStamp; 12802772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch 12902772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch if (!timeStampInMilliseconds) { 13002772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch // We treat a value of 0 (which is the default value) as special, meaning "now". 13102772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch // We need to translate it exactly to 0 seconds. 13202772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch timeStamp = 0; 13302772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch } else { 13402772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch Document* document = toDocument(scriptExecutionContext()); 13502772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch ASSERT(document); 13602772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch double documentStartTime = document->loader()->timing()->referenceMonotonicTime(); 13702772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch timeStamp = documentStartTime + 0.001 * timeStampInMilliseconds; 13802772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch } 13902772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch 14002772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch m_accessor->sendMIDIData(portIndex, data, length, timeStamp); 14102772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch } 14202772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch} 14302772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch 144e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdochvoid MIDIAccess::stop() 145591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch{ 146e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch m_hasAccess = false; 147e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch if (!m_requesting) 148e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch return; 149e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch m_requesting = false; 150e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch Document* document = toDocument(scriptExecutionContext()); 151e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch ASSERT(document); 152e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch MIDIController* controller = MIDIController::from(document->page()); 153e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch ASSERT(controller); 154e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch controller->cancelSysExPermissionRequest(this); 15502772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch 15602772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch m_accessor.clear(); 157e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch} 158591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch 159e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdochvoid MIDIAccess::startRequest() 160e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch{ 161e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch if (!m_promise->options()->sysex) { 162e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch m_accessor->startSession(); 163e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch return; 164e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch } 165e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch Document* document = toDocument(scriptExecutionContext()); 166e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch ASSERT(document); 167e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch MIDIController* controller = MIDIController::from(document->page()); 168e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch if (controller) { 169e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch m_requesting = true; 170e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch controller->requestSysExPermission(this); 171e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch } else { 172e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch permissionDenied(); 173e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch } 174591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch} 175591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch 176e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdochvoid MIDIAccess::permissionDenied() 177591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch{ 178e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch ASSERT(isMainThread()); 179e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch 180591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch m_hasAccess = false; 181e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch RefPtr<DOMError> error = DOMError::create("SecurityError"); 182e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch m_promise->reject(error); 1835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} // namespace WebCore 186