Tizen 2.1 base
[framework/web/webkit-efl.git] / Source / WebKit2 / UIProcess / API / efl / tizen / ClipboardHelper.cpp
1 /*
2  * Copyright (C) 2012 Samsung Electronics
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27
28 #if ENABLE(TIZEN_WEBKIT2_CLIPBOARD_HELPER)
29 #include "ClipboardHelper.h"
30
31 #include "PageClientImpl.h"
32 #include <Evas.h>
33 #include <Ecore_Evas.h>
34 #include <Ecore_X.h>
35 #include <Elementary.h>
36 #include <X11/Xatom.h>
37 #include <X11/Xlib.h>
38
39 namespace WebKit {
40
41 static Eina_Bool clearSelectionCallback(void* context, int type, void* event);
42
43 ClipboardHelper::~ClipboardHelper()
44 {
45 #if ENABLE(TIZEN_WEBKIT2_CONTEXT_MENU_CLIPBOARD)
46     clearClipboardSelectionClearHandler();
47 #endif
48 }
49
50 // This function references from elementary's cbhm_helper.c
51 int ClipboardHelper::numberOfItems()
52 {
53     // 1. Get CBHM Ecore_X_Window.
54     Ecore_X_Atom xAtom;
55     xAtom = ecore_x_atom_get("CBHM_XWIN");
56     if (!xAtom)
57         return 0;
58
59     unsigned char* data = 0;
60     int numberOfWindow = 0;
61     int result = ecore_x_window_prop_property_get(0, xAtom, XA_WINDOW, 0, &data, &numberOfWindow);
62
63     Ecore_X_Window xWindow = 0;
64     if (result && numberOfWindow)
65         memcpy(&xWindow, data, sizeof(Ecore_X_Window));
66
67     if (data)
68         free(data);
69
70     if (!xWindow)
71         return 0;
72
73     ecore_x_sync();
74
75     // 2. Get number of clipboard items.
76     xAtom = ecore_x_atom_get("CBHM_cCOUNT");
77     if (!xAtom)
78         return 0;
79
80     Display* display = static_cast<Display*>(ecore_x_display_get());
81
82     Ecore_X_Atom type;
83     int format;
84     long unsigned numberOfItems = 0;
85     long unsigned bytes = 0;
86     unsigned char* dataInFormat = 0;
87     result = XGetWindowProperty(display, xWindow, xAtom, 0, LONG_MAX, False, ecore_x_window_prop_any_type(),
88                                 reinterpret_cast<Atom*>(&type), &format, &numberOfItems, &bytes, &dataInFormat);
89     if (result != Success)
90         return 0;
91
92     if (!numberOfItems) {
93         XFree(dataInFormat);
94         return 0;
95     }
96
97     if (!(data = static_cast<unsigned char*>(malloc(numberOfItems * format / 8)))) {
98         XFree(dataInFormat);
99         return 0;
100     }
101
102     switch (format) {
103     case 8:
104         for (long unsigned i = 0; i < numberOfItems; i++)
105             (data)[i] = dataInFormat[i];
106         break;
107     case 16:
108         for (long unsigned i = 0; i < numberOfItems; i++)
109             (reinterpret_cast<unsigned short*>(data))[i] = (reinterpret_cast<unsigned short*>(dataInFormat))[i];
110         break;
111     case 32:
112         for (long unsigned i = 0; i < numberOfItems; i++)
113             (reinterpret_cast<unsigned int*>(data))[i] = (reinterpret_cast<unsigned long*>(dataInFormat))[i];
114         break;
115     }
116
117     XFree(dataInFormat);
118
119     if (data) {
120         char count = atoi(reinterpret_cast<const char*>(data));
121         free(data);
122         return count;
123     }
124
125     return 0;
126 }
127
128 void ClipboardHelper::setData(const String& data, const String& type)
129 {
130     if (type == "PlainText" || type == "URIList" || type == "URL") {
131         elm_cnp_selection_set(elm_object_parent_widget_get(m_ewkView), ELM_SEL_TYPE_CLIPBOARD,
132                               ELM_SEL_FORMAT_TEXT, data.utf8().data(), data.length());
133     } else if (type == "Markup") {
134         elm_cnp_selection_set(elm_object_parent_widget_get(m_ewkView), ELM_SEL_TYPE_CLIPBOARD,
135                               ELM_SEL_FORMAT_HTML, data.utf8().data(), data.length());
136     } else if (type == "Image") {
137         // FIXME: We pass data.length() + 1 because of a bug in elementary which adds some random characters
138         // to copied image path. '+1' should be removed when the bug will be fixed on elementary side.
139         elm_cnp_selection_set(elm_object_parent_widget_get(m_ewkView), ELM_SEL_TYPE_CLIPBOARD,
140                               ELM_SEL_FORMAT_IMAGE, data.utf8().data(), data.length() + 1);
141     }
142 }
143
144 void ClipboardHelper::clear()
145 {
146     elm_object_cnp_selection_clear(elm_object_parent_widget_get(m_ewkView), ELM_SEL_TYPE_CLIPBOARD);
147 }
148
149 static Eina_Bool requestClipboardDataCallback(void* data, Evas_Object* object, Elm_Selection_Data* selectionData)
150 {
151     ClipboardHelper* helper = static_cast<ClipboardHelper*>(data);
152
153     if (!selectionData || selectionData->len <= 0) {
154         helper->processResult(String(""), String("PlainText"));
155         return true;
156     }
157
158     String clipboardData = String::fromUTF8(static_cast<const char*>(selectionData->data));
159     switch(selectionData->format) {
160     case ELM_SEL_FORMAT_TEXT:
161         helper->processResult(clipboardData, String("PlainText"));
162         break;
163     case ELM_SEL_FORMAT_HTML:
164         helper->processResult(clipboardData, String("Markup"));
165         break;
166     case ELM_SEL_FORMAT_IMAGE:
167         helper->processResult(clipboardData, String("Image"));
168         break;
169     default:
170         helper->processResult(String(""), String("PlainText"));
171         break;
172     }
173
174     return true;
175 }
176
177 void ClipboardHelper::requestData(void* context, ClipboardDataResultFunction function, SelectionType type)
178 {
179     if (!function)
180         return;
181
182     Elm_Sel_Type selectionType;
183     switch (type) {
184     case SelectionTypePrimary:
185         selectionType = ELM_SEL_TYPE_PRIMARY;
186         break;
187     case SelectionTypeSecondary:
188         selectionType = ELM_SEL_TYPE_SECONDARY;
189         break;
190     case SelectionTypeXDND:
191         selectionType = ELM_SEL_TYPE_XDND;
192         break;
193     default:
194         selectionType = ELM_SEL_TYPE_CLIPBOARD;
195         break;
196     }
197     Eina_Bool result = elm_cnp_selection_get(elm_object_parent_widget_get(m_ewkView), selectionType,
198                                              static_cast<Elm_Sel_Format>(ELM_SEL_FORMAT_TEXT | ELM_SEL_FORMAT_HTML | ELM_SEL_FORMAT_IMAGE),
199                                              requestClipboardDataCallback, this);
200
201     if (!result) {
202         function(String(""), String("PlainText"), context);
203         return;
204     }
205
206     MutexLocker locker(m_callbackQueueLock);
207     m_callbackQueue.append(ClipboardCallback::create(context, function));
208 }
209
210 void ClipboardHelper::processResult(const String& data, const String& type)
211 {
212     MutexLocker locker(m_callbackQueueLock);
213     while(!m_callbackQueue.isEmpty()) {
214         RefPtr<ClipboardCallback> callback = m_callbackQueue.takeFirst();
215         callback->performCallback(data, type);
216     }
217 }
218
219 #if ENABLE(TIZEN_WEBKIT2_CONTEXT_MENU_CLIPBOARD)
220 void ClipboardHelper::openClipboardWindow(bool isRichEditable)
221 {
222     clearClipboardSelectionClearHandler();
223     m_selectionClearHandler = ecore_event_handler_add(ECORE_X_EVENT_SELECTION_CLEAR, clearSelectionCallback, this);
224
225     Ecore_X_Atom xAtomCbhm = ecore_x_atom_get("CBHM_XWIN");
226     unsigned char* buf = 0;
227     int num = 0;
228     int ret = ecore_x_window_prop_property_get(0, xAtomCbhm, XA_WINDOW, 0, &buf, &num);
229
230     Ecore_X_Window xCbhmWin;
231     if (ret && num)
232         memcpy(&xCbhmWin, buf, sizeof(Ecore_X_Window));
233     if (buf)
234         free(buf);
235
236     Ecore_X_Atom xAtomCbhmMsg = ecore_x_atom_get("CBHM_MSG");
237     Evas_Object* parent = elm_object_parent_widget_get(m_ewkView);
238     Ecore_X_Window xWin = ecore_evas_gl_x11_window_get(ecore_evas_ecore_evas_get(evas_object_evas_get(parent)));
239     if (!xWin)
240         xWin = ecore_evas_software_x11_window_get(ecore_evas_ecore_evas_get(evas_object_evas_get(parent)));
241
242     if (!xCbhmWin || !xAtomCbhmMsg)
243         return;
244
245     XClientMessageEvent messageEvent;
246     memset(&messageEvent, 0, sizeof(messageEvent));
247     messageEvent.type = ClientMessage;
248     messageEvent.display = static_cast<Display *> (ecore_x_display_get());
249     messageEvent.window = xWin;
250     messageEvent.message_type = xAtomCbhmMsg;
251     messageEvent.format = 8;
252
253     if (isRichEditable)
254         snprintf(messageEvent.data.b, 20, "%s", "show1");
255     else
256         snprintf(messageEvent.data.b, 20, "%s", "show0");
257
258     XSendEvent(static_cast<Display *> (ecore_x_display_get()), xCbhmWin, false, NoEventMask, (XEvent*)&messageEvent);
259
260     connectClipboardWindow();
261
262     ecore_x_sync();
263
264     evas_object_smart_callback_call(m_ewkView, "clipboard,opened", 0);
265     m_pageClient->updateTextSelectionHandlesAndContextMenu(false);
266 }
267
268 bool ClipboardHelper::isClipboardWindowOpened()
269 {
270     char* windowTitle = ecore_x_icccm_title_get(ecore_x_window_focus_get());
271
272     if (!windowTitle)
273          return false;
274
275     if (!(strcmp("Clipboard History Manager", windowTitle)))
276         return true;
277
278     return false;
279 }
280
281 void ClipboardHelper::connectClipboardWindow()
282 {
283     ecore_x_selection_secondary_set(elm_win_xwindow_get(elm_object_parent_widget_get(m_ewkView)), "",1);
284 }
285
286 void ClipboardHelper::clearClipboardSelectionClearHandler()
287 {
288     if (m_selectionClearHandler) {
289         ecore_event_handler_del(m_selectionClearHandler);
290         m_selectionClearHandler = 0;
291     }
292 }
293
294 static void clipboardDataCallbackForClipboardMenu(const String& data, const String& type, void* context)
295 {
296     ClipboardHelper* clipboardHelper = static_cast<ClipboardHelper*>(context);
297     clipboardHelper->pageClient()->page()->executePasteFromClipboardItem(data, type);
298
299     clipboardHelper->connectClipboardWindow();
300 }
301
302 static Eina_Bool clearSelectionCallback(void* context, int type, void* event)
303 {
304     ClipboardHelper* clipboardHelper = static_cast<ClipboardHelper*>(context);
305
306     Ecore_X_Event_Selection_Clear *ev = static_cast<Ecore_X_Event_Selection_Clear*>(event);
307         if (ev->selection != ECORE_X_SELECTION_SECONDARY)
308             return true;
309
310     if (clipboardHelper)
311         clipboardHelper->requestData(clipboardHelper, clipboardDataCallbackForClipboardMenu, ClipboardHelper::SelectionTypeSecondary);
312
313     return true;
314 }
315 #endif // TIZEN_WEBKIT2_CONTEXT_MENU_CLIPBOARD
316 }// namespace WebKit
317
318 #endif // TIZEN_WEBKIT2_CLIPBOARD_HELPER