blob: b4b3950bce8529abb02bc19a665dca60d3128093 [file] [log] [blame]
// Copyright 2019 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/dom/element_intersection_observer_module.h"
#include <algorithm>
#include "cobalt/dom/document.h"
#include "cobalt/dom/dom_rect_read_only.h"
#include "cobalt/dom/element.h"
#include "cobalt/dom/intersection_observer_entry_init.h"
#include "cobalt/dom/performance.h"
namespace cobalt {
namespace dom {
ElementIntersectionObserverModule::ElementIntersectionObserverModule(
Element* element)
: element_(element) {}
ElementIntersectionObserverModule::~ElementIntersectionObserverModule() {
// The intersection observer vectors may hold the last reference to the
// corresponding intersection observer object. Since the intersection observer
// dtor calls UnregisterIntersectionObserverForRoot, make sure the call
// doesn't try to release the scoped_refptr<IntersectionObserver> again during
// destruction.
IntersectionObserverVector temp_root_registered_observers;
IntersectionObserverVector temp_target_registered_observers;
// Swap out the intersection observer vectors so that the call to
// UnregisterIntersectionObserverForRoot won't do anything, as the objects are
// already being destroyed here.
temp_root_registered_observers.swap(root_registered_intersection_observers_);
temp_target_registered_observers.swap(
target_registered_intersection_observers_);
// Force destruction of the intersection observer objects here so that the
// call to UnregisterIntersectionObserverForRoot occurs before this object is
// fully destroyed.
temp_root_registered_observers.clear();
temp_target_registered_observers.clear();
}
void ElementIntersectionObserverModule::RegisterIntersectionObserverForRoot(
IntersectionObserver* observer) {
auto it = std::find(root_registered_intersection_observers_.begin(),
root_registered_intersection_observers_.end(), observer);
if (it != root_registered_intersection_observers_.end()) {
return;
}
root_registered_intersection_observers_.push_back(
base::WrapRefCounted(observer));
// Also create the observer's layout root at this time.
scoped_refptr<layout::IntersectionObserverRoot> new_root =
new layout::IntersectionObserverRoot(
observer->root_margin_property_value(),
observer->thresholds_vector());
observer->set_layout_root(new_root);
InvalidateLayoutBoxesForElement();
}
void ElementIntersectionObserverModule::UnregisterIntersectionObserverForRoot(
IntersectionObserver* observer) {
auto it = std::find(root_registered_intersection_observers_.begin(),
root_registered_intersection_observers_.end(), observer);
if (it != root_registered_intersection_observers_.end()) {
root_registered_intersection_observers_.erase(it);
InvalidateLayoutBoxesForElement();
return;
}
DLOG(WARNING)
<< "Did not find an intersection observer to unregister for the root.";
}
void ElementIntersectionObserverModule::RegisterIntersectionObserverForTarget(
IntersectionObserver* observer) {
auto it =
std::find(target_registered_intersection_observers_.begin(),
target_registered_intersection_observers_.end(), observer);
if (it != target_registered_intersection_observers_.end()) {
return;
}
target_registered_intersection_observers_.push_back(
base::WrapRefCounted(observer));
AddLayoutTargetForObserver(observer);
InvalidateLayoutBoxesForElement();
// We record a mutation when an intersection observer target is added, in
// order to mark the layout as dirty and make sure that the intersection
// observer creates an entry for any new intersection.
Document* node_document = element_->node_document();
if (node_document != nullptr) {
node_document->RecordMutation();
}
}
void ElementIntersectionObserverModule::UnregisterIntersectionObserverForTarget(
IntersectionObserver* observer) {
auto it =
std::find(target_registered_intersection_observers_.begin(),
target_registered_intersection_observers_.end(), observer);
if (it != target_registered_intersection_observers_.end()) {
target_registered_intersection_observers_.erase(it);
RemoveLayoutTargetForObserver(observer);
InvalidateLayoutBoxesForElement();
return;
}
DLOG(WARNING)
<< "Did not find an intersection observer to unregister for the target.";
}
ElementIntersectionObserverModule::LayoutIntersectionObserverRootVector
ElementIntersectionObserverModule::
GetLayoutIntersectionObserverRootsForElement() {
ElementIntersectionObserverModule::LayoutIntersectionObserverRootVector
layout_roots;
for (const auto& intersection_observer :
root_registered_intersection_observers_) {
layout_roots.push_back(intersection_observer->layout_root());
}
return layout_roots;
}
ElementIntersectionObserverModule::LayoutIntersectionObserverTargetVector
ElementIntersectionObserverModule::
GetLayoutIntersectionObserverTargetsForElement() {
return layout_targets_;
}
void ElementIntersectionObserverModule::
CreateIntersectionObserverEntryForObserver(
const base::WeakPtr<IntersectionObserver>& observer,
math::RectF root_bounds, math::RectF target_rect,
math::RectF intersection_rect, bool is_intersecting,
float intersection_ratio) {
if (!observer) {
return;
}
IntersectionObserverEntryInit init_dict;
init_dict.set_time(element_->owner_document()
->window()
->performance()
->timing()
->GetNavigationStartClock()
->Now()
.InMillisecondsF());
init_dict.set_root_bounds(
base::WrapRefCounted(new DOMRectReadOnly(root_bounds)));
init_dict.set_bounding_client_rect(
base::WrapRefCounted(new DOMRectReadOnly(target_rect)));
init_dict.set_intersection_rect(
base::WrapRefCounted(new DOMRectReadOnly(intersection_rect)));
init_dict.set_is_intersecting(is_intersecting);
init_dict.set_intersection_ratio(intersection_ratio);
init_dict.set_target(base::WrapRefCounted(element_));
observer->QueueIntersectionObserverEntry(
base::WrapRefCounted(new IntersectionObserverEntry(init_dict)));
}
void ElementIntersectionObserverModule::TraceMembers(script::Tracer* tracer) {
tracer->TraceItems(root_registered_intersection_observers_);
tracer->TraceItems(target_registered_intersection_observers_);
}
void ElementIntersectionObserverModule::AddLayoutTargetForObserver(
IntersectionObserver* observer) {
layout::IntersectionObserverTarget::OnIntersectionCallback
on_intersection_callback =
base::Bind(&ElementIntersectionObserverModule::
CreateIntersectionObserverEntryForObserver,
base::Unretained(this), base::AsWeakPtr(observer));
scoped_refptr<layout::IntersectionObserverTarget> layout_target =
new layout::IntersectionObserverTarget(on_intersection_callback,
observer->layout_root());
layout_targets_.push_back(layout_target);
}
void ElementIntersectionObserverModule::RemoveLayoutTargetForObserver(
IntersectionObserver* observer) {
for (auto it = layout_targets_.begin(); it != layout_targets_.end(); ++it) {
if ((*it)->intersection_observer_root() == observer->layout_root()) {
layout_targets_.erase(it);
return;
}
}
NOTREACHED() << "Did not find a layout target to remove";
}
void ElementIntersectionObserverModule::InvalidateLayoutBoxesForElement() {
HTMLElement* html_element = element_->AsHTMLElement();
if (!html_element) {
NOTREACHED();
return;
}
html_element->InvalidateLayoutBoxesOfNodeAndAncestors();
}
} // namespace dom
} // namespace cobalt