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