1 /*
2  * Copyright (c) 2021 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 "event_target.h"
16 
17 #include <cstring>
18 
19 #include "securec.h"
20 #include "utils/log.h"
21 
22 #define LISTENER_TYPTE_MAX_LENGTH 64
23 
24 struct EventListener {
25     char type[LISTENER_TYPTE_MAX_LENGTH] = { 0 };
26     bool isOnce = false;
27     napi_ref handlerRef = nullptr;
28     EventListener* back = nullptr;
29     EventListener* next = nullptr;
30 };
31 
EventTarget(napi_env env,napi_value thisVar)32 EventTarget::EventTarget(napi_env env, napi_value thisVar)
33     : env_(env), thisVarRef_(nullptr), first_(nullptr), last_(nullptr)
34 {
35     napi_create_reference(env, thisVar, 1, &thisVarRef_);
36 }
37 
~EventTarget()38 EventTarget::~EventTarget()
39 {
40     EventListener* temp = nullptr;
41     for (EventListener* i = first_; i != nullptr; i = temp) {
42         temp = i->next;
43         if (i == first_) {
44             first_ = first_->next;
45         } else if (i == last_) {
46             last_ = last_->back;
47         } else {
48             i->next->back = i->back;
49             i->back->next = i->next;
50         }
51         napi_delete_reference(env_, i->handlerRef);
52         delete i;
53     }
54     napi_delete_reference(env_, thisVarRef_);
55 }
56 
On(const char * type,napi_value handler)57 void EventTarget::On(const char* type, napi_value handler)
58 {
59     auto tmp = new EventListener();
60 
61     if (strncpy_s(tmp->type, LISTENER_TYPTE_MAX_LENGTH, type, strlen(type)) != EOK) {
62         delete tmp;
63         tmp = nullptr;
64         return;
65     }
66 
67     if (first_ == nullptr) {
68         first_ = last_ = tmp;
69     } else {
70         last_->next = tmp;
71         last_->next->back = last_;
72         last_ = last_->next;
73     }
74     last_->isOnce = false;
75     napi_create_reference(env_, handler, 1, &last_->handlerRef);
76 }
77 
Once(const char * type,napi_value handler)78 void EventTarget::Once(const char* type, napi_value handler)
79 {
80     auto tmp = new EventListener();
81 
82     if (strncpy_s(tmp->type, LISTENER_TYPTE_MAX_LENGTH, type, strlen(type)) != EOK) {
83         delete tmp;
84         tmp = nullptr;
85         return;
86     }
87 
88     if (first_ == nullptr) {
89         first_ = last_ = tmp;
90     } else {
91         last_->next = tmp;
92         last_->next->back = last_;
93         last_ = last_->next;
94     }
95     last_->isOnce = true;
96     napi_create_reference(env_, handler, 1, &last_->handlerRef);
97 }
98 
Off(const char * type,napi_value handler)99 void EventTarget::Off(const char* type, napi_value handler)
100 {
101     napi_handle_scope scope = nullptr;
102     napi_open_handle_scope(env_, &scope);
103     if (scope == nullptr) {
104         HILOG_ERROR("scope is nullptr");
105         return;
106     }
107 
108     EventListener* temp = nullptr;
109     for (EventListener* eventListener = first_; eventListener != nullptr; eventListener = temp) {
110         temp = eventListener->next;
111         bool isEquals = false;
112         napi_value handlerTemp = nullptr;
113         napi_get_reference_value(env_, eventListener->handlerRef, &handlerTemp);
114         napi_strict_equals(env_, handlerTemp, handler, &isEquals);
115         if (strcmp(eventListener->type, type) == 0 && isEquals) {
116             if (eventListener == first_) {
117                 first_ = first_->next;
118             } else if (eventListener == last_) {
119                 last_ = last_->back;
120             } else {
121                 eventListener->next->back = eventListener->back;
122                 eventListener->back->next = eventListener->next;
123             }
124             napi_delete_reference(env_, eventListener->handlerRef);
125             delete eventListener;
126             eventListener = nullptr;
127             break;
128         }
129     }
130     napi_close_handle_scope(env_, scope);
131 }
132 
Off(const char * type)133 void EventTarget::Off(const char* type)
134 {
135     EventListener* temp = nullptr;
136     for (EventListener* eventListener = first_; eventListener != nullptr; eventListener = temp) {
137         temp = eventListener->next;
138         if (strcmp(eventListener->type, type) == 0) {
139             if (eventListener == first_) {
140                 first_ = first_->next;
141             } else if (eventListener == last_) {
142                 last_ = last_->back;
143             } else {
144                 eventListener->next->back = eventListener->back;
145                 eventListener->back->next = eventListener->next;
146             }
147             napi_delete_reference(env_, eventListener->handlerRef);
148             delete eventListener;
149             eventListener = nullptr;
150         }
151     }
152 }
153 
Emit(const char * type,Event * event)154 void EventTarget::Emit(const char* type, Event* event)
155 {
156     napi_handle_scope scope = nullptr;
157     napi_open_handle_scope(env_, &scope);
158 
159     napi_value thisVar = nullptr;
160     napi_get_reference_value(env_, thisVarRef_, &thisVar);
161     for (EventListener* eventListener = first_; eventListener != nullptr; eventListener = eventListener->next) {
162         if (strcmp(eventListener->type, type) == 0) {
163             napi_value jsEvent = event ? event->ToJsObject() : nullptr;
164             napi_value handler = nullptr;
165             napi_get_reference_value(env_, eventListener->handlerRef, &handler);
166             napi_call_function(env_, thisVar, handler, jsEvent ? 1 : 0, jsEvent ? &jsEvent : nullptr, nullptr);
167             if (eventListener->isOnce) {
168                 Off(type, handler);
169             }
170         }
171     }
172 
173     napi_close_handle_scope(env_, scope);
174 }
175