Home | History | Annotate | Download | only in EWebLauncher
      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 
     62 static int currentZoomLevel = DEFAULT_ZOOM_LEVEL;
     63 static float currentZoom = 1.0;
     64 
     65 // the zoom values are chosen to be like in Mozilla Firefox 3
     66 static int zoomLevels[] = {30, 50, 67, 80, 90,
     67                             100,
     68                             110, 120, 133, 150, 170, 200, 240, 300};
     69 
     70 static int verbose = 0;
     71 
     72 static Eina_List *windows = NULL;
     73 
     74 static char *themePath = NULL;
     75 
     76 static const char *backingStores[] = {
     77     "tiled",
     78     "single",
     79     NULL
     80 };
     81 
     82 typedef 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 
     89 Window_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 
     96 static 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 
    140 typedef 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 
    150 typedef 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 
    162 static void browserDestroy(Ecore_Evas *ee);
    163 static void closeWindow(Ecore_Evas *ee);
    164 static 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 
    166 static void
    167 print_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 
    199 static int
    200 nearest_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 
    211 static Eina_Bool
    212 zoom_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 
    223 static void
    224 on_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 
    241 static void
    242 title_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  */
    269 static void
    270 viewport_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 
    282 static void
    283 on_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 
    291 static void
    292 on_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 
    300 static void
    301 on_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 
    318 static void
    319 on_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 
    331 static void
    332 on_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 
    338 static void
    339 on_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 
    351 static void
    352 on_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 
    358 static void
    359 on_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 
    371 static void
    372 on_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 
    378 static void
    379 on_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 
    391 static void
    392 on_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 
    398 static void
    399 on_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 
    406 static void
    407 on_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  */
    430 static void
    431 on_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 
    468 static void
    469 on_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 
    476 static void
    477 on_focus_out(void *data, Evas *e, Evas_Object *obj, void *event_info)
    478 {
    479     info("the webview lost keyboard focus\n");
    480 }
    481 
    482 static void
    483 on_focus_in(void *data, Evas *e, Evas_Object *obj, void *event_info)
    484 {
    485     info("the webview gained keyboard focus\n");
    486 }
    487 
    488 static void
    489 on_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 
    616 static void
    617 on_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 
    628 static void
    629 on_closeWindow(Ecore_Evas *ee)
    630 {
    631     browserDestroy(ee);
    632 }
    633 
    634 static int
    635 quit(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 
    654 static int
    655 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)
    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 
    752 static void
    753 browserDestroy(Ecore_Evas *ee)
    754 {
    755     ecore_evas_free(ee);
    756     if (!eina_list_count(windows))
    757         ecore_main_loop_quit();
    758 }
    759 
    760 static void
    761 closeWindow(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 
    775 static Eina_Bool
    776 main_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 
    789 static char *
    790 findThemePath(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 
    811 int
    812 main(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