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 
16 #include "ui_observer.h"
17 
18 #include "bridge/common/utils/engine_helper.h"
19 
20 #include <algorithm>
21 
22 namespace OHOS::Ace::Napi {
23 std::list<std::shared_ptr<UIObserverListener>> UIObserver::unspecifiedNavigationListeners_;
24 std::unordered_map<std::string, std::list<std::shared_ptr<UIObserverListener>>>
25     UIObserver::specifiedCNavigationListeners_;
26 
27 std::list<std::shared_ptr<UIObserverListener>> UIObserver::scrollEventListeners_;
28 std::unordered_map<std::string, std::list<std::shared_ptr<UIObserverListener>>>
29     UIObserver::specifiedScrollEventListeners_;
30 
31 std::unordered_map<napi_ref, std::list<std::shared_ptr<UIObserverListener>>>
32     UIObserver::abilityContextRouterPageListeners_;
33 std::unordered_map<int32_t, std::list<std::shared_ptr<UIObserverListener>>>
34     UIObserver::specifiedRouterPageListeners_;
35 std::unordered_map<napi_ref, NG::AbilityContextInfo> UIObserver::infosForRouterPage_;
36 
37 std::unordered_map<int32_t, std::list<std::shared_ptr<UIObserverListener>>>
38     UIObserver::specifiedDensityListeners_;
39 std::unordered_map<int32_t, std::list<std::shared_ptr<UIObserverListener>>> UIObserver::specifiedDrawListeners_;
40 std::unordered_map<int32_t, std::list<std::shared_ptr<UIObserverListener>>> UIObserver::specifiedLayoutListeners_;
41 
42 std::unordered_map<napi_ref, UIObserver::NavIdAndListenersMap> UIObserver::abilityUIContextNavDesSwitchListeners_;
43 std::unordered_map<int32_t, UIObserver::NavIdAndListenersMap> UIObserver::uiContextNavDesSwitchListeners_;
44 std::unordered_map<napi_ref, NG::AbilityContextInfo> UIObserver::infosForNavDesSwitch_;
45 
46 std::unordered_map<napi_ref, std::list<std::shared_ptr<UIObserverListener>>>
47     UIObserver::abilityContextWillClickListeners_;
48 std::unordered_map<int32_t, std::list<std::shared_ptr<UIObserverListener>>>
49     UIObserver::specifiedWillClickListeners_;
50 std::unordered_map<napi_ref, NG::AbilityContextInfo> UIObserver::willClickInfos_;
51 
52 std::unordered_map<napi_ref, std::list<std::shared_ptr<UIObserverListener>>>
53     UIObserver::abilityContextDidClickListeners_;
54 std::unordered_map<int32_t, std::list<std::shared_ptr<UIObserverListener>>>
55     UIObserver::specifiedDidClickListeners_;
56 std::unordered_map<napi_ref, NG::AbilityContextInfo> UIObserver::didClickInfos_;
57 
58 std::list<std::shared_ptr<UIObserverListener>> UIObserver::tabContentStateListeners_;
59 std::unordered_map<std::string, std::list<std::shared_ptr<UIObserverListener>>>
60     UIObserver::specifiedTabContentStateListeners_;
61 
62 // UIObserver.on(type: "navDestinationUpdate", callback)
63 // register a global listener without options
RegisterNavigationCallback(const std::shared_ptr<UIObserverListener> & listener)64 void UIObserver::RegisterNavigationCallback(const std::shared_ptr<UIObserverListener>& listener)
65 {
66     if (std::find(unspecifiedNavigationListeners_.begin(), unspecifiedNavigationListeners_.end(), listener) !=
67         unspecifiedNavigationListeners_.end()) {
68         return;
69     }
70     unspecifiedNavigationListeners_.emplace_back(listener);
71 }
72 
73 // UIObserver.on(type: "navDestinationUpdate", options, callback)
74 // register a listener on a specified Navigation
RegisterNavigationCallback(std::string navigationId,const std::shared_ptr<UIObserverListener> & listener)75 void UIObserver::RegisterNavigationCallback(
76     std::string navigationId, const std::shared_ptr<UIObserverListener>& listener)
77 {
78     auto iter = specifiedCNavigationListeners_.find(navigationId);
79     if (iter == specifiedCNavigationListeners_.end()) {
80         specifiedCNavigationListeners_.emplace(
81             navigationId, std::list<std::shared_ptr<UIObserverListener>>({ listener }));
82         return;
83     }
84     auto& holder = iter->second;
85     if (std::find(holder.begin(), holder.end(), listener) != holder.end()) {
86         return;
87     }
88     holder.emplace_back(listener);
89 }
90 
91 // UIObserver.off(type: "navDestinationUpdate", callback)
UnRegisterNavigationCallback(napi_value cb)92 void UIObserver::UnRegisterNavigationCallback(napi_value cb)
93 {
94     if (cb == nullptr) {
95         unspecifiedNavigationListeners_.clear();
96         return;
97     }
98 
99     unspecifiedNavigationListeners_.erase(
100         std::remove_if(
101             unspecifiedNavigationListeners_.begin(),
102             unspecifiedNavigationListeners_.end(),
103             [cb](const std::shared_ptr<UIObserverListener>& registeredListener) {
104                 return registeredListener->NapiEqual(cb);
105             }
106         ),
107         unspecifiedNavigationListeners_.end()
108     );
109 }
110 
111 // UIObserver.off(type: "navDestinationUpdate", options, callback)
UnRegisterNavigationCallback(std::string navigationId,napi_value cb)112 void UIObserver::UnRegisterNavigationCallback(std::string navigationId, napi_value cb)
113 {
114     auto iter = specifiedCNavigationListeners_.find(navigationId);
115     if (iter == specifiedCNavigationListeners_.end()) {
116         return;
117     }
118     auto& holder = iter->second;
119     if (cb == nullptr) {
120         holder.clear();
121         return;
122     }
123     holder.erase(
124         std::remove_if(
125             holder.begin(),
126             holder.end(),
127             [cb](const std::shared_ptr<UIObserverListener>& registeredListener) {
128                 return registeredListener->NapiEqual(cb);
129             }
130         ),
131         holder.end()
132     );
133 }
134 
HandleNavigationStateChange(const NG::NavDestinationInfo & info)135 void UIObserver::HandleNavigationStateChange(const NG::NavDestinationInfo& info)
136 {
137     auto unspecifiedHolder = unspecifiedNavigationListeners_;
138     for (const auto& listener : unspecifiedHolder) {
139         listener->OnNavigationStateChange(info);
140     }
141     auto iter = specifiedCNavigationListeners_.find(info.navigationId);
142     if (iter == specifiedCNavigationListeners_.end()) {
143         return;
144     }
145 
146     auto holder = iter->second;
147 
148     for (const auto& listener : holder) {
149         listener->OnNavigationStateChange(info);
150     }
151 }
152 
153 // UIObserver.on(type: "scrollEvent", callback)
154 // register a global listener without options
RegisterScrollEventCallback(const std::shared_ptr<UIObserverListener> & listener)155 void UIObserver::RegisterScrollEventCallback(const std::shared_ptr<UIObserverListener>& listener)
156 {
157     if (std::find(scrollEventListeners_.begin(), scrollEventListeners_.end(), listener) !=
158         scrollEventListeners_.end()) {
159         return;
160     }
161     scrollEventListeners_.emplace_back(listener);
162 }
163 
164 // UIObserver.on(type: "scrollEvent", options, callback)
165 // register a listener on a specified scrollEvent
RegisterScrollEventCallback(const std::string & id,const std::shared_ptr<UIObserverListener> & listener)166 void UIObserver::RegisterScrollEventCallback(
167     const std::string& id, const std::shared_ptr<UIObserverListener>& listener)
168 {
169     if (specifiedScrollEventListeners_.find(id) == specifiedScrollEventListeners_.end()) {
170         specifiedScrollEventListeners_[id] = std::list<std::shared_ptr<UIObserverListener>>({ listener });
171         return;
172     }
173     auto& holder = specifiedScrollEventListeners_[id];
174     if (std::find(holder.begin(), holder.end(), listener) != holder.end()) {
175         return;
176     }
177     holder.emplace_back(listener);
178 }
179 
180 // UIObserver.off(type: "scrollEvent", callback)
UnRegisterScrollEventCallback(napi_value cb)181 void UIObserver::UnRegisterScrollEventCallback(napi_value cb)
182 {
183     if (cb == nullptr) {
184         scrollEventListeners_.clear();
185         return;
186     }
187 
188     scrollEventListeners_.erase(
189         std::remove_if(
190             scrollEventListeners_.begin(),
191             scrollEventListeners_.end(),
192             [cb](const std::shared_ptr<UIObserverListener>& registeredListener) {
193                 return registeredListener->NapiEqual(cb);
194             }),
195         scrollEventListeners_.end()
196     );
197 }
198 
199 // UIObserver.off(type: "scrollEvent", options, callback)
UnRegisterScrollEventCallback(const std::string & id,napi_value cb)200 void UIObserver::UnRegisterScrollEventCallback(const std::string& id, napi_value cb)
201 {
202     auto iter = specifiedScrollEventListeners_.find(id);
203     if (iter == specifiedScrollEventListeners_.end()) {
204         return;
205     }
206     auto& holder = iter->second;
207     if (cb == nullptr) {
208         holder.clear();
209         return;
210     }
211     holder.erase(
212         std::remove_if(
213             holder.begin(),
214             holder.end(),
215             [cb](const std::shared_ptr<UIObserverListener>& registeredListener) {
216                 return registeredListener->NapiEqual(cb);
217             }),
218         holder.end()
219     );
220 }
221 
HandleScrollEventStateChange(const std::string & id,int32_t uniqueId,NG::ScrollEventType eventType,float offset)222 void UIObserver::HandleScrollEventStateChange(const std::string& id, int32_t uniqueId,
223     NG::ScrollEventType eventType, float offset)
224 {
225     for (const auto& listener : scrollEventListeners_) {
226         listener->OnScrollEventStateChange(id, uniqueId, eventType, offset);
227     }
228 
229     auto iter = specifiedScrollEventListeners_.find(id);
230     if (iter == specifiedScrollEventListeners_.end()) {
231         return;
232     }
233 
234     auto& holder = iter->second;
235 
236     for (const auto& listener : holder) {
237         listener->OnScrollEventStateChange(id, uniqueId, eventType, offset);
238     }
239 }
240 
241 // UIObserver.on(type: "routerPageUpdate", UIAbilityContext, callback)
242 // register a listener on current page
RegisterRouterPageCallback(napi_env env,napi_value uiAbilityContext,const std::shared_ptr<UIObserverListener> & listener)243 void UIObserver::RegisterRouterPageCallback(
244     napi_env env, napi_value uiAbilityContext, const std::shared_ptr<UIObserverListener>& listener)
245 {
246     NG::AbilityContextInfo info;
247     GetAbilityInfos(env, uiAbilityContext, info);
248     for (auto listenerPair : abilityContextRouterPageListeners_) {
249         auto ref = listenerPair.first;
250         auto localInfo = infosForRouterPage_[ref];
251         if (info.IsEqual(localInfo)) {
252             auto& holder = abilityContextRouterPageListeners_[ref];
253             if (std::find(holder.begin(), holder.end(), listener) != holder.end()) {
254                 return;
255             }
256             holder.emplace_back(listener);
257             return;
258         }
259     }
260     napi_ref newRef = nullptr;
261     napi_create_reference(env, uiAbilityContext, 1, &newRef);
262     abilityContextRouterPageListeners_[newRef] = std::list<std::shared_ptr<UIObserverListener>>({ listener });
263     infosForRouterPage_[newRef] = info;
264 }
265 
266 // UIObserver.on(type: "routerPageUpdate", uiContext | null, callback)
267 // register a listener on current page
RegisterRouterPageCallback(int32_t uiContextInstanceId,const std::shared_ptr<UIObserverListener> & listener)268 void UIObserver::RegisterRouterPageCallback(
269     int32_t uiContextInstanceId, const std::shared_ptr<UIObserverListener>& listener)
270 {
271     if (uiContextInstanceId == 0) {
272         uiContextInstanceId = Container::CurrentId();
273     }
274     auto iter = specifiedRouterPageListeners_.find(uiContextInstanceId);
275     if (iter == specifiedRouterPageListeners_.end()) {
276         specifiedRouterPageListeners_.emplace(
277             uiContextInstanceId, std::list<std::shared_ptr<UIObserverListener>>({ listener }));
278         return;
279     }
280     auto& holder = iter->second;
281     if (std::find(holder.begin(), holder.end(), listener) != holder.end()) {
282         return;
283     }
284     holder.emplace_back(listener);
285 }
286 
287 // UIObserver.off(type: "routerPageUpdate", uiAbilityContext, callback)
UnRegisterRouterPageCallback(napi_env env,napi_value uiAbilityContext,napi_value callback)288 void UIObserver::UnRegisterRouterPageCallback(napi_env env, napi_value uiAbilityContext, napi_value callback)
289 {
290     NG::AbilityContextInfo info;
291     GetAbilityInfos(env, uiAbilityContext, info);
292     for (auto listenerPair : abilityContextRouterPageListeners_) {
293         auto ref = listenerPair.first;
294         auto localInfo = infosForRouterPage_[ref];
295         if (info.IsEqual(localInfo)) {
296             auto& holder = abilityContextRouterPageListeners_[listenerPair.first];
297             if (callback == nullptr) {
298                 holder.clear();
299             } else {
300                 holder.erase(
301                     std::remove_if(
302                         holder.begin(),
303                         holder.end(),
304                         [callback](const std::shared_ptr<UIObserverListener>& registeredListener) {
305                             return registeredListener->NapiEqual(callback);
306                         }),
307                     holder.end());
308             }
309             if (holder.empty()) {
310                 infosForRouterPage_.erase(ref);
311                 abilityContextRouterPageListeners_.erase(ref);
312                 napi_delete_reference(env, ref);
313             }
314             return;
315         }
316     }
317 }
318 
319 // UIObserver.off(type: "routerPageUpdate", uiContext | null, callback)
UnRegisterRouterPageCallback(int32_t uiContextInstanceId,napi_value callback)320 void UIObserver::UnRegisterRouterPageCallback(int32_t uiContextInstanceId, napi_value callback)
321 {
322     if (uiContextInstanceId == 0) {
323         uiContextInstanceId = Container::CurrentId();
324     }
325     auto iter = specifiedRouterPageListeners_.find(uiContextInstanceId);
326     if (iter == specifiedRouterPageListeners_.end()) {
327         return;
328     }
329     auto& holder = iter->second;
330     if (callback == nullptr) {
331         holder.clear();
332         return;
333     }
334     holder.erase(
335         std::remove_if(
336             holder.begin(),
337             holder.end(),
338             [callback](const std::shared_ptr<UIObserverListener>& registeredListener) {
339                 return registeredListener->NapiEqual(callback);
340             }),
341         holder.end());
342 }
343 
344 // UIObserver.on(type: "willDraw", uiContext | null, callback)
345 // register a listener on current page
RegisterDrawCallback(int32_t uiContextInstanceId,const std::shared_ptr<UIObserverListener> & listener)346 void UIObserver::RegisterDrawCallback(int32_t uiContextInstanceId, const std::shared_ptr<UIObserverListener>& listener)
347 {
348     if (uiContextInstanceId == 0) {
349         uiContextInstanceId = Container::CurrentId();
350     }
351     if (specifiedDrawListeners_.find(uiContextInstanceId) == specifiedDrawListeners_.end()) {
352         specifiedDrawListeners_[uiContextInstanceId] = std::list<std::shared_ptr<UIObserverListener>>({ listener });
353         return;
354     }
355     auto& holder = specifiedDrawListeners_[uiContextInstanceId];
356     if (std::find(holder.begin(), holder.end(), listener) != holder.end()) {
357         return;
358     }
359     holder.emplace_back(listener);
360 }
361 
362 // UIObserver.off(type: "willDraw", uiContext | null, callback)
UnRegisterDrawCallback(int32_t uiContextInstanceId,napi_value callback)363 void UIObserver::UnRegisterDrawCallback(int32_t uiContextInstanceId, napi_value callback)
364 {
365     if (uiContextInstanceId == 0) {
366         uiContextInstanceId = Container::CurrentId();
367     }
368     if (specifiedDrawListeners_.find(uiContextInstanceId) == specifiedDrawListeners_.end()) {
369         return;
370     }
371     auto& holder = specifiedDrawListeners_[uiContextInstanceId];
372     if (callback == nullptr) {
373         holder.clear();
374         return;
375     }
376     holder.erase(
377         std::remove_if(
378             holder.begin(),
379             holder.end(),
380             [callback](const std::shared_ptr<UIObserverListener>& registeredListener) {
381                 return registeredListener->NapiEqual(callback);
382             }),
383         holder.end());
384 }
385 
386 // UIObserver.on(type: "didLayout", uiContext | null, callback)
387 // register a listener on current page
RegisterLayoutCallback(int32_t uiContextInstanceId,const std::shared_ptr<UIObserverListener> & listener)388 void UIObserver::RegisterLayoutCallback(
389     int32_t uiContextInstanceId, const std::shared_ptr<UIObserverListener>& listener)
390 {
391     if (uiContextInstanceId == 0) {
392         uiContextInstanceId = Container::CurrentId();
393     }
394     if (specifiedLayoutListeners_.find(uiContextInstanceId) == specifiedLayoutListeners_.end()) {
395         specifiedLayoutListeners_[uiContextInstanceId] = std::list<std::shared_ptr<UIObserverListener>>({ listener });
396         return;
397     }
398     auto& holder = specifiedLayoutListeners_[uiContextInstanceId];
399     if (std::find(holder.begin(), holder.end(), listener) != holder.end()) {
400         return;
401     }
402     holder.emplace_back(listener);
403 }
404 
405 // UIObserver.off(type: "didLayout", uiContext | null, callback)
UnRegisterLayoutCallback(int32_t uiContextInstanceId,napi_value callback)406 void UIObserver::UnRegisterLayoutCallback(int32_t uiContextInstanceId, napi_value callback)
407 {
408     if (uiContextInstanceId == 0) {
409         uiContextInstanceId = Container::CurrentId();
410     }
411     if (specifiedLayoutListeners_.find(uiContextInstanceId) == specifiedLayoutListeners_.end()) {
412         return;
413     }
414     auto& holder = specifiedLayoutListeners_[uiContextInstanceId];
415     if (callback == nullptr) {
416         holder.clear();
417         return;
418     }
419     holder.erase(
420         std::remove_if(
421             holder.begin(),
422             holder.end(),
423             [callback](const std::shared_ptr<UIObserverListener>& registeredListener) {
424                 return registeredListener->NapiEqual(callback);
425             }),
426         holder.end());
427 }
428 
HandleRouterPageStateChange(NG::AbilityContextInfo & info,const NG::RouterPageInfoNG & pageInfo)429 void UIObserver::HandleRouterPageStateChange(NG::AbilityContextInfo& info, const NG::RouterPageInfoNG& pageInfo)
430 {
431     for (auto listenerPair : abilityContextRouterPageListeners_) {
432         auto ref = listenerPair.first;
433         auto localInfo = infosForRouterPage_[ref];
434         if (info.IsEqual(localInfo)) {
435             auto env = GetCurrentNapiEnv();
436             napi_value abilityContext = nullptr;
437             napi_get_reference_value(env, ref, &abilityContext);
438 
439             NG::RouterPageInfoNG abilityPageInfo(
440                 abilityContext, pageInfo.index, pageInfo.name, pageInfo.path, pageInfo.state, pageInfo.pageId);
441             auto holder = abilityContextRouterPageListeners_[ref];
442             for (const auto& listener : holder) {
443                 listener->OnRouterPageStateChange(abilityPageInfo);
444             }
445             break;
446         }
447     }
448 
449     auto currentId = Container::CurrentId();
450     auto iter = specifiedRouterPageListeners_.find(currentId);
451     if (iter == specifiedRouterPageListeners_.end()) {
452         return;
453     }
454     auto holder = iter->second;
455     for (const auto& listener : holder) {
456         listener->OnRouterPageStateChange(pageInfo);
457     }
458 }
459 
460 // UIObserver.on(type: "densityUpdate", uiContext | null, callback)
461 // register a listener on current page
RegisterDensityCallback(int32_t uiContextInstanceId,const std::shared_ptr<UIObserverListener> & listener)462 void UIObserver::RegisterDensityCallback(
463     int32_t uiContextInstanceId, const std::shared_ptr<UIObserverListener>& listener)
464 {
465     if (uiContextInstanceId == 0) {
466         uiContextInstanceId = Container::CurrentId();
467     }
468     if (specifiedDensityListeners_.find(uiContextInstanceId) == specifiedDensityListeners_.end()) {
469         specifiedDensityListeners_[uiContextInstanceId] =
470             std::list<std::shared_ptr<UIObserverListener>>({ listener });
471         return;
472     }
473     auto& holder = specifiedDensityListeners_[uiContextInstanceId];
474     if (std::find(holder.begin(), holder.end(), listener) != holder.end()) {
475         return;
476     }
477     holder.emplace_back(listener);
478 }
479 
480 // UIObserver.off(type: "densityUpdate", uiContext | null, callback)
UnRegisterDensityCallback(int32_t uiContextInstanceId,napi_value callback)481 void UIObserver::UnRegisterDensityCallback(int32_t uiContextInstanceId, napi_value callback)
482 {
483     if (uiContextInstanceId == 0) {
484         uiContextInstanceId = Container::CurrentId();
485     }
486     if (specifiedDensityListeners_.find(uiContextInstanceId) == specifiedDensityListeners_.end()) {
487         return;
488     }
489     auto& holder = specifiedDensityListeners_[uiContextInstanceId];
490     if (callback == nullptr) {
491         holder.clear();
492         return;
493     }
494     holder.erase(
495         std::remove_if(
496             holder.begin(),
497             holder.end(),
498             [callback](const std::shared_ptr<UIObserverListener>& registeredListener) {
499                 return registeredListener->NapiEqual(callback);
500             }),
501         holder.end());
502 }
503 
HandleDensityChange(NG::AbilityContextInfo & info,double density)504 void UIObserver::HandleDensityChange(NG::AbilityContextInfo& info, double density)
505 {
506     auto currentId = Container::CurrentId();
507     if (specifiedDensityListeners_.find(currentId) == specifiedDensityListeners_.end()) {
508         return;
509     }
510     auto& holder = specifiedDensityListeners_[currentId];
511     for (const auto& listener : holder) {
512         listener->OnDensityChange(density);
513     }
514 }
515 
HandDrawCommandSendChange()516 void UIObserver::HandDrawCommandSendChange()
517 {
518     auto currentId = Container::CurrentId();
519     if (specifiedDrawListeners_.find(currentId) == specifiedDrawListeners_.end()) {
520         return;
521     }
522     auto& holder = specifiedDrawListeners_[currentId];
523     for (const auto& listener : holder) {
524         listener->OnDrawOrLayout();
525     }
526 }
527 
HandLayoutDoneChange()528 void UIObserver::HandLayoutDoneChange()
529 {
530     auto currentId = Container::CurrentId();
531     if (specifiedLayoutListeners_.find(currentId) == specifiedLayoutListeners_.end()) {
532         return;
533     }
534     auto& holder = specifiedLayoutListeners_[currentId];
535     for (const auto& listener : holder) {
536         listener->OnDrawOrLayout();
537     }
538 }
539 
540 /**
541  * observer.on('navDestinationSwitch', context: UIAbilityContext, callback)
542  * observer.on('navDestinationSwitch', context: UIAbilityContext, { navigationId: navId }, callback)
543  */
RegisterNavDestinationSwitchCallback(napi_env env,napi_value uiAbilityContext,const std::optional<std::string> & navigationId,const std::shared_ptr<UIObserverListener> & listener)544 void UIObserver::RegisterNavDestinationSwitchCallback(napi_env env, napi_value uiAbilityContext,
545     const std::optional<std::string>& navigationId, const std::shared_ptr<UIObserverListener>& listener)
546 {
547     NG::AbilityContextInfo info;
548     GetAbilityInfos(env, uiAbilityContext, info);
549     for (auto& listenerPair : abilityUIContextNavDesSwitchListeners_) {
550         auto ref = listenerPair.first;
551         auto localInfo = infosForNavDesSwitch_[ref];
552         if (!info.IsEqual(localInfo)) {
553             continue;
554         }
555 
556         auto& listenersMap = listenerPair.second;
557         auto it = listenersMap.find(navigationId);
558         if (it == listenersMap.end()) {
559             listenersMap[navigationId] = std::list<std::shared_ptr<UIObserverListener>>({listener});
560             return;
561         }
562         if (std::find(it->second.begin(), it->second.end(), listener) == it->second.end()) {
563             it->second.emplace_back(listener);
564         }
565         return;
566     }
567     napi_ref newRef = nullptr;
568     napi_create_reference(env, uiAbilityContext, 1, &newRef);
569     NavIdAndListenersMap listenersMap;
570     listenersMap.emplace(navigationId, std::list<std::shared_ptr<UIObserverListener>>({ listener }));
571     abilityUIContextNavDesSwitchListeners_[newRef] = listenersMap;
572     infosForNavDesSwitch_[newRef] = info;
573 }
574 
575 /**
576  * UIObserver.on('navDestinationSwitch', { navigationId: navId }, callback)
577  * UIObserver.on('navDestinationSwitch', callback)
578  * observer.on('navDestinationSwitch', context: UIContext, { navigationId?: navId }, callback)
579  */
RegisterNavDestinationSwitchCallback(int32_t uiContextInstanceId,const std::optional<std::string> & navigationId,const std::shared_ptr<UIObserverListener> & listener)580 void UIObserver::RegisterNavDestinationSwitchCallback(int32_t uiContextInstanceId,
581     const std::optional<std::string>& navigationId, const std::shared_ptr<UIObserverListener>& listener)
582 {
583     if (uiContextInstanceId == 0) {
584         uiContextInstanceId = Container::CurrentId();
585     }
586     auto listenersMapIter = uiContextNavDesSwitchListeners_.find(uiContextInstanceId);
587     if (listenersMapIter == uiContextNavDesSwitchListeners_.end()) {
588         NavIdAndListenersMap listenersMap;
589         listenersMap.emplace(navigationId, std::list<std::shared_ptr<UIObserverListener>>({ listener }));
590         uiContextNavDesSwitchListeners_[uiContextInstanceId] = listenersMap;
591         return;
592     }
593 
594     auto& listenersMap = listenersMapIter->second;
595     auto it = listenersMap.find(navigationId);
596     if (it == listenersMap.end()) {
597         listenersMap[navigationId] = std::list<std::shared_ptr<UIObserverListener>>({listener});
598         return;
599     }
600 
601     if (std::find(it->second.begin(), it->second.end(), listener) == it->second.end()) {
602         it->second.emplace_back(listener);
603     }
604 }
605 
606 /**
607  * observer.off('navDestinationSwitch', context: AbilityUIContext})
608  * observer.off('navDestinationSwitch', context: AbilityUIContext, callback })
609  * observer.off('navDestinationSwitch', context: AbilityUIContext, { navigationId: navId } })
610  * observer.off('navDestinationSwitch', context: AbilityUIContext, { navigationId: navId }, callback })
611  */
UnRegisterNavDestinationSwitchCallback(napi_env env,napi_value uiAbilityContext,const std::optional<std::string> & navigationId,napi_value callback)612 void UIObserver::UnRegisterNavDestinationSwitchCallback(napi_env env, napi_value uiAbilityContext,
613     const std::optional<std::string>& navigationId, napi_value callback)
614 {
615     NG::AbilityContextInfo info;
616     GetAbilityInfos(env, uiAbilityContext, info);
617     for (auto listenerPair : abilityUIContextNavDesSwitchListeners_) {
618         auto ref = listenerPair.first;
619         auto localInfo = infosForNavDesSwitch_[ref];
620         if (!info.IsEqual(localInfo)) {
621             continue;
622         }
623 
624         auto& listenersMap = listenerPair.second;
625         auto it = listenersMap.find(navigationId);
626         if (it == listenersMap.end()) {
627             return;
628         }
629         auto& listeners = it->second;
630         if (callback == nullptr) {
631             listeners.clear();
632         } else {
633             listeners.erase(std::remove_if(listeners.begin(), listeners.end(),
634                 [callback](const std::shared_ptr<UIObserverListener>& registeredListener) {
635                     return registeredListener->NapiEqual(callback);
636                 }), listeners.end());
637         }
638         if (listeners.empty()) {
639             listenersMap.erase(it);
640         }
641         if (listenersMap.empty()) {
642             infosForNavDesSwitch_.erase(ref);
643             abilityUIContextNavDesSwitchListeners_.erase(ref);
644             napi_delete_reference(env, ref);
645         }
646         return;
647     }
648 }
649 
650 /**
651  * observer.off('navDestinationSwitch', context: UIContext})
652  * observer.off('navDestinationSwitch', context: UIContext, callback })
653  * observer.off('navDestinationSwitch', context: UIContext, { navigationId: navId })
654  * observer.off('navDestinationSwitch', context: UIContext, { navigationId: navId }, callback )
655  * UIObserver.off('navDestinationSwitch')
656  * UIObserver.off('navDestinationSwitch', callback)
657  * UIObserver.off('navDestinationSwitch', { navigationId: navId })
658  * UIObserver.off('navDestinationSwitch', { navigationId: navId }, callback )
659  */
UnRegisterNavDestinationSwitchCallback(int32_t uiContextInstanceId,const std::optional<std::string> & navigationId,napi_value callback)660 void UIObserver::UnRegisterNavDestinationSwitchCallback(int32_t uiContextInstanceId,
661     const std::optional<std::string>& navigationId, napi_value callback)
662 {
663     if (uiContextInstanceId == 0) {
664         uiContextInstanceId = Container::CurrentId();
665     }
666     auto listenersMapIter = uiContextNavDesSwitchListeners_.find(uiContextInstanceId);
667     if (listenersMapIter == uiContextNavDesSwitchListeners_.end()) {
668         return;
669     }
670     auto& listenersMap = listenersMapIter->second;
671     auto it = listenersMap.find(navigationId);
672     if (it == listenersMap.end()) {
673         return;
674     }
675     auto& listeners = it->second;
676     if (callback == nullptr) {
677         listeners.clear();
678     } else {
679         listeners.erase(std::remove_if(listeners.begin(), listeners.end(),
680             [callback](const std::shared_ptr<UIObserverListener>& registeredListener) {
681                 return registeredListener->NapiEqual(callback);
682             }), listeners.end());
683     }
684     if (listeners.empty()) {
685         listenersMap.erase(it);
686     }
687     if (listenersMap.empty()) {
688         uiContextNavDesSwitchListeners_.erase(listenersMapIter);
689     }
690 }
691 
HandleNavDestinationSwitch(const NG::AbilityContextInfo & info,NG::NavDestinationSwitchInfo & switchInfo)692 void UIObserver::HandleNavDestinationSwitch(
693     const NG::AbilityContextInfo& info, NG::NavDestinationSwitchInfo& switchInfo)
694 {
695     HandleAbilityUIContextNavDestinationSwitch(info, switchInfo);
696     HandleUIContextNavDestinationSwitch(switchInfo);
697 }
698 
HandleAbilityUIContextNavDestinationSwitch(const NG::AbilityContextInfo & info,NG::NavDestinationSwitchInfo & switchInfo)699 void UIObserver::HandleAbilityUIContextNavDestinationSwitch(
700     const NG::AbilityContextInfo& info, NG::NavDestinationSwitchInfo& switchInfo)
701 {
702     napi_value uiContextBackup = switchInfo.context;
703     for (auto listenerPair : abilityUIContextNavDesSwitchListeners_) {
704         auto ref = listenerPair.first;
705         auto localInfo = infosForNavDesSwitch_[ref];
706         if (!info.IsEqual(localInfo)) {
707             continue;
708         }
709 
710         auto env = GetCurrentNapiEnv();
711         napi_value abilityContext = nullptr;
712         napi_get_reference_value(env, ref, &abilityContext);
713 
714         switchInfo.context = abilityContext;
715         auto listenersMap = listenerPair.second;
716         HandleListenersWithEmptyNavigationId(listenersMap, switchInfo);
717         HandleListenersWithSpecifiedNavigationId(listenersMap, switchInfo);
718         break;
719     }
720     switchInfo.context = uiContextBackup;
721 }
722 
HandleUIContextNavDestinationSwitch(const NG::NavDestinationSwitchInfo & switchInfo)723 void UIObserver::HandleUIContextNavDestinationSwitch(const NG::NavDestinationSwitchInfo& switchInfo)
724 {
725     auto currentId = Container::CurrentId();
726     auto listenersMapIter = uiContextNavDesSwitchListeners_.find(currentId);
727     if (listenersMapIter == uiContextNavDesSwitchListeners_.end()) {
728         return;
729     }
730     auto listenersMap = listenersMapIter->second;
731     HandleListenersWithEmptyNavigationId(listenersMap, switchInfo);
732     HandleListenersWithSpecifiedNavigationId(listenersMap, switchInfo);
733 }
734 
HandleListenersWithEmptyNavigationId(const NavIdAndListenersMap & listenersMap,const NG::NavDestinationSwitchInfo & switchInfo)735 void UIObserver::HandleListenersWithEmptyNavigationId(
736     const NavIdAndListenersMap& listenersMap, const NG::NavDestinationSwitchInfo& switchInfo)
737 {
738     std::optional<std::string> navId;
739     auto it = listenersMap.find(navId);
740     if (it != listenersMap.end()) {
741         const auto listeners = it->second;
742         for (const auto& listener : listeners) {
743             listener->OnNavDestinationSwitch(switchInfo);
744         }
745     }
746 }
747 
HandleListenersWithSpecifiedNavigationId(const NavIdAndListenersMap & listenersMap,const NG::NavDestinationSwitchInfo & switchInfo)748 void UIObserver::HandleListenersWithSpecifiedNavigationId(
749     const NavIdAndListenersMap& listenersMap, const NG::NavDestinationSwitchInfo& switchInfo)
750 {
751     std::string navigationId;
752     if (switchInfo.from.has_value()) {
753         navigationId = switchInfo.from.value().navigationId;
754     } else if (switchInfo.to.has_value()) {
755         navigationId = switchInfo.to.value().navigationId;
756     }
757     if (!navigationId.empty()) {
758         std::optional<std::string> navId{navigationId};
759         auto it = listenersMap.find(navId);
760         if (it != listenersMap.end()) {
761             const auto listeners = it->second;
762             for (const auto& listener : listeners) {
763                 listener->OnNavDestinationSwitch(switchInfo);
764             }
765         }
766     }
767 }
768 
RegisterWillClickCallback(napi_env env,napi_value uiAbilityContext,const std::shared_ptr<UIObserverListener> & listener)769 void UIObserver::RegisterWillClickCallback(
770     napi_env env, napi_value uiAbilityContext, const std::shared_ptr<UIObserverListener>& listener)
771 {
772     napi_handle_scope scope = nullptr;
773     auto status = napi_open_handle_scope(env, &scope);
774     if (status != napi_ok) {
775         return;
776     }
777     NG::AbilityContextInfo info;
778     GetAbilityInfos(env, uiAbilityContext, info);
779     for (auto listenerPair : abilityContextWillClickListeners_) {
780         auto ref = listenerPair.first;
781         auto localInfo = willClickInfos_[ref];
782         if (info.IsEqual(localInfo)) {
783             auto& holder = abilityContextWillClickListeners_[ref];
784             if (std::find(holder.begin(), holder.end(), listener) != holder.end()) {
785                 napi_close_handle_scope(env, scope);
786                 return;
787             }
788             holder.emplace_back(listener);
789             napi_close_handle_scope(env, scope);
790             return;
791         }
792     }
793     napi_ref newRef = nullptr;
794     napi_create_reference(env, uiAbilityContext, 1, &newRef);
795     abilityContextWillClickListeners_[newRef] = std::list<std::shared_ptr<UIObserverListener>>({ listener });
796     willClickInfos_[newRef] = info;
797     napi_close_handle_scope(env, scope);
798 }
799 
RegisterWillClickCallback(int32_t uiContextInstanceId,const std::shared_ptr<UIObserverListener> & listener)800 void UIObserver::RegisterWillClickCallback(
801     int32_t uiContextInstanceId, const std::shared_ptr<UIObserverListener>& listener)
802 {
803     if (uiContextInstanceId == 0) {
804         uiContextInstanceId = Container::CurrentId();
805     }
806     auto iter = specifiedWillClickListeners_.find(uiContextInstanceId);
807     if (iter == specifiedWillClickListeners_.end()) {
808         specifiedWillClickListeners_.emplace(
809             uiContextInstanceId, std::list<std::shared_ptr<UIObserverListener>>({ listener }));
810         return;
811     }
812     auto& holder = iter->second;
813     if (std::find(holder.begin(), holder.end(), listener) != holder.end()) {
814         return;
815     }
816     holder.emplace_back(listener);
817 }
818 
UnRegisterWillClickCallback(napi_env env,napi_value uiAbilityContext,napi_value callback)819 void UIObserver::UnRegisterWillClickCallback(napi_env env, napi_value uiAbilityContext, napi_value callback)
820 {
821     napi_handle_scope scope = nullptr;
822     auto status = napi_open_handle_scope(env, &scope);
823     if (status != napi_ok) {
824         return;
825     }
826     NG::AbilityContextInfo info;
827     GetAbilityInfos(env, uiAbilityContext, info);
828     for (auto listenerPair : abilityContextWillClickListeners_) {
829         auto ref = listenerPair.first;
830         auto localInfo = willClickInfos_[ref];
831         if (!info.IsEqual(localInfo)) {
832             continue;
833         }
834         auto& holder = abilityContextWillClickListeners_[listenerPair.first];
835         if (callback == nullptr) {
836             holder.clear();
837         } else {
838             holder.erase(
839                 std::remove_if(
840                     holder.begin(),
841                     holder.end(),
842                     [callback](const std::shared_ptr<UIObserverListener>& registeredListener) {
843                         return registeredListener->NapiEqual(callback);
844                     }),
845                 holder.end());
846         }
847         if (holder.empty()) {
848             willClickInfos_.erase(ref);
849             abilityContextWillClickListeners_.erase(ref);
850             napi_delete_reference(env, ref);
851         }
852     }
853     napi_close_handle_scope(env, scope);
854 }
855 
UnRegisterWillClickCallback(int32_t uiContextInstanceId,napi_value callback)856 void UIObserver::UnRegisterWillClickCallback(int32_t uiContextInstanceId, napi_value callback)
857 {
858     if (uiContextInstanceId == 0) {
859         uiContextInstanceId = Container::CurrentId();
860     }
861     auto iter = specifiedWillClickListeners_.find(uiContextInstanceId);
862     if (iter == specifiedWillClickListeners_.end()) {
863         return;
864     }
865     auto& holder = iter->second;
866     if (callback == nullptr) {
867         holder.clear();
868         return;
869     }
870     holder.erase(
871         std::remove_if(
872             holder.begin(),
873             holder.end(),
874             [callback](const std::shared_ptr<UIObserverListener>& registeredListener) {
875                 return registeredListener->NapiEqual(callback);
876             }),
877         holder.end());
878 }
879 
HandleWillClick(NG::AbilityContextInfo & info,const GestureEvent & gestureEventInfo,const ClickInfo & clickInfo,const RefPtr<NG::FrameNode> & frameNode)880 void UIObserver::HandleWillClick(NG::AbilityContextInfo& info, const GestureEvent& gestureEventInfo,
881     const ClickInfo& clickInfo, const RefPtr<NG::FrameNode>& frameNode)
882 {
883     auto env = GetCurrentNapiEnv();
884     napi_handle_scope scope = nullptr;
885     auto status = napi_open_handle_scope(env, &scope);
886     if (status != napi_ok) {
887         return;
888     }
889     for (auto listenerPair : abilityContextWillClickListeners_) {
890         auto ref = listenerPair.first;
891         auto localInfo = willClickInfos_[ref];
892         if (info.IsEqual(localInfo)) {
893             napi_value abilityContext = nullptr;
894             napi_get_reference_value(env, ref, &abilityContext);
895 
896             auto& holder = abilityContextWillClickListeners_[ref];
897             for (const auto& listener : holder) {
898                 listener->OnWillClick(gestureEventInfo, clickInfo, frameNode);
899             }
900             break;
901         }
902     }
903 
904     auto currentId = Container::CurrentId();
905     auto iter = specifiedWillClickListeners_.find(currentId);
906     if (iter == specifiedWillClickListeners_.end()) {
907         napi_close_handle_scope(env, scope);
908         return;
909     }
910     auto& holder = iter->second;
911     for (const auto& listener : holder) {
912         listener->OnWillClick(gestureEventInfo, clickInfo, frameNode);
913     }
914     napi_close_handle_scope(env, scope);
915 }
916 
RegisterDidClickCallback(napi_env env,napi_value uiAbilityContext,const std::shared_ptr<UIObserverListener> & listener)917 void UIObserver::RegisterDidClickCallback(
918     napi_env env, napi_value uiAbilityContext, const std::shared_ptr<UIObserverListener>& listener)
919 {
920     napi_handle_scope scope = nullptr;
921     auto status = napi_open_handle_scope(env, &scope);
922     if (status != napi_ok) {
923         return;
924     }
925     NG::AbilityContextInfo info;
926     GetAbilityInfos(env, uiAbilityContext, info);
927     for (auto listenerPair : abilityContextDidClickListeners_) {
928         auto ref = listenerPair.first;
929         auto localInfo = didClickInfos_[ref];
930         if (info.IsEqual(localInfo)) {
931             auto& holder = abilityContextDidClickListeners_[ref];
932             if (std::find(holder.begin(), holder.end(), listener) != holder.end()) {
933                 napi_close_handle_scope(env, scope);
934                 return;
935             }
936             holder.emplace_back(listener);
937             napi_close_handle_scope(env, scope);
938             return;
939         }
940     }
941     napi_ref newRef = nullptr;
942     napi_create_reference(env, uiAbilityContext, 1, &newRef);
943     abilityContextDidClickListeners_[newRef] = std::list<std::shared_ptr<UIObserverListener>>({ listener });
944     didClickInfos_[newRef] = info;
945     napi_close_handle_scope(env, scope);
946 }
947 
RegisterDidClickCallback(int32_t uiContextInstanceId,const std::shared_ptr<UIObserverListener> & listener)948 void UIObserver::RegisterDidClickCallback(
949     int32_t uiContextInstanceId, const std::shared_ptr<UIObserverListener>& listener)
950 {
951     if (uiContextInstanceId == 0) {
952         uiContextInstanceId = Container::CurrentId();
953     }
954     auto iter = specifiedDidClickListeners_.find(uiContextInstanceId);
955     if (iter == specifiedDidClickListeners_.end()) {
956         specifiedDidClickListeners_.emplace(
957             uiContextInstanceId, std::list<std::shared_ptr<UIObserverListener>>({ listener }));
958         return;
959     }
960     auto& holder = iter->second;
961     if (std::find(holder.begin(), holder.end(), listener) != holder.end()) {
962         return;
963     }
964     holder.emplace_back(listener);
965 }
966 
UnRegisterDidClickCallback(napi_env env,napi_value uiAbilityContext,napi_value callback)967 void UIObserver::UnRegisterDidClickCallback(napi_env env, napi_value uiAbilityContext, napi_value callback)
968 {
969     napi_handle_scope scope = nullptr;
970     auto status = napi_open_handle_scope(env, &scope);
971     if (status != napi_ok) {
972         return;
973     }
974     NG::AbilityContextInfo info;
975     GetAbilityInfos(env, uiAbilityContext, info);
976     for (auto listenerPair : abilityContextDidClickListeners_) {
977         auto ref = listenerPair.first;
978         auto localInfo = didClickInfos_[ref];
979         if (!info.IsEqual(localInfo)) {
980             continue;
981         }
982         auto& holder = abilityContextDidClickListeners_[listenerPair.first];
983         if (callback == nullptr) {
984             holder.clear();
985         } else {
986             holder.erase(
987                 std::remove_if(
988                     holder.begin(),
989                     holder.end(),
990                     [callback](const std::shared_ptr<UIObserverListener>& registeredListener) {
991                         return registeredListener->NapiEqual(callback);
992                     }),
993                 holder.end());
994         }
995         if (holder.empty()) {
996             didClickInfos_.erase(ref);
997             abilityContextDidClickListeners_.erase(ref);
998             napi_delete_reference(env, ref);
999         }
1000     }
1001     napi_close_handle_scope(env, scope);
1002 }
1003 
UnRegisterDidClickCallback(int32_t uiContextInstanceId,napi_value callback)1004 void UIObserver::UnRegisterDidClickCallback(int32_t uiContextInstanceId, napi_value callback)
1005 {
1006     if (uiContextInstanceId == 0) {
1007         uiContextInstanceId = Container::CurrentId();
1008     }
1009     auto iter = specifiedDidClickListeners_.find(uiContextInstanceId);
1010     if (iter == specifiedDidClickListeners_.end()) {
1011         return;
1012     }
1013     auto& holder = iter->second;
1014     if (callback == nullptr) {
1015         holder.clear();
1016         return;
1017     }
1018     holder.erase(
1019         std::remove_if(
1020             holder.begin(),
1021             holder.end(),
1022             [callback](const std::shared_ptr<UIObserverListener>& registeredListener) {
1023                 return registeredListener->NapiEqual(callback);
1024             }),
1025         holder.end());
1026 }
1027 
HandleDidClick(NG::AbilityContextInfo & info,const GestureEvent & gestureEventInfo,const ClickInfo & clickInfo,const RefPtr<NG::FrameNode> & frameNode)1028 void UIObserver::HandleDidClick(NG::AbilityContextInfo& info, const GestureEvent& gestureEventInfo,
1029     const ClickInfo& clickInfo, const RefPtr<NG::FrameNode>& frameNode)
1030 {
1031     auto env = GetCurrentNapiEnv();
1032     napi_handle_scope scope = nullptr;
1033     auto status = napi_open_handle_scope(env, &scope);
1034     if (status != napi_ok) {
1035         return;
1036     }
1037     for (auto listenerPair : abilityContextDidClickListeners_) {
1038         auto ref = listenerPair.first;
1039         auto localInfo = didClickInfos_[ref];
1040         if (info.IsEqual(localInfo)) {
1041             napi_value abilityContext = nullptr;
1042             napi_get_reference_value(env, ref, &abilityContext);
1043 
1044             auto& holder = abilityContextDidClickListeners_[ref];
1045             for (const auto& listener : holder) {
1046                 listener->OnDidClick(gestureEventInfo, clickInfo, frameNode);
1047             }
1048             break;
1049         }
1050     }
1051 
1052     auto currentId = Container::CurrentId();
1053     auto iter = specifiedDidClickListeners_.find(currentId);
1054     if (iter == specifiedDidClickListeners_.end()) {
1055         napi_close_handle_scope(env, scope);
1056         return;
1057     }
1058     auto& holder = iter->second;
1059     for (const auto& listener : holder) {
1060         listener->OnDidClick(gestureEventInfo, clickInfo, frameNode);
1061     }
1062     napi_close_handle_scope(env, scope);
1063 }
1064 
1065 // UIObserver.on(type: "tabContentState", callback)
1066 // register a global listener without options
RegisterTabContentStateCallback(const std::shared_ptr<UIObserverListener> & listener)1067 void UIObserver::RegisterTabContentStateCallback(const std::shared_ptr<UIObserverListener>& listener)
1068 {
1069     if (std::find(tabContentStateListeners_.begin(), tabContentStateListeners_.end(), listener) !=
1070         tabContentStateListeners_.end()) {
1071         return;
1072     }
1073     tabContentStateListeners_.emplace_back(listener);
1074 }
1075 
1076 // UIObserver.on(type: "tabContentState", options, callback)
1077 // register a listener on a specified tabContentState
RegisterTabContentStateCallback(const std::string & id,const std::shared_ptr<UIObserverListener> & listener)1078 void UIObserver::RegisterTabContentStateCallback(
1079     const std::string& id, const std::shared_ptr<UIObserverListener>& listener)
1080 {
1081     auto iter = specifiedTabContentStateListeners_.find(id);
1082     if (iter == specifiedTabContentStateListeners_.end()) {
1083         specifiedTabContentStateListeners_.emplace(id, std::list<std::shared_ptr<UIObserverListener>>({ listener }));
1084         return;
1085     }
1086     auto& holder = iter->second;
1087     if (std::find(holder.begin(), holder.end(), listener) != holder.end()) {
1088         return;
1089     }
1090     holder.emplace_back(listener);
1091 }
1092 
1093 // UIObserver.off(type: "tabContentState", callback)
UnRegisterTabContentStateCallback(napi_value cb)1094 void UIObserver::UnRegisterTabContentStateCallback(napi_value cb)
1095 {
1096     if (cb == nullptr) {
1097         tabContentStateListeners_.clear();
1098         return;
1099     }
1100 
1101     tabContentStateListeners_.erase(
1102         std::remove_if(
1103             tabContentStateListeners_.begin(),
1104             tabContentStateListeners_.end(),
1105             [cb](const std::shared_ptr<UIObserverListener>& registeredListener) {
1106                 return registeredListener->NapiEqual(cb);
1107             }),
1108         tabContentStateListeners_.end()
1109     );
1110 }
1111 
1112 // UIObserver.off(type: "tabContentState", options, callback)
UnRegisterTabContentStateCallback(const std::string & id,napi_value cb)1113 void UIObserver::UnRegisterTabContentStateCallback(const std::string& id, napi_value cb)
1114 {
1115     auto iter = specifiedTabContentStateListeners_.find(id);
1116     if (iter == specifiedTabContentStateListeners_.end()) {
1117         return;
1118     }
1119     auto& holder = iter->second;
1120     if (cb == nullptr) {
1121         holder.clear();
1122         return;
1123     }
1124     holder.erase(
1125         std::remove_if(
1126             holder.begin(),
1127             holder.end(),
1128             [cb](const std::shared_ptr<UIObserverListener>& registeredListener) {
1129                 return registeredListener->NapiEqual(cb);
1130             }),
1131         holder.end()
1132     );
1133 }
1134 
HandleTabContentStateChange(const NG::TabContentInfo & tabContentInfo)1135 void UIObserver::HandleTabContentStateChange(const NG::TabContentInfo& tabContentInfo)
1136 {
1137     for (const auto& listener : tabContentStateListeners_) {
1138         listener->OnTabContentStateChange(tabContentInfo);
1139     }
1140 
1141     auto iter = specifiedTabContentStateListeners_.find(tabContentInfo.id);
1142     if (iter == specifiedTabContentStateListeners_.end()) {
1143         return;
1144     }
1145 
1146     auto& holder = iter->second;
1147 
1148     for (const auto& listener : holder) {
1149         listener->OnTabContentStateChange(tabContentInfo);
1150     }
1151 }
1152 
GetAbilityInfos(napi_env env,napi_value abilityContext,NG::AbilityContextInfo & info)1153 void UIObserver::GetAbilityInfos(napi_env env, napi_value abilityContext, NG::AbilityContextInfo& info)
1154 {
1155     if (!env || !abilityContext) {
1156         return;
1157     }
1158     napi_value napiInfo = nullptr;
1159     napi_get_named_property(env, abilityContext, "abilityInfo", &napiInfo);
1160     CHECK_NULL_VOID(napiInfo);
1161     napi_value name = nullptr;
1162     napi_get_named_property(env, napiInfo, "name", &name);
1163     ParseStringFromNapi(env, name, info.name);
1164     napi_get_named_property(env, napiInfo, "bundleName", &name);
1165     ParseStringFromNapi(env, name, info.bundleName);
1166     napi_get_named_property(env, napiInfo, "moduleName", &name);
1167     ParseStringFromNapi(env, name, info.moduleName);
1168 }
1169 
ParseStringFromNapi(napi_env env,napi_value val,std::string & str)1170 bool UIObserver::ParseStringFromNapi(napi_env env, napi_value val, std::string& str)
1171 {
1172     if (!val || !MatchValueType(env, val, napi_string)) {
1173         return false;
1174     }
1175     size_t len = 0;
1176     napi_get_value_string_utf8(env, val, nullptr, 0, &len);
1177     std::unique_ptr<char[]> result = std::make_unique<char[]>(len + 1);
1178     napi_get_value_string_utf8(env, val, result.get(), len + 1, &len);
1179     str = result.get();
1180     return true;
1181 }
1182 
MatchValueType(napi_env env,napi_value value,napi_valuetype targetType)1183 bool UIObserver::MatchValueType(napi_env env, napi_value value, napi_valuetype targetType)
1184 {
1185     napi_valuetype valueType = napi_undefined;
1186     napi_typeof(env, value, &valueType);
1187     return valueType == targetType;
1188 }
1189 
GetCurrentNapiEnv()1190 napi_env UIObserver::GetCurrentNapiEnv()
1191 {
1192     auto engine = EngineHelper::GetCurrentEngine();
1193     CHECK_NULL_RETURN(engine, nullptr);
1194     NativeEngine* nativeEngine = engine->GetNativeEngine();
1195     CHECK_NULL_RETURN(nativeEngine, nullptr);
1196     return reinterpret_cast<napi_env>(nativeEngine);
1197 }
1198 } // namespace OHOS::Ace::Napi
1199