blob: f8c741b695df8ac97c553e3052492a7a98fd7a16 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Embedded Framework 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 "libcef/common/value_base.h"
CefValueController::CefValueController()
: owner_value_(nullptr), owner_object_(nullptr) {}
CefValueController::~CefValueController() {
// Everything should already have been removed.
DCHECK(!owner_value_ && !owner_object_);
DCHECK(reference_map_.empty());
DCHECK(dependency_map_.empty());
}
void CefValueController::SetOwner(void* value, Object* object) {
DCHECK(value && object);
// Controller should already be locked.
DCHECK(locked());
// Owner should only be set once.
DCHECK(!owner_value_ && !owner_object_);
owner_value_ = value;
owner_object_ = object;
}
void CefValueController::AddReference(void* value, Object* object) {
DCHECK(value && object);
// Controller should already be locked.
DCHECK(locked());
// Controller should currently have an owner.
DCHECK(owner_value_);
// Values should only be added once.
DCHECK(reference_map_.find(value) == reference_map_.end());
DCHECK(value != owner_value_);
reference_map_.insert(std::make_pair(value, object));
}
void CefValueController::Remove(void* value, bool notify_object) {
DCHECK(value);
// Controller should already be locked.
DCHECK(locked());
// Controller should currently have an owner.
DCHECK(owner_value_);
if (value == owner_value_) {
// Should never notify when removing the owner object.
DCHECK(!notify_object);
owner_value_ = nullptr;
owner_object_ = nullptr;
// Remove all references.
if (reference_map_.size() > 0) {
ReferenceMap::iterator it = reference_map_.begin();
for (; it != reference_map_.end(); ++it)
it->second->OnControlRemoved();
reference_map_.clear();
}
// Remove all dependencies.
dependency_map_.clear();
} else {
ReferenceMap::iterator it = reference_map_.find(value);
if (it != reference_map_.end()) {
// Remove the reference.
if (notify_object)
it->second->OnControlRemoved();
reference_map_.erase(it);
}
}
}
CefValueController::Object* CefValueController::Get(void* value) {
DCHECK(value);
// Controller should already be locked.
DCHECK(locked());
if (value == owner_value_) {
return owner_object_;
} else {
ReferenceMap::iterator it = reference_map_.find(value);
if (it != reference_map_.end())
return it->second;
return nullptr;
}
}
void CefValueController::AddDependency(void* parent, void* child) {
DCHECK(parent && child && parent != child);
// Controller should already be locked.
DCHECK(locked());
DependencyMap::iterator it = dependency_map_.find(parent);
if (it == dependency_map_.end()) {
// New set.
DependencySet set;
set.insert(child);
dependency_map_.insert(std::make_pair(parent, set));
} else if (it->second.find(child) == it->second.end()) {
// Update existing set.
it->second.insert(child);
}
}
void CefValueController::RemoveDependencies(void* value) {
DCHECK(value);
// Controller should already be locked.
DCHECK(locked());
if (dependency_map_.empty())
return;
DependencyMap::iterator it_dependency = dependency_map_.find(value);
if (it_dependency == dependency_map_.end())
return;
// Start with the set of dependencies for the current value.
DependencySet remove_set = it_dependency->second;
dependency_map_.erase(it_dependency);
DependencySet::iterator it_value;
ReferenceMap::iterator it_reference;
while (remove_set.size() > 0) {
it_value = remove_set.begin();
value = *it_value;
remove_set.erase(it_value);
// Does the current value have dependencies?
it_dependency = dependency_map_.find(value);
if (it_dependency != dependency_map_.end()) {
// Append the dependency set to the remove set.
remove_set.insert(it_dependency->second.begin(),
it_dependency->second.end());
dependency_map_.erase(it_dependency);
}
// Does the current value have a reference?
it_reference = reference_map_.find(value);
if (it_reference != reference_map_.end()) {
// Remove the reference.
it_reference->second->OnControlRemoved();
reference_map_.erase(it_reference);
}
}
}
void CefValueController::TakeFrom(CefValueController* other) {
DCHECK(other);
// Both controllers should already be locked.
DCHECK(locked());
DCHECK(other->locked());
if (!other->reference_map_.empty()) {
// Transfer references from the other to this.
ReferenceMap::iterator it = other->reference_map_.begin();
for (; it != other->reference_map_.end(); ++it) {
// References should only be added once.
DCHECK(reference_map_.find(it->first) == reference_map_.end());
reference_map_.insert(std::make_pair(it->first, it->second));
}
other->reference_map_.clear();
}
if (!other->dependency_map_.empty()) {
// Transfer dependencies from the other to this.
DependencyMap::iterator it_other = other->dependency_map_.begin();
for (; it_other != other->dependency_map_.end(); ++it_other) {
DependencyMap::iterator it_me = dependency_map_.find(it_other->first);
if (it_me == dependency_map_.end()) {
// All children are new.
dependency_map_.insert(
std::make_pair(it_other->first, it_other->second));
} else {
// Evaluate each child.
DependencySet::iterator it_other_set = it_other->second.begin();
for (; it_other_set != it_other->second.end(); ++it_other_set) {
if (it_me->second.find(*it_other_set) == it_me->second.end())
it_me->second.insert(*it_other_set);
}
}
}
}
}
void CefValueController::Swap(void* old_value, void* new_value) {
DCHECK(old_value && new_value && old_value != new_value);
// Controller should already be locked.
DCHECK(locked());
if (owner_value_ == old_value)
owner_value_ = new_value;
if (!reference_map_.empty()) {
ReferenceMap::iterator it = reference_map_.find(old_value);
if (it != reference_map_.end()) {
// References should only be added once.
DCHECK(reference_map_.find(new_value) == reference_map_.end());
reference_map_.insert(std::make_pair(new_value, it->second));
reference_map_.erase(it);
}
}
if (!dependency_map_.empty()) {
DependencyMap::iterator it = dependency_map_.find(old_value);
if (it != dependency_map_.end()) {
dependency_map_.insert(std::make_pair(new_value, it->second));
dependency_map_.erase(it);
}
it = dependency_map_.begin();
for (; it != dependency_map_.end(); ++it) {
DependencySet::iterator dit = it->second.find(old_value);
if (dit != it->second.end()) {
it->second.insert(new_value);
it->second.erase(dit);
}
}
}
}