Add mainloop interface and backend layer 12/107312/7
authorKyungwook Tak <k.tak@samsung.com>
Tue, 27 Dec 2016 11:42:35 +0000 (20:42 +0900)
committerKyungwook Tak <k.tak@samsung.com>
Mon, 2 Jan 2017 00:38:36 +0000 (09:38 +0900)
popup service needs to use ecore loop based mainloop so as to expand
ecore main loop life cycle to process life cycle to fix restart ecore
loop bug.

Make Mainloop class abstract and declare Event which can converted to
backend specific event enums(e.g., epoll events, ecore events) then
mainloop and service class can be defined generically.

Mainloop backend is selected in each service impl's constructor.

Change-Id: Ie965bbcca45c31a950cbb86649a2d22dd0d494d1
Signed-off-by: Kyungwook Tak <k.tak@samsung.com>
18 files changed:
src/framework/CMakeLists.txt
src/framework/client/async-logic.cpp
src/framework/client/async-logic.h
src/framework/common/mainloop.h
src/framework/common/native-mainloop.cpp [moved from src/framework/common/mainloop.cpp with 60% similarity]
src/framework/common/native-mainloop.h [new file with mode: 0644]
src/framework/common/service.cpp
src/framework/common/service.h
src/framework/common/socket.h
src/framework/service/server-service.cpp
src/framework/ui/popup/CMakeLists.txt
src/framework/ui/popup/ecore-mainloop.cpp [new file with mode: 0644]
src/framework/ui/popup/ecore-mainloop.h [new file with mode: 0644]
src/framework/ui/popup/logic.cpp [changed mode: 0755->0644]
src/framework/ui/popup/logic.h
src/framework/ui/popup/popup-service.cpp
src/framework/ui/popup/popup.cpp
src/framework/ui/popup/popup.h

index 1db36f9..8cad4a3 100644 (file)
@@ -48,7 +48,7 @@ SET(${TARGET_CSR_COMMON}_SRCS
        common/em-context.cpp
        common/kvp-container.cpp
        common/dispatcher.cpp
-       common/mainloop.cpp
+       common/native-mainloop.cpp
        common/service.cpp
        common/socket.cpp
        common/socket-descriptor.cpp
index 43ca501..ebe97f7 100644 (file)
 
 #include <cstdint>
 #include <utility>
-#include <sys/epoll.h>
 
 #include "common/exception.h"
 #include "common/cs-detected.h"
 #include "common/connection.h"
 #include "common/async-protocol.h"
+#include "common/native-mainloop.h"
 #include "common/audit/logger.h"
 
 namespace Csr {
 namespace Client {
 
 AsyncLogic::AsyncLogic(HandleExt *handle, void *userdata) :
-       m_handle(handle), m_userdata(userdata), m_dispatcherAsync(new Dispatcher(SockId::CS))
+       m_loop(new NativeMainloop()),
+       m_handle(handle),
+       m_userdata(userdata),
+       m_dispatcherAsync(new Dispatcher(SockId::CS))
 {
 }
 
@@ -63,15 +66,15 @@ void AsyncLogic::scanHelper(const CommandId &id, const StrSet &s)
        auto fd = this->m_dispatcherAsync->getFd();
        auto cancelEventFd = this->m_cancelSignal.getFd();
 
-       this->m_loop.addEventSource(cancelEventFd, EPOLLIN,
-       [&](uint32_t) {
+       this->m_loop->addEventSource(cancelEventFd, Mainloop::Event::READ,
+       [&](Mainloop::Event) {
                this->m_cancelSignal.receive();
                ThrowExcInfo(ASYNC_EVENT_CANCEL, "Async event cancelled on fd: " << fd);
        });
 
-       this->m_loop.addEventSource(fd, EPOLLIN | EPOLLHUP | EPOLLRDHUP,
-       [&](uint32_t e) {
-               if (e & (EPOLLHUP | EPOLLRDHUP))
+       this->m_loop->addEventSource(fd, Mainloop::Event::READ | Mainloop::Event::CLOSE,
+       [&](Mainloop::Event e) {
+               if ((e & Mainloop::Event::CLOSE) != Mainloop::Event::NONE)
                        ThrowExc(CSR_ERROR_SOCKET, "csr-server might be crashed. Finish async client loop");
 
                // read event
@@ -121,8 +124,7 @@ void AsyncLogic::scanHelper(const CommandId &id, const StrSet &s)
        });
 
        try {
-               while (true)
-                       this->m_loop.dispatch(-1);
+               this->m_loop->run(-1);
        } catch (const Exception &e) {
                switch (e.error()) {
                        case ASYNC_EVENT_COMPLETE:
index 7e82857..6ef4b58 100644 (file)
@@ -46,9 +46,9 @@ public:
 private:
        void scanHelper(const CommandId &id, const StrSet &s);
 
+       std::unique_ptr<Mainloop> m_loop;
        HandleExt *m_handle; // for registering results for auto-release
        void *m_userdata;
-       Mainloop m_loop;
        EventFd m_cancelSignal;
        std::unique_ptr<Dispatcher> m_dispatcherAsync;
 };
index 18b04d2..3333214 100644 (file)
@@ -1,40 +1,41 @@
 /*
- *  Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
  *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *        http://www.apache.org/licenses/LICENSE-2.0
  *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License
- */
-/*
- * @file        mainloop.h
- * @author      Kyungwook Tak (k.tak@samsung.com)
- * @version     1.0
- * @brief       Manageloop of csr-server with epoll
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
  */
 #pragma once
 
 #include <functional>
-#include <mutex>
-#include <unordered_map>
+#include <utility>
+#include <type_traits>
 
 #include "common/macros.h"
 
 namespace Csr {
 
-class API Mainloop {
+class Mainloop {
 public:
-       using Callback = std::function<void(uint32_t event)>;
+       enum class Event : uint32_t {
+               NONE  = 0,
+               READ  = 1 << 0,
+               WRITE = 1 << 1,
+               CLOSE = 1 << 2
+       };
+
+       using Callback = std::function<void(Event event)>;
 
-       Mainloop();
-       virtual ~Mainloop();
+       Mainloop() : m_domainSourceNum(0) {}
+       virtual ~Mainloop() {}
 
        Mainloop(const Mainloop &) = delete;
        Mainloop &operator=(const Mainloop &) = delete;
@@ -43,27 +44,48 @@ public:
 
        // timeout unit: seconds
        // if timeout is negative value, no timeout on idle.
-       void run(int timeout);
+       virtual void run(int timeout) = 0;
 
-       // Moved to public to customize stop condition
-       void dispatch(int timeout);
+       virtual void addEventSource(int fd, Event event, Callback &&callback) = 0;
+       virtual void removeEventSource(int fd) = 0;
 
-       void addEventSource(int fd, uint32_t event, Callback &&callback);
-       void removeEventSource(int fd);
-       size_t countEventSource(void) const;
+       void addDomainEventSource(int fd, Event event, Callback &&callback)
+       {
+               ++this->m_domainSourceNum;
+               this->addEventSource(fd, event, std::move(callback));
+       }
 
-       void setIdleChecker(std::function<bool()> &&idleChecker);
+protected:
+       size_t m_domainSourceNum;
+};
 
-private:
+template<typename T> using Underlying = typename std::underlying_type<T>::type;
+template<typename T>
+constexpr Underlying<T> underlying(T t) { return Underlying<T>(t); }
 
-       bool m_isTimedOut;
-       int m_pollfd;
-       mutable std::mutex m_mutex;
-       std::unordered_map<int, Callback> m_callbacks;
+inline constexpr Mainloop::Event operator&(Mainloop::Event e1, Mainloop::Event e2)
+{
+       return Mainloop::Event(underlying(e1) & underlying(e2));
+}
 
-       std::function<bool()> m_isIdle;
+inline Mainloop::Event &operator&=(Mainloop::Event &e1, Mainloop::Event e2)
+{
+       return e1 = e1 & e2;
+}
 
-       constexpr static size_t MAX_EPOLL_EVENTS = 32;
-};
+inline constexpr Mainloop::Event operator|(Mainloop::Event e1, Mainloop::Event e2)
+{
+       return Mainloop::Event(underlying(e1) | underlying(e2));
+}
 
+inline Mainloop::Event &operator|=(Mainloop::Event &e1, Mainloop::Event e2)
+{
+       return e1 = e1 | e2;
 }
+
+inline constexpr Mainloop::Event operator~(Mainloop::Event e)
+{
+       return Mainloop::Event(~underlying(e));
+}
+
+} // namespace Csr
similarity index 60%
rename from src/framework/common/mainloop.cpp
rename to src/framework/common/native-mainloop.cpp
index 55eb7c8..8e2de51 100644 (file)
  *  limitations under the License
  */
 /*
- * @file        mainloop.cpp
+ * @file        native-mainloop.cpp
  * @author      Kyungwook Tak (k.tak@samsung.com)
  * @version     1.0
  * @brief       Mainloop of csr-server with epoll
  */
-#include "common/mainloop.h"
+#include "common/native-mainloop.h"
 
 #include <system_error>
 #include <sys/epoll.h>
 
 namespace Csr {
 
-Mainloop::Mainloop() :
+uint32_t NativeMainloop::convertFlags(Mainloop::Event events)
+{
+       uint32_t flags = 0;
+
+       if ((events & Mainloop::Event::READ) != Mainloop::Event::NONE)
+               flags |= EPOLLIN;
+       if ((events & Mainloop::Event::WRITE) != Mainloop::Event::NONE)
+               flags |= EPOLLOUT;
+       if ((events & Mainloop::Event::CLOSE) != Mainloop::Event::NONE)
+               flags |= (EPOLLHUP | EPOLLRDHUP);
+
+       return flags;
+}
+
+Mainloop::Event NativeMainloop::convertFlags(uint32_t events)
+{
+       Mainloop::Event flags = Mainloop::Event::NONE;
+
+       if (events & EPOLLIN)
+               flags |= Mainloop::Event::READ;
+       if (events & EPOLLOUT)
+               flags |= Mainloop::Event::WRITE;
+       if (events & (EPOLLHUP | EPOLLRDHUP))
+               flags |= Mainloop::Event::CLOSE;
+
+       return flags;
+}
+
+NativeMainloop::NativeMainloop() :
        m_isTimedOut(false),
        m_pollfd(::epoll_create1(EPOLL_CLOEXEC))
 {
@@ -40,7 +68,7 @@ Mainloop::Mainloop() :
                        "Failed to epoll_create1");
 }
 
-Mainloop::~Mainloop()
+NativeMainloop::~NativeMainloop()
 {
        if (!this->m_isTimedOut && !this->m_callbacks.empty())
                ERROR("mainloop registered callbacks should be empty except timed out case");
@@ -48,7 +76,7 @@ Mainloop::~Mainloop()
        ::close(m_pollfd);
 }
 
-void Mainloop::run(int timeout)
+void NativeMainloop::run(int timeout)
 {
        this->m_isTimedOut = false;
 
@@ -56,21 +84,23 @@ void Mainloop::run(int timeout)
                this->dispatch(timeout);
        }
 
-       DEBUG("Mainloop run stopped");
+       DEBUG("NativeMainloop run stopped");
 }
 
-void Mainloop::addEventSource(int fd, uint32_t event, Callback &&callback)
+void NativeMainloop::addEventSource(
+       int fd, Mainloop::Event event, Mainloop::Callback &&callback)
 {
        std::lock_guard<std::mutex> l(this->m_mutex);
 
        if (this->m_callbacks.count(fd) != 0)
                ThrowExc(CSR_ERROR_SERVER, "event source on fd[" << fd << "] already added!");
 
-       DEBUG("Add event[" << event << "] source on fd[" << fd << "]");
+       DEBUG("Add event[" << static_cast<uint32_t>(event)
+                 << "] source on fd[" << fd << "]");
 
        epoll_event e;
 
-       e.events = event;
+       e.events = NativeMainloop::convertFlags(event);
        e.data.fd = fd;
 
        if (::epoll_ctl(m_pollfd, EPOLL_CTL_ADD, fd, &e) == -1)
@@ -81,16 +111,17 @@ void Mainloop::addEventSource(int fd, uint32_t event, Callback &&callback)
        this->m_callbacks[fd] = std::move(callback);
 }
 
-void Mainloop::removeEventSource(int fd)
+void NativeMainloop::removeEventSource(int fd)
 {
        std::lock_guard<std::mutex> l(this->m_mutex);
 
-       if (this->m_callbacks.count(fd) == 0)
+       auto it = this->m_callbacks.find(fd);
+       if (it == this->m_callbacks.end())
                ThrowExc(CSR_ERROR_SERVER, "event source on fd[" << fd << "] isn't added at all");
 
        DEBUG("Remove event source on fd[" << fd << "]");
 
-       this->m_callbacks.erase(fd);
+       this->m_callbacks.erase(it);
 
        if (::epoll_ctl(m_pollfd, EPOLL_CTL_DEL, fd, nullptr) == -1) {
                if (errno == ENOENT)
@@ -102,18 +133,12 @@ void Mainloop::removeEventSource(int fd)
        }
 }
 
-size_t Mainloop::countEventSource() const
-{
-       std::lock_guard<std::mutex> l(this->m_mutex);
-       return this->m_callbacks.size();
-}
-
-void Mainloop::dispatch(int timeout)
+void NativeMainloop::dispatch(int timeout)
 {
        int nfds = -1;
        epoll_event event[MAX_EPOLL_EVENTS];
 
-       DEBUG("Mainloop dispatched with timeout: " << timeout);
+       DEBUG("NativeMainloop dispatched with timeout: " << timeout);
 
        do {
                nfds = ::epoll_wait(this->m_pollfd, event, MAX_EPOLL_EVENTS,
@@ -126,13 +151,13 @@ void Mainloop::dispatch(int timeout)
                        "epoll_wait failed!");
 
        if (nfds == 0) {
-               DEBUG("Mainloop timed out!");
-               if (this->m_isIdle && !this->m_isIdle()) {
-                       INFO("Mainloop timed out but there's running task on upper layer. "
+               DEBUG("NativeMainloop timed out!");
+               if (this->m_callbacks.size() > this->m_domainSourceNum) {
+                       INFO("NativeMainloop timed out but there's running task on upper layer. "
                                 "Re-dispatch.");
                        this->m_isTimedOut = false;
                } else {
-                       INFO("Mainloop timed out! stop the loop!");
+                       INFO("NativeMainloop timed out! stop the loop!");
                        this->m_isTimedOut = true;
                }
 
@@ -142,24 +167,22 @@ void Mainloop::dispatch(int timeout)
        for (int i = 0; i < nfds; i++) {
                int fd = event[i].data.fd;
 
-               if (this->m_callbacks.count(fd) == 0)
+               auto it = this->m_callbacks.find(fd);
+               if (it == this->m_callbacks.end())
                        ThrowExc(CSR_ERROR_SERVER, "event in on fd[" << fd <<
                                         "] but associated callback isn't exist!");
 
-               if (event[i].events & (EPOLLHUP | EPOLLRDHUP)) {
+               auto events = convertFlags(event[i].events);
+               if ((events & Mainloop::Event::CLOSE) != Mainloop::Event::NONE) {
                        INFO("peer connection closed on fd[" << fd << "]");
-                       event[i].events &= ~EPOLLIN;
+                       events &= ~Mainloop::Event::READ;
                }
 
-               DEBUG("event[" << event[i].events << "] polled on fd[" << fd << "]");
+               DEBUG("event[" << static_cast<uint32_t>(events)
+                         << "] polled on fd[" << fd << "]");
 
-               this->m_callbacks[fd](event[i].events);
+               it->second(events);
        }
 }
 
-void Mainloop::setIdleChecker(std::function<bool()> &&idleChecker)
-{
-       this->m_isIdle = std::move(idleChecker);
-}
-
 }
diff --git a/src/framework/common/native-mainloop.h b/src/framework/common/native-mainloop.h
new file mode 100644 (file)
index 0000000..04d6fa1
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ *  Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+/*
+ * @file        native-mainloop.h
+ * @author      Kyungwook Tak (k.tak@samsung.com)
+ * @version     1.0
+ * @brief       Manageloop of csr-server with epoll
+ */
+#pragma once
+
+#include <functional>
+#include <mutex>
+#include <unordered_map>
+
+#include "common/mainloop.h"
+#include "common/macros.h"
+
+namespace Csr {
+
+class API NativeMainloop : public Mainloop {
+public:
+       NativeMainloop();
+       virtual ~NativeMainloop();
+
+       virtual void run(int timeout) override;
+       virtual void addEventSource(
+               int fd, Mainloop::Event event, Mainloop::Callback &&callback) override;
+       virtual void removeEventSource(int fd) override;
+
+private:
+       void dispatch(int timeout);
+       static uint32_t convertFlags(Mainloop::Event events);
+       static Mainloop::Event convertFlags(uint32_t events);
+
+       bool m_isTimedOut;
+       int m_pollfd;
+       mutable std::mutex m_mutex;
+       std::unordered_map<int, Mainloop::Callback> m_callbacks;
+
+       constexpr static size_t MAX_EPOLL_EVENTS = 32;
+};
+
+}
index aef65e6..36430d0 100644 (file)
 
 namespace Csr {
 
-Service::Service() noexcept
+Service::Service(Mainloop *mainloop)
 {
+       if (mainloop == nullptr)
+               throw std::invalid_argument("mainloop shouldn't be null");
+
+       this->m_loop.reset(mainloop);
 }
 
 Service::~Service()
@@ -53,16 +57,18 @@ void Service::start(int timeout)
                DEBUG("Get systemd socket[" << socket->getFd() <<
                          "] for sock id: " << static_cast<int>(id));
 
-               this->m_loop.addEventSource(socket->getFd(), EPOLLIN | EPOLLHUP | EPOLLRDHUP,
-               [this, socket](uint32_t event) {
-                       if (event != EPOLLIN)
-                               return;
+               this->m_loop->addDomainEventSource(
+                       socket->getFd(),
+                       Mainloop::Event::READ | Mainloop::Event::CLOSE,
+                       [this, socket](Mainloop::Event events) {
+                               if ((events & Mainloop::Event::READ) == Mainloop::Event::NONE)
+                                       return;
 
-                       this->onNewConnection(std::make_shared<Connection>(socket->accept()));
-               });
+                               this->onNewConnection(std::make_shared<Connection>(socket->accept()));
+                       });
        }
 
-       this->m_loop.run(timeout);
+       this->m_loop->run(timeout);
 }
 
 void Service::onNewConnection(ConnShPtr &&connection)
@@ -74,28 +80,29 @@ void Service::onNewConnection(ConnShPtr &&connection)
 
        INFO("welcome! accepted client socket fd[" << fd << "]");
 
-       this->m_loop.addEventSource(fd, EPOLLIN | EPOLLHUP | EPOLLRDHUP,
-       [&, fd](uint32_t event) {
-               std::lock_guard<std::mutex> lock(this->m_crMtx);
+       this->m_loop->addEventSource(fd,
+               Mainloop::Event::READ | Mainloop::Event::CLOSE,
+               [&, fd](Mainloop::Event events) {
+                       std::lock_guard<std::mutex> lock(this->m_crMtx);
 
-               DEBUG("read event comes in to fd[" << fd << "]");
+                       DEBUG("read event comes in to fd[" << fd << "]");
 
-               if (this->m_connectionRegistry.count(fd) == 0)
-                       ThrowExc(CSR_ERROR_SERVER, "get event on fd[" << fd <<
-                                        "] but no associated connection exist");
+                       auto it = this->m_connectionRegistry.find(fd);
+                       if (it == this->m_connectionRegistry.end())
+                               ThrowExc(CSR_ERROR_SERVER, "get event on fd[" << fd <<
+                                                "] but no associated connection exist");
 
-               auto &conn = this->m_connectionRegistry[fd];
-
-               if (event & (EPOLLHUP | EPOLLRDHUP)) {
-                       DEBUG("event of epoll hup. close connection on fd[" << fd << "]");
-                       this->onCloseConnection(conn);
-                       return;
-               }
+                       auto &conn = it->second;
 
-               DEBUG("Start message process on fd[" << fd << "]");
+                       if ((events & Mainloop::Event::CLOSE) != Mainloop::Event::NONE) {
+                               DEBUG("close event occured. close connection on fd[" << fd << "]");
+                               this->onCloseConnection(conn);
+                               return;
+                       }
 
-               onMessageProcess(conn);
-       });
+                       this->onMessageProcess(conn);
+               }
+       );
 
        std::lock_guard<std::mutex> lock(this->m_crMtx);
        this->m_connectionRegistry[fd] = std::move(connection);
@@ -108,15 +115,15 @@ void Service::onCloseConnection(const ConnShPtr &connection)
 
        auto fd = connection->getFd();
 
-       if (this->m_connectionRegistry.count(fd) == 0)
+       auto it = this->m_connectionRegistry.find(fd);
+       if (it == this->m_connectionRegistry.end())
                ThrowExc(CSR_ERROR_SERVER, "no connection in registry to remove "
                                 "associated to fd[" << fd << "]");
 
        INFO("good-bye! close socket fd[" << fd << "]");
 
-       this->m_loop.removeEventSource(fd);
-
-       this->m_connectionRegistry.erase(fd);
+       this->m_loop->removeEventSource(fd);
+       this->m_connectionRegistry.erase(it);
 }
 
 }
index 0e4195c..77ca336 100644 (file)
@@ -21,9 +21,9 @@
  */
 #pragma once
 
-#include <string>
-#include <functional>
 #include <set>
+#include <memory>
+#include <unordered_map>
 #include <mutex>
 
 #include "common/macros.h"
@@ -35,7 +35,7 @@ namespace Csr {
 
 class API Service {
 public:
-       Service() noexcept;
+       Service(Mainloop *mainloop);
        virtual ~Service();
 
        Service(const Service &) = delete;
@@ -47,7 +47,7 @@ public:
        virtual void start(int timeout) final;
 
 protected:
-       Mainloop m_loop;
+       std::unique_ptr<Mainloop> m_loop;
 
 private:
        virtual void onMessageProcess(const ConnShPtr &) = 0;
index 52a6a8a..24fd296 100644 (file)
@@ -23,7 +23,6 @@
 
 #include <string>
 
-#include "common/macros.h"
 #include "common/types.h"
 #include "common/socket-descriptor.h"
 
index 24bd1bf..b540269 100644 (file)
@@ -34,6 +34,7 @@
 #include "common/wp-result.h"
 #include "common/exception.h"
 #include "common/async-protocol.h"
+#include "common/native-mainloop.h"
 #include "service/exception.h"
 #include "service/access-control.h"
 #include "service/core-usage.h"
@@ -84,7 +85,7 @@ inline CommandId extractCommandId(BinaryQueue &q)
 
 } // namespace anonymous
 
-ServerService::ServerService() : Service(), m_workqueue(5)
+ServerService::ServerService() : Service(new NativeMainloop()), m_workqueue(5)
 {
        this->m_db = std::make_shared<Db::Manager>(RW_DBSPACE "/.csr.db", RO_DBSPACE);
 
@@ -111,11 +112,6 @@ ServerService::ServerService() : Service(), m_workqueue(5)
        this->add(SockId::CS);
        this->add(SockId::WP);
        this->add(SockId::ADMIN);
-
-       // if task is not running in workqueue, it's idle.
-       this->m_loop.setIdleChecker([this]()->bool {
-               return (!this->m_workqueue.isTaskRunning() && this->m_loop.countEventSource() == 3);
-       });
 }
 
 RawBuffer ServerService::processCs(const ConnShPtr &conn, RawBuffer &data)
index 6f8dd01..ddc667b 100644 (file)
@@ -39,6 +39,7 @@ SET(${TARGET_CSR_POPUP}_SRCS
        logic.cpp
        popup.cpp
        popup-service.cpp
+       ecore-mainloop.cpp
        package-info.cpp
        ${PROJECT_SOURCE_DIR}/src/framework/ui/common.cpp
 )
diff --git a/src/framework/ui/popup/ecore-mainloop.cpp b/src/framework/ui/popup/ecore-mainloop.cpp
new file mode 100644 (file)
index 0000000..4e97e60
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#include "ecore-mainloop.h"
+
+#include <utility>
+#include <exception>
+
+#include "common/audit/logger.h"
+
+namespace Csr {
+namespace Ui {
+
+Ecore_Fd_Handler_Flags EcoreMainloop::convertFlags(Mainloop::Event events)
+{
+       uint32_t flags = 0;
+
+       if ((events & Mainloop::Event::READ) != Mainloop::Event::NONE)
+               flags |= ECORE_FD_READ;
+       if ((events & Mainloop::Event::WRITE) != Mainloop::Event::NONE)
+               flags |= ECORE_FD_WRITE;
+
+       return static_cast<Ecore_Fd_Handler_Flags>(flags);
+}
+
+Mainloop::Event EcoreMainloop::convertFlags(Ecore_Fd_Handler *handler)
+{
+       Mainloop::Event flags = Mainloop::Event::NONE;
+
+       if (ecore_main_fd_handler_active_get(handler, ECORE_FD_READ))
+               flags |= Mainloop::Event::READ;
+       if (ecore_main_fd_handler_active_get(handler, ECORE_FD_WRITE))
+               flags |= Mainloop::Event::WRITE;
+       if (ecore_main_fd_handler_active_get(handler, ECORE_FD_ERROR))
+               flags |= Mainloop::Event::CLOSE;
+
+       return flags;
+}
+
+EcoreMainloop::EcoreMainloop() : m_isExitCalled(false), m_timer(nullptr) {}
+
+EcoreMainloop::~EcoreMainloop()
+{
+       if (this->m_timer)
+               ecore_timer_del(this->m_timer);
+
+       if (!this->m_isExitCalled)
+               elm_exit();
+}
+
+void EcoreMainloop::run(int timeout)
+{
+       this->dispatch(timeout);
+}
+
+void EcoreMainloop::dispatch(int timeout)
+{
+       if (timeout >= 0) {
+               this->m_timer = ecore_timer_add(
+                       static_cast<double>(timeout), &EcoreMainloop::ecoreTimeoutCb, this);
+
+               if (this->m_timer == nullptr) {
+                       ERROR("Failed to ecore_timer_add()...");
+               }
+       }
+
+       elm_run();
+}
+
+void EcoreMainloop::addEventSource(
+       int fd, Mainloop::Event events, Mainloop::Callback &&callback)
+{
+       auto handler = ecore_main_fd_handler_add(
+               fd, convertFlags(events),
+               &EcoreMainloop::ecoreFdCallback, this, nullptr, nullptr);
+
+       if (handler == nullptr) {
+               ERROR("failed to handle fd(" << fd << ") by ecore_main_fd_handler_add()");
+               return;
+       }
+
+       std::unique_ptr<EventSource> source(new EventSource);
+       source->callback = std::move(callback);
+       source->handler = handler;
+
+       this->m_sources[fd] = std::move(source);
+}
+
+void EcoreMainloop::removeEventSource(int fd)
+{
+       auto it = this->m_sources.find(fd);
+
+       if (it == this->m_sources.end()) {
+               ERROR("fd(" << fd << ") associated source is not found");
+               return;
+       }
+
+       ecore_main_fd_handler_del(it->second->handler);
+
+       this->m_sources.erase(it);
+}
+
+Eina_Bool EcoreMainloop::ecoreFdCallback(void *data, Ecore_Fd_Handler *handler)
+{
+       auto mainloop = static_cast<EcoreMainloop *>(data);
+
+       if (mainloop == nullptr)
+               throw std::invalid_argument("userdata for ecore fd callback is invalid");
+
+       int fd = ecore_main_fd_handler_fd_get(handler);
+
+       auto it = mainloop->m_sources.find(fd);
+       if (it == mainloop->m_sources.end() || it->second == nullptr) {
+               ERROR("no associated source found with fd: " << fd);
+               return ECORE_CALLBACK_CANCEL;
+       }
+
+       it->second->callback(convertFlags(handler));
+
+       return ECORE_CALLBACK_RENEW;
+}
+
+Eina_Bool EcoreMainloop::ecoreTimeoutCb(void *data)
+{
+       auto mainloop = static_cast<EcoreMainloop *>(data);
+
+       if (mainloop == nullptr)
+               throw std::invalid_argument("userdata for ecore timeout callback is invalid");
+
+       if (mainloop->m_sources.size() == mainloop->m_domainSourceNum) {
+               INFO("There's no alive connection. Only listening socket opened. "
+                        "Let's exit the ecore main loop!");
+
+               elm_exit();
+
+               mainloop->m_timer = nullptr;
+               mainloop->m_isExitCalled = true;
+
+               return ECORE_CALLBACK_CANCEL;
+       } else {
+               INFO("Time out expired but there's alive connection "
+                        "so go ahead to next tick!");
+               return ECORE_CALLBACK_RENEW;
+       }
+}
+
+} // namespace Ui
+} // namespace Csr
diff --git a/src/framework/ui/popup/ecore-mainloop.h b/src/framework/ui/popup/ecore-mainloop.h
new file mode 100644 (file)
index 0000000..271b011
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+#pragma once
+
+#include <unordered_map>
+#include <memory>
+#include <Elementary.h>
+
+#include "common/mainloop.h"
+
+namespace Csr {
+namespace Ui {
+
+class EcoreMainloop : public Mainloop {
+public:
+       EcoreMainloop();
+       virtual ~EcoreMainloop();
+
+       virtual void run(int timeout) override;
+
+       virtual void addEventSource(
+               int fd, Mainloop::Event event, Mainloop::Callback &&callback) override;
+       virtual void removeEventSource(int fd) override;
+
+private:
+       struct EventSource {
+               Mainloop::Callback callback;
+               Ecore_Fd_Handler *handler;
+       };
+
+       void dispatch(int timeout);
+
+       static Ecore_Fd_Handler_Flags convertFlags(Mainloop::Event events);
+       static Mainloop::Event convertFlags(Ecore_Fd_Handler *handler);
+
+       static Eina_Bool ecoreFdCallback(void *data, Ecore_Fd_Handler *handler);
+       static Eina_Bool ecoreTimeoutCb(void *data);
+
+       std::unordered_map<int, std::unique_ptr<EventSource>> m_sources;
+
+       bool m_isExitCalled;
+       Ecore_Timer *m_timer;
+};
+
+} // namespace Ui
+} // namespace Csr
old mode 100755 (executable)
new mode 100644 (file)
index ce8c83b..7cb5999
@@ -65,254 +65,230 @@ std::string formatToString(const char *form, std::string arg)
 }
 } // namespace anonymous
 
-RawBuffer Logic::csPromptData(const std::string &message, const CsDetected &d) const
+void Logic::csPromptData(const std::string &message, const CsDetected &d)
 {
        std::string risk(d.severity ==
                CSR_CS_SEVERITY_LOW ? LABEL_RISK_LEVEL_LOW : LABEL_RISK_LEVEL_MEDIUM);
 
-       Popup p(2);
+       m_popup.setButtons(2);
 
-       p.setMessage(message);
-       p.setTitle(CS_TITLE);
-       p.setHeader(CS_PROMPT_DATA_HEADER);
-       p.setBody(FORMAT(
+       m_popup.setMessage(message);
+       m_popup.setTitle(CS_TITLE);
+       m_popup.setHeader(CS_PROMPT_DATA_HEADER);
+       m_popup.setBody(FORMAT(
                "- " << LABEL_RISK << risk << " (" << d.malwareName << ")"));
-       p.setFooter(CS_PROMPT_DATA_FOOTER);
+       m_popup.setFooter(CS_PROMPT_DATA_FOOTER);
 
-       p.setText(p.m_buttons[0], BTN_CANCEL);
-       p.setText(p.m_buttons[1], BTN_OPEN);
+       m_popup.setText(m_popup.m_buttons[0], BTN_CANCEL);
+       m_popup.setText(m_popup.m_buttons[1], BTN_OPEN);
 
-       p.m_types.emplace_back(
+       m_popup.m_types.emplace_back(
                static_cast<int>(CSR_CS_USER_RESPONSE_PROCESSING_DISALLOWED));
-       p.m_types.emplace_back(
+       m_popup.m_types.emplace_back(
                static_cast<int>(CSR_CS_USER_RESPONSE_PROCESSING_ALLOWED));
 
-       p.callbackRegister(p.m_buttons[0], &p.m_types[0]);
-       p.callbackRegister(p.m_buttons[1], &p.m_types[1]);
-       p.callbackRegister(p.m_hypertext, d.detailedUrl);
-
-       p.run();
-       return p.getResult();
+       m_popup.callbackRegister(m_popup.m_buttons[0], &m_popup.m_types[0]);
+       m_popup.callbackRegister(m_popup.m_buttons[1], &m_popup.m_types[1]);
+       m_popup.callbackRegister(m_popup.m_hypertext, d.detailedUrl);
 }
 
-RawBuffer Logic::csPromptFile(const std::string &message, const CsDetected &d) const
+void Logic::csPromptFile(const std::string &message, const CsDetected &d)
 {
        std::string risk(d.severity ==
                CSR_CS_SEVERITY_LOW ? LABEL_RISK_LEVEL_LOW : LABEL_RISK_LEVEL_MEDIUM);
        std::string fileName, extraPath;
        split(d.targetName, fileName, extraPath);
 
-       Popup p(3);
+       m_popup.setButtons(3);
 
-       p.setMessage(message);
-       p.setTitle(CS_TITLE);
-       p.setHeader(CS_PROMPT_FILE_HEADER);
-       p.setBody(FORMAT(
+       m_popup.setMessage(message);
+       m_popup.setTitle(CS_TITLE);
+       m_popup.setHeader(CS_PROMPT_FILE_HEADER);
+       m_popup.setBody(FORMAT(
                "- " << formatToString(LABEL_FILE_NAME, fileName) << "<br>" <<
                "- " << formatToString(LABEL_FILE_PATH, extraPath) << "<br>" <<
                "- " << LABEL_RISK << risk << " (" << d.malwareName << ")"));
        std::string prefix(CS_NOTIFY_FILE_FOOTER);
-       p.setFooter(FORMAT(prefix << "<br>" << CS_PROMPT_FILE_FOOTER));
+       m_popup.setFooter(FORMAT(prefix << "<br>" << CS_PROMPT_FILE_FOOTER));
 
-       p.setText(p.m_buttons[0], BTN_CANCEL);
-       p.setText(p.m_buttons[1], BTN_OPEN);
-       p.setText(p.m_buttons[2], BTN_DELETE);
+       m_popup.setText(m_popup.m_buttons[0], BTN_CANCEL);
+       m_popup.setText(m_popup.m_buttons[1], BTN_OPEN);
+       m_popup.setText(m_popup.m_buttons[2], BTN_DELETE);
 
-       p.m_types.emplace_back(
+       m_popup.m_types.emplace_back(
                static_cast<int>(CSR_CS_USER_RESPONSE_PROCESSING_DISALLOWED));
-       p.m_types.emplace_back(
+       m_popup.m_types.emplace_back(
                static_cast<int>(CSR_CS_USER_RESPONSE_PROCESSING_ALLOWED));
-       p.m_types.emplace_back(
+       m_popup.m_types.emplace_back(
                static_cast<int>(CSR_CS_USER_RESPONSE_REMOVE));
 
-       p.callbackRegister(p.m_buttons[0], &p.m_types[0]);
-       p.callbackRegister(p.m_buttons[1], &p.m_types[1]);
-       p.callbackRegister(p.m_buttons[2], &p.m_types[2]);
-       p.callbackRegister(p.m_hypertext, d.detailedUrl);
-
-       p.run();
-       return p.getResult();
+       m_popup.callbackRegister(m_popup.m_buttons[0], &m_popup.m_types[0]);
+       m_popup.callbackRegister(m_popup.m_buttons[1], &m_popup.m_types[1]);
+       m_popup.callbackRegister(m_popup.m_buttons[2], &m_popup.m_types[2]);
+       m_popup.callbackRegister(m_popup.m_hypertext, d.detailedUrl);
 }
 
-RawBuffer Logic::csPromptApp(const std::string &message, const CsDetected &d) const
+void Logic::csPromptApp(const std::string &message, const CsDetected &d)
 {
        std::string risk(d.severity ==
                CSR_CS_SEVERITY_LOW ? LABEL_RISK_LEVEL_LOW : LABEL_RISK_LEVEL_MEDIUM);
        PackageInfo info(d.pkgId);
 
-       Popup p(3);
+       m_popup.setButtons(3);
 
-       p.setMessage(message);
-       p.setTitle(CS_TITLE);
-       p.setHeader(CS_PROMPT_APP_HEADER);
-       p.setBody(FORMAT(
+       m_popup.setMessage(message);
+       m_popup.setTitle(CS_TITLE);
+       m_popup.setHeader(CS_PROMPT_APP_HEADER);
+       m_popup.setBody(FORMAT(
                LABEL_APP_NAME << info.getLabel() << "<br>" <<
                LABEL_VERSION << info.getVersion() << "<br>" <<
                LABEL_RISK << risk << " (" << d.malwareName << ")"));
-       p.setIcon(info.getIconPath());
+       m_popup.setIcon(info.getIconPath());
        std::string prefix = formatToString(CS_NOTIFY_APP_FOOTER, BTN_UNINSTALL);
-       p.setFooter(FORMAT(prefix << "<br>" << CS_PROMPT_APP_FOOTER));
+       m_popup.setFooter(FORMAT(prefix << "<br>" << CS_PROMPT_APP_FOOTER));
 
-       p.setText(p.m_buttons[0], BTN_CANCEL);
-       p.setText(p.m_buttons[1], BTN_OPEN);
-       p.setText(p.m_buttons[2], BTN_UNINSTALL);
+       m_popup.setText(m_popup.m_buttons[0], BTN_CANCEL);
+       m_popup.setText(m_popup.m_buttons[1], BTN_OPEN);
+       m_popup.setText(m_popup.m_buttons[2], BTN_UNINSTALL);
 
-       p.m_types.emplace_back(
+       m_popup.m_types.emplace_back(
                static_cast<int>(CSR_CS_USER_RESPONSE_PROCESSING_DISALLOWED));
-       p.m_types.emplace_back(
+       m_popup.m_types.emplace_back(
                static_cast<int>(CSR_CS_USER_RESPONSE_PROCESSING_ALLOWED));
-       p.m_types.emplace_back(
+       m_popup.m_types.emplace_back(
                static_cast<int>(CSR_CS_USER_RESPONSE_REMOVE));
 
-       p.callbackRegister(p.m_buttons[0], &p.m_types[0]);
-       p.callbackRegister(p.m_buttons[1], &p.m_types[1]);
-       p.callbackRegister(p.m_buttons[2], &p.m_types[2]);
-       p.callbackRegister(p.m_hypertext, d.detailedUrl);
-
-       p.run();
-       return p.getResult();
+       m_popup.callbackRegister(m_popup.m_buttons[0], &m_popup.m_types[0]);
+       m_popup.callbackRegister(m_popup.m_buttons[1], &m_popup.m_types[1]);
+       m_popup.callbackRegister(m_popup.m_buttons[2], &m_popup.m_types[2]);
+       m_popup.callbackRegister(m_popup.m_hypertext, d.detailedUrl);
 }
 
-RawBuffer Logic::csNotifyData(const std::string &message, const CsDetected &d) const
+void Logic::csNotifyData(const std::string &message, const CsDetected &d)
 {
-       Popup p(1);
+       m_popup.setButtons(1);
 
-       p.setMessage(message);
-       p.setTitle(CS_TITLE);
-       p.setHeader(CS_NOTIFY_DATA_HEADER);
-       p.setBody(FORMAT(
+       m_popup.setMessage(message);
+       m_popup.setTitle(CS_TITLE);
+       m_popup.setHeader(CS_NOTIFY_DATA_HEADER);
+       m_popup.setBody(FORMAT(
                "- " << LABEL_RISK << LABEL_RISK_LEVEL_HIGH <<
                " (" << d.malwareName << ")"));
-       p.setFooter(CS_NOTIFY_DATA_FOOTER);
+       m_popup.setFooter(CS_NOTIFY_DATA_FOOTER);
 
-       p.setText(p.m_buttons[0], BTN_OK);
+       m_popup.setText(m_popup.m_buttons[0], BTN_OK);
 
-       p.m_types.emplace_back(
+       m_popup.m_types.emplace_back(
                static_cast<int>(CSR_CS_USER_RESPONSE_PROCESSING_DISALLOWED));
 
-       p.callbackRegister(p.m_buttons[0], &p.m_types[0]);
-       p.callbackRegister(p.m_hypertext, d.detailedUrl);
-
-       p.run();
-       return p.getResult();
+       m_popup.callbackRegister(m_popup.m_buttons[0], &m_popup.m_types[0]);
+       m_popup.callbackRegister(m_popup.m_hypertext, d.detailedUrl);
 }
 
-RawBuffer Logic::csNotifyFile(const std::string &message, const CsDetected &d) const
+void Logic::csNotifyFile(const std::string &message, const CsDetected &d)
 {
-       Popup p(2);
+       m_popup.setButtons(2);
        std::string fileName, extraPath;
        split(d.targetName, fileName, extraPath);
 
-       p.setMessage(message);
-       p.setTitle(CS_TITLE);
-       p.setHeader(CS_NOTIFY_FILE_HEADER);
-       p.setBody(FORMAT(
+       m_popup.setMessage(message);
+       m_popup.setTitle(CS_TITLE);
+       m_popup.setHeader(CS_NOTIFY_FILE_HEADER);
+       m_popup.setBody(FORMAT(
                "- " << formatToString(LABEL_FILE_NAME, fileName) << "<br>" <<
                "- " << formatToString(LABEL_FILE_PATH, extraPath) << "<br>" <<
                "- " << LABEL_RISK << LABEL_RISK_LEVEL_HIGH <<
                " (" << d.malwareName << ")"));
-       p.setFooter(CS_NOTIFY_FILE_FOOTER);
+       m_popup.setFooter(CS_NOTIFY_FILE_FOOTER);
 
-       p.setText(p.m_buttons[0], BTN_CANCEL);
-       p.setText(p.m_buttons[1], BTN_DELETE);
+       m_popup.setText(m_popup.m_buttons[0], BTN_CANCEL);
+       m_popup.setText(m_popup.m_buttons[1], BTN_DELETE);
 
-       p.m_types.emplace_back(
+       m_popup.m_types.emplace_back(
                static_cast<int>(CSR_CS_USER_RESPONSE_PROCESSING_DISALLOWED));
-       p.m_types.emplace_back(
+       m_popup.m_types.emplace_back(
                static_cast<int>(CSR_CS_USER_RESPONSE_REMOVE));
 
-       p.callbackRegister(p.m_buttons[0], &p.m_types[0]);
-       p.callbackRegister(p.m_buttons[1], &p.m_types[1]);
-       p.callbackRegister(p.m_hypertext, d.detailedUrl);
-
-       p.run();
-       return p.getResult();
+       m_popup.callbackRegister(m_popup.m_buttons[0], &m_popup.m_types[0]);
+       m_popup.callbackRegister(m_popup.m_buttons[1], &m_popup.m_types[1]);
+       m_popup.callbackRegister(m_popup.m_hypertext, d.detailedUrl);
 }
 
-RawBuffer Logic::csNotifyApp(const std::string &message, const CsDetected &d) const
+void Logic::csNotifyApp(const std::string &message, const CsDetected &d)
 {
        PackageInfo info(d.pkgId);
 
-       Popup p(2);
+       m_popup.setButtons(2);
 
-       p.setMessage(message);
-       p.setTitle(CS_TITLE);
-       p.setHeader(CS_NOTIFY_APP_HEADER);
-       p.setIcon(info.getIconPath());
-       p.setBody(FORMAT(
+       m_popup.setMessage(message);
+       m_popup.setTitle(CS_TITLE);
+       m_popup.setHeader(CS_NOTIFY_APP_HEADER);
+       m_popup.setIcon(info.getIconPath());
+       m_popup.setBody(FORMAT(
                LABEL_APP_NAME << info.getLabel() << "<br>" <<
                LABEL_VERSION << info.getVersion() << "<br>" <<
                LABEL_RISK << LABEL_RISK_LEVEL_HIGH << " (" << d.malwareName << ")"));
-       p.setFooter(formatToString(CS_NOTIFY_APP_FOOTER, BTN_UNINSTALL));
+       m_popup.setFooter(formatToString(CS_NOTIFY_APP_FOOTER, BTN_UNINSTALL));
 
-       p.setText(p.m_buttons[0], BTN_CANCEL);
-       p.setText(p.m_buttons[1], BTN_UNINSTALL);
+       m_popup.setText(m_popup.m_buttons[0], BTN_CANCEL);
+       m_popup.setText(m_popup.m_buttons[1], BTN_UNINSTALL);
 
-       p.m_types.emplace_back(
+       m_popup.m_types.emplace_back(
                static_cast<int>(CSR_CS_USER_RESPONSE_PROCESSING_DISALLOWED));
-       p.m_types.emplace_back(
+       m_popup.m_types.emplace_back(
                static_cast<int>(CSR_CS_USER_RESPONSE_PROCESSING_ALLOWED));
 
-       p.callbackRegister(p.m_buttons[0], &p.m_types[0]);
-       p.callbackRegister(p.m_buttons[1], &p.m_types[1]);
-       p.callbackRegister(p.m_hypertext, d.detailedUrl);
-
-       p.run();
-       return p.getResult();
+       m_popup.callbackRegister(m_popup.m_buttons[0], &m_popup.m_types[0]);
+       m_popup.callbackRegister(m_popup.m_buttons[1], &m_popup.m_types[1]);
+       m_popup.callbackRegister(m_popup.m_hypertext, d.detailedUrl);
 }
 
-RawBuffer Logic::wpPrompt(const std::string &message, const UrlItem &item) const
+void Logic::wpPrompt(const std::string &message, const UrlItem &item)
 {
        std::string risk(item.risk ==
                CSR_WP_RISK_LOW ? LABEL_RISK_LEVEL_LOW : LABEL_RISK_LEVEL_MEDIUM);
 
-       Popup p(2);
+       m_popup.setButtons(2);
 
-       p.setMessage(message);
-       p.setTitle(WP_TITLE);
-       p.setHeader(WP_PROMPT_HEADER);
-       p.setBody(FORMAT(
+       m_popup.setMessage(message);
+       m_popup.setTitle(WP_TITLE);
+       m_popup.setHeader(WP_PROMPT_HEADER);
+       m_popup.setBody(FORMAT(
                "- " << formatToString(LABEL_URL, item.url) << "<br>" <<
                "- " << LABEL_RISK << risk));
-       p.setFooter(WP_PROMPT_FOOTER);
+       m_popup.setFooter(WP_PROMPT_FOOTER);
 
-       p.setText(p.m_buttons[0], BTN_BLOCK);
-       p.setText(p.m_buttons[1], BTN_VIEW);
+       m_popup.setText(m_popup.m_buttons[0], BTN_BLOCK);
+       m_popup.setText(m_popup.m_buttons[1], BTN_VIEW);
 
-       p.m_types.emplace_back(
+       m_popup.m_types.emplace_back(
                static_cast<int>(CSR_WP_USER_RESPONSE_PROCESSING_DISALLOWED));
-       p.m_types.emplace_back(
+       m_popup.m_types.emplace_back(
                static_cast<int>(CSR_WP_USER_RESPONSE_PROCESSING_ALLOWED));
 
-       p.callbackRegister(p.m_buttons[0], &p.m_types[0]);
-       p.callbackRegister(p.m_buttons[1], &p.m_types[1]);
-       p.callbackRegister(p.m_hypertext, item.url);
-
-       p.run();
-       return p.getResult();
+       m_popup.callbackRegister(m_popup.m_buttons[0], &m_popup.m_types[0]);
+       m_popup.callbackRegister(m_popup.m_buttons[1], &m_popup.m_types[1]);
+       m_popup.callbackRegister(m_popup.m_hypertext, item.url);
 }
 
-RawBuffer Logic::wpNotify(const std::string &message, const UrlItem &item) const
+void Logic::wpNotify(const std::string &message, const UrlItem &item)
 {
-       Popup p(1);
+       m_popup.setButtons(1);
 
-       p.setMessage(message);
-       p.setTitle(WP_TITLE);
-       p.setHeader(WP_NOTIFY_HEADER);
-       p.setBody(FORMAT(
+       m_popup.setMessage(message);
+       m_popup.setTitle(WP_TITLE);
+       m_popup.setHeader(WP_NOTIFY_HEADER);
+       m_popup.setBody(FORMAT(
                "- " << formatToString(LABEL_URL, item.url) << "<br>" <<
                "- " << LABEL_RISK << LABEL_RISK_LEVEL_HIGH));
-       p.setFooter(WP_NOTIFY_FOOTER);
-
-       p.setText(p.m_buttons[0], BTN_OK);
+       m_popup.setFooter(WP_NOTIFY_FOOTER);
 
-       p.m_types.emplace_back(static_cast<int>(CSR_WP_USER_RESPONSE_PROCESSING_DISALLOWED));
+       m_popup.setText(m_popup.m_buttons[0], BTN_OK);
 
-       p.callbackRegister(p.m_buttons[0], &p.m_types[0]);
-       p.callbackRegister(p.m_hypertext, item.url);
+       m_popup.m_types.emplace_back(static_cast<int>(CSR_WP_USER_RESPONSE_PROCESSING_DISALLOWED));
 
-       p.run();
-       return p.getResult();
+       m_popup.callbackRegister(m_popup.m_buttons[0], &m_popup.m_types[0]);
+       m_popup.callbackRegister(m_popup.m_hypertext, item.url);
 }
 } // namespace Ui
 } // namespace Csr
index 50a50dd..bdde6cd 100644 (file)
 #include "common/types.h"
 #include "common/cs-detected.h"
 #include "ui/common.h"
+#include "popup.h"
 
 namespace Csr {
 namespace Ui {
 
 class Logic {
 public:
-       Logic() = default;
-       virtual ~Logic() = default;
-
-       RawBuffer csPromptData(const std::string &, const CsDetected &) const;
-       RawBuffer csPromptApp(const std::string &, const CsDetected &) const;
-       RawBuffer csPromptFile(const std::string &, const CsDetected &) const;
-       RawBuffer csNotifyData(const std::string &, const CsDetected &) const;
-       RawBuffer csNotifyApp(const std::string &, const CsDetected &) const;
-       RawBuffer csNotifyFile(const std::string &, const CsDetected &) const;
-
-       RawBuffer wpPrompt(const std::string &, const UrlItem &) const;
-       RawBuffer wpNotify(const std::string &, const UrlItem &) const;
+       Logic() {}
+       virtual ~Logic() {}
+
+       void csPromptData(const std::string &, const CsDetected &);
+       void csPromptApp(const std::string &, const CsDetected &);
+       void csPromptFile(const std::string &, const CsDetected &);
+       void csNotifyData(const std::string &, const CsDetected &);
+       void csNotifyApp(const std::string &, const CsDetected &);
+       void csNotifyFile(const std::string &, const CsDetected &);
+
+       void wpPrompt(const std::string &, const UrlItem &);
+       void wpNotify(const std::string &, const UrlItem &);
+
+       void setSender(Popup::Sender &&sender) {
+               m_popup.setSender(std::move(sender));
+       }
+
+private:
+       Popup m_popup;
 };
 
 } // namespace Ui
index 186b847..43ed62f 100644 (file)
@@ -1,30 +1,27 @@
 /*
- *  Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
  *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *        http://www.apache.org/licenses/LICENSE-2.0
  *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License
- */
-/*
- * @file        popup-service.cpp
- * @author      Kyungwook Tak (k.tak@samsung.com)
- * @version     1.0
- * @brief
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
  */
+
 #include "popup-service.h"
 
 #include "common/binary-queue.h"
 #include "common/audit/logger.h"
+#include "common/types.h"
 #include "common/exception.h"
-#include "common/cs-detected.h"
+
+#include "ecore-mainloop.h"
 
 namespace Csr {
 namespace Ui {
@@ -51,22 +48,30 @@ bool isCsCommand(const CommandId &cid)
        }
 }
 
-} // namespace nonymous
+} // namespace anonymous
 
-PopupService::PopupService() : Service()
+PopupService::PopupService() : Csr::Service(new EcoreMainloop())
 {
        this->add(SockId::POPUP);
 }
 
-RawBuffer PopupService::process(const ConnShPtr &, RawBuffer &data)
+void PopupService::onMessageProcess(const ConnShPtr &conn)
 {
+       auto in = conn->receive();
+
+       // some fd handler from mainloop makes empty read event when client
+       // close connection. returning false from process() means connection
+       // is closed
+       if (in.empty())
+               throw std::invalid_argument("empty read event!");
+
        BinaryQueue q;
-       q.push(data);
+       q.push(std::move(in));
 
        int intCid;
        q.Deserialize(intCid);
 
-       INFO("Request dispatch on popup-service. CommandId: " << static_cast<int>(intCid));
+       INFO("Request dispatch on popup service. CommandId: " << static_cast<int>(intCid));
 
        if (isCsCommand(static_cast<Ui::CommandId>(intCid))) {
                std::string message;
@@ -75,22 +80,28 @@ RawBuffer PopupService::process(const ConnShPtr &, RawBuffer &data)
 
                switch (static_cast<Ui::CommandId>(intCid)) {
                case CommandId::CS_PROMPT_DATA:
-                       return m_logic.csPromptData(message, d);
+                       m_logic.csPromptData(message, d);
+                       break;
 
                case CommandId::CS_PROMPT_APP:
-                       return m_logic.csPromptApp(message, d);
+                       m_logic.csPromptApp(message, d);
+                       break;
 
                case CommandId::CS_PROMPT_FILE:
-                       return m_logic.csPromptFile(message, d);
+                       m_logic.csPromptFile(message, d);
+                       break;
 
                case CommandId::CS_NOTIFY_DATA:
-                       return m_logic.csNotifyData(message, d);
+                       m_logic.csNotifyData(message, d);
+                       break;
 
                case CommandId::CS_NOTIFY_APP:
-                       return m_logic.csNotifyApp(message, d);
+                       m_logic.csNotifyApp(message, d);
+                       break;
 
                case CommandId::CS_NOTIFY_FILE:
-                       return m_logic.csNotifyFile(message, d);
+                       m_logic.csNotifyFile(message, d);
+                       break;
 
                default:
                        ThrowExc(CSR_ERROR_SERVER, "protocol error. invalid ui command id.");
@@ -102,25 +113,19 @@ RawBuffer PopupService::process(const ConnShPtr &, RawBuffer &data)
 
                switch (static_cast<Ui::CommandId>(intCid)) {
                case CommandId::WP_PROMPT:
-                       return m_logic.wpPrompt(message, item);
+                       m_logic.wpPrompt(message, item);
+                       break;
 
                case CommandId::WP_NOTIFY:
-                       return m_logic.wpNotify(message, item);
+                       m_logic.wpNotify(message, item);
+                       break;
 
                default:
                        ThrowExc(CSR_ERROR_SERVER, "protocol error. invalid ui command id.");
                }
        }
-}
-
-void PopupService::onMessageProcess(const ConnShPtr &connection)
-{
-       DEBUG("process message on popup service");
-
-       auto in = connection->receive();
-       connection->send(this->process(connection, in));
 
-       DEBUG("process done on popup service");
+       m_logic.setSender([conn](const RawBuffer &out) { conn->send(out); });
 }
 
 } // namespace Ui
index 46a0cd3..844f1fb 100644 (file)
@@ -43,8 +43,19 @@ namespace {
 
 }
 
-Popup::Popup(int buttonN)
+void Popup::setSender(Sender &&sender)
 {
+       m_sender = std::move(sender);
+}
+
+Popup::Popup() : m_win(nullptr)
+{
+}
+
+void Popup::setButtons(int n)
+{
+       clear();
+
        // Set win properties.
        m_win = elm_win_add(nullptr, "CSR popup", ELM_WIN_NOTIFICATION);
        elm_win_indicator_opacity_set(m_win, ELM_WIN_INDICATOR_TRANSLUCENT);
@@ -56,7 +67,7 @@ Popup::Popup(int buttonN)
        setRotationToWin(m_win);
 
        eext_win_keygrab_set(m_win, HOME_KEY.c_str());
-       ecore_event_handler_add(ECORE_EVENT_KEY_DOWN, keyDownCb, NULL);
+       ecore_event_handler_add(ECORE_EVENT_KEY_DOWN, keyDownCb, this);
 
        // Set popup properties.
        m_popup = elm_popup_add(m_win);
@@ -111,8 +122,7 @@ Popup::Popup(int buttonN)
        elm_box_horizontal_set(m_btnBox, EINA_TRUE);
        elm_box_padding_set(m_btnBox, 0, 0);
 
-       for(int i=1 ; i <= buttonN; i++) {
-               std::string id("button" + std::to_string(i));
+       for (int i = 0; i < n; ++i) {
                Evas_Object *button = elm_button_add(m_popup);
                elm_object_style_set(button, "bottom");
                setDefaultProperties(button);
@@ -132,7 +142,8 @@ Popup::Popup(int buttonN)
 
 Popup::~Popup()
 {
-       evas_object_del(m_win);
+       if (m_win != nullptr)
+               evas_object_del(m_win);
 }
 
 void Popup::setHeader(const std::string &header) noexcept
@@ -163,11 +174,6 @@ void Popup::setIcon(const std::string &path) noexcept
                elm_image_file_set(m_icon, path.c_str(), NULL);
 }
 
-void Popup::run(void)
-{
-       elm_run();
-}
-
 int Popup::response = -1;
 
 RawBuffer Popup::getResult(void)
@@ -211,7 +217,8 @@ void Popup::setText(Evas_Object *obj, const std::string &text) noexcept
 
 void Popup::callbackRegister(Evas_Object *obj, int *type)
 {
-       evas_object_smart_callback_add(obj, "clicked", btnClickedCb, type);
+       m_buttonSelectorMap[obj] = [type]() { return *type; };
+       evas_object_smart_callback_add(obj, "clicked", btnClickedCb, this);
 }
 
 void Popup::callbackRegister(Evas_Object *obj, const std::string &url)
@@ -274,26 +281,50 @@ void Popup::hypertextClickedCb(void *data, Evas_Object *, void *)
        }
 }
 
-void Popup::btnClickedCb(void *data, Evas_Object *, void *)
+void Popup::btnClickedCb(void *data, Evas_Object *obj, void *)
 {
-       response = *(reinterpret_cast<int *>(data));
-       elm_exit();
-}
+       auto popup = reinterpret_cast<Popup *>(data);
+       if (popup == nullptr)
+               throw std::invalid_argument("Popup instance is null in static btnClickedCb");
 
+       response = popup->m_buttonSelectorMap.at(obj)();
+       popup->terminateWindow();
+}
 
-Eina_Bool Popup::keyDownCb(void *, int , void *ev)
+Eina_Bool Popup::keyDownCb(void *data, int , void *ev)
 {
        DEBUG("Key down event caught.");
        auto event = reinterpret_cast<Ecore_Event_Key *>(ev);
 
-       if(event->key == HOME_KEY) {
+       if (event->key == HOME_KEY) {
+               auto popup = reinterpret_cast<Popup *>(data);
                response = -1;
-               elm_exit();
+               popup->terminateWindow();
        }
 
        // Let the event continue to other callbacks.
        return ECORE_CALLBACK_PASS_ON;
 }
 
+void Popup::terminateWindow()
+{
+       INFO("send result based on response and sender");
+       m_sender(getResult());
+
+       clear();
+}
+
+void Popup::clear()
+{
+       if (m_win != nullptr) {
+               // clear all resources
+               evas_object_del(m_win);
+               m_win = nullptr;
+       }
+
+       m_buttons.clear();
+       m_types.clear();
+}
+
 } // namespace Ui
 } // namespace Csr
index 4475d96..821c7ec 100644 (file)
@@ -32,6 +32,7 @@
 
 #include "common/audit/logger.h"
 #include "common/binary-queue.h"
+#include "common/types.h"
 
 namespace Csr {
 namespace Ui {
@@ -55,10 +56,11 @@ namespace Ui {
 
 class Popup {
 public:
-       Popup(int buttonN);
+       Popup();
        virtual ~Popup();
 
-       void run(void);
+       void setButtons(int n);
+
        RawBuffer getResult(void);
        void setMessage(const std::string &msg) noexcept;
 
@@ -67,6 +69,9 @@ public:
        Popup(Popup &&) = delete;
        Popup &operator=(Popup &&) = delete;
 
+       using Sender = std::function<void(const RawBuffer &)>;
+       void setSender(Sender &&sender);
+
        void setTitle(const std::string &title) noexcept;
        void setHeader(const std::string &header) noexcept;
        void setBody(const std::string &body) noexcept;
@@ -76,9 +81,6 @@ public:
 
        void callbackRegister(Evas_Object *obj, int *type);
        void callbackRegister(Evas_Object *obj, const std::string &url);
-       static void btnClickedCb(void *data, Evas_Object *, void *);
-       static void hypertextClickedCb(void *data, Evas_Object *, void *);
-       static void rotationChangedCb(void *data, Evas_Object *, void *);
        static Eina_Bool keyDownCb(void *, int, void *);
 
        std::vector<Evas_Object *> m_buttons;
@@ -86,6 +88,15 @@ public:
        std::vector<int> m_types;
 
 private:
+       static void btnClickedCb(void *data, Evas_Object *, void *);
+       static void hypertextClickedCb(void *data, Evas_Object *, void *);
+       static void rotationChangedCb(void *data, Evas_Object *, void *);
+
+       using ButtonSelector = std::function<int()>;
+       std::map<Evas_Object *, ButtonSelector> m_buttonSelectorMap;
+       void terminateWindow();
+       void clear();
+
        void setDefaultProperties(Evas_Object *obj) noexcept;
        void setRotationToWin(Evas_Object *obj) noexcept;
 
@@ -103,10 +114,12 @@ private:
        std::string m_iconPath;
        std::string m_hypertextUrl;
 
-       static int response;
-
        int m_winW;
        int m_winH;
+
+       Sender m_sender;
+
+       static int response;
 };
 
 } // namespace Ui