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