Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/vipshop add intersection observer #698

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
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) {
guopf marked this conversation as resolved.
Show resolved Hide resolved
// 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