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