1 /*
2 * Copyright (c) 2024 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/style_string/js_span_string.h"
17
18 #include <unordered_set>
19 #include "securec.h"
20
21 #include "base/utils/utils.h"
22 #include "core/common/ace_engine.h"
23 #include "core/common/container.h"
24 #include "core/common/container_scope.h"
25 #include "core/components_ng/pattern/text/span/mutable_span_string.h"
26 #include "core/components_ng/pattern/text/span/span_object.h"
27 #include "core/text/html_utils.h"
28 #include "frameworks/bridge/common/utils/engine_helper.h"
29 #include "frameworks/bridge/common/utils/utils.h"
30 #include "frameworks/bridge/declarative_frontend/engine/js_converter.h"
31 #include "frameworks/bridge/declarative_frontend/engine/functions/js_function.h"
32 #include "frameworks/bridge/declarative_frontend/jsview/js_image.h"
33 #include "frameworks/bridge/declarative_frontend/jsview/js_view_abstract.h"
34 #include "frameworks/bridge/declarative_frontend/style_string/js_span_object.h"
35
36 namespace OHOS::Ace::Framework {
37 namespace {
38 struct HtmlConverterAsyncCtx {
39 napi_env env = nullptr;
40 napi_deferred deferred = nullptr;
41 int32_t errCode = -1;
42 int32_t instanceId = -1;
43 };
44 struct AsyncContext {
45 napi_env env = nullptr;
46 napi_deferred deferred = nullptr;
47 napi_async_work asyncWork;
48 std::vector<uint8_t> buffer;
49 RefPtr<SpanString> spanString;
50 int32_t status = -1;
51 };
52
53 std::unordered_map<int32_t, std::string> ASYNC_ERROR_MAP = {
54 { ERROR_CODE_FROM_HTML_CONVERT_ERROR, "Convert error." },
55 { ERROR_CODE_STYLED_STRING_CONVERT_ERROR, "Styled string decode error."},
56 { ERROR_CODE_PARAM_INVALID, "Parameter error. Possible causes: 1. Mandatory parameters are left unspecified;"
57 "2. Incorrect parameter types; 3. Parameter verification failed." }
58 };
59
CreateErrorValue(napi_env env,int32_t errCode,const std::string & errMsg="")60 napi_value CreateErrorValue(napi_env env, int32_t errCode, const std::string& errMsg = "")
61 {
62 napi_value code = nullptr;
63 std::string codeStr = std::to_string(errCode);
64 napi_create_string_utf8(env, codeStr.c_str(), codeStr.length(), &code);
65 napi_value msg = nullptr;
66 napi_create_string_utf8(env, errMsg.c_str(), errMsg.length(), &msg);
67 napi_value error = nullptr;
68 napi_create_error(env, code, msg, &error);
69 return error;
70 }
71
ProcessPromiseCallback(std::shared_ptr<HtmlConverterAsyncCtx> asyncContext,int32_t callbackCode,napi_value spanStr=nullptr)72 void ProcessPromiseCallback(std::shared_ptr<HtmlConverterAsyncCtx> asyncContext,
73 int32_t callbackCode, napi_value spanStr = nullptr)
74 {
75 CHECK_NULL_VOID(asyncContext);
76 CHECK_NULL_VOID(asyncContext->env);
77 CHECK_NULL_VOID(asyncContext->deferred);
78 napi_handle_scope scope = nullptr;
79 auto status = napi_open_handle_scope(asyncContext->env, &scope);
80 if (status != napi_ok) {
81 return;
82 }
83 CHECK_NULL_VOID(scope);
84 if (callbackCode == ERROR_CODE_NO_ERROR) {
85 napi_resolve_deferred(asyncContext->env, asyncContext->deferred, spanStr);
86 } else {
87 napi_value error = CreateErrorValue(asyncContext->env, callbackCode, ASYNC_ERROR_MAP[callbackCode]);
88 napi_reject_deferred(asyncContext->env, asyncContext->deferred, error);
89 }
90 napi_close_handle_scope(asyncContext->env, scope);
91 }
92
ReturnPromise(const JSCallbackInfo & info,int32_t errCode)93 void ReturnPromise(const JSCallbackInfo& info, int32_t errCode)
94 {
95 auto engine = EngineHelper::GetCurrentEngine();
96 CHECK_NULL_VOID(engine);
97 NativeEngine* nativeEngine = engine->GetNativeEngine();
98 auto env = reinterpret_cast<napi_env>(nativeEngine);
99 napi_deferred deferred = nullptr;
100 napi_value promise = nullptr;
101 napi_create_promise(env, &deferred, &promise);
102
103 if (errCode != ERROR_CODE_NO_ERROR) {
104 napi_value result = CreateErrorValue(env, errCode, ASYNC_ERROR_MAP[errCode]);
105 napi_reject_deferred(env, deferred, result);
106 } else {
107 napi_value result = nullptr;
108 napi_get_undefined(env, &result);
109 napi_resolve_deferred(env, deferred, result);
110 }
111 CHECK_NULL_VOID(promise);
112 auto jsPromise = JsConverter::ConvertNapiValueToJsVal(promise);
113 if (!jsPromise->IsObject()) {
114 TAG_LOGE(AceLogTag::ACE_TEXT, "Return promise failed.");
115 return;
116 }
117 info.SetReturnValue(JSRef<JSObject>::Cast(jsPromise));
118 }
119
120 static std::atomic<int32_t> gestureStyleStoreIndex_;
121 static std::atomic<int32_t> spanStringStoreIndex_;
122 };
123
124 const std::unordered_set<SpanType> types = { SpanType::Font, SpanType::Gesture, SpanType::BaselineOffset,
125 SpanType::Decoration, SpanType::LetterSpacing, SpanType::TextShadow, SpanType::LineHeight, SpanType::Image,
126 SpanType::CustomSpan, SpanType::ParagraphStyle, SpanType::ExtSpan };
127
128 const std::unordered_map<SpanType, std::function<JSRef<JSObject>(const RefPtr<SpanBase>&)>> spanCreators = {
129 { SpanType::Font, JSSpanString::CreateJsFontSpan }, { SpanType::Decoration, JSSpanString::CreateJsDecorationSpan },
130 { SpanType::BaselineOffset, JSSpanString::CreateJsBaselineOffsetSpan },
131 { SpanType::LetterSpacing, JSSpanString::CreateJsLetterSpacingSpan },
132 { SpanType::Gesture, JSSpanString::CreateJsGestureSpan },
133 { SpanType::TextShadow, JSSpanString::CreateJsTextShadowSpan },
134 { SpanType::LineHeight, JSSpanString::CreateJsLineHeightSpan },
135 { SpanType::Image, JSSpanString::CreateJsImageSpan },
136 { SpanType::ParagraphStyle, JSSpanString::CreateJsParagraphStyleSpan },
137 };
138
Constructor(const JSCallbackInfo & args)139 void JSSpanString::Constructor(const JSCallbackInfo& args)
140 {
141 auto jsSpanString = Referenced::MakeRefPtr<JSSpanString>();
142 jsSpanString->IncRefCount();
143 std::string data;
144 RefPtr<SpanString> spanString;
145 if (args.Length() == 0) {
146 spanString = AceType::MakeRefPtr<SpanString>(data);
147 } else {
148 if (args[0]->IsString()) {
149 JSViewAbstract::ParseJsString(args[0], data);
150 spanString = AceType::MakeRefPtr<SpanString>(data);
151 if (args.Length() > 1) {
152 auto thisObj = args.This();
153 auto spanBases = JSSpanString::ParseJsSpanBaseVector(args[1], StringUtils::ToWstring(data).length(),
154 thisObj);
155 spanString->BindWithSpans(spanBases);
156 }
157 } else {
158 auto* base = JSRef<JSObject>::Cast(args[0])->Unwrap<AceType>();
159 auto* imageAttachment = AceType::DynamicCast<JSImageAttachment>(base);
160 if (imageAttachment) {
161 auto attachment = JSSpanString::ParseJsImageAttachment(args[0]);
162 spanString = AceType::MakeRefPtr<SpanString>(attachment);
163 } else {
164 RefPtr<CustomSpan> customSpan = JSSpanString::ParseJsCustomSpan(args);
165 spanString = AceType::MakeRefPtr<SpanString>(customSpan);
166 }
167 if (args.Length() > 1) {
168 TAG_LOGW(ACE_TEXT, "initialization of styledstring with image or custom span will only use first arg");
169 }
170 }
171 }
172 jsSpanString->SetController(spanString);
173 args.SetReturnValue(Referenced::RawPtr(jsSpanString));
174 }
175
Destructor(JSSpanString * spanString)176 void JSSpanString::Destructor(JSSpanString* spanString)
177 {
178 if (spanString != nullptr) {
179 spanString->DecRefCount();
180 }
181 }
182
JSBind(BindingTarget globalObj)183 void JSSpanString::JSBind(BindingTarget globalObj)
184 {
185 JSClass<JSSpanString>::Declare("StyledString");
186 JSClass<JSSpanString>::CustomMethod("getString", &JSSpanString::GetString);
187 JSClass<JSSpanString>::CustomProperty("length", &JSSpanString::GetLength, &JSSpanString::SetLength);
188 JSClass<JSSpanString>::CustomMethod("equals", &JSSpanString::IsEqualToSpanString);
189 JSClass<JSSpanString>::CustomMethod("subStyledString", &JSSpanString::GetSubSpanString);
190 JSClass<JSSpanString>::CustomMethod("getStyles", &JSSpanString::GetSpans);
191 JSClass<JSSpanString>::StaticMethod("fromHtml", &JSSpanString::FromHtml);
192 JSClass<JSSpanString>::StaticMethod("marshalling", &JSSpanString::Marshalling);
193 JSClass<JSSpanString>::StaticMethod("unmarshalling", &JSSpanString::Unmarshalling);
194 JSClass<JSSpanString>::Bind(globalObj, JSSpanString::Constructor, JSSpanString::Destructor);
195 }
196
GetString(const JSCallbackInfo & info)197 void JSSpanString::GetString(const JSCallbackInfo& info)
198 {
199 auto ret = JSRef<JSVal>::Make(JSVal(ToJSValue(spanString_->GetString())));
200 info.SetReturnValue(ret);
201 }
202
GetLength(const JSCallbackInfo & info)203 void JSSpanString::GetLength(const JSCallbackInfo& info)
204 {
205 auto ret = JSRef<JSVal>::Make(JSVal(ToJSValue(spanString_->GetLength())));
206 info.SetReturnValue(ret);
207 }
208
SetLength(const JSCallbackInfo & info)209 void JSSpanString::SetLength(const JSCallbackInfo& info) {}
210
IsEqualToSpanString(const JSCallbackInfo & info)211 void JSSpanString::IsEqualToSpanString(const JSCallbackInfo& info)
212 {
213 if (info.Length() != 1 || !info[0]->IsObject()) {
214 info.SetReturnValue(JSRef<JSVal>::Make(JSVal(ToJSValue(false))));
215 return;
216 }
217 auto jsSpanString = JSRef<JSObject>::Cast(info[0])->Unwrap<JSSpanString>();
218 if (!jsSpanString || !jsSpanString->GetController()) {
219 info.SetReturnValue(JSRef<JSVal>::Make(JSVal(ToJSValue(false))));
220 return;
221 }
222 auto spanString = jsSpanString->GetController();
223 auto ret = JSRef<JSVal>::Make(JSVal(ToJSValue(spanString_->IsEqualToSpanString(spanString))));
224 info.SetReturnValue(ret);
225 }
226
GetSubSpanString(const JSCallbackInfo & info)227 void JSSpanString::GetSubSpanString(const JSCallbackInfo& info)
228 {
229 if (info.Length() < 1 || !info[0]->IsNumber() || (info.Length() == 2 && !info[1]->IsNumber())) {
230 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed.");
231 return;
232 }
233 auto start = info[0]->ToNumber<int32_t>();
234 auto length = spanString_->GetLength() - start;
235 if (info.Length() == 2) {
236 length = info[1]->ToNumber<int32_t>();
237 }
238 if (!CheckParameters(start, length)) {
239 return;
240 }
241 auto spanString = spanString_->GetSubSpanString(start, length);
242 CHECK_NULL_VOID(spanString);
243 JSRef<JSObject> obj = JSClass<JSSpanString>::NewInstance();
244 auto jsSpanString = Referenced::Claim(obj->Unwrap<JSSpanString>());
245 jsSpanString->SetController(spanString);
246 info.SetReturnValue(obj);
247 }
248
GetSpans(const JSCallbackInfo & info)249 void JSSpanString::GetSpans(const JSCallbackInfo& info)
250 {
251 if (info.Length() < 2 || !info[0]->IsNumber() || !info[1]->IsNumber() ||
252 (info.Length() == 3 && !info[2]->IsNumber())) {
253 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed.");
254 return;
255 }
256 auto start = info[0]->ToNumber<int32_t>();
257 auto length = info[1]->ToNumber<int32_t>();
258 if (!CheckParameters(start, length)) {
259 return;
260 }
261 std::vector<RefPtr<SpanBase>> spans;
262 if (info.Length() >= 3) {
263 auto spanType = info[2]->ToNumber<int32_t>();
264 if (!CheckSpanType(spanType)) {
265 return;
266 }
267 auto type = static_cast<SpanType>(spanType);
268 spans = spanString_->GetSpans(start, length, type);
269 } else {
270 spans = spanString_->GetSpans(start, length);
271 }
272
273 JSRef<JSArray> spanObjectArray = JSRef<JSArray>::New();
274 uint32_t idx = 0;
275 for (const RefPtr<SpanBase>& spanObject : spans) {
276 spanObjectArray->SetValueAt(idx++, CreateJsSpanBaseObject(spanObject));
277 }
278 info.SetReturnValue(JSRef<JSVal>::Cast(spanObjectArray));
279 }
280
CreateJsSpanBaseObject(const RefPtr<SpanBase> & spanObject)281 JSRef<JSObject> JSSpanString::CreateJsSpanBaseObject(const RefPtr<SpanBase>& spanObject)
282 {
283 JSRef<JSObject> resultObj = JSRef<JSObject>::New();
284 resultObj->SetProperty<int32_t>("start", spanObject->GetStartIndex());
285 resultObj->SetProperty<int32_t>("length", spanObject->GetLength());
286 resultObj->SetProperty<int32_t>("styledKey", static_cast<int32_t>(spanObject->GetSpanType()));
287 JSRef<JSObject> obj = CreateJsSpanObject(spanObject);
288 resultObj->SetPropertyObject("styledValue", obj);
289 return resultObj;
290 }
291
CreateJsSpanObject(const RefPtr<SpanBase> & spanObject)292 JSRef<JSObject> JSSpanString::CreateJsSpanObject(const RefPtr<SpanBase>& spanObject)
293 {
294 JSRef<JSObject> obj;
295 auto type = spanObject->GetSpanType();
296 auto it = spanCreators.find(type);
297 if (it != spanCreators.end()) {
298 obj = it->second(spanObject);
299 } else if (type == SpanType::CustomSpan) {
300 obj = AceType::DynamicCast<JSCustomSpan>(spanObject)->GetJsCustomSpanObject();
301 } else if (type == SpanType::ExtSpan) {
302 obj = AceType::DynamicCast<JSExtSpan>(spanObject)->GetJsExtSpanObject();
303 }
304 return obj;
305 }
306
CreateJsParagraphStyleSpan(const RefPtr<SpanBase> & spanObject)307 JSRef<JSObject> JSSpanString::CreateJsParagraphStyleSpan(const RefPtr<SpanBase>& spanObject)
308 {
309 auto span = AceType::DynamicCast<ParagraphStyleSpan>(spanObject);
310 CHECK_NULL_RETURN(span, JSRef<JSObject>::New());
311 JSRef<JSObject> obj = JSClass<JSParagraphStyleSpan>::NewInstance();
312 auto paragraphSpan = Referenced::Claim(obj->Unwrap<JSParagraphStyleSpan>());
313 paragraphSpan->SetParagraphStyleSpan(span);
314 return obj;
315 }
316
CreateJsFontSpan(const RefPtr<SpanBase> & spanObject)317 JSRef<JSObject> JSSpanString::CreateJsFontSpan(const RefPtr<SpanBase>& spanObject)
318 {
319 auto span = AceType::DynamicCast<FontSpan>(spanObject);
320 CHECK_NULL_RETURN(span, JSRef<JSObject>::New());
321 JSRef<JSObject> obj = JSClass<JSFontSpan>::NewInstance();
322 auto fontSpan = Referenced::Claim(obj->Unwrap<JSFontSpan>());
323 fontSpan->SetFontSpan(span);
324 return obj;
325 }
326
CreateJsDecorationSpan(const RefPtr<SpanBase> & spanObject)327 JSRef<JSObject> JSSpanString::CreateJsDecorationSpan(const RefPtr<SpanBase>& spanObject)
328 {
329 auto span = AceType::DynamicCast<DecorationSpan>(spanObject);
330 CHECK_NULL_RETURN(span, JSRef<JSObject>::New());
331 JSRef<JSObject> obj = JSClass<JSDecorationSpan>::NewInstance();
332 auto decorationSpan = Referenced::Claim(obj->Unwrap<JSDecorationSpan>());
333 decorationSpan->SetDecorationSpan(span);
334 return obj;
335 }
336
CreateJsBaselineOffsetSpan(const RefPtr<SpanBase> & spanObject)337 JSRef<JSObject> JSSpanString::CreateJsBaselineOffsetSpan(const RefPtr<SpanBase>& spanObject)
338 {
339 auto span = AceType::DynamicCast<BaselineOffsetSpan>(spanObject);
340 CHECK_NULL_RETURN(span, JSRef<JSObject>::New());
341 JSRef<JSObject> obj = JSClass<JSBaselineOffsetSpan>::NewInstance();
342 auto baselineOffsetSpan = Referenced::Claim(obj->Unwrap<JSBaselineOffsetSpan>());
343 baselineOffsetSpan->SetBaselineOffsetSpan(span);
344 return obj;
345 }
346
CreateJsLetterSpacingSpan(const RefPtr<SpanBase> & spanObject)347 JSRef<JSObject> JSSpanString::CreateJsLetterSpacingSpan(const RefPtr<SpanBase>& spanObject)
348 {
349 auto span = AceType::DynamicCast<LetterSpacingSpan>(spanObject);
350 CHECK_NULL_RETURN(span, JSRef<JSObject>::New());
351 JSRef<JSObject> obj = JSClass<JSLetterSpacingSpan>::NewInstance();
352 auto letterSpacingSpan = Referenced::Claim(obj->Unwrap<JSLetterSpacingSpan>());
353 letterSpacingSpan->SetLetterSpacingSpan(span);
354 return obj;
355 }
356
CreateJsGestureSpan(const RefPtr<SpanBase> & spanObject)357 JSRef<JSObject> JSSpanString::CreateJsGestureSpan(const RefPtr<SpanBase>& spanObject)
358 {
359 auto span = AceType::DynamicCast<GestureSpan>(spanObject);
360 CHECK_NULL_RETURN(span, JSRef<JSObject>::New());
361 JSRef<JSObject> obj = JSClass<JSGestureSpan>::NewInstance();
362 auto gestureSpan = Referenced::Claim(obj->Unwrap<JSGestureSpan>());
363 gestureSpan->SetGestureSpan(span);
364 return obj;
365 }
366
CreateJsTextShadowSpan(const RefPtr<SpanBase> & spanObject)367 JSRef<JSObject> JSSpanString::CreateJsTextShadowSpan(const RefPtr<SpanBase>& spanObject)
368 {
369 auto span = AceType::DynamicCast<TextShadowSpan>(spanObject);
370 CHECK_NULL_RETURN(span, JSRef<JSObject>::New());
371 JSRef<JSObject> obj = JSClass<JSTextShadowSpan>::NewInstance();
372 auto textShadowSpan = Referenced::Claim(obj->Unwrap<JSTextShadowSpan>());
373 textShadowSpan->SetTextShadowSpan(span);
374 return obj;
375 }
376
CreateJsLineHeightSpan(const RefPtr<SpanBase> & spanObject)377 JSRef<JSObject> JSSpanString::CreateJsLineHeightSpan(const RefPtr<SpanBase>& spanObject)
378 {
379 auto span = AceType::DynamicCast<LineHeightSpan>(spanObject);
380 CHECK_NULL_RETURN(span, JSRef<JSObject>::New());
381 JSRef<JSObject> obj = JSClass<JSLineHeightSpan>::NewInstance();
382 auto lineHeightSpan = Referenced::Claim(obj->Unwrap<JSLineHeightSpan>());
383 lineHeightSpan->SetLineHeightSpan(span);
384 return obj;
385 }
386
CreateJsImageSpan(const RefPtr<SpanBase> & spanObject)387 JSRef<JSObject> JSSpanString::CreateJsImageSpan(const RefPtr<SpanBase>& spanObject)
388 {
389 auto span = AceType::DynamicCast<ImageSpan>(spanObject);
390 CHECK_NULL_RETURN(span, JSRef<JSObject>::New());
391 JSRef<JSObject> obj = JSClass<JSImageAttachment>::NewInstance();
392 auto imageSpan = Referenced::Claim(obj->Unwrap<JSImageAttachment>());
393 imageSpan->SetImageSpan(span);
394 return obj;
395 }
396
ParseJsSpanBaseWithoutSpecialSpan(int32_t start,int32_t length,SpanType type,const JSRef<JSObject> & obj,const JSCallbackInfo & info)397 RefPtr<SpanBase> JSSpanString::ParseJsSpanBaseWithoutSpecialSpan(
398 int32_t start, int32_t length, SpanType type, const JSRef<JSObject>& obj, const JSCallbackInfo& info)
399 {
400 if (type == SpanType::CustomSpan) {
401 return ParseJsCustomSpan(start, length, info);
402 }
403 return JSSpanString::ParseJsSpanBase(start, length, type, obj);
404 }
405
ParseJsSpanBase(int32_t start,int32_t length,SpanType type,const JSRef<JSObject> & obj)406 RefPtr<SpanBase> JSSpanString::ParseJsSpanBase(int32_t start, int32_t length, SpanType type, const JSRef<JSObject>& obj)
407 {
408 switch (type) {
409 case SpanType::Font:
410 return ParseJsFontSpan(start, length, obj);
411 case SpanType::Decoration:
412 return ParseJsDecorationSpan(start, length, obj);
413 case SpanType::LetterSpacing:
414 return ParseJsLetterSpacingSpan(start, length, obj);
415 case SpanType::BaselineOffset:
416 return ParseJsBaselineOffsetSpan(start, length, obj);
417 case SpanType::Gesture:
418 return ParseJsGestureSpan(start, length, obj);
419 case SpanType::TextShadow:
420 return ParseJsTextShadowSpan(start, length, obj);
421 case SpanType::LineHeight:
422 return ParseJsLineHeightSpan(start, length, obj);
423 case SpanType::Image:
424 return GetImageAttachment(start, length, obj);
425 case SpanType::ParagraphStyle:
426 return ParseJsParagraphStyleSpan(start, length, obj);
427 case SpanType::ExtSpan:
428 return ParseJsExtSpan(start, length, obj);
429 default:
430 break;
431 }
432 return nullptr;
433 }
434
ParseJsFontSpan(int32_t start,int32_t length,const JSRef<JSObject> & obj)435 RefPtr<SpanBase> JSSpanString::ParseJsFontSpan(int32_t start, int32_t length, const JSRef<JSObject>& obj)
436 {
437 auto* base = obj->Unwrap<AceType>();
438 auto* fontSpan = AceType::DynamicCast<JSFontSpan>(base);
439 if (fontSpan && fontSpan->GetFontSpan()) {
440 return AceType::MakeRefPtr<FontSpan>(fontSpan->GetFontSpan()->GetFont(), start, start + length);
441 }
442 return nullptr;
443 }
444
ParseJsParagraphStyleSpan(int32_t start,int32_t length,const JSRef<JSObject> & obj)445 RefPtr<SpanBase> JSSpanString::ParseJsParagraphStyleSpan(int32_t start, int32_t length, const JSRef<JSObject>& obj)
446 {
447 auto* base = obj->Unwrap<AceType>();
448 auto* paragraphStyleSpan = AceType::DynamicCast<JSParagraphStyleSpan>(base);
449 if (paragraphStyleSpan && paragraphStyleSpan->GetParagraphStyleSpan()) {
450 return AceType::MakeRefPtr<ParagraphStyleSpan>(
451 paragraphStyleSpan->GetParagraphStyleSpan()->GetParagraphStyle(), start, start + length);
452 }
453 return nullptr;
454 }
455
ParseJsDecorationSpan(int32_t start,int32_t length,const JSRef<JSObject> & obj)456 RefPtr<SpanBase> JSSpanString::ParseJsDecorationSpan(int32_t start, int32_t length, const JSRef<JSObject>& obj)
457 {
458 auto* base = obj->Unwrap<AceType>();
459 auto* decorationSpan = AceType::DynamicCast<JSDecorationSpan>(base);
460 if (decorationSpan && decorationSpan->GetDecorationSpan()) {
461 return AceType::MakeRefPtr<DecorationSpan>(decorationSpan->GetDecorationSpan()->GetTextDecorationType(),
462 decorationSpan->GetDecorationSpan()->GetColor(),
463 decorationSpan->GetDecorationSpan()->GetTextDecorationStyle(), start, start + length);
464 }
465 return nullptr;
466 }
467
ParseJsBaselineOffsetSpan(int32_t start,int32_t length,const JSRef<JSObject> & obj)468 RefPtr<SpanBase> JSSpanString::ParseJsBaselineOffsetSpan(int32_t start, int32_t length, const JSRef<JSObject>& obj)
469 {
470 auto* base = obj->Unwrap<AceType>();
471 auto* baselineOffsetSpan = AceType::DynamicCast<JSBaselineOffsetSpan>(base);
472 if (baselineOffsetSpan && baselineOffsetSpan->GetBaselineOffsetSpan()) {
473 return AceType::MakeRefPtr<BaselineOffsetSpan>(
474 baselineOffsetSpan->GetBaselineOffsetSpan()->GetBaselineOffset(), start, start + length);
475 }
476 return nullptr;
477 }
478
ParseJsLetterSpacingSpan(int32_t start,int32_t length,const JSRef<JSObject> & obj)479 RefPtr<SpanBase> JSSpanString::ParseJsLetterSpacingSpan(int32_t start, int32_t length, const JSRef<JSObject>& obj)
480 {
481 auto* base = obj->Unwrap<AceType>();
482 auto* letterSpacingSpan = AceType::DynamicCast<JSLetterSpacingSpan>(base);
483 if (letterSpacingSpan && letterSpacingSpan->GetLetterSpacingSpan()) {
484 return AceType::MakeRefPtr<LetterSpacingSpan>(
485 letterSpacingSpan->GetLetterSpacingSpan()->GetLetterSpacing(), start, start + length);
486 }
487 return nullptr;
488 }
489
ParseJsGestureSpan(int32_t start,int32_t length,const JSRef<JSObject> & obj)490 RefPtr<SpanBase> JSSpanString::ParseJsGestureSpan(int32_t start, int32_t length, const JSRef<JSObject>& obj)
491 {
492 auto* base = obj->Unwrap<AceType>();
493 auto* gestureSpan = AceType::DynamicCast<JSGestureSpan>(base);
494 if (gestureSpan && gestureSpan->GetGestureSpan()) {
495 return AceType::MakeRefPtr<GestureSpan>(
496 gestureSpan->GetGestureSpan()->GetGestureStyle(), start, start + length);
497 }
498 return nullptr;
499 }
500
ParseJsTextShadowSpan(int32_t start,int32_t length,const JSRef<JSObject> & obj)501 RefPtr<SpanBase> JSSpanString::ParseJsTextShadowSpan(int32_t start, int32_t length, const JSRef<JSObject>& obj)
502 {
503 auto* base = obj->Unwrap<AceType>();
504 auto* textShadowSpan = AceType::DynamicCast<JSTextShadowSpan>(base);
505 if (textShadowSpan && textShadowSpan->GetTextShadowSpan()) {
506 return AceType::MakeRefPtr<TextShadowSpan>(
507 textShadowSpan->GetTextShadowSpan()->GetTextShadow(), start, start + length);
508 }
509 return nullptr;
510 }
511
ParseJsLineHeightSpan(int32_t start,int32_t length,const JSRef<JSObject> & obj)512 RefPtr<SpanBase> JSSpanString::ParseJsLineHeightSpan(int32_t start, int32_t length, const JSRef<JSObject>& obj)
513 {
514 auto* base = obj->Unwrap<AceType>();
515 auto* lineHeightSpan = AceType::DynamicCast<JSLineHeightSpan>(base);
516 if (lineHeightSpan && lineHeightSpan->GetLineHeightSpan()) {
517 return AceType::MakeRefPtr<LineHeightSpan>(
518 lineHeightSpan->GetLineHeightSpan()->GetLineHeight(), start, start + length);
519 }
520 return nullptr;
521 }
522
GetImageAttachment(int32_t start,int32_t length,const JSRef<JSObject> & obj)523 RefPtr<SpanBase> JSSpanString::GetImageAttachment(int32_t start, int32_t length, const JSRef<JSObject>& obj)
524 {
525 auto* base = obj->Unwrap<AceType>();
526 auto* imageAttachment = AceType::DynamicCast<JSImageAttachment>(base);
527 if (imageAttachment && imageAttachment->GetImageSpan()) {
528 auto imageSpan = imageAttachment->GetImageSpan();
529 imageSpan->UpdateStartIndex(start);
530 imageSpan->UpdateEndIndex(start + length);
531 return imageSpan;
532 }
533 return nullptr;
534 }
535
ParseJsCustomSpan(int32_t start,int32_t length,const JSCallbackInfo & args)536 RefPtr<SpanBase> JSSpanString::ParseJsCustomSpan(int32_t start, int32_t length, const JSCallbackInfo& args)
537 {
538 if (args.Length() == 0) {
539 return nullptr;
540 }
541 auto paramObj = args[0];
542 if (paramObj->IsUndefined()) {
543 return nullptr;
544 }
545 if (!paramObj->IsObject()) {
546 return nullptr;
547 }
548 auto styledValueObj = JSRef<JSObject>::Cast(paramObj)->GetProperty("styledValue");
549 if (!styledValueObj->IsObject()) {
550 return nullptr;
551 }
552 auto styleStringValue = JSRef<JSObject>::Cast(styledValueObj);
553 if (styleStringValue->IsUndefined()) {
554 return nullptr;
555 }
556 auto typeObj = styleStringValue->GetProperty("type_");
557 if (!typeObj->IsString() || typeObj->ToString() != "CustomSpan") {
558 return nullptr;
559 }
560 auto spanBase = AceType::MakeRefPtr<JSCustomSpan>(JSRef<JSObject>(styleStringValue), args);
561 spanBase->UpdateStartIndex(start);
562 spanBase->UpdateEndIndex(start + length);
563 return spanBase;
564 }
565
ParseJsExtSpan(int32_t start,int32_t length,const JSRef<JSObject> & obj)566 RefPtr<SpanBase> JSSpanString::ParseJsExtSpan(int32_t start, int32_t length, const JSRef<JSObject>& obj)
567 {
568 auto typeObj = obj->GetProperty("type_");
569 if (!typeObj->IsString() || typeObj->ToString() != "ExtSpan") {
570 return nullptr;
571 }
572 auto spanBase = AceType::MakeRefPtr<JSExtSpan>(obj, start, start + length);
573 return spanBase;
574 }
575
CheckSpanType(int32_t spanType)576 bool JSSpanString::CheckSpanType(int32_t spanType)
577 {
578 if (types.find(static_cast<SpanType>(spanType)) == types.end()) {
579 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "CheckSpanType failed: Ilegal span type");
580 return false;
581 }
582 return true;
583 }
584
CheckParameters(int32_t start,int32_t length)585 bool JSSpanString::CheckParameters(int32_t start, int32_t length)
586 {
587 // The input parameter must not cross the boundary.
588 if (!spanString_->CheckRange(start, length)) {
589 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s start:%d length:%d", "CheckBoundary failed:", start, length);
590 return false;
591 }
592 return true;
593 }
594
ParseJsSpanBaseVector(const JSRef<JSObject> & obj,int32_t maxLength,JsiRef<JsiObject> thisObj)595 std::vector<RefPtr<SpanBase>> JSSpanString::ParseJsSpanBaseVector(const JSRef<JSObject>& obj, int32_t maxLength,
596 JsiRef<JsiObject> thisObj)
597 {
598 std::vector<RefPtr<SpanBase>> spanBaseVector;
599 auto arrays = JSRef<JSArray>::Cast(obj);
600 for (size_t i = 0; i < arrays->Length(); i++) {
601 JSRef<JSVal> value = arrays->GetValueAt(i);
602 if (value->IsNull() || value->IsUndefined() || (!value->IsObject())) {
603 continue;
604 }
605 auto valueObj = JSRef<JSObject>::Cast(value);
606 auto startProperty = valueObj->GetProperty("start");
607 auto lengthProperty = valueObj->GetProperty("length");
608 int32_t start = 0;
609 if (!startProperty->IsNull() && startProperty->IsNumber()) {
610 start = startProperty->ToNumber<int32_t>();
611 start = start < 0 || start >= maxLength ? 0 : start;
612 }
613 int32_t length = maxLength - start;
614 if (!lengthProperty->IsNull() && lengthProperty->IsNumber()) {
615 length = lengthProperty->ToNumber<int32_t>();
616 length = length > maxLength - start || length <= 0 ? maxLength - start : length;
617 }
618 auto styleKey = valueObj->GetProperty("styledKey");
619 if (styleKey->IsNull() || !styleKey->IsNumber()) {
620 continue;
621 }
622 auto styleStringValue = valueObj->GetProperty("styledValue");
623 if (!styleStringValue->IsObject()) {
624 continue;
625 }
626 auto type = static_cast<SpanType>(styleKey->ToNumber<int32_t>());
627 if (type == SpanType::Image || type == SpanType::CustomSpan) {
628 continue;
629 }
630 if (type == SpanType::Gesture) {
631 auto newIndex = gestureStyleStoreIndex_.fetch_add(1);
632 std::string key = "STYLED_STRING_GESTURESTYLE_STORE_" + std::to_string(newIndex);
633 thisObj->SetPropertyObject(key.c_str(), styleStringValue);
634 }
635 auto spanBase = ParseJsSpanBase(start, length, type, JSRef<JSObject>::Cast(styleStringValue));
636 if (spanBase) {
637 spanBaseVector.emplace_back(spanBase);
638 }
639 }
640 return spanBaseVector;
641 }
642
GetController()643 const RefPtr<SpanString>& JSSpanString::GetController()
644 {
645 return spanString_;
646 }
647
SetController(const RefPtr<SpanString> & spanString)648 void JSSpanString::SetController(const RefPtr<SpanString>& spanString)
649 {
650 spanString_ = spanString;
651 }
652
ParseJsImageAttachment(const JSRef<JSObject> & info)653 ImageSpanOptions JSSpanString::ParseJsImageAttachment(const JSRef<JSObject>& info)
654 {
655 ImageSpanOptions options;
656 auto* base = info->Unwrap<AceType>();
657 auto* imageAttachment = AceType::DynamicCast<JSImageAttachment>(base);
658 if (!imageAttachment) {
659 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Parse JsImageAttachment Failed");
660 return options;
661 }
662 return imageAttachment->GetImageOptions();
663 }
664
ParseJsCustomSpan(const JSCallbackInfo & args)665 RefPtr<CustomSpan> JSSpanString::ParseJsCustomSpan(const JSCallbackInfo& args)
666 {
667 return AceType::MakeRefPtr<JSCustomSpan>(args[0], args);
668 }
669
FromHtml(const JSCallbackInfo & info)670 void JSSpanString::FromHtml(const JSCallbackInfo& info)
671 {
672 ContainerScope scope(Container::CurrentIdSafely());
673 if (info.Length() != 1 || !info[0]->IsString()) {
674 ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
675 return;
676 }
677 std::string arg = info[0]->ToString();
678 auto container = Container::CurrentSafely();
679 if (!container) {
680 ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
681 return;
682 }
683 auto taskExecutor = container->GetTaskExecutor();
684 CHECK_NULL_VOID(taskExecutor);
685 auto engine = EngineHelper::GetCurrentEngine();
686 CHECK_NULL_VOID(engine);
687 NativeEngine* nativeEngine = engine->GetNativeEngine();
688 CHECK_NULL_VOID(nativeEngine);
689 auto asyncContext = std::make_shared<HtmlConverterAsyncCtx>();
690 asyncContext->instanceId = Container::CurrentIdSafely();
691 asyncContext->env = reinterpret_cast<napi_env>(nativeEngine);
692 napi_value result = nullptr;
693 napi_create_promise(asyncContext->env, &asyncContext->deferred, &result);
694 taskExecutor->PostTask(
695 [htmlStr = arg, asyncContext]() mutable {
696 ContainerScope scope(asyncContext->instanceId);
697 // FromHtml may cost much time because of pixelmap.
698 // Therefore this function should be called in Background thread.
699 auto styledString = HtmlUtils::FromHtml(htmlStr);
700 auto container = AceEngine::Get().GetContainer(asyncContext->instanceId);
701 CHECK_NULL_VOID(container);
702 auto taskExecutor = container->GetTaskExecutor();
703 taskExecutor->PostTask(
704 [styledString, asyncContext]() mutable {
705 ContainerScope scope(asyncContext->instanceId);
706 if (!styledString) {
707 ProcessPromiseCallback(asyncContext, ERROR_CODE_FROM_HTML_CONVERT_ERROR);
708 return;
709 }
710 JSRef<JSObject> obj = JSClass<JSSpanString>::NewInstance();
711 auto jsSpanString = Referenced::Claim(obj->Unwrap<JSSpanString>());
712 jsSpanString->SetController(styledString);
713 auto spanStrNapi = JsConverter::ConvertJsValToNapiValue(obj);
714 ProcessPromiseCallback(asyncContext, ERROR_CODE_NO_ERROR, spanStrNapi);
715 },
716 TaskExecutor::TaskType::UI, "FromHtmlReturnPromise", PriorityType::IMMEDIATE);
717 },
718 TaskExecutor::TaskType::BACKGROUND, "FromHtml", PriorityType::IMMEDIATE);
719 auto jsPromise = JsConverter::ConvertNapiValueToJsVal(result);
720 CHECK_NULL_VOID(jsPromise->IsObject());
721 info.SetReturnValue(JSRef<JSObject>::Cast(jsPromise));
722 }
723
Marshalling(const JSCallbackInfo & info)724 void JSSpanString::Marshalling(const JSCallbackInfo& info)
725 {
726 auto arg = info[0];
727 if (info.Length() != 1 || !arg->IsObject()) {
728 ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
729 return;
730 }
731
732 auto* spanString = JSRef<JSObject>::Cast(arg)->Unwrap<JSSpanString>();
733 CHECK_NULL_VOID(spanString);
734 auto spanStringController = spanString->GetController();
735 CHECK_NULL_VOID(spanStringController);
736 std::vector<uint8_t> buff;
737 spanStringController->EncodeTlv(buff);
738
739 size_t bufferSize = buff.size();
740 JSRef<JSArrayBuffer> arrayBuffer = JSRef<JSArrayBuffer>::New(bufferSize);
741 auto* buffer = static_cast<uint8_t*>(arrayBuffer->GetBuffer());
742 if (memcpy_s(buffer, bufferSize, buff.data(), bufferSize) != 0) {
743 return;
744 }
745 info.SetReturnValue(arrayBuffer);
746 }
747
UnmarshallingExec(napi_env env,void * data)748 void JSSpanString::UnmarshallingExec(napi_env env, void *data)
749 {
750 CHECK_NULL_VOID(data);
751 auto asyncContext = static_cast<AsyncContext*>(data);
752 asyncContext->spanString = SpanString::DecodeTlv(asyncContext->buffer);
753 CHECK_NULL_VOID(asyncContext->spanString);
754 asyncContext->status = napi_ok;
755 }
756
UnmarshallingComplete(napi_env env,napi_status status,void * data)757 void JSSpanString::UnmarshallingComplete(napi_env env, napi_status status, void *data)
758 {
759 CHECK_NULL_VOID(data);
760 auto asyncContext = static_cast<AsyncContext*>(data);
761 JSRef<JSObject> obj = JSClass<JSSpanString>::NewInstance();
762 auto jsSpanString = Referenced::Claim(obj->Unwrap<JSSpanString>());
763 CHECK_NULL_VOID(jsSpanString);
764 jsSpanString->SetController(asyncContext->spanString);
765 auto spanStrNapi = JsConverter::ConvertJsValToNapiValue(obj);
766
767 if (status == napi_ok && asyncContext->status == napi_ok) {
768 napi_resolve_deferred(env, asyncContext->deferred, spanStrNapi);
769 } else {
770 napi_value error = CreateErrorValue(asyncContext->env, ERROR_CODE_STYLED_STRING_CONVERT_ERROR,
771 ASYNC_ERROR_MAP[ERROR_CODE_STYLED_STRING_CONVERT_ERROR]);
772 napi_reject_deferred(env, asyncContext->deferred, error);
773 }
774 delete asyncContext;
775 }
776
Unmarshalling(const JSCallbackInfo & info)777 void JSSpanString::Unmarshalling(const JSCallbackInfo& info)
778 {
779 auto arg = info[0];
780 if (info.Length() != 1 || !arg->IsArrayBuffer()) {
781 ReturnPromise(info, ERROR_CODE_PARAM_INVALID);
782 return;
783 }
784 JSRef<JSArrayBuffer> arrayBuffer = JSRef<JSArrayBuffer>::Cast(arg);
785 size_t bufferSize = static_cast<size_t>(arrayBuffer->ByteLength());
786 void* buffer = arrayBuffer->GetBuffer();
787 std::vector<uint8_t> buff(static_cast<uint8_t*>(buffer), static_cast<uint8_t*>(buffer) + bufferSize);
788 auto asyncContext = new AsyncContext();
789 asyncContext->buffer = buff;
790
791 auto engine = EngineHelper::GetCurrentEngineSafely();
792 CHECK_NULL_VOID(engine);
793 NativeEngine* nativeEngine = engine->GetNativeEngine();
794 CHECK_NULL_VOID(nativeEngine);
795 asyncContext->env = reinterpret_cast<napi_env>(nativeEngine);
796 napi_value promise = nullptr;
797 napi_create_promise(asyncContext->env, &asyncContext->deferred, &promise);
798 napi_value resourceName = nullptr;
799 napi_create_string_utf8(asyncContext->env, "ArkUISpanStringUnmarshalling", NAPI_AUTO_LENGTH, &resourceName);
800 napi_create_async_work(asyncContext->env, nullptr, resourceName, UnmarshallingExec, UnmarshallingComplete,
801 asyncContext, &asyncContext->asyncWork);
802 napi_queue_async_work(asyncContext->env, asyncContext->asyncWork);
803
804 auto jsPromise = JsConverter::ConvertNapiValueToJsVal(promise);
805 CHECK_NULL_VOID(jsPromise->IsObject());
806 info.SetReturnValue(JSRef<JSObject>::Cast(jsPromise));
807 }
808
809 // JSMutableSpanString
Constructor(const JSCallbackInfo & args)810 void JSMutableSpanString::Constructor(const JSCallbackInfo& args)
811 {
812 auto jsSpanString = Referenced::MakeRefPtr<JSMutableSpanString>();
813 jsSpanString->IncRefCount();
814 std::string data;
815
816 RefPtr<MutableSpanString> spanString;
817 if (args.Length() == 0) {
818 spanString = AceType::MakeRefPtr<MutableSpanString>(data);
819 } else {
820 if (args[0]->IsString()) {
821 JSViewAbstract::ParseJsString(args[0], data);
822 spanString = AceType::MakeRefPtr<MutableSpanString>(data);
823 if (args.Length() > 1) {
824 auto thisObj = args.This();
825 auto spanBases = JSSpanString::ParseJsSpanBaseVector(args[1],
826 StringUtils::ToWstring(data).length(), thisObj);
827 spanString->BindWithSpans(spanBases);
828 }
829 } else {
830 if (!args[0]->IsObject()) {
831 return;
832 }
833 auto* base = JSRef<JSObject>::Cast(args[0])->Unwrap<AceType>();
834 auto* imageAttachment = AceType::DynamicCast<JSImageAttachment>(base);
835 if (imageAttachment) {
836 auto attachment = JSSpanString::ParseJsImageAttachment(args[0]);
837 spanString = AceType::MakeRefPtr<MutableSpanString>(attachment);
838 } else {
839 RefPtr<CustomSpan> customSpan = JSSpanString::ParseJsCustomSpan(args);
840 spanString = AceType::MakeRefPtr<MutableSpanString>(customSpan);
841 }
842 }
843 }
844 jsSpanString->SetController(spanString);
845 jsSpanString->SetMutableController(spanString);
846 args.SetReturnValue(Referenced::RawPtr(jsSpanString));
847 }
848
Destructor(JSMutableSpanString * spanString)849 void JSMutableSpanString::Destructor(JSMutableSpanString* spanString)
850 {
851 if (spanString != nullptr) {
852 spanString->DecRefCount();
853 }
854 }
855
JSBind(BindingTarget globalObj)856 void JSMutableSpanString::JSBind(BindingTarget globalObj)
857 {
858 JSClass<JSMutableSpanString>::Declare("MutableStyledString");
859 JSClass<JSMutableSpanString>::CustomMethod("getString", &JSSpanString::GetString);
860 JSClass<JSMutableSpanString>::CustomProperty("length", &JSSpanString::GetLength, &JSSpanString::SetLength);
861 JSClass<JSMutableSpanString>::CustomMethod("equals", &JSSpanString::IsEqualToSpanString);
862 JSClass<JSMutableSpanString>::CustomMethod("subStyledString", &JSSpanString::GetSubSpanString);
863 JSClass<JSMutableSpanString>::CustomMethod("getStyles", &JSSpanString::GetSpans);
864
865 JSClass<JSMutableSpanString>::CustomMethod("replaceString", &JSMutableSpanString::ReplaceString);
866 JSClass<JSMutableSpanString>::CustomMethod("insertString", &JSMutableSpanString::InsertString);
867 JSClass<JSMutableSpanString>::CustomMethod("removeString", &JSMutableSpanString::RemoveString);
868 JSClass<JSMutableSpanString>::CustomMethod("replaceStyle", &JSMutableSpanString::ReplaceSpan);
869 JSClass<JSMutableSpanString>::CustomMethod("setStyle", &JSMutableSpanString::AddSpan);
870 JSClass<JSMutableSpanString>::CustomMethod("removeStyle", &JSMutableSpanString::RemoveSpan);
871 JSClass<JSMutableSpanString>::CustomMethod("removeStyles", &JSMutableSpanString::RemoveSpans);
872 JSClass<JSMutableSpanString>::Method("clearStyles", &JSMutableSpanString::ClearAllSpans);
873 JSClass<JSMutableSpanString>::CustomMethod("replaceStyledString", &JSMutableSpanString::ReplaceSpanString);
874 JSClass<JSMutableSpanString>::CustomMethod("insertStyledString", &JSMutableSpanString::InsertSpanString);
875 JSClass<JSMutableSpanString>::CustomMethod("appendStyledString", &JSMutableSpanString::AppendSpanString);
876 JSClass<JSMutableSpanString>::Bind(globalObj, JSMutableSpanString::Constructor, JSMutableSpanString::Destructor);
877 }
878
ReplaceString(const JSCallbackInfo & info)879 void JSMutableSpanString::ReplaceString(const JSCallbackInfo& info)
880 {
881 if (info.Length() != 3 || !info[0]->IsNumber() || !info[1]->IsNumber() || !info[2]->IsString()) {
882 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed.");
883 return;
884 }
885 int32_t start = info[0]->ToNumber<int32_t>();
886 int32_t length = info[1]->ToNumber<int32_t>();
887 auto controller = GetMutableController().Upgrade();
888 CHECK_NULL_VOID(controller);
889 if (!CheckParameters(start, length)) {
890 return;
891 }
892 std::string data = info[2]->ToString();
893 controller->ReplaceString(start, length, data);
894 }
895
InsertString(const JSCallbackInfo & info)896 void JSMutableSpanString::InsertString(const JSCallbackInfo& info)
897 {
898 if (info.Length() != 2 || !info[0]->IsNumber() || !info[1]->IsString()) {
899 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed.");
900 return;
901 }
902 auto start = info[0]->ToNumber<int32_t>();
903 std::string data = info[1]->ToString();
904 auto controller = GetMutableController().Upgrade();
905 CHECK_NULL_VOID(controller);
906 // The input parameter must not cross the boundary.
907 auto characterLength = controller->GetLength();
908 if (start < 0 || start > characterLength) {
909 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s start: %d StyledStringLength: %d",
910 "Out of bounds", start, characterLength);
911 return;
912 }
913 controller->InsertString(start, data);
914 }
915
RemoveString(const JSCallbackInfo & info)916 void JSMutableSpanString::RemoveString(const JSCallbackInfo& info)
917 {
918 if (info.Length() != 2 || !info[0]->IsNumber() || !info[1]->IsNumber()) {
919 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed.");
920 return;
921 }
922 auto start = info[0]->ToNumber<int32_t>();
923 auto length = info[1]->ToNumber<int32_t>();
924 auto controller = GetMutableController().Upgrade();
925 CHECK_NULL_VOID(controller);
926 if (!CheckParameters(start, length)) {
927 return;
928 }
929 controller->RemoveString(start, length);
930 }
931
IsImageNode(int32_t location)932 bool JSMutableSpanString::IsImageNode(int32_t location)
933 {
934 auto mutableSpanString = mutableSpanString_.Upgrade();
935 CHECK_NULL_RETURN(mutableSpanString, false);
936 return mutableSpanString->IsSpeicalNode(location, SpanType::Image);
937 }
938
IsCustomSpanNode(int32_t location)939 bool JSMutableSpanString::IsCustomSpanNode(int32_t location)
940 {
941 auto mutableSpanString = mutableSpanString_.Upgrade();
942 CHECK_NULL_RETURN(mutableSpanString, false);
943 return mutableSpanString->IsSpeicalNode(location, SpanType::CustomSpan);
944 }
945
VerifyImageParameters(int32_t start,int32_t length)946 bool JSMutableSpanString::VerifyImageParameters(int32_t start, int32_t length)
947 {
948 if (length != 1) {
949 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "VerifyImageParameters failed: length should be one");
950 return false;
951 }
952 if (!IsImageNode(start)) {
953 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "VerifyCustomSpanParameters failed: Not ImageNode");
954 return false;
955 }
956 return true;
957 }
958
VerifyCustomSpanParameters(int32_t start,int32_t length)959 bool JSMutableSpanString::VerifyCustomSpanParameters(int32_t start, int32_t length)
960 {
961 if (length != 1) {
962 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "VerifyCustomSpanParameters failed: length should be one");
963 return false;
964 }
965 if (!IsCustomSpanNode(start)) {
966 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "VerifyCustomSpanParameters failed: Not CustomSpanNode");
967 return false;
968 }
969 return true;
970 }
971
ReplaceSpan(const JSCallbackInfo & info)972 void JSMutableSpanString::ReplaceSpan(const JSCallbackInfo& info)
973 {
974 if (info.Length() != 1 || !info[0]->IsObject()) {
975 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed.");
976 return;
977 }
978 auto paramObject = JSRef<JSObject>::Cast(info[0]);
979 auto startObj = paramObject->GetProperty("start");
980 auto lengthObj = paramObject->GetProperty("length");
981 auto styleKeyObj = paramObject->GetProperty("styledKey");
982 auto styleValueObj = paramObject->GetProperty("styledValue");
983 if (!startObj->IsNumber() || !lengthObj->IsNumber() || !styleKeyObj->IsNumber() || !styleValueObj->IsObject()) {
984 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed.");
985 return;
986 }
987 auto spanType = styleKeyObj->ToNumber<int32_t>();
988 if (!CheckSpanType(spanType)) {
989 return;
990 }
991 auto start = startObj->ToNumber<int32_t>();
992 auto length = lengthObj->ToNumber<int32_t>();
993 auto type = static_cast<SpanType>(spanType);
994 if (type == SpanType::Image && !VerifyImageParameters(start, length)) {
995 return;
996 }
997 if (type == SpanType::CustomSpan && !VerifyCustomSpanParameters(start, length)) {
998 return;
999 }
1000 if (!styleValueObj->IsObject()) {
1001 return;
1002 }
1003 auto spanBase = ParseJsSpanBaseWithoutSpecialSpan(start, length, type, JSRef<JSObject>::Cast(styleValueObj), info);
1004 if (!spanBase) {
1005 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s",
1006 "ReplaceSpan failed: maybe styledKey & corresponding value not match");
1007 return;
1008 }
1009 auto controller = GetMutableController().Upgrade();
1010 CHECK_NULL_VOID(controller);
1011 if (!CheckParameters(start, length)) {
1012 return;
1013 }
1014 if (type == SpanType::Gesture) {
1015 auto thisObj = info.This();
1016 auto newIndex = gestureStyleStoreIndex_.fetch_add(1);
1017 std::string key = "STYLED_STRING_GESTURESTYLE_STORE_" + std::to_string(newIndex);
1018 thisObj->SetPropertyObject(key.c_str(), styleValueObj);
1019 }
1020 controller->ReplaceSpan(start, length, spanBase);
1021 }
1022
AddSpan(const JSCallbackInfo & info)1023 void JSMutableSpanString::AddSpan(const JSCallbackInfo& info)
1024 {
1025 if (info.Length() != 1 || !info[0]->IsObject()) {
1026 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed.");
1027 return;
1028 }
1029 auto paramObject = JSRef<JSObject>::Cast(info[0]);
1030 auto startObj = paramObject->GetProperty("start");
1031 auto lengthObj = paramObject->GetProperty("length");
1032 auto styleKeyObj = paramObject->GetProperty("styledKey");
1033 auto styleValueObj = paramObject->GetProperty("styledValue");
1034 if (!startObj->IsNumber() || !lengthObj->IsNumber() || !styleKeyObj->IsNumber() || !styleValueObj->IsObject()) {
1035 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed.");
1036 return;
1037 }
1038 auto spanType = styleKeyObj->ToNumber<int32_t>();
1039 CHECK_NULL_VOID(CheckSpanType(spanType));
1040 auto start = startObj->ToNumber<int32_t>();
1041 auto length = lengthObj->ToNumber<int32_t>();
1042 auto type = static_cast<SpanType>(spanType);
1043 if (type == SpanType::Image && !VerifyImageParameters(start, length)) {
1044 return;
1045 }
1046 if (type == SpanType::CustomSpan && !VerifyCustomSpanParameters(start, length)) {
1047 return;
1048 }
1049 CHECK_NULL_VOID(styleValueObj->IsObject());
1050 auto spanBase = ParseJsSpanBaseWithoutSpecialSpan(start, length, type, JSRef<JSObject>::Cast(styleValueObj), info);
1051 if (!spanBase) {
1052 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s",
1053 "AddSpan failed: maybe styledKey & corresponding value not match");
1054 return;
1055 }
1056 auto controller = GetMutableController().Upgrade();
1057 CHECK_NULL_VOID(controller);
1058 if (!CheckParameters(start, length)) {
1059 return;
1060 }
1061 if (type == SpanType::Image) {
1062 controller->RemoveSpan(start, length, SpanType::Image);
1063 } else if (type == SpanType::CustomSpan) {
1064 controller->RemoveSpan(start, length, SpanType::CustomSpan);
1065 } else if (type == SpanType::Gesture) {
1066 auto thisObj = info.This();
1067 auto newIndex = gestureStyleStoreIndex_.fetch_add(1);
1068 std::string key = "STYLED_STRING_GESTURESTYLE_STORE_" + std::to_string(newIndex);
1069 thisObj->SetPropertyObject(key.c_str(), styleValueObj);
1070 }
1071 controller->AddSpan(spanBase);
1072 }
1073
RemoveSpan(const JSCallbackInfo & info)1074 void JSMutableSpanString::RemoveSpan(const JSCallbackInfo& info)
1075 {
1076 if (info.Length() != 3 || !info[0]->IsNumber() || !info[1]->IsNumber() || !info[2]->IsNumber()) {
1077 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed.");
1078 return;
1079 }
1080 auto start = info[0]->ToNumber<int32_t>();
1081 auto length = info[1]->ToNumber<int32_t>();
1082 auto spanType = info[2]->ToNumber<int32_t>();
1083 if (!CheckSpanType(spanType)) {
1084 return;
1085 }
1086 auto type = static_cast<SpanType>(spanType);
1087 auto controller = GetMutableController().Upgrade();
1088 CHECK_NULL_VOID(controller);
1089 if (!CheckParameters(start, length)) {
1090 return;
1091 }
1092 controller->RemoveSpan(start, length, type);
1093 }
1094
RemoveSpans(const JSCallbackInfo & info)1095 void JSMutableSpanString::RemoveSpans(const JSCallbackInfo& info)
1096 {
1097 if (info.Length() != 2 || !info[0]->IsNumber() || !info[1]->IsNumber()) {
1098 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed.");
1099 return;
1100 }
1101 auto controller = GetMutableController().Upgrade();
1102 CHECK_NULL_VOID(controller);
1103 auto start = info[0]->ToNumber<int32_t>();
1104 auto length = info[1]->ToNumber<int32_t>();
1105 if (!CheckParameters(start, length)) {
1106 return;
1107 }
1108 controller->RemoveSpans(start, length);
1109 }
1110
ClearAllSpans()1111 void JSMutableSpanString::ClearAllSpans()
1112 {
1113 auto controller = GetMutableController().Upgrade();
1114 CHECK_NULL_VOID(controller);
1115 controller->ClearAllSpans();
1116 }
1117
ReplaceSpanString(const JSCallbackInfo & info)1118 void JSMutableSpanString::ReplaceSpanString(const JSCallbackInfo& info)
1119 {
1120 if (info.Length() != 3 || !info[0]->IsNumber() || !info[1]->IsNumber() || !info[2]->IsObject()) {
1121 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed.");
1122 return;
1123 }
1124 auto start = info[0]->ToNumber<int32_t>();
1125 auto length = info[1]->ToNumber<int32_t>();
1126 auto* spanString = JSRef<JSObject>::Cast(info[2])->Unwrap<JSSpanString>();
1127 if (!spanString) {
1128 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Failed To Parse StyledString");
1129 return;
1130 }
1131 auto spanStringController = spanString->GetController();
1132 CHECK_NULL_VOID(spanStringController);
1133 auto controller = GetMutableController().Upgrade();
1134 CHECK_NULL_VOID(controller);
1135 if (!CheckParameters(start, length)) {
1136 return;
1137 }
1138 auto thisObj = info.This();
1139 auto newIndex = spanStringStoreIndex_.fetch_add(1);
1140 std::string key = "STYLED_STRING_SPANSTRING_STORE_" + std::to_string(newIndex);
1141 thisObj->SetPropertyObject(key.c_str(), info[0]);
1142 controller->ReplaceSpanString(start, length, spanStringController);
1143 }
1144
InsertSpanString(const JSCallbackInfo & info)1145 void JSMutableSpanString::InsertSpanString(const JSCallbackInfo& info)
1146 {
1147 if (info.Length() != 2 || !info[0]->IsNumber() || !info[1]->IsObject()) {
1148 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed.");
1149 return;
1150 }
1151 auto start = info[0]->ToNumber<int32_t>();
1152 auto* spanString = JSRef<JSObject>::Cast(info[1])->Unwrap<JSSpanString>();
1153 if (!spanString) {
1154 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Failed To Parse StyledString");
1155 return;
1156 }
1157 auto spanStringController = spanString->GetController();
1158 CHECK_NULL_VOID(spanStringController);
1159 auto controller = GetMutableController().Upgrade();
1160 CHECK_NULL_VOID(controller);
1161 // The input parameter must not cross the boundary.
1162 auto characterLength = controller->GetLength();
1163 if (start < 0 || start > characterLength) {
1164 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s start: %d StyledStringLength: %d",
1165 "Out of bounds", start, characterLength);
1166 return;
1167 }
1168 auto thisObj = info.This();
1169 auto newIndex = spanStringStoreIndex_.fetch_add(1);
1170 std::string key = "STYLED_STRING_SPANSTRING_STORE_" + std::to_string(newIndex);
1171 thisObj->SetPropertyObject(key.c_str(), info[0]);
1172 controller->InsertSpanString(start, spanStringController);
1173 }
1174
AppendSpanString(const JSCallbackInfo & info)1175 void JSMutableSpanString::AppendSpanString(const JSCallbackInfo& info)
1176 {
1177 if (info.Length() != 1 || !info[0]->IsObject()) {
1178 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed.");
1179 return;
1180 }
1181 auto* spanString = JSRef<JSObject>::Cast(info[0])->Unwrap<JSSpanString>();
1182 if (!spanString) {
1183 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Failed To Parse StyledString");
1184 return;
1185 }
1186 auto spanStringController = spanString->GetController();
1187 CHECK_NULL_VOID(spanStringController);
1188 auto controller = GetMutableController().Upgrade();
1189 CHECK_NULL_VOID(controller);
1190 auto thisObj = info.This();
1191 auto newIndex = spanStringStoreIndex_.fetch_add(1);
1192 std::string key = "STYLED_STRING_SPANSTRING_STORE_" + std::to_string(newIndex);
1193 thisObj->SetPropertyObject(key.c_str(), info[0]);
1194 controller->AppendSpanString(spanStringController);
1195 }
1196
GetMutableController()1197 WeakPtr<MutableSpanString>& JSMutableSpanString::GetMutableController()
1198 {
1199 return mutableSpanString_;
1200 }
1201
SetMutableController(const RefPtr<MutableSpanString> & mutableSpanString)1202 void JSMutableSpanString::SetMutableController(const RefPtr<MutableSpanString>& mutableSpanString)
1203 {
1204 mutableSpanString_ = mutableSpanString;
1205 }
1206
1207 } // namespace OHOS::Ace::Framework