2 * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 using System.Collections.Generic;
20 using System.ComponentModel;
21 using System.Runtime.InteropServices;
22 using System.Threading.Tasks;
24 namespace Tizen.WebView
27 /// Enumeration values used to specify search options.
29 /// <since_tizen> 6 </since_tizen>
31 public enum FindOption
34 /// No search flags, this means a case sensitive, no wrap, forward only search.
38 /// Case insensitive search.
40 CaseInsensitive = 1 << 0,
42 /// Search text only at the beginning of the words.
46 /// Treat capital letters in the middle of words as word start.
48 TreatMedialCapitalAsWordStart = 1 << 2,
54 /// If not present the search stops at the end of the document.
64 ShowIndicator = 1 << 6,
68 ShowHighlight = 1 << 7,
72 /// Enumeration for Http Method.
74 /// <since_tizen> 6 </since_tizen>
75 public enum HttpMethod
100 /// Enumeration for Orientation of the device.
102 /// <since_tizen> 6 </since_tizen>
103 public enum Orientation
106 /// 0 degrees when the device is oriented to natural position.
110 /// -90 degrees when it's left side is at the top.
114 /// 90 degrees when it's right side is at the top.
118 /// 180 degrees when it is upside down.
124 /// A view used to render the web contents.
126 /// <since_tizen> 4 </since_tizen>
127 public class WebView : EvasObject
129 private static IDictionary<string, JavaScriptMessageHandler> _javaScriptMessageHandlerMap = new Dictionary<string, JavaScriptMessageHandler>();
131 private IntPtr _handle;
132 private IntPtr _realHandle;
133 private Context _context;
134 private Settings _settings;
136 private IDictionary<IntPtr, Interop.ChromiumEwk.ScriptExcuteCallback> _evalCallbacks = new Dictionary<IntPtr, Interop.ChromiumEwk.ScriptExcuteCallback>();
137 private int _evalCallbackId = 0;
140 private SmartEvent _focusIn;
141 private SmartEvent _focusOut;
144 private SmartEvent _loadStarted;
145 private SmartEvent _loadFinished;
146 private SmartEvent<SmartCallbackLoadErrorArgs> _loadError;
147 private SmartEvent<SmartCallbackArgs> _titleChanged;
148 private SmartEvent<SmartCallbackArgs> _urlChanged;
149 private SmartEvent<NavigationPolicyEventArgs> _policyNavigationDecide;
150 private SmartEvent<NewWindowPolicyEventArgs> _policyNewWindowDecide;
151 private SmartEvent<ResponsePolicyEventArgs> _policyResponseDecide;
153 private SmartEvent<ContextMenuItemEventArgs> _contextMenuItemSelected;
154 private SmartEvent<ContextMenuCustomizeEventArgs> _contextMenuCustomize;
156 private ContextMenuCustomize _contextMenuCustomizeDelegate;
159 /// Event that occurs when the load is started.
161 /// <since_tizen> 4 </since_tizen>
162 public event EventHandler LoadStarted;
165 /// Event that occurs when the load is finished.
167 /// <since_tizen> 4 </since_tizen>
168 public event EventHandler LoadFinished;
171 /// Event that occurs when the load throws an error.
173 /// <since_tizen> 4 </since_tizen>
174 public event EventHandler<SmartCallbackLoadErrorArgs> LoadError;
177 /// Event that occurs when the title of the main frame is changed.
179 /// <since_tizen> 4 </since_tizen>
180 public event EventHandler<SmartCallbackArgs> TitleChanged;
183 /// Event that occurs when the URL of the main frame is changed.
185 /// <since_tizen> 4 </since_tizen>
186 public event EventHandler<SmartCallbackArgs> UrlChanged;
189 /// Event that occurs when the policy navigation is decided.
191 /// <since_tizen> 6 </since_tizen>
192 public event EventHandler<NavigationPolicyEventArgs> NavigationPolicyDecideRequested;
195 /// Event that occurs when the policy new window is decided.
197 /// <since_tizen> 6 </since_tizen>
198 public event EventHandler<NewWindowPolicyEventArgs> NewWindowPolicyDecideRequested;
201 /// Event that occurs when the policy response is decided.
203 /// <since_tizen> 6 </since_tizen>
204 public event EventHandler<ResponsePolicyEventArgs> ResponsePolicyDecideRequested;
207 /// Event that occurs when the context menu item selected.
209 /// <since_tizen> 6 </since_tizen>
210 public event EventHandler<ContextMenuItemEventArgs> ContextMenuItemSelected;
213 /// The delegate is invoked when context menu customization is needed.
215 /// <param name="menu">The instance of ContextMenu.</param>
216 /// <since_tizen> 6 </since_tizen>
217 public delegate void ContextMenuCustomize(ContextMenu menu);
221 /// Current URL of the main frame.
223 /// <since_tizen> 4 </since_tizen>
228 return Interop.ChromiumEwk.ewk_view_url_get(_realHandle);
233 /// Current title of the main frame.
235 /// <since_tizen> 4 </since_tizen>
240 return Interop.ChromiumEwk.ewk_view_title_get(_realHandle);
245 /// Current user agent string of this view.
247 /// <since_tizen> 4 </since_tizen>
248 public string UserAgent
252 return Interop.ChromiumEwk.ewk_view_user_agent_get(_realHandle);
257 Interop.ChromiumEwk.ewk_view_user_agent_set(_realHandle, value);
262 /// Whether a view has the focus.
264 /// <since_tizen> 4 </since_tizen>
269 return Interop.ChromiumEwk.ewk_view_focus_get(_realHandle);
274 /// Creates a WebView object.
276 /// <param name="parent">Parent object of the WebView.</param>
277 /// <since_tizen> 4 </since_tizen>
278 public WebView(EvasObject parent) : base(parent)
280 InitializeSmartEvent();
284 /// Gets the context object of this view.
286 /// <returns>The context object of this view.</returns>
287 /// <since_tizen> 4 </since_tizen>
288 public Context GetContext()
290 if (_context == null)
292 IntPtr contextHandle = Interop.ChromiumEwk.ewk_view_context_get(_realHandle);
293 if (contextHandle == IntPtr.Zero)
297 _context = new Context(contextHandle);
303 /// Gets the settings object of this view.
305 /// <returns>The settings object of this view.</returns>
306 /// <since_tizen> 4 </since_tizen>
307 public Settings GetSettings()
309 if (_settings == null)
311 IntPtr settingsHandle = Interop.ChromiumEwk.ewk_view_settings_get(_realHandle);
312 if (settingsHandle == IntPtr.Zero)
316 _settings = new Settings(settingsHandle);
322 /// Gets the back/forward list object of this view.
324 /// <returns>The BackForward List object of this view.</returns>
325 /// <since_tizen> 6 </since_tizen>
326 public BackForwardList GetBackForwardList()
328 IntPtr backforwardlistHandle = Interop.ChromiumEwk.ewk_view_back_forward_list_get(_realHandle);
329 if (backforwardlistHandle == IntPtr.Zero)
333 return new BackForwardList(backforwardlistHandle);
337 /// Clear the back/forward list object of this view.
339 /// <since_tizen> 6 </since_tizen>
340 public void ClearBackForwardList()
342 Interop.ChromiumEwk.ewk_view_back_forward_list_clear(_realHandle);
346 /// Asks the object to load the given URL.
349 /// You can only be sure that the URL changes after UrlChanged event.
351 /// <param name="url">The uniform resource identifier to load.</param>
352 /// <since_tizen> 4 </since_tizen>
353 public void LoadUrl(string url)
355 Interop.ChromiumEwk.ewk_view_url_set(_realHandle, url);
359 /// Loads the specified HTML string as the content of the view.
361 /// <param name="html">HTML data to load.</param>
362 /// <param name="baseUrl">Base URL used for relative paths to external objects.</param>
363 /// <since_tizen> 4 </since_tizen>
364 public void LoadHtml(string html, string baseUrl)
366 Interop.ChromiumEwk.ewk_view_html_string_load(_realHandle, html, baseUrl, null);
370 /// Asks the main frame to stop loading.
372 /// <since_tizen> 4 </since_tizen>
373 public void StopLoading()
375 Interop.ChromiumEwk.ewk_view_stop(_realHandle);
379 /// Asks the main frame to reload the current document.
381 /// <since_tizen> 4 </since_tizen>
384 Interop.ChromiumEwk.ewk_view_reload(_realHandle);
388 /// Asks the main frame to navigate back in history.
390 /// <since_tizen> 4 </since_tizen>
393 Interop.ChromiumEwk.ewk_view_back(_realHandle);
397 /// Asks the main frame to navigate forward in history.
399 /// <since_tizen> 4 </since_tizen>
400 public void GoForward()
402 Interop.ChromiumEwk.ewk_view_forward(_realHandle);
406 /// Checks whether it is possible to navigate backward one item in history.
408 /// <returns>Whether it is possible to navigate backward one item in history.</returns>
409 /// <since_tizen> 4 </since_tizen>
410 public bool CanGoBack()
412 return Interop.ChromiumEwk.ewk_view_back_possible(_realHandle);
416 /// Checks whether it is possible to navigate forward one item in history.
418 /// <returns>Whether it is possible to navigate forward one item in history.</returns>
419 /// <since_tizen> 4 </since_tizen>
420 public bool CanGoForward()
422 return Interop.ChromiumEwk.ewk_view_forward_possible(_realHandle);
426 /// Injects the supplied javascript message handler into the view.
428 /// <param name="name"> The message callback.</param>
429 /// <param name="handler">The name used to expose the object in JavaScript.</param>
430 /// <returns>'true' on success, otherwise 'false'.</returns>
431 /// <since_tizen> 4 </since_tizen>
432 public bool AddJavaScriptMessageHandler(string name, JavaScriptMessageHandler handler)
434 lock (_javaScriptMessageHandlerMap)
436 if (_javaScriptMessageHandlerMap.ContainsKey(name))
440 _javaScriptMessageHandlerMap[name] = handler;
442 Interop.ChromiumEwk.ScriptMessageCallback callback = (handle, message) =>
444 JavaScriptMessage convertedMessage = new JavaScriptMessage(message);
445 lock (_javaScriptMessageHandlerMap)
447 if (_javaScriptMessageHandlerMap.ContainsKey(convertedMessage.Name))
449 _javaScriptMessageHandlerMap[convertedMessage.Name](convertedMessage);
453 if (!Interop.ChromiumEwk.ewk_view_javascript_message_handler_add(_realHandle, callback, name))
455 lock (_javaScriptMessageHandlerMap)
457 _javaScriptMessageHandlerMap.Remove(name);
465 /// Requests the execution of a given name and the result to the JavaScript runtime.
467 /// <param name="name">The name used to expose the object in JavaScript.</param>
468 /// <param name="result">The result to the JavaScript runtime.</param>
469 /// <since_tizen> 4 </since_tizen>
470 public void EvalWithResult(string name, string result)
472 Interop.ChromiumEwk.ewk_view_evaluate_javascript(_realHandle, name, result);
476 /// Requests the execution of the given script.
478 /// <param name="script">The JavaScript code string to execute.</param>
479 /// <since_tizen> 4 </since_tizen>
480 public void Eval(string script)
482 Interop.ChromiumEwk.ewk_view_script_execute(_realHandle, script, null, IntPtr.Zero);
486 /// Requests the evaluation of the given script.
488 /// <param name="script">The JavaScript code string to evaluate.</param>
489 /// <returns>A task that contains the result of the evaluation as a string.</returns>
490 /// <since_tizen> 8 </since_tizen>
491 /// <exception cref="ArgumentException">Thrown when a script is null or empty string.</exception>
492 public async Task<string> EvalAsync(string script)
494 if (string.IsNullOrEmpty(script))
496 throw new ArgumentException(nameof(script));
499 var tcs = new TaskCompletionSource<string>();
500 IntPtr id = IntPtr.Zero;
502 lock (_evalCallbacks)
504 id = (IntPtr)_evalCallbackId++;
505 _evalCallbacks[id] = (obj, returnValue, userData) =>
507 tcs.SetResult(Marshal.PtrToStringAnsi(returnValue));
508 lock (_evalCallbacks)
510 _evalCallbacks.Remove(userData);
515 Interop.ChromiumEwk.ewk_view_script_execute(_realHandle, script, _evalCallbacks[id], id);
516 return await tcs.Task;
520 /// Requests to set or unset a view as the currently focused one.
522 /// <param name="focused">'true' to set the focus on the view, 'false' to remove the focus from the view.</param>
523 /// <since_tizen> 4 </since_tizen>
524 public void SetFocus(bool focused)
526 Interop.ChromiumEwk.ewk_view_focus_set(_realHandle, focused);
530 /// Gets size of the content.
532 /// <returns> size of the coordinate.</returns>
533 /// <since_tizen> 6 </since_tizen>
534 public Size ContentsSize
539 Interop.ChromiumEwk.ewk_view_contents_size_get(_realHandle, out width, out height);
540 return new Size(width, height);
545 /// Exit full screen.
547 /// <since_tizen> 6 </since_tizen>
548 public void ExitFullscreen ()
550 Interop.ChromiumEwk.ewk_view_fullscreen_exit(_realHandle);
554 /// Gets the current load progress of the page.
556 /// <returns>'value 0.0 to 1.0' on success, otherwise '-1.0'.</returns>
557 /// <since_tizen> 6 </since_tizen>
558 public double LoadProgress
562 return Interop.ChromiumEwk.ewk_view_load_progress_get(_realHandle);
567 /// Sends the orientation of the device.
569 /// <param name="orientation">The new orientation of the device in degree.</param>
570 /// <since_tizen> 6 </since_tizen>
571 public void SendOrientation (Orientation orientation)
573 Interop.ChromiumEwk.ewk_view_orientation_send(_realHandle, orientation);
577 /// Suspends the operation associated with the view.
579 /// <since_tizen> 6 </since_tizen>
580 public void Suspend ()
582 Interop.ChromiumEwk.ewk_view_suspend(_realHandle);
586 /// Resumes the operation associated with the view.
588 /// <since_tizen> 6 </since_tizen>
589 public void Resume ()
591 Interop.ChromiumEwk.ewk_view_resume(_realHandle);
595 /// Gets the current scale factor of the page.
597 /// <since_tizen> 6 </since_tizen>
602 return Interop.ChromiumEwk.ewk_view_scale_get(_realHandle);
607 /// Sets the current scale factor of the page.
609 /// <param name="scaleFactor">A new level to set.</param>
610 /// <param name="scrollTo">The class Point object with X, Y coordinates.</param>
611 /// <since_tizen> 6 </since_tizen>
612 public void SetScale (double scaleFactor, Point scrollTo)
614 Interop.ChromiumEwk.ewk_view_scale_set(_realHandle, scaleFactor, scrollTo.X, scrollTo.Y);
618 /// Sets the current page's visibility.
620 /// <param name="enable">'true' to set on the visibility of the page, 'false' otherwise.</param>
621 /// <since_tizen> 6 </since_tizen>
622 public void SetViewVisibility (bool enable)
624 Interop.ChromiumEwk.ewk_view_visibility_set(_realHandle, enable);
628 /// Get and Sets the scroll position of the page.
630 /// <returns>The class Point object with X, Y coordinates.</returns>
631 /// <since_tizen> 6 </since_tizen>
632 public Point ScrollPosition
637 Interop.ChromiumEwk.ewk_view_scroll_pos_get(_realHandle, out p.X, out p.Y);
642 Interop.ChromiumEwk.ewk_view_scroll_set(_realHandle, value.X, value.Y);
647 /// Scrolls the webpage by the given amount.
649 /// <param name="delta">The class Point object with X, Y coordinates.</param>
650 /// <since_tizen> 6 </since_tizen>
651 public void ScrollBy (Point delta)
653 Interop.ChromiumEwk.ewk_view_scroll_by(_realHandle, delta.X, delta.Y);
657 /// Searches and highlights the given text string in the document.
659 /// <param name="text">The text to find.</param>
660 /// <param name="option">The options to find.</param>
661 /// <param name="maxMatchCount">The maximum match count to find, unlimited if 0.</param>
662 /// <since_tizen> 6 </since_tizen>
663 public void FindText (string text, FindOption option, int maxMatchCount)
665 Interop.ChromiumEwk.ewk_view_text_find(_realHandle, text, option, maxMatchCount);
669 /// Requests loading of the given request data.
671 /// <param name="url">The uniform resource identifier to load.</param>
672 /// <param name="httpMethod">The http method.</param>
673 /// <param name="httpHeaders">The http headers.</param>
674 /// <param name="httpBody">The http body data.</param>
675 /// <since_tizen> 6 </since_tizen>
676 public void SetUrlRequest (string url, HttpMethod httpMethod, IDictionary<string, string> httpHeaders, string httpBody)
678 List<IntPtr> stringHandles = new List<IntPtr>();
679 IntPtr hashHttpHeaders = Interop.Eina.eina_hash_string_small_new(IntPtr.Zero);
681 foreach (KeyValuePair<string, string> entry in httpHeaders)
683 IntPtr keyHandle = Marshal.StringToHGlobalAnsi(entry.Key);
684 IntPtr valueHandle = Marshal.StringToHGlobalAnsi(entry.Value);
685 Interop.Eina.eina_hash_add(hashHttpHeaders, keyHandle, valueHandle);
686 stringHandles.Add(keyHandle);
687 stringHandles.Add(valueHandle);
689 Interop.ChromiumEwk.ewk_view_url_request_set(_realHandle, url, httpMethod, hashHttpHeaders, httpBody);
691 foreach(IntPtr handle in stringHandles)
693 Marshal.FreeHGlobal(handle);
698 /// Creates a widget handle.
700 /// <param name="parent">Parent EvasObject.</param>
701 /// <returns>IntPtr of the widget handle.</returns>
702 /// <since_tizen> 4 </since_tizen>
703 protected override IntPtr CreateHandle(EvasObject parent)
706 _handle = Interop.Elementary.elm_layout_add((IntPtr)parent);
707 Interop.Elementary.elm_layout_theme_set(_handle, "layout", "elm_widget", "default");
708 Interop.Elementary.elm_object_focus_allow_set(_handle, true);
710 IntPtr evas = Interop.Evas.evas_object_evas_get(parent);
711 _realHandle = Interop.ChromiumEwk.ewk_view_add(evas);
712 Interop.Elementary.elm_object_part_content_set(_handle, "elm.swallow.content", _realHandle);
718 /// Sets the delegate for context menu customization.
720 /// <param name="contextMenuCustomizeDelegate">The delegate for context menu customization.</param>
721 /// <since_tizen> 6 </since_tizen>
722 public void SetContextMenuCustomizeDelegate(ContextMenuCustomize contextMenuCustomizeDelegate)
724 _contextMenuCustomizeDelegate = contextMenuCustomizeDelegate;
727 private void InitializeSmartEvent()
730 _focusIn = new SmartEvent(this, "focused");
731 _focusOut = new SmartEvent(this, "unfocused");
733 _focusIn.On += (s, e) => { ((WebView)s).SetFocus(true); };
734 _focusOut.On += (s, e) => { ((WebView)s).SetFocus(false); };
736 _loadStarted = new SmartEvent(this, _realHandle, "load,started");
737 _loadFinished = new SmartEvent(this, _realHandle, "load,finished");
738 _loadError = new SmartEvent<SmartCallbackLoadErrorArgs>(this, _realHandle, "load,error", SmartCallbackLoadErrorArgs.CreateFromSmartEvent);
739 _titleChanged = new SmartEvent<SmartCallbackArgs>(this, _realHandle, "title,changed", SmartCallbackArgs.CreateFromSmartEvent);
740 _urlChanged = new SmartEvent<SmartCallbackArgs>(this, _realHandle, "url,changed", SmartCallbackArgs.CreateFromSmartEvent);
741 _contextMenuCustomize = new SmartEvent<ContextMenuCustomizeEventArgs>(this, _realHandle, "contextmenu,customize", ContextMenuCustomizeEventArgs.CreateFromSmartEvent);
742 _contextMenuItemSelected = new SmartEvent<ContextMenuItemEventArgs>(this, _realHandle, "contextmenu,selected", ContextMenuItemEventArgs.CreateFromSmartEvent);
743 _policyNavigationDecide = new SmartEvent<NavigationPolicyEventArgs>(this, _realHandle, "policy,navigation,decide", NavigationPolicyEventArgs.CreateFromSmartEvent);
744 _policyNewWindowDecide = new SmartEvent<NewWindowPolicyEventArgs>(this, _realHandle, "policy,newwindow,decide", NewWindowPolicyEventArgs.CreateFromSmartEvent);
745 _policyResponseDecide = new SmartEvent<ResponsePolicyEventArgs>(this, _realHandle, "policy,response,decide", ResponsePolicyEventArgs.CreateFromSmartEvent);
747 _loadStarted.On += (s, e) => { LoadStarted?.Invoke(this, EventArgs.Empty); };
748 _loadFinished.On += (s, e) => { LoadFinished?.Invoke(this, EventArgs.Empty); };
749 _loadError.On += (s, e) => { LoadError?.Invoke(this, e); };
750 _titleChanged.On += (s, e) => { TitleChanged?.Invoke(this, e); };
751 _urlChanged.On += (s, e) => { UrlChanged?.Invoke(this, e); };
752 _policyNavigationDecide.On += (s, e) => { NavigationPolicyDecideRequested?.Invoke(this, e); };
753 _policyNewWindowDecide.On += (s, e) => { NewWindowPolicyDecideRequested?.Invoke(this, e); };
754 _policyResponseDecide.On += (s, e) => { ResponsePolicyDecideRequested?.Invoke(this, e); };
755 _contextMenuItemSelected.On += (s, e) => { ContextMenuItemSelected?.Invoke(this, e); };
756 _contextMenuCustomize.On += (s, e) => { _contextMenuCustomizeDelegate?.Invoke(e.Menu); };