1/*
2 * Copyright (C) 2006 Zack Rusin <zack@kde.org>
3 * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
4 * Copyright (C) 2008 Kenneth Rohde Christiansen
5 * Copyright (C) 2008 Diego Gonzalez
6 * Copyright (C) 2009-2010 ProFUSION embedded systems
7 * Copyright (C) 2009-2010 Samsung Electronics
8 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
9 *
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
29 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include "config.h"
35#include "ChromeClientEfl.h"
36
37#if ENABLE(DATABASE)
38#include "DatabaseDetails.h"
39#include "DatabaseTracker.h"
40#endif
41#include "EWebKit.h"
42#include "FileChooser.h"
43#include "FloatRect.h"
44#include "FrameLoader.h"
45#include "FrameLoaderClientEfl.h"
46#include "HitTestResult.h"
47#include "IntRect.h"
48#include "KURL.h"
49#include "NavigationAction.h"
50#include "NotImplemented.h"
51#include "PlatformString.h"
52#include "SecurityOrigin.h"
53#include "PopupMenuEfl.h"
54#include "SearchPopupMenuEfl.h"
55#include "ViewportArguments.h"
56#include "WindowFeatures.h"
57#include "ewk_private.h"
58#include <Ecore_Evas.h>
59#include <Evas.h>
60#include <wtf/text/CString.h>
61
62using namespace WebCore;
63
64static inline Evas_Object* kit(Frame* frame)
65{
66    if (!frame)
67        return 0;
68
69    FrameLoaderClientEfl* client = static_cast<FrameLoaderClientEfl*>(frame->loader()->client());
70    return client ? client->webFrame() : 0;
71}
72
73namespace WebCore {
74
75ChromeClientEfl::ChromeClientEfl(Evas_Object* view)
76    : m_view(view)
77{
78}
79
80ChromeClientEfl::~ChromeClientEfl()
81{
82}
83
84void ChromeClientEfl::chromeDestroyed()
85{
86    delete this;
87}
88
89void ChromeClientEfl::focusedNodeChanged(Node*)
90{
91    notImplemented();
92}
93
94void ChromeClientEfl::focusedFrameChanged(Frame*)
95{
96}
97
98FloatRect ChromeClientEfl::windowRect()
99{
100    Ecore_Evas* ee = 0;
101    int x, y, w, h;
102
103    if (!m_view)
104        return FloatRect();
105
106    ee = ecore_evas_ecore_evas_get(evas_object_evas_get(m_view));
107    ecore_evas_geometry_get(ee, &x, &y, &w, &h);
108    return FloatRect(x, y, w, h);
109}
110
111void ChromeClientEfl::setWindowRect(const FloatRect& rect)
112{
113    Ecore_Evas* ee = 0;
114    IntRect intrect = IntRect(rect);
115
116    if (!m_view)
117        return;
118
119    if (!ewk_view_setting_enable_auto_resize_window_get(m_view))
120        return;
121
122    ee = ecore_evas_ecore_evas_get(evas_object_evas_get(m_view));
123    ecore_evas_move(ee, intrect.x(), intrect.y());
124    ecore_evas_resize(ee, intrect.width(), intrect.height());
125}
126
127FloatRect ChromeClientEfl::pageRect()
128{
129    if (!m_view)
130        return FloatRect();
131
132    return ewk_view_page_rect_get(m_view);
133}
134
135float ChromeClientEfl::scaleFactor()
136{
137    notImplemented();
138    return 1.0;
139}
140
141void ChromeClientEfl::focus()
142{
143    evas_object_focus_set(m_view, EINA_TRUE);
144}
145
146void ChromeClientEfl::unfocus()
147{
148    evas_object_focus_set(m_view, EINA_FALSE);
149}
150
151Page* ChromeClientEfl::createWindow(Frame*, const FrameLoadRequest& frameLoadRequest, const WindowFeatures& features, const NavigationAction&)
152{
153    Evas_Object* newView = ewk_view_window_create(m_view, EINA_TRUE, &features);
154    if (!newView)
155        return 0;
156
157    return ewk_view_core_page_get(newView);
158}
159
160void ChromeClientEfl::show()
161{
162    ewk_view_ready(m_view);
163}
164
165bool ChromeClientEfl::canRunModal()
166{
167    notImplemented();
168    return false;
169}
170
171void ChromeClientEfl::runModal()
172{
173    notImplemented();
174}
175
176void ChromeClientEfl::setToolbarsVisible(bool visible)
177{
178    ewk_view_toolbars_visible_set(m_view, visible);
179}
180
181bool ChromeClientEfl::toolbarsVisible()
182{
183    Eina_Bool visible;
184
185    ewk_view_toolbars_visible_get(m_view, &visible);
186    return visible;
187}
188
189void ChromeClientEfl::setStatusbarVisible(bool visible)
190{
191    ewk_view_statusbar_visible_set(m_view, visible);
192}
193
194bool ChromeClientEfl::statusbarVisible()
195{
196    Eina_Bool visible;
197
198    ewk_view_statusbar_visible_get(m_view, &visible);
199    return visible;
200}
201
202void ChromeClientEfl::setScrollbarsVisible(bool visible)
203{
204    ewk_view_scrollbars_visible_set(m_view, visible);
205}
206
207bool ChromeClientEfl::scrollbarsVisible()
208{
209    Eina_Bool visible;
210
211    ewk_view_scrollbars_visible_get(m_view, &visible);
212    return visible;
213}
214
215void ChromeClientEfl::setMenubarVisible(bool visible)
216{
217    ewk_view_menubar_visible_set(m_view, visible);
218}
219
220bool ChromeClientEfl::menubarVisible()
221{
222    Eina_Bool visible;
223
224    ewk_view_menubar_visible_get(m_view, &visible);
225    return visible;
226}
227
228void ChromeClientEfl::createSelectPopup(PopupMenuClient* client, int selected, const IntRect& rect)
229{
230    ewk_view_popup_new(m_view, client, selected, rect);
231}
232
233bool ChromeClientEfl::destroySelectPopup()
234{
235    return ewk_view_popup_destroy(m_view);
236}
237
238void ChromeClientEfl::setResizable(bool)
239{
240    notImplemented();
241}
242
243void ChromeClientEfl::closeWindowSoon()
244{
245    ewk_view_window_close(m_view);
246}
247
248bool ChromeClientEfl::canTakeFocus(FocusDirection)
249{
250    // This is called when cycling through links/focusable objects and we
251    // reach the last focusable object.
252    return false;
253}
254
255void ChromeClientEfl::takeFocus(FocusDirection)
256{
257    unfocus();
258}
259
260bool ChromeClientEfl::canRunBeforeUnloadConfirmPanel()
261{
262    return true;
263}
264
265bool ChromeClientEfl::runBeforeUnloadConfirmPanel(const String& message, Frame* frame)
266{
267    return runJavaScriptConfirm(frame, message);
268}
269
270void ChromeClientEfl::addMessageToConsole(MessageSource, MessageType, MessageLevel, const String& message,
271                                          unsigned int lineNumber, const String& sourceID)
272{
273    ewk_view_add_console_message(m_view, message.utf8().data(), lineNumber, sourceID.utf8().data());
274}
275
276void ChromeClientEfl::runJavaScriptAlert(Frame* frame, const String& message)
277{
278    ewk_view_run_javascript_alert(m_view, kit(frame), message.utf8().data());
279}
280
281bool ChromeClientEfl::runJavaScriptConfirm(Frame* frame, const String& message)
282{
283    return ewk_view_run_javascript_confirm(m_view, kit(frame), message.utf8().data());
284}
285
286bool ChromeClientEfl::runJavaScriptPrompt(Frame* frame, const String& message, const String& defaultValue, String& result)
287{
288    char* value = 0;
289    ewk_view_run_javascript_prompt(m_view, kit(frame), message.utf8().data(), defaultValue.utf8().data(), &value);
290    if (value) {
291        result = String::fromUTF8(value);
292        free(value);
293        return true;
294    }
295    return false;
296}
297
298void ChromeClientEfl::setStatusbarText(const String& string)
299{
300    ewk_view_statusbar_text_set(m_view, string.utf8().data());
301}
302
303bool ChromeClientEfl::shouldInterruptJavaScript()
304{
305    return ewk_view_should_interrupt_javascript(m_view);
306}
307
308KeyboardUIMode ChromeClientEfl::keyboardUIMode()
309{
310    return KeyboardAccessTabsToLinks;
311}
312
313IntRect ChromeClientEfl::windowResizerRect() const
314{
315    notImplemented();
316    // Implementing this function will make repaint being
317    // called during resize, but as this will be done with
318    // a minor delay it adds a weird "filling" effect due
319    // to us using an evas image for showing the cairo
320    // context. So instead of implementing this function
321    // we call paint directly during resize with
322    // the new object size as its argument.
323    return IntRect();
324}
325
326void ChromeClientEfl::contentsSizeChanged(Frame* frame, const IntSize& size) const
327{
328    ewk_frame_contents_size_changed(kit(frame), size.width(), size.height());
329    if (ewk_view_frame_main_get(m_view) == kit(frame))
330        ewk_view_contents_size_changed(m_view, size.width(), size.height());
331}
332
333IntRect ChromeClientEfl::windowToScreen(const IntRect& rect) const
334{
335    notImplemented();
336    return rect;
337}
338
339IntPoint ChromeClientEfl::screenToWindow(const IntPoint& point) const
340{
341    notImplemented();
342    return point;
343}
344
345PlatformPageClient ChromeClientEfl::platformPageClient() const
346{
347    return m_view;
348}
349
350void ChromeClientEfl::scrollbarsModeDidChange() const
351{
352}
353
354void ChromeClientEfl::mouseDidMoveOverElement(const HitTestResult& hit, unsigned modifierFlags)
355{
356    // FIXME, compare with old link, look at Qt impl.
357    bool isLink = hit.isLiveLink();
358    if (isLink) {
359        KURL url = hit.absoluteLinkURL();
360        if (!url.isEmpty() && url != m_hoveredLinkURL) {
361            const char* link[2];
362            TextDirection dir;
363            CString urlStr = url.string().utf8();
364            CString titleStr = hit.title(dir).utf8();
365            link[0] = urlStr.data();
366            link[1] = titleStr.data();
367            ewk_view_mouse_link_hover_in(m_view, link);
368            m_hoveredLinkURL = url;
369        }
370    } else if (!isLink && !m_hoveredLinkURL.isEmpty()) {
371        ewk_view_mouse_link_hover_out(m_view);
372        m_hoveredLinkURL = KURL();
373    }
374}
375
376void ChromeClientEfl::setToolTip(const String& toolTip, TextDirection)
377{
378    ewk_view_tooltip_text_set(m_view, toolTip.utf8().data());
379}
380
381void ChromeClientEfl::print(Frame* frame)
382{
383    notImplemented();
384}
385
386#if ENABLE(OFFLINE_WEB_APPLICATIONS)
387void ChromeClientEfl::reachedMaxAppCacheSize(int64_t spaceNeeded)
388{
389    // FIXME: Free some space.
390    notImplemented();
391}
392
393void ChromeClientEfl::reachedApplicationCacheOriginQuota(SecurityOrigin*)
394{
395    notImplemented();
396}
397#endif
398
399#if ENABLE(TOUCH_EVENTS)
400void ChromeClientEfl::needTouchEvents(bool needed)
401{
402    ewk_view_need_touch_events_set(m_view, needed);
403}
404#endif
405
406#if ENABLE(DATABASE)
407void ChromeClientEfl::exceededDatabaseQuota(Frame* frame, const String& databaseName)
408{
409    uint64_t quota;
410    SecurityOrigin* origin = frame->document()->securityOrigin();
411
412    DatabaseDetails details = DatabaseTracker::tracker().detailsForNameAndOrigin(databaseName, origin);
413    quota = ewk_view_exceeded_database_quota(m_view,
414            kit(frame), databaseName.utf8().data(),
415            details.currentUsage(), details.expectedUsage());
416
417    /* if client did not set quota, and database is being created now, the
418     * default quota is applied
419     */
420    if (!quota && !DatabaseTracker::tracker().hasEntryForOrigin(origin))
421        quota = ewk_settings_web_database_default_quota_get();
422
423    DatabaseTracker::tracker().setQuota(origin, quota);
424}
425#endif
426
427void ChromeClientEfl::runOpenPanel(Frame* frame, PassRefPtr<FileChooser> prpFileChooser)
428{
429    RefPtr<FileChooser> chooser = prpFileChooser;
430    bool confirm;
431    Eina_List* selectedFilenames = 0;
432    Eina_List* suggestedFilenames = 0;
433    void* filename;
434    Vector<String> filenames;
435
436    for (unsigned i = 0; i < chooser->filenames().size(); i++) {
437        CString str = chooser->filenames()[i].utf8();
438        filename = strdup(str.data());
439        suggestedFilenames = eina_list_append(suggestedFilenames, filename);
440    }
441
442    confirm = ewk_view_run_open_panel(m_view, kit(frame), chooser->allowsMultipleFiles(), suggestedFilenames, &selectedFilenames);
443    EINA_LIST_FREE(suggestedFilenames, filename)
444        free(filename);
445
446    if (!confirm)
447        return;
448
449    EINA_LIST_FREE(selectedFilenames, filename) {
450        filenames.append((char *)filename);
451        free(filename);
452    }
453
454    if (chooser->allowsMultipleFiles())
455        chooser->chooseFiles(filenames);
456    else
457        chooser->chooseFile(filenames[0]);
458}
459
460void ChromeClientEfl::formStateDidChange(const Node*)
461{
462    notImplemented();
463}
464
465void ChromeClientEfl::setCursor(const Cursor&)
466{
467    notImplemented();
468}
469
470void ChromeClientEfl::requestGeolocationPermissionForFrame(Frame*, Geolocation*)
471{
472    // See the comment in WebCore/page/ChromeClient.h
473    notImplemented();
474}
475
476void ChromeClientEfl::cancelGeolocationPermissionRequestForFrame(Frame*, Geolocation*)
477{
478    notImplemented();
479}
480
481void ChromeClientEfl::cancelGeolocationPermissionForFrame(Frame*, Geolocation*)
482{
483    notImplemented();
484}
485
486void ChromeClientEfl::invalidateContents(const IntRect& updateRect, bool immediate)
487{
488    notImplemented();
489}
490
491void ChromeClientEfl::invalidateWindow(const IntRect& updateRect, bool immediate)
492{
493    notImplemented();
494}
495
496void ChromeClientEfl::invalidateContentsAndWindow(const IntRect& updateRect, bool immediate)
497{
498    Evas_Coord x, y, w, h;
499
500    x = updateRect.x();
501    y = updateRect.y();
502    w = updateRect.width();
503    h = updateRect.height();
504    ewk_view_repaint(m_view, x, y, w, h);
505}
506
507void ChromeClientEfl::invalidateContentsForSlowScroll(const IntRect& updateRect, bool immediate)
508{
509    invalidateContentsAndWindow(updateRect, immediate);
510}
511
512void ChromeClientEfl::scroll(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect)
513{
514    ewk_view_scroll(m_view, scrollDelta.width(), scrollDelta.height(), rectToScroll.x(), rectToScroll.y(), rectToScroll.width(), rectToScroll.height(), clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height(), EINA_TRUE);
515}
516
517void ChromeClientEfl::cancelGeolocationPermissionRequestForFrame(Frame*)
518{
519    notImplemented();
520}
521
522void ChromeClientEfl::iconForFiles(const Vector<String, 0u>&, PassRefPtr<FileChooser>)
523{
524    notImplemented();
525}
526
527void ChromeClientEfl::chooseIconForFiles(const Vector<String>&, FileChooser*)
528{
529    notImplemented();
530}
531
532void ChromeClientEfl::dispatchViewportDataDidChange(const ViewportArguments& arguments) const
533{
534    ewk_view_viewport_attributes_set(m_view, arguments);
535}
536
537bool ChromeClientEfl::selectItemWritingDirectionIsNatural()
538{
539    return true;
540}
541
542bool ChromeClientEfl::selectItemAlignmentFollowsMenuWritingDirection()
543{
544    return false;
545}
546
547PassRefPtr<PopupMenu> ChromeClientEfl::createPopupMenu(PopupMenuClient* client) const
548{
549    return adoptRef(new PopupMenuEfl(client));
550}
551
552PassRefPtr<SearchPopupMenu> ChromeClientEfl::createSearchPopupMenu(PopupMenuClient* client) const
553{
554    return adoptRef(new SearchPopupMenuEfl(client));
555}
556
557}
558