[WebView][TCSACR-219] Add Context Menu API (#674)
[platform/core/csapi/tizenfx.git] / src / Tizen.WebView / Tizen.WebView / WebView.cs
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 using ElmSharp;
18 using System;
19 using System.Collections.Generic;
20 using System.Runtime.InteropServices;
21
22 namespace Tizen.WebView
23 {
24     /// <summary>
25     /// A view used to render the web contents.
26     /// </summary>
27     /// <since_tizen> 4 </since_tizen>
28     public class WebView : EvasObject
29     {
30         private static IDictionary<string, JavaScriptMessageHandler> _javaScriptMessageHandlerMap = new Dictionary<string, JavaScriptMessageHandler>();
31
32         private IntPtr _handle;
33         private IntPtr _realHandle;
34         private Context _context;
35         private Settings _settings;
36
37         // focus dummy
38         private SmartEvent _focusIn;
39         private SmartEvent _focusOut;
40
41         // Smart events
42         private SmartEvent _loadStarted;
43         private SmartEvent _loadFinished;
44         private SmartEvent<SmartCallbackLoadErrorArgs> _loadError;
45         private SmartEvent<SmartCallbackArgs> _titleChanged;
46         private SmartEvent<SmartCallbackArgs> _urlChanged;
47
48         private SmartEvent<ContextMenuItemEventArgs> _contextMenuItemSelected;
49         private SmartEvent<ContextMenuCustomizeEventArgs> _contextMenuCustomize;
50
51         private ContextMenuCustomize _contextMenuCustomizeDelegate;
52
53         /// <summary>
54         /// Event that occurs when the load is started.
55         /// </summary>
56         /// <since_tizen> 4 </since_tizen>
57         public event EventHandler LoadStarted;
58
59         /// <summary>
60         /// Event that occurs when the load is finished.
61         /// </summary>
62         /// <since_tizen> 4 </since_tizen>
63         public event EventHandler LoadFinished;
64
65         /// <summary>
66         /// Event that occurs when the load throws an error.
67         /// </summary>
68         /// <since_tizen> 4 </since_tizen>
69         public event EventHandler<SmartCallbackLoadErrorArgs> LoadError;
70
71         /// <summary>
72         /// Event that occurs when the title of the main frame is changed.
73         /// </summary>
74         /// <since_tizen> 4 </since_tizen>
75         public event EventHandler<SmartCallbackArgs> TitleChanged;
76
77         /// <summary>
78         /// Event that occurs when the URL of the main frame is changed.
79         /// </summary>
80         /// <since_tizen> 4 </since_tizen>
81         public event EventHandler<SmartCallbackArgs> UrlChanged;
82
83         /// <summary>
84         /// The delegate is invoked when context menu customization is needed.
85         /// </summary>
86         /// <param name="menu">The instance of ContextMenu.</param>
87         /// <since_tizen> 6 </since_tizen>
88         public delegate void ContextMenuCustomize(ContextMenu menu);
89
90         /// <summary>
91         /// Event that occurs when the context menu item selected.
92         /// </summary>
93         /// <since_tizen> 6 </since_tizen>
94         public event EventHandler<ContextMenuItemEventArgs> ContextMenuItemSelected;
95
96         /// <summary>
97         /// Current URL of the main frame.
98         /// </summary>
99         /// <since_tizen> 4 </since_tizen>
100         public string Url
101         {
102             get
103             {
104                 return Interop.ChromiumEwk.ewk_view_url_get(_realHandle);
105             }
106         }
107
108         /// <summary>
109         /// Current title of the main frame.
110         /// </summary>
111         /// <since_tizen> 4 </since_tizen>
112         public string Title
113         {
114             get
115             {
116                 return Interop.ChromiumEwk.ewk_view_title_get(_realHandle);
117             }
118         }
119
120         /// <summary>
121         /// Current user agent string of this view.
122         /// </summary>
123         /// <since_tizen> 4 </since_tizen>
124         public string UserAgent
125         {
126             get
127             {
128                 return Interop.ChromiumEwk.ewk_view_user_agent_get(_realHandle);
129             }
130
131             set
132             {
133                 Interop.ChromiumEwk.ewk_view_user_agent_set(_realHandle, value);
134             }
135         }
136
137         /// <summary>
138         /// Whether a view has the focus.
139         /// </summary>
140         /// <since_tizen> 4 </since_tizen>
141         public bool HasFocus
142         {
143             get
144             {
145                 return Interop.ChromiumEwk.ewk_view_focus_get(_realHandle);
146             }
147         }
148
149         /// <summary>
150         /// Creates a WebView object.
151         /// </summary>
152         /// <param name="parent">Parent object of the WebView.</param>
153         /// <since_tizen> 4 </since_tizen>
154         public WebView(EvasObject parent) : base(parent)
155         {
156             InitializeSmartEvent();
157         }
158
159         /// <summary>
160         /// Gets the context object of this view.
161         /// </summary>
162         /// <returns>The context object of this view.</returns>
163         /// <since_tizen> 4 </since_tizen>
164         public Context GetContext()
165         {
166             if (_context == null)
167             {
168                 IntPtr contextHandle = Interop.ChromiumEwk.ewk_view_context_get(_realHandle);
169                 if (contextHandle == IntPtr.Zero)
170                 {
171                     return null;
172                 }
173                 _context = new Context(contextHandle);
174             }
175             return _context;
176         }
177
178         /// <summary>
179         /// Gets the settings object of this view.
180         /// </summary>
181         /// <returns>The settings object of this view.</returns>
182         /// <since_tizen> 4 </since_tizen>
183         public Settings GetSettings()
184         {
185             if (_settings == null)
186             {
187                 IntPtr settingsHandle = Interop.ChromiumEwk.ewk_view_settings_get(_realHandle);
188                 if (settingsHandle == IntPtr.Zero)
189                 {
190                     return null;
191                 }
192                 _settings = new Settings(settingsHandle);
193             }
194             return _settings;
195         }
196
197         /// <summary>
198         /// Gets the back/forward list object of this view.
199         /// </summary>
200         /// <returns>The BackForward List object of this view.</returns>
201         /// <since_tizen> 6 </since_tizen>
202         public BackForwardList GetBackForwardList()
203         {
204             IntPtr backforwardlistHandle = Interop.ChromiumEwk.ewk_view_back_forward_list_get(_realHandle);
205             if (backforwardlistHandle == IntPtr.Zero)
206             {
207                 return null;
208             }
209             return new BackForwardList(backforwardlistHandle);
210         }
211
212         /// <summary>
213         /// Clear the back/forward list object of this view.
214         /// </summary>
215         /// <since_tizen> 6 </since_tizen>
216         public void ClearBackForwardList()
217         {
218             Interop.ChromiumEwk.ewk_view_back_forward_list_clear(_realHandle);
219         }
220
221         /// <summary>
222         /// Asks the object to load the given URL.
223         /// </summary>
224         /// <remarks>
225         /// You can only be sure that the URL changes after UrlChanged event.
226         /// </remarks>
227         /// <param name="url">The uniform resource identifier to load.</param>
228         /// <since_tizen> 4 </since_tizen>
229         public void LoadUrl(string url)
230         {
231             Interop.ChromiumEwk.ewk_view_url_set(_realHandle, url);
232         }
233
234         /// <summary>
235         /// Loads the specified HTML string as the content of the view.
236         /// </summary>
237         /// <param name="html">HTML data to load.</param>
238         /// <param name="baseUrl">Base URL used for relative paths to external objects.</param>
239         /// <since_tizen> 4 </since_tizen>
240         public void LoadHtml(string html, string baseUrl)
241         {
242             Interop.ChromiumEwk.ewk_view_html_string_load(_realHandle, html, baseUrl, null);
243         }
244
245         /// <summary>
246         /// Asks the main frame to stop loading.
247         /// </summary>
248         /// <since_tizen> 4 </since_tizen>
249         public void StopLoading()
250         {
251             Interop.ChromiumEwk.ewk_view_stop(_realHandle);
252         }
253
254         /// <summary>
255         /// Asks the main frame to reload the current document.
256         /// </summary>
257         /// <since_tizen> 4 </since_tizen>
258         public void Reload()
259         {
260             Interop.ChromiumEwk.ewk_view_reload(_realHandle);
261         }
262
263         /// <summary>
264         /// Asks the main frame to navigate back in history.
265         /// </summary>
266         /// <since_tizen> 4 </since_tizen>
267         public void GoBack()
268         {
269             Interop.ChromiumEwk.ewk_view_back(_realHandle);
270         }
271
272         /// <summary>
273         /// Asks the main frame to navigate forward in history.
274         /// </summary>
275         /// <since_tizen> 4 </since_tizen>
276         public void GoForward()
277         {
278             Interop.ChromiumEwk.ewk_view_forward(_realHandle);
279         }
280
281         /// <summary>
282         /// Checks whether it is possible to navigate backward one item in history.
283         /// </summary>
284         /// <returns>Whether it is possible to navigate backward one item in history.</returns>
285         /// <since_tizen> 4 </since_tizen>
286         public bool CanGoBack()
287         {
288             return Interop.ChromiumEwk.ewk_view_back_possible(_realHandle);
289         }
290
291         /// <summary>
292         /// Checks whether it is possible to navigate forward one item in history.
293         /// </summary>
294         /// <returns>Whether it is possible to navigate forward one item in history.</returns>
295         /// <since_tizen> 4 </since_tizen>
296         public bool CanGoForward()
297         {
298             return Interop.ChromiumEwk.ewk_view_forward_possible(_realHandle);
299         }
300
301         /// <summary>
302         /// Injects the supplied javascript message handler into the view.
303         /// </summary>
304         /// <param name="name"> The message callback.</param>
305         /// <param name="handler">The name used to expose the object in JavaScript.</param>
306         /// <returns>'true' on success, otherwise 'false'.</returns>
307         /// <since_tizen> 4 </since_tizen>
308         public bool AddJavaScriptMessageHandler(string name, JavaScriptMessageHandler handler)
309         {
310             lock (_javaScriptMessageHandlerMap)
311             {
312                 if (_javaScriptMessageHandlerMap.ContainsKey(name))
313                 {
314                     return false;
315                 }
316                 _javaScriptMessageHandlerMap[name] = handler;
317             }
318             Interop.ChromiumEwk.ScriptMessageCallback callback = (handle, message) =>
319             {
320                 JavaScriptMessage convertedMessage = new JavaScriptMessage(message);
321                 lock (_javaScriptMessageHandlerMap)
322                 {
323                     if (_javaScriptMessageHandlerMap.ContainsKey(convertedMessage.Name))
324                     {
325                         _javaScriptMessageHandlerMap[convertedMessage.Name](convertedMessage);
326                     }
327                 }
328             };
329             if (!Interop.ChromiumEwk.ewk_view_javascript_message_handler_add(_realHandle, callback, name))
330             {
331                 lock (_javaScriptMessageHandlerMap)
332                 {
333                     _javaScriptMessageHandlerMap.Remove(name);
334                     return false;
335                 }
336             }
337             return true;
338         }
339
340         /// <summary>
341         /// Requests the execution of a given name and the result to the JavaScript runtime.
342         /// </summary>
343         /// <param name="name">The name used to expose the object in JavaScript.</param>
344         /// <param name="result">The result to the JavaScript runtime.</param>
345         /// <since_tizen> 4 </since_tizen>
346         public void EvalWithResult(string name, string result)
347         {
348             Interop.ChromiumEwk.ewk_view_evaluate_javascript(_realHandle, name, result);
349         }
350
351         /// <summary>
352         /// Requests the execution of the given script.
353         /// </summary>
354         /// <param name="script">The JavaScript code string to execute.</param>
355         /// <since_tizen> 4 </since_tizen>
356         public void Eval(string script)
357         {
358             Interop.ChromiumEwk.ewk_view_script_execute(_realHandle, script, null, IntPtr.Zero);
359         }
360
361         /// <summary>
362         /// Requests to set or unset a view as the currently focused one.
363         /// </summary>
364         /// <param name="focused">'true' to set the focus on the view, 'false' to remove the focus from the view.</param>
365         /// <since_tizen> 4 </since_tizen>
366         public void SetFocus(bool focused)
367         {
368             Interop.ChromiumEwk.ewk_view_focus_set(_realHandle, focused);
369         }
370
371         /// <summary>
372         /// Creates a widget handle.
373         /// </summary>
374         /// <param name="parent">Parent EvasObject.</param>
375         /// <returns>IntPtr of the widget handle.</returns>
376         /// <since_tizen> 4 </since_tizen>
377         protected override IntPtr CreateHandle(EvasObject parent)
378         {
379             // focus dummy
380             _handle = Interop.Elementary.elm_layout_add((IntPtr)parent);
381             Interop.Elementary.elm_layout_theme_set(_handle, "layout", "elm_widget", "default");
382             Interop.Elementary.elm_object_focus_allow_set(_handle, true);
383
384             IntPtr evas = Interop.Evas.evas_object_evas_get(parent);
385             _realHandle = Interop.ChromiumEwk.ewk_view_add(evas);
386             Interop.Elementary.elm_object_part_content_set(_handle, "elm.swallow.content", _realHandle);
387
388             return _handle;
389         }
390
391         /// <summary>
392         /// Sets the delegate for context menu customization.
393         /// </summary>
394         /// <param name="contextMenuCustomizeDelegate">The delegate for context menu customization.</param>
395         /// <since_tizen> 6 </since_tizen>
396         public void SetContextMenuCustomizeDelegate(ContextMenuCustomize contextMenuCustomizeDelegate)
397         {
398             _contextMenuCustomizeDelegate = contextMenuCustomizeDelegate;
399         }
400
401         private void InitializeSmartEvent()
402         {
403             // focus dummy
404             _focusIn = new SmartEvent(this, "focused");
405             _focusOut = new SmartEvent(this, "unfocused");
406
407             _focusIn.On += (s, e) => { ((WebView)s).SetFocus(true); };
408             _focusOut.On += (s, e) => { ((WebView)s).SetFocus(false); };
409
410             _loadStarted = new SmartEvent(this, _realHandle, "load,started");
411             _loadFinished = new SmartEvent(this, _realHandle, "load,finished");
412             _loadError = new SmartEvent<SmartCallbackLoadErrorArgs>(this, _realHandle, "load,error", SmartCallbackLoadErrorArgs.CreateFromSmartEvent);
413             _titleChanged = new SmartEvent<SmartCallbackArgs>(this, _realHandle, "title,changed", SmartCallbackArgs.CreateFromSmartEvent);
414             _urlChanged = new SmartEvent<SmartCallbackArgs>(this, _realHandle, "url,changed", SmartCallbackArgs.CreateFromSmartEvent);
415             _contextMenuCustomize = new SmartEvent<ContextMenuCustomizeEventArgs>(this, _realHandle, "contextmenu,customize", ContextMenuCustomizeEventArgs.CreateFromSmartEvent);
416             _contextMenuItemSelected = new SmartEvent<ContextMenuItemEventArgs>(this, _realHandle, "contextmenu,selected", ContextMenuItemEventArgs.CreateFromSmartEvent);
417
418             _loadStarted.On += (s, e) => { LoadStarted?.Invoke(this, EventArgs.Empty); };
419             _loadFinished.On += (s, e) => { LoadFinished?.Invoke(this, EventArgs.Empty); };
420             _loadError.On += (s, e) => { LoadError?.Invoke(this, e); };
421             _titleChanged.On += (s, e) => { TitleChanged?.Invoke(this, e); };
422             _urlChanged.On += (s, e) => { UrlChanged?.Invoke(this, e); };
423
424             _contextMenuItemSelected.On += (s, e) => { ContextMenuItemSelected?.Invoke(this, e); };
425             _contextMenuCustomize.On += (s, e) => { _contextMenuCustomizeDelegate?.Invoke(e.Menu); };
426         }
427         
428     }
429 }