blob: 5f83ae85ca72937f9f2039bbe37cc05db03aed89 [file] [log] [blame]
// Copyright 2016 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 "modules/sensor/Sensor.h"
#include "core/dom/Document.h"
#include "core/dom/ExceptionCode.h"
#include "core/dom/ExecutionContextTask.h"
#include "core/dom/TaskRunnerHelper.h"
#include "core/inspector/ConsoleMessage.h"
#include "device/generic_sensor/public/interfaces/sensor.mojom-blink.h"
#include "modules/sensor/SensorErrorEvent.h"
#include "modules/sensor/SensorProviderProxy.h"
#include "modules/sensor/SensorReading.h"
using namespace device::mojom::blink;
namespace blink {
Sensor::Sensor(ExecutionContext* executionContext,
const SensorOptions& sensorOptions,
ExceptionState& exceptionState,
SensorType type)
: ContextLifecycleObserver(executionContext),
m_sensorOptions(sensorOptions),
m_type(type),
m_state(Sensor::SensorState::Idle),
m_lastUpdateTimestamp(0.0) {
// Check secure context.
String errorMessage;
if (!executionContext->isSecureContext(errorMessage)) {
exceptionState.throwDOMException(SecurityError, errorMessage);
return;
}
// Check top-level browsing context.
if (!toDocument(executionContext)->domWindow()->frame() ||
!toDocument(executionContext)->frame()->isMainFrame()) {
exceptionState.throwSecurityError(
"Must be in a top-level browsing context");
return;
}
// Check the given frequency value.
if (m_sensorOptions.hasFrequency()) {
double frequency = m_sensorOptions.frequency();
if (frequency <= 0.0) {
exceptionState.throwRangeError("Frequency must be positive.");
return;
}
if (frequency > SensorConfiguration::kMaxAllowedFrequency) {
m_sensorOptions.setFrequency(SensorConfiguration::kMaxAllowedFrequency);
ConsoleMessage* consoleMessage = ConsoleMessage::create(
JSMessageSource, InfoMessageLevel, "Frequency is limited to 60 Hz.");
executionContext->addConsoleMessage(consoleMessage);
}
}
}
Sensor::~Sensor() = default;
void Sensor::start(ScriptState* scriptState, ExceptionState& exceptionState) {
if (m_state != Sensor::SensorState::Idle &&
m_state != Sensor::SensorState::Errored) {
exceptionState.throwDOMException(
InvalidStateError,
"Cannot start because SensorState is not Idle or errored");
return;
}
initSensorProxyIfNeeded();
if (!m_sensorProxy) {
exceptionState.throwDOMException(
InvalidStateError, "The Sensor is no longer associated to a frame.");
return;
}
startListening();
}
void Sensor::stop(ScriptState*, ExceptionState& exceptionState) {
if (m_state == Sensor::SensorState::Idle ||
m_state == Sensor::SensorState::Errored) {
exceptionState.throwDOMException(
InvalidStateError,
"Cannot stop because SensorState is either Idle or errored");
return;
}
stopListening();
}
static String ToString(Sensor::SensorState state) {
switch (state) {
case Sensor::SensorState::Idle:
return "idle";
case Sensor::SensorState::Activating:
return "activating";
case Sensor::SensorState::Activated:
return "activated";
case Sensor::SensorState::Errored:
return "errored";
default:
NOTREACHED();
}
return "idle";
}
// Getters
String Sensor::state() const {
return ToString(m_state);
}
SensorReading* Sensor::reading() const {
if (m_state != Sensor::SensorState::Activated)
return nullptr;
DCHECK(m_sensorProxy);
return m_sensorProxy->sensorReading();
}
DEFINE_TRACE(Sensor) {
visitor->trace(m_sensorProxy);
ActiveScriptWrappable::trace(visitor);
ContextLifecycleObserver::trace(visitor);
EventTargetWithInlineData::trace(visitor);
}
bool Sensor::hasPendingActivity() const {
if (m_state == Sensor::SensorState::Idle ||
m_state == Sensor::SensorState::Errored)
return false;
return getExecutionContext() && hasEventListeners();
}
auto Sensor::createSensorConfig() -> SensorConfigurationPtr {
auto result = SensorConfiguration::New();
double defaultFrequency = m_sensorProxy->defaultConfig()->frequency;
double maximumFrequency = m_sensorProxy->maximumFrequency();
double frequency = m_sensorOptions.hasFrequency()
? m_sensorOptions.frequency()
: defaultFrequency;
if (frequency > maximumFrequency)
frequency = maximumFrequency;
result->frequency = frequency;
return result;
}
void Sensor::initSensorProxyIfNeeded() {
if (m_sensorProxy)
return;
Document* document = toDocument(getExecutionContext());
if (!document || !document->frame())
return;
auto provider = SensorProviderProxy::from(document->frame());
m_sensorProxy = provider->getSensorProxy(m_type);
if (!m_sensorProxy) {
m_sensorProxy = provider->createSensorProxy(m_type, document,
createSensorReadingFactory());
}
}
void Sensor::contextDestroyed() {
if (m_state == Sensor::SensorState::Activated ||
m_state == Sensor::SensorState::Activating)
stopListening();
}
void Sensor::onSensorInitialized() {
if (m_state != Sensor::SensorState::Activating)
return;
startListening();
}
void Sensor::onSensorReadingChanged(double timestamp) {
if (m_state != Sensor::SensorState::Activated)
return;
DCHECK_GT(m_configuration->frequency, 0.0);
double period = 1 / m_configuration->frequency;
if (timestamp - m_lastUpdateTimestamp >= period) {
m_lastUpdateTimestamp = timestamp;
notifySensorReadingChanged();
}
}
void Sensor::onSensorError(ExceptionCode code,
const String& sanitizedMessage,
const String& unsanitizedMessage) {
reportError(code, sanitizedMessage, unsanitizedMessage);
}
void Sensor::onStartRequestCompleted(bool result) {
if (m_state != Sensor::SensorState::Activating)
return;
if (!result) {
reportError(
OperationError,
"start() call has failed possibly due to inappropriate options.");
return;
}
updateState(Sensor::SensorState::Activated);
}
void Sensor::startListening() {
DCHECK(m_sensorProxy);
updateState(Sensor::SensorState::Activating);
m_sensorProxy->addObserver(this);
if (!m_sensorProxy->isInitialized()) {
m_sensorProxy->initialize();
return;
}
if (!m_configuration) {
m_configuration = createSensorConfig();
DCHECK(m_configuration);
DCHECK(m_configuration->frequency > 0 &&
m_configuration->frequency <= m_sensorProxy->maximumFrequency());
}
auto startCallback =
WTF::bind(&Sensor::onStartRequestCompleted, wrapWeakPersistent(this));
m_sensorProxy->addConfiguration(m_configuration->Clone(),
std::move(startCallback));
}
void Sensor::stopListening() {
DCHECK(m_sensorProxy);
updateState(Sensor::SensorState::Idle);
if (m_sensorProxy->isInitialized()) {
DCHECK(m_configuration);
m_sensorProxy->removeConfiguration(m_configuration->Clone());
}
m_sensorProxy->removeObserver(this);
}
void Sensor::updateState(Sensor::SensorState newState) {
if (newState == m_state)
return;
if (newState == SensorState::Activated && getExecutionContext()) {
DCHECK_EQ(SensorState::Activating, m_state);
getExecutionContext()->postTask(
TaskType::Sensor, BLINK_FROM_HERE,
createSameThreadTask(&Sensor::notifyOnActivate,
wrapWeakPersistent(this)));
}
m_state = newState;
}
void Sensor::reportError(ExceptionCode code,
const String& sanitizedMessage,
const String& unsanitizedMessage) {
updateState(Sensor::SensorState::Errored);
if (getExecutionContext()) {
auto error =
DOMException::create(code, sanitizedMessage, unsanitizedMessage);
getExecutionContext()->postTask(
TaskType::Sensor, BLINK_FROM_HERE,
createSameThreadTask(&Sensor::notifyError, wrapWeakPersistent(this),
wrapPersistent(error)));
}
}
void Sensor::notifySensorReadingChanged() {
DCHECK(m_sensorProxy);
DCHECK(m_sensorProxy->sensorReading());
if (m_sensorProxy->sensorReading()->isReadingUpdated(m_storedData)) {
m_storedData = m_sensorProxy->sensorReading()->data();
dispatchEvent(Event::create(EventTypeNames::change));
}
}
void Sensor::notifyOnActivate() {
dispatchEvent(Event::create(EventTypeNames::activate));
}
void Sensor::notifyError(DOMException* error) {
dispatchEvent(
SensorErrorEvent::create(EventTypeNames::error, std::move(error)));
}
} // namespace blink