blob: df8c396854c9c7fe5334fd2c48161525b3f0e248 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/webmidi/midi_access_initializer.h"
#include <memory>
#include <utility>
#include "third_party/blink/public/mojom/permissions/permission.mojom-blink.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_midi_options.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/navigator.h"
#include "third_party/blink/renderer/modules/permissions/permission_utils.h"
#include "third_party/blink/renderer/modules/webmidi/midi_access.h"
#include "third_party/blink/renderer/modules/webmidi/midi_port.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/mojo/mojo_helper.h"
namespace blink {
using midi::mojom::PortState;
using midi::mojom::Result;
using mojom::blink::PermissionStatus;
MIDIAccessInitializer::MIDIAccessInitializer(ScriptState* script_state,
const MIDIOptions* options)
: ScriptPromiseResolver(script_state),
options_(options),
permission_service_(ExecutionContext::From(script_state)) {}
void MIDIAccessInitializer::ContextDestroyed() {
ScriptPromiseResolver::ContextDestroyed();
}
ScriptPromise MIDIAccessInitializer::Start() {
ScriptPromise promise = this->Promise();
// See https://bit.ly/2S0zRAS for task types.
scoped_refptr<base::SingleThreadTaskRunner> task_runner =
GetExecutionContext()->GetTaskRunner(TaskType::kMiscPlatformAPI);
ConnectToPermissionService(
GetExecutionContext(),
permission_service_.BindNewPipeAndPassReceiver(std::move(task_runner)));
LocalDOMWindow* window = To<LocalDOMWindow>(GetExecutionContext());
permission_service_->RequestPermission(
CreateMidiPermissionDescriptor(options_->hasSysex() && options_->sysex()),
LocalFrame::HasTransientUserActivation(window->GetFrame()),
WTF::Bind(&MIDIAccessInitializer::OnPermissionsUpdated,
WrapPersistent(this)));
return promise;
}
void MIDIAccessInitializer::DidAddInputPort(const String& id,
const String& manufacturer,
const String& name,
const String& version,
PortState state) {
DCHECK(dispatcher_);
port_descriptors_.push_back(PortDescriptor(
id, manufacturer, name, MIDIPort::kTypeInput, version, state));
}
void MIDIAccessInitializer::DidAddOutputPort(const String& id,
const String& manufacturer,
const String& name,
const String& version,
PortState state) {
DCHECK(dispatcher_);
port_descriptors_.push_back(PortDescriptor(
id, manufacturer, name, MIDIPort::kTypeOutput, version, state));
}
void MIDIAccessInitializer::DidSetInputPortState(unsigned port_index,
PortState state) {
// didSetInputPortState() is not allowed to call before didStartSession()
// is called. Once didStartSession() is called, MIDIAccessorClient methods
// are delegated to MIDIAccess. See constructor of MIDIAccess.
NOTREACHED();
}
void MIDIAccessInitializer::DidSetOutputPortState(unsigned port_index,
PortState state) {
// See comments on didSetInputPortState().
NOTREACHED();
}
void MIDIAccessInitializer::DidStartSession(Result result) {
DCHECK(dispatcher_);
// We would also have AbortError and SecurityError according to the spec.
// SecurityError is handled in onPermission(s)Updated().
switch (result) {
case Result::NOT_INITIALIZED:
break;
case Result::OK:
return Resolve(MakeGarbageCollected<MIDIAccess>(
dispatcher_, options_->hasSysex() && options_->sysex(),
port_descriptors_, GetExecutionContext()));
case Result::NOT_SUPPORTED:
return Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNotSupportedError));
case Result::INITIALIZATION_ERROR:
return Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kInvalidStateError,
"Platform dependent initialization failed."));
}
NOTREACHED();
Reject(
MakeGarbageCollected<DOMException>(DOMExceptionCode::kInvalidStateError,
"Unknown internal error occurred."));
}
void MIDIAccessInitializer::Trace(Visitor* visitor) const {
visitor->Trace(dispatcher_);
visitor->Trace(options_);
visitor->Trace(permission_service_);
ScriptPromiseResolver::Trace(visitor);
}
ExecutionContext* MIDIAccessInitializer::GetExecutionContext() const {
return ExecutionContext::From(GetScriptState());
}
void MIDIAccessInitializer::StartSession() {
DCHECK(!dispatcher_);
dispatcher_ = MakeGarbageCollected<MIDIDispatcher>(GetExecutionContext());
dispatcher_->SetClient(this);
}
void MIDIAccessInitializer::OnPermissionsUpdated(PermissionStatus status) {
permission_service_.reset();
if (status == PermissionStatus::GRANTED) {
StartSession();
} else {
Reject(
MakeGarbageCollected<DOMException>(DOMExceptionCode::kSecurityError));
}
}
void MIDIAccessInitializer::OnPermissionUpdated(PermissionStatus status) {
permission_service_.reset();
if (status == PermissionStatus::GRANTED) {
StartSession();
} else {
Reject(
MakeGarbageCollected<DOMException>(DOMExceptionCode::kSecurityError));
}
}
} // namespace blink