• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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