| // 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; |
| |
| m_configuration = createSensorConfig(m_sensorOptions); |
| if (!m_configuration) { |
| reportError(); |
| return; |
| } |
| |
| DCHECK(m_sensorProxy); |
| auto startCallback = WTF::bind(&Sensor::onStartRequestCompleted, wrapWeakPersistent(this)); |
| m_sensorProxy->addConfiguration(m_configuration->Clone(), std::move(startCallback)); |
| } |
| |
| 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); |
| |
| m_sensorProxy->addObserver(this); |
| if (m_sensorProxy->isInitialized()) { |
| auto callback = WTF::bind(&Sensor::onStartRequestCompleted, wrapWeakPersistent(this)); |
| DCHECK(m_configuration); |
| m_sensorProxy->addConfiguration(m_configuration->Clone(), std::move(callback)); |
| } else { |
| m_sensorProxy->initialize(); |
| } |
| } |
| |
| 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 |