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
24struct 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
32EventTarget::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
38EventTarget::~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
57void 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
78void 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
99void 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
133void 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
154void 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