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 all 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 @@ -446,6 +446,10 @@ if (${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 @@ -588,6 +592,11 @@ if (${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
6 changes: 6 additions & 0 deletions bridge/bindings/qjs/binding_initializer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@
#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 @@ -208,6 +210,10 @@ void InstallBindings(ExecutingContext* context) {
QJSSVGLineElement::Install(context);
QJSNativeLoader::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 @@ -120,6 +120,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
10 changes: 6 additions & 4 deletions bridge/core/binding_object.cc
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,12 @@ void NativeBindingObject::HandleCallFromDartSide(const DartIsolateContext* dart_

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

const AtomicString method =
AtomicString(binding_object->binding_target_->ctx(),
std::unique_ptr<AutoFreeNativeString>(static_cast<AutoFreeNativeString*>(native_method->u.ptr)));
const NativeValue result = binding_object->binding_target_->HandleCallFromDartSide(method, argc, argv, dart_object);
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();
std::memcpy(return_value, &result, sizeof(NativeValue));
Expand Down
1 change: 1 addition & 0 deletions bridge/core/binding_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ enum CreateBindingObjectType {
kCreateDOMMatrix = 0,
kCreatePath2D = 1,
kCreateDOMPoint = 2,
kCreateIntersectionObserver = 3,
};

struct BindingObjectPromiseContext : public DartReadable {
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
145 changes: 145 additions & 0 deletions bridge/core/dom/intersection_observer.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// 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);
}

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
Loading