1 /*
2 * Copyright (c) 2021-2023 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_span.h"
17 #include "frameworks/bridge/declarative_frontend/jsview/js_container_span.h"
18
19 #include <optional>
20 #include <sstream>
21 #include <string>
22 #include <vector>
23 #if !defined(PREVIEW) && defined(OHOS_PLATFORM)
24 #include "interfaces/inner_api/ui_session/ui_session_manager.h"
25 #endif
26
27 #include "base/geometry/dimension.h"
28 #include "base/log/ace_scoring_log.h"
29 #include "base/log/ace_trace.h"
30 #include "base/utils/utils.h"
31 #include "bridge/common/utils/utils.h"
32 #include "bridge/declarative_frontend/engine/functions/js_click_function.h"
33 #include "bridge/declarative_frontend/jsview/js_interactable_view.h"
34 #include "bridge/declarative_frontend/jsview/js_utils.h"
35 #include "bridge/declarative_frontend/jsview/js_view_abstract.h"
36 #include "bridge/declarative_frontend/jsview/models/span_model_impl.h"
37 #include "bridge/declarative_frontend/jsview/models/text_model_impl.h"
38 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
39 #ifndef NG_BUILD
40 #include "bridge/declarative_frontend/view_stack_processor.h"
41 #endif
42 #include "bridge/declarative_frontend/jsview/js_text.h"
43 #include "core/common/container.h"
44 #include "core/components_ng/pattern/text/span_model.h"
45 #include "core/components_ng/pattern/text/span_model_ng.h"
46 #include "core/components_ng/pattern/text/text_model.h"
47
48 namespace OHOS::Ace {
49
50 std::unique_ptr<SpanModel> SpanModel::instance_ = nullptr;
51 std::mutex SpanModel::mutex_;
52
GetInstance()53 SpanModel* SpanModel::GetInstance()
54 {
55 #ifdef NG_BUILD
56 static NG::SpanModelNG instance;
57 return &instance;
58 #else
59 if (Container::IsCurrentUseNewPipeline()) {
60 static NG::SpanModelNG instance;
61 return &instance;
62 } else {
63 static Framework::SpanModelImpl instance;
64 return &instance;
65 }
66 #endif
67 }
68
69 } // namespace OHOS::Ace
70
71 namespace OHOS::Ace::Framework {
72 namespace {
73
74 const std::vector<FontStyle> FONT_STYLES = { FontStyle::NORMAL, FontStyle::ITALIC };
75 const std::vector<TextCase> TEXT_CASES = { TextCase::NORMAL, TextCase::LOWERCASE, TextCase::UPPERCASE };
76 constexpr TextDecorationStyle DEFAULT_TEXT_DECORATION_STYLE = TextDecorationStyle::SOLID;
77
78 } // namespace
79
SetFont(const JSCallbackInfo& info)80 void JSSpan::SetFont(const JSCallbackInfo& info)
81 {
82 if (info.Length() < 1) {
83 return;
84 }
85 auto infoZero = info[0];
86 if (infoZero->IsUndefined() || infoZero->IsNull()) {
87 SpanModel::GetInstance()->ResetFont();
88 return;
89 }
90 Font font;
91 JSText::GetFontInfo(info, font);
92 SpanModel::GetInstance()->SetFont(font);
93 }
94
SetFontSize(const JSCallbackInfo& info)95 void JSSpan::SetFontSize(const JSCallbackInfo& info)
96 {
97 if (info.Length() < 1) {
98 return;
99 }
100 CalcDimension fontSize;
101 if (!ParseJsDimensionFpNG(info[0], fontSize, false) || fontSize.IsNegative()) {
102 SpanModel::GetInstance()->ResetFontSize();
103 return;
104 }
105 SpanModel::GetInstance()->SetFontSize(fontSize);
106 }
107
SetFontWeight(const std::string& value)108 void JSSpan::SetFontWeight(const std::string& value)
109 {
110 SpanModel::GetInstance()->SetFontWeight(ConvertStrToFontWeight(value));
111 }
112
SetTextColor(const JSCallbackInfo& info)113 void JSSpan::SetTextColor(const JSCallbackInfo& info)
114 {
115 Color textColor;
116 auto infoZero = info[0];
117 if (infoZero->IsUndefined() || infoZero->IsNull()) {
118 SpanModel::GetInstance()->ResetTextColor();
119 return;
120 }
121 if (!ParseJsColor(infoZero, textColor)) {
122 SpanModel::GetInstance()->ResetTextColor();
123 return;
124 }
125 SpanModel::GetInstance()->SetTextColor(textColor);
126 }
127
SetFontStyle(int32_t value)128 void JSSpan::SetFontStyle(int32_t value)
129 {
130 if (value >= 0 && value < static_cast<int32_t>(FONT_STYLES.size())) {
131 auto style = FONT_STYLES[value];
132 SpanModel::GetInstance()->SetItalicFontStyle(style);
133 } else {
134 SpanModel::GetInstance()->ResetItalicFontStyle();
135 }
136 }
137
SetFontFamily(const JSCallbackInfo& info)138 void JSSpan::SetFontFamily(const JSCallbackInfo& info)
139 {
140 if (info.Length() < 1) {
141 return;
142 }
143 std::vector<std::string> fontFamilies;
144 if (!ParseJsFontFamilies(info[0], fontFamilies)) {
145 SpanModel::GetInstance()->ResetFontFamily();
146 return;
147 }
148 SpanModel::GetInstance()->SetFontFamily(fontFamilies);
149 }
150
SetLetterSpacing(const JSCallbackInfo& info)151 void JSSpan::SetLetterSpacing(const JSCallbackInfo& info)
152 {
153 if (info.Length() < 1) {
154 return;
155 }
156 CalcDimension value;
157 if (!ParseJsDimensionFpNG(info[0], value, false)) {
158 value.Reset();
159 SpanModel::GetInstance()->SetLetterSpacing(value);
160 return;
161 }
162 SpanModel::GetInstance()->SetLetterSpacing(value);
163 }
164
SetBaselineOffset(const JSCallbackInfo& info)165 void JSSpan::SetBaselineOffset(const JSCallbackInfo& info)
166 {
167 if (info.Length() < 1) {
168 return;
169 }
170 NG::CalcLength value;
171 if (ConvertFromJSValueNG(info[0], value) &&
172 value.GetDimensionContainsNegative().Unit() != DimensionUnit::PERCENT) {
173 SpanModel::GetInstance()->SetBaselineOffset(value.GetDimensionContainsNegative());
174 return;
175 }
176 value.Reset();
177 SpanModel::GetInstance()->SetBaselineOffset(value.GetDimensionContainsNegative());
178 }
179
SetTextCase(int32_t value)180 void JSSpan::SetTextCase(int32_t value)
181 {
182 if (value >= 0 && value < static_cast<int32_t>(TEXT_CASES.size())) {
183 auto textCase = TEXT_CASES[value];
184 SpanModel::GetInstance()->SetTextCase(textCase);
185 }
186 }
187
SetDecoration(const JSCallbackInfo& info)188 void JSSpan::SetDecoration(const JSCallbackInfo& info)
189 {
190 if (info[0]->IsUndefined()) {
191 SpanModel::GetInstance()->SetTextDecoration(TextDecoration::NONE);
192 return;
193 }
194 if (!info[0]->IsObject()) {
195 return;
196 }
197 JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
198 JSRef<JSVal> typeValue = obj->GetProperty("type");
199 JSRef<JSVal> colorValue = obj->GetProperty("color");
200 JSRef<JSVal> styleValue = obj->GetProperty("style");
201
202 std::optional<TextDecoration> textDecoration;
203 if (typeValue->IsNumber()) {
204 textDecoration = static_cast<TextDecoration>(typeValue->ToNumber<int32_t>());
205 } else {
206 auto theme = GetTheme<TextTheme>();
207 CHECK_NULL_VOID(theme);
208 textDecoration = theme->GetTextStyle().GetTextDecoration();
209 }
210 std::optional<TextDecorationStyle> textDecorationStyle;
211 if (styleValue->IsNumber()) {
212 textDecorationStyle = static_cast<TextDecorationStyle>(styleValue->ToNumber<int32_t>());
213 } else {
214 textDecorationStyle = DEFAULT_TEXT_DECORATION_STYLE;
215 }
216 std::optional<Color> colorVal;
217 Color result;
218 if (ParseJsColor(colorValue, result)) {
219 colorVal = result;
220 } else {
221 auto theme = GetTheme<TextTheme>();
222 CHECK_NULL_VOID(theme);
223 if (SystemProperties::GetColorMode() == ColorMode::DARK) {
224 colorVal = theme->GetTextStyle().GetTextColor();
225 } else {
226 colorVal = Color::BLACK;
227 }
228 }
229 SpanModel::GetInstance()->SetTextDecoration(textDecoration.value());
230 SpanModel::GetInstance()->SetTextDecorationColor(colorVal.value());
231 if (textDecorationStyle) {
232 SpanModel::GetInstance()->SetTextDecorationStyle(textDecorationStyle.value());
233 }
234 }
235
JsOnClick(const JSCallbackInfo& info)236 void JSSpan::JsOnClick(const JSCallbackInfo& info)
237 {
238 if (Container::IsCurrentUseNewPipeline()) {
239 if (info[0]->IsUndefined() && IsDisableEventVersion()) {
240 SpanModel::GetInstance()->ClearOnClick();
241 return;
242 }
243 if (!info[0]->IsFunction()) {
244 return;
245 }
246 auto jsOnClickFunc = AceType::MakeRefPtr<JsClickFunction>(JSRef<JSFunc>::Cast(info[0]));
247 auto targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
248 auto onClick = [execCtx = info.GetExecutionContext(), func = jsOnClickFunc, node = targetNode](
249 BaseEventInfo* info) {
250 auto* clickInfo = TypeInfoHelper::DynamicCast<GestureEvent>(info);
251 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
252 ACE_SCORING_EVENT("onClick");
253 PipelineContext::SetCallBackNode(node);
254 func->Execute(*clickInfo);
255 #if !defined(PREVIEW) && defined(OHOS_PLATFORM)
256 JSInteractableView::ReportClickEvent(node);
257 #endif
258 };
259 SpanModel::GetInstance()->SetOnClick(std::move(onClick));
260 return;
261 }
262 #ifndef NG_BUILD
263 if (info[0]->IsFunction()) {
264 auto inspector = ViewStackProcessor::GetInstance()->GetInspectorComposedComponent();
265 CHECK_NULL_VOID(inspector);
266 auto impl = inspector->GetInspectorFunctionImpl();
267 RefPtr<JsClickFunction> jsOnClickFunc = AceType::MakeRefPtr<JsClickFunction>(JSRef<JSFunc>::Cast(info[0]));
268 auto targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
269 auto clickFunc = [execCtx = info.GetExecutionContext(), func = std::move(jsOnClickFunc), impl,
270 node = targetNode](const BaseEventInfo* info) {
271 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
272 const auto* clickInfo = TypeInfoHelper::DynamicCast<ClickInfo>(info);
273 auto newInfo = *clickInfo;
274 if (impl) {
275 impl->UpdateEventInfo(newInfo);
276 }
277 ACE_SCORING_EVENT("Span.onClick");
278 PipelineContext::SetCallBackNode(node);
279 func->Execute(newInfo);
280 };
281 SpanModel::GetInstance()->SetOnClick(std::move(clickFunc));
282 }
283 #endif
284 }
285
JsRemoteMessage(const JSCallbackInfo& info)286 void JSSpan::JsRemoteMessage(const JSCallbackInfo& info)
287 {
288 #ifndef NG_BUILD
289 RemoteCallback remoteCallback;
290 JSInteractableView::JsRemoteMessage(info, remoteCallback);
291 EventMarker remoteMessageEventId(std::move(remoteCallback));
292 auto* stack = ViewStackProcessor::GetInstance();
293 auto textSpanComponent = AceType::DynamicCast<TextSpanComponent>(stack->GetMainComponent());
294 textSpanComponent->SetRemoteMessageEventId(remoteMessageEventId);
295 #endif
296 }
297
SetLineHeight(const JSCallbackInfo& info)298 void JSSpan::SetLineHeight(const JSCallbackInfo& info)
299 {
300 CalcDimension value;
301 if (!ParseJsDimensionFpNG(info[0], value)) {
302 value.Reset();
303 SpanModel::GetInstance()->SetLineHeight(value);
304 return;
305 }
306 if (value.IsNegative()) {
307 value.Reset();
308 }
309 SpanModel::GetInstance()->SetLineHeight(value);
310 }
311
SetTextShadow(const JSCallbackInfo& info)312 void JSSpan::SetTextShadow(const JSCallbackInfo& info)
313 {
314 if (info.Length() < 1) {
315 return;
316 }
317 std::vector<Shadow> shadows;
318 ParseTextShadowFromShadowObject(info[0], shadows);
319 SpanModel::GetInstance()->SetTextShadow(shadows);
320 }
321
322
SetAccessibilityText(const JSCallbackInfo& info)323 void JSSpan::SetAccessibilityText(const JSCallbackInfo& info)
324 {
325 std::string text;
326 if ((info.Length() > 0) && info[0]->IsString()) {
327 text = info[0]->ToString();
328 }
329 SpanModel::GetInstance()->SetAccessibilityText(text);
330 }
331
SetAccessibilityDescription(const JSCallbackInfo& info)332 void JSSpan::SetAccessibilityDescription(const JSCallbackInfo& info)
333 {
334 std::string description;
335 if ((info.Length() > 0) && info[0]->IsString()) {
336 description = info[0]->ToString();
337 }
338 SpanModel::GetInstance()->SetAccessibilityDescription(description);
339 }
340
SetAccessibilityLevel(const JSCallbackInfo& info)341 void JSSpan::SetAccessibilityLevel(const JSCallbackInfo& info)
342 {
343 std::string level;
344 if ((info.Length() > 0) && info[0]->IsString()) {
345 level = info[0]->ToString();
346 }
347 SpanModel::GetInstance()->SetAccessibilityImportance(level);
348 }
349
JSBind(BindingTarget globalObj)350 void JSSpan::JSBind(BindingTarget globalObj)
351 {
352 JSClass<JSSpan>::Declare("Span");
353 MethodOptions opt = MethodOptions::NONE;
354 JSClass<JSSpan>::StaticMethod("create", &JSSpan::Create, opt);
355 JSClass<JSSpan>::StaticMethod("font", &JSSpan::SetFont, opt);
356 JSClass<JSSpan>::StaticMethod("fontColor", &JSSpan::SetTextColor, opt);
357 JSClass<JSSpan>::StaticMethod("fontSize", &JSSpan::SetFontSize, opt);
358 JSClass<JSSpan>::StaticMethod("fontWeight", &JSSpan::SetFontWeight, opt);
359 JSClass<JSSpan>::StaticMethod("fontStyle", &JSSpan::SetFontStyle, opt);
360 JSClass<JSSpan>::StaticMethod("fontFamily", &JSSpan::SetFontFamily, opt);
361 JSClass<JSSpan>::StaticMethod("letterSpacing", &JSSpan::SetLetterSpacing, opt);
362 JSClass<JSSpan>::StaticMethod("baselineOffset", &JSSpan::SetBaselineOffset, opt);
363 JSClass<JSSpan>::StaticMethod("textCase", &JSSpan::SetTextCase, opt);
364 JSClass<JSSpan>::StaticMethod("textShadow", &JSSpan::SetTextShadow, opt);
365 JSClass<JSSpan>::StaticMethod("decoration", &JSSpan::SetDecoration);
366 JSClass<JSSpan>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
367 JSClass<JSSpan>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
368 JSClass<JSSpan>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
369 JSClass<JSSpan>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
370 JSClass<JSSpan>::StaticMethod("remoteMessage", &JSSpan::JsRemoteMessage);
371 JSClass<JSSpan>::StaticMethod("onClick", &JSSpan::JsOnClick);
372 JSClass<JSSpan>::StaticMethod("lineHeight", &JSSpan::SetLineHeight, opt);
373 JSClass<JSSpan>::StaticMethod("textBackgroundStyle", &JSContainerSpan::SetTextBackgroundStyle, opt);
374 JSClass<JSSpan>::StaticMethod("accessibilityText", &JSSpan::SetAccessibilityText, opt);
375 JSClass<JSSpan>::StaticMethod("accessibilityDescription", &JSSpan::SetAccessibilityDescription, opt);
376 JSClass<JSSpan>::StaticMethod("accessibilityLevel", &JSSpan::SetAccessibilityLevel, opt);
377 JSClass<JSSpan>::InheritAndBind<JSContainerBase>(globalObj);
378 }
379
Create(const JSCallbackInfo& info)380 void JSSpan::Create(const JSCallbackInfo& info)
381 {
382 std::string label;
383 if (info.Length() > 0) {
384 ParseJsString(info[0], label);
385 }
386 SpanModel::GetInstance()->Create(label);
387 }
388
389 } // namespace OHOS::Ace::Framework
390