blob: f0ed3d8095b0b6f98af81ac24997d34f138ad836 [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 "core/timing/DOMWindowPerformance.h"
#include "core/timing/Performance.h"
#include "device/generic_sensor/public/interfaces/sensor.mojom-blink.h"
#include "modules/sensor/SensorErrorEvent.h"
#include "modules/sensor/SensorProviderProxy.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::Unconnected),
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() {
if (m_state != Sensor::SensorState::Unconnected &&
m_state != Sensor::SensorState::Idle &&
m_state != Sensor::SensorState::Errored)
return;
initSensorProxyIfNeeded();
if (!m_sensorProxy) {
reportError(InvalidStateError,
"The Sensor is no longer associated to a frame.");
return;
}
m_lastUpdateTimestamp = WTF::monotonicallyIncreasingTime();
startListening();
}
void Sensor::stop() {
if (m_state == Sensor::SensorState::Unconnected ||
m_state == Sensor::SensorState::Idle ||
m_state == Sensor::SensorState::Errored)
return;
stopListening();
}
static String ToString(Sensor::SensorState state) {
switch (state) {
case Sensor::SensorState::Unconnected:
return "unconnected";
case Sensor::SensorState::Activating:
return "activating";
case Sensor::SensorState::Activated:
return "activated";
case Sensor::SensorState::Idle:
return "idle";
case Sensor::SensorState::Errored:
return "errored";
default:
NOTREACHED();
}
return "idle";
}
// Getters
String Sensor::state() const {
return ToString(m_state);
}
DOMHighResTimeStamp Sensor::timestamp(ScriptState* scriptState,
bool& isNull) const {
if (!canReturnReadings()) {
isNull = true;
return 0.0;
}
LocalDOMWindow* window = scriptState->domWindow();
if (!window) {
isNull = true;
return 0.0;
}
Performance* performance = DOMWindowPerformance::performance(*window);
DCHECK(performance);
DCHECK(m_sensorProxy);
isNull = false;
return performance->monotonicTimeToDOMHighResTimeStamp(
m_sensorProxy->reading().timestamp);
}
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::Unconnected ||
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;
}
double Sensor::readingValue(int index, bool& isNull) const {
if (!canReturnReadings()) {
isNull = true;
return 0.0;
}
DCHECK(m_sensorProxy);
DCHECK(index >= 0 && index < device::SensorReading::kValuesCount);
return m_sensorProxy->reading().values[index];
}
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->page());
}
void Sensor::contextDestroyed(ExecutionContext*) {
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);
// The initial value for m_lastUpdateTimestamp is set to current time,
// so that the first reading update will be notified considering the given
// frequency hint.
m_lastUpdateTimestamp = WTF::monotonicallyIncreasingTime();
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);
if (m_sensorProxy->reading().timestamp != m_storedData.timestamp) {
m_storedData = m_sensorProxy->reading();
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)));
}
bool Sensor::canReturnReadings() const {
if (m_state != Sensor::SensorState::Activated)
return false;
DCHECK(m_sensorProxy);
return m_sensorProxy->reading().timestamp != 0.0;
}
} // namespace blink