• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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