[Tizen] Update contents of webview when frame is rendered.
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / web-view / web-view-impl.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd.
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
18 // CLASS HEADER
19 #include "web-view-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <cstring>
23 #include <dali/devel-api/adaptor-framework/web-engine-back-forward-list.h>
24 #include <dali/devel-api/adaptor-framework/web-engine-context.h>
25 #include <dali/devel-api/adaptor-framework/web-engine-cookie-manager.h>
26 #include <dali/devel-api/adaptor-framework/web-engine-policy-decision.h>
27 #include <dali/devel-api/adaptor-framework/web-engine-settings.h>
28 #include <dali/devel-api/scripting/enum-helper.h>
29 #include <dali/devel-api/scripting/scripting.h>
30 #include <dali/devel-api/common/stage.h>
31 #include <dali/public-api/adaptor-framework/native-image-source.h>
32 #include <dali/public-api/object/type-registry.h>
33 #include <dali/public-api/object/type-registry-helper.h>
34
35 // INTERNAL INCLUDES
36 #include <dali-toolkit/devel-api/controls/control-devel.h>
37 #include <dali-toolkit/devel-api/controls/web-view/web-back-forward-list.h>
38 #include <dali-toolkit/devel-api/controls/web-view/web-context.h>
39 #include <dali-toolkit/devel-api/controls/web-view/web-cookie-manager.h>
40 #include <dali-toolkit/devel-api/controls/web-view/web-settings.h>
41 #include <dali-toolkit/devel-api/image-loader/texture-manager.h>
42 #include <dali-toolkit/internal/visuals/visual-factory-impl.h>
43 #include <dali-toolkit/public-api/image-loader/image.h>
44 #include <dali-toolkit/public-api/visuals/image-visual-properties.h>
45
46 #include <functional>
47
48 namespace Dali
49 {
50
51 namespace Toolkit
52 {
53
54 namespace Internal
55 {
56
57 namespace
58 {
59
60 BaseHandle Create()
61 {
62   return Toolkit::WebView::New();
63 }
64
65 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::WebView, Toolkit::Control, Create )
66
67 DALI_PROPERTY_REGISTRATION( Toolkit, WebView, "url",              STRING,  URL                )
68 DALI_PROPERTY_REGISTRATION( Toolkit, WebView, "userAgent",        STRING,  USER_AGENT         )
69 DALI_PROPERTY_REGISTRATION( Toolkit, WebView, "scrollPosition",   VECTOR2, SCROLL_POSITION    )
70 DALI_PROPERTY_REGISTRATION( Toolkit, WebView, "scrollSize",       VECTOR2, SCROLL_SIZE        )
71 DALI_PROPERTY_REGISTRATION( Toolkit, WebView, "contentSize",      VECTOR2, CONTENT_SIZE       )
72 DALI_PROPERTY_REGISTRATION( Toolkit, WebView, "title",            STRING,  TITLE              )
73 DALI_PROPERTY_REGISTRATION( Toolkit, WebView, "videoHoleEnabled", BOOLEAN, VIDEO_HOLE_ENABLED )
74
75 DALI_TYPE_REGISTRATION_END()
76
77 const std::string kEmptyString;
78
79 const char* DEFAULT_SAMPLER_TYPENAME = "sampler2D";
80
81 const char* FRAGMENT_SHADER_TEXTURE = DALI_COMPOSE_SHADER(
82   varying mediump vec2 vTexCoord;\n
83   uniform sampler2D sTexture;\n
84   uniform lowp vec4 uColor;\n
85   uniform lowp vec3 mixColor;\n
86   uniform lowp float preMultipliedAlpha;\n
87   \n
88   void main()\n
89   {\n
90       gl_FragColor = texture2D(sTexture, vTexCoord) * uColor * vec4(mixColor, 1.0);\n
91   }\n
92 );
93
94 Dali::Toolkit::Visual::Base CreateNativeImageVisual(NativeImageInterfacePtr nativeImageInterface)
95 {
96   std::string fragmentShader;
97   const char* fragmentPrefix = nativeImageInterface->GetCustomFragmentPrefix();
98   if(fragmentPrefix)
99   {
100     fragmentShader = fragmentPrefix;
101     fragmentShader += FRAGMENT_SHADER_TEXTURE;
102   }
103   else
104   {
105     fragmentShader = FRAGMENT_SHADER_TEXTURE;
106   }
107
108   const char* customSamplerTypename = nativeImageInterface->GetCustomSamplerTypename();
109   if(customSamplerTypename)
110   {
111     fragmentShader.replace(fragmentShader.find(DEFAULT_SAMPLER_TYPENAME), strlen(DEFAULT_SAMPLER_TYPENAME), customSamplerTypename);
112   }
113
114   Texture texture = Dali::Texture::New(*nativeImageInterface);
115   const std::string nativeImageUrl = Dali::Toolkit::TextureManager::AddTexture(texture);
116
117   return Toolkit::VisualFactory::Get().CreateVisual(
118     { { Toolkit::Visual::Property::TYPE,  Toolkit::Visual::IMAGE } ,
119       { Toolkit::Visual::Property::SHADER, { { Toolkit::Visual::Shader::Property::FRAGMENT_SHADER, fragmentShader } } },
120       { Toolkit::ImageVisual::Property::URL, nativeImageUrl } } );
121 }
122
123 } // anonymous namepsace
124
125 WebView::WebView( const std::string& locale, const std::string& timezoneId )
126 : Control( ControlBehaviour( ACTOR_BEHAVIOUR_DEFAULT | DISABLE_STYLE_CHANGE_SIGNALS ) ),
127   mVisual(),
128   mWebViewSize( Stage::GetCurrent().GetSize() ),
129   mWebEngine(),
130   mVideoHoleEnabled( true ),
131   mWebViewArea ( 0, 0, mWebViewSize.width, mWebViewSize.height )
132 {
133   mWebEngine = Dali::WebEngine::New();
134
135   // WebEngine is empty when it is not properly initialized.
136   if( mWebEngine )
137   {
138     mWebEngine.Create( mWebViewSize.width, mWebViewSize.height, locale, timezoneId );
139   }
140 }
141
142 WebView::WebView( int argc, char** argv )
143 : Control( ControlBehaviour( ACTOR_BEHAVIOUR_DEFAULT | DISABLE_STYLE_CHANGE_SIGNALS ) ),
144   mVisual(),
145   mWebViewSize( Stage::GetCurrent().GetSize() ),
146   mWebEngine(),
147   mVideoHoleEnabled( true ),
148   mWebViewArea ( 0, 0, mWebViewSize.width, mWebViewSize.height )
149 {
150   mWebEngine = Dali::WebEngine::New();
151
152   // WebEngine is empty when it is not properly initialized.
153   if ( mWebEngine )
154   {
155     mWebEngine.Create( mWebViewSize.width, mWebViewSize.height, argc, argv );
156   }
157 }
158
159 WebView::WebView()
160 : WebView( "", "" )
161 {
162 }
163
164 WebView::~WebView()
165 {
166   if( mWebEngine )
167   {
168     mWebEngine.Destroy();
169   }
170 }
171
172 Toolkit::WebView WebView::New()
173 {
174   WebView* impl = new WebView();
175   Toolkit::WebView handle = Toolkit::WebView( *impl );
176
177   impl->Initialize();
178   return handle;
179 }
180
181 Toolkit::WebView WebView::New( const std::string& locale, const std::string& timezoneId )
182 {
183   WebView* impl = new WebView( locale, timezoneId );
184   Toolkit::WebView handle = Toolkit::WebView( *impl );
185
186   impl->Initialize();
187   return handle;
188 }
189
190 Toolkit::WebView WebView::New( int argc, char** argv )
191 {
192   WebView* impl = new WebView( argc, argv );
193   Toolkit::WebView handle = Toolkit::WebView( *impl );
194
195   impl->Initialize();
196   return handle;
197 }
198
199 void WebView::OnInitialize()
200 {
201   Actor self = Self();
202
203   self.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE, true);
204   self.TouchedSignal().Connect(this, &WebView::OnTouchEvent);
205
206   mPositionUpdateNotification = self.AddPropertyNotification( Actor::Property::WORLD_POSITION, StepCondition( 1.0f, 1.0f ) );
207   mSizeUpdateNotification = self.AddPropertyNotification( Actor::Property::SIZE, StepCondition( 1.0f, 1.0f ) );
208   mScaleUpdateNotification = self.AddPropertyNotification( Actor::Property::WORLD_SCALE, StepCondition( 0.1f, 1.0f ) );
209   mPositionUpdateNotification.NotifySignal().Connect( this, &WebView::OnDisplayAreaUpdated );
210   mSizeUpdateNotification.NotifySignal().Connect( this, &WebView::OnDisplayAreaUpdated );
211   mScaleUpdateNotification.NotifySignal().Connect( this, &WebView::OnDisplayAreaUpdated );
212
213   if(mWebEngine)
214   {
215     mWebEngine.RegisterFrameRenderedCallback(std::bind(&WebView::OnFrameRendered, this));
216     mWebContext = std::unique_ptr<Dali::Toolkit::WebContext>(new WebContext(mWebEngine.GetContext()));
217     mWebCookieManager = std::unique_ptr<Dali::Toolkit::WebCookieManager>(new WebCookieManager(mWebEngine.GetCookieManager()));
218     mWebSettings = std::unique_ptr<Dali::Toolkit::WebSettings>(new WebSettings(mWebEngine.GetSettings()));
219     mWebBackForwardList = std::unique_ptr<Dali::Toolkit::WebBackForwardList>(new WebBackForwardList(mWebEngine.GetBackForwardList()));
220   }
221 }
222
223 Dali::Toolkit::WebSettings* WebView::GetSettings() const
224 {
225   return mWebSettings.get();
226 }
227
228 Dali::Toolkit::WebContext* WebView::GetContext() const
229 {
230   return mWebContext.get();
231 }
232
233 Dali::Toolkit::WebCookieManager* WebView::GetCookieManager() const
234 {
235   return mWebCookieManager.get();
236 }
237
238 Dali::Toolkit::WebBackForwardList* WebView::GetBackForwardList() const
239 {
240   return mWebBackForwardList.get();
241 }
242
243 Dali::WebEnginePlugin* WebView::GetPlugin() const
244 {
245   return mWebEngine ? mWebEngine.GetPlugin() : nullptr;
246 }
247
248 Dali::Toolkit::ImageView WebView::GetFavicon() const
249 {
250   Dali::Toolkit::ImageView faviconView;
251   if(mWebEngine)
252   {
253     Dali::PixelData pixelData = mWebEngine.GetFavicon();
254     if(pixelData)
255     {
256       std::string url = Dali::Toolkit::Image::GenerateUrl(pixelData);
257       faviconView     = Dali::Toolkit::ImageView::New(url);
258       faviconView.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
259     }
260   }
261   return faviconView;
262 }
263
264 void WebView::LoadUrl(const std::string& url)
265 {
266   if(mWebEngine)
267   {
268     mWebEngine.LoadUrl(url);
269   }
270 }
271
272 void WebView::LoadHtmlString(const std::string& htmlString)
273 {
274   if(mWebEngine)
275   {
276     mWebEngine.LoadHtmlString(htmlString);
277   }
278 }
279
280 void WebView::Reload()
281 {
282   if( mWebEngine )
283   {
284     mWebEngine.Reload();
285   }
286 }
287
288 void WebView::StopLoading()
289 {
290   if( mWebEngine )
291   {
292     mWebEngine.StopLoading();
293   }
294 }
295
296 void WebView::Suspend()
297 {
298   if( mWebEngine )
299   {
300     mWebEngine.Suspend();
301   }
302 }
303
304 void WebView::Resume()
305 {
306   if( mWebEngine )
307   {
308     mWebEngine.Resume();
309   }
310 }
311
312 void WebView::ScrollBy( int deltaX, int deltaY )
313 {
314   if ( mWebEngine )
315   {
316     mWebEngine.ScrollBy( deltaX, deltaY );
317   }
318 }
319
320 bool WebView::CanGoForward()
321 {
322   return mWebEngine ? mWebEngine.CanGoForward() : false;
323 }
324
325 void WebView::GoForward()
326 {
327   if( mWebEngine )
328   {
329     mWebEngine.GoForward();
330   }
331 }
332
333 bool WebView::CanGoBack()
334 {
335   return mWebEngine ? mWebEngine.CanGoBack() : false;
336 }
337
338 void WebView::GoBack()
339 {
340   if( mWebEngine )
341   {
342     mWebEngine.GoBack();
343   }
344 }
345
346 void WebView::EvaluateJavaScript( const std::string& script, Dali::WebEnginePlugin::JavaScriptMessageHandlerCallback resultHandler )
347 {
348   if( mWebEngine )
349   {
350     mWebEngine.EvaluateJavaScript( script, resultHandler );
351   }
352 }
353
354 void WebView::AddJavaScriptMessageHandler( const std::string& exposedObjectName, Dali::WebEnginePlugin::JavaScriptMessageHandlerCallback handler )
355 {
356   if( mWebEngine )
357   {
358     mWebEngine.AddJavaScriptMessageHandler( exposedObjectName, handler );
359   }
360 }
361
362 void WebView::ClearAllTilesResources()
363 {
364   if( mWebEngine )
365   {
366     mWebEngine.ClearAllTilesResources();
367   }
368 }
369
370 void WebView::ClearHistory()
371 {
372   if( mWebEngine )
373   {
374     mWebEngine.ClearHistory();
375   }
376 }
377
378 void WebView::SetTtsFocus(bool focused)
379 {
380   if(mWebEngine && !HasKeyInputFocus())
381   {
382     mWebEngine.SetFocus(focused);
383   }
384 }
385
386 void WebView::EnableVideoHole( bool enabled )
387 {
388   mVideoHoleEnabled = enabled;
389   EnableBlendMode(!mVideoHoleEnabled);
390   if(mWebEngine)
391   {
392     mWebEngine.EnableVideoHole(mVideoHoleEnabled);
393   }
394 }
395
396 void WebView::EnableBlendMode(bool blendEnabled)
397 {
398   Actor self = Self();
399   for (uint32_t i = 0; i < self.GetRendererCount(); i++)
400   {
401     Dali::Renderer render = self.GetRendererAt(i);
402     render.SetProperty(Renderer::Property::BLEND_MODE, blendEnabled ? BlendMode::ON : BlendMode::OFF);
403   }
404 }
405
406 void WebView::RegisterPageLoadStartedCallback(Dali::WebEnginePlugin::WebEnginePageLoadCallback callback)
407 {
408   if(mWebEngine)
409   {
410     mWebEngine.RegisterPageLoadStartedCallback(callback);
411   }
412 }
413
414 void WebView::RegisterPageLoadFinishedCallback(Dali::WebEnginePlugin::WebEnginePageLoadCallback callback)
415 {
416   if(mWebEngine)
417   {
418     mWebEngine.RegisterPageLoadFinishedCallback(callback);
419   }
420 }
421
422 void WebView::RegisterPageLoadErrorCallback(Dali::WebEnginePlugin::WebEnginePageLoadErrorCallback callback)
423 {
424   if(mWebEngine)
425   {
426     mWebEngine.RegisterPageLoadErrorCallback(callback);
427   }
428 }
429
430 void WebView::RegisterScrollEdgeReachedCallback(Dali::WebEnginePlugin::WebEngineScrollEdgeReachedCallback callback)
431 {
432   if(mWebEngine)
433   {
434     mWebEngine.RegisterScrollEdgeReachedCallback(callback);
435   }
436 }
437
438 void WebView::RegisterUrlChangedCallback(Dali::WebEnginePlugin::WebEngineUrlChangedCallback callback)
439 {
440   if(mWebEngine)
441   {
442     mWebEngine.RegisterUrlChangedCallback(callback);
443   }
444 }
445
446 void WebView::RegisterNavigationPolicyDecidedCallback(Dali::WebEnginePlugin::WebEngineNavigationPolicyDecidedCallback callback)
447 {
448   if(mWebEngine)
449   {
450     mWebEngine.RegisterNavigationPolicyDecidedCallback(callback);
451   }
452 }
453
454 void WebView::RegisterNewWindowCreatedCallback(Dali::WebEnginePlugin::WebEngineNewWindowCreatedCallback callback)
455 {
456   if(mWebEngine)
457   {
458     mWebEngine.RegisterNewWindowCreatedCallback(callback);
459   }
460 }
461
462 void WebView::GetPlainTextAsynchronously(Dali::WebEnginePlugin::PlainTextReceivedCallback callback)
463 {
464   if(mWebEngine)
465   {
466     mWebEngine.GetPlainTextAsynchronously(callback);
467   }
468 }
469
470 Vector3 WebView::GetNaturalSize()
471 {
472   if( mVisual )
473   {
474     Vector2 rendererNaturalSize;
475     mVisual.GetNaturalSize( rendererNaturalSize );
476     return Vector3( rendererNaturalSize );
477   }
478   return Vector3( mWebViewSize );
479 }
480
481 void WebView::OnSceneConnection( int depth )
482 {
483   Control::OnSceneConnection( depth );
484   EnableBlendMode( !mVideoHoleEnabled );
485 }
486
487 void WebView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
488 {
489   Toolkit::WebView webView = Toolkit::WebView::DownCast( Dali::BaseHandle( object ) );
490   if( webView )
491   {
492     WebView& impl = GetImpl( webView );
493     switch( index )
494     {
495       case Toolkit::WebView::Property::URL:
496       {
497         std::string url;
498         if( value.Get( url ) )
499         {
500           impl.LoadUrl( url );
501         }
502         break;
503       }
504       case Toolkit::WebView::Property::USER_AGENT:
505       {
506         std::string input;
507         if( value.Get( input ) )
508         {
509           impl.SetUserAgent( input );
510         }
511         break;
512       }
513       case Toolkit::WebView::Property::SCROLL_POSITION:
514       {
515         Vector2 input;
516         if ( value.Get( input ) )
517         {
518           impl.SetScrollPosition( input.x, input.y );
519         }
520         break;
521       }
522       case Toolkit::WebView::Property::VIDEO_HOLE_ENABLED:
523       {
524         bool input;
525         if( value.Get( input ) )
526         {
527           impl.EnableVideoHole( input );
528         }
529         break;
530       }
531     }
532   }
533 }
534
535 Property::Value WebView::GetProperty( BaseObject* object, Property::Index propertyIndex )
536 {
537   Property::Value value;
538   Toolkit::WebView webView = Toolkit::WebView::DownCast( Dali::BaseHandle( object ) );
539   if( webView )
540   {
541     WebView& impl = GetImpl( webView );
542     switch( propertyIndex )
543     {
544       case Toolkit::WebView::Property::URL:
545       {
546         value = impl.GetUrl();
547         break;
548       }
549       case Toolkit::WebView::Property::USER_AGENT:
550       {
551         value = impl.GetUserAgent();
552         break;
553       }
554       case Toolkit::WebView::Property::SCROLL_POSITION:
555       {
556         int x, y;
557         impl.GetScrollPosition( x, y );
558         value = Vector2( x, y );
559         break;
560       }
561       case Toolkit::WebView::Property::SCROLL_SIZE:
562       {
563         int width, height;
564         impl.GetScrollSize( width, height );
565         value = Vector2( width, height );
566         break;
567       }
568       case Toolkit::WebView::Property::CONTENT_SIZE:
569       {
570         int width, height;
571         impl.GetContentSize( width, height );
572         value = Vector2( width, height );
573         break;
574       }
575       case Toolkit::WebView::Property::TITLE:
576       {
577         value = impl.GetTitle();
578         break;
579       }
580       case Toolkit::WebView::Property::VIDEO_HOLE_ENABLED:
581       {
582         value = impl.mVideoHoleEnabled;
583         break;
584       }
585       default:
586          break;
587     }
588   }
589
590   return value;
591 }
592
593 bool WebView::OnTouchEvent( Actor actor, const Dali::TouchEvent& touch )
594 {
595   bool result = false;
596   if( mWebEngine )
597   {
598     result = mWebEngine.SendTouchEvent( touch );
599   }
600   return result;
601 }
602
603 bool WebView::OnKeyEvent( const Dali::KeyEvent& event )
604 {
605   bool result = false;
606   if( mWebEngine )
607   {
608     result = mWebEngine.SendKeyEvent( event );
609   }
610   return result;
611 }
612
613 void WebView::OnKeyInputFocusGained()
614 {
615   if( mWebEngine )
616   {
617     mWebEngine.SetFocus( true );
618   }
619   EmitKeyInputFocusSignal( true ); // Calls back into the Control hence done last.
620 }
621
622 void WebView::OnKeyInputFocusLost()
623 {
624   if( mWebEngine )
625   {
626     mWebEngine.SetFocus( false );
627   }
628   EmitKeyInputFocusSignal( false ); // Calls back into the Control hence done last.
629 }
630
631 void WebView::OnFrameRendered()
632 {
633   // Make sure that mVisual is created only once.
634   if (mVisual)
635     return;
636
637   mVisual = CreateNativeImageVisual(mWebEngine.GetNativeImageSource());
638   if (mVisual)
639   {
640     DevelControl::RegisterVisual(*this, Toolkit::WebView::Property::URL, mVisual);
641     EnableBlendMode(!mVideoHoleEnabled);
642   }
643 }
644
645 void WebView::OnDisplayAreaUpdated(Dali::PropertyNotification& /*source*/)
646 {
647   if( !mWebEngine )
648     return;
649
650   Actor self( Self() );
651
652   bool positionUsesAnchorPoint = self.GetProperty< bool >( Actor::Property::POSITION_USES_ANCHOR_POINT );
653   Vector3 actorSize = self.GetCurrentProperty< Vector3 >( Actor::Property::SIZE ) * self.GetCurrentProperty< Vector3 >( Actor::Property::SCALE );
654   Vector3 anchorPointOffSet = actorSize * ( positionUsesAnchorPoint ? self.GetCurrentProperty< Vector3 >( Actor::Property::ANCHOR_POINT ) : AnchorPoint::TOP_LEFT );
655   Vector2 screenPosition = self.GetProperty< Vector2 >( Actor::Property::SCREEN_POSITION );
656
657   Dali::Rect< int > displayArea;
658   displayArea.x = screenPosition.x - anchorPointOffSet.x;
659   displayArea.y = screenPosition.y - anchorPointOffSet.y;
660   displayArea.width = actorSize.x;
661   displayArea.height = actorSize.y;
662
663   Size displaySize = Size( displayArea.width, displayArea.height );
664   if ( mWebViewSize != displaySize )
665   {
666     mWebViewSize = displaySize;
667   }
668
669   if (mWebViewArea != displayArea )
670   {
671     mWebViewArea = displayArea;
672     mWebEngine.UpdateDisplayArea( mWebViewArea );
673   }
674 }
675
676 void WebView::SetScrollPosition( int x, int y )
677 {
678   if( mWebEngine )
679   {
680     mWebEngine.SetScrollPosition( x, y );
681   }
682 }
683
684 void WebView::GetScrollPosition( int& x, int& y ) const
685 {
686   if( mWebEngine )
687   {
688     mWebEngine.GetScrollPosition( x, y );
689   }
690 }
691
692 void WebView::GetScrollSize( int& width, int& height ) const
693 {
694   if( mWebEngine )
695   {
696     mWebEngine.GetScrollSize( width, height );
697   }
698 }
699
700 void WebView::GetContentSize( int& width, int& height ) const
701 {
702   if( mWebEngine )
703   {
704     mWebEngine.GetContentSize( width, height );
705   }
706 }
707
708 std::string WebView::GetUrl() const
709 {
710   return mWebEngine ? mWebEngine.GetUrl() : kEmptyString;
711 }
712
713 std::string WebView::GetTitle() const
714 {
715   return mWebEngine ?  mWebEngine.GetTitle() : kEmptyString;
716 }
717
718 std::string WebView::GetUserAgent() const
719 {
720   return mWebEngine ? mWebEngine.GetUserAgent() : kEmptyString;
721 }
722
723 void WebView::SetUserAgent( const std::string& userAgent )
724 {
725   if( mWebEngine )
726   {
727     mWebEngine.SetUserAgent( userAgent );
728   }
729 }
730
731 } // namespace Internal
732
733 } // namespace Toolkit
734
735 } // namespace Dali