Chromium Code Reviews| Index: Source/modules/webmidi/MIDIAccess.cpp |
| diff --git a/Source/modules/webmidi/MIDIAccess.cpp b/Source/modules/webmidi/MIDIAccess.cpp |
| index 39a7ad86fb746cd02795d68adf15bb674251c0a2..d513a480249612584b3eadc3a8b2e135e7ded9ae 100644 |
| --- a/Source/modules/webmidi/MIDIAccess.cpp |
| +++ b/Source/modules/webmidi/MIDIAccess.cpp |
| @@ -31,36 +31,44 @@ |
| #include "config.h" |
| #include "modules/webmidi/MIDIAccess.h" |
| +#include "bindings/v8/DOMRequestState.h" |
| +#include "bindings/v8/DOMWrapperWorld.h" |
| +#include "bindings/v8/ScriptPromise.h" |
| +#include "bindings/v8/ScriptPromiseResolver.h" |
| +#include "bindings/v8/V8Binding.h" |
| #include "core/dom/DOMError.h" |
| #include "core/dom/Document.h" |
| #include "core/loader/DocumentLoadTiming.h" |
| #include "core/loader/DocumentLoader.h" |
| -#include "modules/webmidi/MIDIAccessPromise.h" |
| #include "modules/webmidi/MIDIConnectionEvent.h" |
| #include "modules/webmidi/MIDIController.h" |
| +#include "modules/webmidi/MIDIOptions.h" |
| #include "modules/webmidi/MIDIPort.h" |
| +#include "modules/webmidi/NavigatorWebMIDI.h" |
| namespace WebCore { |
| -PassRefPtrWillBeRawPtr<MIDIAccess> MIDIAccess::create(ExecutionContext* context, MIDIAccessPromise* promise) |
| +PassRefPtrWillBeRawPtr<MIDIAccess> MIDIAccess::create(const MIDIOptions& options, ExecutionContext* context) |
| { |
| - RefPtrWillBeRawPtr<MIDIAccess> midiAccess(adoptRefCountedWillBeRefCountedGarbageCollected(new MIDIAccess(context, promise))); |
| + RefPtrWillBeRawPtr<MIDIAccess> midiAccess(adoptRefCountedWillBeRefCountedGarbageCollected(new MIDIAccess(options, context))); |
| midiAccess->suspendIfNeeded(); |
| - midiAccess->startRequest(); |
| return midiAccess.release(); |
| } |
| MIDIAccess::~MIDIAccess() |
| { |
| - stop(); |
| + ASSERT(!m_requesting); |
| } |
| -MIDIAccess::MIDIAccess(ExecutionContext* context, MIDIAccessPromise* promise) |
| +MIDIAccess::MIDIAccess(const MIDIOptions& options, ExecutionContext* context) |
| : ActiveDOMObject(context) |
| - , m_promise(promise) |
| + , m_activeDOMObjectState(Active) |
| + , m_promiseState(Pending) |
| + , m_options(options) |
| , m_hasAccess(false) |
| , m_sysExEnabled(false) |
| , m_requesting(false) |
| + , m_resumeTimer(this, &MIDIAccess::resumeTimerFired) |
| { |
| ScriptWrappable::init(this); |
| m_accessor = MIDIAccessor::create(this); |
| @@ -68,12 +76,13 @@ MIDIAccess::MIDIAccess(ExecutionContext* context, MIDIAccessPromise* promise) |
| void MIDIAccess::setSysExEnabled(bool enable) |
| { |
| - m_requesting = false; |
| m_sysExEnabled = enable; |
| - if (enable) |
| + if (enable) { |
| m_accessor->startSession(); |
| - else |
| - permissionDenied(); |
| + } else { |
| + reject(DOMError::create("SecurityError")); |
| + // |this| may be deleted here. |
| + } |
| } |
| void MIDIAccess::didAddInputPort(const String& id, const String& manufacturer, const String& name, const String& version) |
| @@ -94,12 +103,13 @@ void MIDIAccess::didAddOutputPort(const String& id, const String& manufacturer, |
| void MIDIAccess::didStartSession(bool success) |
| { |
| ASSERT(isMainThread()); |
| - |
| - m_hasAccess = success; |
| + if (!m_requesting) |
| + return; |
| if (success) |
| - m_promise->fulfill(); |
| + resolve(); |
| else |
| - m_promise->reject(DOMError::create("InvalidStateError")); |
| + reject(DOMError::create("InvalidStateError")); |
| + // |this| may be deleted here. |
| } |
| void MIDIAccess::didReceiveMIDIData(unsigned portIndex, const unsigned char* data, size_t length, double timeStamp) |
| @@ -141,49 +151,151 @@ void MIDIAccess::sendMIDIData(unsigned portIndex, const unsigned char* data, siz |
| } |
| } |
| +void MIDIAccess::suspend() |
| +{ |
| + ASSERT(m_activeDOMObjectState == Active); |
| + m_activeDOMObjectState = Suspended; |
| + m_resumeTimer.stop(); |
| +} |
| + |
| +void MIDIAccess::resume() |
| +{ |
| + ASSERT(m_activeDOMObjectState == Suspended); |
| + m_activeDOMObjectState = Active; |
| + if (m_promiseState == Resolving || m_promiseState == Rejecting) |
| + m_resumeTimer.startOneShot(0); |
| +} |
| + |
| void MIDIAccess::stop() |
| { |
| + RefPtrWillBeRawPtr<MIDIAccess> protect(this); |
| + m_activeDOMObjectState = Stopped; |
| m_hasAccess = false; |
| + m_promiseState = ResolvedOrRejected; |
| + m_error.clear(); |
| + m_resumeTimer.stop(); |
| + m_accessor.clear(); |
| if (!m_requesting) |
| return; |
| - m_requesting = false; |
| Document* document = toDocument(executionContext()); |
| ASSERT(document); |
| MIDIController* controller = MIDIController::from(document->page()); |
| ASSERT(controller); |
| controller->cancelSysExPermissionRequest(this); |
| + setRequesting(false); |
| +} |
| + |
| +void MIDIAccess::permissionDenied() |
| +{ |
| + ASSERT(isMainThread()); |
| + reject(DOMError::create("SecurityError")); |
| } |
| -void MIDIAccess::startRequest() |
| +void MIDIAccess::setRequesting(bool value) |
| { |
| - if (!m_promise->options()->sysex) { |
| + ASSERT(m_requesting != value); |
| + if (value) { |
| + setPendingActivity(this); |
| + m_requesting = true; |
|
haraken
2014/02/26 11:30:39
Could you avoid using setPendingActivity/unsetPend
yhirano
2014/02/26 11:55:15
Because MIDIAccess is not exposed to the V8 GC at
haraken
2014/02/26 12:09:52
Then probably should we use ContextLifecycleObserv
yhirano
2014/02/26 12:16:37
MIDIAccess will be exposed to the GC, but it is no
yhirano
2014/02/26 12:37:12
Sorry, I was wrong. The wrapper will be created wh
haraken
2014/02/26 12:52:40
Let's discuss this issue offline tomorrow. I think
yhirano
2014/03/04 17:52:59
Done.
|
| + } else { |
| + m_requesting = false; |
| + unsetPendingActivity(this); |
| + } |
| +} |
| + |
| +ScriptPromise MIDIAccess::startRequest() |
| +{ |
| + ASSERT(!m_requesting); |
| + ScriptPromise promise = ScriptPromise::createPending(); |
| + m_resolver = MIDIAccessResolver::create(ScriptPromiseResolver::create(promise, executionContext()), executionContext()); |
| + setRequesting(true); |
| + if (!m_options.sysex) { |
| m_accessor->startSession(); |
| - return; |
| + return promise; |
| } |
| Document* document = toDocument(executionContext()); |
| ASSERT(document); |
| MIDIController* controller = MIDIController::from(document->page()); |
| if (controller) { |
| - m_requesting = true; |
| controller->requestSysExPermission(this); |
| } else { |
| - permissionDenied(); |
| + reject(DOMError::create("SecurityError")); |
| + // |this| may be deleted here. |
| } |
| + return promise; |
| } |
| -void MIDIAccess::permissionDenied() |
| +void MIDIAccess::resolve() |
| { |
| - ASSERT(isMainThread()); |
| + if (m_promiseState != Pending || m_activeDOMObjectState == Stopped) |
| + return; |
| + if (m_activeDOMObjectState == Active) { |
| + resolveNow(); |
| + // |this| may be deleted here. |
| + } else { |
| + ASSERT(m_activeDOMObjectState == Suspended); |
| + m_promiseState = Resolving; |
| + } |
| +} |
| + |
| +void MIDIAccess::reject(PassRefPtr<DOMError> error) |
| +{ |
| + if (m_promiseState != Pending || m_activeDOMObjectState == Stopped) |
| + return; |
| + if (m_activeDOMObjectState == Active) { |
| + rejectNow(error); |
| + // |this| may be deleted here. |
| + } else { |
| + ASSERT(m_activeDOMObjectState == Suspended); |
| + m_promiseState = Rejecting; |
| + m_error = error; |
| + } |
| +} |
| + |
| +void MIDIAccess::resolveNow() |
| +{ |
| + if (!m_requesting) |
| + return; |
| + ASSERT(m_promiseState != ResolvedOrRejected); |
| + ASSERT(m_activeDOMObjectState == Active); |
| + |
| + RefPtrWillBeRawPtr<MIDIAccess> protect(this); |
| + m_promiseState = ResolvedOrRejected; |
| + m_hasAccess = true; |
| + setRequesting(false); |
| + m_resolver->resolve(this); |
| +} |
| +void MIDIAccess::rejectNow(PassRefPtr<DOMError> error) |
| +{ |
| + if (!m_requesting) |
| + return; |
| + ASSERT(m_promiseState != ResolvedOrRejected); |
| + ASSERT(m_activeDOMObjectState == Active); |
| + |
| + RefPtrWillBeRawPtr<MIDIAccess> protect(this); |
| + m_promiseState = ResolvedOrRejected; |
| m_hasAccess = false; |
| - m_promise->reject(DOMError::create("SecurityError")); |
| + setRequesting(false); |
| + m_resolver->reject(error); |
| +} |
| + |
| +void MIDIAccess::resumeTimerFired(Timer<MIDIAccess>*) |
| +{ |
| + ASSERT(m_activeDOMObjectState == Active); |
| + if (m_promiseState == Resolving) { |
| + resolveNow(); |
| + } else { |
| + ASSERT(m_promiseState == Rejecting); |
| + rejectNow(m_error.release()); |
| + } |
| + // |this| may be deleted here. |
| } |
| void MIDIAccess::trace(Visitor* visitor) |
| { |
| visitor->trace(m_inputs); |
| visitor->trace(m_outputs); |
| - visitor->trace(m_promise); |
| } |
| } // namespace WebCore |