blob: 8aac32b8908838871f72bea4eeb0f3587593327f [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// 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 "base/task/single_thread_task_runner.h"
#include "third_party/blink/public/common/features.h"
#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/garbage_collected.h"
namespace blink {
using midi::mojom::PortState;
using midi::mojom::Result;
MIDIAccessInitializer::MIDIAccessInitializer(ScriptState* script_state,
const MIDIOptions* options)
: resolver_(MakeGarbageCollected<ScriptPromiseResolverTyped<MIDIAccess>>(
script_state)),
options_(options),
permission_service_(ExecutionContext::From(script_state)) {}
ScriptPromiseTyped<MIDIAccess> MIDIAccessInitializer::Start(
LocalDOMWindow* window) {
// See https://bit.ly/2S0zRAS for task types.
scoped_refptr<base::SingleThreadTaskRunner> task_runner =
window->GetTaskRunner(TaskType::kMiscPlatformAPI);
ConnectToPermissionService(
window,
permission_service_.BindNewPipeAndPassReceiver(std::move(task_runner)));
permission_service_->RequestPermission(
CreateMidiPermissionDescriptor(
base::FeatureList::IsEnabled(blink::features::kBlockMidiByDefault)
? true
: options_->hasSysex() && options_->sysex()),
LocalFrame::HasTransientUserActivation(window->GetFrame()),
WTF::BindOnce(&MIDIAccessInitializer::OnPermissionsUpdated,
WrapPersistent(this)));
return resolver_->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, MIDIPortType::kInput, 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, MIDIPortType::kOutput, 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:
NOTREACHED();
return;
case Result::OK:
resolver_->Resolve(MakeGarbageCollected<MIDIAccess>(
dispatcher_, options_->hasSysex() && options_->sysex(),
port_descriptors_, resolver_->GetExecutionContext()));
return;
case Result::NOT_SUPPORTED:
resolver_->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNotSupportedError));
return;
case Result::INITIALIZATION_ERROR:
resolver_->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kInvalidStateError,
"Platform dependent initialization failed."));
return;
}
}
void MIDIAccessInitializer::Trace(Visitor* visitor) const {
visitor->Trace(resolver_);
visitor->Trace(dispatcher_);
visitor->Trace(options_);
visitor->Trace(permission_service_);
}
void MIDIAccessInitializer::StartSession() {
DCHECK(!dispatcher_);
dispatcher_ =
MakeGarbageCollected<MIDIDispatcher>(resolver_->GetExecutionContext());
dispatcher_->SetClient(this);
}
void MIDIAccessInitializer::OnPermissionsUpdated(
mojom::blink::PermissionStatus status) {
permission_service_.reset();
if (status == mojom::blink::PermissionStatus::GRANTED) {
StartSession();
} else {
resolver_->Reject(
MakeGarbageCollected<DOMException>(DOMExceptionCode::kSecurityError));
}
}
void MIDIAccessInitializer::OnPermissionUpdated(
mojom::blink::PermissionStatus status) {
permission_service_.reset();
if (status == mojom::blink::PermissionStatus::GRANTED) {
StartSession();
} else {
resolver_->Reject(
MakeGarbageCollected<DOMException>(DOMExceptionCode::kSecurityError));
}
}
} // namespace blink