1 /*
2  * Copyright (c) 2020-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 "stylemgr/app_style_manager.h"
17 #include "ace_log.h"
18 #include "component.h"
19 #include "js_app_context.h"
20 
21 namespace OHOS {
22 namespace ACELite {
23 const char * const AppStyleManager::ID = "id";
24 const char * const AppStyleManager::ATTR_STATIC_STYLE = "staticStyle";
25 const char * const AppStyleManager::ATTR_DYNAMIC_STYLE = "dynamicStyle";
26 const char * const AppStyleManager::ATTR_STATIC_CLASS = "staticClass";
AppStyleManager()27 AppStyleManager::AppStyleManager() : styleSheet_(nullptr), flags_(nullptr)
28 {
29     const uint8_t divisor = 2;
30     flagsLen_ = (KEYWORDS_MAX & 1) ? (KEYWORDS_MAX / divisor) + 1 : (KEYWORDS_MAX / divisor);
31     // prepare jerry string value in advance to less the overhead
32     idStrValue_ = jerry_create_string(reinterpret_cast<const jerry_char_t *>(ID));
33     attrsStrValue_ = jerry_create_string(reinterpret_cast<const jerry_char_t *>(ATTR_ATTRS));
34     staticStyleStrValue_ = jerry_create_string(reinterpret_cast<const jerry_char_t *>(ATTR_STATIC_STYLE));
35     dynamicStyleStrValue_ = jerry_create_string(reinterpret_cast<const jerry_char_t *>(ATTR_DYNAMIC_STYLE));
36     staticClassStrValue_ = jerry_create_string(reinterpret_cast<const jerry_char_t *>(ATTR_STATIC_CLASS));
37 }
38 
~AppStyleManager()39 AppStyleManager::~AppStyleManager()
40 {
41     ACE_FREE(flags_);
42     if (styleSheet_ != nullptr) {
43         styleSheet_->Reset();
44         delete styleSheet_;
45         styleSheet_ = nullptr;
46     }
47     ReleaseJerryValue(idStrValue_, attrsStrValue_, dynamicStyleStrValue_, staticStyleStrValue_, staticClassStrValue_,
48                       VA_ARG_END_FLAG);
49 }
50 
Prepare()51 void AppStyleManager::Prepare()
52 {
53     if (flags_ != nullptr) {
54         return;
55     }
56     if (flagsLen_ == 0) {
57         return;
58     }
59 
60     flags_ = static_cast<StyleFlag *>(ace_malloc(flagsLen_ * sizeof(StyleFlag)));
61     if (flags_ == nullptr) {
62         HILOG_ERROR(HILOG_MODULE_ACE, "malloc flags data buffer failed");
63     }
64 }
65 
GetStyleSheet()66 const AppStyleSheet *AppStyleManager::GetStyleSheet()
67 {
68     return styleSheet_;
69 }
70 
ApplyComponentStyles(const jerry_value_t options,Component & curr)71 void AppStyleManager::ApplyComponentStyles(const jerry_value_t options, Component& curr)
72 {
73     ResetFlags();
74     HandleStaticStyle(options, curr);
75     HandleDynamicStyle(options, curr);
76     HandleIDSelectors(options, curr);
77     HandleClassSelectors(options, curr);
78 }
79 
ResetFlags()80 void AppStyleManager::ResetFlags()
81 {
82     if (flags_ == nullptr) {
83         return;
84     }
85 
86     for (uint16_t i = 0; i < flagsLen_; i++) {
87         flags_[i].u8Byte = 0;
88     }
89 }
90 
InitStyleSheet(jerry_value_t jsStyleSheetObj)91 void AppStyleManager::InitStyleSheet(jerry_value_t jsStyleSheetObj)
92 {
93     if (jerry_value_is_undefined(jsStyleSheetObj) || jerry_value_is_error(jsStyleSheetObj)) {
94         return;
95     }
96 
97     if (styleSheet_ == nullptr) {
98         styleSheet_ = new AppStyleSheet();
99     }
100 
101     if (styleSheet_ != nullptr) {
102         styleSheet_->InitSheet(jsStyleSheetObj);
103     }
104 }
105 
106 // return value must be released by caller
GetStyleObjFromOptions(jerry_value_t options,const jerry_value_t name)107 jerry_value_t AppStyleManager::GetStyleObjFromOptions(jerry_value_t options, const jerry_value_t name)
108 {
109     jerry_value_t value = UNDEFINED;
110     if (jerry_value_is_undefined(options) || jerry_value_is_undefined(name)) {
111         return value;
112     }
113 
114     jerry_value_t valueContainer = options;
115     bool containerNeedReleased = false;
116     if (name == idStrValue_) {
117         // get attrs object from options
118         if (!HasOwnProperty(options, attrsStrValue_)) {
119             // if no attrs in options, return undefined directly
120             return value;
121         }
122         valueContainer = jerry_get_property(options, attrsStrValue_);
123         // mark that the valueContainer need to be released when getting out of scope
124         containerNeedReleased = true;
125     }
126 
127     // check if container object have target property, if not, just return undefined
128     if (HasOwnProperty(valueContainer, name)) {
129         value = jerry_get_property(valueContainer, name);
130     }
131 
132     if (containerNeedReleased) {
133         jerry_release_value(valueContainer);
134     }
135     return value;
136 }
137 
MarkStandardFlag(uint16_t styleItemNameId)138 void AppStyleManager::MarkStandardFlag(uint16_t styleItemNameId)
139 {
140     uint16_t index = styleItemNameId >> 1;
141     if (styleItemNameId & 1) {
142         flags_[index].u8Bits.standardEven_ = 1;
143     } else {
144         flags_[index].u8Bits.standardOdd_ = 1;
145     }
146 }
147 
HandleStaticStyle(const jerry_value_t options,Component & curr)148 void AppStyleManager::HandleStaticStyle(const jerry_value_t options, Component &curr)
149 {
150     jerry_value_t staticStyleValue = GetStyleObjFromOptions(options, staticStyleStrValue_);
151     if (jerry_value_is_undefined(staticStyleValue)) {
152         return;
153     }
154 
155     jerry_value_t propKeys = jerry_get_object_keys(staticStyleValue);
156     uint32_t propKeySize = jerry_get_array_length(propKeys);
157     for (uint32_t index = 0; index < propKeySize; index++) {
158         jerry_value_t propKey = jerry_get_property_by_index(propKeys, index);
159         jerry_value_t propValue = jerry_get_property(staticStyleValue, propKey);
160         AppStyleItem *newStyleItem = AppStyleItem::GenerateFromJSValue(propKey, propValue);
161         if (newStyleItem != nullptr) {
162             curr.ApplyStyle(newStyleItem);
163             MarkStandardFlag(newStyleItem->GetPropNameId());
164             delete newStyleItem;
165             newStyleItem = nullptr;
166         }
167         ReleaseJerryValue(propKey, propValue, VA_ARG_END_FLAG);
168     }
169     ReleaseJerryValue(propKeys, staticStyleValue, VA_ARG_END_FLAG);
170 }
171 
HandleDynamicStyle(const jerry_value_t options,Component & curr)172 void AppStyleManager::HandleDynamicStyle(const jerry_value_t options, Component &curr)
173 {
174     jerry_value_t dynamicStyleValue = GetStyleObjFromOptions(options, dynamicStyleStrValue_);
175     if (jerry_value_is_undefined(dynamicStyleValue)) {
176         return;
177     }
178 
179     jerry_value_t propKeys = jerry_get_object_keys(dynamicStyleValue);
180     uint32_t propKeySize = jerry_get_array_length(propKeys);
181     for (uint32_t index = 0; index < propKeySize; index++) {
182         jerry_value_t propKey = jerry_get_property_by_index(propKeys, index);
183         jerry_value_t propValue = jerry_get_property(dynamicStyleValue, propKey);
184         // parse expression and new watcher for it
185         if (jerry_value_is_function(propValue)) {
186             jerry_value_t expressionValue;
187             if (curr.IsFreeze()) {
188                 expressionValue = JSFunction::Call(propValue, curr.GetViewModel(), nullptr, 0);
189             } else {
190 #if (FEATURE_LAZY_LOADING_MODULE == 1)
191                 expressionValue = CallJSFunction(propValue, curr.GetNativeElement(), nullptr, 0);
192                 JsAppContext *context = JsAppContext::GetInstance();
193                 LazyLoadManager *lazyLoadManager = const_cast<LazyLoadManager *>(context->GetLazyLoadManager());
194                 lazyLoadManager->AddLazyLoadWatcher(curr.GetNativeElement(), propKey, propValue);
195 #else
196                 expressionValue = curr.AddWatcherItem(propKey, propValue);
197 #endif
198             }
199             // the expression's calculating result should be used, not the function value itself
200             AppStyleItem *newStyleItem = AppStyleItem::GenerateFromJSValue(propKey, expressionValue);
201             if (newStyleItem != nullptr) {
202                 curr.ApplyStyle(newStyleItem);
203                 MarkStandardFlag(newStyleItem->GetPropNameId());
204                 delete newStyleItem;
205                 newStyleItem = nullptr;
206             }
207             if (expressionValue != propValue) {
208                 // generally, ParseExpression() will calculate the expression function's value and return
209                 // the result back, so it's need to be released
210                 jerry_release_value(expressionValue);
211             }
212         }
213         ReleaseJerryValue(propKey, propValue, VA_ARG_END_FLAG);
214     }
215     ReleaseJerryValue(propKeys, dynamicStyleValue, VA_ARG_END_FLAG);
216 }
217 
HandleSingleSelector(const jerry_value_t prop,const jerry_value_t type,Component & curr)218 void AppStyleManager::HandleSingleSelector(const jerry_value_t prop, const jerry_value_t type, Component &curr)
219 {
220     if ((styleSheet_ == nullptr) || (jerry_value_is_undefined(prop)) || (jerry_value_is_undefined(type))) {
221         return;
222     }
223 
224     uint16_t strLen = 0;
225     char *valueBuffer = MallocStringOf(prop, &strLen);
226     if (valueBuffer == nullptr) {
227         HILOG_ERROR(HILOG_MODULE_ACE, "convert id value to char failed, will be dropped");
228         return;
229     }
230 
231     do {
232         // malloc successfully but length is not correct, give up
233         if (strLen == 0) {
234             break;
235         }
236 
237         AppStyle *style = nullptr;
238         if (type == idStrValue_) {
239             style = styleSheet_->GetStyleFromIDSelectors(valueBuffer);
240         } else if (type == staticClassStrValue_) {
241             style = styleSheet_->GetStyleFromClassSelectors(valueBuffer);
242         } else {
243             HILOG_WARN(HILOG_MODULE_ACE, "not supported yet");
244         }
245         if (style == nullptr) {
246             // malloc failed or not supported
247             break;
248         }
249         ApplySingleStyle(*style, curr);
250     } while (0);
251 
252     // free value buffer
253     ace_free(valueBuffer);
254     valueBuffer = nullptr;
255 }
256 
HandleSelectors(const jerry_value_t options,const jerry_value_t type,Component & curr)257 void AppStyleManager::HandleSelectors(const jerry_value_t options, const jerry_value_t type, Component &curr)
258 {
259     if (styleSheet_ == nullptr) {
260         return;
261     }
262 
263     jerry_value_t propValue = GetStyleObjFromOptions(options, type);
264     if (jerry_value_is_undefined(propValue)) {
265         return;
266     }
267 
268     if (!jerry_value_is_array(propValue)) {
269         // if is not array, just handle it
270         HandleSingleSelector(propValue, type, curr);
271     } else {
272         // support array
273         uint32_t propCount = jerry_get_array_length(propValue);
274         if (propCount > 0 && propCount < UINT8_MAX) {
275             // do reverse traversal to ensure style override correctly
276             while (propCount > 0) {
277                 jerry_value_t property = jerry_get_property_by_index(propValue, (propCount - 1));
278                 HandleSingleSelector(property, type, curr);
279                 jerry_release_value(property);
280                 propCount--;
281             }
282         } else {
283             HILOG_ERROR(HILOG_MODULE_ACE, "invalid prop count");
284         }
285     }
286     jerry_release_value(propValue);
287 }
288 
HandleIDSelectors(const jerry_value_t options,Component & curr)289 void AppStyleManager::HandleIDSelectors(const jerry_value_t options, Component &curr)
290 {
291     HandleSelectors(options, idStrValue_, curr);
292 }
293 
HandleClassSelectors(const jerry_value_t options,Component & curr)294 void AppStyleManager::HandleClassSelectors(const jerry_value_t options, Component &curr)
295 {
296     HandleSelectors(options, staticClassStrValue_, curr);
297 }
298 
isStandardExist(uint16_t styleItemNameId)299 bool AppStyleManager::isStandardExist(uint16_t styleItemNameId)
300 {
301     uint16_t index = styleItemNameId >> 1;
302     if (styleItemNameId & 1) {
303         return flags_[index].u8Bits.standardEven_;
304     }
305 
306     return  flags_[index].u8Bits.standardOdd_;
307 }
308 
isPseudoExist(uint16_t styleItemNameId,uint16_t type)309 bool AppStyleManager::isPseudoExist(uint16_t styleItemNameId, uint16_t type)
310 {
311     uint16_t index = styleItemNameId >> 1;
312     if (styleItemNameId & 1) {
313         if (type == PSEUDO_CLASS_ACTIVE) {
314             return flags_[index].u8Bits.pseudoActiveEven_;
315         }
316         return flags_[index].u8Bits.pseudoCheckedEven_;
317     }
318     if (type == PSEUDO_CLASS_ACTIVE) {
319         return flags_[index].u8Bits.pseudoActiveOdd_;
320     }
321     return flags_[index].u8Bits.pseudoCheckedOdd_;
322 }
323 
MarkPseudoFlag(uint16_t styleItemNameId,uint16_t type)324 void AppStyleManager::MarkPseudoFlag(uint16_t styleItemNameId, uint16_t type)
325 {
326     uint16_t index = styleItemNameId >> 1;
327     if (styleItemNameId & 1) {
328         if (type == PSEUDO_CLASS_ACTIVE) {
329             flags_[index].u8Bits.pseudoActiveEven_ = 1;
330         } else {
331             flags_[index].u8Bits.pseudoCheckedEven_ = 1;
332         }
333     } else {
334         if (type == PSEUDO_CLASS_ACTIVE) {
335             flags_[index].u8Bits.pseudoActiveOdd_ = 1;
336         } else {
337             flags_[index].u8Bits.pseudoCheckedOdd_ = 1;
338         }
339     }
340 }
341 
ApplySingleStyle(AppStyle & appStyle,Component & curr)342 void AppStyleManager::ApplySingleStyle(AppStyle &appStyle, Component &curr)
343 {
344     // go through id style's all items, apply it to component
345     const AppStyleItem *styleItem = appStyle.GetFirst();
346     while (styleItem != nullptr) {
347         uint16_t styleItemNameId = styleItem->GetPropNameId();
348         uint8_t pseudoClassType = styleItem->GetPseudoClassType();
349         // inline style not exists and pseudo class type is normal, set normal style
350         if (pseudoClassType == 0 && !isStandardExist(styleItemNameId)) {
351             MarkStandardFlag(styleItemNameId);
352             curr.ApplyStyle(styleItem);
353             styleItem = styleItem->GetNext();
354             continue;
355         }
356         // pseudo class type exists and no other same pseudo style has been applied, set pseudo class style
357         if (pseudoClassType != 0 && !isPseudoExist(styleItemNameId, pseudoClassType)) {
358             MarkPseudoFlag(styleItemNameId, pseudoClassType);
359             curr.ApplyStyle(styleItem);
360         }
361         styleItem = styleItem->GetNext();
362     }
363 }
364 } // namespace ACELite
365 } // namespace OHOS
366