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 Font font;
83 JSText::GetFontInfo(info, font);
84 SpanModel::GetInstance()->SetFont(font);
85 }
86
SetFontSize(const JSCallbackInfo & info)87 void JSSpan::SetFontSize(const JSCallbackInfo& info)
88 {
89 if (info.Length() < 1) {
90 return;
91 }
92 CalcDimension fontSize;
93 if (!ParseJsDimensionFpNG(info[0], fontSize, false) || fontSize.IsNegative()) {
94 auto pipelineContext = PipelineBase::GetCurrentContext();
95 CHECK_NULL_VOID(pipelineContext);
96 auto theme = pipelineContext->GetTheme<TextTheme>();
97 CHECK_NULL_VOID(theme);
98 fontSize = theme->GetTextStyle().GetFontSize();
99 SpanModel::GetInstance()->SetFontSize(fontSize);
100 return;
101 }
102
103 SpanModel::GetInstance()->SetFontSize(fontSize);
104 }
105
SetFontWeight(const std::string & value)106 void JSSpan::SetFontWeight(const std::string& value)
107 {
108 SpanModel::GetInstance()->SetFontWeight(ConvertStrToFontWeight(value));
109 }
110
SetTextColor(const JSCallbackInfo & info)111 void JSSpan::SetTextColor(const JSCallbackInfo& info)
112 {
113 Color textColor;
114 if (!ParseJsColor(info[0], textColor)) {
115 auto pipelineContext = PipelineBase::GetCurrentContext();
116 CHECK_NULL_VOID(pipelineContext);
117 auto theme = pipelineContext->GetTheme<TextTheme>();
118 CHECK_NULL_VOID(theme);
119 textColor = theme->GetTextStyle().GetTextColor();
120 }
121 SpanModel::GetInstance()->SetTextColor(textColor);
122 }
123
SetFontStyle(int32_t value)124 void JSSpan::SetFontStyle(int32_t value)
125 {
126 if (value >= 0 && value < static_cast<int32_t>(FONT_STYLES.size())) {
127 auto style = FONT_STYLES[value];
128 SpanModel::GetInstance()->SetItalicFontStyle(style);
129 }
130 }
131
SetFontFamily(const JSCallbackInfo & info)132 void JSSpan::SetFontFamily(const JSCallbackInfo& info)
133 {
134 if (info.Length() < 1) {
135 return;
136 }
137 std::vector<std::string> fontFamilies;
138 if (!ParseJsFontFamilies(info[0], fontFamilies)) {
139 return;
140 }
141 SpanModel::GetInstance()->SetFontFamily(fontFamilies);
142 }
143
SetLetterSpacing(const JSCallbackInfo & info)144 void JSSpan::SetLetterSpacing(const JSCallbackInfo& info)
145 {
146 if (info.Length() < 1) {
147 return;
148 }
149 CalcDimension value;
150 if (!ParseJsDimensionFpNG(info[0], value, false)) {
151 value.Reset();
152 SpanModel::GetInstance()->SetLetterSpacing(value);
153 return;
154 }
155 SpanModel::GetInstance()->SetLetterSpacing(value);
156 }
157
SetBaselineOffset(const JSCallbackInfo & info)158 void JSSpan::SetBaselineOffset(const JSCallbackInfo& info)
159 {
160 if (info.Length() < 1) {
161 return;
162 }
163 NG::CalcLength value;
164 if (ConvertFromJSValueNG(info[0], value) &&
165 value.GetDimensionContainsNegative().Unit() != DimensionUnit::PERCENT) {
166 SpanModel::GetInstance()->SetBaselineOffset(value.GetDimensionContainsNegative());
167 return;
168 }
169 value.Reset();
170 SpanModel::GetInstance()->SetBaselineOffset(value.GetDimensionContainsNegative());
171 }
172
SetTextCase(int32_t value)173 void JSSpan::SetTextCase(int32_t value)
174 {
175 if (value >= 0 && value < static_cast<int32_t>(TEXT_CASES.size())) {
176 auto textCase = TEXT_CASES[value];
177 SpanModel::GetInstance()->SetTextCase(textCase);
178 }
179 }
180
SetDecoration(const JSCallbackInfo & info)181 void JSSpan::SetDecoration(const JSCallbackInfo& info)
182 {
183 if (info[0]->IsUndefined()) {
184 SpanModel::GetInstance()->SetTextDecoration(TextDecoration::NONE);
185 return;
186 }
187 if (!info[0]->IsObject()) {
188 return;
189 }
190 JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
191 JSRef<JSVal> typeValue = obj->GetProperty("type");
192 JSRef<JSVal> colorValue = obj->GetProperty("color");
193 JSRef<JSVal> styleValue = obj->GetProperty("style");
194
195 std::optional<TextDecoration> textDecoration;
196 if (typeValue->IsNumber()) {
197 textDecoration = static_cast<TextDecoration>(typeValue->ToNumber<int32_t>());
198 } else {
199 auto theme = GetTheme<TextTheme>();
200 CHECK_NULL_VOID(theme);
201 textDecoration = theme->GetTextStyle().GetTextDecoration();
202 }
203 std::optional<TextDecorationStyle> textDecorationStyle;
204 if (styleValue->IsNumber()) {
205 textDecorationStyle = static_cast<TextDecorationStyle>(styleValue->ToNumber<int32_t>());
206 } else {
207 textDecorationStyle = DEFAULT_TEXT_DECORATION_STYLE;
208 }
209 std::optional<Color> colorVal;
210 Color result;
211 if (ParseJsColor(colorValue, result)) {
212 colorVal = result;
213 } else {
214 auto theme = GetTheme<TextTheme>();
215 CHECK_NULL_VOID(theme);
216 if (SystemProperties::GetColorMode() == ColorMode::DARK) {
217 colorVal = theme->GetTextStyle().GetTextColor();
218 } else {
219 colorVal = Color::BLACK;
220 }
221 }
222 SpanModel::GetInstance()->SetTextDecoration(textDecoration.value());
223 SpanModel::GetInstance()->SetTextDecorationColor(colorVal.value());
224 if (textDecorationStyle) {
225 SpanModel::GetInstance()->SetTextDecorationStyle(textDecorationStyle.value());
226 }
227 }
228
JsOnClick(const JSCallbackInfo & info)229 void JSSpan::JsOnClick(const JSCallbackInfo& info)
230 {
231 if (Container::IsCurrentUseNewPipeline()) {
232 if (info[0]->IsUndefined() && IsDisableEventVersion()) {
233 SpanModel::GetInstance()->ClearOnClick();
234 return;
235 }
236 if (!info[0]->IsFunction()) {
237 return;
238 }
239 auto jsOnClickFunc = AceType::MakeRefPtr<JsClickFunction>(JSRef<JSFunc>::Cast(info[0]));
240 auto targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
241 auto onClick = [execCtx = info.GetExecutionContext(), func = jsOnClickFunc, node = targetNode](
242 BaseEventInfo* info) {
243 auto* clickInfo = TypeInfoHelper::DynamicCast<GestureEvent>(info);
244 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
245 ACE_SCORING_EVENT("onClick");
246 PipelineContext::SetCallBackNode(node);
247 func->Execute(*clickInfo);
248 #if !defined(PREVIEW) && defined(OHOS_PLATFORM)
249 JSInteractableView::ReportClickEvent(node);
250 #endif
251 };
252 SpanModel::GetInstance()->SetOnClick(std::move(onClick));
253 return;
254 }
255 #ifndef NG_BUILD
256 if (info[0]->IsFunction()) {
257 auto inspector = ViewStackProcessor::GetInstance()->GetInspectorComposedComponent();
258 CHECK_NULL_VOID(inspector);
259 auto impl = inspector->GetInspectorFunctionImpl();
260 RefPtr<JsClickFunction> jsOnClickFunc = AceType::MakeRefPtr<JsClickFunction>(JSRef<JSFunc>::Cast(info[0]));
261 auto targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
262 auto clickFunc = [execCtx = info.GetExecutionContext(), func = std::move(jsOnClickFunc), impl,
263 node = targetNode](const BaseEventInfo* info) {
264 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
265 const auto* clickInfo = TypeInfoHelper::DynamicCast<ClickInfo>(info);
266 auto newInfo = *clickInfo;
267 if (impl) {
268 impl->UpdateEventInfo(newInfo);
269 }
270 ACE_SCORING_EVENT("Span.onClick");
271 PipelineContext::SetCallBackNode(node);
272 func->Execute(newInfo);
273 };
274 SpanModel::GetInstance()->SetOnClick(std::move(clickFunc));
275 }
276 #endif
277 }
278
JsRemoteMessage(const JSCallbackInfo & info)279 void JSSpan::JsRemoteMessage(const JSCallbackInfo& info)
280 {
281 #ifndef NG_BUILD
282 RemoteCallback remoteCallback;
283 JSInteractableView::JsRemoteMessage(info, remoteCallback);
284 EventMarker remoteMessageEventId(std::move(remoteCallback));
285 auto* stack = ViewStackProcessor::GetInstance();
286 auto textSpanComponent = AceType::DynamicCast<TextSpanComponent>(stack->GetMainComponent());
287 textSpanComponent->SetRemoteMessageEventId(remoteMessageEventId);
288 #endif
289 }
290
SetLineHeight(const JSCallbackInfo & info)291 void JSSpan::SetLineHeight(const JSCallbackInfo& info)
292 {
293 CalcDimension value;
294 if (!ParseJsDimensionFpNG(info[0], value)) {
295 value.Reset();
296 SpanModel::GetInstance()->SetLineHeight(value);
297 return;
298 }
299 if (value.IsNegative()) {
300 value.Reset();
301 }
302 SpanModel::GetInstance()->SetLineHeight(value);
303 }
304
SetTextShadow(const JSCallbackInfo & info)305 void JSSpan::SetTextShadow(const JSCallbackInfo& info)
306 {
307 if (info.Length() < 1) {
308 return;
309 }
310 std::vector<Shadow> shadows;
311 ParseTextShadowFromShadowObject(info[0], shadows);
312 SpanModel::GetInstance()->SetTextShadow(shadows);
313 }
314
SetAccessibilityText(const JSCallbackInfo & info)315 void JSSpan::SetAccessibilityText(const JSCallbackInfo& info)
316 {
317 std::string text;
318 if ((info.Length() > 0) && info[0]->IsString()) {
319 text = info[0]->ToString();
320 }
321 SpanModel::GetInstance()->SetAccessibilityText(text);
322 }
323
SetAccessibilityDescription(const JSCallbackInfo & info)324 void JSSpan::SetAccessibilityDescription(const JSCallbackInfo& info)
325 {
326 std::string description;
327 if ((info.Length() > 0) && info[0]->IsString()) {
328 description = info[0]->ToString();
329 }
330 SpanModel::GetInstance()->SetAccessibilityDescription(description);
331 }
332
SetAccessibilityLevel(const JSCallbackInfo & info)333 void JSSpan::SetAccessibilityLevel(const JSCallbackInfo& info)
334 {
335 std::string level;
336 if ((info.Length() > 0) && info[0]->IsString()) {
337 level = info[0]->ToString();
338 }
339 SpanModel::GetInstance()->SetAccessibilityImportance(level);
340 }
341
JSBind(BindingTarget globalObj)342 void JSSpan::JSBind(BindingTarget globalObj)
343 {
344 JSClass<JSSpan>::Declare("Span");
345 MethodOptions opt = MethodOptions::NONE;
346 JSClass<JSSpan>::StaticMethod("create", &JSSpan::Create, opt);
347 JSClass<JSSpan>::StaticMethod("font", &JSSpan::SetFont, opt);
348 JSClass<JSSpan>::StaticMethod("fontColor", &JSSpan::SetTextColor, opt);
349 JSClass<JSSpan>::StaticMethod("fontSize", &JSSpan::SetFontSize, opt);
350 JSClass<JSSpan>::StaticMethod("fontWeight", &JSSpan::SetFontWeight, opt);
351 JSClass<JSSpan>::StaticMethod("fontStyle", &JSSpan::SetFontStyle, opt);
352 JSClass<JSSpan>::StaticMethod("fontFamily", &JSSpan::SetFontFamily, opt);
353 JSClass<JSSpan>::StaticMethod("letterSpacing", &JSSpan::SetLetterSpacing, opt);
354 JSClass<JSSpan>::StaticMethod("baselineOffset", &JSSpan::SetBaselineOffset, opt);
355 JSClass<JSSpan>::StaticMethod("textCase", &JSSpan::SetTextCase, opt);
356 JSClass<JSSpan>::StaticMethod("textShadow", &JSSpan::SetTextShadow, opt);
357 JSClass<JSSpan>::StaticMethod("decoration", &JSSpan::SetDecoration);
358 JSClass<JSSpan>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
359 JSClass<JSSpan>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
360 JSClass<JSSpan>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
361 JSClass<JSSpan>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
362 JSClass<JSSpan>::StaticMethod("remoteMessage", &JSSpan::JsRemoteMessage);
363 JSClass<JSSpan>::StaticMethod("onClick", &JSSpan::JsOnClick);
364 JSClass<JSSpan>::StaticMethod("lineHeight", &JSSpan::SetLineHeight, opt);
365 JSClass<JSSpan>::StaticMethod("textBackgroundStyle", &JSContainerSpan::SetTextBackgroundStyle, opt);
366 JSClass<JSSpan>::StaticMethod("accessibilityText", &JSSpan::SetAccessibilityText, opt);
367 JSClass<JSSpan>::StaticMethod("accessibilityDescription", &JSSpan::SetAccessibilityDescription, opt);
368 JSClass<JSSpan>::StaticMethod("accessibilityLevel", &JSSpan::SetAccessibilityLevel, opt);
369 JSClass<JSSpan>::InheritAndBind<JSContainerBase>(globalObj);
370 }
371
Create(const JSCallbackInfo & info)372 void JSSpan::Create(const JSCallbackInfo& info)
373 {
374 std::string label;
375 if (info.Length() > 0) {
376 ParseJsString(info[0], label);
377 }
378 SpanModel::GetInstance()->Create(label);
379 }
380
381 } // namespace OHOS::Ace::Framework
382