Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions bridge/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,10 @@ if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs")
core/dom/legacy/bounding_client_rect.cc
core/input/touch.cc
core/input/touch_list.cc

#IntersectionObserver
core/dom/intersection_observer.cc
core/dom/intersection_observer_entry.cc
)

# Gen sources.
Expand Down Expand Up @@ -534,6 +538,11 @@ if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs")
out/element_attribute_names.cc
out/element_namespace_uris.cc

# IntersectionObserver
out/qjs_intersection_observer.cc
out/qjs_intersection_observer_entry.cc
out/qjs_intersection_observer_init.cc

# SVG generated
out/svg_names.cc
out/svg_element_factory.cc
Expand Down
8 changes: 7 additions & 1 deletion bridge/bindings/qjs/binding_initializer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,13 @@
#include "qjs_html_template_element.h"
#include "qjs_html_textarea_element.h"
#include "qjs_html_unknown_element.h"
#include "qjs_hybrid_router_change_event.h"
#include "qjs_image.h"
#include "qjs_inline_css_style_declaration.h"
#include "qjs_input_event.h"
#include "qjs_intersection_change_event.h"
#include "qjs_intersection_observer.h"
#include "qjs_intersection_observer_entry.h"
#include "qjs_keyboard_event.h"
#include "qjs_location.h"
#include "qjs_message_event.h"
Expand Down Expand Up @@ -92,7 +95,6 @@
#include "qjs_text.h"
#include "qjs_touch.h"
#include "qjs_touch_event.h"
#include "qjs_hybrid_router_change_event.h"
#include "qjs_touch_list.h"
#include "qjs_transition_event.h"
#include "qjs_ui_event.h"
Expand Down Expand Up @@ -200,6 +202,10 @@ void InstallBindings(ExecutingContext* context) {
QJSSVGStyleElement::Install(context);
QJSSVGLineElement::Install(context);

// IntersectionObserver
QJSIntersectionObserver::Install(context);
QJSIntersectionObserverEntry::Install(context);

// Legacy bindings, not standard.
QJSElementAttributes::Install(context);
}
Expand Down
4 changes: 4 additions & 0 deletions bridge/bindings/qjs/wrapper_type_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ enum {
JS_CLASS_SVG_LENGTH,
JS_CLASS_SVG_ANIMATED_LENGTH,

// IntersectionObserver
JS_CLASS_INTERSECTION_OBSERVER,
JS_CLASS_INTERSECTION_OBSERVER_ENTRY,

JS_CLASS_CUSTOM_CLASS_INIT_COUNT /* last entry for predefined classes */
};

Expand Down
8 changes: 5 additions & 3 deletions bridge/core/binding_object.cc
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,11 @@ void NativeBindingObject::HandleCallFromDartSide(DartIsolateContext* dart_isolat

dart_isolate_context->profiler()->StartTrackEvaluation(profile_id);

AtomicString method = AtomicString(
binding_object->binding_target_->ctx(),
std::unique_ptr<AutoFreeNativeString>(reinterpret_cast<AutoFreeNativeString*>(native_method->u.ptr)));
auto context = binding_object->binding_target_->ctx();
AtomicString method = native_method != nullptr
? AtomicString(context, std::unique_ptr<AutoFreeNativeString>(
reinterpret_cast<AutoFreeNativeString*>(native_method->u.ptr)))
: AtomicString(context, "");
NativeValue result = binding_object->binding_target_->HandleCallFromDartSide(method, argc, argv, dart_object);

auto* return_value = new NativeValue();
Expand Down
2 changes: 1 addition & 1 deletion bridge/core/binding_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ enum BindingMethodCallOperations {
kAsyncAnonymousFunction,
};

enum CreateBindingObjectType { kCreateDOMMatrix = 0 };
enum CreateBindingObjectType { kCreateDOMMatrix = 0, kCreateIntersectionObserver };

struct BindingObjectPromiseContext : public DartReadable {
ExecutingContext* context;
Expand Down
3 changes: 1 addition & 2 deletions bridge/core/dart_isolate_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,7 @@ void DartIsolateContext::InitializeJSRuntime() {
}

void DartIsolateContext::FinalizeJSRuntime() {
if (running_dart_isolates > 0 ||
runtime_ == nullptr) {
if (running_dart_isolates > 0 || runtime_ == nullptr) {
return;
}

Expand Down
3 changes: 2 additions & 1 deletion bridge/core/dom/dom_string_map.cc
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ bool DOMStringMap::SetItem(const webf::AtomicString& key,
}

auto attribute_name = AtomicString(ctx(), ConvertPropertyNameToAttributeName(key.ToStdString(ctx())));
return owner_element_->attributes()->setAttribute(attribute_name, value, exception_state);
owner_element_->setAttribute(attribute_name, value, exception_state);
return true;
}

bool DOMStringMap::DeleteItem(const webf::AtomicString& key, webf::ExceptionState& exception_state) {
Expand Down
3 changes: 2 additions & 1 deletion bridge/core/dom/events/event_target.cc
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ bool EventTarget::addEventListener(const AtomicString& event_type,
const std::shared_ptr<EventListener>& event_listener,
const std::shared_ptr<QJSUnionAddEventListenerOptionsBoolean>& options,
ExceptionState& exception_state) {
if (event_listener == nullptr) return false;
if (event_listener == nullptr)
return false;
std::shared_ptr<AddEventListenerOptions> event_listener_options;
if (options == nullptr) {
event_listener_options = AddEventListenerOptions::Create();
Expand Down
161 changes: 161 additions & 0 deletions bridge/core/dom/intersection_observer.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Copyright (C) 2024-present The WebF authors. All rights reserved.

#include "core/dom/intersection_observer.h"

#include <algorithm>
#include <limits>

#include <native_value_converter.h>
#include "bindings/qjs/converter_impl.h"
#include "core/dom/element.h"
#include "core/dom/intersection_observer_entry.h"
#include "core/dom/node.h"
#include "core/executing_context.h"
#include "foundation/logging.h"
#include "qjs_intersection_observer_init.h"

namespace webf {

IntersectionObserver* IntersectionObserver::Create(ExecutingContext* context,
const std::shared_ptr<QJSFunction>& function,
ExceptionState& exception_state) {
return MakeGarbageCollected<IntersectionObserver>(context, function);
}

IntersectionObserver* IntersectionObserver::Create(ExecutingContext* context,
const std::shared_ptr<QJSFunction>& function,
const std::shared_ptr<IntersectionObserverInit>& observer_init,
ExceptionState& exception_state) {
return MakeGarbageCollected<IntersectionObserver>(context, function, observer_init);
}

IntersectionObserver::IntersectionObserver(ExecutingContext* context, const std::shared_ptr<QJSFunction>& function)
: BindingObject(context->ctx()), function_(function) {
GetExecutingContext()->dartMethodPtr()->createBindingObject(
GetExecutingContext()->isDedicated(), GetExecutingContext()->contextId(), bindingObject(),
CreateBindingObjectType::kCreateIntersectionObserver, nullptr, 0);
}

IntersectionObserver::IntersectionObserver(ExecutingContext* context,
const std::shared_ptr<QJSFunction>& function,
const std::shared_ptr<IntersectionObserverInit>& observer_init)
: BindingObject(context->ctx()), function_(function) {
if (observer_init && observer_init->hasRoot()) {
root_ = observer_init->root();
}
NativeValue arguments[1];
if (observer_init && observer_init->hasThreshold()) {
#if ENABLE_LOG
WEBF_LOG(DEBUG) << "[IntersectionObserver]: Constructor threshold.size = " << observer_init->threshold().size()
<< std::endl;
#endif
thresholds_ = std::move(observer_init->threshold());
std::sort(thresholds_.begin(), thresholds_.end());
arguments[0] = NativeValueConverter<NativeTypeArray<NativeTypeDouble>>::ToNativeValue(thresholds_);
}
GetExecutingContext()->dartMethodPtr()->createBindingObject(
GetExecutingContext()->isDedicated(), GetExecutingContext()->contextId(), bindingObject(),
CreateBindingObjectType::kCreateIntersectionObserver, arguments, 1);
}

bool IntersectionObserver::RootIsValid() const {
return RootIsImplicit() || root();
}

void IntersectionObserver::observe(Element* target, ExceptionState& exception_state) {
if (!RootIsValid() || !target) {
WEBF_LOG(ERROR) << "[IntersectionObserver]: observe valid:" << std::endl;
return;
}

#if ENABLE_LOG
WEBF_LOG(DEBUG) << "[IntersectionObserver]: observe target=" << target << ",tagName=" << target->nodeName()
<< std::endl;
#endif
GetExecutingContext()->uiCommandBuffer()->AddCommand(UICommand::kAddIntersectionObserver, nullptr, bindingObject(),
target->bindingObject());
}

void IntersectionObserver::unobserve(Element* target, ExceptionState& exception_state) {
if (!target) {
WEBF_LOG(ERROR) << "[IntersectionObserver]: unobserve valid:" << std::endl;
return;
}

GetExecutingContext()->uiCommandBuffer()->AddCommand(UICommand::kRemoveIntersectionObserver, nullptr, bindingObject(),
target->bindingObject());
}

void IntersectionObserver::disconnect(ExceptionState& exception_state) {
GetExecutingContext()->uiCommandBuffer()->AddCommand(UICommand::kDisconnectIntersectionObserver, nullptr,
bindingObject(), nullptr);
}

// std::vector<Member<IntersectionObserverEntry>> IntersectionObserver::takeRecords(ExceptionState& exception_state) {
// std::vector<Member<IntersectionObserverEntry>> entries;
// for (auto& observation : observations_)
// observation->TakeRecords(entries);
// active_observations_.clear();
// return entries;
// }

// AtomicString IntersectionObserver::rootMargin() const {
// return StringifyMargin(RootMargin());
// }

// AtomicString IntersectionObserver::scrollMargin() const {
// return StringifyMargin(ScrollMargin());
// }

NativeValue IntersectionObserver::HandleCallFromDartSide(const AtomicString& method,
int32_t argc,
const NativeValue* argv,
Dart_Handle dart_object) {
if (!GetExecutingContext() || !GetExecutingContext()->IsContextValid()) {
WEBF_LOG(ERROR) << "[IntersectionObserver]: HandleCallFromDartSide Context Valid" << std::endl;
return Native_NewNull();
}

MemberMutationScope scope{GetExecutingContext()};

NativeIntersectionObserverEntry* native_entry =
NativeValueConverter<NativeTypePointer<NativeIntersectionObserverEntry>>::FromNativeValue(argv[0]);
size_t length = NativeValueConverter<NativeTypeInt64>::FromNativeValue(argv[1]);

if (length > 0) {
assert(function_ != nullptr);
JSValue js_array = JS_NewArray(ctx());
for (int i = 0; i < length; i++) {
auto* entry = MakeGarbageCollected<IntersectionObserverEntry>(
GetExecutingContext(), native_entry[i].is_intersecting, native_entry[i].intersectionRatio,
DynamicTo<Element>(BindingObject::From(native_entry[i].target)));
JS_SetPropertyUint32(ctx(), js_array, i, entry->ToQuickJS());
}
ScriptValue arguments[] = {ScriptValue(ctx(), js_array), ToValue()};

#if ENABLE_LOG
WEBF_LOG(DEBUG) << "[IntersectionObserver]: HandleCallFromDartSide length=" << length << ",JS function_ Invoke"
<< std::endl;
#endif

function_->Invoke(ctx(), ToValue(), 2, arguments);

JS_FreeValue(ctx(), js_array);
} else {
WEBF_LOG(ERROR) << "[IntersectionObserver]: HandleCallFromDartSide entries is empty";
}

return Native_NewNull();
}

void IntersectionObserver::Trace(GCVisitor* visitor) const {
BindingObject::Trace(visitor);

function_->Trace(visitor);
}

} // namespace webf
28 changes: 28 additions & 0 deletions bridge/core/dom/intersection_observer.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// https://www.w3.org/TR/intersection-observer/#intersection-observer-interface

// Copyright (C) 2024-present The WebF authors. All rights reserved.

import {IntersectionObserverInit} from "./intersection_observer_init";
import {IntersectionObserverEntry} from "./intersection_observer_entry";
import {Node} from "./node";
import {Element} from "./element";

interface IntersectionObserver {
new(callback: Function, options?: IntersectionObserverInit): IntersectionObserver;

//readonly root: Node | null;
//readonly rootMargin: string;
//readonly scrollMargin: string;
//readonly thresholds: number[];
//readonly delay: number;
//readonly trackVisibility: boolean;

observe(target: Element): void;
unobserve(target: Element): void;
disconnect(): void;
//takeRecords(): IntersectionObserverEntry[];
}
Loading