• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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 "frameworks/bridge/declarative_frontend/jsview/js_lazy_foreach.h"
17 
18 #include <functional>
19 #include <set>
20 #include <string>
21 
22 #include "base/memory/ace_type.h"
23 #include "base/memory/referenced.h"
24 #include "base/utils/utils.h"
25 #include "bridge/common/utils/utils.h"
26 #include "bridge/declarative_frontend/ark_theme/theme_apply/js_lazy_foreach_theme.h"
27 #include "bridge/declarative_frontend/engine/js_object_template.h"
28 #include "bridge/declarative_frontend/jsview/js_lazy_foreach_actuator.h"
29 #include "bridge/declarative_frontend/jsview/js_lazy_foreach_builder.h"
30 #ifndef NG_BUILD
31 #include "bridge/declarative_frontend/jsview/js_lazy_foreach_component.h"
32 #endif
33 #include "bridge/declarative_frontend/jsview/js_view.h"
34 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
35 #include "bridge/declarative_frontend/jsview/models/lazy_for_each_model_impl.h"
36 #include "bridge/declarative_frontend/view_stack_processor.h"
37 #include "core/common/container.h"
38 #include "core/common/container_scope.h"
39 #include "core/components_ng/base/view_stack_model.h"
40 #include "core/components_ng/syntax/lazy_for_each_model.h"
41 #include "core/components_ng/syntax/lazy_for_each_model_ng.h"
42 
43 namespace OHOS::Ace {
GetInstance()44 LazyForEachModel* LazyForEachModel::GetInstance()
45 {
46 #ifdef NG_BUILD
47     static NG::LazyForEachModelNG instance;
48     return &instance;
49 #else
50     if (Container::IsCurrentUseNewPipeline()) {
51         static NG::LazyForEachModelNG instance;
52         return &instance;
53     } else {
54         static Framework::LazyForEachModelImpl instance;
55         return &instance;
56     }
57 #endif
58 }
59 } // namespace OHOS::Ace
60 
61 namespace OHOS::Ace::Framework {
62 
JSBind(BindingTarget globalObj)63 void JSDataChangeListener::JSBind(BindingTarget globalObj)
64 {
65     JSClass<JSDataChangeListener>::Declare("__ohos_ace_inner_JSDataChangeListener__");
66     // API7 onEditChanged deprecated
67     JSClass<JSDataChangeListener>::CustomMethod("onDataReloaded", &JSDataChangeListener::OnDataReloaded);
68     JSClass<JSDataChangeListener>::CustomMethod("onDataReload", &JSDataChangeListener::OnDataReloaded);
69     // API7 onDataAdded deprecated
70     JSClass<JSDataChangeListener>::CustomMethod("onDataAdded", &JSDataChangeListener::OnDataAdded);
71     JSClass<JSDataChangeListener>::CustomMethod("onDataAdd", &JSDataChangeListener::OnDataAdded);
72     // API7 onDataDeleted deprecated
73     JSClass<JSDataChangeListener>::CustomMethod("onDataDeleted", &JSDataChangeListener::OnDataDeleted);
74     JSClass<JSDataChangeListener>::CustomMethod("onDataDelete", &JSDataChangeListener::OnDataDeleted);
75     // API7 onDataChanged deprecated
76     JSClass<JSDataChangeListener>::CustomMethod("onDataChanged", &JSDataChangeListener::OnDataChanged);
77     JSClass<JSDataChangeListener>::CustomMethod("onDataChange", &JSDataChangeListener::OnDataChanged);
78     // API7 onDataMoved deprecated
79     JSClass<JSDataChangeListener>::CustomMethod("onDataMoved", &JSDataChangeListener::OnDataMoved);
80     JSClass<JSDataChangeListener>::CustomMethod("onDataMove", &JSDataChangeListener::OnDataMoved);
81     // temporary interface
82     JSClass<JSDataChangeListener>::CustomMethod("onDataBulkAdded", &JSDataChangeListener::OnDataBulkAdded);
83     JSClass<JSDataChangeListener>::CustomMethod("onDataBulkAdd", &JSDataChangeListener::OnDataBulkAdded);
84     JSClass<JSDataChangeListener>::CustomMethod("onDataBulkDeleted", &JSDataChangeListener::OnDataBulkDeleted);
85     JSClass<JSDataChangeListener>::CustomMethod("onDataBulkDelete", &JSDataChangeListener::OnDataBulkDeleted);
86     JSClass<JSDataChangeListener>::CustomMethod("onDataBulkChanged", &JSDataChangeListener::OnDataBulkChanged);
87     JSClass<JSDataChangeListener>::CustomMethod("onDataBulkChange", &JSDataChangeListener::OnDataBulkChanged);
88     // API12 onDatasetChange
89     JSClass<JSDataChangeListener>::CustomMethod("onDatasetChange", &JSDataChangeListener::OnDatasetChange);
90     JSClass<JSDataChangeListener>::Bind(
91         globalObj, &JSDataChangeListener::Constructor, &JSDataChangeListener::Destructor);
92 }
93 
CreateActuator(const std::string & viewId)94 RefPtr<JSLazyForEachActuator> CreateActuator(const std::string& viewId)
95 {
96 #ifdef NG_BUILD
97     return AceType::MakeRefPtr<JSLazyForEachBuilder>();
98 #else
99     if (Container::IsCurrentUseNewPipeline()) {
100         return AceType::MakeRefPtr<JSLazyForEachBuilder>();
101     } else {
102         return AceType::MakeRefPtr<JSLazyForEachComponent>(viewId);
103     }
104 #endif
105 }
106 
107 namespace {
108 
109 enum {
110     PARAM_VIEW_ID = 0,
111     PARAM_PARENT_VIEW,
112     PARAM_DATA_SOURCE,
113     PARAM_ITEM_GENERATOR,
114     PARAM_KEY_GENERATOR,
115     PARAM_UPDATE_CHANGEDNODE,
116 
117     MIN_PARAM_SIZE = PARAM_KEY_GENERATOR,
118     MAX_PARAM_SIZE = 6,
119 };
120 
ParseAndVerifyParams(const JSCallbackInfo & info,JSRef<JSVal> (& params)[MAX_PARAM_SIZE])121 bool ParseAndVerifyParams(const JSCallbackInfo& info, JSRef<JSVal> (&params)[MAX_PARAM_SIZE])
122 {
123     if (info.Length() < MIN_PARAM_SIZE) {
124         return false;
125     }
126 
127     if (!info[PARAM_VIEW_ID]->IsNumber() && !info[PARAM_VIEW_ID]->IsString()) {
128         return false;
129     }
130     if (!info[PARAM_PARENT_VIEW]->IsObject()) {
131         return false;
132     }
133     if (!info[PARAM_DATA_SOURCE]->IsObject()) {
134         return false;
135     }
136     if (!info[PARAM_ITEM_GENERATOR]->IsFunction()) {
137         return false;
138     }
139     if (info.Length() > MIN_PARAM_SIZE &&
140         !(info[PARAM_KEY_GENERATOR]->IsFunction() || info[PARAM_KEY_GENERATOR]->IsUndefined())) {
141         return false;
142     }
143     if (info.Length() > MIN_PARAM_SIZE + 1 && !info[PARAM_UPDATE_CHANGEDNODE]->IsBoolean()) {
144         return false;
145     }
146 
147     for (int32_t idx = PARAM_VIEW_ID; idx < std::min(info.Length(), static_cast<uint32_t>(MAX_PARAM_SIZE)); ++idx) {
148         params[idx] = info[idx];
149     }
150     return true;
151 }
152 
153 } // namespace
154 
JSBind(BindingTarget globalObj)155 void JSLazyForEach::JSBind(BindingTarget globalObj)
156 {
157     JSClass<JSLazyForEach>::Declare("LazyForEach");
158     JSClass<JSLazyForEach>::StaticMethod("create", &JSLazyForEach::Create);
159     JSClass<JSLazyForEach>::StaticMethod("pop", &JSLazyForEach::Pop);
160     JSClass<JSLazyForEach>::StaticMethod("onMove", &JSLazyForEach::OnMove);
161     JSClass<JSLazyForEach>::Bind(globalObj);
162 
163     JSDataChangeListener::JSBind(globalObj);
164 }
165 
Create(const JSCallbackInfo & info)166 void JSLazyForEach::Create(const JSCallbackInfo& info)
167 {
168     JSRef<JSVal> params[MAX_PARAM_SIZE];
169     if (!ParseAndVerifyParams(info, params)) {
170         TAG_LOGW(AceLogTag::ACE_LAZY_FOREACH, "Invalid arguments for LazyForEach");
171         return;
172     }
173     if (!params[PARAM_PARENT_VIEW]->IsObject()|| !params[PARAM_DATA_SOURCE]->IsObject()
174         || !params[PARAM_ITEM_GENERATOR]->IsFunction() || !params[PARAM_VIEW_ID]->IsString()) {
175             return;
176     }
177     std::string viewId = ViewStackModel::GetInstance()->ProcessViewId(params[PARAM_VIEW_ID]->ToString());
178 
179     JSRef<JSObject> parentViewObj = JSRef<JSObject>::Cast(params[PARAM_PARENT_VIEW]);
180 
181     // LazyForEach is not in observeComponentCreation, mark isDeleting_ here
182     JSRef<JSVal> isDeleting = parentViewObj->GetProperty("isDeleting_");
183     if (isDeleting->IsBoolean() && isDeleting->ToBoolean()) {
184         return;
185     }
186 
187     JSRef<JSObject> dataSourceObj = JSRef<JSObject>::Cast(params[PARAM_DATA_SOURCE]);
188     JSRef<JSFunc> itemGenerator = JSRef<JSFunc>::Cast(params[PARAM_ITEM_GENERATOR]);
189     JSLazyForEachTheme::ObtainItemGeneratorForThemeSupport(info.GetVm(), itemGenerator);
190     ItemKeyGenerator keyGenFunc;
191     bool updateChangedNodeFlag = false;
192 
193     if (params[PARAM_KEY_GENERATOR]->IsUndefined()) {
194         keyGenFunc = [viewId](const JSRef<JSVal>&, size_t index) { return viewId + "-" + std::to_string(index); };
195     } else if (params[PARAM_KEY_GENERATOR]->IsFunction()) {
196         keyGenFunc = [viewId, keyGenerator = JSRef<JSFunc>::Cast(params[PARAM_KEY_GENERATOR])](
197                          const JSRef<JSVal>& jsVal, size_t index) {
198             JSRef<JSVal> params[] = { jsVal, JSRef<JSVal>::Make(ToJSValue(index)) };
199             auto key = keyGenerator->Call(JSRef<JSObject>(), ArraySize(params), params);
200             return viewId + "-" + (key->IsString() || key->IsNumber() ? key->ToString() : std::to_string(index));
201         };
202     }
203 
204     if (!params[PARAM_UPDATE_CHANGEDNODE]->IsUndefined()) {
205         updateChangedNodeFlag = params[PARAM_UPDATE_CHANGEDNODE]->ToBoolean();
206     }
207 
208     const auto& actuator = CreateActuator(viewId);
209     actuator->SetJSExecutionContext(info.GetExecutionContext());
210     actuator->SetParentViewObj(parentViewObj);
211     actuator->SetDataSourceObj(dataSourceObj);
212     actuator->SetItemGenerator(itemGenerator, std::move(keyGenFunc));
213     actuator->SetUpdateChangedNodeFlag(updateChangedNodeFlag);
214     if (ViewStackModel::GetInstance()->IsPrebuilding()) {
215         auto createFunc = [actuator]() {
216             LazyForEachModel::GetInstance()->Create(actuator);
217         };
218         return ViewStackModel::GetInstance()->PushPrebuildCompCmd("[JSLazyForEach][create]", createFunc);
219     }
220     LazyForEachModel::GetInstance()->Create(actuator);
221 }
222 
Pop()223 void JSLazyForEach::Pop()
224 {
225     if (ViewStackModel::GetInstance()->IsPrebuilding()) {
226         return ViewStackModel::GetInstance()->PushPrebuildCompCmd("[JSLazyForEach][pop]", &JSLazyForEach::Pop);
227     }
228     auto* stack = NG::ViewStackProcessor::GetInstance();
229     if (stack->GetMainFrameNode() && stack->GetMainFrameNode()->GetTag() == V2::TABS_ETS_TAG) {
230         return;
231     }
232     ViewStackModel::GetInstance()->PopContainer();
233 }
234 
OnMove(const JSCallbackInfo & info)235 void JSLazyForEach::OnMove(const JSCallbackInfo& info)
236 {
237     if (info[0]->IsFunction()) {
238         auto context = info.GetExecutionContext();
239         auto onMove = [execCtx = context, func = JSRef<JSFunc>::Cast(info[0])](int32_t from, int32_t to) {
240             auto params = ConvertToJSValues(from, to);
241             func->Call(JSRef<JSObject>(), params.size(), params.data());
242         };
243         LazyForEachModel::GetInstance()->OnMove(std::move(onMove));
244         if ((info.Length() > 1) && info[1]->IsObject()) {
245             JsParseItemDragEventHandler(context, info[1]);
246         } else {
247             LazyForEachModel::GetInstance()->SetItemDragHandler(nullptr, nullptr, nullptr, nullptr);
248         }
249     } else {
250         LazyForEachModel::GetInstance()->OnMove(nullptr);
251         LazyForEachModel::GetInstance()->SetItemDragHandler(nullptr, nullptr, nullptr, nullptr);
252     }
253 }
254 
JsParseItemDragEventHandler(const JsiExecutionContext & context,const JSRef<JSVal> & jsValue)255 void JSLazyForEach::JsParseItemDragEventHandler(
256     const JsiExecutionContext& context, const JSRef<JSVal>& jsValue)
257 {
258     auto itemDragEventObj = JSRef<JSObject>::Cast(jsValue);
259 
260     auto onLongPress = itemDragEventObj->GetProperty("onLongPress");
261     std::function<void(int32_t)> onLongPressCallback;
262     if (onLongPress->IsFunction()) {
263         onLongPressCallback = [execCtx = context, func = JSRef<JSFunc>::Cast(onLongPress)](int32_t index) {
264             auto params = ConvertToJSValues(index);
265             func->Call(JSRef<JSObject>(), params.size(), params.data());
266         };
267     }
268 
269     auto onDragStart = itemDragEventObj->GetProperty("onDragStart");
270     std::function<void(int32_t)> onDragStartCallback;
271     if (onDragStart->IsFunction()) {
272         onDragStartCallback = [execCtx = context, func = JSRef<JSFunc>::Cast(onDragStart)](int32_t index) {
273             auto params = ConvertToJSValues(index);
274             func->Call(JSRef<JSObject>(), params.size(), params.data());
275         };
276     }
277 
278     auto onMoveThrough = itemDragEventObj->GetProperty("onMoveThrough");
279     std::function<void(int32_t, int32_t)> onMoveThroughCallback;
280     if (onMoveThrough->IsFunction()) {
281         onMoveThroughCallback = [execCtx = context, func = JSRef<JSFunc>::Cast(onMoveThrough)](
282                                     int32_t from, int32_t to) {
283             auto params = ConvertToJSValues(from, to);
284             func->Call(JSRef<JSObject>(), params.size(), params.data());
285         };
286     }
287 
288     auto onDrop = itemDragEventObj->GetProperty("onDrop");
289     std::function<void(int32_t)> onDropCallback;
290     if (onDrop->IsFunction()) {
291         onDropCallback = [execCtx = context, func = JSRef<JSFunc>::Cast(onDrop)](int32_t index) {
292             auto params = ConvertToJSValues(index);
293             func->Call(JSRef<JSObject>(), params.size(), params.data());
294         };
295     }
296     LazyForEachModel::GetInstance()->SetItemDragHandler(std::move(onLongPressCallback), std::move(onDragStartCallback),
297         std::move(onMoveThroughCallback), std::move(onDropCallback));
298 }
299 } // namespace OHOS::Ace::Framework
300