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