[WebView][TCSACR-219] Add Policy Decision APIs (#689)
[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         private SmartEvent<NavigationPolicyEventArgs> _policyNavigationDecide;
48         private SmartEvent<NewWindowPolicyEventArgs> _policyNewWindowDecide;
49         private SmartEvent<ResponsePolicyEventArgs> _policyResponseDecide;
50
51         private SmartEvent<ContextMenuItemEventArgs> _contextMenuItemSelected;
52         private SmartEvent<ContextMenuCustomizeEventArgs> _contextMenuCustomize;
53
54         private ContextMenuCustomize _contextMenuCustomizeDelegate;
55
56         /// <summary>
57         /// Event that occurs when the load is started.
58         /// </summary>
59         /// <since_tizen> 4 </since_tizen>
60         public event EventHandler LoadStarted;
61
62         /// <summary>
63         /// Event that occurs when the load is finished.
64         /// </summary>
65         /// <since_tizen> 4 </since_tizen>
66         public event EventHandler LoadFinished;
67
68         /// <summary>
69         /// Event that occurs when the load throws an error.
70         /// </summary>
71         /// <since_tizen> 4 </since_tizen>
72         public event EventHandler<SmartCallbackLoadErrorArgs> LoadError;
73
74         /// <summary>
75         /// Event that occurs when the title of the main frame is changed.
76         /// </summary>
77         /// <since_tizen> 4 </since_tizen>
78         public event EventHandler<SmartCallbackArgs> TitleChanged;
79
80         /// <summary>
81         /// Event that occurs when the URL of the main frame is changed.
82         /// </summary>
83         /// <since_tizen> 4 </since_tizen>
84         public event EventHandler<SmartCallbackArgs> UrlChanged;
85
86         /// <summary>
87
88         /// Event that occurs when the policy navigation is decided.
89         /// </summary>
90         /// <since_tizen> 6 </since_tizen>
91         public event EventHandler<NavigationPolicyEventArgs> NavigationPolicyDecideRequested;
92
93         /// <summary>
94         /// Event that occurs when the policy new window is decided.
95         /// </summary>
96         /// <since_tizen> 6 </since_tizen>
97         public event EventHandler<NewWindowPolicyEventArgs> NewWindowPolicyDecideRequested;
98
99         /// <summary>
100         /// Event that occurs when the policy response is decided.
101         /// </summary>
102         /// <since_tizen> 6 </since_tizen>
103         public event EventHandler<ResponsePolicyEventArgs> ResponsePolicyDecideRequested;
104
105         /// <summary>
106         /// Event that occurs when the context menu item selected.
107         /// </summary>
108         /// <since_tizen> 6 </since_tizen>
109         public event EventHandler<ContextMenuItemEventArgs> ContextMenuItemSelected;
110         
111         /// The delegate is invoked when context menu customization is needed.
112         /// </summary>
113         /// <param name="menu">The instance of ContextMenu.</param>
114         /// <since_tizen> 6 </since_tizen>
115         public delegate void ContextMenuCustomize(ContextMenu menu);
116
117
118         /// <summary>
119         /// Current URL of the main frame.
120         /// </summary>
121         /// <since_tizen> 4 </since_tizen>
122         public string Url
123         {
124             get
125             {
126                 return Interop.ChromiumEwk.ewk_view_url_get(_realHandle);
127             }
128         }
129
130         /// <summary>
131         /// Current title of the main frame.
132         /// </summary>
133         /// <since_tizen> 4 </since_tizen>
134         public string Title
135         {
136             get
137             {
138                 return Interop.ChromiumEwk.ewk_view_title_get(_realHandle);
139             }
140         }
141
142         /// <summary>
143         /// Current user agent string of this view.
144         /// </summary>
145         /// <since_tizen> 4 </since_tizen>
146         public string UserAgent
147         {
148             get
149             {
150                 return Interop.ChromiumEwk.ewk_view_user_agent_get(_realHandle);
151             }
152
153             set
154             {
155                 Interop.ChromiumEwk.ewk_view_user_agent_set(_realHandle, value);
156             }
157         }
158
159         /// <summary>
160         /// Whether a view has the focus.
161         /// </summary>
162         /// <since_tizen> 4 </since_tizen>
163         public bool HasFocus
164         {
165             get
166             {
167                 return Interop.ChromiumEwk.ewk_view_focus_get(_realHandle);
168             }
169         }
170
171         /// <summary>
172         /// Creates a WebView object.
173         /// </summary>
174         /// <param name="parent">Parent object of the WebView.</param>
175         /// <since_tizen> 4 </since_tizen>
176         public WebView(EvasObject parent) : base(parent)
177         {
178             InitializeSmartEvent();
179         }
180
181         /// <summary>
182         /// Gets the context object of this view.
183         /// </summary>
184         /// <returns>The context object of this view.</returns>
185         /// <since_tizen> 4 </since_tizen>
186         public Context GetContext()
187         {
188             if (_context == null)
189             {
190                 IntPtr contextHandle = Interop.ChromiumEwk.ewk_view_context_get(_realHandle);
191                 if (contextHandle == IntPtr.Zero)
192                 {
193                     return null;
194                 }
195                 _context = new Context(contextHandle);
196             }
197             return _context;
198         }
199
200         /// <summary>
201         /// Gets the settings object of this view.
202         /// </summary>
203         /// <returns>The settings object of this view.</returns>
204         /// <since_tizen> 4 </since_tizen>
205         public Settings GetSettings()
206         {
207             if (_settings == null)
208             {
209                 IntPtr settingsHandle = Interop.ChromiumEwk.ewk_view_settings_get(_realHandle);
210                 if (settingsHandle == IntPtr.Zero)
211                 {
212                     return null;
213                 }
214                 _settings = new Settings(settingsHandle);
215             }
216             return _settings;
217         }
218
219         /// <summary>
220         /// Gets the back/forward list object of this view.
221         /// </summary>
222         /// <returns>The BackForward List object of this view.</returns>
223         /// <since_tizen> 6 </since_tizen>
224         public BackForwardList GetBackForwardList()
225         {
226             IntPtr backforwardlistHandle = Interop.ChromiumEwk.ewk_view_back_forward_list_get(_realHandle);
227             if (backforwardlistHandle == IntPtr.Zero)
228             {
229                 return null;
230             }
231             return new BackForwardList(backforwardlistHandle);
232         }
233
234         /// <summary>
235         /// Clear the back/forward list object of this view.
236         /// </summary>
237         /// <since_tizen> 6 </since_tizen>
238         public void ClearBackForwardList()
239         {
240             Interop.ChromiumEwk.ewk_view_back_forward_list_clear(_realHandle);
241         }
242
243         /// <summary>
244         /// Asks the object to load the given URL.
245         /// </summary>
246         /// <remarks>
247         /// You can only be sure that the URL changes after UrlChanged event.
248         /// </remarks>
249         /// <param name="url">The uniform resource identifier to load.</param>
250         /// <since_tizen> 4 </since_tizen>
251         public void LoadUrl(string url)
252         {
253             Interop.ChromiumEwk.ewk_view_url_set(_realHandle, url);
254         }
255
256         /// <summary>
257         /// Loads the specified HTML string as the content of the view.
258         /// </summary>
259         /// <param name="html">HTML data to load.</param>
260         /// <param name="baseUrl">Base URL used for relative paths to external objects.</param>
261         /// <since_tizen> 4 </since_tizen>
262         public void LoadHtml(string html, string baseUrl)
263         {
264             Interop.ChromiumEwk.ewk_view_html_string_load(_realHandle, html, baseUrl, null);
265         }
266
267         /// <summary>
268         /// Asks the main frame to stop loading.
269         /// </summary>
270         /// <since_tizen> 4 </since_tizen>
271         public void StopLoading()
272         {
273             Interop.ChromiumEwk.ewk_view_stop(_realHandle);
274         }
275
276         /// <summary>
277         /// Asks the main frame to reload the current document.
278         /// </summary>
279         /// <since_tizen> 4 </since_tizen>
280         public void Reload()
281         {
282             Interop.ChromiumEwk.ewk_view_reload(_realHandle);
283         }
284
285         /// <summary>
286         /// Asks the main frame to navigate back in history.
287         /// </summary>
288         /// <since_tizen> 4 </since_tizen>
289         public void GoBack()
290         {
291             Interop.ChromiumEwk.ewk_view_back(_realHandle);
292         }
293
294         /// <summary>
295         /// Asks the main frame to navigate forward in history.
296         /// </summary>
297         /// <since_tizen> 4 </since_tizen>
298         public void GoForward()
299         {
300             Interop.ChromiumEwk.ewk_view_forward(_realHandle);
301         }
302
303         /// <summary>
304         /// Checks whether it is possible to navigate backward one item in history.
305         /// </summary>
306         /// <returns>Whether it is possible to navigate backward one item in history.</returns>
307         /// <since_tizen> 4 </since_tizen>
308         public bool CanGoBack()
309         {
310             return Interop.ChromiumEwk.ewk_view_back_possible(_realHandle);
311         }
312
313         /// <summary>
314         /// Checks whether it is possible to navigate forward one item in history.
315         /// </summary>
316         /// <returns>Whether it is possible to navigate forward one item in history.</returns>
317         /// <since_tizen> 4 </since_tizen>
318         public bool CanGoForward()
319         {
320             return Interop.ChromiumEwk.ewk_view_forward_possible(_realHandle);
321         }
322
323         /// <summary>
324         /// Injects the supplied javascript message handler into the view.
325         /// </summary>
326         /// <param name="name"> The message callback.</param>
327         /// <param name="handler">The name used to expose the object in JavaScript.</param>
328         /// <returns>'true' on success, otherwise 'false'.</returns>
329         /// <since_tizen> 4 </since_tizen>
330         public bool AddJavaScriptMessageHandler(string name, JavaScriptMessageHandler handler)
331         {
332             lock (_javaScriptMessageHandlerMap)
333             {
334                 if (_javaScriptMessageHandlerMap.ContainsKey(name))
335                 {
336                     return false;
337                 }
338                 _javaScriptMessageHandlerMap[name] = handler;
339             }
340             Interop.ChromiumEwk.ScriptMessageCallback callback = (handle, message) =>
341             {
342                 JavaScriptMessage convertedMessage = new JavaScriptMessage(message);
343                 lock (_javaScriptMessageHandlerMap)
344                 {
345                     if (_javaScriptMessageHandlerMap.ContainsKey(convertedMessage.Name))
346                     {
347                         _javaScriptMessageHandlerMap[convertedMessage.Name](convertedMessage);
348                     }
349                 }
350             };
351             if (!Interop.ChromiumEwk.ewk_view_javascript_message_handler_add(_realHandle, callback, name))
352             {
353                 lock (_javaScriptMessageHandlerMap)
354                 {
355                     _javaScriptMessageHandlerMap.Remove(name);
356                     return false;
357                 }
358             }
359             return true;
360         }
361
362         /// <summary>
363         /// Requests the execution of a given name and the result to the JavaScript runtime.
364         /// </summary>
365         /// <param name="name">The name used to expose the object in JavaScript.</param>
366         /// <param name="result">The result to the JavaScript runtime.</param>
367         /// <since_tizen> 4 </since_tizen>
368         public void EvalWithResult(string name, string result)
369         {
370             Interop.ChromiumEwk.ewk_view_evaluate_javascript(_realHandle, name, result);
371         }
372
373         /// <summary>
374         /// Requests the execution of the given script.
375         /// </summary>
376         /// <param name="script">The JavaScript code string to execute.</param>
377         /// <since_tizen> 4 </since_tizen>
378         public void Eval(string script)
379         {
380             Interop.ChromiumEwk.ewk_view_script_execute(_realHandle, script, null, IntPtr.Zero);
381         }
382
383         /// <summary>
384         /// Requests to set or unset a view as the currently focused one.
385         /// </summary>
386         /// <param name="focused">'true' to set the focus on the view, 'false' to remove the focus from the view.</param>
387         /// <since_tizen> 4 </since_tizen>
388         public void SetFocus(bool focused)
389         {
390             Interop.ChromiumEwk.ewk_view_focus_set(_realHandle, focused);
391         }
392
393         /// <summary>
394         /// Creates a widget handle.
395         /// </summary>
396         /// <param name="parent">Parent EvasObject.</param>
397         /// <returns>IntPtr of the widget handle.</returns>
398         /// <since_tizen> 4 </since_tizen>
399         protected override IntPtr CreateHandle(EvasObject parent)
400         {
401             // focus dummy
402             _handle = Interop.Elementary.elm_layout_add((IntPtr)parent);
403             Interop.Elementary.elm_layout_theme_set(_handle, "layout", "elm_widget", "default");
404             Interop.Elementary.elm_object_focus_allow_set(_handle, true);
405
406             IntPtr evas = Interop.Evas.evas_object_evas_get(parent);
407             _realHandle = Interop.ChromiumEwk.ewk_view_add(evas);
408             Interop.Elementary.elm_object_part_content_set(_handle, "elm.swallow.content", _realHandle);
409
410             return _handle;
411         }
412
413         /// <summary>
414         /// Sets the delegate for context menu customization.
415         /// </summary>
416         /// <param name="contextMenuCustomizeDelegate">The delegate for context menu customization.</param>
417         /// <since_tizen> 6 </since_tizen>
418         public void SetContextMenuCustomizeDelegate(ContextMenuCustomize contextMenuCustomizeDelegate)
419         {
420             _contextMenuCustomizeDelegate = contextMenuCustomizeDelegate;
421         }
422
423         private void InitializeSmartEvent()
424         {
425             // focus dummy
426             _focusIn = new SmartEvent(this, "focused");
427             _focusOut = new SmartEvent(this, "unfocused");
428
429             _focusIn.On += (s, e) => { ((WebView)s).SetFocus(true); };
430             _focusOut.On += (s, e) => { ((WebView)s).SetFocus(false); };
431
432             _loadStarted = new SmartEvent(this, _realHandle, "load,started");
433             _loadFinished = new SmartEvent(this, _realHandle, "load,finished");
434             _loadError = new SmartEvent<SmartCallbackLoadErrorArgs>(this, _realHandle, "load,error", SmartCallbackLoadErrorArgs.CreateFromSmartEvent);
435             _titleChanged = new SmartEvent<SmartCallbackArgs>(this, _realHandle, "title,changed", SmartCallbackArgs.CreateFromSmartEvent);
436             _urlChanged = new SmartEvent<SmartCallbackArgs>(this, _realHandle, "url,changed", SmartCallbackArgs.CreateFromSmartEvent);
437             _contextMenuCustomize = new SmartEvent<ContextMenuCustomizeEventArgs>(this, _realHandle, "contextmenu,customize", ContextMenuCustomizeEventArgs.CreateFromSmartEvent);
438             _contextMenuItemSelected = new SmartEvent<ContextMenuItemEventArgs>(this, _realHandle, "contextmenu,selected", ContextMenuItemEventArgs.CreateFromSmartEvent);
439             _policyNavigationDecide = new SmartEvent<NavigationPolicyEventArgs>(this, _realHandle, "policy,navigation,decide", NavigationPolicyEventArgs.CreateFromSmartEvent);
440             _policyNewWindowDecide = new SmartEvent<NewWindowPolicyEventArgs>(this, _realHandle, "policy,newwindow,decide", NewWindowPolicyEventArgs.CreateFromSmartEvent);
441             _policyResponseDecide = new SmartEvent<ResponsePolicyEventArgs>(this, _realHandle, "policy,response,decide", ResponsePolicyEventArgs.CreateFromSmartEvent);            
442
443             _loadStarted.On += (s, e) => { LoadStarted?.Invoke(this, EventArgs.Empty); };
444             _loadFinished.On += (s, e) => { LoadFinished?.Invoke(this, EventArgs.Empty); };
445             _loadError.On += (s, e) => { LoadError?.Invoke(this, e); };
446             _titleChanged.On += (s, e) => { TitleChanged?.Invoke(this, e); };
447             _urlChanged.On += (s, e) => { UrlChanged?.Invoke(this, e); };
448             _policyNavigationDecide.On += (s, e) => { NavigationPolicyDecideRequested?.Invoke(this, e); };
449             _policyNewWindowDecide.On += (s, e) => { NewWindowPolicyDecideRequested?.Invoke(this, e); };
450             _policyResponseDecide.On += (s, e) => { ResponsePolicyDecideRequested?.Invoke(this, e); };
451             _contextMenuItemSelected.On += (s, e) => { ContextMenuItemSelected?.Invoke(this, e); };
452             _contextMenuCustomize.On += (s, e) => { _contextMenuCustomizeDelegate?.Invoke(e.Menu); };
453         }
454         
455     }
456 }