[Release] wrt_0.8.268
[platform/framework/web/wrt.git] / src / view / webkit / view_logic_web_notification_support.cpp
index 9300391..1cda2c6 100644 (file)
  */
 
 #include "view_logic_web_notification_support.h"
+#include "view_logic_web_notification_data.h"
+
+#include <stdint.h>
 #include <string>
-#include <dpl/log/log.h>
-#include <dpl/string.h>
-#include <wrt-commons/security-origin-dao/security_origin_dao_types.h>
-#include <wrt-commons/security-origin-dao/security_origin_dao.h>
-#include <common/view_logic_security_origin_support.h>
+#include <map>
+#include <dpl/log/secure_log.h>
+#include <dpl/assert.h>
+#include <dpl/exception.h>
+#include <dpl/wrt-dao-ro/widget_config.h>
 
 #include <notification.h>
 #include <pcrecpp.h>
 #include <sstream>
 #include <curl/curl.h>
-#include <widget_model.h>
-#include <EWebKit2.h>
-#include <widget_string.h>
 
+namespace ViewModule {
 namespace {
 const char* PATTERN_CHECK_EXTERNAL = "^http(s)?://\\w+.*$";
-}
-
-namespace ViewModule {
-namespace WebNotification {
-using namespace SecurityOriginDB;
-using namespace ViewModule::SecurityOriginSupportUtil;
-
-bool notificationShow(WebNotificationDataPtr notiData);
-bool notificationHide(WebNotificationDataPtr notiData);
-bool isExternalUri(const std::string &pattern, const std::string &uri);
-bool downloadImage(WebNotificationDataPtr notiData);
-size_t curlWriteData(void *ptr, size_t size, size_t nmemb, FILE *stream);
-void askUserForWebNotificationPermission(
-    Evas_Object* window,
-    PermissionData* data);
-static void popupCallback(void* data, Evas_Object* obj, void* eventInfo);
-
-WebNotificationData::WebNotificationData(WidgetModel* widgetModel,
-                                         uint64_t id) :
-    m_widgetModel(widgetModel),
-    m_id(id)
-{
-    Assert(m_widgetModel);
-}
 
-WebNotificationData::~WebNotificationData()
-{}
+// Function declare
+bool isExternalUri(const std::string &uri);
+size_t curlWriteData(void* ptr, size_t size, size_t nmemb, FILE* stream);
 
-void webNotificationPermissionRequest(
-    Evas_Object* window,
-    SecurityOriginDAO* securityOriginDAO,
-    void* data)
+bool isExternalUri(const std::string &uri)
 {
-    LogDebug("permissionRequest called");
-    Assert(securityOriginDAO);
-    Assert(data);
-    Ewk_Notification_Permission_Request* request =
-        static_cast<Ewk_Notification_Permission_Request*>(data);
-    const Ewk_Security_Origin* ewkOrigin =
-        ewk_notification_permission_request_origin_get(request);
-    Assert(ewkOrigin);
-
-    SecurityOriginData securityOriginData(
-        WrtDB::FEATURE_WEB_NOTIFICATION,
-        Origin(
-            DPL::FromUTF8String(ewk_security_origin_protocol_get(ewkOrigin)),
-            DPL::FromUTF8String(ewk_security_origin_host_get(ewkOrigin)),
-            ewk_security_origin_port_get(ewkOrigin)));
-
-    // check cache database
-    Result result = securityOriginDAO->getResult(securityOriginData);
-    if (RESULT_ALLOW_ONCE == result || RESULT_ALLOW_ALWAYS == result) {
-        LogDebug("allow");
-        ewk_notification_permission_request_set(request, EINA_TRUE);
-        return;
-    } else if (RESULT_DENY_ONCE == result || RESULT_DENY_ALWAYS == result) {
-        LogDebug("deny");
-        ewk_notification_permission_request_set(request, EINA_FALSE);
-        return;
-    }
-
-    // ask to user
-    PermissionData* permissionData =
-        new PermissionData(securityOriginDAO,
-                           securityOriginData,
-                           request);
+    pcrecpp::RE_Options pcreOpt;
+    pcreOpt.set_caseless(true);
+    pcrecpp::RE re(PATTERN_CHECK_EXTERNAL, pcreOpt);
 
-    // suspend notification
-    ewk_notification_permission_request_suspend(request);
-    askUserForWebNotificationPermission(window, permissionData);
-    return;
+    return re.FullMatch(uri);
 }
 
-bool showWebNotification(WebNotificationDataPtr notiData)
+size_t curlWriteData(void* ptr, size_t size, size_t nmemb, FILE* stream)
 {
-    LogDebug("showWebNotification called");
-
-    /* TODO : register notification to database */
-    return notificationShow(notiData);
+    size_t written = fwrite(ptr, size, nmemb, stream);
+    return written;
 }
+} // anonymous namespace
 
-bool notificationShow(WebNotificationDataPtr notiData)
+// Implementation class
+class WebNotificationSupportImplementation
 {
-    LogDebug("notificationShow called");
-    Assert(notiData);
-    notification_h noti_h = NULL;
-    notification_error_e error = NOTIFICATION_ERROR_NONE;
-
-    Try {
-        // create notification
-        noti_h = notification_new(
-                NOTIFICATION_TYPE_NOTI,
-                NOTIFICATION_GROUP_ID_NONE,
-                notiData->m_id);
-        if (!noti_h) {
-            LogError("Fail to notification_new");
-            return false;
+  private:
+    class Exception
+    {
+      public:
+        DECLARE_EXCEPTION_TYPE(DPL::Exception, Base)
+        DECLARE_EXCEPTION_TYPE(Base, InitError)
+        DECLARE_EXCEPTION_TYPE(Base, NotificationShowError)
+        DECLARE_EXCEPTION_TYPE(Base, DownloadImageError)
+    };
+
+    bool m_initialized;
+    std::string m_persistentPath;
+
+    typedef std::map<uint64_t, WebNotificationDataPtr> NotiMap;
+    typedef std::map<uint64_t, WebNotificationDataPtr>::iterator NotiMapIt;
+    NotiMap m_notiDataMap;
+
+    std::string createDownloadPath(const std::string& iconUrl)
+    {
+        // Make valid filename
+        // If there is already exist filename, rename to "filename_%d"
+
+        std::string downloadPath = m_persistentPath;
+        std::string fileName = iconUrl.substr(iconUrl.rfind('/') + 1);
+        _D("fileName from URL: %s", fileName.c_str());
+        std::string rename = fileName;
+        unsigned int renameSuffixNb = 0;
+        while (0 == access((m_persistentPath + rename).c_str(), F_OK)) {
+            std::ostringstream suffixOstr;
+            suffixOstr.str("");
+            suffixOstr << fileName << "_" << renameSuffixNb++;
+
+            rename = fileName;
+            rename.insert(rename.find('.'), suffixOstr.str());
         }
 
-        // set notification title
-        error = notification_set_text(
-                noti_h,
-                NOTIFICATION_TEXT_TYPE_TITLE,
-                notiData->m_title.c_str(),
-                NULL,
-                NOTIFICATION_VARIABLE_TYPE_NONE);
-        if (error != NOTIFICATION_ERROR_NONE) {
-            ThrowMsg(Exception::NotificationShowError, "Fail to set title");
-        }
+        downloadPath += rename;
+        _D("Valid file path : %s", downloadPath.c_str());
+        return downloadPath;
+    }
 
-        // set notification content
-        error = notification_set_text(
-                noti_h,
-                NOTIFICATION_TEXT_TYPE_CONTENT,
-                notiData->m_body.c_str(),
-                NULL,
-                NOTIFICATION_VARIABLE_TYPE_NONE);
-        if (error != NOTIFICATION_ERROR_NONE) {
-            ThrowMsg(Exception::NotificationShowError,
-                     "Fail to set content:" << error);
+    std::string downloadImage(const std::string& iconUrl)
+    {
+        if (iconUrl.empty()) {
+            _W("Icon url is empty");
+            return std::string();
         }
+        std::string downloadPath = createDownloadPath(iconUrl);
 
-        //check uri is "http", https" or not
-        bool validIconURL = true;
-        LogDebug("url path is " << notiData->m_iconURL);
-        if (isExternalUri(PATTERN_CHECK_EXTERNAL, notiData->m_iconURL)) {
-            //download image from url
-            validIconURL = downloadImage(notiData);
+        // Download image by curl
+        FILE *fp = NULL;
+        CURL *curl = NULL;
+
+        Try {
+            curl = curl_easy_init();
+            if (NULL == curl) {
+                _W("fail to curl_easy_init");
+                Throw(Exception::InitError);
+            }
+            fp = fopen(downloadPath.c_str(), "wb");
+            if (fp == NULL) {
+                _W("fail to open");
+                Throw(Exception::InitError);
+            }
+
+            curl_easy_setopt(curl, CURLOPT_URL, iconUrl.c_str());
+            curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
+            curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curlWriteData);
+
+            CURLcode err = curl_easy_perform(curl);
+            if (0 != err) {
+                _W("fail to curl_easy_perform: %d", err);
+                Throw(Exception::DownloadImageError);
+            }
+            fclose(fp);
+            curl_easy_cleanup(curl);
+        } Catch(DPL::Exception) {
+            fclose(fp);
+            curl_easy_cleanup(curl);
+            return std::string();
         }
+        _D("Download success");
+        return downloadPath;
+    }
 
-        //set image
-        // If fail to download external image, skip to set image.
-        // In this case, set to default package image.
-        if (true == validIconURL) {
-            error = notification_set_image(
+    int showNotification(WebNotificationDataPtr notiData)
+    {
+        Assert(notiData);
+        notification_h noti_h = NULL;
+        notification_error_e error = NOTIFICATION_ERROR_NONE;
+
+        Try {
+            // create notification
+            noti_h = notification_new(
+                    NOTIFICATION_TYPE_NOTI,
+                    NOTIFICATION_GROUP_ID_DEFAULT,
+                    NOTIFICATION_PRIV_ID_NONE);
+            if (!noti_h) {
+                _E("Fail to notification_new");
+                Throw(Exception::NotificationShowError);
+            }
+
+            // set notification title
+            error = notification_set_text(
                     noti_h,
-                    NOTIFICATION_IMAGE_TYPE_ICON,
-                    notiData->m_iconURL.c_str());
+                    NOTIFICATION_TEXT_TYPE_TITLE,
+                    notiData->getTitle(),
+                    NULL,
+                    NOTIFICATION_VARIABLE_TYPE_NONE);
             if (error != NOTIFICATION_ERROR_NONE) {
-                ThrowMsg(Exception::NotificationShowError,
-                         "Fail to free notification: " << error);
+                _E("Fail to set title");
+                Throw(Exception::NotificationShowError);
             }
-        }
 
-        // insert notification
-        int priv_id = NOTIFICATION_PRIV_ID_NONE;
-        error = notification_insert(noti_h, &priv_id);
-        if (error != NOTIFICATION_ERROR_NONE) {
-            ThrowMsg(Exception::NotificationShowError,
-                     "Fail to insert notification: " << error);
-        }
+            // set notification content
+            error = notification_set_text(
+                    noti_h,
+                    NOTIFICATION_TEXT_TYPE_CONTENT,
+                    notiData->getBody(),
+                    NULL,
+                    NOTIFICATION_VARIABLE_TYPE_NONE);
+            if (error != NOTIFICATION_ERROR_NONE) {
+                _E("Fail to set content: %d", error);
+                Throw(Exception::NotificationShowError);
+            }
 
-        LogDebug("Notification is inserted!");
-        LogDebug("noti id =[" << notiData->m_id << "] " <<
-                "priv_id =[" << priv_id << "]");
+            //check uri is "http", https" or not
+            if (notiData->getIconUrl()) {
+                std::string iconPath;
+                if (isExternalUri(notiData->getIconUrl())) {
+                    //download image from url
+                    iconPath = downloadImage(notiData->getIconUrl());
+                }
+
+                //set image
+                // If fail to download external image, skip to set image.
+                // In this case, set to default package image.
+                if (!iconPath.empty()) {
+                    error = notification_set_image(
+                            noti_h,
+                            NOTIFICATION_IMAGE_TYPE_ICON,
+                            iconPath.c_str());
+                    if (error != NOTIFICATION_ERROR_NONE) {
+                        _E("Fail to free notification: %d", error);
+                        Throw(Exception::NotificationShowError);
+                    }
+                }
+            }
 
-        if (noti_h) {
-            error = notification_free(noti_h);
+            // insert notification
+            int privId = NOTIFICATION_PRIV_ID_NONE;
+            error = notification_insert(noti_h, &privId);
             if (error != NOTIFICATION_ERROR_NONE) {
-                ThrowMsg(Exception::NotificationShowError,
-                         "Fail to free notification: " << error);
+                _E("Fail to insert notification : %d", error);
+                Throw(Exception::NotificationShowError);
             }
+            _D("ewkId=[%u], notiId=[%d]", notiData->getEwkNotiId(), privId);
+
+            if (noti_h) {
+                error = notification_free(noti_h);
+                if (error != NOTIFICATION_ERROR_NONE) {
+                    _E("Fail to free notification : %d", error);
+                    Throw(Exception::NotificationShowError);
+                }
+                noti_h = NULL;
+            }
+            notiData->setNotiId(privId);
+        } Catch(Exception::NotificationShowError) {
+            notification_free(noti_h);
             noti_h = NULL;
+            return false;
+        } Catch(DPL::Exception) {
+            _E("Fail to show notification");
+            return false;
         }
         return true;
-    } Catch(Exception::NotificationShowError) {
-        LogError(_rethrown_exception.GetMessage());
-        notification_free(noti_h);
-        noti_h = NULL;
-        return false;
-    } Catch(DPL::Exception) {
-        LogError(_rethrown_exception.GetMessage());
-        return false;
     }
-}
 
-bool notificationHide(WebNotificationDataPtr notiData)
-{
-    LogDebug("notificationHide called");
+    bool hideNotification(uint64_t ewkNotiId)
+    {
+        notification_error_e error = NOTIFICATION_ERROR_NONE;
+        NotiMapIt it = m_notiDataMap.find(ewkNotiId);
+        error =
+            notification_delete_by_priv_id(NULL,
+                                           NOTIFICATION_TYPE_NOTI,
+                                           it->second->getNotiId());
+        if (error == NOTIFICATION_ERROR_NONE) {
+            _D("Success to hide");
+            return true;
+        } else if (error == NOTIFICATION_ERROR_NOT_EXIST_ID) {
+            _D("Success to hide. Not exist noti");
+            return true;
+        } else {
+            _W("Error to hide : %d", error);
+            return false;
+        }
+    }
 
-    Assert(notiData);
-    return true;
-}
+    // manage noti data
+    bool isExistData(uint64_t ewkNotiId)
+    {
+        if (m_notiDataMap.find(ewkNotiId) == m_notiDataMap.end()) {
+            return false;
+        }
+        return true;
+    }
 
-bool isExternalUri(const std::string &pattern, const std::string &uri)
-{
-    LogDebug("isExternalUri called");
+    void insertData(WebNotificationDataPtr notiData)
+    {
+        m_notiDataMap[notiData->getEwkNotiId()] = notiData;
+    }
 
-    pcrecpp::RE_Options pcreOpt;
-    pcreOpt.set_caseless(true);
-    pcrecpp::RE re(pattern, pcreOpt);
+    void removeData(uint64_t ewkNotiId)
+    {
+        m_notiDataMap.erase(ewkNotiId);
+    }
 
-    return re.FullMatch(uri);
-}
+    WebNotificationDataPtr getData(uint64_t ewkNotiId)
+    {
+        return m_notiDataMap.find(ewkNotiId)->second;
+    }
 
-bool downloadImage(WebNotificationDataPtr notiData)
-{
-    LogDebug("downloadImage called");
-
-    Assert(notiData);
-    // download path is
-    // /opt/apps/tizen_id/data + '/' + filename
-    std::string downloadPath =
-        DPL::ToUTF8String(
-            notiData->m_widgetModel->PersistentStoragePath.Get()) + "/";
-    LogDebug("downloadPath " << downloadPath);
-
-    // Make valid filename
-    // If there is already exist filename, rename to "filename_%d"
-    std::string fileName =
-        notiData->m_iconURL.substr(notiData->m_iconURL.rfind('/') + 1);
-    LogDebug("fileName from URL: " << fileName);
-    std::string rename = fileName;
-    unsigned int renameSuffixNb = 0;
-    while (0 == access((downloadPath + rename).c_str(), F_OK)) {
-        std::ostringstream suffixOstr;
-        suffixOstr.str("");
-        suffixOstr << fileName << "_" << renameSuffixNb++;
-
-        rename = fileName;
-        rename.insert(rename.find('.'), suffixOstr.str());
+  public:
+    WebNotificationSupportImplementation() :
+        m_initialized(false)
+    {
     }
 
-    downloadPath += rename;
-    LogDebug("Valid file path : " << downloadPath);
+    void initialize(WrtDB::TizenPkgId pkgId)
+    {
+        // icon download path
+        // /opt/apps/tizen_id/data + '/' + filename
+        m_persistentPath =
+            WrtDB::WidgetConfig::GetWidgetPersistentStoragePath(pkgId) + '/';
+        _D("path %s", m_persistentPath.c_str());
+        m_initialized = true;
+    }
 
-    // Download image by curl
-    FILE *fp = NULL;
-    CURL *curl = NULL;
+    void deinitialize(void)
+    {
+        _D("called");
+        m_initialized = false;
+    }
 
-    Try {
-        curl = curl_easy_init();
-        if (NULL == curl) {
-            ThrowMsg(Exception::InitError, "fail to curl_easy_init");
+    bool show(WebNotificationDataPtr notiData)
+    {
+        Assert(m_initialized);
+        if (isExistData(notiData->getEwkNotiId())) {
+            // Web Notifications (http://www.w3.org/TR/notifications/)
+            // 4.7 Replacing a notification
+            //   3. If old is in the list of pending notifications, queue a
+            //      task to replace old with new, in the same position, in the
+            //      list of pending notifications, and fire an event named
+            //      close on old.
+            //   4. Otherwise, queue a task to replace old with new, in the
+            //      same position, in the list of active notifications, fire
+            //      an event named close on old, and fire an event named show
+            //      on new.
+            hideNotification(notiData->getEwkNotiId());
+            removeData(notiData->getEwkNotiId());
         }
-
-        fp = fopen(downloadPath.c_str(), "wb");
-        if (fp == NULL) {
-            ThrowMsg(Exception::InitError, "fail to open");
+        if (showNotification(notiData)) {
+            insertData(notiData);
+            return true;
         }
+        return false;
+    }
 
-        curl_easy_setopt(curl, CURLOPT_URL, notiData->m_iconURL.c_str());
-        curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
-        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curlWriteData);
-
-        CURLcode err = curl_easy_perform(curl);
-        if (0 != err) {
-            ThrowMsg(Exception::DownloadImageError,
-                     "fail to curl_easy_perform: " << err);
+    void* hide(uint64_t ewkNotiId)
+    {
+        Assert(m_initialized);
+        if (!isExistData(ewkNotiId)) {
+            _W("Noti isn't exist");
+            return NULL;
         }
-        fclose(fp);
-        curl_easy_cleanup(curl);
-    } Catch(DPL::Exception) {
-        LogError(_rethrown_exception.GetMessage());
-        fclose(fp);
-        curl_easy_cleanup(curl);
-        return false;
+        if (!hideNotification(ewkNotiId)) {
+            return NULL;
+        }
+        WebNotificationDataPtr data = getData(ewkNotiId);
+        removeData(ewkNotiId);
+        return static_cast<void*>(data->getData());
     }
+};
 
-    LogDebug("Download success.. downloadedImgPath: " << downloadPath);
-    notiData->m_iconURL = downloadPath;
-    return true;
+WebNotificationSupport::WebNotificationSupport() :
+    m_impl(new WebNotificationSupportImplementation())
+{
 }
 
-size_t curlWriteData(void *ptr, size_t size, size_t nmemb, FILE *stream)
+WebNotificationSupport::~WebNotificationSupport()
 {
-    size_t written = fwrite(ptr, size, nmemb, stream);
-    return written;
 }
 
-void askUserForWebNotificationPermission(
-    Evas_Object* window,
-    PermissionData* data)
+void WebNotificationSupport::initialize(WrtDB::TizenAppId appId)
 {
-    LogDebug("askUserForWebNotificationPermission called");
-    std::string origin = DPL::ToUTF8String(data->m_originData.origin.host);
-    if (origin.empty()) {
-        origin = "local";
-    }
-    std::string appname;
-    char* name = NULL;
-    if (app_get_name(&name) == APP_ERROR_NONE) {
-        appname = name;
-        free(name);
-    } else {
-        appname = "application";
-    }
+    m_impl->initialize(appId);
+}
 
-    std::string body =
-        WrtText::replacePS({WRT_POP_WEB_NOTIFICATION_PERMISSION,
-                           appname,
-                           origin});
-    Evas_Object* popup = createPopup(window,
-                                     body.c_str(),
-                                     WRT_BODY_REMEMBER_PREFERENCE,
-                                     popupCallback,
-                                     data);
-    if (popup == NULL) {
-        LogError("Fail to create popup object");
-        delete data;
-        return;
-    } else {
-        evas_object_show(popup);
-    }
+void WebNotificationSupport::deinitialize(void)
+{
+    m_impl->deinitialize();
 }
 
-void popupCallback(void* data, Evas_Object* obj, void* /*eventInfo*/)
+bool WebNotificationSupport::show(WebNotificationDataPtr notiData)
 {
-    LogDebug("popupCallback");
-    Assert(data);
-    PermissionData* permData = static_cast<PermissionData*>(data);
-    Ewk_Notification_Permission_Request* request =
-        static_cast<Ewk_Notification_Permission_Request*>(permData->m_data);
-
-    Evas_Object* popup = getPopup(obj);
-    Result result = getResult(obj);
-
-    if (result != RESULT_UNKNOWN) {
-        permData->m_originDao->setSecurityOriginData(permData->m_originData,
-                                                     result);
-    }
-    Eina_Bool ret =
-        (result == RESULT_ALLOW_ALWAYS || result == RESULT_ALLOW_ONCE) ?
-        EINA_TRUE : EINA_FALSE;
-    ewk_notification_permission_request_set(request, ret);
-    delete permData;
-    evas_object_hide(popup);
-    evas_object_del(popup);
+    return m_impl->show(notiData);
+}
+
+void* WebNotificationSupport::hide(uint64_t ewkNotiId)
+{
+    return m_impl->hide(ewkNotiId);
 }
-} // namespace WebNotification
 } //namespace ViewModule