• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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 "js_inspector.h"
16 
17 #include <algorithm>
18 
19 #include "napi/native_common.h"
20 #include "napi/native_node_api.h"
21 
22 #include "base/log/log.h"
23 #include "base/memory/referenced.h"
24 
25 namespace OHOS::Ace::Napi {
26 namespace {
27 constexpr size_t STR_BUFFER_SIZE = 1024;
28 constexpr uint8_t PARA_COUNT = 2;
29 } // namespace
30 
GetObserver(napi_env env,napi_value thisVar)31 static ComponentObserver* GetObserver(napi_env env, napi_value thisVar)
32 {
33     ComponentObserver* observer = nullptr;
34     napi_unwrap(env, thisVar, (void**)&observer);
35     if (observer->thisVarRef_ == nullptr) {
36         observer->Initialize(env, thisVar);
37     }
38     return observer;
39 }
40 
ParseArgs(napi_env & env,napi_callback_info & info,napi_value & thisVar,napi_value & cb,CalloutType & calloutType)41 static size_t ParseArgs(
42     napi_env& env, napi_callback_info& info, napi_value& thisVar, napi_value& cb, CalloutType& calloutType)
43 {
44     const size_t argNum = 2;
45     size_t argc = argNum;
46     napi_value argv[argNum] = { 0 };
47     void* data = nullptr;
48     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
49     NAPI_ASSERT_BASE(env, argc > 0, "too few parameter", 0);
50 
51     napi_valuetype napiType;
52     NAPI_CALL_BASE(env, napi_typeof(env, argv[0], &napiType), 0);
53     NAPI_ASSERT_BASE(env, napiType == napi_string, "parameter 1 should be string", 0);
54     char type[STR_BUFFER_SIZE] = { 0 };
55     size_t len = 0;
56     napi_get_value_string_utf8(env, argv[0], type, STR_BUFFER_SIZE, &len);
57     NAPI_ASSERT_BASE(env, len < STR_BUFFER_SIZE, "condition string too long", 0);
58     NAPI_ASSERT_BASE(
59         env, (strcmp("layout", type) == 0 || strcmp("draw", type) == 0), "type mismatch('layout' or 'draw')", 0);
60     if (strcmp("layout", type) == 0) {
61         calloutType = CalloutType::LAYOUTCALLOUT;
62     } else if (strcmp("draw", type) == 0) {
63         calloutType = CalloutType::DRAWCALLOUT;
64     } else {
65         calloutType = CalloutType::UNKNOW;
66     }
67 
68     if (argc <= 1) {
69         return argc;
70     }
71 
72     NAPI_CALL_BASE(env, napi_typeof(env, argv[1], &napiType), 0);
73     NAPI_ASSERT_BASE(env, napiType == napi_function, "type mismatch for parameter 2", 0);
74     cb = argv[1];
75     return argc;
76 }
77 
callUserFunction(std::list<napi_ref> & cbList)78 void ComponentObserver::callUserFunction(std::list<napi_ref>& cbList)
79 {
80     for (auto& cbRef : cbList) {
81         napi_handle_scope scope = nullptr;
82         napi_open_handle_scope(env_, &scope);
83         if (scope == nullptr) {
84             return;
85         }
86         napi_value thisVal = nullptr;
87         napi_get_reference_value(env_, thisVarRef_, &thisVal);
88 
89         napi_value cb = nullptr;
90         napi_get_reference_value(env_, cbRef, &cb);
91 
92         napi_value resultArg = nullptr;
93         napi_value result = nullptr;
94         napi_call_function(env_, thisVal, cb, 1, &resultArg, &result);
95         napi_close_handle_scope(env_, scope);
96     }
97 }
98 
FindCbList(napi_value cb,CalloutType calloutType)99 std::list<napi_ref>::iterator ComponentObserver::FindCbList(napi_value cb, CalloutType calloutType)
100 {
101     if (calloutType == CalloutType::LAYOUTCALLOUT) {
102         return std::find_if(cbLayoutList_.begin(), cbLayoutList_.end(), [env = env_, cb](const napi_ref& item) -> bool {
103             bool result = false;
104             napi_value refItem;
105             napi_get_reference_value(env, item, &refItem);
106             napi_strict_equals(env, refItem, cb, &result);
107             return result;
108         });
109     } else {
110         return std::find_if(cbDrawList_.begin(), cbDrawList_.end(), [env = env_, cb](const napi_ref& item) -> bool {
111             bool result = false;
112             napi_value refItem;
113             napi_get_reference_value(env, item, &refItem);
114             napi_strict_equals(env, refItem, cb, &result);
115             return result;
116         });
117     }
118 }
119 
AddCallbackToList(napi_value cb,std::list<napi_ref> & cbList,CalloutType calloutType,napi_env env,napi_handle_scope scope)120 void ComponentObserver::AddCallbackToList(
121     napi_value cb, std::list<napi_ref>& cbList, CalloutType calloutType, napi_env env, napi_handle_scope scope)
122 {
123     auto iter = FindCbList(cb, calloutType);
124     if (iter != cbList.end()) {
125         napi_close_handle_scope(env, scope);
126         return;
127     }
128     napi_ref ref = nullptr;
129     napi_create_reference(env, cb, 1, &ref);
130     cbList.emplace_back(ref);
131     napi_close_handle_scope(env, scope);
132 }
133 
DeleteCallbackFromList(size_t argc,std::list<napi_ref> & cbList,CalloutType calloutType,napi_value cb,napi_env env)134 void ComponentObserver::DeleteCallbackFromList(
135     size_t argc, std::list<napi_ref>& cbList, CalloutType calloutType, napi_value cb, napi_env env)
136 {
137     if (argc == 1) {
138         for (auto& item : cbList) {
139             napi_delete_reference(env_, item);
140         }
141         cbList.clear();
142     } else {
143         NAPI_ASSERT_RETURN_VOID(env, (argc == PARA_COUNT && cb != nullptr), "Invalid arguments");
144         auto iter = FindCbList(cb, calloutType);
145         if (iter != cbList.end()) {
146             napi_delete_reference(env_, *iter);
147             cbList.erase(iter);
148         }
149     }
150 }
151 
FunctionOn(napi_env & env,napi_value result,const char * funName)152 void ComponentObserver::FunctionOn(napi_env& env, napi_value result, const char* funName)
153 {
154     napi_value funcValue = nullptr;
155     auto On = [](napi_env env, napi_callback_info info) -> napi_value {
156         auto jsEngine = EngineHelper::GetCurrentEngine();
157         if (!jsEngine) {
158             LOGE("get jsEngine failed");
159             return nullptr;
160         }
161 
162         napi_handle_scope scope = nullptr;
163         napi_open_handle_scope(env, &scope);
164         if (scope == nullptr) {
165             return nullptr;
166         }
167         napi_value thisVar = nullptr;
168         napi_value cb = nullptr;
169         CalloutType calloutType = CalloutType::UNKNOW;
170         size_t argc = ParseArgs(env, info, thisVar, cb, calloutType);
171         NAPI_ASSERT(env, (argc == 2 && thisVar != nullptr && cb != nullptr), "Invalid arguments");
172 
173         ComponentObserver* observer = GetObserver(env, thisVar);
174         if (!observer) {
175             LOGE("observer is null");
176             napi_close_handle_scope(env, scope);
177             return nullptr;
178         }
179 
180         if (calloutType == CalloutType::LAYOUTCALLOUT) {
181             observer->AddCallbackToList(cb, observer->cbLayoutList_, calloutType, env, scope);
182         } else if (calloutType == CalloutType::DRAWCALLOUT) {
183             observer->AddCallbackToList(cb, observer->cbDrawList_, calloutType, env, scope);
184         }
185         return nullptr;
186     };
187     napi_create_function(env, funName, NAPI_AUTO_LENGTH, On, nullptr, &funcValue);
188     napi_set_named_property(env, result, funName, funcValue);
189 }
190 
FunctionOff(napi_env & env,napi_value result,const char * funName)191 void ComponentObserver::FunctionOff(napi_env& env, napi_value result, const char* funName)
192 {
193     napi_value funcValue = nullptr;
194     auto Off = [](napi_env env, napi_callback_info info) -> napi_value {
195         LOGI("NAPI ComponentObserver off called");
196         napi_value thisVar = nullptr;
197         napi_value cb = nullptr;
198         CalloutType calloutType = CalloutType::UNKNOW;
199         size_t argc = ParseArgs(env, info, thisVar, cb, calloutType);
200         ComponentObserver* observer = GetObserver(env, thisVar);
201         if (!observer) {
202             LOGE("observer is null");
203             return nullptr;
204         }
205         if (calloutType == CalloutType::LAYOUTCALLOUT) {
206             LOGI("NAPI ComponentObserver Off called NapiLayoutCallback");
207             observer->DeleteCallbackFromList(argc, observer->cbLayoutList_, calloutType, cb, env);
208         } else if (calloutType == CalloutType::DRAWCALLOUT) {
209             LOGI("NAPI ComponentObserver Off called NapiDrawCallback");
210             observer->DeleteCallbackFromList(argc, observer->cbDrawList_, calloutType, cb, env);
211         }
212         return nullptr;
213     };
214 
215     napi_create_function(env, funName, NAPI_AUTO_LENGTH, Off, nullptr, &funcValue);
216     napi_set_named_property(env, result, funName, funcValue);
217 }
218 
NapiSerializer(napi_env & env,napi_value & result)219 void ComponentObserver::NapiSerializer(napi_env& env, napi_value& result)
220 {
221     napi_create_object(env, &result);
222     napi_handle_scope scope = nullptr;
223     napi_open_handle_scope(env, &scope);
224     if (scope == nullptr) {
225         return;
226     }
227 
228     napi_value componentIdVal = nullptr;
229     napi_create_string_utf8(env, componentId_.c_str(), componentId_.size(), &componentIdVal);
230     napi_set_named_property(env, result, "componentId", componentIdVal);
231     napi_close_handle_scope(env, scope);
232 
233     napi_wrap(
234         env, result, this,
235         [](napi_env env, void* data, void* hint) {
236             ComponentObserver* observer = static_cast<ComponentObserver*>(data);
237             if (observer != nullptr) {
238                 delete observer;
239             }
240         },
241         nullptr, nullptr);
242 
243     FunctionOn(env, result, "on");
244     FunctionOff(env, result, "off");
245 }
246 
Initialize(napi_env env,napi_value thisVar)247 void ComponentObserver::Initialize(napi_env env, napi_value thisVar)
248 {
249     napi_handle_scope scope = nullptr;
250     napi_open_handle_scope(env, &scope);
251     if (scope == nullptr) {
252         return;
253     }
254     if (env_ == nullptr) {
255         env_ = env;
256     }
257     napi_create_reference(env, thisVar, 1, &thisVarRef_);
258     napi_close_handle_scope(env, scope);
259 }
260 
JSCreateComponentObserver(napi_env env,napi_callback_info info)261 static napi_value JSCreateComponentObserver(napi_env env, napi_callback_info info)
262 {
263     LOGI("napi_value JSCreateComponentObserve");
264     /* Get arguments */
265     size_t argc = 1;
266     napi_value argv = nullptr;
267     napi_value thisVar = nullptr;
268     void* data = nullptr;
269     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &argv, &thisVar, &data));
270     NAPI_ASSERT(env, argc == 1, "requires 1 parameter");
271 
272     /* Checkout arguments */
273     napi_valuetype type;
274     NAPI_CALL(env, napi_typeof(env, argv, &type));
275     NAPI_ASSERT(env, type == napi_string, "type mismatch");
276     char componentId[STR_BUFFER_SIZE] = { 0 };
277     size_t len = 0;
278     napi_get_value_string_utf8(env, argv, componentId, STR_BUFFER_SIZE, &len);
279     NAPI_ASSERT(env, len < STR_BUFFER_SIZE, "condition string too long");
280 
281     /* construct object for query */
282     std::string componentIdStr(componentId, len);
283     ComponentObserver* observer = new ComponentObserver(componentIdStr);
284     napi_value result = nullptr;
285     observer->NapiSerializer(env, result);
286     auto layoutCallback = [observer]() { observer->callUserFunction(observer->cbLayoutList_); };
287     observer->layoutEvent_ = AceType::MakeRefPtr<InspectorEvent>(std::move(layoutCallback));
288 
289     auto drawCallback = [observer]() { observer->callUserFunction(observer->cbDrawList_); };
290     observer->drawEvent_ = AceType::MakeRefPtr<InspectorEvent>(std::move(drawCallback));
291 
292     auto jsEngine = EngineHelper::GetCurrentEngine();
293     if (!jsEngine) {
294         LOGE("get jsEngine failed");
295         return nullptr;
296     }
297 
298     jsEngine->RegisterLayoutInspectorCallback(observer->layoutEvent_, observer->componentId_);
299     jsEngine->RegisterDrawInspectorCallback(observer->drawEvent_, observer->componentId_);
300 #if defined(PREVIEW)
301     layoutCallback();
302     drawCallback();
303 #endif
304     return result;
305 }
306 
Export(napi_env env,napi_value exports)307 static napi_value Export(napi_env env, napi_value exports)
308 {
309     napi_property_descriptor properties[] = { DECLARE_NAPI_FUNCTION(
310         "createComponentObserver", JSCreateComponentObserver) };
311 
312     NAPI_CALL(env, napi_define_properties(env, exports, sizeof(properties) / sizeof(properties[0]), properties));
313     return exports;
314 }
315 
316 static napi_module inspector_module = {
317     .nm_version = 1,
318     .nm_flags = 0,
319     .nm_filename = nullptr,
320     .nm_register_func = Export,
321     .nm_modname = "arkui.inspector", // relative to the dynamic library's name
322     .nm_priv = ((void*)0),
323     .reserved = { 0 },
324 };
325 
Register()326 extern "C" __attribute__((constructor)) void Register()
327 {
328     napi_module_register(&inspector_module);
329 }
330 } // namespace OHOS::Ace::Napi
331