chromium/extensions/browser/api/networking_private/networking_private_event_router_chromeos.cc

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "extensions/browser/api/networking_private/networking_private_event_router.h"

#include "base/memory/raw_ptr.h"
#include "chromeos/ash/components/network/device_state.h"
#include "chromeos/ash/components/network/network_certificate_handler.h"
#include "chromeos/ash/components/network/network_event_log.h"
#include "chromeos/ash/components/network/network_state.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "chromeos/ash/components/network/network_state_handler_observer.h"
#include "chromeos/ash/components/network/onc/onc_translator.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "content/public/browser/browser_context.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/api/networking_private.h"

using ::ash::DeviceState;
using ::ash::NetworkHandler;
using ::ash::NetworkState;
using ::ash::NetworkStateHandler;

namespace extensions {

namespace {

api::networking_private::CaptivePortalStatus GetCaptivePortalStatus(
    const NetworkState* network) {
  if (!network) {
    return api::networking_private::CaptivePortalStatus::kUnknown;
  }
  if (!network->IsConnectedState()) {
    return api::networking_private::CaptivePortalStatus::kOffline;
  }
  switch (network->GetPortalState()) {
    case NetworkState::PortalState::kUnknown:
      return api::networking_private::CaptivePortalStatus::kUnknown;
    case NetworkState::PortalState::kOnline:
      return api::networking_private::CaptivePortalStatus::kOnline;
    case NetworkState::PortalState::kPortalSuspected:
    case NetworkState::PortalState::kPortal:
    case NetworkState::PortalState::kNoInternet:
      return api::networking_private::CaptivePortalStatus::kPortal;
  }
}

}  // namespace

class NetworkingPrivateEventRouterImpl
    : public NetworkingPrivateEventRouter,
      public ash::NetworkStateHandlerObserver,
      public ash::NetworkCertificateHandler::Observer {
 public:
  explicit NetworkingPrivateEventRouterImpl(content::BrowserContext* context);

  NetworkingPrivateEventRouterImpl(const NetworkingPrivateEventRouterImpl&) =
      delete;
  NetworkingPrivateEventRouterImpl& operator=(
      const NetworkingPrivateEventRouterImpl&) = delete;

  ~NetworkingPrivateEventRouterImpl() override;

 protected:
  // KeyedService overrides:
  void Shutdown() override;

  // EventRouter::Observer overrides:
  void OnListenerAdded(const EventListenerInfo& details) override;
  void OnListenerRemoved(const EventListenerInfo& details) override;

  // NetworkStateHandlerObserver overrides:
  void NetworkListChanged() override;
  void DeviceListChanged() override;
  void PortalStateChanged(const NetworkState* default_network,
                          NetworkState::PortalState portal_state) override;
  void NetworkPropertiesUpdated(const NetworkState* network) override;
  void DevicePropertiesUpdated(const DeviceState* device) override;
  void ScanCompleted(const DeviceState* device) override;

  // NetworkCertificateHandler::Observer overrides:
  void OnCertificatesChanged() override;

 private:
  // Decide if we should listen for network changes or not. If there are any
  // JavaScript listeners registered for the onNetworkChanged event, then we
  // want to register for change notification from the network state handler.
  // Otherwise, we want to unregister and not be listening to network changes.
  void StartOrStopListeningForNetworkChanges();

  raw_ptr<content::BrowserContext> context_;
  bool listening_ = false;
};

NetworkingPrivateEventRouterImpl::NetworkingPrivateEventRouterImpl(
    content::BrowserContext* context)
    : context_(context) {
  // Register with the event router so we know when renderers are listening to
  // our events. We first check and see if there *is* an event router, because
  // some unit tests try to create all context services, but don't initialize
  // the event router first.
  EventRouter* event_router = EventRouter::Get(context_);
  if (event_router) {
    event_router->RegisterObserver(
        this, api::networking_private::OnNetworksChanged::kEventName);
    event_router->RegisterObserver(
        this, api::networking_private::OnNetworkListChanged::kEventName);
    event_router->RegisterObserver(
        this, api::networking_private::OnDeviceStateListChanged::kEventName);
    event_router->RegisterObserver(
        this, api::networking_private::OnPortalDetectionCompleted::kEventName);
    event_router->RegisterObserver(
        this, api::networking_private::OnCertificateListsChanged::kEventName);
    StartOrStopListeningForNetworkChanges();
  }
}

NetworkingPrivateEventRouterImpl::~NetworkingPrivateEventRouterImpl() {
  DCHECK(!listening_);
}

void NetworkingPrivateEventRouterImpl::Shutdown() {
  // Unregister with the event router. We first check and see if there *is* an
  // event router, because some unit tests try to shutdown all context services,
  // but didn't initialize the event router first.
  EventRouter* event_router = EventRouter::Get(context_);
  if (event_router) {
    event_router->UnregisterObserver(this);
  }

  if (listening_) {
    NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
                                                                   FROM_HERE);
  }
  listening_ = false;
}

void NetworkingPrivateEventRouterImpl::OnListenerAdded(
    const EventListenerInfo& details) {
  // Start listening to events from the network state handler.
  StartOrStopListeningForNetworkChanges();
}

void NetworkingPrivateEventRouterImpl::OnListenerRemoved(
    const EventListenerInfo& details) {
  // Stop listening to events from the network state handler if there are no
  // more listeners.
  StartOrStopListeningForNetworkChanges();
}

void NetworkingPrivateEventRouterImpl::StartOrStopListeningForNetworkChanges() {
  EventRouter* event_router = EventRouter::Get(context_);
  bool should_listen =
      event_router->HasEventListener(
          api::networking_private::OnNetworksChanged::kEventName) ||
      event_router->HasEventListener(
          api::networking_private::OnNetworkListChanged::kEventName) ||
      event_router->HasEventListener(
          api::networking_private::OnDeviceStateListChanged::kEventName) ||
      event_router->HasEventListener(
          api::networking_private::OnPortalDetectionCompleted::kEventName) ||
      event_router->HasEventListener(
          api::networking_private::OnCertificateListsChanged::kEventName);

  if (should_listen && !listening_) {
    NetworkHandler::Get()->network_state_handler()->AddObserver(this,
                                                                FROM_HERE);
    NetworkHandler::Get()->network_certificate_handler()->AddObserver(this);
  } else if (!should_listen && listening_) {
    NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
                                                                   FROM_HERE);
    NetworkHandler::Get()->network_certificate_handler()->RemoveObserver(this);
  }
  listening_ = should_listen;
}

void NetworkingPrivateEventRouterImpl::NetworkListChanged() {
  EventRouter* event_router = EventRouter::Get(context_);
  if (!event_router->HasEventListener(
          api::networking_private::OnNetworkListChanged::kEventName)) {
    return;
  }

  NetworkStateHandler::NetworkStateList networks;
  NetworkHandler::Get()->network_state_handler()->GetVisibleNetworkList(
      &networks);
  std::vector<std::string> changes;
  for (NetworkStateHandler::NetworkStateList::const_iterator iter =
           networks.begin();
       iter != networks.end(); ++iter) {
    changes.push_back((*iter)->guid());
  }

  auto args(api::networking_private::OnNetworkListChanged::Create(changes));
  std::unique_ptr<Event> extension_event(
      new Event(events::NETWORKING_PRIVATE_ON_NETWORK_LIST_CHANGED,
                api::networking_private::OnNetworkListChanged::kEventName,
                std::move(args)));
  event_router->BroadcastEvent(std::move(extension_event));
}

void NetworkingPrivateEventRouterImpl::DeviceListChanged() {
  EventRouter* event_router = EventRouter::Get(context_);
  if (!event_router->HasEventListener(
          api::networking_private::OnDeviceStateListChanged::kEventName)) {
    return;
  }

  auto args(api::networking_private::OnDeviceStateListChanged::Create());
  std::unique_ptr<Event> extension_event(
      new Event(events::NETWORKING_PRIVATE_ON_DEVICE_STATE_LIST_CHANGED,
                api::networking_private::OnDeviceStateListChanged::kEventName,
                std::move(args)));
  event_router->BroadcastEvent(std::move(extension_event));
}

void NetworkingPrivateEventRouterImpl::NetworkPropertiesUpdated(
    const NetworkState* network) {
  EventRouter* event_router = EventRouter::Get(context_);
  if (!event_router->HasEventListener(
          api::networking_private::OnNetworksChanged::kEventName)) {
    NET_LOG(EVENT)
        << "NetworkingPrivate.NetworkPropertiesUpdated: No Listeners: "
        << NetworkId(network);
    return;
  }
  NET_LOG(EVENT) << "NetworkingPrivate.NetworkPropertiesUpdated: "
                 << NetworkId(network);
  auto args(api::networking_private::OnNetworksChanged::Create(
      std::vector<std::string>(1, network->guid())));
  std::unique_ptr<Event> extension_event(new Event(
      events::NETWORKING_PRIVATE_ON_NETWORKS_CHANGED,
      api::networking_private::OnNetworksChanged::kEventName, std::move(args)));
  event_router->BroadcastEvent(std::move(extension_event));
}

void NetworkingPrivateEventRouterImpl::DevicePropertiesUpdated(
    const DeviceState* device) {
  // networkingPrivate uses a single event for device changes.
  DeviceListChanged();

  // DeviceState changes may affect Cellular networks.
  if (device->type() != shill::kTypeCellular) {
    return;
  }

  NetworkStateHandler::NetworkStateList cellular_networks;
  NetworkHandler::Get()->network_state_handler()->GetNetworkListByType(
      ash::NetworkTypePattern::Cellular(), false /* configured_only */,
      true /* visible_only */, -1 /* default limit */, &cellular_networks);
  for (const NetworkState* network : cellular_networks) {
    NetworkPropertiesUpdated(network);
  }
}

void NetworkingPrivateEventRouterImpl::ScanCompleted(
    const DeviceState* device) {
  // We include the scanning state for Cellular networks, so notify the UI when
  // a scan completes.
  if (ash::NetworkTypePattern::Wireless().MatchesType(device->type())) {
    DevicePropertiesUpdated(device);
  }
}

void NetworkingPrivateEventRouterImpl::OnCertificatesChanged() {
  EventRouter* event_router = EventRouter::Get(context_);
  if (!event_router->HasEventListener(
          api::networking_private::OnCertificateListsChanged::kEventName)) {
    NET_LOG(EVENT) << "NetworkingPrivate.OnCertificatesChanged: No Listeners";
    return;
  }
  NET_LOG(EVENT) << "NetworkingPrivate.OnCertificatesChanged";

  auto args(api::networking_private::OnCertificateListsChanged::Create());
  std::unique_ptr<Event> extension_event(
      new Event(events::NETWORKING_PRIVATE_ON_CERTIFICATE_LISTS_CHANGED,
                api::networking_private::OnCertificateListsChanged::kEventName,
                std::move(args)));
  event_router->BroadcastEvent(std::move(extension_event));
}

void NetworkingPrivateEventRouterImpl::PortalStateChanged(
    const NetworkState* network,
    NetworkState::PortalState portal_state) {
  const std::string guid = network ? network->guid() : std::string();

  EventRouter* event_router = EventRouter::Get(context_);
  if (!event_router->HasEventListener(
          api::networking_private::OnPortalDetectionCompleted::kEventName)) {
    NET_LOG(EVENT)
        << "NetworkingPrivate.OnPortalDetectionCompleted: No Listeners: "
        << (network ? NetworkId(network) : "");
    return;
  }
  NET_LOG(EVENT) << "NetworkingPrivate.OnPortalDetectionCompleted: "
                 << (network ? NetworkId(network) : "");

  auto args(api::networking_private::OnPortalDetectionCompleted::Create(
      guid, GetCaptivePortalStatus(network)));
  std::unique_ptr<Event> extension_event(
      new Event(events::NETWORKING_PRIVATE_ON_PORTAL_DETECTION_COMPLETED,
                api::networking_private::OnPortalDetectionCompleted::kEventName,
                std::move(args)));
  event_router->BroadcastEvent(std::move(extension_event));
}

std::unique_ptr<NetworkingPrivateEventRouter>
NetworkingPrivateEventRouter::Create(content::BrowserContext* context) {
  return std::make_unique<NetworkingPrivateEventRouterImpl>(context);
}

}  // namespace extensions