1 /*
2 * Copyright (c) 2025 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 #include "bridge/arkts_frontend/arkts_frontend.h"
16
17 #include <ani.h>
18
19 #include "interfaces/inner_api/ace/constants.h"
20
21 #include "base/subwindow/subwindow_manager.h"
22 #include "bridge/arkts_frontend/arkts_ani_utils.h"
23 #include "bridge/arkts_frontend/ani_context_module.h"
24 #include "bridge/arkts_frontend/entry/arkts_entry_loader.h"
25 #include "core/components_ng/pattern/stage/page_pattern.h"
26 #include "core/pipeline_ng/pipeline_context.h"
27
28 namespace OHOS::Ace {
RunPage(const std::shared_ptr<std::vector<uint8_t>> & content,const std::string & params)29 UIContentErrorCode ArktsFrontend::RunPage(
30 const std::shared_ptr<std::vector<uint8_t>>& content, const std::string& params)
31 {
32 return UIContentErrorCode::NO_ERRORS;
33 }
34
35 namespace {
36 /* copied from arkcompiler_ets_frontend vmloader.cc*/
37 struct AppInfo {
38 const char* className;
39 const char* createMethodName;
40 const char* createMethodSig;
41 const char* startMethodName;
42 const char* startMethodSig;
43 const char* enterMethodName;
44 const char* enterMethodSig;
45 const char* emitEventMethodName;
46 const char* emitEventMethodSig;
47 };
48 /* copied from arkcompiler_ets_frontend vmloader.cc*/
49 const AppInfo KOALA_APP_INFO = {
50 "Larkui/ArkUIEntry/Application;",
51 "createApplication",
52 "Lstd/core/String;Lstd/core/String;ZLstd/core/String;Larkui/UserView/UserView;Larkui/UserView/EntryPoint;"
53 ":Larkui/ArkUIEntry/Application;",
54 "start",
55 ":J",
56 "enter",
57 "IIJ:Z",
58 "emitEvent",
59 "IIII:V",
60 };
61
62 // void TryEmitError(EtsEnv& env)
63 // {
64 // if (env.ErrorCheck()) {
65 // env.ErrorDescribe();
66 // env.ErrorClear();
67 // }
68 // }
69
GetErrorProperty(ani_env * aniEnv,ani_error aniError,const char * property)70 std::string GetErrorProperty(ani_env* aniEnv, ani_error aniError, const char* property)
71 {
72 TAG_LOGD(AceLogTag::ACE_SUB_WINDOW, "called");
73 std::string propertyValue;
74 ani_status status = ANI_ERROR;
75 ani_type errorType = nullptr;
76 if ((status = aniEnv->Object_GetType(aniError, &errorType)) != ANI_OK) {
77 TAG_LOGE(AceLogTag::ACE_SUB_WINDOW, "Object_GetType failed, status : %{public}d", status);
78 return propertyValue;
79 }
80 ani_method getterMethod = nullptr;
81 if ((status = aniEnv->Class_FindGetter(static_cast<ani_class>(errorType), property, &getterMethod)) != ANI_OK) {
82 TAG_LOGE(AceLogTag::ACE_SUB_WINDOW, "Class_FindGetter failed, status : %{public}d", status);
83 return propertyValue;
84 }
85 ani_ref aniRef = nullptr;
86 if ((status = aniEnv->Object_CallMethod_Ref(aniError, getterMethod, &aniRef)) != ANI_OK) {
87 TAG_LOGE(AceLogTag::ACE_SUB_WINDOW, "Object_CallMethod_Ref failed, status : %{public}d", status);
88 return propertyValue;
89 }
90 ani_string aniString = reinterpret_cast<ani_string>(aniRef);
91 ani_size sz {};
92 if ((status = aniEnv->String_GetUTF8Size(aniString, &sz)) != ANI_OK) {
93 TAG_LOGE(AceLogTag::ACE_SUB_WINDOW, "String_GetUTF8Size failed, status : %{public}d", status);
94 return propertyValue;
95 }
96 propertyValue.resize(sz + 1);
97 if ((status = aniEnv->String_GetUTF8SubString(
98 aniString, 0, sz, propertyValue.data(), propertyValue.size(), &sz))!= ANI_OK) {
99 TAG_LOGE(AceLogTag::ACE_SUB_WINDOW, "String_GetUTF8SubString failed, status : %{public}d", status);
100 return propertyValue;
101 }
102 propertyValue.resize(sz);
103 return propertyValue;
104 }
105
RunArkoalaEventLoop(ani_env * env,ani_ref app)106 void RunArkoalaEventLoop(ani_env* env, ani_ref app)
107 {
108 ani_boolean errorExists;
109 env->ExistUnhandledError(&errorExists);
110 ani_status status;
111 ani_class appClass;
112 if ((status = env->FindClass(KOALA_APP_INFO.className, &appClass)) != ANI_OK) {
113 ani_error aniError;
114 env->GetUnhandledError(&aniError);
115 env->ResetError();
116 std::string errorMsg = GetErrorProperty(env, aniError, "message");
117 std::string errorName = GetErrorProperty(env, aniError, "name");
118 std::string errorStack = GetErrorProperty(env, aniError, "stack");
119 LOGE("[%{public}s] Cannot load main class %{public}s, status: %{public}d, \nerrorMsg: %{public}s, \nerrorName: "
120 "%{public}s, \nerrorStack: %{public}s",
121 __func__, KOALA_APP_INFO.className, status, errorMsg.c_str(), errorName.c_str(), errorStack.c_str());
122 return;
123 }
124
125 ani_method enter = nullptr;
126 if (env->Class_FindMethod(appClass, KOALA_APP_INFO.enterMethodName, KOALA_APP_INFO.enterMethodSig, &enter) !=
127 ANI_OK) {
128 LOGE("[%{public}s] Cannot find enter method %{public}s", __func__, KOALA_APP_INFO.enterMethodName);
129 // TryEmitError(env);
130 return;
131 }
132
133 ani_int arg0 = 0;
134 ani_int arg1 = 0;
135 ani_boolean result;
136 if (env->Object_CallMethod_Boolean(static_cast<ani_object>(app), enter, &result, arg0, arg1, nullptr) != ANI_OK) {
137 LOGE("[%{public}s] Call enter method failed", __func__);
138 return;
139 }
140 // auto terminate = env.CallBooleanMethod((ets_object)(app), (ets_method)(enter), (ets_int)0, (ets_int)0);
141 // TryEmitError(env);
142 // if (terminate) {
143 // exit(0);
144 // }
145 }
146 } // namespace
147
ArktsFrontend(void * runtime)148 ArktsFrontend::ArktsFrontend(void* runtime)
149 {
150 auto* env = reinterpret_cast<ani_env*>(runtime);
151 if (!env) {
152 LOGW("ArktsFrontend AniEnv is invalid!");
153 return;
154 }
155 type_ = FrontendType::ARK_TS;
156 env->GetVM(&vm_);
157 }
158
LegacyLoadPage(ani_env * env)159 ani_object LegacyLoadPage(ani_env* env)
160 {
161 do {
162 ani_status state;
163 ani_ref linkerRef;
164 if ((state = static_cast<ani_status>(ArktsAniUtils::GetNearestNonBootRuntimeLinker(env, linkerRef))) !=
165 ANI_OK) {
166 LOGE("Get getNearestNonBootRuntimeLinker failed, %{public}d", state);
167 break;
168 }
169
170 std::string entryPath = "entry/src/main/ets/pages/Index/ComExampleTrivialApplication";
171 ani_string entryClassStr;
172 env->String_NewUTF8(entryPath.c_str(), entryPath.length(), &entryClassStr);
173 ani_class entryClass = nullptr;
174 ani_ref entryClassRef = nullptr;
175
176 ani_class cls = nullptr;
177 if ((state = env->FindClass("Lstd/core/RuntimeLinker;", &cls)) != ANI_OK) {
178 LOGE("FindClass RuntimeLinker failed, %{public}d", state);
179 break;
180 }
181
182 ani_method loadClassMethod;
183 if ((state = env->Class_FindMethod(cls, "loadClass", "Lstd/core/String;Lstd/core/Boolean;:Lstd/core/Class;",
184 &loadClassMethod)) != ANI_OK) {
185 LOGE("Class_FindMethod loadClass failed, %{public}d", state);
186 break;
187 }
188
189 ani_object isInit;
190 if ((state = static_cast<ani_status>(ArktsAniUtils::CreateAniBoolean(env, false, isInit))) != ANI_OK) {
191 LOGE("Create Boolean object failed, %{public}d", state);
192 break;
193 }
194
195 if ((state = env->Object_CallMethod_Ref(
196 (ani_object)linkerRef, loadClassMethod, &entryClassRef, entryClassStr, isInit)) != ANI_OK) {
197 LOGE("Object_CallMethod_Ref loadClassMethod failed");
198 ani_error errorInfo;
199 env->GetUnhandledError(&errorInfo);
200 env->ResetError();
201 break;
202 }
203 entryClass = static_cast<ani_class>(entryClassRef);
204
205 ani_method entryMethod = nullptr;
206 if (env->Class_FindMethod(entryClass, "<ctor>", ":V", &entryMethod) != ANI_OK) {
207 LOGE("Class_FindMethod ctor failed");
208 break;
209 }
210
211 ani_object entryObject = nullptr;
212 if (env->Object_New(entryClass, entryMethod, &entryObject) != ANI_OK) {
213 LOGE("Object_New AbcRuntimeLinker failed");
214 break;
215 }
216 return entryObject;
217 } while (false);
218 return nullptr;
219 }
220
RunPage(const std::string & url,const std::string & params)221 UIContentErrorCode ArktsFrontend::RunPage(const std::string& url, const std::string& params)
222 {
223 auto* env_ = ArktsAniUtils::GetAniEnv(vm_);
224 CHECK_NULL_RETURN(env_, UIContentErrorCode::INVALID_URL);
225
226 ani_class appClass;
227 EntryLoader entryLoader(url, env_);
228
229 pageRouterManager_ = NG::PageRouterManagerFactory::CreateManager();
230
231 if (env_->FindClass(KOALA_APP_INFO.className, &appClass) != ANI_OK) {
232 LOGE("Cannot load main class %{public}s", KOALA_APP_INFO.className);
233 return UIContentErrorCode::INVALID_URL;
234 }
235
236 ani_static_method create;
237 if (env_->Class_FindStaticMethod(
238 appClass, KOALA_APP_INFO.createMethodName, KOALA_APP_INFO.createMethodSig, &create) != ANI_OK) {
239 LOGE("Cannot find create method %{public}s", KOALA_APP_INFO.createMethodName);
240 // TryEmitError(*env_);
241 return UIContentErrorCode::INVALID_URL;
242 }
243
244 ani_string aniUrl;
245 env_->String_NewUTF8(url.c_str(), url.size(), &aniUrl);
246 ani_string aniParams;
247 env_->String_NewUTF8(params.c_str(), params.size(), &aniParams);
248
249 ani_ref appLocal;
250 ani_ref optionalEntry;
251 env_->GetUndefined(&optionalEntry);
252 auto entryPointObj = entryLoader.GetPageEntryObj();
253 auto legacyEntryPointObj = LegacyLoadPage(env_);
254 auto container = Container::Current();
255 CHECK_NULL_RETURN(container, UIContentErrorCode::INVALID_URL);
256 std::string moduleName = container->GetModuleName();
257 ani_string module;
258 env_->String_NewUTF8(moduleName.c_str(), moduleName.size(), &module);
259 if (env_->Class_CallStaticMethod_Ref(appClass, create, &appLocal, aniUrl, aniParams, false, module,
260 legacyEntryPointObj ? legacyEntryPointObj : optionalEntry,
261 entryPointObj ? entryPointObj : optionalEntry) != ANI_OK) {
262 LOGE("createApplication returned null");
263 // TryEmitError(*env_);
264 return UIContentErrorCode::INVALID_URL;
265 }
266
267 env_->GlobalReference_Create(appLocal, &app_);
268
269 ani_method start;
270 if (env_->Class_FindMethod(appClass, KOALA_APP_INFO.startMethodName, KOALA_APP_INFO.startMethodSig, &start) !=
271 ANI_OK) {
272 LOGE("find start method returned null");
273 // TryEmitError(*env_);
274 return UIContentErrorCode::INVALID_URL;
275 }
276
277 ani_long result;
278 if (env_->Object_CallMethod_Long(static_cast<ani_object>(app_), start, &result) != ANI_OK) {
279 LOGE("call start method returned null");
280 // TryEmitError(*env_);
281 return UIContentErrorCode::INVALID_URL;
282 }
283
284 // TODO: init event loop
285 CHECK_NULL_RETURN(pipeline_, UIContentErrorCode::NULL_POINTER);
286 pipeline_->SetVsyncListener([env = env_, app = app_]() { RunArkoalaEventLoop(env, app); });
287
288 return UIContentErrorCode::NO_ERRORS;
289 }
290
AttachPipelineContext(const RefPtr<PipelineBase> & context)291 void ArktsFrontend::AttachPipelineContext(const RefPtr<PipelineBase>& context)
292 {
293 pipeline_ = DynamicCast<NG::PipelineContext>(context);
294 if (accessibilityManager_) {
295 accessibilityManager_->SetPipelineContext(context);
296 accessibilityManager_->InitializeCallback();
297 }
298 }
299
OnBackPressed()300 bool ArktsFrontend::OnBackPressed()
301 {
302 CHECK_NULL_RETURN(pageRouterManager_, false);
303 auto pageNode = pageRouterManager_->GetCurrentPageNode();
304 CHECK_NULL_RETURN(pageNode, false);
305 auto pagePattern = pageNode->GetPattern<NG::PagePattern>();
306 CHECK_NULL_RETURN(pagePattern, false);
307 if (pagePattern->OnBackPressed()) {
308 return true;
309 }
310 return pageRouterManager_->Pop();
311 }
312
OnShow()313 void ArktsFrontend::OnShow()
314 {
315 CHECK_NULL_VOID(pageRouterManager_);
316 auto pageNode = pageRouterManager_->GetCurrentPageNode();
317 CHECK_NULL_VOID(pageNode);
318 auto pagePattern = pageNode->GetPattern<NG::PagePattern>();
319 CHECK_NULL_VOID(pagePattern);
320 pagePattern->OnShow();
321 }
322
OnHide()323 void ArktsFrontend::OnHide()
324 {
325 CHECK_NULL_VOID(pageRouterManager_);
326 auto pageNode = pageRouterManager_->GetCurrentPageNode();
327 CHECK_NULL_VOID(pageNode);
328 auto pagePattern = pageNode->GetPattern<NG::PagePattern>();
329 CHECK_NULL_VOID(pagePattern);
330 pagePattern->OnHide();
331 }
332
GetShared(int32_t id)333 void* ArktsFrontend::GetShared(int32_t id)
334 {
335 int32_t currentInstance = id;
336 if (currentInstance >= MIN_SUBCONTAINER_ID && currentInstance < MIN_PLUGIN_SUBCONTAINER_ID) {
337 currentInstance = SubwindowManager::GetInstance()->GetParentContainerId(currentInstance);
338 }
339 auto it = storageMap_.find(currentInstance);
340 if (it == storageMap_.end()) {
341 LOGW("LocalStorage with ID %{public}d not found!", currentInstance);
342 return nullptr;
343 }
344 return it->second;
345 }
346
Destroy()347 void ArktsFrontend::Destroy()
348 {
349 CHECK_NULL_VOID(vm_);
350 auto* env = ArktsAniUtils::GetAniEnv(vm_);
351 CHECK_NULL_VOID(env);
352 env->GlobalReference_Delete(app_);
353 app_ = nullptr;
354 }
355
CallGetUIContextFunc()356 ani_object ArktsFrontend::CallGetUIContextFunc()
357 {
358 ani_object result = nullptr;
359 ani_status status;
360
361 auto* env = ArktsAniUtils::GetAniEnv(vm_);
362 CHECK_NULL_RETURN(env, result);
363
364 ani_class uiContextClass;
365 if ((status = env->FindClass("L@ohos/arkui/UIContext/UIContext;", &uiContextClass)) != ANI_OK) {
366 LOGE("FindClass UIContext failed, %{public}d", status);
367 return result;
368 }
369 ani_method uiContextClassCtor;
370 if ((status = env->Class_FindMethod(uiContextClass, "<ctor>", "I:V", &uiContextClassCtor)) != ANI_OK) {
371 LOGE("Class_FindMethod UIContext ctor failed, %{public}d", status);
372 return result;
373 }
374 ani_int instanceId = 100000;
375 if ((status = env->Object_New(uiContextClass, uiContextClassCtor, &result, instanceId)) != ANI_OK) {
376 LOGE("New UIContext object failed, %{public}d", status);
377 return result;
378 }
379 return result;
380 }
381
GetEnv()382 void* ArktsFrontend::GetEnv()
383 {
384 return ArktsAniUtils::GetAniEnv(vm_);
385 }
386
387 void* ArktsFrontend::preloadArkTSRuntime = nullptr;
PreloadAceModule(void * aniEnv)388 void ArktsFrontend::PreloadAceModule(void* aniEnv)
389 {
390 ArktsFrontend::preloadArkTSRuntime = aniEnv;
391 }
392
OHOS_ACE_PreloadAceArkTSModule(void * aniEnv)393 extern "C" ACE_FORCE_EXPORT void OHOS_ACE_PreloadAceArkTSModule(void* aniEnv)
394 {
395 ArktsFrontend::PreloadAceModule(aniEnv);
396 }
397
SetAniContext(int32_t instanceId,ani_ref * context)398 void ArktsFrontend::SetAniContext(int32_t instanceId, ani_ref* context)
399 {
400 Framework::AniContextModule::AddAniContext(instanceId, context);
401 }
402 } // namespace OHOS::Ace
403