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