1 /*
2 * Copyright (c) 2021 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_view.h"
17
18 #include "base/log/ace_trace.h"
19 #include "core/pipeline/base/composed_element.h"
20 #include "frameworks/bridge/declarative_frontend/engine/content_storage_set.h"
21 #include "frameworks/bridge/declarative_frontend/engine/js_execution_scope_defines.h"
22 #include "frameworks/bridge/declarative_frontend/jsview/js_view_register.h"
23 #include "frameworks/bridge/declarative_frontend/view_stack_processor.h"
24
25 namespace OHOS::Ace::Framework {
26
JSView(const std::string & viewId,JSRef<JSObject> jsObject,JSRef<JSFunc> jsRenderFunction)27 JSView::JSView(const std::string& viewId, JSRef<JSObject> jsObject, JSRef<JSFunc> jsRenderFunction) : viewId_(viewId)
28 {
29 jsViewFunction_ = AceType::MakeRefPtr<ViewFunctions>(jsObject, jsRenderFunction);
30 instanceId_ = Container::CurrentId();
31 LOGD("JSView constructor");
32 }
33
~JSView()34 JSView::~JSView()
35 {
36 LOGD("Destroy");
37 jsViewFunction_.Reset();
38 };
39
CreateComponent()40 RefPtr<OHOS::Ace::Component> JSView::CreateComponent()
41 {
42 ACE_SCOPED_TRACE("JSView::CreateSpecializedComponent");
43 // create component, return new something, need to set proper ID
44
45 std::string key = ViewStackProcessor::GetInstance()->ProcessViewId(viewId_);
46 auto composedComponent = AceType::MakeRefPtr<ComposedComponent>(key, "view");
47
48 // add callback for element creation to component, and get pointer reference
49 // to the element on creation. When state of this view changes, mark the
50 // element to dirty.
51 auto renderFunction = [weak = AceType::WeakClaim(this)](const RefPtr<Component>& component) -> RefPtr<Component> {
52 auto jsView = weak.Upgrade();
53 return jsView ? jsView->InternalRender(component) : nullptr;
54 };
55
56 auto removeFunction = [weak = AceType::WeakClaim(this)]() -> void {
57 auto jsView = weak.Upgrade();
58 if (jsView && jsView->jsViewFunction_) {
59 jsView->jsViewFunction_->ExecuteDisappear();
60 }
61 };
62
63 auto elementFunction = [weak = AceType::WeakClaim(this), renderFunction, removeFunction](
64 const RefPtr<ComposedElement>& element) {
65 auto jsView = weak.Upgrade();
66 if (!jsView) {
67 LOGE("the js view is nullptr in element function");
68 return;
69 }
70 if (jsView->element_.Invalid()) {
71 ACE_SCORING_EVENT("Component[" + jsView->viewId_ + "].Appear");
72 if (jsView->jsViewFunction_) {
73 jsView->jsViewFunction_->ExecuteAppear();
74 }
75 }
76 jsView->element_ = element;
77 // add render function callback to element. when the element rebuilds due
78 // to state update it will call this callback to get the new child component.
79 if (element) {
80 element->SetRenderFunction(std::move(renderFunction));
81 element->SetRemoveFunction(std::move(removeFunction));
82 if (jsView->jsViewFunction_ && jsView->jsViewFunction_->HasPageTransition()) {
83 auto pageTransitionFunction = [weak]() -> RefPtr<Component> {
84 auto jsView = weak.Upgrade();
85 if (!jsView || !jsView->jsViewFunction_) {
86 return nullptr;
87 }
88 {
89 ACE_SCORING_EVENT("Component[" + jsView->viewId_ + "].Transition");
90 jsView->jsViewFunction_->ExecuteTransition();
91 }
92 return jsView->BuildPageTransitionComponent();
93 };
94 element->SetPageTransitionFunction(std::move(pageTransitionFunction));
95 }
96 }
97 };
98
99 composedComponent->SetElementFunction(std::move(elementFunction));
100
101 if (IsStatic()) {
102 LOGD("will mark composedComponent as static");
103 composedComponent->SetStatic();
104 }
105 return composedComponent;
106 }
107
BuildPageTransitionComponent()108 RefPtr<OHOS::Ace::PageTransitionComponent> JSView::BuildPageTransitionComponent()
109 {
110 auto pageTransitionComponent = ViewStackProcessor::GetInstance()->GetPageTransitionComponent();
111 ViewStackProcessor::GetInstance()->ClearPageTransitionComponent();
112 return pageTransitionComponent;
113 }
114
InternalRender(const RefPtr<Component> & parent)115 RefPtr<OHOS::Ace::Component> JSView::InternalRender(const RefPtr<Component>& parent)
116 {
117 JAVASCRIPT_EXECUTION_SCOPE_STATIC;
118 needsUpdate_ = false;
119 if (!jsViewFunction_) {
120 LOGE("JSView: InternalRender jsViewFunction_ error");
121 return nullptr;
122 }
123 {
124 ACE_SCORING_EVENT("Component[" + viewId_ + "].AboutToRender");
125 jsViewFunction_->ExecuteAboutToRender();
126 }
127 {
128 ACE_SCORING_EVENT("Component[" + viewId_ + "].Build");
129 jsViewFunction_->ExecuteRender();
130 }
131 {
132 ACE_SCORING_EVENT("Component[" + viewId_ + "].OnRenderDone");
133 jsViewFunction_->ExecuteOnRenderDone();
134 }
135 CleanUpAbandonedChild();
136 jsViewFunction_->Destroy(this);
137 auto buildComponent = ViewStackProcessor::GetInstance()->Finish();
138 return buildComponent;
139 }
140
141 /**
142 * marks the JSView's composed component as needing update / rerender
143 */
MarkNeedUpdate()144 void JSView::MarkNeedUpdate()
145 {
146 ACE_DCHECK((!GetElement().Invalid()));
147 ACE_SCOPED_TRACE("JSView::MarkNeedUpdate");
148
149 auto element = GetElement().Upgrade();
150 if (element) {
151 element->MarkDirty();
152 }
153 needsUpdate_ = true;
154 }
155
SyncInstanceId()156 void JSView::SyncInstanceId()
157 {
158 restoreInstanceId_ = Container::CurrentId();
159 ContainerScope::UpdateCurrent(instanceId_);
160 }
161
RestoreInstanceId()162 void JSView::RestoreInstanceId()
163 {
164 ContainerScope::UpdateCurrent(restoreInstanceId_);
165 }
166
Destroy(JSView * parentCustomView)167 void JSView::Destroy(JSView* parentCustomView)
168 {
169 LOGD("JSView::Destroy start");
170 DestroyChild(parentCustomView);
171 {
172 ACE_SCORING_EVENT("Component[" + viewId_ + "].Disappear");
173 jsViewFunction_->ExecuteDisappear();
174 }
175 {
176 ACE_SCORING_EVENT("Component[" + viewId_ + "].AboutToBeDeleted");
177 jsViewFunction_->ExecuteAboutToBeDeleted();
178 }
179 LOGD("JSView::Destroy end");
180 }
181
Create(const JSCallbackInfo & info)182 void JSView::Create(const JSCallbackInfo& info)
183 {
184 if (info[0]->IsObject()) {
185 JSRef<JSObject> object = JSRef<JSObject>::Cast(info[0]);
186 auto* view = object->Unwrap<JSView>();
187 if (view == nullptr) {
188 LOGE("JSView is null");
189 return;
190 }
191 ViewStackProcessor::GetInstance()->Push(view->CreateComponent(), true);
192 } else {
193 LOGE("JSView Object is expected.");
194 }
195 }
196
JSBind(BindingTarget object)197 void JSView::JSBind(BindingTarget object)
198 {
199 JSClass<JSView>::Declare("NativeView");
200 JSClass<JSView>::StaticMethod("create", &JSView::Create);
201 JSClass<JSView>::Method("markNeedUpdate", &JSView::MarkNeedUpdate);
202 JSClass<JSView>::Method("syncInstanceId", &JSView::SyncInstanceId);
203 JSClass<JSView>::Method("restoreInstanceId", &JSView::RestoreInstanceId);
204 JSClass<JSView>::Method("needsUpdate", &JSView::NeedsUpdate);
205 JSClass<JSView>::Method("markStatic", &JSView::MarkStatic);
206 JSClass<JSView>::CustomMethod("getContext", &JSView::GetContext);
207 JSClass<JSView>::CustomMethod("getContentStorage", &JSView::GetContentStorage);
208 JSClass<JSView>::CustomMethod("findChildById", &JSView::FindChildById);
209 JSClass<JSView>::Inherit<JSViewAbstract>();
210 JSClass<JSView>::Bind(object, ConstructorCallback, DestructorCallback);
211 }
212
GetContext(const JSCallbackInfo & info)213 void JSView::GetContext(const JSCallbackInfo& info)
214 {
215 info.SetReturnValue(ContentStorageSet::GetCurrentContext());
216 }
217
GetContentStorage(const JSCallbackInfo & info)218 void JSView::GetContentStorage(const JSCallbackInfo& info)
219 {
220 info.SetReturnValue(ContentStorageSet::GetCurrentStorage());
221 }
222
FindChildById(const JSCallbackInfo & info)223 void JSView::FindChildById(const JSCallbackInfo& info)
224 {
225 LOGD("JSView::FindChildById");
226 if (info[0]->IsNumber() || info[0]->IsString()) {
227 std::string viewId = info[0]->ToString();
228 info.SetReturnValue(GetChildById(viewId));
229 } else {
230 LOGE("JSView FindChildById with invalid arguments.");
231 JSException::Throw("%s", "JSView FindChildById with invalid arguments.");
232 }
233 }
234
ConstructorCallback(const JSCallbackInfo & info)235 void JSView::ConstructorCallback(const JSCallbackInfo& info)
236 {
237 JSRef<JSObject> thisObj = info.This();
238 JSRef<JSVal> renderFunc = thisObj->GetProperty("render");
239 if (!renderFunc->IsFunction()) {
240 LOGE("View derived classes must provide render(){...} function");
241 JSException::Throw("%s", "View derived classes must provide render(){...} function");
242 return;
243 }
244
245 int argc = info.Length();
246 if (argc > 1 && (info[0]->IsNumber() || info[0]->IsString())) {
247 std::string viewId = info[0]->ToString();
248 auto instance = AceType::MakeRefPtr<JSView>(viewId, info.This(), JSRef<JSFunc>::Cast(renderFunc));
249 auto context = info.GetExecutionContext();
250 instance->SetContext(context);
251 instance->IncRefCount();
252 info.SetReturnValue(AceType::RawPtr(instance));
253 if (!info[1]->IsUndefined() && info[1]->IsObject()) {
254 JSRef<JSObject> parentObj = JSRef<JSObject>::Cast(info[1]);
255 auto* parentView = parentObj->Unwrap<JSView>();
256 if (parentView != nullptr) {
257 auto id = parentView->AddChildById(viewId, info.This());
258 instance->id_ = id;
259 }
260 }
261 LOGD("JSView ConstructorCallback: %{public}s", instance->id_.c_str());
262 } else {
263 LOGE("JSView creation with invalid arguments.");
264 JSException::Throw("%s", "JSView creation with invalid arguments.");
265 }
266 }
267
DestructorCallback(JSView * view)268 void JSView::DestructorCallback(JSView* view)
269 {
270 if (view == nullptr) {
271 LOGE("DestructorCallback failed: the view is nullptr");
272 return;
273 }
274 LOGD("JSView(DestructorCallback) start");
275 view->DecRefCount();
276 LOGD("JSView(DestructorCallback) end");
277 }
278
DestroyChild(JSView * parentCustomView)279 void JSView::DestroyChild(JSView* parentCustomView)
280 {
281 LOGD("JSView::DestroyChild start");
282 for (auto&& child : customViewChildren_) {
283 auto* view = child.second->Unwrap<JSView>();
284 if (view != nullptr) {
285 view->Destroy(this);
286 }
287 child.second.Reset();
288 }
289 customViewChildren_.clear();
290 for (auto&& lazyChild : customViewChildrenWithLazy_) {
291 auto* view = lazyChild.second->Unwrap<JSView>();
292 if (view != nullptr) {
293 view->Destroy(this);
294 }
295 lazyChild.second.Reset();
296 }
297 customViewChildrenWithLazy_.clear();
298 LOGD("JSView::DestroyChild end");
299 }
300
CleanUpAbandonedChild()301 void JSView::CleanUpAbandonedChild()
302 {
303 auto startIter = customViewChildren_.begin();
304 auto endIter = customViewChildren_.end();
305 std::vector<std::string> removedViewIds;
306 while (startIter != endIter) {
307 auto found = lastAccessedViewIds_.find(startIter->first);
308 if (found == lastAccessedViewIds_.end()) {
309 LOGD(" found abandoned view with id %{public}s", startIter->first.c_str());
310 removedViewIds.emplace_back(startIter->first);
311 auto* view = startIter->second->Unwrap<JSView>();
312 if (view != nullptr) {
313 view->Destroy(this);
314 }
315 startIter->second.Reset();
316 }
317 ++startIter;
318 }
319
320 for (auto& viewId : removedViewIds) {
321 customViewChildren_.erase(viewId);
322 }
323
324 lastAccessedViewIds_.clear();
325 }
326
GetChildById(const std::string & viewId)327 JSRef<JSObject> JSView::GetChildById(const std::string& viewId)
328 {
329 auto id = ViewStackProcessor::GetInstance()->ProcessViewId(viewId);
330 auto found = customViewChildren_.find(id);
331 if (found != customViewChildren_.end()) {
332 ChildAccessedById(id);
333 return found->second;
334 }
335 auto lazyItem = customViewChildrenWithLazy_.find(id);
336 if (lazyItem != customViewChildrenWithLazy_.end()) {
337 return lazyItem->second;
338 }
339 return {};
340 }
341
AddChildById(const std::string & viewId,const JSRef<JSObject> & obj)342 std::string JSView::AddChildById(const std::string& viewId, const JSRef<JSObject>& obj)
343 {
344 auto id = ViewStackProcessor::GetInstance()->ProcessViewId(viewId);
345 JSView* jsview = nullptr;
346 if (isLazyForEachProcessed_) {
347 auto result = customViewChildrenWithLazy_.try_emplace(id, obj);
348 if (!result.second) {
349 jsview = result.first->second->Unwrap<JSView>();
350 result.first->second = obj;
351 }
352 lazyItemGroups_[lazyItemGroupId_].emplace_back(id);
353 } else {
354 auto result = customViewChildren_.try_emplace(id, obj);
355 if (!result.second) {
356 jsview = result.first->second->Unwrap<JSView>();
357 result.first->second = obj;
358 }
359 ChildAccessedById(id);
360 }
361 if (jsview != nullptr) {
362 jsview->Destroy(this);
363 }
364 return id;
365 }
366
RemoveChildGroupById(const std::string & viewId)367 void JSView::RemoveChildGroupById(const std::string& viewId)
368 {
369 JAVASCRIPT_EXECUTION_SCOPE_STATIC;
370 LOGD("RemoveChildGroupById in lazy for each case: %{public}s", viewId.c_str());
371 auto iter = lazyItemGroups_.find(viewId);
372 if (iter == lazyItemGroups_.end()) {
373 LOGI("can not find this groud to delete: %{public}s", viewId.c_str());
374 return;
375 }
376 std::vector<std::string> removedViewIds;
377 for (auto&& item : iter->second) {
378 auto removeView = customViewChildrenWithLazy_.find(item);
379 if (removeView != customViewChildrenWithLazy_.end()) {
380 auto* view = removeView->second->Unwrap<JSView>();
381 if (view != nullptr) {
382 view->Destroy(this);
383 }
384 removeView->second.Reset();
385 removedViewIds.emplace_back(item);
386 }
387 }
388
389 for (auto&& removeId : removedViewIds) {
390 customViewChildrenWithLazy_.erase(removeId);
391 }
392 lazyItemGroups_.erase(iter);
393 }
394
ChildAccessedById(const std::string & viewId)395 void JSView::ChildAccessedById(const std::string& viewId)
396 {
397 lastAccessedViewIds_.emplace(viewId);
398 }
399
400 } // namespace OHOS::Ace::Framework
401