Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(121)

Side by Side Diff: Source/modules/webmidi/MIDIAccess.cpp

Issue 77773003: Make WebMIDI use blink Promise. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2013 Google Inc. All rights reserved. 2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
(...skipping 13 matching lines...) Expand all
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */ 29 */
30 30
31 #include "config.h" 31 #include "config.h"
32 #include "modules/webmidi/MIDIAccess.h" 32 #include "modules/webmidi/MIDIAccess.h"
33 33
34 #include "bindings/v8/MIDIAccessResolver.h"
35 #include "bindings/v8/ScriptPromise.h"
36 #include "bindings/v8/ScriptPromiseResolver.h"
37 #include "bindings/v8/V8Binding.h"
34 #include "core/dom/DOMError.h" 38 #include "core/dom/DOMError.h"
35 #include "core/dom/Document.h" 39 #include "core/dom/Document.h"
36 #include "core/loader/DocumentLoadTiming.h" 40 #include "core/loader/DocumentLoadTiming.h"
37 #include "core/loader/DocumentLoader.h" 41 #include "core/loader/DocumentLoader.h"
38 #include "modules/webmidi/MIDIAccessPromise.h"
39 #include "modules/webmidi/MIDIConnectionEvent.h" 42 #include "modules/webmidi/MIDIConnectionEvent.h"
40 #include "modules/webmidi/MIDIController.h" 43 #include "modules/webmidi/MIDIController.h"
44 #include "modules/webmidi/MIDIOptions.h"
41 #include "modules/webmidi/MIDIPort.h" 45 #include "modules/webmidi/MIDIPort.h"
46 #include "platform/AsyncMethodRunner.h"
42 47
43 namespace WebCore { 48 namespace WebCore {
44 49
45 PassRefPtrWillBeRawPtr<MIDIAccess> MIDIAccess::create(ExecutionContext* context, MIDIAccessPromise* promise) 50 class MIDIAccess::PostAction : public ScriptFunction {
51 public:
52 static PassOwnPtr<MIDIAccess::PostAction> create(v8::Isolate* isolate, MIDIA ccess* owner, State state) { return adoptPtr(new PostAction(isolate, owner, stat e)); }
53
54 private:
55 PostAction(v8::Isolate* isolate, MIDIAccess* owner, State state): ScriptFunc tion(isolate), m_owner(owner), m_state(state) { }
56 virtual ScriptValue call(ScriptValue value) OVERRIDE
57 {
58 m_owner->doPostAction(m_state);
59 return value;
60 }
61
62 // MIDIAccess must be accessible when |call| function is called.
63 MIDIAccess* m_owner;
64 State m_state;
65 };
66
67 ScriptPromise MIDIAccess::request(const MIDIOptions& options, ExecutionContext* context)
46 { 68 {
47 RefPtrWillBeRawPtr<MIDIAccess> midiAccess(adoptRefWillBeRefCountedGarbageCol lected(new MIDIAccess(context, promise))); 69 RefPtrWillBeRawPtr<MIDIAccess> midiAccess(adoptRefWillBeRefCountedGarbageCol lected(new MIDIAccess(options, context)));
48 midiAccess->suspendIfNeeded(); 70 midiAccess->suspendIfNeeded();
49 midiAccess->startRequest(); 71 return midiAccess->startRequest();
50 return midiAccess.release();
51 } 72 }
52 73
53 MIDIAccess::~MIDIAccess() 74 MIDIAccess::~MIDIAccess()
54 { 75 {
55 stop();
56 } 76 }
57 77
58 MIDIAccess::MIDIAccess(ExecutionContext* context, MIDIAccessPromise* promise) 78 MIDIAccess::MIDIAccess(const MIDIOptions& options, ExecutionContext* context)
59 : ActiveDOMObject(context) 79 : ActiveDOMObject(context)
60 , m_promise(promise) 80 , m_state(Requesting)
61 , m_hasAccess(false) 81 , m_options(options)
62 , m_sysExEnabled(false) 82 , m_sysExEnabled(false)
63 , m_requesting(false) 83 , m_asyncResolveRunner(this, &MIDIAccess::resolveNow)
84 , m_asyncRejectRunner(this, &MIDIAccess::rejectNow)
64 { 85 {
65 ScriptWrappable::init(this); 86 ScriptWrappable::init(this);
66 m_accessor = MIDIAccessor::create(this); 87 m_accessor = MIDIAccessor::create(this);
88 // Create a wrapper to expose this object to the V8 GC so that hasPendingAct ivity takes effect.
89 toV8NoInline(this, executionContext());
haraken 2014/03/13 02:22:33 You might want to implement a helper method that d
yhirano 2014/03/13 05:43:22 It doesn't work because toV8NoInline can't be appl
90 // Now this object is retained because m_state equals to Requesting.
91 // startRequest should be called at once.
67 } 92 }
68 93
69 void MIDIAccess::setSysExEnabled(bool enable) 94 void MIDIAccess::setSysExEnabled(bool enable)
70 { 95 {
71 m_requesting = false;
72 m_sysExEnabled = enable; 96 m_sysExEnabled = enable;
73 if (enable) 97 if (enable) {
74 m_accessor->startSession(); 98 m_accessor->startSession();
75 else 99 } else {
76 permissionDenied(); 100 reject(DOMError::create("SecurityError"));
101 }
77 } 102 }
78 103
79 void MIDIAccess::didAddInputPort(const String& id, const String& manufacturer, c onst String& name, const String& version) 104 void MIDIAccess::didAddInputPort(const String& id, const String& manufacturer, c onst String& name, const String& version)
80 { 105 {
81 ASSERT(isMainThread()); 106 ASSERT(isMainThread());
82 107
83 m_inputs.append(MIDIInput::create(this, id, manufacturer, name, version)); 108 m_inputs.append(MIDIInput::create(this, id, manufacturer, name, version));
84 } 109 }
85 110
86 void MIDIAccess::didAddOutputPort(const String& id, const String& manufacturer, const String& name, const String& version) 111 void MIDIAccess::didAddOutputPort(const String& id, const String& manufacturer, const String& name, const String& version)
87 { 112 {
88 ASSERT(isMainThread()); 113 ASSERT(isMainThread());
89 114
90 unsigned portIndex = m_outputs.size(); 115 unsigned portIndex = m_outputs.size();
91 m_outputs.append(MIDIOutput::create(this, portIndex, id, manufacturer, name, version)); 116 m_outputs.append(MIDIOutput::create(this, portIndex, id, manufacturer, name, version));
92 } 117 }
93 118
94 void MIDIAccess::didStartSession(bool success) 119 void MIDIAccess::didStartSession(bool success)
95 { 120 {
96 ASSERT(isMainThread()); 121 ASSERT(isMainThread());
97
98 m_hasAccess = success;
99 if (success) 122 if (success)
100 m_promise->fulfill(); 123 resolve();
101 else 124 else
102 m_promise->reject(DOMError::create("InvalidStateError")); 125 reject(DOMError::create("InvalidStateError"));
103 } 126 }
104 127
105 void MIDIAccess::didReceiveMIDIData(unsigned portIndex, const unsigned char* dat a, size_t length, double timeStamp) 128 void MIDIAccess::didReceiveMIDIData(unsigned portIndex, const unsigned char* dat a, size_t length, double timeStamp)
106 { 129 {
107 ASSERT(isMainThread()); 130 ASSERT(isMainThread());
108 131
109 if (m_hasAccess && portIndex < m_inputs.size()) { 132 if (m_state == Resolved && portIndex < m_inputs.size()) {
110 // Convert from time in seconds which is based on the time coordinate sy stem of monotonicallyIncreasingTime() 133 // Convert from time in seconds which is based on the time coordinate sy stem of monotonicallyIncreasingTime()
111 // into time in milliseconds (a DOMHighResTimeStamp) according to the sa me time coordinate system as performance.now(). 134 // into time in milliseconds (a DOMHighResTimeStamp) according to the sa me time coordinate system as performance.now().
112 // This is how timestamps are defined in the Web MIDI spec. 135 // This is how timestamps are defined in the Web MIDI spec.
113 Document* document = toDocument(executionContext()); 136 Document* document = toDocument(executionContext());
114 ASSERT(document); 137 ASSERT(document);
115 138
116 double timeStampInMilliseconds = 1000 * document->loader()->timing()->mo notonicTimeToZeroBasedDocumentTime(timeStamp); 139 double timeStampInMilliseconds = 1000 * document->loader()->timing()->mo notonicTimeToZeroBasedDocumentTime(timeStamp);
117 140
118 m_inputs[portIndex]->didReceiveMIDIData(portIndex, data, length, timeSta mpInMilliseconds); 141 m_inputs[portIndex]->didReceiveMIDIData(portIndex, data, length, timeSta mpInMilliseconds);
119 } 142 }
120 } 143 }
121 144
122 void MIDIAccess::sendMIDIData(unsigned portIndex, const unsigned char* data, siz e_t length, double timeStampInMilliseconds) 145 void MIDIAccess::sendMIDIData(unsigned portIndex, const unsigned char* data, siz e_t length, double timeStampInMilliseconds)
123 { 146 {
124 if (m_hasAccess && portIndex < m_outputs.size() && data && length > 0) { 147 if (m_state == Resolved && portIndex < m_outputs.size() && data && length > 0) {
125 // Convert from a time in milliseconds (a DOMHighResTimeStamp) according to the same time coordinate system as performance.now() 148 // Convert from a time in milliseconds (a DOMHighResTimeStamp) according to the same time coordinate system as performance.now()
126 // into a time in seconds which is based on the time coordinate system o f monotonicallyIncreasingTime(). 149 // into a time in seconds which is based on the time coordinate system o f monotonicallyIncreasingTime().
127 double timeStamp; 150 double timeStamp;
128 151
129 if (!timeStampInMilliseconds) { 152 if (!timeStampInMilliseconds) {
130 // We treat a value of 0 (which is the default value) as special, me aning "now". 153 // We treat a value of 0 (which is the default value) as special, me aning "now".
131 // We need to translate it exactly to 0 seconds. 154 // We need to translate it exactly to 0 seconds.
132 timeStamp = 0; 155 timeStamp = 0;
133 } else { 156 } else {
134 Document* document = toDocument(executionContext()); 157 Document* document = toDocument(executionContext());
135 ASSERT(document); 158 ASSERT(document);
136 double documentStartTime = document->loader()->timing()->referenceMo notonicTime(); 159 double documentStartTime = document->loader()->timing()->referenceMo notonicTime();
137 timeStamp = documentStartTime + 0.001 * timeStampInMilliseconds; 160 timeStamp = documentStartTime + 0.001 * timeStampInMilliseconds;
138 } 161 }
139 162
140 m_accessor->sendMIDIData(portIndex, data, length, timeStamp); 163 m_accessor->sendMIDIData(portIndex, data, length, timeStamp);
141 } 164 }
142 } 165 }
143 166
167 void MIDIAccess::suspend()
168 {
169 m_asyncResolveRunner.suspend();
170 m_asyncRejectRunner.suspend();
171 }
172
173 void MIDIAccess::resume()
174 {
175 m_asyncResolveRunner.resume();
176 m_asyncRejectRunner.resume();
177 }
178
144 void MIDIAccess::stop() 179 void MIDIAccess::stop()
145 { 180 {
146 m_hasAccess = false; 181 if (m_state == Stopped)
147 if (!m_requesting)
148 return; 182 return;
149 m_requesting = false; 183 m_error.clear();
150 Document* document = toDocument(executionContext()); 184 m_accessor.clear();
151 ASSERT(document); 185 m_asyncResolveRunner.stop();
152 MIDIController* controller = MIDIController::from(document->page()); 186 m_asyncRejectRunner.stop();
153 ASSERT(controller); 187 if (m_state == Requesting) {
154 controller->cancelSysExPermissionRequest(this); 188 Document* document = toDocument(executionContext());
189 ASSERT(document);
190 MIDIController* controller = MIDIController::from(document->page());
191 ASSERT(controller);
192 controller->cancelSysExPermissionRequest(this);
193 }
194 m_state = Stopped;
155 } 195 }
156 196
157 void MIDIAccess::startRequest() 197 bool MIDIAccess::hasPendingActivity() const
158 { 198 {
159 if (!m_promise->options()->sysex) { 199 return m_state == Requesting;
200 }
201
202 void MIDIAccess::permissionDenied()
203 {
204 ASSERT(isMainThread());
205 reject(DOMError::create("SecurityError"));
206 }
207
208 ScriptPromise MIDIAccess::startRequest()
209 {
210 ScriptPromise promise = ScriptPromise::createPending();
haraken 2014/03/13 02:22:33 Shall we add ASSERT(frame()) ?
yhirano 2014/03/13 05:43:22 Done.
211 promise.then(PostAction::create(toIsolate(executionContext()), this, Resolve d),
212 PostAction::create(toIsolate(executionContext()), this, Stopped));
213
214 m_resolver = MIDIAccessResolver::create(ScriptPromiseResolver::create(promis e, executionContext()), executionContext());
215 if (!m_options.sysex) {
160 m_accessor->startSession(); 216 m_accessor->startSession();
161 return; 217 return promise;
162 } 218 }
163 Document* document = toDocument(executionContext()); 219 Document* document = toDocument(executionContext());
164 ASSERT(document); 220 ASSERT(document);
165 MIDIController* controller = MIDIController::from(document->page()); 221 MIDIController* controller = MIDIController::from(document->page());
166 if (controller) { 222 if (controller) {
167 m_requesting = true;
168 controller->requestSysExPermission(this); 223 controller->requestSysExPermission(this);
169 } else { 224 } else {
170 permissionDenied(); 225 reject(DOMError::create("SecurityError"));
171 } 226 }
227 return promise;
172 } 228 }
173 229
174 void MIDIAccess::permissionDenied() 230 void MIDIAccess::resolve()
175 { 231 {
176 ASSERT(isMainThread()); 232 m_asyncResolveRunner.runAsync();
233 }
177 234
178 m_hasAccess = false; 235 void MIDIAccess::reject(PassRefPtr<DOMError> error)
179 m_promise->reject(DOMError::create("SecurityError")); 236 {
237 m_error = error;
238 m_asyncRejectRunner.runAsync();
239 }
240
241 void MIDIAccess::resolveNow()
242 {
243 m_resolver->resolve(this, executionContext());
244 }
245
246 void MIDIAccess::rejectNow()
247 {
248 m_resolver->reject(m_error.release().get(), executionContext());
249 }
250
251 void MIDIAccess::doPostAction(State state)
252 {
253 ASSERT(m_state == Requesting);
254 ASSERT(state == Resolved || state == Stopped);
255 m_error.clear();
256 if (state == Stopped) {
257 m_accessor.clear();
258 }
259 m_state = state;
180 } 260 }
181 261
182 void MIDIAccess::trace(Visitor* visitor) 262 void MIDIAccess::trace(Visitor* visitor)
183 { 263 {
184 visitor->trace(m_inputs); 264 visitor->trace(m_inputs);
185 visitor->trace(m_outputs); 265 visitor->trace(m_outputs);
186 visitor->trace(m_promise);
187 } 266 }
188 267
189 } // namespace WebCore 268 } // namespace WebCore
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698