8c54cbd7380db9e810aad2407874069a60507883
[WebKit-https.git] / Tools / MiniBrowser / efl / main.c
1 /*
2  * Copyright (C) 2012 Samsung Electronics
3  * Copyright (C) 2012 Intel Corporation. All rights reserved.
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19
20 #include "EWebKit2.h"
21 #include "url_utils.h"
22 #include <Ecore.h>
23 #include <Ecore_Evas.h>
24 #include <Ecore_Getopt.h>
25 #include <Eet.h>
26 #include <Eina.h>
27 #include <Elementary.h>
28 #include <Evas.h>
29
30 static const char DEFAULT_URL[] = "http://www.google.com/";
31 static const char APP_NAME[] = "EFL MiniBrowser";
32 static const int TOOL_BAR_ICON_SIZE = 24;
33 static const int TOOL_BAR_BUTTON_SIZE = 32;
34 static const int SEARCH_FIELD_SIZE = 200;
35 static const int SEARCH_BUTTON_SIZE = 25;
36 static const int MAX_SEARCH_COUNT = 100;
37 static const int DEFAULT_SEARCH_FLAGS = EWK_FIND_OPTIONS_SHOW_HIGHLIGHT | EWK_FIND_OPTIONS_CASE_INSENSITIVE | EWK_FIND_OPTIONS_WRAP_AROUND;
38 static const double TOOLTIP_DELAY_SECONDS = 1.0;
39
40 #define info(format, args...)       \
41     do {                            \
42         if (verbose)                \
43             printf(format"\n", ##args); \
44     } while (0)
45
46 static int verbose = 1;
47 static Eina_List *windows = NULL;
48 static char *evas_engine_name = NULL;
49 static char *user_agent_string = NULL;
50 static Eina_Bool encoding_detector_enabled = EINA_FALSE;
51 static Eina_Bool frame_flattening_enabled = EINA_FALSE;
52 static Eina_Bool local_storage_enabled = EINA_TRUE;
53 static Eina_Bool fullscreen_enabled = EINA_FALSE;
54 static Eina_Bool spell_checking_enabled = EINA_FALSE;
55 static Eina_Bool touch_events_enabled = EINA_FALSE;
56 static Eina_Bool fixed_layout_enabled = EINA_FALSE;
57 static int window_width = 800;
58 static int window_height = 600;
59 /* Default value of device_pixel_ratio is '0' so that we don't set custom device
60  * scale factor unless it's required by the User. */
61 static double device_pixel_ratio = 0;
62 static Eina_Bool legacy_behavior_enabled = EINA_FALSE;
63
64 #define DEFAULT_ZOOM_LEVEL 5 // Set default zoom level to 1.0 (index 5 on zoomLevels).
65 // The zoom values are chosen to be like in Mozilla Firefox 3.
66 const static float zoomLevels[] = {0.3, 0.5, 0.67, 0.8, 0.9, 1.0, 1.1, 1.2, 1.33, 1.5, 1.7, 2.0, 2.4, 3.0};
67
68 static Eina_Bool
69 zoom_level_set(Evas_Object *webview, int level)
70 {
71     if (level < 0  || level >= sizeof(zoomLevels) / sizeof(float))
72         return EINA_FALSE;
73
74     Evas_Coord ox, oy, mx, my, cx, cy;
75     evas_pointer_canvas_xy_get(evas_object_evas_get(webview), &mx, &my); // Get current mouse position on window.
76     evas_object_geometry_get(webview, &ox, &oy, NULL, NULL); // Get webview's position on window.
77     cx = mx - ox; // current x position = mouse x position - webview x position
78     cy = my - oy; // current y position = mouse y position - webview y position
79
80     Eina_Bool result = ewk_view_scale_set(webview, zoomLevels[level], cx, cy);
81     return result;
82 }
83
84 static Ewk_View_Smart_Class *miniBrowserViewSmartClass()
85 {
86     static Ewk_View_Smart_Class ewkViewClass = EWK_VIEW_SMART_CLASS_INIT_NAME_VERSION("MiniBrowser_View");
87     return &ewkViewClass;
88 }
89
90 typedef struct _Tooltip_Information {
91     Ecore_Timer *show_timer;
92     Eina_Bool activated;
93     Eina_Bool text_set;
94     Eina_Bool shown;
95 } Tooltip_Information;
96
97 typedef struct _Color_Selector {
98     Ewk_Color_Picker *ewk_picker;
99     Evas_Object *elm_selector;
100     Evas_Object *elm_selector_window;
101 } Color_Selector;
102
103 typedef struct _Browser_Window {
104     Evas_Object *elm_window;
105     Evas_Object *ewk_view;
106     Evas_Object *url_bar;
107     Evas_Object *back_button;
108     Evas_Object *forward_button;
109     struct {
110         Evas_Object *elm_menu;
111         Ewk_Popup_Menu *ewk_menu;
112     } popup;
113     struct {
114         Evas_Object *search_bar;
115         Evas_Object *search_field;
116         Evas_Object *backward_button;
117         Evas_Object *forward_button;
118     } search;
119     int current_zoom_level; 
120     Tooltip_Information tooltip;
121     Color_Selector color_selector;
122     struct {
123         Evas_Object *elm_menu;
124         Ewk_Context_Menu *ewk_menu;
125     } context_menu;
126 } Browser_Window;
127
128 typedef struct _File_Selector_Data {
129     Browser_Window* parent;
130     Evas_Object *elm_window;
131     Ewk_File_Chooser_Request *request;
132 } File_Selector_Data;
133
134 typedef struct _Auth_Data {
135     Evas_Object *popup;
136     Ewk_Auth_Request *request;
137     Evas_Object *username_entry;
138     Evas_Object *password_entry;
139 } Auth_Data;
140
141 static const Ecore_Getopt options = {
142     "MiniBrowser",
143     "%prog [options] [url]",
144     "0.0.1",
145     "(C)2012 Samsung Electronics\n (C)2012 Intel Corporation\n",
146     "",
147     "Test Web Browser using the Enlightenment Foundation Libraries (EFL) port of WebKit2",
148     EINA_TRUE, {
149         ECORE_GETOPT_STORE_STR
150             ('e', "engine", "ecore-evas engine to use."),
151         ECORE_GETOPT_STORE_STR
152             ('s', "window-size", "window size in following format (width)x(height)."),
153         ECORE_GETOPT_STORE_STR
154             ('u', "user-agent", "user agent to set."),
155         ECORE_GETOPT_STORE_DOUBLE
156             ('r', "device-pixel-ratio", "Ratio between the CSS units and device pixels."),
157         ECORE_GETOPT_CALLBACK_NOARGS
158             ('E', "list-engines", "list ecore-evas engines.",
159              ecore_getopt_callback_ecore_evas_list_engines, NULL),
160         ECORE_GETOPT_STORE_DEF_BOOL
161             ('c', "encoding-detector", "enable/disable encoding detector", EINA_FALSE),
162         ECORE_GETOPT_STORE_DEF_BOOL
163             ('f', "flattening", "frame flattening.", EINA_FALSE),
164         ECORE_GETOPT_STORE_DEF_BOOL
165             ('l', "local-storage", "HTML5 local storage support (enabled by default).", EINA_TRUE),
166         ECORE_GETOPT_STORE_DEF_BOOL
167             ('F', "full-screen", "start in full-screen.", EINA_FALSE),
168         ECORE_GETOPT_STORE_DEF_BOOL
169             ('t', "text-checking", "text spell checking enabled", EINA_TRUE),
170         ECORE_GETOPT_STORE_DEF_BOOL
171             ('T', "touch-events", "touch events enabled", EINA_FALSE),
172         ECORE_GETOPT_STORE_DEF_BOOL
173             ('L', "fixed-layout", "enable/disable to use fixed layout", EINA_FALSE),
174         ECORE_GETOPT_STORE_DEF_STR
175             ('p', "policy-cookies", "Cookies policy:\n  always - always accept,\n  never - never accept,\n  no-third-party - don't accept third-party cookies.", "no-third-party"),
176         ECORE_GETOPT_VERSION
177             ('V', "version"),
178         ECORE_GETOPT_COPYRIGHT
179             ('R', "copyright"),
180         ECORE_GETOPT_HELP
181             ('h', "help"),
182         ECORE_GETOPT_SENTINEL
183     }
184 };
185
186 static Eina_Stringshare *show_file_entry_dialog(Browser_Window *window, const char *label_tag, const char *default_text);
187 static Browser_Window *window_create(Evas_Object* opener, int width, int height, Eina_Bool view_mode);
188
189 static Browser_Window *window_find_with_elm_window(Evas_Object *elm_window)
190 {
191     Eina_List *l;
192     void *data;
193
194     if (!elm_window)
195         return NULL;
196
197     EINA_LIST_FOREACH(windows, l, data) {
198         Browser_Window *window = (Browser_Window *)data;
199         if (window->elm_window == elm_window)
200             return window;
201     }
202     return NULL;
203 }
204
205 static Browser_Window *window_find_with_ewk_view(Evas_Object *ewk_view)
206 {
207     Eina_List *l;
208     void *data;
209
210     if (!ewk_view)
211         return NULL;
212
213     EINA_LIST_FOREACH(windows, l, data) {
214         Browser_Window *window = (Browser_Window *)data;
215         if (window->ewk_view == ewk_view)
216             return window;
217     }
218     return NULL;
219 }
220
221 static Eina_Bool
222 on_tooltip_show(void *user_data)
223 {
224     Browser_Window *window = (Browser_Window *)user_data;
225
226     window->tooltip.show_timer = NULL;
227     elm_object_tooltip_show(window->elm_window);
228     window->tooltip.shown = EINA_TRUE;
229     return ECORE_CALLBACK_CANCEL;
230 }
231
232 static void
233 window_tooltip_hide(Browser_Window *window)
234 {
235     if (window->tooltip.show_timer) {
236         ecore_timer_del(window->tooltip.show_timer);
237         window->tooltip.show_timer = NULL;
238     }
239
240     if (window->tooltip.shown) {
241         elm_object_tooltip_hide(window->elm_window);
242         window->tooltip.shown = EINA_FALSE;
243     }
244 }
245
246 static void
247 window_tooltip_update(Browser_Window *window)
248 {
249     window_tooltip_hide(window);
250
251     if (window->tooltip.activated && window->tooltip.text_set)
252         window->tooltip.show_timer = ecore_timer_add(TOOLTIP_DELAY_SECONDS, on_tooltip_show, window);
253 }
254
255 static void
256 on_mouse_in(void *user_data, Evas *e, Evas_Object *ewk_view, void *event_info)
257 {
258     Browser_Window *window = (Browser_Window *)user_data;
259
260     window->tooltip.activated = EINA_TRUE;
261     window_tooltip_update(window);
262 }
263
264 static void
265 on_mouse_move(void *user_data, Evas *e, Evas_Object *ewk_view, void *event_info)
266 {
267     window_tooltip_update((Browser_Window *)user_data);
268 }
269
270 static void
271 on_mouse_out(void *user_data, Evas *e, Evas_Object *ewk_view, void *event_info)
272 {
273     Browser_Window *window = (Browser_Window *)user_data;
274
275     window->tooltip.activated = EINA_FALSE;
276     window_tooltip_update(window);
277 }
278
279 static void
280 on_window_resize(void *user_data, Evas *e, Evas_Object *elm_window, void *event_info)
281 {
282     Browser_Window *window = (Browser_Window *)user_data;
283
284     if (!window) {
285         info("ERROR: window is NULL.");
286         return;
287     }
288
289     if (window->context_menu.ewk_menu)
290         ewk_context_menu_hide(window->context_menu.ewk_menu);
291     if (window->popup.ewk_menu)
292         ewk_popup_menu_close(window->popup.ewk_menu);
293     if (window->popup.elm_menu)
294         elm_menu_close(window->popup.elm_menu);
295 }
296
297 static void window_free(Browser_Window *window)
298 {
299     evas_object_event_callback_del(window->ewk_view, EVAS_CALLBACK_MOUSE_IN, on_mouse_in);
300     evas_object_event_callback_del(window->ewk_view, EVAS_CALLBACK_MOUSE_OUT, on_mouse_out);
301     evas_object_event_callback_del(window->ewk_view, EVAS_CALLBACK_MOUSE_MOVE, on_mouse_move);
302
303     evas_object_event_callback_del(window->elm_window, EVAS_CALLBACK_RESIZE, on_window_resize);
304
305     evas_object_del(window->ewk_view);
306     /* The elm_win will take care of freeing its children */
307     evas_object_del(window->elm_window);
308
309     if (window->tooltip.show_timer)
310         ecore_timer_del(window->tooltip.show_timer);
311
312     if (window->color_selector.elm_selector_window)
313         evas_object_del(window->color_selector.elm_selector_window);
314
315     free(window);
316 }
317
318 static void window_close(Browser_Window *window)
319 {
320     windows = eina_list_remove(windows, window);
321     window_free(window);
322
323     if (!windows)
324         elm_exit();
325 }
326
327 static void
328 search_box_show(Browser_Window *window)
329 {
330     evas_object_size_hint_min_set(window->search.search_bar, SEARCH_FIELD_SIZE + 2 * SEARCH_BUTTON_SIZE, SEARCH_BUTTON_SIZE);
331
332     evas_object_show(window->search.search_bar);
333     evas_object_show(window->search.search_field);
334     evas_object_show(window->search.backward_button);
335     evas_object_show(window->search.forward_button);
336
337     /* Grab focus from the view */
338     evas_object_focus_set(window->ewk_view, EINA_FALSE);
339     elm_object_focus_set(window->search.search_field, EINA_TRUE);
340 }
341
342 static void
343 search_box_hide(Browser_Window *window)
344 {
345     ewk_view_text_find_highlight_clear(window->ewk_view);
346
347     evas_object_size_hint_min_set(window->search.search_bar, SEARCH_FIELD_SIZE + 2 * SEARCH_BUTTON_SIZE, 0);
348     evas_object_hide(window->search.search_bar);
349     evas_object_hide(window->search.search_field);
350     evas_object_hide(window->search.backward_button);
351     evas_object_hide(window->search.forward_button);
352
353     /* Give focus back to the view */
354     elm_object_focus_set(window->search.search_field, EINA_FALSE);
355     evas_object_focus_set(window->ewk_view, EINA_TRUE);
356 }
357
358 static void save_page_contents_callback(Ewk_Page_Contents_Type type, const char *data, void *user_data)
359 {
360     Eet_File *ef;
361     Eina_Stringshare *fileName = (Eina_Stringshare *)user_data;
362
363     if (!eina_str_has_extension(fileName, ".mht")) {
364         Eina_Strbuf *fileNameWithMht = eina_strbuf_new();
365         eina_strbuf_append_printf(fileNameWithMht, "%s.mht", fileName);
366         ef = eet_open(eina_strbuf_string_get(fileNameWithMht), EET_FILE_MODE_WRITE);
367         info("Saving file to: %s", eina_strbuf_string_get(fileNameWithMht));
368         eina_strbuf_free(fileNameWithMht);
369     } else {
370         ef = eet_open(fileName, EET_FILE_MODE_WRITE);
371         info("Saving file to: %s", fileName);
372     }
373
374     if (!ef) {
375         info("ERROR: Could not create File");
376         return;
377     }
378
379     eet_write(ef, "MHTML data", data, strlen(data), 0 /* compress */);
380     eet_close(ef);
381     info("SUCCESS: saved.");
382
383     eina_stringshare_del(fileName);
384 }
385
386 static void
387 on_key_down(void *user_data, Evas *e, Evas_Object *ewk_view, void *event_info)
388 {
389     Browser_Window *window = (Browser_Window *)user_data;
390     Evas_Event_Key_Down *ev = (Evas_Event_Key_Down*) event_info;
391
392     static const char *encodings[] = {
393         "ISO-8859-1",
394         "UTF-8",
395         NULL
396     };
397     static int currentEncoding = -1;
398     const Evas_Modifier *mod = evas_key_modifier_get(e);
399     Eina_Bool ctrlPressed = evas_key_modifier_is_set(mod, "Control");
400     Eina_Bool altPressed = evas_key_modifier_is_set(mod, "Alt");
401
402     if (!strcmp(ev->key, "Left") && altPressed) {
403         info("Back (Alt+Left) was pressed");
404         if (!ewk_view_back(ewk_view))
405             info("Back ignored: No back history");
406     } else if (!strcmp(ev->key, "Right") && altPressed) {
407         info("Forward (Alt+Right) was pressed");
408         if (!ewk_view_forward(ewk_view))
409             info("Forward ignored: No forward history");
410     } else if (!strcmp(ev->key, "Home") && altPressed) {
411         info("Home (Alt+Home) was pressed");
412         ewk_view_url_set(window->ewk_view, DEFAULT_URL);
413     } else if (!strcmp(ev->key, "F3")) {
414         currentEncoding = (currentEncoding + 1) % (sizeof(encodings) / sizeof(encodings[0]));
415         info("Set encoding (F3) pressed. New encoding to %s", encodings[currentEncoding]);
416         ewk_view_custom_encoding_set(ewk_view, encodings[currentEncoding]);
417     } else if (!strcmp(ev->key, "F5")) {
418         info("Reload (F5) was pressed, reloading.");
419         ewk_view_reload(ewk_view);
420     } else if (!strcmp(ev->key, "F6")) {
421         info("Stop (F6) was pressed, stop loading.");
422         ewk_view_stop(ewk_view);
423     } else if (!strcmp(ev->key, "F7")) {
424         Ewk_Pagination_Mode mode =  ewk_view_pagination_mode_get(ewk_view);
425         mode = (++mode) % (EWK_PAGINATION_MODE_BOTTOM_TO_TOP + 1);
426         if (ewk_view_pagination_mode_set(ewk_view, mode))
427             info("Change Pagination Mode (F7) was pressed, changed to: %d", mode);
428         else
429             info("Change Pagination Mode (F7) was pressed, but NOT changed!");
430     } else if (!strcmp(ev->key, "F8")) {
431         info("Create souce code window (F8) was pressed.");
432         Browser_Window *window = window_create(ewk_view, 0, 0, EINA_TRUE);
433         ewk_view_url_set(window->ewk_view, ewk_view_url_get(ewk_view));
434         windows = eina_list_append(windows, window);
435     } else if (!strcmp(ev->key, "F11")) {
436         info("Fullscreen (F11) was pressed, toggling window/fullscreen.");
437         elm_win_fullscreen_set(window->elm_window, !elm_win_fullscreen_get(window->elm_window));
438     } else if (!strcmp(ev->key, "n") && ctrlPressed) {
439         info("Create new window (Ctrl+n) was pressed.");
440         Browser_Window *window = window_create(NULL, 0, 0, EINA_FALSE);
441         ewk_view_url_set(window->ewk_view, DEFAULT_URL);
442         // 0 equals default width and height.
443         windows = eina_list_append(windows, window);
444     } else if (!strcmp(ev->key, "i") && ctrlPressed) {
445         info("Show Inspector (Ctrl+i) was pressed.");
446         ewk_view_inspector_show(ewk_view);
447     } else if (!strcmp(ev->key, "f") && ctrlPressed) {
448         info("Show Search Box (Ctrl+f) was pressed.");
449         search_box_show(window);
450     } else if (!strcmp(ev->key, "Escape")) {
451         if (evas_object_visible_get(window->search.search_bar))
452             search_box_hide(window);
453         else if (elm_win_fullscreen_get(window->elm_window))
454             ewk_view_fullscreen_exit(ewk_view);
455     } else if (ctrlPressed && (!strcmp(ev->key, "minus") || !strcmp(ev->key, "KP_Subtract"))) {
456         if (zoom_level_set(ewk_view, window->current_zoom_level - 1))
457             window->current_zoom_level--;
458         info("Zoom out (Ctrl + '-') was pressed, zoom level became %.2f", zoomLevels[window->current_zoom_level]);
459     } else if (ctrlPressed && (!strcmp(ev->key, "equal") || !strcmp(ev->key, "KP_Add"))) {
460         if (zoom_level_set(ewk_view, window->current_zoom_level + 1))
461             window->current_zoom_level++;
462         info("Zoom in (Ctrl + '+') was pressed, zoom level became %.2f", zoomLevels[window->current_zoom_level]);
463     } else if (ctrlPressed && !strcmp(ev->key, "0")) {
464         if (zoom_level_set(ewk_view, DEFAULT_ZOOM_LEVEL))
465             window->current_zoom_level = DEFAULT_ZOOM_LEVEL;
466         info("Zoom to default (Ctrl + '0') was pressed, zoom level became %.2f", zoomLevels[window->current_zoom_level]);
467     } else if (ctrlPressed && !strcmp(ev->key, "s")) {
468         Eina_Strbuf *default_file = eina_strbuf_new();
469         const char *home_path = getenv("HOME");
470         const char *title = ewk_view_title_get(window->ewk_view);
471         eina_strbuf_append_printf(default_file, "%s/%s.mht", home_path ? home_path : "/home", title ? title : "title");
472         info("Pressed (CTRL + S) : Saving Current Page.");
473         Eina_Stringshare *save_file_name = show_file_entry_dialog(window, "SAVE", eina_strbuf_string_get(default_file));
474         if (!save_file_name)
475             return;
476         ewk_view_page_contents_get(ewk_view, EWK_PAGE_CONTENTS_TYPE_MHTML, save_page_contents_callback, (void *)save_file_name);
477         eina_strbuf_free(default_file);
478     } else if (ctrlPressed && !strcmp(ev->key, "l")) {
479         const char *home_path =  getenv("HOME");
480         Eina_Stringshare *open_file_name = show_file_entry_dialog(window, "LOAD", home_path ? home_path : "/home");
481         if (!open_file_name)
482             return;
483         Eina_Strbuf *uri_path = eina_strbuf_new();
484         eina_strbuf_append_printf(uri_path, "file://%s", open_file_name);
485         info("pressed (CTRL + L) : Loading Page %s", eina_strbuf_string_get(uri_path));
486         ewk_view_url_set(ewk_view, eina_strbuf_string_get(uri_path));
487         eina_strbuf_free(uri_path);
488         eina_stringshare_del(open_file_name);
489     }
490 }
491
492 static void
493 view_focus_set(Browser_Window *window, Eina_Bool focus)
494 {
495     /* We steal focus away from elm's focus model and start to do things
496      * manually here, so elm now has no clue what's up. Tell elm that its
497      * toplevel widget is to be unfocused so elm gives up the focus */
498     elm_object_focus_set(elm_object_top_widget_get(window->elm_window), EINA_FALSE);
499     evas_object_focus_set(window->ewk_view, focus);
500 }
501
502 static void
503 on_mouse_down(void *user_data, Evas *e, Evas_Object *ewk_view, void *event_info)
504 {
505     Browser_Window *window = (Browser_Window *)user_data;
506     Evas_Event_Mouse_Down *ev = (Evas_Event_Mouse_Down *)event_info;
507
508     /* Clear selection from the URL bar */
509     elm_entry_select_none(window->url_bar);
510
511     if (ev->button == 1)
512         view_focus_set(window, EINA_TRUE);
513     else if (ev->button == 2)
514         view_focus_set(window, !evas_object_focus_get(ewk_view));
515 }
516
517 static void
518 title_set(Evas_Object *elm_window, const char *title, int progress)
519 {
520     Eina_Strbuf *buffer;
521
522     if (!title || !*title) {
523         elm_win_title_set(elm_window, APP_NAME);
524         return;
525     }
526
527     buffer = eina_strbuf_new();
528     if (progress < 100)
529         eina_strbuf_append_printf(buffer, "%s (%d%%) - %s", title, progress, APP_NAME);
530     else
531         eina_strbuf_append_printf(buffer, "%s - %s", title, APP_NAME);
532
533     elm_win_title_set(elm_window, eina_strbuf_string_get(buffer));
534     eina_strbuf_free(buffer);
535 }
536
537 static void
538 on_title_changed(void *user_data, Evas_Object *ewk_view, void *event_info)
539 {
540     Browser_Window *window = (Browser_Window *)user_data;
541     const char *title = (const char *)event_info;
542
543     title_set(window->elm_window, title, 100);
544 }
545
546 static void
547 on_url_changed(void *user_data, Evas_Object *ewk_view, void *event_info)
548 {
549     Browser_Window *window = (Browser_Window *)user_data;
550
551     char *url = elm_entry_utf8_to_markup(ewk_view_url_get(window->ewk_view));
552     elm_entry_entry_set(window->url_bar, url);
553
554     free(url);
555
556     search_box_hide(window);
557 }
558
559 static void
560 on_back_forward_list_changed(void *user_data, Evas_Object *ewk_view, void *event_info)
561 {
562     Browser_Window *window = (Browser_Window *)user_data;
563
564     /* Update navigation buttons state */
565     elm_object_disabled_set(window->back_button, !ewk_view_back_possible(ewk_view));
566     elm_object_disabled_set(window->forward_button, !ewk_view_forward_possible(ewk_view));
567 }
568
569 static void
570 on_progress(void *user_data, Evas_Object *ewk_view, void *event_info)
571 {
572     Browser_Window *window = (Browser_Window *)user_data;
573     double progress = *(double *)event_info;
574
575     title_set(window->elm_window, ewk_view_title_get(window->ewk_view), progress * 100);
576 }
577
578 static void
579 on_error(void *user_data, Evas_Object *ewk_view, void *event_info)
580 {
581     Eina_Strbuf *buffer;
582     const Ewk_Error *error = (const Ewk_Error *)event_info;
583
584     /* This is a cancellation, do not display the error page */
585     if (ewk_error_cancellation_get(error))
586         return;
587
588     buffer = eina_strbuf_new();
589     eina_strbuf_append_printf(buffer, "<html><body><div style=\"color:#ff0000\">ERROR!</div><br><div>Code: %d<br>Description: %s<br>URL: %s</div></body</html>",
590         ewk_error_code_get(error), ewk_error_description_get(error), ewk_error_url_get(error));
591
592     ewk_view_html_string_load(ewk_view, eina_strbuf_string_get(buffer), 0, ewk_error_url_get(error));
593     eina_strbuf_free(buffer);
594 }
595
596 static void
597 on_download_request(void *user_data, Evas_Object *ewk_view, void *event_info)
598 {
599     Ewk_Download_Job *download = (Ewk_Download_Job *)event_info;
600
601     // FIXME: The destination folder should be selected by the user but we set it to '/tmp' for now.
602     Eina_Strbuf *destination_path = eina_strbuf_new();
603
604     const char *suggested_name = ewk_download_job_suggested_filename_get(download);
605     if (suggested_name && *suggested_name)
606         eina_strbuf_append_printf(destination_path, "/tmp/%s", suggested_name);
607     else {
608         // Generate a unique file name since no name was suggested.
609         char unique_path[] = "/tmp/downloaded-file.XXXXXX";
610         eina_strbuf_append(destination_path, mktemp(unique_path));
611     }
612
613     ewk_download_job_destination_set(download, eina_strbuf_string_get(destination_path));
614     info("Downloading: %s", eina_strbuf_string_get(destination_path));
615     eina_strbuf_free(destination_path);
616 }
617
618 static void on_filepicker_parent_deletion(void *user_data, Evas *evas, Evas_Object *elm_window, void *event);
619
620 static void close_file_picker(File_Selector_Data *fs_data)
621 {
622     evas_object_event_callback_del(fs_data->parent->elm_window, EVAS_CALLBACK_DEL, on_filepicker_parent_deletion);
623     evas_object_del(fs_data->elm_window);
624     ewk_object_unref(fs_data->request);
625     free(fs_data);
626 }
627
628 static void
629 on_filepicker_parent_deletion(void *user_data, Evas *evas, Evas_Object *elm_window, void *event)
630 {
631     close_file_picker((File_Selector_Data *)user_data);
632 }
633
634 static void
635 on_filepicker_deletion(void *user_data, Evas_Object *elm_window, void *event_info)
636 {
637     close_file_picker((File_Selector_Data *)user_data);
638 }
639
640 static void
641 on_fileselector_done(void *user_data, Evas_Object *file_selector, void *event_info)
642 {
643     File_Selector_Data *fs_data = (File_Selector_Data *)user_data;
644
645     const char *selected = (const char *)event_info;
646     if (selected && *selected)
647         ewk_file_chooser_request_file_choose(fs_data->request, selected);
648
649     close_file_picker(fs_data);
650 }
651
652 static void
653 on_file_chooser_request(void *user_data, Evas_Object *ewk_view, void *event_info)
654 {
655     Evas_Object *bg;
656     Browser_Window *window = (Browser_Window *)user_data;
657     Ewk_File_Chooser_Request *request = (Ewk_File_Chooser_Request *)event_info;
658
659     // Show basic file picker which does not currently support multiple files
660     // or MIME type filtering.
661     Evas_Object *elm_window = elm_win_add(window->elm_window, "file-picker-window", ELM_WIN_DIALOG_BASIC);
662     elm_win_title_set(elm_window, "File picker");
663     elm_win_modal_set(elm_window, EINA_TRUE);
664
665     bg = elm_bg_add(elm_window);
666     evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
667     elm_win_resize_object_add(elm_window, bg);
668     evas_object_show(bg);
669
670     File_Selector_Data *fs_data = (File_Selector_Data *)malloc(sizeof(File_Selector_Data));
671     fs_data->parent = window;
672     fs_data->elm_window = elm_window;
673     fs_data->request = ewk_object_ref(request);
674     evas_object_smart_callback_add(elm_window, "delete,request", on_filepicker_deletion, fs_data);
675     evas_object_event_callback_add(window->elm_window, EVAS_CALLBACK_DEL, on_filepicker_parent_deletion, fs_data);
676
677     Evas_Object *file_selector = elm_fileselector_add(elm_window);
678     const char *home_path = getenv("HOME");
679     elm_fileselector_path_set(file_selector, home_path ? home_path : "/home");
680     evas_object_size_hint_weight_set(file_selector, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
681     elm_win_resize_object_add(elm_window, file_selector);
682     evas_object_show(file_selector);
683
684     evas_object_smart_callback_add(file_selector, "done", on_fileselector_done, fs_data);
685
686     evas_object_resize(elm_window, 400, 400);
687     elm_win_center(elm_window, EINA_TRUE, EINA_TRUE);
688     evas_object_show(elm_window);
689 }
690
691 static void
692 on_download_finished(void *user_data, Evas_Object *ewk_view, void *event_info)
693 {
694     Ewk_Download_Job *download = (Ewk_Download_Job *)event_info;
695     info("Download finished: %s",  ewk_download_job_destination_get(download));
696 }
697
698 static void
699 on_download_failed(void *user_data, Evas_Object *ewk_view, void *event_info)
700 {
701     info("Download failed!");
702 }
703
704 static void
705 on_color_changed(void *data, Evas_Object *obj, void *event_info)
706 {
707     int r, g, b, a;
708
709     elm_colorselector_color_get(obj, &r, &g, &b, &a);
710     evas_object_color_set(data, r, g, b, a);
711 }
712
713 static void
714 on_color_item_selected(void *data, Evas_Object *obj, void *event_info)
715 {
716     int r, g, b, a;
717     Elm_Object_Item *color_item = (Elm_Object_Item *)event_info;
718
719     elm_colorselector_palette_item_color_get(color_item, &r, &g, &b, &a);
720     evas_object_color_set(data, r, g, b, a);
721 }
722
723 static void
724 on_color_picker_ok_clicked(void *data, Evas_Object *obj, void *event_info)
725 {
726     int r, g, b, a;
727     Color_Selector *color_selector = (Color_Selector *)data;
728
729     elm_colorselector_color_get(color_selector->elm_selector, &r, &g, &b, &a);
730     ewk_color_picker_color_set(color_selector->ewk_picker, r, g, b, a);
731 }
732
733 static void
734 on_color_picker_cancel_clicked(void *data, Evas_Object *obj, void *event_info)
735 {
736     int r, g, b, a;
737
738     ewk_color_picker_color_get(data, &r, &g, &b, &a);
739     ewk_color_picker_color_set(data, r, g, b, a);
740 }
741
742 static Eina_Bool
743 on_color_picker_dismiss(Ewk_View_Smart_Data *sd)
744 {
745     Browser_Window *window = window_find_with_ewk_view(sd->self);
746
747     evas_object_del(window->color_selector.elm_selector_window);
748     window->color_selector.elm_selector_window = NULL;
749
750     return EINA_TRUE;
751 }
752
753 static Eina_Bool
754 on_color_picker_request(Ewk_View_Smart_Data *sd, Ewk_Color_Picker *color_picker)
755 {
756     int r, g, b, a;
757     Evas_Object *background, *rect, *box, *button_box, *rect_frame, *cs_frame, *ok_button, *cancel_button;
758
759     Browser_Window *window = window_find_with_ewk_view(sd->self);
760     window->color_selector.elm_selector_window = elm_win_add(window->elm_window, "color selector", ELM_WIN_BASIC);
761     window->color_selector.ewk_picker = color_picker;
762
763     elm_win_title_set(window->color_selector.elm_selector_window, "Color selector");
764
765     /* Show color view */
766     background = elm_bg_add(window->color_selector.elm_selector_window);
767     evas_object_size_hint_weight_set(background, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
768     elm_win_resize_object_add(window->color_selector.elm_selector_window, background);
769     evas_object_show(background);
770
771     box = elm_box_add(window->color_selector.elm_selector_window);
772     evas_object_size_hint_weight_set(box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
773     elm_win_resize_object_add(window->color_selector.elm_selector_window, box);
774     evas_object_show(box);
775
776     rect_frame = elm_frame_add(window->color_selector.elm_selector_window);
777     evas_object_size_hint_weight_set(rect_frame, EVAS_HINT_EXPAND, 0.3);
778     evas_object_size_hint_align_set(rect_frame, EVAS_HINT_FILL, EVAS_HINT_FILL);
779     elm_object_text_set(rect_frame, "Color View");
780     elm_box_pack_end(box, rect_frame);
781     evas_object_show(rect_frame);
782
783     rect = evas_object_rectangle_add(evas_object_evas_get(window->color_selector.elm_selector_window));
784     elm_object_content_set(rect_frame, rect);
785     ewk_color_picker_color_get(window->color_selector.ewk_picker, &r, &g, &b, &a);
786     evas_object_color_set(rect, r, g, b, a);
787     evas_object_show(rect);
788
789     /* Show color selector */
790     cs_frame = elm_frame_add(window->color_selector.elm_selector_window);
791     evas_object_size_hint_weight_set(cs_frame, EVAS_HINT_EXPAND, 0.7);
792     evas_object_size_hint_align_set(cs_frame, EVAS_HINT_FILL, EVAS_HINT_FILL);
793     elm_object_text_set(cs_frame, "Color Selector");
794     elm_box_pack_end(box, cs_frame);
795     evas_object_show(cs_frame);
796
797     window->color_selector.elm_selector = elm_colorselector_add(window->color_selector.elm_selector_window);
798     elm_object_content_set(cs_frame, window->color_selector.elm_selector);
799     evas_object_show(window->color_selector.elm_selector);
800
801     /* OK, Cancel Buttons */
802     button_box = elm_box_add(window->color_selector.elm_selector_window);
803     elm_box_horizontal_set(button_box, EINA_TRUE);
804     evas_object_size_hint_min_set(button_box, 200, 50);
805     elm_box_pack_end(box, button_box);
806     evas_object_show(button_box);
807
808     ok_button = elm_button_add(window->color_selector.elm_selector_window);
809     elm_object_text_set(ok_button, "OK");
810     elm_box_pack_end(button_box, ok_button);
811     evas_object_show(ok_button);
812
813     cancel_button = elm_button_add(window->color_selector.elm_selector_window);
814     elm_object_text_set(cancel_button, "Cancel");
815     elm_box_pack_end(button_box, cancel_button);
816     evas_object_show(cancel_button);
817
818     evas_object_smart_callback_add(ok_button, "clicked", on_color_picker_ok_clicked, &(window->color_selector));
819     evas_object_smart_callback_add(cancel_button, "clicked", on_color_picker_cancel_clicked, window->color_selector.ewk_picker);
820     evas_object_smart_callback_add(window->color_selector.elm_selector_window, "delete,request", on_color_picker_cancel_clicked, window->color_selector.ewk_picker);
821     evas_object_smart_callback_add(window->color_selector.elm_selector, "changed", on_color_changed, rect);
822     evas_object_smart_callback_add(window->color_selector.elm_selector, "color,item,selected", on_color_item_selected, rect);
823
824     elm_win_center(window->color_selector.elm_selector_window, EINA_TRUE, EINA_TRUE);
825     evas_object_resize(window->color_selector.elm_selector_window, 350, 500);
826     evas_object_show(window->color_selector.elm_selector_window);
827
828     return EINA_TRUE;
829 }
830
831 static void
832 update_view_favicon(Browser_Window *window, Evas_Object *icon)
833 {
834     /* Remove previous icon from URL bar */
835     Evas_Object *old_icon = elm_object_part_content_unset(window->url_bar, "icon");
836     if (old_icon) {
837         evas_object_unref(old_icon);
838         evas_object_del(old_icon);
839     }
840
841     /* Show new icon in URL bar */
842     if (icon) {
843         /* Workaround for icon display bug:
844          * http://trac.enlightenment.org/e/ticket/1616 */
845         evas_object_size_hint_min_set(icon, 48, 24);
846         evas_object_image_filled_set(icon, EINA_FALSE);
847         evas_object_image_fill_set(icon, 24, 0, 24, 24);
848         elm_object_part_content_set(window->url_bar, "icon", icon);
849         evas_object_ref(icon);
850     }
851 }
852
853 static void
854 on_view_favicon_changed(void *user_data, Evas_Object *ewk_view, void *event_info)
855 {
856     Browser_Window *window = (Browser_Window *)user_data;
857
858     Evas_Object* favicon = ewk_view_favicon_get(ewk_view);
859     update_view_favicon(window, favicon);
860
861     if (favicon)
862         evas_object_unref(favicon);
863 }
864
865 static int
866 quit(Eina_Bool success, const char *msg)
867 {
868     ewk_shutdown();
869     elm_shutdown();
870
871     if (msg)
872         fputs(msg, (success) ? stdout : stderr);
873
874     if (!success)
875         return EXIT_FAILURE;
876
877     return EXIT_SUCCESS;
878 }
879
880 static void
881 on_url_bar_activated(void *user_data, Evas_Object *url_bar, void *event_info)
882 {
883     Browser_Window *window = (Browser_Window *)user_data;
884
885     const char *markup_url = elm_entry_entry_get(url_bar);
886     char *user_url = elm_entry_markup_to_utf8(markup_url);
887     char *url = url_from_user_input(user_url);
888     ewk_view_url_set(window->ewk_view, url);
889
890     free(user_url);
891     free(url);
892
893     /* Give focus back to the view */
894     view_focus_set(window, EINA_TRUE);
895 }
896
897 static void
898 on_url_bar_clicked(void *user_data, Evas_Object *url_bar, void *event_info)
899 {
900     Browser_Window *window = (Browser_Window *)user_data;
901
902     /* Grab focus from the view */
903     evas_object_focus_set(window->ewk_view, EINA_FALSE);
904     elm_object_focus_set(url_bar, EINA_TRUE);
905 }
906
907 static void
908 on_search_field_aborted(void *user_data, Evas_Object *search_field, void *event_info)
909 {
910     Browser_Window *window = (Browser_Window *)user_data;
911     search_box_hide(window);
912
913     /* Give focus back to the view */
914     view_focus_set(window, EINA_TRUE);
915 }
916
917 static void
918 on_search_field_activated(void *user_data, Evas_Object *search_field, void *event_info)
919 {
920     Browser_Window *window = (Browser_Window *)user_data;
921
922     const char *markup_text = elm_entry_entry_get(search_field);
923     char *text = elm_entry_markup_to_utf8(markup_text);
924     ewk_view_text_find(window->ewk_view, text, DEFAULT_SEARCH_FLAGS, MAX_SEARCH_COUNT);
925     free(text);
926
927     /* Grab focus from the view */
928     evas_object_focus_set(window->ewk_view, EINA_FALSE);
929     elm_object_focus_set(search_field, EINA_TRUE);
930 }
931
932 static void
933 on_search_field_clicked(void *user_data, Evas_Object *search_field, void *event_info)
934 {
935     Browser_Window *window = (Browser_Window *)user_data;
936
937     /* Grab focus from the view */
938     evas_object_focus_set(window->ewk_view, EINA_FALSE);
939     elm_object_focus_set(search_field, EINA_TRUE);
940 }
941
942 static void
943 on_back_button_clicked(void *user_data, Evas_Object *back_button, void *event_info)
944 {
945     Browser_Window *window = (Browser_Window *)user_data;
946
947     ewk_view_back(window->ewk_view);
948     /* Update back button state */
949     elm_object_disabled_set(back_button, !ewk_view_back_possible(window->ewk_view));
950 }
951
952 static void
953 on_forward_button_clicked(void *user_data, Evas_Object *forward_button, void *event_info)
954 {
955     Browser_Window *window = (Browser_Window *)user_data;
956
957     ewk_view_forward(window->ewk_view);
958     /* Update forward button state */
959     elm_object_disabled_set(forward_button, !ewk_view_forward_possible(window->ewk_view));
960 }
961
962 static void
963 on_search_backward_button_clicked(void *user_data, Evas_Object *search_backward_button, void *event_info)
964 {
965     Browser_Window *window = (Browser_Window *)user_data;
966
967     char *text = elm_entry_markup_to_utf8(elm_entry_entry_get(window->search.search_field));
968     ewk_view_text_find(window->ewk_view, text, DEFAULT_SEARCH_FLAGS | EWK_FIND_OPTIONS_BACKWARDS, MAX_SEARCH_COUNT);
969     free(text);
970 }
971
972 static void
973 on_search_forward_button_clicked(void *user_data, Evas_Object *search_forward_button, void *event_info)
974 {
975     Browser_Window *window = (Browser_Window *)user_data;
976
977     char *text = elm_entry_markup_to_utf8(elm_entry_entry_get(window->search.search_field));
978     ewk_view_text_find(window->ewk_view, text, DEFAULT_SEARCH_FLAGS, MAX_SEARCH_COUNT);
979     free(text);
980 }
981
982 static void
983 on_refresh_button_clicked(void *user_data, Evas_Object *refresh_button, void *event_info)
984 {
985     Browser_Window *window = (Browser_Window *)user_data;
986
987     Evas *evas = evas_object_evas_get(refresh_button);
988     Eina_Bool ctrlPressed = evas_key_modifier_is_set(evas_key_modifier_get(evas), "Control");
989     if (ctrlPressed) {
990         info("Reloading and bypassing cache...");
991         ewk_view_reload_bypass_cache(window->ewk_view);
992     } else {
993         info("Reloading...");
994         ewk_view_reload(window->ewk_view);
995     }
996 }
997
998 static void
999 quit_event_loop(void *user_data, Evas_Object *obj, void *event_info)
1000 {
1001     ecore_main_loop_quit();
1002 }
1003
1004 static void
1005 on_ok_clicked(void *user_data, Evas_Object *obj, void *event_info)
1006 {
1007     Eina_Bool *confirmed = (Eina_Bool *)user_data;
1008     *confirmed = EINA_TRUE;
1009
1010     ecore_main_loop_quit();
1011 }
1012
1013 static Eina_Stringshare *
1014 show_file_entry_dialog(Browser_Window *window, const char *label_tag, const char *default_text)
1015 {
1016     Evas_Object *file_popup = elm_popup_add(window->elm_window);
1017     evas_object_size_hint_weight_set(file_popup, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1018     evas_object_show(file_popup);
1019
1020     Evas_Object *vbox = elm_box_add(file_popup);
1021     evas_object_size_hint_weight_set(vbox, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1022     evas_object_size_hint_align_set(vbox, EVAS_HINT_FILL, EVAS_HINT_FILL);
1023     elm_object_content_set(file_popup, vbox);
1024     evas_object_show(vbox);
1025
1026     Evas_Object *label = elm_label_add(window->elm_window);
1027     elm_object_text_set(label, label_tag);
1028     evas_object_size_hint_weight_set(label, EVAS_HINT_EXPAND, 0.0);
1029     evas_object_size_hint_align_set(label, EVAS_HINT_FILL, 0.5);
1030     evas_object_color_set(label, 23, 45, 67, 142);
1031     elm_box_pack_end(vbox, label);
1032     evas_object_show(label);
1033
1034     Evas_Object *fs_entry = elm_fileselector_entry_add(file_popup);
1035     elm_fileselector_entry_is_save_set(fs_entry, EINA_TRUE);
1036     evas_object_size_hint_align_set(fs_entry, EVAS_HINT_FILL, 0);
1037     elm_fileselector_entry_path_set(fs_entry, default_text);
1038     elm_object_text_set(fs_entry, "FileChooser");
1039     elm_box_pack_end(vbox, fs_entry);
1040     evas_object_show(fs_entry);
1041
1042     Evas_Object *hbox = elm_box_add(file_popup);
1043     evas_object_size_hint_weight_set(hbox, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1044     elm_box_horizontal_set(hbox, EINA_TRUE);
1045     evas_object_size_hint_align_set(hbox, EVAS_HINT_FILL, EVAS_HINT_FILL);
1046     elm_box_pack_end(vbox, hbox);
1047     evas_object_show(hbox);
1048
1049     Eina_Bool ok = EINA_FALSE;
1050     Evas_Object *ok_button = elm_button_add(file_popup);
1051     elm_object_text_set(ok_button, "OK");
1052     evas_object_smart_callback_add(ok_button, "clicked", on_ok_clicked, &ok);
1053     elm_box_pack_end(hbox, ok_button);
1054     evas_object_show(ok_button);
1055
1056     Evas_Object *cancel_button = elm_button_add(file_popup);
1057     elm_object_text_set(cancel_button, "Cancel");
1058     evas_object_smart_callback_add(cancel_button, "clicked", quit_event_loop, NULL);
1059     elm_box_pack_end(hbox, cancel_button);
1060     evas_object_show(cancel_button);
1061
1062     ecore_main_loop_begin();
1063
1064     Eina_Stringshare *file_path = ok ? eina_stringshare_add(elm_fileselector_entry_path_get(fs_entry)) : NULL;
1065     evas_object_del(file_popup);
1066     return file_path;
1067 }
1068
1069 static void
1070 on_javascript_alert(Ewk_View_Smart_Data *smartData, const char *message)
1071 {
1072     Browser_Window *window = window_find_with_ewk_view(smartData->self);
1073
1074     Evas_Object *alert_popup = elm_popup_add(window->elm_window);
1075     evas_object_size_hint_weight_set(alert_popup, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1076     elm_object_text_set(alert_popup, message);
1077     elm_object_part_text_set(alert_popup, "title,text", "Alert");
1078
1079     /* Popup buttons */
1080     Evas_Object *button = elm_button_add(alert_popup);
1081     elm_object_text_set(button, "OK");
1082     elm_object_part_content_set(alert_popup, "button1", button);
1083     evas_object_smart_callback_add(button, "clicked", quit_event_loop, NULL);
1084     elm_object_focus_set(button, EINA_TRUE);
1085     evas_object_show(alert_popup);
1086
1087     /* Make modal */
1088     ecore_main_loop_begin();
1089
1090     evas_object_del(alert_popup);
1091 }
1092
1093 static Eina_Bool
1094 on_javascript_confirm(Ewk_View_Smart_Data *smartData, const char *message)
1095 {
1096     Browser_Window *window = window_find_with_ewk_view(smartData->self);
1097
1098     Eina_Bool ok = EINA_FALSE;
1099
1100     Evas_Object *confirm_popup = elm_popup_add(window->elm_window);
1101     evas_object_size_hint_weight_set(confirm_popup, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1102     elm_object_text_set(confirm_popup, message);
1103     elm_object_part_text_set(confirm_popup, "title,text", "Confirmation");
1104
1105     /* Popup buttons */
1106     Evas_Object *cancel_button = elm_button_add(confirm_popup);
1107     elm_object_text_set(cancel_button, "Cancel");
1108     elm_object_part_content_set(confirm_popup, "button1", cancel_button);
1109     evas_object_smart_callback_add(cancel_button, "clicked", quit_event_loop, NULL);
1110     Evas_Object *ok_button = elm_button_add(confirm_popup);
1111     elm_object_text_set(ok_button, "OK");
1112     elm_object_part_content_set(confirm_popup, "button2", ok_button);
1113     evas_object_smart_callback_add(ok_button, "clicked", on_ok_clicked, &ok);
1114     elm_object_focus_set(ok_button, EINA_TRUE);
1115     evas_object_show(confirm_popup);
1116
1117     /* Make modal */
1118     ecore_main_loop_begin();
1119
1120     evas_object_del(confirm_popup);
1121
1122     return ok;
1123 }
1124
1125 static const char *
1126 on_javascript_prompt(Ewk_View_Smart_Data *smartData, const char *message, const char *default_value)
1127 {
1128     Browser_Window *window = window_find_with_ewk_view(smartData->self);
1129
1130     Eina_Bool ok = EINA_FALSE;
1131
1132     Evas_Object *prompt_popup = elm_popup_add(window->elm_window);
1133     evas_object_size_hint_weight_set(prompt_popup, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1134     elm_object_part_text_set(prompt_popup, "title,text", "Prompt");
1135
1136     /* Popup Content */
1137     Evas_Object *box = elm_box_add(window->elm_window);
1138     elm_box_padding_set(box, 0, 4);
1139     evas_object_size_hint_weight_set(box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1140     evas_object_size_hint_align_set(box, EVAS_HINT_FILL, EVAS_HINT_FILL);
1141     evas_object_show(box);
1142
1143     Evas_Object *prompt = elm_label_add(window->elm_window);
1144     elm_object_text_set(prompt, message);
1145     evas_object_size_hint_weight_set(prompt, EVAS_HINT_EXPAND, 0.0);
1146     evas_object_size_hint_align_set(prompt, EVAS_HINT_FILL, 0.5);
1147     elm_box_pack_end(box, prompt);
1148     evas_object_show(prompt);
1149
1150     Evas_Object *entry = elm_entry_add(window->elm_window);
1151     elm_entry_scrollable_set(entry, EINA_TRUE);
1152     elm_entry_single_line_set(entry, EINA_TRUE);
1153     elm_entry_text_style_user_push(entry, "DEFAULT='font_size=18'");
1154     elm_entry_entry_set(entry, default_value ? default_value : "");
1155     elm_entry_select_all(entry);
1156     evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, 0.0);
1157     evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, 0.5);
1158     elm_box_pack_end(box, entry);
1159     elm_object_focus_set(entry, EINA_TRUE);
1160     evas_object_show(entry);
1161
1162     elm_object_content_set(prompt_popup, box);
1163
1164     /* Popup buttons */
1165     Evas_Object *cancel_button = elm_button_add(prompt_popup);
1166     elm_object_text_set(cancel_button, "Cancel");
1167     elm_object_part_content_set(prompt_popup, "button1", cancel_button);
1168     evas_object_smart_callback_add(cancel_button, "clicked", quit_event_loop, NULL);
1169     Evas_Object *ok_button = elm_button_add(prompt_popup);
1170     elm_object_text_set(ok_button, "OK");
1171     elm_object_part_content_set(prompt_popup, "button2", ok_button);
1172     evas_object_smart_callback_add(ok_button, "clicked", on_ok_clicked, &ok);
1173     evas_object_show(prompt_popup);
1174
1175     /* Make modal */
1176     ecore_main_loop_begin();
1177
1178     /* The return string need to be stringshared */
1179     const char *prompt_text = ok ? eina_stringshare_add(elm_entry_entry_get(entry)) : NULL;
1180     evas_object_del(prompt_popup);
1181
1182     return prompt_text;
1183 }
1184
1185 static void
1186 on_popup_menu_discarded(void *user_data, Evas_Object *obj, void *event_info)
1187 {
1188     // The user clicked outside the menu to discard it.
1189     info("Popup menu was discarded.");
1190     Browser_Window *window = (Browser_Window *)user_data;
1191     ewk_popup_menu_close(window->popup.ewk_menu);
1192 }
1193
1194 static void
1195 on_popup_menu_item_clicked(void *user_data, Evas_Object *obj, void *event_info)
1196 {
1197     Browser_Window *window = (Browser_Window *)user_data;
1198     Elm_Object_Item *item = (Elm_Object_Item *)event_info;
1199
1200     info("Selected popup menu index: %u", elm_menu_item_index_get(item));
1201     ewk_popup_menu_selected_index_set(window->popup.ewk_menu, elm_menu_item_index_get(item));
1202
1203     // Close popup menu.
1204     ewk_popup_menu_close(window->popup.ewk_menu);
1205 }
1206
1207 static void
1208 popup_menu_populate(Evas_Object *elm_menu, Ewk_Popup_Menu *ewk_menu, void *user_data)
1209 {
1210     const Eina_List* ewk_items = ewk_popup_menu_items_get(ewk_menu);
1211
1212     void *data;
1213     const Eina_List *l;
1214     EINA_LIST_FOREACH(ewk_items, l, data) {
1215         Ewk_Popup_Menu_Item *ewk_item = (Ewk_Popup_Menu_Item *)data;
1216         switch (ewk_popup_menu_item_type_get(ewk_item)) {
1217         case EWK_POPUP_MENU_SEPARATOR:
1218             elm_menu_item_separator_add(elm_menu, NULL);
1219             break;
1220         case EWK_POPUP_MENU_ITEM:
1221             if (ewk_popup_menu_item_is_label_get(ewk_item)) {
1222                 Elm_Object_Item *item = elm_menu_item_add(elm_menu, NULL, NULL, ewk_popup_menu_item_text_get(ewk_item), NULL, NULL);
1223                 elm_object_item_disabled_set(item, EINA_TRUE);
1224             } else {
1225                 Elm_Object_Item *item = elm_menu_item_add(elm_menu, NULL, NULL, ewk_popup_menu_item_text_get(ewk_item), on_popup_menu_item_clicked, user_data);
1226                 const char *tooltip_text = ewk_popup_menu_item_tooltip_get(ewk_item);
1227                 if (tooltip_text && tooltip_text[0] != '\0')
1228                     elm_object_item_tooltip_text_set(item, tooltip_text);
1229                 elm_object_item_disabled_set(item, !ewk_popup_menu_item_enabled_get(ewk_item));
1230                 elm_menu_item_selected_set(item, ewk_popup_menu_item_selected_get(ewk_item));
1231             }
1232             break;
1233         default:
1234             info("Unrecognized popup menu item type!");
1235             break;
1236         }
1237     }
1238 }
1239
1240 static Eina_Bool
1241 on_popup_menu_show(Ewk_View_Smart_Data *smartData, Eina_Rectangle rect, Ewk_Text_Direction text_direction, double page_scale_factor, Ewk_Popup_Menu *ewk_menu)
1242 {
1243     Browser_Window *window = window_find_with_ewk_view(smartData->self);
1244
1245     if (window->popup.elm_menu)
1246         evas_object_del(window->popup.elm_menu);
1247
1248     window->popup.elm_menu = elm_menu_add(window->elm_window);
1249     window->popup.ewk_menu = ewk_menu;
1250     evas_object_smart_callback_add(window->popup.elm_menu, "clicked", on_popup_menu_discarded, window);
1251
1252     popup_menu_populate(window->popup.elm_menu, ewk_menu, window);
1253
1254     info("Showing popup menu at (%d, %d)", rect.x, rect.y);
1255     elm_menu_move(window->popup.elm_menu, rect.x, rect.y);
1256     evas_object_show(window->popup.elm_menu);
1257
1258     return EINA_TRUE;
1259 }
1260
1261 static Eina_Bool
1262 on_popup_menu_hide(Ewk_View_Smart_Data *smartData)
1263 {
1264     Browser_Window *window = window_find_with_ewk_view(smartData->self);
1265
1266     if (!window->popup.elm_menu)
1267         return EINA_FALSE;
1268
1269     elm_menu_close(window->popup.elm_menu);
1270
1271     return EINA_TRUE;
1272 }
1273
1274 static Eina_Bool on_window_geometry_get(Ewk_View_Smart_Data *sd, Evas_Coord *x, Evas_Coord *y, Evas_Coord *width, Evas_Coord *height)
1275 {
1276     Browser_Window *window = window_find_with_ewk_view(sd->self);
1277
1278     evas_object_geometry_get(window->elm_window, x, y, width, height);
1279
1280     return EINA_TRUE;
1281 }
1282
1283 static Eina_Bool on_window_geometry_set(Ewk_View_Smart_Data *sd, Evas_Coord x, Evas_Coord y, Evas_Coord width, Evas_Coord height)
1284 {
1285     Browser_Window *window = window_find_with_ewk_view(sd->self);
1286
1287     evas_object_move(window->elm_window, x, y);
1288     evas_object_resize(window->elm_window, width, height);
1289
1290     return EINA_TRUE;
1291 }
1292
1293 typedef struct {
1294     Evas_Object *ewk_view;
1295     Evas_Object *permission_popup;
1296 } PermissionData;
1297
1298 static void
1299 on_fullscreen_accept(void *user_data, Evas_Object *obj, void *event_info)
1300 {
1301     PermissionData *permission_data = (PermissionData *)user_data;
1302
1303     evas_object_del(permission_data->permission_popup);
1304     evas_object_focus_set(permission_data->ewk_view, EINA_TRUE);
1305     free(permission_data);
1306 }
1307
1308 static void
1309 on_fullscreen_deny(void *user_data, Evas_Object *obj, void *event_info)
1310 {
1311     PermissionData *permission_data = (PermissionData *)user_data;
1312
1313     ewk_view_fullscreen_exit(permission_data->ewk_view);
1314     evas_object_del(permission_data->permission_popup);
1315     evas_object_focus_set(permission_data->ewk_view, EINA_TRUE);
1316     free(permission_data);
1317 }
1318
1319 static Eina_Bool on_fullscreen_enter(Ewk_View_Smart_Data *sd, Ewk_Security_Origin *origin)
1320 {
1321     Browser_Window *window = window_find_with_ewk_view(sd->self);
1322
1323     /* Go fullscreen */
1324     elm_win_fullscreen_set(window->elm_window, EINA_TRUE);
1325
1326     /* Show user popup */
1327     Evas_Object *permission_popup = elm_popup_add(window->elm_window);
1328     evas_object_size_hint_weight_set(permission_popup, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1329
1330     Eina_Strbuf *message = eina_strbuf_new();
1331     eina_strbuf_append_printf(message, "%s is now fullscreen.<br>Press ESC at any time to exit fullscreen.<br>Allow fullscreen?", ewk_security_origin_host_get(origin));
1332     elm_object_text_set(permission_popup, eina_strbuf_string_get(message));
1333     eina_strbuf_free(message);
1334     elm_object_part_text_set(permission_popup, "title,text", "Fullscreen Permission");
1335
1336     /* Popup buttons */
1337     PermissionData *permission_data = (PermissionData *)malloc(sizeof(PermissionData));
1338     permission_data->ewk_view = window->ewk_view;
1339     permission_data->permission_popup = permission_popup;
1340     Evas_Object *accept_button = elm_button_add(permission_popup);
1341     elm_object_text_set(accept_button, "Accept");
1342     elm_object_part_content_set(permission_popup, "button1", accept_button);
1343     evas_object_smart_callback_add(accept_button, "clicked", on_fullscreen_accept, permission_data);
1344
1345     Evas_Object *deny_button = elm_button_add(permission_popup);
1346     elm_object_text_set(deny_button, "Deny");
1347     elm_object_part_content_set(permission_popup, "button2", deny_button);
1348     evas_object_smart_callback_add(deny_button, "clicked", on_fullscreen_deny, permission_data);
1349
1350     evas_object_show(permission_popup);
1351
1352     return EINA_TRUE;
1353 }
1354
1355 static Eina_Bool on_fullscreen_exit(Ewk_View_Smart_Data *sd)
1356 {
1357     Browser_Window *window = window_find_with_ewk_view(sd->self);
1358
1359     elm_win_fullscreen_set(window->elm_window, EINA_FALSE);
1360
1361     return EINA_TRUE;
1362 }
1363
1364 static Evas_Object *
1365 on_window_create(Ewk_View_Smart_Data *smartData, const Ewk_Window_Features *window_features)
1366 {
1367     int x = 0;
1368     int y = 0;
1369     int width = 0;
1370     int height = 0;
1371
1372     ewk_window_features_geometry_get(window_features, &x, &y, &width, &height);
1373
1374     if (!width)
1375         width = window_width;
1376
1377     if (!height)
1378         height = window_height;
1379
1380     Browser_Window *window = window_create(smartData->self, width, height, EINA_FALSE);
1381     Evas_Object *new_view = window->ewk_view;
1382
1383     windows = eina_list_append(windows, window);
1384
1385     info("minibrowser: location(%d,%d) size=(%d,%d)", x, y, width, height);
1386
1387     return new_view;
1388 }
1389
1390 static void
1391 on_window_close(Ewk_View_Smart_Data *smartData)
1392 {
1393     Browser_Window *window = window_find_with_ewk_view(smartData->self);
1394     window_close(window);
1395 }
1396
1397 static void
1398 context_menu_item_selected_cb(void *data, Evas_Object *obj, void *event_info)
1399 {
1400     if (!data) {
1401         info("ERROR: context menu callback data is NULL.");
1402         return;
1403     }
1404
1405     Ewk_Context_Menu_Item *ewk_item = (Ewk_Context_Menu_Item *)data;
1406     info("Selected context menu item: %s.", ewk_context_menu_item_title_get(ewk_item));
1407     ewk_context_menu_item_select(ewk_context_menu_item_parent_menu_get(ewk_item), ewk_item);
1408     ewk_context_menu_hide(ewk_context_menu_item_parent_menu_get(ewk_item));
1409 }
1410
1411 static void
1412 context_menu_populate(Evas_Object* context_menu, Ewk_Context_Menu *ewk_menu, Elm_Object_Item *parent_item)
1413 {
1414     if (!context_menu || !ewk_menu) {
1415         info("ERROR: necessary objects are NULL.");
1416         return;
1417     }
1418
1419     const Eina_List *list = ewk_context_menu_items_get(ewk_menu);
1420     const Eina_List *l;
1421     void *data;
1422
1423     Ewk_Context_Menu_Item *ewk_item;
1424     Elm_Object_Item *elm_menu_item;
1425     Evas_Object *elm_check_item;
1426
1427     EINA_LIST_FOREACH(list, l, data) {
1428         ewk_item = (Ewk_Context_Menu_Item *)data;
1429         switch (ewk_context_menu_item_type_get(ewk_item)) {
1430         case EWK_ACTION_TYPE:
1431             elm_menu_item = elm_menu_item_add(context_menu, parent_item, NULL, ewk_context_menu_item_title_get(ewk_item), context_menu_item_selected_cb, ewk_item);
1432             break;
1433         case EWK_CHECKABLE_ACTION_TYPE:
1434             elm_check_item = elm_check_add(context_menu);
1435             elm_menu_item = elm_menu_item_add(context_menu, parent_item, NULL, ewk_context_menu_item_title_get(ewk_item), context_menu_item_selected_cb, ewk_item);
1436             elm_object_item_content_set(elm_menu_item, elm_check_item);
1437             elm_check_state_set(elm_check_item, ewk_context_menu_item_checked_get(ewk_item));
1438             break;
1439         case EWK_SUBMENU_TYPE:
1440             elm_menu_item = elm_menu_item_add(context_menu, parent_item, NULL, ewk_context_menu_item_title_get(ewk_item), NULL, ewk_item);
1441             if (elm_menu_item)
1442                 context_menu_populate(context_menu, ewk_context_menu_item_submenu_get(ewk_item), elm_menu_item);
1443             break;
1444         default:
1445             continue;
1446         }
1447         elm_object_item_disabled_set(elm_menu_item, !ewk_context_menu_item_enabled_get(ewk_item));
1448     }
1449 }
1450
1451 static Eina_Bool
1452 on_context_menu_show(Ewk_View_Smart_Data *sd, Evas_Coord x, Evas_Coord y, Ewk_Context_Menu *menu)
1453 {
1454     Browser_Window *window = window_find_with_ewk_view(sd->self);
1455
1456     if (!window || !menu) {
1457         info("ERROR: necessary objects are NULL.");
1458         return EINA_FALSE;
1459     }
1460
1461     window->context_menu.elm_menu = elm_menu_add(window->elm_window);
1462
1463     if (!window->context_menu.elm_menu) {
1464         info("ERROR: could not create menu widget.");
1465         return EINA_FALSE;
1466     }
1467
1468     window->context_menu.ewk_menu = menu;
1469
1470     context_menu_populate(window->context_menu.elm_menu, menu, NULL);
1471
1472     info("Showing context menu at (%d, %d).", x, y);
1473     elm_menu_move(window->context_menu.elm_menu, x, y);
1474     evas_object_show(window->context_menu.elm_menu);
1475
1476     return EINA_TRUE;
1477 }
1478
1479 static Eina_Bool
1480 on_context_menu_hide(Ewk_View_Smart_Data *sd)
1481 {
1482     Browser_Window *window = window_find_with_ewk_view(sd->self);
1483
1484     if (!window || !window->context_menu.elm_menu) {
1485         info("ERROR: necessary objects are NULL.");
1486         return EINA_FALSE;
1487     }
1488
1489     elm_menu_close(window->context_menu.elm_menu);
1490     evas_object_del(window->context_menu.elm_menu);
1491     window->context_menu.elm_menu = NULL;
1492     window->context_menu.ewk_menu = NULL;
1493
1494     return EINA_TRUE;
1495 }
1496
1497 static void
1498 auth_popup_close(Auth_Data *auth_data)
1499 {
1500     ewk_object_unref(auth_data->request);
1501     evas_object_del(auth_data->popup);
1502     free(auth_data);
1503 }
1504
1505 static void
1506 on_auth_cancel(void *user_data, Evas_Object *obj, void *event_info)
1507 {
1508     Auth_Data *auth_data = (Auth_Data *)user_data;
1509
1510     ewk_auth_request_cancel(auth_data->request);
1511
1512     auth_popup_close(auth_data);
1513 }
1514
1515 static void
1516 on_auth_ok(void *user_data, Evas_Object *obj, void *event_info)
1517 {
1518     Auth_Data *auth_data = (Auth_Data *)user_data;
1519
1520     const char *username = elm_entry_entry_get(auth_data->username_entry);
1521     const char *password = elm_entry_entry_get(auth_data->password_entry);
1522     ewk_auth_request_authenticate(auth_data->request, username, password);
1523
1524     auth_popup_close(auth_data);
1525 }
1526
1527 static void
1528 on_authentication_request(void *user_data, Evas_Object *obj, void *event_info)
1529 {
1530     Browser_Window *window = (Browser_Window *)user_data;
1531     Ewk_Auth_Request *request = ewk_object_ref((Ewk_Auth_Request *)event_info);
1532
1533     Auth_Data *auth_data = (Auth_Data *)malloc(sizeof(Auth_Data));
1534     auth_data->request = request;
1535
1536     Evas_Object *auth_popup = elm_popup_add(window->elm_window);
1537     auth_data->popup = auth_popup;
1538     evas_object_size_hint_weight_set(auth_popup, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1539     elm_object_part_text_set(auth_popup, "title,text", "Authentication Required");
1540
1541     /* Popup Content */
1542     Evas_Object *vbox = elm_box_add(auth_popup);
1543     elm_box_padding_set(vbox, 0, 4);
1544     evas_object_size_hint_weight_set(vbox, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1545     evas_object_size_hint_align_set(vbox, EVAS_HINT_FILL, EVAS_HINT_FILL);
1546     elm_object_content_set(auth_popup, vbox);
1547     evas_object_show(vbox);
1548
1549     /* Authentication message */
1550     Evas_Object *label = elm_label_add(auth_popup);
1551     elm_label_line_wrap_set(label, ELM_WRAP_WORD);
1552     Eina_Strbuf *auth_text = eina_strbuf_new();
1553     const char *host = ewk_auth_request_host_get(request);
1554     const char *realm = ewk_auth_request_realm_get(request);
1555     eina_strbuf_append_printf(auth_text, "A username and password are being requested by %s. The site says: \"%s\"", host, realm ? realm : "");
1556     elm_object_text_set(label, eina_strbuf_string_get(auth_text));
1557     eina_strbuf_free(auth_text);
1558     evas_object_size_hint_weight_set(label, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1559     evas_object_size_hint_align_set(label, EVAS_HINT_FILL, EVAS_HINT_FILL);
1560     elm_box_pack_end(vbox, label);
1561     evas_object_show(label);
1562
1563     /* Credential table */
1564     Evas_Object *table = elm_table_add(auth_popup);
1565     elm_table_padding_set(table, 2, 2);
1566     elm_table_homogeneous_set(table, EINA_TRUE);
1567     evas_object_size_hint_weight_set(table, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1568     evas_object_size_hint_align_set(table, EVAS_HINT_FILL, EVAS_HINT_FILL);
1569     elm_box_pack_end(vbox, table);
1570     evas_object_show(table);
1571
1572     /* Username row */
1573     Evas_Object *username_label = elm_label_add(auth_popup);
1574     elm_object_text_set(username_label, "Username:");
1575     evas_object_size_hint_weight_set(username_label, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1576     evas_object_size_hint_align_set(username_label, 1, EVAS_HINT_FILL);
1577     elm_table_pack(table, username_label, 0, 0, 1, 1);
1578     evas_object_show(username_label);
1579
1580     Evas_Object *username_entry = elm_entry_add(auth_popup);
1581     auth_data->username_entry = username_entry;
1582     elm_entry_scrollable_set(username_entry, EINA_TRUE);
1583     elm_entry_single_line_set(username_entry, EINA_TRUE);
1584     elm_entry_text_style_user_push(username_entry, "DEFAULT='font_size=18'");
1585     const char *suggested_username = ewk_auth_request_suggested_username_get(request);
1586     elm_entry_entry_set(username_entry, suggested_username ? suggested_username : "");
1587     evas_object_size_hint_weight_set(username_entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1588     evas_object_size_hint_align_set(username_entry, EVAS_HINT_FILL, EVAS_HINT_FILL);
1589     elm_table_pack(table, username_entry, 1, 0, 2, 1);
1590     elm_object_focus_set(username_entry, EINA_TRUE);
1591     evas_object_show(username_entry);
1592
1593     /* Password row */
1594     Evas_Object *password_label = elm_label_add(auth_popup);
1595     elm_object_text_set(password_label, "Password:");
1596     evas_object_size_hint_weight_set(password_label, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1597     evas_object_size_hint_align_set(password_label, 1, EVAS_HINT_FILL);
1598     elm_table_pack(table, password_label, 0, 1, 1, 1);
1599     evas_object_show(password_label);
1600
1601     Evas_Object *password_entry = elm_entry_add(auth_popup);
1602     auth_data->password_entry = password_entry;
1603     elm_entry_scrollable_set(password_entry, EINA_TRUE);
1604     elm_entry_single_line_set(password_entry, EINA_TRUE);
1605     elm_entry_password_set(password_entry, EINA_TRUE);
1606     elm_entry_text_style_user_push(password_entry, "DEFAULT='font_size=18'");
1607     evas_object_size_hint_weight_set(password_entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1608     evas_object_size_hint_align_set(password_entry, EVAS_HINT_FILL, EVAS_HINT_FILL);
1609     elm_table_pack(table, password_entry, 1, 1, 2, 1);
1610     evas_object_show(password_entry);
1611
1612     /* Popup buttons */
1613     Evas_Object *cancel_button = elm_button_add(auth_popup);
1614     elm_object_text_set(cancel_button, "Cancel");
1615     elm_object_part_content_set(auth_popup, "button1", cancel_button);
1616     evas_object_smart_callback_add(cancel_button, "clicked", on_auth_cancel, auth_data);
1617     Evas_Object *ok_button = elm_button_add(auth_popup);
1618     elm_object_text_set(ok_button, "OK");
1619     elm_object_part_content_set(auth_popup, "button2", ok_button);
1620     evas_object_smart_callback_add(ok_button, "clicked", on_auth_ok, auth_data);
1621     evas_object_show(auth_popup);
1622 }
1623
1624 static void
1625 on_tooltip_text_set(void *user_data, Evas_Object *obj, void *event_info)
1626 {
1627     Browser_Window *window = (Browser_Window *)user_data;
1628     const char *message = (const char*)event_info;
1629
1630     elm_object_tooltip_text_set(window->elm_window, message);
1631     window->tooltip.text_set = EINA_TRUE;
1632     window_tooltip_update(window);
1633 }
1634
1635 static void
1636 on_tooltip_text_unset(void *user_data, Evas_Object *obj, void *event_info)
1637 {
1638     Browser_Window *window = (Browser_Window *)user_data;
1639
1640     window_tooltip_hide(window);
1641     elm_object_tooltip_unset(window->elm_window);
1642     window->tooltip.text_set = EINA_FALSE;
1643 }
1644
1645 static void
1646 on_home_button_clicked(void *user_data, Evas_Object *home_button, void *event_info)
1647 {
1648     Browser_Window *window = (Browser_Window *)user_data;
1649
1650     ewk_view_url_set(window->ewk_view, DEFAULT_URL);
1651 }
1652
1653 static void
1654 on_window_deletion(void *user_data, Evas_Object *elm_window, void *event_info)
1655 {
1656     window_close(window_find_with_elm_window(elm_window));
1657 }
1658
1659 static Evas_Object *
1660 create_toolbar_button(Evas_Object *elm_window, const char *icon_name)
1661 {
1662     Evas_Object *button = elm_button_add(elm_window);
1663
1664     Evas_Object *icon = elm_icon_add(elm_window);
1665     elm_icon_standard_set(icon, icon_name);
1666     evas_object_size_hint_max_set(icon, TOOL_BAR_ICON_SIZE, TOOL_BAR_ICON_SIZE);
1667     evas_object_color_set(icon, 44, 44, 102, 128);
1668     evas_object_show(icon);
1669     elm_object_part_content_set(button, "icon", icon);
1670     evas_object_size_hint_min_set(button, TOOL_BAR_BUTTON_SIZE, TOOL_BAR_BUTTON_SIZE);
1671
1672     return button;
1673 }
1674
1675 static Browser_Window *window_create(Evas_Object *opener, int width, int height, Eina_Bool view_mode)
1676 {
1677     Browser_Window *window = calloc(1, sizeof(Browser_Window));
1678     if (!window) {
1679         info("ERROR: could not create browser window.");
1680         return NULL;
1681     }
1682
1683     /* Initialize tooltip information */
1684     window->tooltip.show_timer = NULL;
1685     window->tooltip.activated = EINA_FALSE;
1686     window->tooltip.text_set = EINA_FALSE;
1687     window->tooltip.shown = EINA_FALSE;
1688
1689     /* Create window */
1690     window->elm_window = elm_win_add(NULL, "minibrowser-window", ELM_WIN_BASIC);
1691     elm_win_title_set(window->elm_window, APP_NAME);
1692     evas_object_smart_callback_add(window->elm_window, "delete,request", on_window_deletion, &window);
1693
1694     /* Create window background */
1695     Evas_Object *bg = elm_bg_add(window->elm_window);
1696     elm_bg_color_set(bg, 193, 192, 191);
1697     evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1698     elm_win_resize_object_add(window->elm_window, bg);
1699     evas_object_show(bg);
1700
1701     /* Create vertical layout */
1702     Evas_Object *vertical_layout = elm_box_add(window->elm_window);
1703     elm_box_padding_set(vertical_layout, 0, 2);
1704     evas_object_size_hint_weight_set(vertical_layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1705     elm_win_resize_object_add(window->elm_window, vertical_layout);
1706     evas_object_show(vertical_layout);
1707
1708     /* Create horizontal layout for top bar */
1709     Evas_Object *horizontal_layout = elm_box_add(window->elm_window);
1710     elm_box_horizontal_set(horizontal_layout, EINA_TRUE);
1711     evas_object_size_hint_weight_set(horizontal_layout, EVAS_HINT_EXPAND, 0.0);
1712     evas_object_size_hint_align_set(horizontal_layout, EVAS_HINT_FILL, 0.0);
1713     elm_box_pack_end(vertical_layout, horizontal_layout);
1714     evas_object_show(horizontal_layout);
1715
1716     /* Create Back button */
1717     window->back_button = create_toolbar_button(window->elm_window, "arrow_left");
1718     evas_object_smart_callback_add(window->back_button, "clicked", on_back_button_clicked, window);
1719     elm_object_disabled_set(window->back_button, EINA_TRUE);
1720     evas_object_size_hint_weight_set(window->back_button, 0.0, EVAS_HINT_EXPAND);
1721     evas_object_size_hint_align_set(window->back_button, 0.0, 0.5);
1722     elm_box_pack_end(horizontal_layout, window->back_button);
1723     evas_object_show(window->back_button);
1724
1725     /* Create Forward button */
1726     window->forward_button = create_toolbar_button(window->elm_window, "arrow_right");
1727     evas_object_smart_callback_add(window->forward_button, "clicked", on_forward_button_clicked, window);
1728     elm_object_disabled_set(window->forward_button, EINA_TRUE);
1729     evas_object_size_hint_weight_set(window->forward_button, 0.0, EVAS_HINT_EXPAND);
1730     evas_object_size_hint_align_set(window->forward_button, 0.0, 0.5);
1731     elm_box_pack_end(horizontal_layout, window->forward_button);
1732     evas_object_show(window->forward_button);
1733
1734     /* Create URL bar */
1735     window->url_bar = elm_entry_add(window->elm_window);
1736     elm_entry_scrollable_set(window->url_bar, EINA_TRUE);
1737     elm_entry_scrollbar_policy_set(window->url_bar, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_OFF);
1738     elm_entry_single_line_set(window->url_bar, EINA_TRUE);
1739     elm_entry_cnp_mode_set(window->url_bar, ELM_CNP_MODE_PLAINTEXT);
1740     elm_entry_text_style_user_push(window->url_bar, "DEFAULT='font_size=18'");
1741     evas_object_smart_callback_add(window->url_bar, "activated", on_url_bar_activated, window);
1742     evas_object_smart_callback_add(window->url_bar, "clicked", on_url_bar_clicked, window);
1743     evas_object_size_hint_weight_set(window->url_bar, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1744     evas_object_size_hint_align_set(window->url_bar, EVAS_HINT_FILL, EVAS_HINT_FILL);
1745     elm_box_pack_end(horizontal_layout, window->url_bar);
1746     evas_object_show(window->url_bar);
1747
1748     /* Create Refresh button */
1749     Evas_Object *refresh_button = create_toolbar_button(window->elm_window, "refresh");
1750     evas_object_smart_callback_add(refresh_button, "clicked", on_refresh_button_clicked, window);
1751     evas_object_size_hint_weight_set(refresh_button, 0.0, EVAS_HINT_EXPAND);
1752     evas_object_size_hint_align_set(refresh_button, 1.0, 0.5);
1753     elm_box_pack_end(horizontal_layout, refresh_button);
1754     evas_object_show(refresh_button);
1755
1756     /* Create Home button */
1757     Evas_Object *home_button = create_toolbar_button(window->elm_window, "home");
1758     evas_object_smart_callback_add(home_button, "clicked", on_home_button_clicked, window);
1759     evas_object_size_hint_weight_set(home_button, 0.0, EVAS_HINT_EXPAND);
1760     evas_object_size_hint_align_set(home_button, 1.0, 0.5);
1761     elm_box_pack_end(horizontal_layout, home_button);
1762     evas_object_show(home_button);
1763
1764     /* Create Search bar */
1765     window->search.search_bar = elm_box_add(window->elm_window);
1766     elm_box_horizontal_set(window->search.search_bar, EINA_TRUE);
1767     evas_object_size_hint_min_set(window->search.search_bar, SEARCH_FIELD_SIZE + 2 * SEARCH_BUTTON_SIZE, 0);
1768     evas_object_size_hint_align_set(window->search.search_bar, 0.0, EVAS_HINT_FILL);
1769     elm_box_pack_end(vertical_layout, window->search.search_bar);
1770
1771     /* Create Search field */
1772     window->search.search_field = elm_entry_add(window->elm_window);
1773     elm_entry_scrollable_set(window->search.search_field, EINA_TRUE);
1774     elm_entry_scrollbar_policy_set(window->search.search_field, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_OFF);
1775     elm_entry_single_line_set(window->search.search_field, EINA_TRUE);
1776     elm_entry_cnp_mode_set(window->search.search_field, ELM_CNP_MODE_PLAINTEXT);
1777     elm_entry_text_style_user_push(window->search.search_field, "DEFAULT='font_size=14'");
1778     evas_object_smart_callback_add(window->search.search_field, "activated", on_search_field_activated, window);
1779     evas_object_smart_callback_add(window->search.search_field, "changed", on_search_field_activated, window);
1780     evas_object_smart_callback_add(window->search.search_field, "aborted", on_search_field_aborted, window);
1781     evas_object_smart_callback_add(window->search.search_field, "clicked", on_search_field_clicked, window);
1782     evas_object_size_hint_weight_set(window->search.search_field, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1783     evas_object_size_hint_align_set(window->search.search_field, EVAS_HINT_FILL, EVAS_HINT_FILL);
1784     elm_box_pack_end(window->search.search_bar, window->search.search_field);
1785
1786     /* Create Search backward button */
1787     window->search.backward_button = create_toolbar_button(window->elm_window, "arrow_up");
1788     evas_object_smart_callback_add(window->search.backward_button, "clicked", on_search_backward_button_clicked, window);
1789     elm_object_disabled_set(window->search.backward_button, EINA_FALSE);
1790     evas_object_size_hint_weight_set(window->search.backward_button, 0.0, EVAS_HINT_EXPAND);
1791     evas_object_size_hint_align_set(window->search.backward_button, 1.0, 0.5);
1792     evas_object_size_hint_min_set(window->search.backward_button, SEARCH_BUTTON_SIZE, SEARCH_BUTTON_SIZE);
1793     elm_box_pack_end(window->search.search_bar, window->search.backward_button);
1794
1795     /* Create Search forwardward button */
1796     window->search.forward_button = create_toolbar_button(window->elm_window, "arrow_down");
1797     evas_object_smart_callback_add(window->search.forward_button, "clicked", on_search_forward_button_clicked, window);
1798     elm_object_disabled_set(window->search.forward_button, EINA_FALSE);
1799     evas_object_size_hint_weight_set(window->search.forward_button, 0.0, EVAS_HINT_EXPAND);
1800     evas_object_size_hint_align_set(window->search.forward_button, 1.0, 0.5);
1801     evas_object_size_hint_min_set(window->search.forward_button, SEARCH_BUTTON_SIZE, SEARCH_BUTTON_SIZE);
1802     elm_box_pack_end(window->search.search_bar, window->search.forward_button);
1803
1804     /* Create ewk_view */
1805     Ewk_View_Smart_Class *ewkViewClass = miniBrowserViewSmartClass();
1806     ewkViewClass->run_javascript_alert = on_javascript_alert;
1807     ewkViewClass->run_javascript_confirm = on_javascript_confirm;
1808     ewkViewClass->run_javascript_prompt = on_javascript_prompt;
1809     ewkViewClass->window_geometry_get = on_window_geometry_get;
1810     ewkViewClass->window_geometry_set = on_window_geometry_set;
1811     ewkViewClass->fullscreen_enter = on_fullscreen_enter;
1812     ewkViewClass->fullscreen_exit = on_fullscreen_exit;
1813     ewkViewClass->window_create = on_window_create;
1814     ewkViewClass->window_close = on_window_close;
1815     ewkViewClass->popup_menu_show = on_popup_menu_show;
1816     ewkViewClass->popup_menu_hide = on_popup_menu_hide;
1817     ewkViewClass->context_menu_show = on_context_menu_show;
1818     ewkViewClass->context_menu_hide = on_context_menu_hide;
1819     ewkViewClass->input_picker_color_request = on_color_picker_request;
1820     ewkViewClass->input_picker_color_dismiss = on_color_picker_dismiss;
1821
1822     Evas *evas = evas_object_evas_get(window->elm_window);
1823     Evas_Smart *smart = evas_smart_class_new(&ewkViewClass->sc);
1824     Ewk_Context *context = opener ? ewk_view_context_get(opener) : ewk_context_default_get();
1825     Ewk_Page_Group *pageGroup = opener ? ewk_view_page_group_get(opener) : ewk_page_group_create("");
1826     window->ewk_view = ewk_view_smart_add(evas, smart, context, pageGroup);
1827
1828     ewk_view_theme_set(window->ewk_view, TEST_THEME_DIR "/default.edj");
1829     if (device_pixel_ratio)
1830         ewk_view_device_pixel_ratio_set(window->ewk_view, (float)device_pixel_ratio);
1831     ewk_view_source_mode_set(window->ewk_view, view_mode);
1832     ewk_view_user_agent_set(window->ewk_view, user_agent_string);
1833     ewk_view_layout_fixed_set(window->ewk_view, fixed_layout_enabled);
1834
1835     if (touch_events_enabled) {
1836         ewk_view_touch_events_enabled_set(window->ewk_view, EINA_TRUE);
1837         ewk_view_mouse_events_enabled_set(window->ewk_view, EINA_FALSE);
1838     }
1839
1840     /* Set the zoom level to default */
1841     window->current_zoom_level = DEFAULT_ZOOM_LEVEL;
1842
1843     Ewk_Settings *settings = ewk_view_settings_get(window->ewk_view);
1844     ewk_settings_file_access_from_file_urls_allowed_set(settings, EINA_TRUE);
1845     ewk_settings_encoding_detector_enabled_set(settings, encoding_detector_enabled);
1846     ewk_settings_frame_flattening_enabled_set(settings, frame_flattening_enabled);
1847     ewk_settings_local_storage_enabled_set(settings, local_storage_enabled);
1848     info("HTML5 local storage is %s for this view.", local_storage_enabled ? "enabled" : "disabled");
1849     elm_win_fullscreen_set(window->elm_window, fullscreen_enabled);
1850     ewk_settings_developer_extras_enabled_set(settings, EINA_TRUE);
1851     ewk_settings_preferred_minimum_contents_width_set(settings, 0);
1852     ewk_text_checker_continuous_spell_checking_enabled_set(spell_checking_enabled);
1853
1854     evas_object_smart_callback_add(window->ewk_view, "authentication,request", on_authentication_request, window);
1855     evas_object_smart_callback_add(window->ewk_view, "download,failed", on_download_failed, window);
1856     evas_object_smart_callback_add(window->ewk_view, "download,finished", on_download_finished, window);
1857     evas_object_smart_callback_add(window->ewk_view, "download,request", on_download_request, window);
1858     evas_object_smart_callback_add(window->ewk_view, "file,chooser,request", on_file_chooser_request, window);
1859     evas_object_smart_callback_add(window->ewk_view, "favicon,changed", on_view_favicon_changed, window);
1860     evas_object_smart_callback_add(window->ewk_view, "load,error", on_error, window);
1861     evas_object_smart_callback_add(window->ewk_view, "load,provisional,failed", on_error, window);
1862     evas_object_smart_callback_add(window->ewk_view, "load,progress", on_progress, window);
1863     evas_object_smart_callback_add(window->ewk_view, "title,changed", on_title_changed, window);
1864     evas_object_smart_callback_add(window->ewk_view, "url,changed", on_url_changed, window);
1865     evas_object_smart_callback_add(window->ewk_view, "back,forward,list,changed", on_back_forward_list_changed, window);
1866     evas_object_smart_callback_add(window->ewk_view, "tooltip,text,set", on_tooltip_text_set, window);
1867     evas_object_smart_callback_add(window->ewk_view, "tooltip,text,unset", on_tooltip_text_unset, window);
1868
1869     evas_object_event_callback_add(window->ewk_view, EVAS_CALLBACK_KEY_DOWN, on_key_down, window);
1870     evas_object_event_callback_add(window->ewk_view, EVAS_CALLBACK_MOUSE_DOWN, on_mouse_down, window);
1871
1872     evas_object_size_hint_weight_set(window->ewk_view, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1873     evas_object_size_hint_align_set(window->ewk_view, EVAS_HINT_FILL, EVAS_HINT_FILL);
1874     elm_box_pack_before(vertical_layout, window->ewk_view, window->search.search_bar);
1875     evas_object_show(window->ewk_view);
1876
1877     evas_object_resize(window->elm_window, width ? width : window_width, height ? height : window_height);
1878     evas_object_show(window->elm_window);
1879     search_box_hide(window);
1880
1881     view_focus_set(window, EINA_TRUE);
1882
1883     evas_object_event_callback_add(window->ewk_view, EVAS_CALLBACK_MOUSE_IN, on_mouse_in, window);
1884     evas_object_event_callback_add(window->ewk_view, EVAS_CALLBACK_MOUSE_OUT, on_mouse_out, window);
1885     evas_object_event_callback_add(window->ewk_view, EVAS_CALLBACK_MOUSE_MOVE, on_mouse_move, window);
1886     evas_object_event_callback_add(window->elm_window, EVAS_CALLBACK_RESIZE, on_window_resize, window);
1887
1888     return window;
1889 }
1890
1891 static Ewk_Cookie_Accept_Policy
1892 parse_cookies_policy(const char *input_string)
1893 {
1894     if (!strcmp(input_string, "always")) 
1895         return EWK_COOKIE_ACCEPT_POLICY_ALWAYS;
1896     if (!strcmp(input_string, "never"))
1897         return EWK_COOKIE_ACCEPT_POLICY_NEVER;
1898     if (strcmp(input_string, "no-third-party"))
1899         info("Unrecognized type for cookies policy: %s.", input_string);
1900     return EWK_COOKIE_ACCEPT_POLICY_NO_THIRD_PARTY;
1901 }
1902
1903 static void
1904 parse_window_size(const char *input_string, int *width, int *height)
1905 {
1906     static const unsigned max_length = 4;
1907     int parsed_width;
1908     int parsed_height;
1909     char **arr;
1910     unsigned elements;
1911
1912     arr = eina_str_split_full(input_string, "x", 0, &elements);
1913
1914     if (elements == 2 && (strlen(arr[0]) <= max_length) && (strlen(arr[1]) <= max_length)) {
1915         parsed_width = atoi(arr[0]);
1916         if (width && parsed_width)
1917             *width = parsed_width;
1918
1919         parsed_height = atoi(arr[1]);
1920         if (height && parsed_height)
1921             *height = parsed_height;
1922     }
1923
1924     free(arr[0]);
1925     free(arr);
1926 }
1927
1928 EAPI_MAIN int
1929 elm_main(int argc, char *argv[])
1930 {
1931     int args = 1;
1932     unsigned char quitOption = 0;
1933     Browser_Window *window;
1934     char *window_size_string = NULL;
1935     char *cookies_policy_string = NULL;
1936
1937     Ecore_Getopt_Value values[] = {
1938         ECORE_GETOPT_VALUE_STR(evas_engine_name),
1939         ECORE_GETOPT_VALUE_STR(window_size_string),
1940         ECORE_GETOPT_VALUE_STR(user_agent_string),
1941         ECORE_GETOPT_VALUE_DOUBLE(device_pixel_ratio),
1942         ECORE_GETOPT_VALUE_BOOL(quitOption),
1943         ECORE_GETOPT_VALUE_BOOL(encoding_detector_enabled),
1944         ECORE_GETOPT_VALUE_BOOL(frame_flattening_enabled),
1945         ECORE_GETOPT_VALUE_BOOL(local_storage_enabled),
1946         ECORE_GETOPT_VALUE_BOOL(fullscreen_enabled),
1947         ECORE_GETOPT_VALUE_BOOL(spell_checking_enabled),
1948         ECORE_GETOPT_VALUE_BOOL(touch_events_enabled),
1949         ECORE_GETOPT_VALUE_BOOL(fixed_layout_enabled),
1950         ECORE_GETOPT_VALUE_STR(cookies_policy_string),
1951         ECORE_GETOPT_VALUE_BOOL(quitOption),
1952         ECORE_GETOPT_VALUE_BOOL(quitOption),
1953         ECORE_GETOPT_VALUE_BOOL(quitOption),
1954         ECORE_GETOPT_VALUE_NONE
1955     };
1956
1957     if (!ewk_init())
1958         return EXIT_FAILURE;
1959
1960     ewk_view_smart_class_set(miniBrowserViewSmartClass());
1961
1962     ecore_app_args_set(argc, (const char **) argv);
1963     args = ecore_getopt_parse(&options, values, argc, argv);
1964
1965     if (args < 0)
1966         return quit(EINA_FALSE, "ERROR: could not parse options.\n");
1967
1968     if (quitOption)
1969         return quit(EINA_TRUE, NULL);
1970
1971     if (evas_engine_name)
1972         elm_config_preferred_engine_set(evas_engine_name);
1973 #if defined(HAVE_ECORE_X)
1974     else {
1975         evas_engine_name = "opengl_x11";
1976         elm_config_preferred_engine_set(evas_engine_name);
1977     }
1978 #endif
1979
1980     // Enable favicon database.
1981     Ewk_Context *context = ewk_context_default_get();
1982     ewk_context_favicon_database_directory_set(context, NULL);
1983
1984     if (cookies_policy_string)
1985         ewk_cookie_manager_accept_policy_set(ewk_context_cookie_manager_get(context), parse_cookies_policy(cookies_policy_string));
1986
1987     if (window_size_string)
1988         parse_window_size(window_size_string, &window_width, &window_height);
1989
1990     if (args < argc) {
1991         char *url = url_from_user_input(argv[args]);
1992         window = window_create(NULL, 0, 0, EINA_FALSE);
1993         ewk_view_url_set(window->ewk_view, url);
1994         free(url);
1995     } else {
1996         window = window_create(NULL, 0, 0, EINA_FALSE);
1997         ewk_view_url_set(window->ewk_view, DEFAULT_URL);
1998     }
1999
2000     if (!window)
2001         return quit(EINA_FALSE, "ERROR: could not create browser window.\n");
2002
2003     windows = eina_list_append(windows, window);
2004
2005     elm_run();
2006
2007     return quit(EINA_TRUE, NULL);
2008 }
2009 ELM_MAIN()
2010