blob: a7d8ccedaf9a07d2bac834a85e981bf1ab73210c [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/device_notifications/device_connection_tracker.h"
#include <string>
#include "base/notreached.h"
#include "build/build_config.h"
#include "chrome/browser/device_notifications/device_system_tray_icon.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/buildflags/buildflags.h"
#include "url/origin.h"
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "extensions/browser/extension_registry.h"
#include "extensions/common/constants.h"
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
namespace {
using base::TimeTicks;
std::string GetOriginName(Profile* profile, const url::Origin& origin) {
#if BUILDFLAG(ENABLE_EXTENSIONS)
if (origin.scheme() == extensions::kExtensionScheme) {
const auto* extension_registry =
extensions::ExtensionRegistry::Get(profile);
CHECK(extension_registry);
const extensions::Extension* extension =
extension_registry->GetExtensionById(
origin.host(), extensions::ExtensionRegistry::EVERYTHING);
// The extension must be installed if we are generating the name.
CHECK(extension);
return extension->name();
}
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
NOTREACHED_NORETURN();
}
} // namespace
DeviceConnectionTracker::DeviceConnectionTracker(Profile* profile)
: profile_(profile) {}
DeviceConnectionTracker::~DeviceConnectionTracker() {
CleanUp();
}
void DeviceConnectionTracker::IncrementConnectionCount(
const url::Origin& origin) {
bool to_stage_profile = origins_.empty();
auto& state = origins_[origin];
CHECK_GE(state.count, 0);
if (state.count == 0) {
state.name = GetOriginName(profile_, origin);
}
++state.count;
state.timestamp = TimeTicks::Now();
++total_connection_count_;
auto* system_tray_icon = GetSystemTrayIcon();
if (!system_tray_icon) {
return;
}
if (to_stage_profile) {
system_tray_icon->StageProfile(profile_);
} else {
system_tray_icon->NotifyConnectionCountUpdated(profile_);
}
}
void DeviceConnectionTracker::DecrementConnectionCount(
const url::Origin& origin) {
auto it = origins_.find(origin);
CHECK(it != origins_.end());
auto& state = it->second;
CHECK_GT(state.count, 0);
--state.count;
state.timestamp = TimeTicks::Now();
--total_connection_count_;
if (state.count == 0) {
content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::UI)
->PostDelayedTask(
FROM_HERE,
base::BindOnce(&DeviceConnectionTracker::CleanUpOrigin,
weak_factory_.GetWeakPtr(), origin, state.timestamp),
kOriginInactiveTime);
}
auto* system_tray_icon = GetSystemTrayIcon();
if (!system_tray_icon) {
return;
}
system_tray_icon->NotifyConnectionCountUpdated(profile_);
}
void DeviceConnectionTracker::ShowSiteSettings(const url::Origin& origin) {
chrome::ShowSiteSettings(profile_, origin.GetURL());
}
void DeviceConnectionTracker::CleanUp() {
if (!origins_.empty()) {
origins_.clear();
total_connection_count_ = 0;
auto* system_tray_icon = GetSystemTrayIcon();
if (system_tray_icon) {
system_tray_icon->UnstageProfile(profile_, /*immediate=*/true);
}
}
}
void DeviceConnectionTracker::CleanUpOrigin(const url::Origin& origin,
const TimeTicks& timestamp) {
auto it = origins_.find(origin);
if (it == origins_.end()) {
// This can happen if the connection bounces within 1 microsecond, which is
// the base unit of base::TimeTicks. The first CleanUpOrigin call will clear
// the origin because it sees the timestamp as the same.
return;
}
auto& state = it->second;
if (state.count == 0 && state.timestamp == timestamp) {
origins_.erase(it);
auto* system_tray_icon = GetSystemTrayIcon();
if (!system_tray_icon) {
return;
}
if (origins_.empty()) {
system_tray_icon->UnstageProfile(profile_, /*immediate=*/true);
} else {
system_tray_icon->NotifyConnectionCountUpdated(profile_);
}
}
}