• 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 <ani.h>
16 #include <string>
17 #include <unistd.h>
18 #include "base/log/log_wrapper.h"
19 #include "base/memory/ace_type.h"
20 #include "bridge/arkts_frontend/arkts_frontend.h"
21 #include "core/components_ng/base/inspector.h"
22 #include "core/pipeline_ng/pipeline_context.h"
23 
24 namespace {
25 const char LAYOUT_TYPE[] = "layout";
26 const char DRAW_TYPE[] = "draw";
27 const char ANI_INSPECTOR_NS[] = "L@ohos/arkui/inspector/inspector;";
28 const char ANI_COMPONENT_OBSERVER_CLS[] = "L@ohos/arkui/inspector/inspector/ComponentObserverImpl;";
29 const char KOALA_INSPECTOR_CLS[] = "L@koalaui/arkts-arkui/generated/arkts/ohos/arkui/inspector/Inspector;";
30 const char KOALA_COMPONENT_CLS[] = "L@koalaui/arkts-arkui/generated/arkts/ohos/arkui/inspector/ComponentObserver;";
31 } // namespace
32 
33 namespace OHOS::Ace {
34 class ComponentObserver {
35 public:
ComponentObserver(std::string key)36     explicit ComponentObserver(std::string key): id_(key) {}
37 
getFronted()38     static RefPtr<ArktsFrontend> getFronted()
39     {
40         auto context = NG::PipelineContext::GetCurrentContextSafely();
41         if (context == nullptr) {
42             TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani can not get current context.");
43             return nullptr;
44         }
45         auto frontend = context->GetFrontend();
46         if (frontend == nullptr) {
47             TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani can not get current frontend.");
48             return nullptr;
49         }
50         auto arkTsFrontend = AceType::DynamicCast<ArktsFrontend>(frontend);
51         if (frontend == nullptr) {
52             TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani can not convert to arkts frontend.");
53             return nullptr;
54         }
55         return arkTsFrontend;
56     }
57 
FindCbList(ani_ref & cb,const std::string & eventType,ani_env * env)58     std::list<ani_ref>::iterator FindCbList(ani_ref& cb, const std::string& eventType, ani_env* env)
59     {
60         if (strcmp(LAYOUT_TYPE, eventType.c_str()) == 0) {
61             return std::find_if(cbLayoutList_.begin(), cbLayoutList_.end(), [env, cb](const ani_ref& item) -> bool {
62                 ani_boolean rs;
63                 env->Reference_StrictEquals(cb, item, &rs);
64                 return rs == ANI_TRUE;
65             });
66         } else {
67             return std::find_if(cbDrawList_.begin(), cbDrawList_.end(), [env, cb](const ani_ref& item) -> bool {
68                 ani_boolean rs;
69                 env->Reference_StrictEquals(cb, item, &rs);
70                 return rs == ANI_TRUE;
71             });
72         }
73     }
74 
AddCallbackToList(std::list<ani_ref> & fnList,ani_ref & cb,const std::string & eventType,ani_env * env)75     void AddCallbackToList(std::list<ani_ref>& fnList, ani_ref& cb, const std::string& eventType, ani_env* env)
76     {
77         auto iter = FindCbList(cb, eventType, env);
78         if (iter != fnList.end()) {
79             return;
80         }
81         TAG_LOGI(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani add %{public}s call back on %{public}s",
82             eventType.c_str(), id_.c_str());
83         fnList.emplace_back(cb);
84     }
85 
RemoveCallbackToList(std::list<ani_ref> & fnList,ani_ref & cb,const std::string & eventType,ani_env * env)86     void RemoveCallbackToList(std::list<ani_ref>& fnList, ani_ref& cb, const std::string& eventType, ani_env* env)
87     {
88         if (cb == nullptr) {
89             TAG_LOGW(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani start to clear all %{public}s callback list",
90                 eventType.c_str());
91             for (auto& ref : fnList) {
92                 env->GlobalReference_Delete(ref);
93             }
94             fnList.clear();
95         } else {
96             auto iter = FindCbList(cb, eventType, env);
97             if (iter != fnList.end()) {
98                 auto& deleteRef = *iter;
99                 fnList.erase(iter);
100                 env->GlobalReference_Delete(deleteRef);
101             }
102         }
103         if (fnList.empty()) {
104             auto arkTsFrontend = ComponentObserver::getFronted();
105             if (arkTsFrontend == nullptr) {
106                 TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani Can not convert to arkts frontend.");
107                 return;
108             }
109             if (strcmp(LAYOUT_TYPE, eventType.c_str()) == 0) {
110                 arkTsFrontend->UnregisterLayoutInspectorCallback(id_);
111             } else {
112                 arkTsFrontend->UnregisterDrawInspectorCallback(id_);
113             }
114         }
115     }
116 
CallUserFunction(ani_env * env,std::list<ani_ref> & cbList)117     void CallUserFunction(ani_env* env, std::list<ani_ref>& cbList)
118     {
119         std::vector<ani_ref> vec;
120         ani_ref fnReturnVal;
121         for (auto& cb : cbList) {
122             TAG_LOGD(AceLogTag::ACE_LAYOUT_INSPECTOR,
123                 "inspector-ani start to call user function for component %{public}s", id_.c_str());
124             env->FunctionalObject_Call(reinterpret_cast<ani_fn_object>(cb), vec.size(), vec.data(), &fnReturnVal);
125         }
126     }
127 
GetCbListByType(const std::string & eventType)128     std::list<ani_ref>&  GetCbListByType(const std::string& eventType)
129     {
130         if (strcmp(LAYOUT_TYPE, eventType.c_str()) == 0) {
131             return cbLayoutList_;
132         }
133         return cbDrawList_;
134     }
135 
GetInspectorFuncByType(const std::string & eventType)136     RefPtr<InspectorEvent> GetInspectorFuncByType(const std::string& eventType)
137     {
138         if (strcmp(LAYOUT_TYPE, eventType.c_str()) == 0) {
139             return layoutEvent_;
140         }
141         return drawEvent_;
142     }
143 
SetInspectorFuncByType(const std::string & eventType,const RefPtr<InspectorEvent> & fun)144     void SetInspectorFuncByType(const std::string& eventType, const RefPtr<InspectorEvent>& fun)
145     {
146         if (strcmp(LAYOUT_TYPE, eventType.c_str()) == 0) {
147             layoutEvent_ = fun;
148         } else {
149             drawEvent_ = fun;
150         }
151     }
152 private:
153     std::string id_;
154     std::list<ani_ref> cbLayoutList_;
155     std::list<ani_ref> cbDrawList_;
156     RefPtr<InspectorEvent> layoutEvent_;
157     RefPtr<InspectorEvent> drawEvent_;
158 };
159 
Unwrapp(ani_env * env,ani_object object)160 static ComponentObserver* Unwrapp(ani_env *env, ani_object object)
161 {
162     ani_long nativeAddr;
163     if (ANI_OK != env->Object_GetFieldByName_Long(object, "nativeComponentObserver", &nativeAddr)) {
164         return nullptr;
165     }
166     return reinterpret_cast<ComponentObserver *>(nativeAddr);
167 }
168 
ANIUtils_ANIStringToStdString(ani_env * env,ani_string ani_str,std::string & str)169 ani_status ANIUtils_ANIStringToStdString(ani_env *env, ani_string ani_str, std::string& str)
170 {
171     ani_size strSize;
172     ani_status status = env->String_GetUTF8Size(ani_str, &strSize);
173     if (status != ANI_OK) {
174         TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani String_GetUTF8Size failed %{public}d.", status);
175         return status;
176     }
177 
178     std::vector<char> buffer(strSize + 1); // +1 for null terminator
179     char* utf8Buffer = buffer.data();
180 
181     ani_size bytes_written = 0;
182     status = env->String_GetUTF8(ani_str, utf8Buffer, buffer.size(), &bytes_written);
183     if (status != ANI_OK) {
184         TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani String_GetUTF8 failed %{public}d.", status);
185         return status;
186     }
187 
188     utf8Buffer[bytes_written] = '\0';
189     str = std::string(utf8Buffer);
190     return status;
191 }
192 
On(ani_env * env,ani_object object,ani_string type,ani_fn_object fnObj)193 static void On(ani_env *env, ani_object object, ani_string type, ani_fn_object fnObj)
194 {
195     if (fnObj == nullptr) {
196         TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani callback is undefined.");
197         return;
198     }
199     std::string typeStr;
200     ANIUtils_ANIStringToStdString(env, type, typeStr);
201     if (strcmp(LAYOUT_TYPE, typeStr.c_str()) != 0 && strcmp(DRAW_TYPE, typeStr.c_str()) != 0) {
202         TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani method on not support event type %{public}s",
203             typeStr.c_str());
204         return;
205     }
206 
207     auto *observer = Unwrapp(env, object);
208     if (observer == nullptr) {
209         TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani context is null.");
210         return;
211     }
212     ani_ref fnObjGlobalRef = nullptr;
213     env->GlobalReference_Create(reinterpret_cast<ani_ref>(fnObj), &fnObjGlobalRef);
214     observer->AddCallbackToList(observer->GetCbListByType(typeStr), fnObjGlobalRef, typeStr, env);
215 }
216 
Off(ani_env * env,ani_object object,ani_string type,ani_fn_object fnObj)217 static void Off(ani_env *env, ani_object object, ani_string type, ani_fn_object fnObj)
218 {
219     std::string typeStr;
220     ANIUtils_ANIStringToStdString(env, type, typeStr);
221     if (strcmp(LAYOUT_TYPE, typeStr.c_str()) != 0 && strcmp(DRAW_TYPE, typeStr.c_str()) != 0) {
222         TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani method on not support event type %{public}s",
223             typeStr.c_str());
224         return;
225     }
226 
227     auto *observer = Unwrapp(env, object);
228     if (observer == nullptr) {
229         TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani context is null.");
230         return;
231     }
232     ani_ref fnObjGlobalRef = nullptr;
233     env->GlobalReference_Create(reinterpret_cast<ani_ref>(fnObj), &fnObjGlobalRef);
234     observer->RemoveCallbackToList(observer->GetCbListByType(typeStr), fnObjGlobalRef, typeStr, env);
235 }
236 
AniSendEventByKey(ani_env * env,ani_string id,ani_double action,ani_string params)237 static ani_boolean AniSendEventByKey(ani_env *env, ani_string id, ani_double action, ani_string params)
238 {
239     std::string keyStr;
240     ani_status status = ANIUtils_ANIStringToStdString(env, id, keyStr);
241     if (status != ANI_OK) {
242         TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani get send event key error.");
243         return ANI_FALSE;
244     }
245     std::string paramsStr;
246     status = ANIUtils_ANIStringToStdString(env, params, paramsStr);
247     if (status != ANI_OK) {
248         TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani get send event params error.");
249         return ANI_FALSE;
250     }
251     ContainerScope scope {Container::CurrentIdSafelyWithCheck()};
252     bool result = NG::Inspector::SendEventByKey(keyStr, action, paramsStr);
253     if (result) {
254         return ANI_TRUE;
255     }
256     return ANI_FALSE;
257 }
258 
AniGetInspectorTree(ani_env * env)259 static ani_object AniGetInspectorTree(ani_env *env)
260 {
261     ContainerScope scope {Container::CurrentIdSafelyWithCheck()};
262     std::string resultStr = NG::Inspector::GetInspector(false);
263     if (resultStr.empty()) {
264         TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani inspector tree is empty.");
265         return nullptr;
266     }
267     ani_string aniResult;
268     ani_status status = env->String_NewUTF8(resultStr.c_str(), resultStr.size(), &aniResult);
269     if (ANI_OK != status) {
270         TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani Can not convert string to ani_string.");
271         return nullptr;
272     }
273     return aniResult;
274 }
275 
AniGetInspectorByKey(ani_env * env,ani_string key)276 static ani_string AniGetInspectorByKey(ani_env *env, ani_string key)
277 {
278     std::string keyStr;
279     ani_status getStdStringStatus = ANIUtils_ANIStringToStdString(env, key, keyStr);
280     if (getStdStringStatus != ANI_OK) {
281         TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani get key failed.");
282         return nullptr;
283     }
284     ContainerScope scope{Container::CurrentIdSafelyWithCheck()};
285     std::string resultStr = NG::Inspector::GetInspectorNodeByKey(keyStr);
286     if (resultStr.empty()) {
287         TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani node %{public}s is empty.", keyStr.c_str());
288         return nullptr;
289     }
290     ani_string ani_str;
291     ani_status status = env->String_NewUTF8(resultStr.c_str(), resultStr.size(), &ani_str);
292     if (ANI_OK != status) {
293         TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani Can not convert string to ani_string.");
294         return nullptr;
295     }
296     return ani_str;
297 }
298 
CreateComponentObserver(ani_env * env,ani_string id,const char * className)299 static ani_object CreateComponentObserver(ani_env *env, ani_string id, const char *className)
300 {
301     ani_class cls;
302     if (ANI_OK != env->FindClass(className, &cls)) {
303         TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani not found class");
304         return nullptr;
305     }
306 
307     ani_method ctor;
308     if (ANI_OK != env->Class_FindMethod(cls, "<ctor>", nullptr, &ctor)) {
309         TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani can not get construct method.");
310         return nullptr;
311     }
312 
313     std::string key;
314     ani_status status = ANIUtils_ANIStringToStdString(env, id, key);
315     if (status != ANI_OK) {
316         TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani get key of observer failed.");
317         return nullptr;
318     }
319     TAG_LOGI(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani start to CreateComponentObserver key is %{public}s",
320         key.c_str());
321 
322     auto arkTsFrontend = ComponentObserver::getFronted();
323     if (arkTsFrontend == nullptr) {
324         TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani Can not convert to arkts frontend.");
325         return nullptr;
326     }
327     auto* observer = new ComponentObserver(key);
328 
329     ani_object context_object;
330     if (ANI_OK != env->Object_New(cls, ctor, &context_object, reinterpret_cast<ani_long>(observer))) {
331         TAG_LOGE(AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani Can not new object.");
332         delete observer;
333         return nullptr;
334     }
335 
336     auto layoutCallback = [observer, env]() -> void {
337         observer->CallUserFunction(env, observer->GetCbListByType(LAYOUT_TYPE));
338     };
339     observer->SetInspectorFuncByType(LAYOUT_TYPE, AceType::MakeRefPtr<InspectorEvent>(std::move(layoutCallback)));
340 
341     auto drawCallback = [observer, env]() -> void {
342         observer->CallUserFunction(env, observer->GetCbListByType(DRAW_TYPE));
343     };
344     observer->SetInspectorFuncByType(DRAW_TYPE, AceType::MakeRefPtr<InspectorEvent>(std::move(drawCallback)));
345 
346     arkTsFrontend->RegisterLayoutInspectorCallback(observer->GetInspectorFuncByType(LAYOUT_TYPE), key);
347     arkTsFrontend->RegisterDrawInspectorCallback(observer->GetInspectorFuncByType(DRAW_TYPE), key);
348     return context_object;
349 }
350 
CreateComponentObserverForAni(ani_env * env,ani_string id)351 static ani_object CreateComponentObserverForAni(ani_env *env, ani_string id)
352 {
353     return CreateComponentObserver(env, id, ANI_COMPONENT_OBSERVER_CLS);
354 }
355 
CreateComponentObserverForKoala(ani_env * env,ani_object object,ani_string id)356 static ani_object CreateComponentObserverForKoala(ani_env *env, [[maybe_unused]] ani_object object, ani_string id)
357 {
358     return CreateComponentObserver(env, id, KOALA_COMPONENT_CLS);
359 }
360 } // namespace OHOS::Ace
361 
ANI_ConstructorForAni(ani_env * env)362 bool ANI_ConstructorForAni(ani_env *env)
363 {
364     ani_namespace ns;
365     if (ANI_OK != env->FindNamespace(ANI_INSPECTOR_NS, &ns)) {
366         TAG_LOGE(OHOS::Ace::AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani Not found ns");
367         return false;
368     }
369     std::array methods = {
370         ani_native_function {"createComponentObserver", nullptr,
371             reinterpret_cast<void *>(OHOS::Ace::CreateComponentObserverForAni)},
372         ani_native_function {"getInspectorByKey", nullptr,
373             reinterpret_cast<void *>(OHOS::Ace::AniGetInspectorByKey)},
374         ani_native_function {"sendEventByKey", nullptr,
375             reinterpret_cast<void *>(OHOS::Ace::AniSendEventByKey)},
376         ani_native_function {"getInspectorTree", nullptr,
377             reinterpret_cast<void *>(OHOS::Ace::AniGetInspectorTree)},
378     };
379 
380     if (ANI_OK != env->Namespace_BindNativeFunctions(ns, methods.data(), methods.size())) {
381         TAG_LOGE(OHOS::Ace::AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani Namespace_BindNativeFunctions error");
382         return false;
383     }
384 
385     ani_class clsInspector;
386     if (ANI_OK != env->FindClass(ANI_COMPONENT_OBSERVER_CLS, &clsInspector)) {
387         TAG_LOGE(OHOS::Ace::AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani not found class");
388         return false;
389     }
390 
391     std::array methodsInspector = {
392         ani_native_function {"on", nullptr, reinterpret_cast<void *>(OHOS::Ace::On)},
393         ani_native_function {"off", nullptr, reinterpret_cast<void *>(OHOS::Ace::Off)},
394     };
395 
396     if (ANI_OK != env->Class_BindNativeMethods(clsInspector, methodsInspector.data(), methodsInspector.size())) {
397         TAG_LOGE(OHOS::Ace::AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani Class_BindNativeFunctions error");
398         return false;
399     }
400     return true;
401 }
402 
ANI_ConstructorForKoala(ani_env * env)403 bool ANI_ConstructorForKoala(ani_env *env)
404 {
405     ani_class clsInspector;
406     if (ANI_OK != env->FindClass(KOALA_INSPECTOR_CLS, &clsInspector)) {
407         TAG_LOGE(OHOS::Ace::AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-koala not found class");
408         return false;
409     }
410     std::array methodsInspector = {
411         ani_native_function {"createComponentObserver", nullptr,
412             reinterpret_cast<void *>(OHOS::Ace::CreateComponentObserverForKoala)},
413     };
414     if (ANI_OK != env->Class_BindNativeMethods(clsInspector, methodsInspector.data(), methodsInspector.size())) {
415         TAG_LOGE(OHOS::Ace::AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-koala Class_BindNativeFunctions error");
416         return false;
417     }
418 
419     ani_class clsObserver;
420     if (ANI_OK != env->FindClass(KOALA_COMPONENT_CLS, &clsObserver)) {
421         TAG_LOGE(OHOS::Ace::AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-koala not found class");
422         return false;
423     }
424     std::array methodsObserver = {
425         ani_native_function {"on", nullptr, reinterpret_cast<void *>(OHOS::Ace::On)},
426         ani_native_function {"off", nullptr, reinterpret_cast<void *>(OHOS::Ace::Off)},
427     };
428     if (ANI_OK != env->Class_BindNativeMethods(clsObserver, methodsObserver.data(), methodsObserver.size())) {
429         TAG_LOGE(OHOS::Ace::AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-koala Class_BindNativeFunctions error");
430         return false;
431     }
432     return true;
433 }
434 
ANI_Constructor(ani_vm * vm,uint32_t * result)435 ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result)
436 {
437     ani_env *env;
438     if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) {
439         TAG_LOGE(OHOS::Ace::AceLogTag::ACE_LAYOUT_INSPECTOR, "inspector-ani Unsupported ANI_VERSION_1");
440         return ANI_ERROR;
441     }
442     if (ANI_ConstructorForAni(env) || ANI_ConstructorForKoala(env)) {
443         *result = ANI_VERSION_1;
444         return ANI_OK;
445     }
446     return ANI_ERROR;
447 }