mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 21:52:45 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			119 lines
		
	
	
	
		
			4.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			119 lines
		
	
	
	
		
			4.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
 | |
|  * Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #include <LibWeb/Bindings/Intrinsics.h>
 | |
| #include <LibWeb/DOM/Document.h>
 | |
| #include <LibWeb/HTML/Scripting/ExceptionReporter.h>
 | |
| #include <LibWeb/HTML/Window.h>
 | |
| #include <LibWeb/ResizeObserver/ResizeObserver.h>
 | |
| #include <LibWeb/WebIDL/AbstractOperations.h>
 | |
| 
 | |
| namespace Web::ResizeObserver {
 | |
| 
 | |
| JS_DEFINE_ALLOCATOR(ResizeObserver);
 | |
| 
 | |
| // https://drafts.csswg.org/resize-observer/#dom-resizeobserver-resizeobserver
 | |
| WebIDL::ExceptionOr<JS::NonnullGCPtr<ResizeObserver>> ResizeObserver::construct_impl(JS::Realm& realm, WebIDL::CallbackType* callback)
 | |
| {
 | |
|     return realm.heap().allocate<ResizeObserver>(realm, realm, callback);
 | |
| }
 | |
| 
 | |
| ResizeObserver::ResizeObserver(JS::Realm& realm, WebIDL::CallbackType* callback)
 | |
|     : PlatformObject(realm)
 | |
|     , m_callback(callback)
 | |
| {
 | |
|     auto navigable = verify_cast<HTML::Window>(HTML::relevant_global_object(*this)).navigable();
 | |
|     m_document = navigable->active_document().ptr();
 | |
|     m_document->register_resize_observer({}, *this);
 | |
| }
 | |
| 
 | |
| ResizeObserver::~ResizeObserver() = default;
 | |
| 
 | |
| void ResizeObserver::initialize(JS::Realm& realm)
 | |
| {
 | |
|     Base::initialize(realm);
 | |
|     set_prototype(&Bindings::ensure_web_prototype<Bindings::ResizeObserverPrototype>(realm, "ResizeObserver"_fly_string));
 | |
| }
 | |
| 
 | |
| void ResizeObserver::visit_edges(JS::Cell::Visitor& visitor)
 | |
| {
 | |
|     Base::visit_edges(visitor);
 | |
|     visitor.visit(m_callback);
 | |
|     for (auto& observation : m_observation_targets)
 | |
|         visitor.visit(observation);
 | |
|     for (auto& observation : m_active_targets)
 | |
|         visitor.visit(observation);
 | |
|     for (auto& observation : m_skipped_targets)
 | |
|         visitor.visit(observation);
 | |
| }
 | |
| 
 | |
| void ResizeObserver::finalize()
 | |
| {
 | |
|     if (m_document)
 | |
|         m_document->unregister_resize_observer({}, *this);
 | |
| }
 | |
| 
 | |
| // https://drafts.csswg.org/resize-observer-1/#dom-resizeobserver-observe
 | |
| void ResizeObserver::observe(DOM::Element& target, ResizeObserverOptions options)
 | |
| {
 | |
|     // 1. If target is in [[observationTargets]] slot, call unobserve() with argument target.
 | |
|     auto observation = m_observation_targets.find_if([&](auto& observation) { return observation->target().ptr() == ⌖ });
 | |
|     if (!observation.is_end())
 | |
|         unobserve(target);
 | |
| 
 | |
|     // 2. Let observedBox be the value of the box dictionary member of options.
 | |
|     auto observed_box = options.box;
 | |
| 
 | |
|     // 3. Let resizeObservation be new ResizeObservation(target, observedBox).
 | |
|     auto resize_observation = MUST(ResizeObservation::create(realm(), target, observed_box));
 | |
| 
 | |
|     // 4. Add the resizeObservation to the [[observationTargets]] slot.
 | |
|     m_observation_targets.append(resize_observation);
 | |
| }
 | |
| 
 | |
| // https://drafts.csswg.org/resize-observer-1/#dom-resizeobserver-unobserve
 | |
| void ResizeObserver::unobserve(DOM::Element& target)
 | |
| {
 | |
|     // 1. Let observation be ResizeObservation in [[observationTargets]] whose target slot is target.
 | |
|     auto observation = m_observation_targets.find_if([&](auto& observation) { return observation->target().ptr() == ⌖ });
 | |
| 
 | |
|     // 2. If observation is not found, return.
 | |
|     if (observation.is_end())
 | |
|         return;
 | |
| 
 | |
|     // 3. Remove observation from [[observationTargets]].
 | |
|     m_observation_targets.remove(observation.index());
 | |
| }
 | |
| 
 | |
| // https://drafts.csswg.org/resize-observer-1/#dom-resizeobserver-disconnect
 | |
| void ResizeObserver::disconnect()
 | |
| {
 | |
|     // 1. Clear the [[observationTargets]] list.
 | |
|     m_observation_targets.clear();
 | |
| 
 | |
|     // 2. Clear the [[activeTargets]] list.
 | |
|     m_active_targets.clear();
 | |
| }
 | |
| 
 | |
| void ResizeObserver::invoke_callback(Vector<JS::NonnullGCPtr<ResizeObserverEntry>>& entries) const
 | |
| {
 | |
|     auto& callback = *m_callback;
 | |
|     auto& realm = callback.callback_context->realm();
 | |
| 
 | |
|     auto wrapped_records = MUST(JS::Array::create(realm, 0));
 | |
|     for (size_t i = 0; i < entries.size(); ++i) {
 | |
|         auto& record = entries.at(i);
 | |
|         auto property_index = JS::PropertyKey { i };
 | |
|         MUST(wrapped_records->create_data_property(property_index, record.ptr()));
 | |
|     }
 | |
| 
 | |
|     auto result = WebIDL::invoke_callback(callback, JS::js_undefined(), wrapped_records);
 | |
|     if (result.is_abrupt())
 | |
|         HTML::report_exception(result, realm);
 | |
| }
 | |
| 
 | |
| }
 | 
