ResizeObserver.cpp 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. /*
  2. * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <LibWeb/Bindings/Intrinsics.h>
  8. #include <LibWeb/DOM/Document.h>
  9. #include <LibWeb/HTML/Scripting/ExceptionReporter.h>
  10. #include <LibWeb/HTML/Window.h>
  11. #include <LibWeb/ResizeObserver/ResizeObserver.h>
  12. #include <LibWeb/WebIDL/AbstractOperations.h>
  13. namespace Web::ResizeObserver {
  14. JS_DEFINE_ALLOCATOR(ResizeObserver);
  15. // https://drafts.csswg.org/resize-observer/#dom-resizeobserver-resizeobserver
  16. WebIDL::ExceptionOr<JS::NonnullGCPtr<ResizeObserver>> ResizeObserver::construct_impl(JS::Realm& realm, WebIDL::CallbackType* callback)
  17. {
  18. return realm.heap().allocate<ResizeObserver>(realm, realm, callback);
  19. }
  20. ResizeObserver::ResizeObserver(JS::Realm& realm, WebIDL::CallbackType* callback)
  21. : PlatformObject(realm)
  22. , m_callback(callback)
  23. {
  24. auto navigable = verify_cast<HTML::Window>(HTML::relevant_global_object(*this)).navigable();
  25. m_document = navigable->active_document().ptr();
  26. m_document->register_resize_observer({}, *this);
  27. }
  28. ResizeObserver::~ResizeObserver() = default;
  29. void ResizeObserver::initialize(JS::Realm& realm)
  30. {
  31. Base::initialize(realm);
  32. WEB_SET_PROTOTYPE_FOR_INTERFACE(ResizeObserver);
  33. }
  34. void ResizeObserver::visit_edges(JS::Cell::Visitor& visitor)
  35. {
  36. Base::visit_edges(visitor);
  37. visitor.visit(m_callback);
  38. for (auto& observation : m_observation_targets)
  39. visitor.visit(observation);
  40. for (auto& observation : m_active_targets)
  41. visitor.visit(observation);
  42. for (auto& observation : m_skipped_targets)
  43. visitor.visit(observation);
  44. }
  45. void ResizeObserver::finalize()
  46. {
  47. if (m_document)
  48. m_document->unregister_resize_observer({}, *this);
  49. }
  50. // https://drafts.csswg.org/resize-observer-1/#dom-resizeobserver-observe
  51. void ResizeObserver::observe(DOM::Element& target, ResizeObserverOptions options)
  52. {
  53. // 1. If target is in [[observationTargets]] slot, call unobserve() with argument target.
  54. auto observation = m_observation_targets.find_if([&](auto& observation) { return observation->target().ptr() == &target; });
  55. if (!observation.is_end())
  56. unobserve(target);
  57. // 2. Let observedBox be the value of the box dictionary member of options.
  58. auto observed_box = options.box;
  59. // 3. Let resizeObservation be new ResizeObservation(target, observedBox).
  60. auto resize_observation = MUST(ResizeObservation::create(realm(), target, observed_box));
  61. // 4. Add the resizeObservation to the [[observationTargets]] slot.
  62. m_observation_targets.append(resize_observation);
  63. }
  64. // https://drafts.csswg.org/resize-observer-1/#dom-resizeobserver-unobserve
  65. void ResizeObserver::unobserve(DOM::Element& target)
  66. {
  67. // 1. Let observation be ResizeObservation in [[observationTargets]] whose target slot is target.
  68. auto observation = m_observation_targets.find_if([&](auto& observation) { return observation->target().ptr() == &target; });
  69. // 2. If observation is not found, return.
  70. if (observation.is_end())
  71. return;
  72. // 3. Remove observation from [[observationTargets]].
  73. m_observation_targets.remove(observation.index());
  74. }
  75. // https://drafts.csswg.org/resize-observer-1/#dom-resizeobserver-disconnect
  76. void ResizeObserver::disconnect()
  77. {
  78. // 1. Clear the [[observationTargets]] list.
  79. m_observation_targets.clear();
  80. // 2. Clear the [[activeTargets]] list.
  81. m_active_targets.clear();
  82. }
  83. void ResizeObserver::invoke_callback(Vector<JS::NonnullGCPtr<ResizeObserverEntry>>& entries) const
  84. {
  85. auto& callback = *m_callback;
  86. auto& realm = callback.callback_context->realm();
  87. auto wrapped_records = MUST(JS::Array::create(realm, 0));
  88. for (size_t i = 0; i < entries.size(); ++i) {
  89. auto& record = entries.at(i);
  90. auto property_index = JS::PropertyKey { i };
  91. MUST(wrapped_records->create_data_property(property_index, record.ptr()));
  92. }
  93. auto result = WebIDL::invoke_callback(callback, JS::js_undefined(), wrapped_records);
  94. if (result.is_abrupt())
  95. HTML::report_exception(result, realm);
  96. }
  97. }