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-2012 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#include "ApplicationCacheStorage.h"
38#include "FileChooser.h"
39#include "FileIconLoader.h"
40#include "FloatRect.h"
41#include "Frame.h"
42#include "FrameLoader.h"
43#include "FrameLoaderClientEfl.h"
44#include "HitTestResult.h"
45#include "IntRect.h"
46#include "KURL.h"
47#include "NavigationAction.h"
48#include "NotImplemented.h"
49#include "PopupMenuEfl.h"
50#include "SearchPopupMenuEfl.h"
51#include "SecurityOrigin.h"
52#include "ViewportArguments.h"
53#include "WindowFeatures.h"
54#include "ewk_custom_handler_private.h"
55#include "ewk_file_chooser_private.h"
56#include "ewk_frame_private.h"
57#include "ewk_private.h"
58#include "ewk_security_origin_private.h"
59#include "ewk_view_private.h"
60#include <Ecore_Evas.h>
61#include <Evas.h>
62#include <wtf/text/CString.h>
63#include <wtf/text/WTFString.h>
64
65#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
66#include "NotificationPresenterClientEfl.h"
67#endif
68
69#if ENABLE(SQL_DATABASE)
70#include "DatabaseDetails.h"
71#include "DatabaseManager.h"
72#endif
73
74#if ENABLE(INPUT_TYPE_COLOR)
75#include "ColorChooserEfl.h"
76#endif
77
78#if ENABLE(FULLSCREEN_API)
79#include "Settings.h"
80#endif
81
82using namespace WebCore;
83
84static inline Evas_Object* kit(Frame* frame)
85{
86    if (!frame)
87        return 0;
88
89    FrameLoaderClientEfl* client = static_cast<FrameLoaderClientEfl*>(frame->loader()->client());
90    return client ? client->webFrame() : 0;
91}
92
93namespace WebCore {
94
95ChromeClientEfl::ChromeClientEfl(Evas_Object* view)
96    : m_view(view)
97{
98    ASSERT(m_view);
99}
100
101ChromeClientEfl::~ChromeClientEfl()
102{
103}
104
105void ChromeClientEfl::chromeDestroyed()
106{
107    delete this;
108}
109
110void ChromeClientEfl::focusedNodeChanged(Node*)
111{
112    notImplemented();
113}
114
115void ChromeClientEfl::focusedFrameChanged(Frame*)
116{
117}
118
119FloatRect ChromeClientEfl::windowRect()
120{
121    int x, y, width, height;
122
123    Ecore_Evas* ee = ecore_evas_ecore_evas_get(evas_object_evas_get(m_view));
124    ecore_evas_request_geometry_get(ee, &x, &y, &width, &height);
125
126    return FloatRect(x, y, width, height);
127}
128
129void ChromeClientEfl::setWindowRect(const FloatRect& rect)
130{
131    if (!ewk_view_setting_enable_auto_resize_window_get(m_view))
132        return;
133
134    Ecore_Evas* ee = ecore_evas_ecore_evas_get(evas_object_evas_get(m_view));
135    ecore_evas_move_resize(ee, rect.x(), rect.y(), rect.width(), rect.height());
136}
137
138FloatRect ChromeClientEfl::pageRect()
139{
140    return ewk_view_page_rect_get(m_view);
141}
142
143void ChromeClientEfl::focus()
144{
145    evas_object_focus_set(m_view, EINA_TRUE);
146}
147
148void ChromeClientEfl::unfocus()
149{
150    evas_object_focus_set(m_view, EINA_FALSE);
151}
152
153Page* ChromeClientEfl::createWindow(Frame*, const FrameLoadRequest&, const WindowFeatures& features, const NavigationAction&)
154{
155    Evas_Object* newView = ewk_view_window_create(m_view, EINA_TRUE, &features);
156    if (!newView)
157        return 0;
158
159    return EWKPrivate::corePage(newView);
160}
161
162void ChromeClientEfl::show()
163{
164    ewk_view_ready(m_view);
165}
166
167bool ChromeClientEfl::canRunModal()
168{
169    notImplemented();
170    return false;
171}
172
173void ChromeClientEfl::runModal()
174{
175    notImplemented();
176}
177
178void ChromeClientEfl::setToolbarsVisible(bool visible)
179{
180    ewk_view_toolbars_visible_set(m_view, visible);
181}
182
183bool ChromeClientEfl::toolbarsVisible()
184{
185    bool visible;
186
187    ewk_view_toolbars_visible_get(m_view, &visible);
188    return visible;
189}
190
191void ChromeClientEfl::setStatusbarVisible(bool visible)
192{
193    ewk_view_statusbar_visible_set(m_view, visible);
194}
195
196bool ChromeClientEfl::statusbarVisible()
197{
198    bool visible;
199
200    ewk_view_statusbar_visible_get(m_view, &visible);
201    return visible;
202}
203
204void ChromeClientEfl::setScrollbarsVisible(bool visible)
205{
206    ewk_view_scrollbars_visible_set(m_view, visible);
207}
208
209bool ChromeClientEfl::scrollbarsVisible()
210{
211    bool visible;
212
213    ewk_view_scrollbars_visible_get(m_view, &visible);
214    return visible;
215}
216
217void ChromeClientEfl::setMenubarVisible(bool visible)
218{
219    ewk_view_menubar_visible_set(m_view, visible);
220}
221
222bool ChromeClientEfl::menubarVisible()
223{
224    bool visible;
225
226    ewk_view_menubar_visible_get(m_view, &visible);
227    return visible;
228}
229
230void ChromeClientEfl::createSelectPopup(PopupMenuClient* client, int selected, const IntRect& rect)
231{
232    ewk_view_popup_new(m_view, client, selected, rect);
233}
234
235bool ChromeClientEfl::destroySelectPopup()
236{
237    return ewk_view_popup_destroy(m_view);
238}
239
240void ChromeClientEfl::setResizable(bool)
241{
242    notImplemented();
243}
244
245void ChromeClientEfl::closeWindowSoon()
246{
247    ewk_view_window_close(m_view);
248}
249
250bool ChromeClientEfl::canTakeFocus(FocusDirection coreDirection)
251{
252    // This is called when cycling through links/focusable objects and we
253    // reach the last focusable object.
254    ASSERT(coreDirection == FocusDirectionForward || coreDirection == FocusDirectionBackward);
255
256    Ewk_Focus_Direction direction = static_cast<Ewk_Focus_Direction>(coreDirection);
257
258    return !ewk_view_focus_can_cycle(m_view, direction);
259}
260
261void ChromeClientEfl::takeFocus(FocusDirection)
262{
263    unfocus();
264}
265
266bool ChromeClientEfl::canRunBeforeUnloadConfirmPanel()
267{
268    return true;
269}
270
271bool ChromeClientEfl::runBeforeUnloadConfirmPanel(const String& message, Frame* frame)
272{
273    return ewk_view_run_before_unload_confirm(m_view, kit(frame), message.utf8().data());
274}
275
276void ChromeClientEfl::addMessageToConsole(MessageSource, MessageLevel, const String& message,
277                                          unsigned lineNumber, unsigned columnNumber, const String& sourceID)
278{
279    UNUSED_PARAM(columnNumber);
280    ewk_view_add_console_message(m_view, message.utf8().data(), lineNumber, sourceID.utf8().data());
281}
282
283void ChromeClientEfl::runJavaScriptAlert(Frame* frame, const String& message)
284{
285    ewk_view_run_javascript_alert(m_view, kit(frame), message.utf8().data());
286}
287
288bool ChromeClientEfl::runJavaScriptConfirm(Frame* frame, const String& message)
289{
290    return ewk_view_run_javascript_confirm(m_view, kit(frame), message.utf8().data());
291}
292
293bool ChromeClientEfl::runJavaScriptPrompt(Frame* frame, const String& message, const String& defaultValue, String& result)
294{
295    const char* value = 0;
296    ewk_view_run_javascript_prompt(m_view, kit(frame), message.utf8().data(), defaultValue.utf8().data(), &value);
297    if (value) {
298        result = String::fromUTF8(value);
299        eina_stringshare_del(value);
300        return true;
301    }
302    return false;
303}
304
305void ChromeClientEfl::setStatusbarText(const String& string)
306{
307    ewk_view_statusbar_text_set(m_view, string.utf8().data());
308}
309
310bool ChromeClientEfl::shouldInterruptJavaScript()
311{
312    return ewk_view_should_interrupt_javascript(m_view);
313}
314
315KeyboardUIMode ChromeClientEfl::keyboardUIMode()
316{
317    return ewk_view_setting_include_links_in_focus_chain_get(m_view) ? KeyboardAccessTabsToLinks : KeyboardAccessDefault;
318}
319
320IntRect ChromeClientEfl::windowResizerRect() const
321{
322    notImplemented();
323    // Implementing this function will make repaint being
324    // called during resize, but as this will be done with
325    // a minor delay it adds a weird "filling" effect due
326    // to us using an evas image for showing the cairo
327    // context. So instead of implementing this function
328    // we call paint directly during resize with
329    // the new object size as its argument.
330    return IntRect();
331}
332
333void ChromeClientEfl::contentsSizeChanged(Frame* frame, const IntSize& size) const
334{
335    ewk_frame_contents_size_changed(kit(frame), size.width(), size.height());
336    if (ewk_view_frame_main_get(m_view) == kit(frame))
337        ewk_view_contents_size_changed(m_view, size.width(), size.height());
338}
339
340IntRect ChromeClientEfl::rootViewToScreen(const IntRect& rect) const
341{
342    notImplemented();
343    return rect;
344}
345
346IntPoint ChromeClientEfl::screenToRootView(const IntPoint& point) const
347{
348    notImplemented();
349    return point;
350}
351
352PlatformPageClient ChromeClientEfl::platformPageClient() const
353{
354    return EWKPrivate::corePageClient(m_view);
355}
356
357void ChromeClientEfl::scrollbarsModeDidChange() const
358{
359}
360
361void ChromeClientEfl::mouseDidMoveOverElement(const HitTestResult& hit, unsigned /*modifierFlags*/)
362{
363    // FIXME, compare with old link, look at Qt impl.
364    bool isLink = hit.isLiveLink();
365    if (isLink) {
366        KURL url = hit.absoluteLinkURL();
367        if (!url.isEmpty() && url != m_hoveredLinkURL) {
368            const char* link[2];
369            TextDirection dir;
370            CString urlStr = url.string().utf8();
371            CString titleStr = hit.title(dir).utf8();
372            link[0] = urlStr.data();
373            link[1] = titleStr.data();
374            ewk_view_mouse_link_hover_in(m_view, link);
375            m_hoveredLinkURL = url;
376        }
377    } else if (!isLink && !m_hoveredLinkURL.isEmpty()) {
378        ewk_view_mouse_link_hover_out(m_view);
379        m_hoveredLinkURL = KURL();
380    }
381}
382
383void ChromeClientEfl::setToolTip(const String& toolTip, TextDirection)
384{
385    ewk_view_tooltip_text_set(m_view, toolTip.utf8().data());
386}
387
388void ChromeClientEfl::print(Frame*)
389{
390    notImplemented();
391}
392
393void ChromeClientEfl::reachedMaxAppCacheSize(int64_t /*spaceNeeded*/)
394{
395    // FIXME: Free some space.
396    notImplemented();
397}
398
399void ChromeClientEfl::reachedApplicationCacheOriginQuota(SecurityOrigin* origin, int64_t totalSpaceNeeded)
400{
401    Ewk_Security_Origin* ewkOrigin = ewk_security_origin_new(origin);
402    int64_t defaultOriginQuota = WebCore::cacheStorage().defaultOriginQuota();
403
404    int64_t newQuota = ewk_view_exceeded_application_cache_quota(m_view, ewkOrigin, defaultOriginQuota, totalSpaceNeeded);
405    if (newQuota)
406        ewk_security_origin_application_cache_quota_set(ewkOrigin, newQuota);
407
408    ewk_security_origin_free(ewkOrigin);
409}
410
411void ChromeClientEfl::populateVisitedLinks()
412{
413    evas_object_smart_callback_call(m_view, "populate,visited,links", 0);
414}
415
416#if ENABLE(TOUCH_EVENTS)
417void ChromeClientEfl::needTouchEvents(bool needed)
418{
419    ewk_view_need_touch_events_set(m_view, needed);
420}
421#endif
422
423#if ENABLE(SQL_DATABASE)
424void ChromeClientEfl::exceededDatabaseQuota(Frame* frame, const String& databaseName, DatabaseDetails details)
425{
426    uint64_t quota;
427    SecurityOrigin* origin = frame->document()->securityOrigin();
428
429    quota = ewk_view_exceeded_database_quota(m_view,
430                                             kit(frame), databaseName.utf8().data(),
431                                             details.currentUsage(), details.expectedUsage());
432
433    /* if client did not set quota, and database is being created now, the
434     * default quota is applied
435     */
436    if (!quota && !DatabaseManager::manager().hasEntryForOrigin(origin))
437        quota = ewk_settings_web_database_default_quota_get();
438
439    DatabaseManager::manager().setQuota(origin, quota);
440}
441#endif
442
443#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
444NotificationClient* ChromeClientEfl::notificationPresenter() const
445{
446    notImplemented();
447    return 0;
448}
449#endif
450
451#if ENABLE(INPUT_TYPE_COLOR)
452PassOwnPtr<ColorChooser> ChromeClientEfl::createColorChooser(ColorChooserClient* colorChooserClient, const Color& initialColor)
453{
454    ewk_view_color_chooser_new(m_view, colorChooserClient, initialColor);
455
456    return adoptPtr(new ColorChooserEfl(this));
457}
458
459void ChromeClientEfl::removeColorChooser()
460{
461    ewk_view_color_chooser_destroy(m_view);
462}
463
464void ChromeClientEfl::updateColorChooser(const Color& color)
465{
466    ewk_view_color_chooser_changed(m_view, color);
467}
468#endif
469
470void ChromeClientEfl::runOpenPanel(Frame* frame, PassRefPtr<FileChooser> prpFileChooser)
471{
472    RefPtr<FileChooser> chooser = prpFileChooser;
473    Eina_List* selectedFilenames = 0;
474    Ewk_File_Chooser* fileChooser = ewk_file_chooser_new(chooser.get());
475    bool confirm = ewk_view_run_open_panel(m_view, kit(frame), fileChooser, &selectedFilenames);
476    ewk_file_chooser_free(fileChooser);
477    if (!confirm)
478        return;
479
480    void* filename;
481    Vector<String> filenames;
482    EINA_LIST_FREE(selectedFilenames, filename) {
483        filenames.append(String::fromUTF8(static_cast<char*>(filename)));
484        free(filename);
485    }
486
487    if (chooser->settings().allowsMultipleFiles)
488        chooser->chooseFiles(filenames);
489    else
490        chooser->chooseFile(filenames[0]);
491}
492
493void ChromeClientEfl::formStateDidChange(const Node*)
494{
495    notImplemented();
496}
497
498void ChromeClientEfl::setCursor(const Cursor& cursor)
499{
500    ewk_view_cursor_set(m_view, cursor);
501}
502
503void ChromeClientEfl::setCursorHiddenUntilMouseMoves(bool)
504{
505    notImplemented();
506}
507
508#if ENABLE(REQUEST_ANIMATION_FRAME) && !USE(REQUEST_ANIMATION_FRAME_TIMER)
509void ChromeClientEfl::scheduleAnimation()
510{
511    notImplemented();
512}
513
514void ChromeClientEfl::serviceScriptedAnimations()
515{
516    notImplemented();
517}
518#endif
519
520void ChromeClientEfl::cancelGeolocationPermissionForFrame(Frame*, Geolocation*)
521{
522    notImplemented();
523}
524
525void ChromeClientEfl::invalidateContents(const IntRect& /*updateRect*/, bool /*immediate*/)
526{
527    notImplemented();
528}
529
530void ChromeClientEfl::invalidateRootView(const IntRect& updateRect, bool /*immediate*/)
531{
532#if USE(TILED_BACKING_STORE)
533    ewk_view_tiled_backing_store_invalidate(m_view, updateRect);
534#else
535    UNUSED_PARAM(updateRect);
536    notImplemented();
537#endif
538}
539
540void ChromeClientEfl::invalidateContentsAndRootView(const IntRect& updateRect, bool /*immediate*/)
541{
542    if (updateRect.isEmpty())
543        return;
544
545    Evas_Coord x, y, w, h;
546
547    x = updateRect.x();
548    y = updateRect.y();
549    w = updateRect.width();
550    h = updateRect.height();
551    ewk_view_repaint(m_view, x, y, w, h);
552}
553
554void ChromeClientEfl::invalidateContentsForSlowScroll(const IntRect& updateRect, bool immediate)
555{
556    invalidateContentsAndRootView(updateRect, immediate);
557}
558
559void ChromeClientEfl::scroll(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect)
560{
561    ewk_view_scroll(m_view, scrollDelta, rectToScroll, clipRect);
562}
563
564void ChromeClientEfl::cancelGeolocationPermissionRequestForFrame(Frame*)
565{
566    notImplemented();
567}
568
569void ChromeClientEfl::iconForFiles(const Vector<String, 0u>&, PassRefPtr<FileChooser>)
570{
571    notImplemented();
572}
573
574void ChromeClientEfl::loadIconForFiles(const Vector<String>&, FileIconLoader*)
575{
576    notImplemented();
577}
578
579void ChromeClientEfl::dispatchViewportPropertiesDidChange(const ViewportArguments& arguments) const
580{
581    ewk_view_viewport_attributes_set(m_view, arguments);
582}
583
584bool ChromeClientEfl::selectItemWritingDirectionIsNatural()
585{
586    return true;
587}
588
589bool ChromeClientEfl::selectItemAlignmentFollowsMenuWritingDirection()
590{
591    return false;
592}
593
594bool ChromeClientEfl::hasOpenedPopup() const
595{
596    notImplemented();
597    return false;
598}
599
600PassRefPtr<PopupMenu> ChromeClientEfl::createPopupMenu(PopupMenuClient* client) const
601{
602    return adoptRef(new PopupMenuEfl(client));
603}
604
605PassRefPtr<SearchPopupMenu> ChromeClientEfl::createSearchPopupMenu(PopupMenuClient* client) const
606{
607    return adoptRef(new SearchPopupMenuEfl(client));
608}
609
610#if USE(ACCELERATED_COMPOSITING)
611void ChromeClientEfl::attachRootGraphicsLayer(Frame*, GraphicsLayer* rootLayer)
612{
613    ewk_view_root_graphics_layer_set(m_view, rootLayer);
614}
615
616void ChromeClientEfl::setNeedsOneShotDrawingSynchronization()
617{
618    ewk_view_mark_for_sync(m_view);
619}
620
621void ChromeClientEfl::scheduleCompositingLayerFlush()
622{
623    ewk_view_mark_for_sync(m_view);
624}
625
626ChromeClient::CompositingTriggerFlags ChromeClientEfl::allowedCompositingTriggers() const
627{
628    return AllTriggers;
629}
630#endif
631
632#if ENABLE(FULLSCREEN_API)
633bool ChromeClientEfl::supportsFullScreenForElement(const WebCore::Element* element, bool withKeyboard)
634{
635    UNUSED_PARAM(withKeyboard);
636
637    if (!element->document()->page())
638        return false;
639    return element->document()->page()->settings()->fullScreenEnabled();
640}
641
642void ChromeClientEfl::enterFullScreenForElement(WebCore::Element* element)
643{
644    // Keep a reference to the element to use it later in
645    // exitFullScreenForElement().
646    m_fullScreenElement = element;
647
648    element->document()->webkitWillEnterFullScreenForElement(element);
649    ewk_view_fullscreen_enter(m_view);
650    element->document()->webkitDidEnterFullScreenForElement(element);
651}
652
653void ChromeClientEfl::exitFullScreenForElement(WebCore::Element*)
654{
655    // The element passed into this function is not reliable, i.e. it could
656    // be null. In addition the parameter may be disappearing in the future.
657    // So we use the reference to the element we saved above.
658    ASSERT(m_fullScreenElement);
659
660    m_fullScreenElement->document()->webkitWillExitFullScreenForElement(m_fullScreenElement.get());
661    ewk_view_fullscreen_exit(m_view);
662    m_fullScreenElement->document()->webkitDidExitFullScreenForElement(m_fullScreenElement.get());
663
664    m_fullScreenElement.clear();
665}
666#endif
667
668#if USE(TILED_BACKING_STORE)
669void ChromeClientEfl::delegatedScrollRequested(const IntPoint&)
670{
671    notImplemented();
672}
673
674IntRect ChromeClientEfl::visibleRectForTiledBackingStore() const
675{
676    WebCore::FloatRect rect = ewk_view_page_rect_get(m_view);
677    const Evas_Object* frame = ewk_view_frame_main_get(m_view);
678
679    int x, y;
680    ewk_frame_scroll_pos_get(frame, &x, &y);
681    return IntRect(x, y, rect.width(), rect.height());
682}
683#endif
684
685}
686