1 /*
2  * Copyright (c) 2020 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 "directive/descriptor_utils.h"
17 #include "ace_log.h"
18 #include "directive/directive_watcher_callback.h"
19 #include "wrapper/js.h"
20 
21 namespace OHOS {
22 namespace ACELite {
23 const char * const DescriptorUtils::DESCRIPTOR_ATTR_GETTER = "getter";
24 const char * const DescriptorUtils::DESCRIPTOR_ATTR_RENDER = "render";
25 const char * const DescriptorUtils::DESCRIPTOR_ATTR_IF = "if";
26 const char * const DescriptorUtils::DESCRIPTOR_ATTR_FOR = "for";
27 const char * const DescriptorUtils::DESCRIPTOR_ATTR_RENDERED = "rendered";
28 const char * const DescriptorUtils::WATCHER_OPTION_ELEMENT = "element";
29 const char * const DescriptorUtils::WATCHER_OPTION_DESCRIPTOR = "descriptor";
30 
CreateIfDescriptor(JSValue getter,JSValue render)31 JSValue DescriptorUtils::CreateIfDescriptor(JSValue getter, JSValue render)
32 {
33     return CreateDescriptor(DESCRIPTOR_ATTR_IF, getter, render);
34 }
35 
CreateForDescriptor(JSValue getter,JSValue render)36 JSValue DescriptorUtils::CreateForDescriptor(JSValue getter, JSValue render)
37 {
38     return CreateDescriptor(DESCRIPTOR_ATTR_FOR, getter, render);
39 }
40 
CreateDescriptorWatcher(JSValue element,JSValue descriptor)41 JSValue DescriptorUtils::CreateDescriptorWatcher(JSValue element, JSValue descriptor)
42 {
43     JSValue getter = JSObject::Get(descriptor, DESCRIPTOR_ATTR_GETTER);
44     JSValue options = JSObject::Create();
45     // We should change the children of element when watcher callaback is triggered.
46     JSObject::Set(options, WATCHER_OPTION_ELEMENT, element);
47     // We should know which descriptor condition is changed.
48     JSObject::Set(options, WATCHER_OPTION_DESCRIPTOR, descriptor);
49 
50     JSValue watcher = CreateWatcher(getter, DirectiveWatcherCallback::Handler, options);
51     JSRelease(options);
52     JSRelease(getter);
53 
54     return watcher;
55 }
56 
IsIfDescriptor(JSValue descriptor)57 bool DescriptorUtils::IsIfDescriptor(JSValue descriptor)
58 {
59     return IsDescriptor(DESCRIPTOR_ATTR_IF, descriptor);
60 }
61 
IsForDescriptor(JSValue descriptor)62 bool DescriptorUtils::IsForDescriptor(JSValue descriptor)
63 {
64     return IsDescriptor(DESCRIPTOR_ATTR_FOR, descriptor);
65 }
66 
IsIfDescriptorShown(JSValue descriptor)67 bool DescriptorUtils::IsIfDescriptorShown(JSValue descriptor)
68 {
69     JSValue getter = JSObject::Get(descriptor, DESCRIPTOR_ATTR_GETTER);
70     JSValue condition = CallWithRootAbilitySlice(getter);
71     bool isShown = JSBoolean::Value(condition);
72     JSRelease(condition);
73     JSRelease(getter);
74     return isShown;
75 }
76 
RenderIfDescriptor(JSValue descriptor)77 JSValue DescriptorUtils::RenderIfDescriptor(JSValue descriptor)
78 {
79     JSValue render = JSObject::Get(descriptor, DESCRIPTOR_ATTR_RENDER);
80     JSValue rendered = CallWithRootAbilitySlice(render);
81     JSRelease(render);
82     JSObject::Set(descriptor, DESCRIPTOR_ATTR_RENDERED, rendered);
83     return rendered;
84 }
85 
RenderForDescriptor(JSValue descriptor)86 JSValue DescriptorUtils::RenderForDescriptor(JSValue descriptor)
87 {
88     JSValue getter = JSObject::Get(descriptor, DESCRIPTOR_ATTR_GETTER);
89 
90     JSValue array = CallWithRootAbilitySlice(getter);
91     JSRelease(getter);
92 
93     if (JSUndefined::Is(array)) {
94         HILOG_ERROR(HILOG_MODULE_ACE, "Failed to RenderForDescriptor because the result value is undefined.");
95         return JSUndefined::Create();
96     }
97 
98     JSValue render = JSObject::Get(descriptor, DESCRIPTOR_ATTR_RENDER);
99     // execute `array.map(render)`.
100     JSValue rendered = JSArray::Map(array, render);
101     JSRelease(render);
102     JSRelease(array);
103     JSObject::Set(descriptor, DESCRIPTOR_ATTR_RENDERED, rendered);
104     return rendered;
105 }
106 
GetDescriptorRendered(JSValue descriptor)107 JSValue DescriptorUtils::GetDescriptorRendered(JSValue descriptor)
108 {
109     return JSObject::Get(descriptor, DESCRIPTOR_ATTR_RENDERED);
110 }
111 
DelIfDescriptorRendered(JSValue descriptor)112 void DescriptorUtils::DelIfDescriptorRendered(JSValue descriptor)
113 {
114     JSValue descriptorOrElement = GetDescriptorRendered(descriptor);
115     if (!JSUndefined::Is(descriptorOrElement)) {
116         JSObject::Del(descriptor, DESCRIPTOR_ATTR_RENDERED);
117         // the rendered property need to be released
118         // and ReleaseDescriptorOrElement function will release it.
119         ReleaseDescriptorOrElement(descriptorOrElement);
120     }
121     // decrease the count of reference increased by jerry_get_property
122     JSRelease(descriptorOrElement);
123 }
124 
DelForDescriptorRendered(JSValue descriptor)125 void DescriptorUtils::DelForDescriptorRendered(JSValue descriptor)
126 {
127     JSValue descriptorOrElements = GetDescriptorRendered(descriptor);
128     if (!JSUndefined::Is(descriptorOrElements)) {
129         JSObject::Del(descriptor, DESCRIPTOR_ATTR_RENDERED);
130         ReleaseDescriptorOrElements(descriptorOrElements);
131         // the rendered property need to be released
132         // but ReleaseDescriptorOrElements function only release its element.
133         JSRelease(descriptorOrElements);
134     }
135     // decrease the count of reference increased by jerry_get_property
136     JSRelease(descriptorOrElements);
137 }
138 
ReleaseDescriptorOrElements(JSValue descriptorOrElements)139 void DescriptorUtils::ReleaseDescriptorOrElements(JSValue descriptorOrElements)
140 {
141     uint16_t size = JSArray::Length(descriptorOrElements);
142     for (uint16_t idx = 0; idx < size; ++idx) {
143         JSValue descriptorOrElement = JSArray::Get(descriptorOrElements, idx);
144         // Don't release descriptorOrElement after ReleaseDescriptorOrElement function
145         // because ReleaseDescriptorOrElement will release descriptorOrElement
146         ReleaseDescriptorOrElement(descriptorOrElement);
147     }
148 }
149 
ReleaseDescriptorOrElement(JSValue descriptorOrElement)150 void DescriptorUtils::ReleaseDescriptorOrElement(JSValue descriptorOrElement)
151 {
152     if (IsForDescriptor(descriptorOrElement)) {
153         ReleaseForDescriptor(descriptorOrElement);
154     } else if (IsIfDescriptor(descriptorOrElement)) {
155         ReleaseIfDescriptor(descriptorOrElement);
156     } else {
157         ReleaseElement(descriptorOrElement);
158     }
159 }
160 
ReleaseForDescriptor(JSValue descriptor)161 void DescriptorUtils::ReleaseForDescriptor(JSValue descriptor)
162 {
163     if (JSUndefined::Is(descriptor)) {
164         return;
165     }
166     DelForDescriptorRendered(descriptor);
167     JSRelease(descriptor);
168 }
169 
ReleaseIfDescriptor(JSValue descriptor)170 void DescriptorUtils::ReleaseIfDescriptor(JSValue descriptor)
171 {
172     if (JSUndefined::Is(descriptor)) {
173         return;
174     }
175     DelIfDescriptorRendered(descriptor);
176     JSRelease(descriptor);
177 }
178 
ReleaseElement(JSValue element)179 void DescriptorUtils::ReleaseElement(JSValue element)
180 {
181     if (JSUndefined::Is(element)) {
182         return;
183     }
184 
185     Component *component = nullptr;
186     bool isBinded = JSObject::GetNativePointer(element, reinterpret_cast<void **>(&component));
187     if (!isBinded || (component == nullptr)) {
188         return;
189     }
190 
191     bool isSuccess = JSObject::DelNativePointer(element);
192     if (!isSuccess) {
193         HILOG_WARN(HILOG_MODULE_ACE, "delete native view pointer from native element failed.");
194     }
195 
196     if (component->GetComponentName() != K_LIST) {
197         JSValue descriptors = component->GetDescriptors();
198         if (!JSUndefined::Is(descriptors)) {
199             ReleaseDescriptorOrElements(descriptors);
200         }
201     }
202 
203     component->Release();
204     delete component;
205     component = nullptr;
206 }
207 
CreateDescriptor(const char * const type,JSValue getter,JSValue render)208 JSValue DescriptorUtils::CreateDescriptor(const char * const type, JSValue getter, JSValue render)
209 {
210     if ((type == nullptr) || (strlen(type) == 0)) {
211         HILOG_ERROR(HILOG_MODULE_ACE, "Failed to create descriptor because the type argument is null or empty.");
212         return UNDEFINED;
213     }
214 
215     if (!JSFunction::Is(getter)) {
216         HILOG_ERROR(HILOG_MODULE_ACE, "Failed to create descriptor because the getter argument is not a function.");
217         return UNDEFINED;
218     }
219 
220     if (!JSFunction::Is(render)) {
221         HILOG_ERROR(HILOG_MODULE_ACE, "Failed to create descriptor because the render argument is not a function.");
222         return UNDEFINED;
223     }
224 
225     JSValue descriptor = JSObject::Create();
226     JSValue value = JSBoolean::Create(true);
227     JSObject::Set(descriptor, type, value);
228     JSRelease(value);
229 
230     JSObject::Set(descriptor, DESCRIPTOR_ATTR_GETTER, getter);
231     JSObject::Set(descriptor, DESCRIPTOR_ATTR_RENDER, render);
232 
233     return descriptor;
234 }
235 
IsDescriptor(const char * const type,JSValue descriptor)236 bool DescriptorUtils::IsDescriptor(const char * const type, JSValue descriptor)
237 {
238     if (JSUndefined::Is(descriptor)) {
239         return false;
240     }
241 
242     JSValue value = JSObject::Get(descriptor, type);
243     bool result = JSBoolean::Value(value);
244     JSRelease(value);
245     return result;
246 }
247 } // namespace ACELite
248 } // namespace OHOS
249