1/*
2 * Copyright (C) 2008 INdT - Instituto Nokia de Tecnologia
3 * Copyright (C) 2009, 2010 ProFUSION embedded systems
4 * Copyright (C) 2009, 2010, 2011 Samsung Electronics
5 *
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include "EWebKit.h"
31
32#include <ctype.h>
33#include <Ecore.h>
34#include <Ecore_Evas.h>
35#include <Ecore_File.h>
36#include <Ecore_Getopt.h>
37#include <Ecore_X.h>
38#include <Edje.h>
39#include <Evas.h>
40#include <limits.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <sys/stat.h>
45#include <sys/types.h>
46#include <unistd.h>
47
48#define DEFAULT_WIDTH      800
49#define DEFAULT_HEIGHT     600
50#define DEFAULT_ZOOM_INIT  1.0
51
52#define info(format, args...)       \
53    do {                            \
54        if (verbose)                \
55            printf(format, ##args); \
56    } while (0)
57
58#define MIN_ZOOM_LEVEL 0
59#define DEFAULT_ZOOM_LEVEL 5
60#define MAX_ZOOM_LEVEL 13
61
62static int currentZoomLevel = DEFAULT_ZOOM_LEVEL;
63static float currentZoom = 1.0;
64
65// the zoom values are chosen to be like in Mozilla Firefox 3
66static int zoomLevels[] = {30, 50, 67, 80, 90,
67                            100,
68                            110, 120, 133, 150, 170, 200, 240, 300};
69
70static int verbose = 0;
71
72static Eina_List *windows = NULL;
73
74static char *themePath = NULL;
75
76static const char *backingStores[] = {
77    "tiled",
78    "single",
79    NULL
80};
81
82typedef struct _Window_Properties {
83    Eina_Bool toolbarsVisible:1;
84    Eina_Bool statusbarVisible:1;
85    Eina_Bool scrollbarsVisible:1;
86    Eina_Bool menubarVisible:1;
87} Window_Properties;
88
89Window_Properties windowProperties = { /* Pretend we have them and they are initially visible */
90    EINA_TRUE,
91    EINA_TRUE,
92    EINA_TRUE,
93    EINA_TRUE
94};
95
96static const Ecore_Getopt options = {
97    "EWebLauncher",
98    "%prog [options] [url]",
99    "0.0.1",
100    "(C)2008 INdT (The Nokia Technology Institute)\n"
101    "(C)2009, 2010 ProFUSION embedded systems\n"
102    "(C)2009, 2010, 2011 Samsung Electronics",
103    "GPL",
104    "Test Web Browser using the Enlightenment Foundation Libraries of WebKit",
105    EINA_TRUE, {
106        ECORE_GETOPT_STORE_STR
107            ('e', "engine", "ecore-evas engine to use."),
108        ECORE_GETOPT_CALLBACK_NOARGS
109            ('E', "list-engines", "list ecore-evas engines.",
110             ecore_getopt_callback_ecore_evas_list_engines, NULL),
111        ECORE_GETOPT_CHOICE
112            ('b', "backing-store", "choose backing store to use.", backingStores),
113        ECORE_GETOPT_STORE_DEF_BOOL
114            ('f', "flattening", "frame flattening.", 0),
115        ECORE_GETOPT_STORE_DEF_BOOL
116            ('F', "fullscreen", "fullscreen mode.", 0),
117        ECORE_GETOPT_CALLBACK_ARGS
118            ('g', "geometry", "geometry to use in x:y:w:h form.", "X:Y:W:H",
119             ecore_getopt_callback_geometry_parse, NULL),
120        ECORE_GETOPT_STORE_STR
121            ('t', "theme", "path to read the theme file from."),
122        ECORE_GETOPT_STORE_STR
123            ('U', "user-agent", "custom user agent string to use."),
124        ECORE_GETOPT_STORE_DEF_BOOL
125            ('S', "sudo-workaround", "Workaround mode for making Flash work with sudo.", 0),
126        ECORE_GETOPT_COUNT
127            ('v', "verbose", "be more verbose."),
128        ECORE_GETOPT_VERSION
129            ('V', "version"),
130        ECORE_GETOPT_COPYRIGHT
131            ('R', "copyright"),
132        ECORE_GETOPT_LICENSE
133            ('L', "license"),
134        ECORE_GETOPT_HELP
135            ('h', "help"),
136        ECORE_GETOPT_SENTINEL
137    }
138};
139
140typedef struct _Viewport {
141    int w;
142    int h;
143    float initScale;
144    float minScale;
145    float maxScale;
146    float devicePixelRatio;
147    Eina_Bool userScalable;
148} Viewport;
149
150typedef struct _ELauncher {
151    Ecore_Evas *ee;
152    Evas *evas;
153    Evas_Object *bg;
154    Evas_Object *browser;
155    const char *theme;
156    const char *userAgent;
157    const char *backingStore;
158    unsigned char isFlattening;
159    Viewport viewport;
160} ELauncher;
161
162static void browserDestroy(Ecore_Evas *ee);
163static void closeWindow(Ecore_Evas *ee);
164static int browserCreate(const char *url, const char *theme, const char *userAgent, Eina_Rectangle geometry, const char *engine, const char *backingStore, unsigned char isFlattening, unsigned char isFullscreen, const char *databasePath);
165
166static void
167print_history(Eina_List *list)
168{
169    Eina_List *l;
170    void *d;
171
172    if (!verbose)
173       return;
174
175    printf("Session history contains:\n");
176
177    EINA_LIST_FOREACH(list, l, d) {
178       Ewk_History_Item *item = (Ewk_History_Item*)d;
179       cairo_surface_t *cs = ewk_history_item_icon_surface_get(item);
180       char buf[PATH_MAX];
181       int s = snprintf(buf, sizeof(buf), "/tmp/favicon-%s.png", ewk_history_item_uri_original_get(item));
182       for (s--; s >= (int)sizeof("/tmp/favicon-"); s--) {
183           if (!isalnum(buf[s]) && buf[s] != '.')
184               buf[s] = '_';
185       }
186       cs = ewk_history_item_icon_surface_get(item);
187
188       if (cs && cairo_surface_status(cs) == CAIRO_STATUS_SUCCESS)
189           cairo_surface_write_to_png(cs, buf);
190       else
191           buf[0] = '\0';
192
193       printf("* '%s' title='%s' icon='%s'\n",
194              ewk_history_item_uri_original_get(item),
195              ewk_history_item_title_get(item), buf);
196    }
197}
198
199static int
200nearest_zoom_level_get(float factor)
201{
202    int i, intFactor = (int)(factor * 100.0);
203    for (i = 0; zoomLevels[i] <= intFactor; i++) { }
204    printf("factor=%f, intFactor=%d, zoomLevels[%d]=%d, zoomLevels[%d]=%d\n",
205           factor, intFactor, i-1, zoomLevels[i-1], i, zoomLevels[i]);
206    if (intFactor - zoomLevels[i-1] < zoomLevels[i] - intFactor)
207        return i - 1;
208    return i;
209}
210
211static Eina_Bool
212zoom_level_set(Evas_Object *webview, int level)
213{
214    float factor = ((float) zoomLevels[level]) / 100.0;
215    Evas_Coord ox, oy, mx, my, cx, cy;
216    evas_pointer_canvas_xy_get(evas_object_evas_get(webview), &mx, &my);
217    evas_object_geometry_get(webview, &ox, &oy, NULL, NULL);
218    cx = mx - ox;
219    cy = my - oy;
220    return ewk_view_zoom_animated_set(webview, factor, 0.5, cx, cy);
221}
222
223static void
224on_ecore_evas_resize(Ecore_Evas *ee)
225{
226    Evas_Object *webview;
227    Evas_Object *bg;
228    int w, h;
229
230    ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
231
232    bg = evas_object_name_find(ecore_evas_get(ee), "bg");
233    evas_object_move(bg, 0, 0);
234    evas_object_resize(bg, w, h);
235
236    webview = evas_object_name_find(ecore_evas_get(ee), "browser");
237    evas_object_move(webview, 10, 10);
238    evas_object_resize(webview, w - 20, h - 20);
239}
240
241static void
242title_set(Ecore_Evas *ee, const char *title, int progress)
243{
244    const char *appname = "EFL Test Launcher";
245    const char *separator = " - ";
246    char label[4096];
247    int size;
248
249    if (!title || !strcmp(title, "")) {
250        ecore_evas_title_set(ee, appname);
251        return;
252    }
253
254    if (progress < 100)
255        size = snprintf(label, sizeof(label), "%s (%d%%)%s%s", title, progress, separator, appname);
256    else
257        size = snprintf(label, sizeof(label), "%s %s%s", title, separator, appname);
258
259    if (size >= (int)sizeof(label))
260        return;
261
262    ecore_evas_title_set(ee, label);
263}
264
265/**
266 * This is en example function to adjust viewport via viewport tag's arguments.
267 * Application can invoke this function in order to adjust viewport tag when it is required.
268 */
269static void
270viewport_set()
271{
272    ELauncher *app;
273    app = (ELauncher*) eina_list_data_get(windows);
274
275    ewk_view_fixed_layout_size_set(app->browser, app->viewport.w, app->viewport.h);
276    ewk_view_zoom_set(app->browser, app->viewport.initScale, 0, 0);
277    if (!ewk_view_zoom_range_set(app->browser, app->viewport.minScale, app->viewport.maxScale))
278        info(" Fail to set zoom range. minScale = %f, maxScale = %f\n", app->viewport.minScale, app->viewport.maxScale);
279    ewk_view_user_scalable_set(app->browser, app->viewport.userScalable);
280}
281
282static void
283on_title_changed(void *user_data, Evas_Object *webview, void *event_info)
284{
285    ELauncher *app = (ELauncher *)user_data;
286    const char *title = (const char *)event_info;
287
288    title_set(app->ee, title, 100);
289}
290
291static void
292on_progress(void *user_data, Evas_Object *webview, void *event_info)
293{
294    ELauncher *app = (ELauncher *)user_data;
295    double *progress = (double *)event_info;
296
297    title_set(app->ee, ewk_view_title_get(app->browser), *progress * 100);
298}
299
300static void
301on_load_finished(void *user_data, Evas_Object *webview, void *event_info)
302{
303    const Ewk_Frame_Load_Error *err = (const Ewk_Frame_Load_Error *)event_info;
304
305    if (!err)
306        info("Succeeded loading page.\n");
307    else if (err->is_cancellation)
308        info("Load was cancelled.\n");
309    else
310        info("Failed loading page: %d %s \"%s\", url=%s\n",
311             err->code, err->domain, err->description, err->failing_url);
312
313    currentZoom = ewk_view_zoom_get(webview);
314    currentZoomLevel = nearest_zoom_level_get(currentZoom);
315    info("WebCore Zoom=%f, currentZoomLevel=%d\n", currentZoom, currentZoomLevel);
316}
317
318static void
319on_toolbars_visible_set(void* user_data, Evas_Object* webview, void* event_info)
320{
321    Eina_Bool *visible = (Eina_Bool *)event_info;
322    if (*visible) {
323        info("Toolbars visible changed: show");
324        windowProperties.toolbarsVisible = EINA_TRUE;
325    } else {
326        info("Toolbars visible changed: hide");
327        windowProperties.toolbarsVisible = EINA_FALSE;
328    }
329}
330
331static void
332on_toolbars_visible_get(void* user_data, Evas_Object* webview, void* event_info)
333{
334    Eina_Bool *visible = (Eina_Bool *)event_info;
335    *visible = windowProperties.toolbarsVisible;
336}
337
338static void
339on_statusbar_visible_set(void* user_data, Evas_Object* webview, void* event_info)
340{
341    Eina_Bool *visible = (Eina_Bool *)event_info;
342    if (*visible) {
343        info("Statusbar visible changed: show");
344        windowProperties.statusbarVisible = EINA_TRUE;
345    } else {
346        info("Statusbar visible changed: hide");
347        windowProperties.statusbarVisible = EINA_FALSE;
348    }
349}
350
351static void
352on_statusbar_visible_get(void* user_data, Evas_Object* webview, void* event_info)
353{
354    Eina_Bool *visible = (Eina_Bool *)event_info;
355    *visible = windowProperties.statusbarVisible;
356}
357
358static void
359on_scrollbars_visible_set(void* user_data, Evas_Object* webview, void* event_info)
360{
361    Eina_Bool *visible = (Eina_Bool *)event_info;
362    if (*visible) {
363        info("Scrollbars visible changed: show");
364        windowProperties.scrollbarsVisible = EINA_TRUE;
365    } else {
366        info("Scrollbars visible changed: hide");
367        windowProperties.scrollbarsVisible = EINA_FALSE;
368    }
369}
370
371static void
372on_scrollbars_visible_get(void* user_data, Evas_Object* webview, void* event_info)
373{
374    Eina_Bool *visible = (Eina_Bool *)event_info;
375    *visible = windowProperties.scrollbarsVisible;
376}
377
378static void
379on_menubar_visible_set(void* user_data, Evas_Object* webview, void* event_info)
380{
381    Eina_Bool *visible = (Eina_Bool *)event_info;
382    if (*visible) {
383        info("Menubar visible changed: show");
384        windowProperties.menubarVisible = EINA_TRUE;
385    } else {
386        info("Menubar visible changed: hide");
387        windowProperties.menubarVisible = EINA_FALSE;
388    }
389}
390
391static void
392on_menubar_visible_get(void* user_data, Evas_Object* webview, void* event_info)
393{
394    Eina_Bool *visible = (Eina_Bool *)event_info;
395    *visible = windowProperties.menubarVisible;
396}
397
398static void
399on_tooltip_text_set(void* user_data, Evas_Object* webview, void* event_info)
400{
401    const char *text = (const char *)event_info;
402    if (text && *text != '\0')
403        info("%s\n", text);
404}
405
406static void
407on_inputmethod_changed(void* user_data, Evas_Object* webview, void* event_info)
408{
409    Eina_Bool active = (Eina_Bool)(long)event_info;
410    unsigned int imh;
411    info("Keyboard changed: %d\n", active);
412
413    if (!active)
414        return;
415
416    imh = ewk_view_imh_get(webview);
417    info("    Keyboard flags: %#.2x\n", imh);
418
419}
420
421/**
422 * "viewport,changed" signal will be always emitted regardless of the viewport existence.
423 *
424 * If you don't want to process the viewport tag, you can either do nothing in this callback
425 * or simply ignore the signal in your application.
426 *
427 * More information about this can be found at http://developer.apple.com/safari/library/docum
428 * entation/appleapplications/reference/safariwebcontent/usingtheviewport/usingtheviewport.html
429 */
430static void
431on_viewport_changed(void* user_data, Evas_Object* webview, void* event_info)
432{
433    ELauncher *app = (ELauncher *)user_data;
434
435    float w, h, initScale, minScale, maxScale, devicePixelRatio;
436    Eina_Bool userScalable;
437
438    ewk_view_viewport_attributes_get(webview, &w, &h, &initScale, &maxScale, &minScale, &devicePixelRatio, &userScalable);
439
440    /**
441     * If there is no argument in viewport tag, argument's value is -1.
442     */
443    if ((int)w == -1)
444        w = DEFAULT_WIDTH;
445    if ((int)h == -1)
446        h = DEFAULT_HEIGHT;
447    if ((int)initScale == -1)
448        initScale = DEFAULT_ZOOM_INIT; // There's no scale separated from zooming in webkit-efl.
449    if ((int)minScale == -1)
450        minScale = ewk_view_zoom_range_min_get(webview);
451    if ((int)maxScale == -1)
452        maxScale = ewk_view_zoom_range_max_get(webview);
453    if ((int)devicePixelRatio == -1)
454        devicePixelRatio = ewk_view_device_pixel_ratio_get(webview);
455    if ((int)userScalable == -1)
456        userScalable = EINA_TRUE;
457
458    app->viewport.w = (int)w;
459    app->viewport.h = (int)h;
460    app->viewport.initScale = initScale;
461    app->viewport.minScale = minScale;
462    app->viewport.maxScale = maxScale;
463    app->viewport.devicePixelRatio = devicePixelRatio;
464    app->viewport.userScalable = userScalable;
465    viewport_set();
466}
467
468static void
469on_mouse_down(void* data, Evas* e, Evas_Object* webview, void* event_info)
470{
471    Evas_Event_Mouse_Down *ev = (Evas_Event_Mouse_Down*) event_info;
472    if (ev->button == 2)
473        evas_object_focus_set(webview, !evas_object_focus_get(webview));
474}
475
476static void
477on_focus_out(void *data, Evas *e, Evas_Object *obj, void *event_info)
478{
479    info("the webview lost keyboard focus\n");
480}
481
482static void
483on_focus_in(void *data, Evas *e, Evas_Object *obj, void *event_info)
484{
485    info("the webview gained keyboard focus\n");
486}
487
488static void
489on_key_down(void *data, Evas *e, Evas_Object *obj, void *event_info)
490{
491    Evas_Event_Key_Down *ev = (Evas_Event_Key_Down*) event_info;
492    ELauncher *app = data;
493    static const char *encodings[] = {
494        "ISO-8859-1",
495        "UTF-8",
496        NULL
497    };
498    static int currentEncoding = -1;
499
500    if (!strcmp(ev->key, "Escape")) {
501        closeWindow(app->ee);
502    } else if (!strcmp(ev->key, "F1")) {
503        info("Back (F1) was pressed\n");
504        if (ewk_view_back_possible(obj)) {
505            Ewk_History *history = ewk_view_history_get(obj);
506            Eina_List *list = ewk_history_back_list_get(history);
507            print_history(list);
508            ewk_history_item_list_free(list);
509            ewk_view_back(obj);
510        } else
511            info("Back ignored: No back history\n");
512    } else if (!strcmp(ev->key, "F2")) {
513        info("Forward (F2) was pressed\n");
514        if (ewk_view_forward_possible(obj)) {
515            Ewk_History *history = ewk_view_history_get(obj);
516            Eina_List *list = ewk_history_forward_list_get(history);
517            print_history(list);
518            ewk_history_item_list_free(list);
519            ewk_view_forward(obj);
520        } else
521            info("Forward ignored: No forward history\n");
522    } else if (!strcmp(ev->key, "F3")) {
523        currentEncoding++;
524        currentEncoding %= (sizeof(encodings) / sizeof(encodings[0]));
525        info("Set encoding (F3) pressed. New encoding to %s", encodings[currentEncoding]);
526        ewk_view_setting_encoding_custom_set(obj, encodings[currentEncoding]);
527    } else if (!strcmp(ev->key, "F4")) {
528        Evas_Object *frame = ewk_view_frame_main_get(obj);
529        Evas_Coord x, y;
530        Ewk_Hit_Test *ht;
531
532        evas_pointer_canvas_xy_get(evas_object_evas_get(obj), &x, &y);
533        ht = ewk_frame_hit_test_new(frame, x, y);
534        if (!ht)
535            printf("No hit test returned for point %d,%d\n", x, y);
536        else {
537            printf("Hit test for point %d,%d\n"
538                   "  pos=%3d,%3d\n"
539                   "  bounding_box=%d,%d + %dx%d\n"
540                   "  title='%s'\n"
541                   "  alternate_text='%s'\n"
542                   "  frame=%p (%s)\n"
543                   "  link {\n"
544                   "    text='%s'\n"
545                   "    url='%s'\n"
546                   "    title='%s'\n"
547                   "    target frame=%p (%s)\n"
548                   "  }\n"
549                   "  flags {\n"
550                   "    editable=%hhu\n"
551                   "    selected=%hhu\n"
552                   "  }\n",
553                   x, y,
554                   ht->x, ht->y,
555                   ht->bounding_box.x, ht->bounding_box.y, ht->bounding_box.w, ht->bounding_box.h,
556                   ht->title,
557                   ht->alternate_text,
558                   ht->frame, evas_object_name_get(ht->frame),
559                   ht->link.text,
560                   ht->link.url,
561                   ht->link.title,
562                   ht->link.target_frame, evas_object_name_get(ht->link.target_frame),
563                   ht->flags.editable,
564                   ht->flags.selected);
565            ewk_frame_hit_test_free(ht);
566        }
567
568    } else if (!strcmp(ev->key, "F5")) {
569        info("Reload (F5) was pressed, reloading.\n");
570        ewk_view_reload(obj);
571    } else if (!strcmp(ev->key, "F6")) {
572        info("Stop (F6) was pressed, stop loading.\n");
573        ewk_view_stop(obj);
574    } else if (!strcmp(ev->key, "F12")) {
575        Eina_Bool status = ewk_view_setting_spatial_navigation_get(obj);
576        ewk_view_setting_spatial_navigation_set(obj, !status);
577        info("Command::keyboard navigation toggle\n");
578    } else if (!strcmp(ev->key, "F7")) {
579        info("Zoom out (F7) was pressed.\n");
580        if (currentZoomLevel > MIN_ZOOM_LEVEL && zoom_level_set(obj, currentZoomLevel - 1))
581            currentZoomLevel--;
582    } else if (!strcmp(ev->key, "F8")) {
583        info("Zoom in (F8) was pressed.\n");
584        if (currentZoomLevel < MAX_ZOOM_LEVEL && zoom_level_set(obj, currentZoomLevel + 1))
585            currentZoomLevel++;
586    } else if (!strcmp(ev->key, "F9")) {
587        info("Create new window (F9) was pressed.\n");
588        Eina_Rectangle geometry = {0, 0, 0, 0};
589        browserCreate("http://www.google.com",
590                       app->theme, app->userAgent, geometry, app-> backingStore,
591                       NULL, app->isFlattening, 0, NULL);
592    } else if (!strcmp(ev->key, "F10")) {
593        Evas_Coord x, y, w, h;
594        Evas_Object *frame = ewk_view_frame_main_get(obj);
595        float zoom = zoomLevels[currentZoomLevel] / 100.0;
596
597        ewk_frame_visible_content_geometry_get(frame, EINA_FALSE, &x, &y, &w, &h);
598        x -= w;
599        y -= h;
600        w *= 4;
601        h *= 4;
602        info("Pre-render %d,%d + %dx%d\n", x, y, w, h);
603        ewk_view_pre_render_region(obj, x, y, w, h, zoom);
604    } else if (!strcmp(ev->key, "F11")) {
605        info("Pre-render 1 extra column/row with current zoom");
606        ewk_view_pre_render_relative_radius(obj, 1);
607    } else if (!strcmp(ev->key, "d")) {
608        info("Render suspended");
609        ewk_view_disable_render(obj);
610    } else if (!strcmp(ev->key, "e")) {
611        info("Render resumed");
612        ewk_view_enable_render(obj);
613    }
614}
615
616static void
617on_browser_del(void *data, Evas *evas, Evas_Object *browser, void *event)
618{
619    ELauncher *app = (ELauncher*) data;
620
621    evas_object_event_callback_del(app->browser, EVAS_CALLBACK_KEY_DOWN, on_key_down);
622    evas_object_event_callback_del(app->browser, EVAS_CALLBACK_MOUSE_DOWN, on_mouse_down);
623    evas_object_event_callback_del(app->browser, EVAS_CALLBACK_FOCUS_IN, on_focus_in);
624    evas_object_event_callback_del(app->browser, EVAS_CALLBACK_FOCUS_OUT, on_focus_out);
625    evas_object_event_callback_del(app->browser, EVAS_CALLBACK_DEL, on_browser_del);
626}
627
628static void
629on_closeWindow(Ecore_Evas *ee)
630{
631    browserDestroy(ee);
632}
633
634static int
635quit(Eina_Bool success, const char *msg)
636{
637    edje_shutdown();
638    ecore_evas_shutdown();
639
640    if (msg)
641        fputs(msg, (success) ? stdout : stderr);
642
643    if (themePath) {
644        free(themePath);
645        themePath = NULL;
646    }
647
648    if (!success)
649        return EXIT_FAILURE;
650
651    return EXIT_SUCCESS;
652}
653
654static int
655browserCreate(const char *url, const char *theme, const char *userAgent, Eina_Rectangle geometry, const char *engine, const char *backingStore, unsigned char isFlattening, unsigned char isFullscreen, const char *databasePath)
656{
657    if ((geometry.w <= 0) && (geometry.h <= 0)) {
658        geometry.w = DEFAULT_WIDTH;
659        geometry.h = DEFAULT_HEIGHT;
660    }
661
662    ELauncher *app = (ELauncher*) malloc(sizeof(ELauncher));
663    if (!app)
664        return quit(EINA_FALSE, "ERROR: could not create EWebLauncher window\n");
665
666    app->ee = ecore_evas_new(engine, 0, 0, geometry.w, geometry.h, NULL);
667
668    if (!app->ee)
669        return quit(EINA_FALSE, "ERROR: could not construct evas-ecore\n");
670
671    if (isFullscreen)
672        ecore_evas_fullscreen_set(app->ee, EINA_TRUE);
673
674    ecore_evas_title_set(app->ee, "EFL Test Launcher");
675    ecore_evas_callback_resize_set(app->ee, on_ecore_evas_resize);
676    ecore_evas_callback_delete_request_set(app->ee, closeWindow);
677
678    app->evas = ecore_evas_get(app->ee);
679
680    if (!app->evas)
681        return quit(EINA_FALSE, "ERROR: could not get evas from evas-ecore\n");
682
683    app->theme = theme;
684    app->userAgent = userAgent;
685    app->backingStore = backingStore;
686    app->isFlattening = isFlattening;
687
688    app->bg = evas_object_rectangle_add(app->evas);
689    evas_object_name_set(app->bg, "bg");
690    evas_object_color_set(app->bg, 255, 0, 255, 255);
691    evas_object_move(app->bg, 0, 0);
692    evas_object_resize(app->bg, geometry.w, geometry.h);
693    evas_object_layer_set(app->bg, EVAS_LAYER_MIN);
694    evas_object_show(app->bg);
695
696    if (backingStore && !strcasecmp(backingStore, "tiled")) {
697        app->browser = ewk_view_tiled_add(app->evas);
698        info("backing store: tiled\n");
699    } else {
700        app->browser = ewk_view_single_add(app->evas);
701        info("backing store: single\n");
702    }
703
704    ewk_view_theme_set(app->browser, theme);
705    if (userAgent)
706        ewk_view_setting_user_agent_set(app->browser, userAgent);
707    ewk_view_setting_local_storage_database_path_set(app->browser, databasePath);
708    ewk_view_setting_enable_frame_flattening_set(app->browser, isFlattening);
709
710    evas_object_name_set(app->browser, "browser");
711
712    evas_object_smart_callback_add(app->browser, "title,changed", on_title_changed, app);
713    evas_object_smart_callback_add(app->browser, "load,progress", on_progress, app);
714    evas_object_smart_callback_add(app->browser, "load,finished", on_load_finished, app);
715    evas_object_smart_callback_add(app->browser, "viewport,changed", on_viewport_changed, app);
716
717    evas_object_smart_callback_add(app->browser, "toolbars,visible,set", on_toolbars_visible_set, app);
718    evas_object_smart_callback_add(app->browser, "toolbars,visible,get", on_toolbars_visible_get, app);
719    evas_object_smart_callback_add(app->browser, "statusbar,visible,set", on_statusbar_visible_set, app);
720    evas_object_smart_callback_add(app->browser, "statusbar,visible,get", on_statusbar_visible_get, app);
721    evas_object_smart_callback_add(app->browser, "scrollbars,visible,set", on_scrollbars_visible_set, app);
722    evas_object_smart_callback_add(app->browser, "scrollbars,visible,get", on_scrollbars_visible_get, app);
723    evas_object_smart_callback_add(app->browser, "menubar,visible,set", on_menubar_visible_set, app);
724    evas_object_smart_callback_add(app->browser, "menubar,visible,get", on_menubar_visible_get, app);
725    evas_object_smart_callback_add(app->browser, "tooltip,text,set", on_tooltip_text_set, app);
726    evas_object_smart_callback_add(app->browser, "inputmethod,changed", on_inputmethod_changed, app);
727
728/*     ewk_callback_resize_requested_add(app->browser, on_resize_requested, app->ee); */
729
730    evas_object_event_callback_add(app->browser, EVAS_CALLBACK_KEY_DOWN, on_key_down, app);
731    evas_object_event_callback_add(app->browser, EVAS_CALLBACK_MOUSE_DOWN, on_mouse_down, app);
732    evas_object_event_callback_add(app->browser, EVAS_CALLBACK_FOCUS_IN, on_focus_in, app);
733    evas_object_event_callback_add(app->browser, EVAS_CALLBACK_FOCUS_OUT, on_focus_out, app);
734    evas_object_event_callback_add(app->browser, EVAS_CALLBACK_DEL, on_browser_del, app);
735
736    evas_object_move(app->browser, 10, 10);
737    evas_object_resize(app->browser, geometry.w - 20, geometry.h - 20);
738
739    if (url && (url[0] != '\0'))
740        ewk_view_uri_set(app->browser, url);
741
742    evas_object_show(app->browser);
743    ecore_evas_show(app->ee);
744
745    evas_object_focus_set(app->browser, EINA_TRUE);
746
747    windows = eina_list_append(windows, app);
748
749    return 1;
750}
751
752static void
753browserDestroy(Ecore_Evas *ee)
754{
755    ecore_evas_free(ee);
756    if (!eina_list_count(windows))
757        ecore_main_loop_quit();
758}
759
760static void
761closeWindow(Ecore_Evas *ee)
762{
763    Eina_List *l;
764    void *app;
765    EINA_LIST_FOREACH(windows, l, app)
766    {
767        if (((ELauncher*) app)->ee == ee)
768            break;
769    }
770    windows = eina_list_remove(windows, app);
771    browserDestroy(ee);
772    free(app);
773}
774
775static Eina_Bool
776main_signal_exit(void *data, int ev_type, void *ev)
777{
778    ELauncher *app;
779    while (windows) {
780        app = (ELauncher*) eina_list_data_get(windows);
781        ecore_evas_free(app->ee);
782        windows = eina_list_remove(windows, app);
783    }
784    if (!eina_list_count(windows))
785        ecore_main_loop_quit();
786    return EINA_TRUE;
787}
788
789static char *
790findThemePath(const char *theme)
791{
792    const char *defaultTheme = DATA_DIR"/default.edj";
793    char *rpath;
794    struct stat st;
795
796    if (!theme)
797        theme = defaultTheme;
798
799    rpath = realpath(theme, NULL);
800    if (!rpath)
801        return NULL;
802
803    if (stat(rpath, &st)) {
804        free(rpath);
805        return NULL;
806    }
807
808    return rpath;
809}
810
811int
812main(int argc, char *argv[])
813{
814    const char *default_url = "http://www.google.com/";
815
816    Eina_Rectangle geometry = {0, 0, 0, 0};
817    char *url = NULL;
818    char *userAgent = NULL;
819    const char *tmp;
820    const char *proxyUri;
821    char path[PATH_MAX];
822
823    char *engine = NULL;
824    char *theme = NULL;
825    char *backingStore = (char *)backingStores[1];
826
827    unsigned char quitOption = 0;
828    unsigned char isFlattening = 0;
829    unsigned char isFullscreen = 0;
830    int args;
831
832    Ecore_Getopt_Value values[] = {
833        ECORE_GETOPT_VALUE_STR(engine),
834        ECORE_GETOPT_VALUE_BOOL(quitOption),
835        ECORE_GETOPT_VALUE_STR(backingStore),
836        ECORE_GETOPT_VALUE_BOOL(isFlattening),
837        ECORE_GETOPT_VALUE_BOOL(isFullscreen),
838        ECORE_GETOPT_VALUE_PTR_CAST(geometry),
839        ECORE_GETOPT_VALUE_STR(theme),
840        ECORE_GETOPT_VALUE_STR(userAgent),
841        ECORE_GETOPT_VALUE_INT(verbose),
842        ECORE_GETOPT_VALUE_BOOL(quitOption),
843        ECORE_GETOPT_VALUE_BOOL(quitOption),
844        ECORE_GETOPT_VALUE_BOOL(quitOption),
845        ECORE_GETOPT_VALUE_BOOL(quitOption),
846        ECORE_GETOPT_VALUE_NONE
847    };
848
849    if (!ecore_evas_init())
850        return EXIT_FAILURE;
851
852    if (!edje_init()) {
853        ecore_evas_shutdown();
854        return EXIT_FAILURE;
855    }
856
857    ecore_app_args_set(argc, (const char**) argv);
858    args = ecore_getopt_parse(&options, values, argc, argv);
859
860    if (args < 0)
861       return quit(EINA_FALSE, "ERROR: could not parse options.\n");
862
863    if (quitOption)
864        return quit(EINA_TRUE, NULL);
865
866    if (args < argc)
867        url = argv[args];
868    else
869        url = (char*) default_url;
870
871    themePath = findThemePath(theme);
872    if (!themePath)
873        return quit(EINA_FALSE, "ERROR: could not find theme.\n");
874
875    ewk_init();
876    tmp = getenv("TMPDIR");
877    if (!tmp)
878        tmp = "/tmp";
879    snprintf(path, sizeof(path), "%s/.ewebkit-%u", tmp, getuid());
880    ecore_file_mkpath(path);
881    ewk_settings_icon_database_path_set(path);
882    ewk_settings_web_database_path_set(path);
883
884    proxyUri = getenv("http_proxy");
885    if (proxyUri)
886        ewk_settings_proxy_uri_set(proxyUri);
887
888    browserCreate(url, themePath, userAgent, geometry, engine, backingStore, isFlattening, isFullscreen, path);
889    ecore_event_handler_add(ECORE_EVENT_SIGNAL_EXIT, main_signal_exit, &windows);
890
891    ecore_main_loop_begin();
892
893    ewk_shutdown();
894
895    return quit(EINA_TRUE, NULL);
896}
897