blob: 1e4fd25384987de49eba22288b353f723b5e5ba9 [file] [log] [blame]
// Copyright 2014 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 "third_party/blink/renderer/modules/netinfo/network_information.h"
#include <algorithm>
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/events/event.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/modules/event_target_modules.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
namespace {
Settings* GetSettings(ExecutionContext* execution_context) {
if (!execution_context)
return nullptr;
if (!execution_context->IsDocument())
return nullptr;
Document* document = ToDocument(execution_context);
// |document| is guaranteed to be non-null since |execution_context| is
// non-null.
return document->GetSettings();
}
bool IsInDataSaverHoldbackWebApi(ExecutionContext* execution_context) {
Settings* settings = GetSettings(execution_context);
if (!settings)
return false;
return settings->GetDataSaverHoldbackWebApi();
}
String ConnectionTypeToString(WebConnectionType type) {
switch (type) {
case kWebConnectionTypeCellular2G:
case kWebConnectionTypeCellular3G:
case kWebConnectionTypeCellular4G:
return "cellular";
case kWebConnectionTypeBluetooth:
return "bluetooth";
case kWebConnectionTypeEthernet:
return "ethernet";
case kWebConnectionTypeWifi:
return "wifi";
case kWebConnectionTypeWimax:
return "wimax";
case kWebConnectionTypeOther:
return "other";
case kWebConnectionTypeNone:
return "none";
case kWebConnectionTypeUnknown:
return "unknown";
}
NOTREACHED();
return "none";
}
} // namespace
NetworkInformation* NetworkInformation::Create(ExecutionContext* context) {
return new NetworkInformation(context);
}
NetworkInformation::~NetworkInformation() {
DCHECK(!IsObserving());
}
bool NetworkInformation::IsObserving() const {
return !!connection_observer_handle_;
};
String NetworkInformation::type() const {
// type_ is only updated when listening for events, so ask
// networkStateNotifier if not listening (crbug.com/379841).
if (!IsObserving())
return ConnectionTypeToString(GetNetworkStateNotifier().ConnectionType());
// If observing, return m_type which changes when the event fires, per spec.
return ConnectionTypeToString(type_);
}
double NetworkInformation::downlinkMax() const {
if (!IsObserving())
return GetNetworkStateNotifier().MaxBandwidth();
return downlink_max_mbps_;
}
String NetworkInformation::effectiveType() const {
// effective_type_ is only updated when listening for events, so ask
// networkStateNotifier if not listening (crbug.com/379841).
if (!IsObserving()) {
return NetworkStateNotifier::EffectiveConnectionTypeToString(
GetNetworkStateNotifier().EffectiveType());
}
// If observing, return m_type which changes when the event fires, per spec.
return NetworkStateNotifier::EffectiveConnectionTypeToString(effective_type_);
}
unsigned long NetworkInformation::rtt() const {
if (!IsObserving()) {
return GetNetworkStateNotifier().RoundRtt(
Host(), GetNetworkStateNotifier().HttpRtt());
}
return http_rtt_msec_;
}
double NetworkInformation::downlink() const {
if (!IsObserving()) {
return GetNetworkStateNotifier().RoundMbps(
Host(), GetNetworkStateNotifier().DownlinkThroughputMbps());
}
return downlink_mbps_;
}
bool NetworkInformation::saveData() const {
return IsObserving()
? save_data_
: GetNetworkStateNotifier().SaveDataEnabled() &&
!IsInDataSaverHoldbackWebApi(GetExecutionContext());
}
void NetworkInformation::ConnectionChange(
WebConnectionType type,
double downlink_max_mbps,
WebEffectiveConnectionType effective_type,
const base::Optional<TimeDelta>& http_rtt,
const base::Optional<TimeDelta>& transport_rtt,
const base::Optional<double>& downlink_mbps,
bool save_data) {
DCHECK(GetExecutionContext()->IsContextThread());
const String host = Host();
unsigned long new_http_rtt_msec =
GetNetworkStateNotifier().RoundRtt(host, http_rtt);
double new_downlink_mbps =
GetNetworkStateNotifier().RoundMbps(host, downlink_mbps);
// This can happen if the observer removes and then adds itself again
// during notification, or if |transport_rtt| was the only metric that
// changed.
if (type_ == type && downlink_max_mbps_ == downlink_max_mbps &&
effective_type_ == effective_type &&
http_rtt_msec_ == new_http_rtt_msec &&
downlink_mbps_ == new_downlink_mbps && save_data_ == save_data) {
return;
}
if (!RuntimeEnabledFeatures::NetInfoDownlinkMaxEnabled() &&
effective_type_ == effective_type &&
http_rtt_msec_ == new_http_rtt_msec &&
downlink_mbps_ == new_downlink_mbps && save_data_ == save_data) {
return;
}
bool type_changed =
RuntimeEnabledFeatures::NetInfoDownlinkMaxEnabled() &&
(type_ != type || downlink_max_mbps_ != downlink_max_mbps);
type_ = type;
downlink_max_mbps_ = downlink_max_mbps;
effective_type_ = effective_type;
http_rtt_msec_ = new_http_rtt_msec;
downlink_mbps_ = new_downlink_mbps;
save_data_ = save_data;
if (type_changed)
DispatchEvent(Event::Create(EventTypeNames::typechange));
DispatchEvent(Event::Create(EventTypeNames::change));
}
const AtomicString& NetworkInformation::InterfaceName() const {
return EventTargetNames::NetworkInformation;
}
ExecutionContext* NetworkInformation::GetExecutionContext() const {
return ContextLifecycleObserver::GetExecutionContext();
}
void NetworkInformation::AddedEventListener(
const AtomicString& event_type,
RegisteredEventListener& registered_listener) {
EventTargetWithInlineData::AddedEventListener(event_type,
registered_listener);
StartObserving();
}
void NetworkInformation::RemovedEventListener(
const AtomicString& event_type,
const RegisteredEventListener& registered_listener) {
EventTargetWithInlineData::RemovedEventListener(event_type,
registered_listener);
if (!HasEventListeners())
StopObserving();
}
void NetworkInformation::RemoveAllEventListeners() {
EventTargetWithInlineData::RemoveAllEventListeners();
DCHECK(!HasEventListeners());
StopObserving();
}
bool NetworkInformation::HasPendingActivity() const {
DCHECK(context_stopped_ || IsObserving() == HasEventListeners());
// Prevent collection of this object when there are active listeners.
return IsObserving();
}
void NetworkInformation::ContextDestroyed(ExecutionContext*) {
context_stopped_ = true;
StopObserving();
}
void NetworkInformation::StartObserving() {
if (!IsObserving() && !context_stopped_) {
type_ = GetNetworkStateNotifier().ConnectionType();
DCHECK(!connection_observer_handle_);
connection_observer_handle_ =
GetNetworkStateNotifier().AddConnectionObserver(
this, GetExecutionContext()->GetTaskRunner(TaskType::kNetworking));
}
}
void NetworkInformation::StopObserving() {
if (IsObserving()) {
DCHECK(connection_observer_handle_);
connection_observer_handle_ = nullptr;
}
}
NetworkInformation::NetworkInformation(ExecutionContext* context)
: ContextLifecycleObserver(context),
type_(GetNetworkStateNotifier().ConnectionType()),
downlink_max_mbps_(GetNetworkStateNotifier().MaxBandwidth()),
effective_type_(GetNetworkStateNotifier().EffectiveType()),
http_rtt_msec_(GetNetworkStateNotifier().RoundRtt(
Host(),
GetNetworkStateNotifier().HttpRtt())),
downlink_mbps_(GetNetworkStateNotifier().RoundMbps(
Host(),
GetNetworkStateNotifier().DownlinkThroughputMbps())),
save_data_(GetNetworkStateNotifier().SaveDataEnabled() &&
!IsInDataSaverHoldbackWebApi(GetExecutionContext())),
context_stopped_(false) {
DCHECK_LE(1u, GetNetworkStateNotifier().RandomizationSalt());
DCHECK_GE(20u, GetNetworkStateNotifier().RandomizationSalt());
}
void NetworkInformation::Trace(blink::Visitor* visitor) {
EventTargetWithInlineData::Trace(visitor);
ContextLifecycleObserver::Trace(visitor);
}
const String NetworkInformation::Host() const {
return GetExecutionContext() ? GetExecutionContext()->Url().Host() : String();
}
} // namespace blink