1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "bridge/declarative_frontend/jsview/js_repeat_virtual_scroll.h"
17 
18 #include <string>
19 
20 #include "base/log/ace_trace.h"
21 #include "base/log/log_wrapper.h"
22 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
23 #include "core/components_ng/syntax/repeat_virtual_scroll_model_ng.h"
24 
25 #define JSFUNC(opts, propName) (JSRef<JSFunc>::Cast((opts)->GetProperty(propName)))
26 
27 namespace OHOS::Ace {
28 
29 std::unique_ptr<RepeatVirtualScrollModel> RepeatVirtualScrollModel::instance_ = nullptr;
30 #define UNUSED_CACHED_SIZE_PARAM 2
31 
GetInstance()32 RepeatVirtualScrollModel* RepeatVirtualScrollModel::GetInstance()
33 {
34     if (!instance_) {
35         instance_.reset(new NG::RepeatVirtualScrollModelNG());
36     }
37     return instance_.get();
38 }
39 } // namespace OHOS::Ace
40 
41 namespace OHOS::Ace::Framework {
42 
43 enum {
44     PARAM_TOTAL_COUNT = 0,
45     PARAM_TEMPLATE_OPTS = 1,
46     PARAM_HANDLERS = 2,
47     MIN_PARAM_SIZE = 3,
48 };
49 
ParseAndVerifyParams(const JSCallbackInfo & info)50 bool ParseAndVerifyParams(const JSCallbackInfo& info)
51 {
52     if (info.Length() < MIN_PARAM_SIZE) {
53         return false;
54     }
55 
56     if (!info[PARAM_TOTAL_COUNT]->IsNumber()) {
57         return false;
58     }
59     if (!info[PARAM_TEMPLATE_OPTS]->IsArray()) {
60         return false;
61     }
62     if (!info[PARAM_HANDLERS]->IsObject()) {
63         return false;
64     }
65 
66     auto templateOptsArray = JSRef<JSArray>::Cast(info[PARAM_TEMPLATE_OPTS]);
67     for (size_t i = 0; i < templateOptsArray->Length(); i++) {
68         JSRef<JSArray> pair = templateOptsArray->GetValueAt(i);
69         if (!pair->GetValueAt(0)->IsString()) {
70             return false;
71         }
72         if (!pair->GetValueAt(1)->IsObject()) {
73             return false;
74         }
75         auto type = pair->GetValueAt(0)->ToString();
76         auto opts = JSRef<JSObject>::Cast(pair->GetValueAt(1));
77         if (!opts->GetProperty("cachedCountSpecified")->IsBoolean()) {
78             return false;
79         }
80     }
81 
82     auto handlers = JSRef<JSObject>::Cast(info[PARAM_HANDLERS]);
83     if (!handlers->GetProperty("onCreateNode")->IsFunction() || !handlers->GetProperty("onUpdateNode")->IsFunction() ||
84         !handlers->GetProperty("onGetKeys4Range")->IsFunction() ||
85         !handlers->GetProperty("onGetTypes4Range")->IsFunction() ||
86         !handlers->GetProperty("onSetActiveRange")->IsFunction()) {
87         return false;
88     }
89 
90     return true;
91 }
92 
Create(const JSCallbackInfo & info)93 void JSRepeatVirtualScroll::Create(const JSCallbackInfo& info)
94 {
95     if (!ParseAndVerifyParams(info)) {
96         TAG_LOGW(AceLogTag::ACE_LAZY_FOREACH, "Invalid arguments for RepeatVirtualScroll");
97         return;
98     }
99 
100     // arg 0
101     auto totalCount = info[PARAM_TOTAL_COUNT]->ToNumber<uint32_t>();
102 
103     // arg 1
104     auto templateOptsArray = JSRef<JSArray>::Cast(info[PARAM_TEMPLATE_OPTS]);
105     std::map<std::string, std::pair<bool, uint32_t>> templateCachedCountMap;
106     for (size_t i = 0; i < templateOptsArray->Length(); i++) {
107         JSRef<JSArray> pair = templateOptsArray->GetValueAt(i);
108         auto type = pair->GetValueAt(0)->ToString();
109         auto opts = JSRef<JSObject>::Cast(pair->GetValueAt(1));
110 
111         templateCachedCountMap[type] =
112             opts->GetProperty("cachedCountSpecified")->ToNumber <bool>()
113                 ? std::pair<bool, uint32_t>(true, opts->GetProperty("cachedCount")->ToNumber<uint32_t>())
114                 : std::pair<bool, uint32_t>(false, UNUSED_CACHED_SIZE_PARAM);
115     }
116 
117     // arg 2
118     auto handlers = JSRef<JSObject>::Cast(info[PARAM_HANDLERS]);
119     auto onCreateNode = [execCtx = info.GetExecutionContext(), func = JSFUNC(handlers, "onCreateNode")](
120                             uint32_t forIndex) -> void {
121         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
122         auto params = ConvertToJSValues(forIndex);
123         func->Call(JSRef<JSObject>(), params.size(), params.data());
124     };
125 
126     auto onUpdateNode = [execCtx = info.GetExecutionContext(), func = JSFUNC(handlers, "onUpdateNode")](
127                             const std::string& fromKey, uint32_t forIndex) -> void {
128         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
129         auto params = ConvertToJSValues(fromKey, forIndex);
130         func->Call(JSRef<JSObject>(), params.size(), params.data());
131     };
132 
133     auto onGetKeys4Range = [execCtx = info.GetExecutionContext(), func = JSFUNC(handlers, "onGetKeys4Range")](
134                                uint32_t from, uint32_t to) -> std::list<std::string> {
135         std::list<std::string> list;
136         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, list);
137         auto params = ConvertToJSValues(from, to);
138         JSRef<JSVal> jsVal = func->Call(JSRef<JSObject>(), params.size(), params.data());
139         // convert js-array to std::list
140         if (!jsVal->IsArray()) {
141             TAG_LOGW(AceLogTag::ACE_REPEAT, "jsVal should be array.");
142             return list;
143         }
144         JSRef<JSArray> jsArr = JSRef<JSArray>::Cast(jsVal);
145         for (size_t i = 0; i < jsArr->Length(); i++) {
146             list.emplace_back(jsArr->GetValueAt(i)->ToString());
147         }
148         return list;
149     };
150 
151     auto onGetTypes4Range = [execCtx = info.GetExecutionContext(), func = JSFUNC(handlers, "onGetTypes4Range")](
152                                 uint32_t from, uint32_t to) -> std::list<std::string> {
153         std::list<std::string> list;
154         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, list);
155         auto params = ConvertToJSValues(from, to);
156         JSRef<JSVal> jsVal = func->Call(JSRef<JSObject>(), params.size(), params.data());
157 
158         // convert js-array to std::list
159         if (!jsVal->IsArray()) {
160             TAG_LOGW(AceLogTag::ACE_REPEAT, "jsVal should be array.");
161             return list;
162         }
163         JSRef<JSArray> jsArr = JSRef<JSArray>::Cast(jsVal);
164         for (size_t i = 0; i < jsArr->Length(); i++) {
165             list.emplace_back(jsArr->GetValueAt(i)->ToString());
166         }
167         return list;
168     };
169 
170     auto onSetActiveRange = [execCtx = info.GetExecutionContext(), func = JSFUNC(handlers, "onSetActiveRange")](
171                             int32_t from, int32_t to) -> void {
172         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
173         auto params = ConvertToJSValues(from, to);
174         func->Call(JSRef<JSObject>(), params.size(), params.data());
175     };
176 
177     RepeatVirtualScrollModel::GetInstance()->Create(
178         totalCount, templateCachedCountMap, onCreateNode, onUpdateNode, onGetKeys4Range, onGetTypes4Range,
179         onSetActiveRange);
180 }
181 
UpdateRenderState(const JSCallbackInfo & info)182 void JSRepeatVirtualScroll::UpdateRenderState(const JSCallbackInfo& info)
183 {
184     ACE_SCOPED_TRACE("RepeatVirtualScroll:UpdateRenderState");
185     TAG_LOGD(AceLogTag::ACE_REPEAT, "JSRepeatVirtualScroll::UpdateRenderState");
186     if (!info[0]->IsNumber() || !info[1]->IsBoolean()) {
187         return;
188     }
189     auto totalCount = info[0]->ToNumber<uint32_t>();
190     auto visibleItemsChanged = info[1]->ToBoolean();
191     RepeatVirtualScrollModel::GetInstance()->UpdateRenderState(totalCount, visibleItemsChanged);
192 }
193 
OnMove(const JSCallbackInfo & info)194 void JSRepeatVirtualScroll::OnMove(const JSCallbackInfo& info)
195 {
196     if (!info[0]->IsFunction()) {
197         RepeatVirtualScrollModel::GetInstance()->OnMove(nullptr);
198         return;
199     }
200     auto onMove = [execCtx = info.GetExecutionContext(), func = JSRef<JSFunc>::Cast(info[0])](
201                       int32_t from, int32_t to) {
202         auto params = ConvertToJSValues(from, to);
203         func->Call(JSRef<JSObject>(), params.size(), params.data());
204     };
205     RepeatVirtualScrollModel::GetInstance()->OnMove(std::move(onMove));
206 }
207 
JSBind(BindingTarget globalObj)208 void JSRepeatVirtualScroll::JSBind(BindingTarget globalObj)
209 {
210     JSClass<JSRepeatVirtualScroll>::Declare("RepeatVirtualScrollNative");
211     JSClass<JSRepeatVirtualScroll>::StaticMethod("create", &JSRepeatVirtualScroll::Create);
212     JSClass<JSRepeatVirtualScroll>::StaticMethod("updateRenderState", &JSRepeatVirtualScroll::UpdateRenderState);
213     JSClass<JSRepeatVirtualScroll>::StaticMethod("onMove", &JSRepeatVirtualScroll::OnMove);
214     JSClass<JSRepeatVirtualScroll>::Bind<>(globalObj);
215 }
216 
217 } // namespace OHOS::Ace::Framework
218