blob: 872afd786fdd7de5f4333a06813c33abd1eb43ad [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 "device/generic_sensor/public/interfaces/sensor.mojom-blink.h"
#include "modules/sensor/SensorErrorEvent.h"
#include "modules/sensor/SensorPollingStrategy.h"
#include "modules/sensor/SensorProviderProxy.h"
#include "modules/sensor/SensorReading.h"
#include "modules/sensor/SensorReadingEvent.h"
using namespace device::mojom::blink;
namespace blink {
Sensor::Sensor(ExecutionContext* executionContext, const SensorOptions& sensorOptions, SensorType type)
: ActiveScriptWrappable(this)
, ContextLifecycleObserver(executionContext)
, PageVisibilityObserver(toDocument(executionContext)->page())
, m_sensorOptions(sensorOptions)
, m_type(type)
, m_state(Sensor::SensorState::IDLE)
, m_storedData()
{
}
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::ACTIVE:
return "active";
case Sensor::SensorState::ERRORED:
return "errored";
default:
NOTREACHED();
}
return "idle";
}
// Getters
String Sensor::state() const
{
return ToString(m_state);
}
SensorReading* Sensor::reading() const
{
return m_sensorReading.get();
}
DEFINE_TRACE(Sensor)
{
visitor->trace(m_sensorProxy);
visitor->trace(m_sensorReading);
ActiveScriptWrappable::trace(visitor);
ContextLifecycleObserver::trace(visitor);
PageVisibilityObserver::trace(visitor);
EventTargetWithInlineData::trace(visitor);
}
bool Sensor::hasPendingActivity() const
{
if (m_state == Sensor::SensorState::IDLE || m_state == Sensor::SensorState::ERRORED)
return false;
return hasEventListeners();
}
void Sensor::initSensorProxyIfNeeded()
{
if (m_sensorProxy)
return;
Document* document = toDocument(getExecutionContext());
if (!document || !document->frame())
return;
m_sensorProxy = SensorProviderProxy::from(document->frame())->getOrCreateSensor(m_type);
}
void Sensor::contextDestroyed()
{
if (m_state == Sensor::SensorState::ACTIVE || m_state == Sensor::SensorState::ACTIVATING)
stopListening();
}
void Sensor::onSensorInitialized()
{
if (m_state != Sensor::SensorState::ACTIVATING)
return;
startListening();
}
void Sensor::onSensorReadingChanged()
{
if (m_polling)
m_polling->onSensorReadingChanged();
}
void Sensor::onSensorError()
{
reportError();
}
void Sensor::onStartRequestCompleted(bool result)
{
if (m_state != Sensor::SensorState::ACTIVATING)
return;
if (!result) {
reportError();
return;
}
DCHECK(m_configuration);
DCHECK(m_sensorProxy);
auto pollCallback = WTF::bind(&Sensor::pollForData, wrapWeakPersistent(this));
DCHECK_GT(m_configuration->frequency, 0);
m_polling = SensorPollingStrategy::create(1 / m_configuration->frequency, std::move(pollCallback), m_sensorProxy->reportingMode());
updateState(Sensor::SensorState::ACTIVE);
}
void Sensor::onStopRequestCompleted(bool result)
{
if (m_state == Sensor::SensorState::IDLE)
return;
if (!result)
reportError();
DCHECK(m_sensorProxy);
m_sensorProxy->removeObserver(this);
}
void Sensor::pageVisibilityChanged()
{
updatePollingStatus();
if (!m_sensorProxy || !m_sensorProxy->isInitialized())
return;
if (page()->visibilityState() != PageVisibilityStateVisible) {
m_sensorProxy->suspend();
} else {
m_sensorProxy->resume();
}
}
void Sensor::startListening()
{
DCHECK(m_sensorProxy);
updateState(Sensor::SensorState::ACTIVATING);
if (!m_sensorReading) {
m_sensorReading = createSensorReading(m_sensorProxy);
DCHECK(m_sensorReading);
}
m_sensorProxy->addObserver(this);
if (!m_sensorProxy->isInitialized()) {
m_sensorProxy->initialize();
return;
}
if (!m_configuration) {
m_configuration = createSensorConfig(m_sensorOptions, *m_sensorProxy->defaultConfig());
DCHECK(m_configuration);
}
auto startCallback = WTF::bind(&Sensor::onStartRequestCompleted, wrapWeakPersistent(this));
m_sensorProxy->addConfiguration(m_configuration->Clone(), std::move(startCallback));
}
void Sensor::stopListening()
{
DCHECK(m_sensorProxy);
m_sensorReading = nullptr;
updateState(Sensor::SensorState::IDLE);
if (m_sensorProxy->isInitialized()) {
auto callback = WTF::bind(&Sensor::onStopRequestCompleted, wrapWeakPersistent(this));
DCHECK(m_configuration);
m_sensorProxy->removeConfiguration(m_configuration->Clone(), std::move(callback));
} else {
m_sensorProxy->removeObserver(this);
}
}
void Sensor::pollForData()
{
if (m_state != Sensor::SensorState::ACTIVE) {
DCHECK(m_polling);
m_polling->stopPolling();
return;
}
DCHECK(m_sensorProxy);
DCHECK(m_sensorProxy->isInitialized());
m_sensorProxy->updateInternalReading();
DCHECK(m_sensorReading);
if (m_sensorReading->isReadingUpdated(m_storedData))
dispatchEvent(SensorReadingEvent::create(EventTypeNames::change, m_sensorReading));
m_storedData = m_sensorProxy->reading();
}
void Sensor::updateState(Sensor::SensorState newState)
{
if (newState == m_state)
return;
m_state = newState;
dispatchEvent(Event::create(EventTypeNames::statechange));
updatePollingStatus();
}
void Sensor::reportError()
{
updateState(Sensor::SensorState::ERRORED);
// TODO(Mikhail) : Dispatch Sensor Error event.
}
void Sensor::updatePollingStatus()
{
if (!m_polling)
return;
if (m_state != Sensor::SensorState::ACTIVE
|| page()->visibilityState() != PageVisibilityStateVisible) {
m_polling->stopPolling();
} else {
m_polling->startPolling();
}
}
} // namespace blink