Add a callback for navigation policy in web engine.
[platform/core/uifw/dali-adaptor.git] / dali / internal / accessibility / bridge / bridge-impl.cpp
1 /*
2  * Copyright (c) 2021 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
20 // EXTERNAL INCLUDES
21 #include <dali/devel-api/common/stage.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/actors/layer.h>
24 #include <iostream>
25 #include <unordered_map>
26
27 // INTERNAL INCLUDES
28 #include <dali/devel-api/adaptor-framework/environment-variable.h>
29 #include <dali/devel-api/adaptor-framework/window-devel.h>
30 #include <dali/internal/accessibility/bridge/bridge-accessible.h>
31 #include <dali/internal/accessibility/bridge/bridge-action.h>
32 #include <dali/internal/accessibility/bridge/bridge-collection.h>
33 #include <dali/internal/accessibility/bridge/bridge-component.h>
34 #include <dali/internal/accessibility/bridge/bridge-editable-text.h>
35 #include <dali/internal/accessibility/bridge/bridge-hypertext.h>
36 #include <dali/internal/accessibility/bridge/bridge-hyperlink.h>
37 #include <dali/internal/accessibility/bridge/bridge-object.h>
38 #include <dali/internal/accessibility/bridge/bridge-selection.h>
39 #include <dali/internal/accessibility/bridge/bridge-text.h>
40 #include <dali/internal/accessibility/bridge/bridge-value.h>
41 #include <dali/internal/accessibility/bridge/bridge-application.h>
42 #include <dali/internal/accessibility/bridge/dummy-atspi.h>
43 #include <dali/internal/adaptor/common/adaptor-impl.h>
44 #include <dali/internal/system/common/environment-variables.h>
45
46 using namespace Dali::Accessibility;
47
48 namespace // unnamed namespace
49 {
50
51 const int RETRY_INTERVAL = 1000;
52
53 } // unnamed namespace
54
55 /**
56  * @brief The BridgeImpl class is to implement some Bridge functions.
57  */
58 class BridgeImpl : public virtual BridgeBase,
59                    public BridgeAccessible,
60                    public BridgeObject,
61                    public BridgeComponent,
62                    public BridgeCollection,
63                    public BridgeAction,
64                    public BridgeValue,
65                    public BridgeText,
66                    public BridgeEditableText,
67                    public BridgeSelection,
68                    public BridgeApplication,
69                    public BridgeHypertext,
70                    public BridgeHyperlink
71 {
72   DBus::DBusClient                                              mAccessibilityStatusClient;
73   DBus::DBusClient                                              mRegistryClient;
74   DBus::DBusClient                                              mDirectReadingClient;
75   bool                                                          mIsScreenReaderEnabled = false;
76   bool                                                          mIsEnabled             = false;
77   bool                                                          mIsShown               = false;
78   std::unordered_map<int32_t, std::function<void(std::string)>> mDirectReadingCallbacks;
79   Dali::Actor                                                   mHighlightedActor;
80   std::function<void(Dali::Actor)>                              mHighlightClearAction;
81   Dali::CallbackBase*                                           mIdleCallback          = NULL;
82   Dali::Timer                                                   mInitializeTimer;
83   Dali::Timer                                                   mReadIsEnabledTimer;
84   Dali::Timer                                                   mReadScreenReaderEnabledTimer;
85
86 public:
87   BridgeImpl()
88   {
89   }
90
91   /**
92    * @copydoc Dali::Accessibility::Bridge::Emit()
93    */
94   Consumed Emit(KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText) override
95   {
96     if(!IsUp())
97     {
98       return Consumed::NO;
99     }
100
101     unsigned int keyType = 0;
102
103     switch(type)
104     {
105       case KeyEventType::KEY_PRESSED:
106       {
107         keyType = 0;
108         break;
109       }
110       case KeyEventType::KEY_RELEASED:
111       {
112         keyType = 1;
113         break;
114       }
115       default:
116       {
117         return Consumed::NO;
118       }
119     }
120
121     auto methodObject = mRegistryClient.method<bool(std::tuple<uint32_t, int32_t, int32_t, int32_t, int32_t, std::string, bool>)>("NotifyListenersSync");
122     auto result       = methodObject.call(std::tuple<uint32_t, int32_t, int32_t, int32_t, int32_t, std::string, bool>{keyType, 0, static_cast<int32_t>(keyCode), 0, static_cast<int32_t>(timeStamp), keyName, isText ? 1 : 0});
123     if(!result)
124     {
125       LOG() << result.getError().message;
126       return Consumed::NO;
127     }
128     return std::get<0>(result) ? Consumed::YES : Consumed::NO;
129   }
130
131   /**
132    * @copydoc Dali::Accessibility::Bridge::Pause()
133    */
134   void Pause() override
135   {
136     if(!IsUp())
137     {
138       return;
139     }
140
141     mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
142       if(!msg)
143       {
144         LOG() << "Direct reading command failed (" << msg.getError().message << ")";
145       }
146     },
147                                                                                         true);
148   }
149
150   /**
151    * @copydoc Dali::Accessibility::Bridge::Resume()
152    */
153   void Resume() override
154   {
155     if(!IsUp())
156     {
157       return;
158     }
159
160     mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
161       if(!msg)
162       {
163         LOG() << "Direct reading command failed (" << msg.getError().message << ")";
164       }
165     },
166                                                                                         false);
167   }
168
169   /**
170    * @copydoc Dali::Accessibility::Bridge::StopReading()
171    */
172   void StopReading(bool alsoNonDiscardable) override
173   {
174     if(!IsUp())
175     {
176       return;
177     }
178
179     mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("StopReading").asyncCall([](DBus::ValueOrError<void> msg) {
180       if(!msg)
181       {
182         LOG() << "Direct reading command failed (" << msg.getError().message << ")";
183       }
184     },
185                                                                                         alsoNonDiscardable);
186   }
187
188   /**
189    * @copydoc Dali::Accessibility::Bridge::Say()
190    */
191   void Say(const std::string& text, bool discardable, std::function<void(std::string)> callback) override
192   {
193     if(!IsUp())
194     {
195       return;
196     }
197
198     mDirectReadingClient.method<DBus::ValueOrError<std::string, bool, int32_t>(std::string, bool)>("ReadCommand").asyncCall([=](DBus::ValueOrError<std::string, bool, int32_t> msg) {
199       if(!msg)
200       {
201         LOG() << "Direct reading command failed (" << msg.getError().message << ")";
202       }
203       else if(callback)
204       {
205         mDirectReadingCallbacks.emplace(std::get<2>(msg), callback);
206       }
207     },
208                                                                                                                            text,
209                                                                                                                            discardable);
210   }
211
212   /**
213    * @copydoc Dali::Accessibility::Bridge::ForceDown()
214    */
215   void ForceDown() override
216   {
217     if(mData)
218     {
219       if(mData->mCurrentlyHighlightedActor && mData->mHighlightActor)
220       {
221         mData->mCurrentlyHighlightedActor.Remove(mData->mHighlightActor);
222       }
223       mData->mCurrentlyHighlightedActor = {};
224       mData->mHighlightActor            = {};
225
226       mDisabledSignal.Emit();
227     }
228     mHighlightedActor     = {};
229     mHighlightClearAction = {};
230     BridgeAccessible::ForceDown();
231     mRegistryClient       = {};
232     mDirectReadingClient  = {};
233     mDirectReadingCallbacks.clear();
234     mApplication.mChildren.clear();
235     mApplication.mWindows.clear();
236   }
237
238   void StopTimer()
239   {
240     if(mInitializeTimer)
241     {
242       mInitializeTimer.Stop();
243     }
244
245     if(mReadIsEnabledTimer)
246     {
247       mReadIsEnabledTimer.Stop();
248     }
249
250     if(mReadScreenReaderEnabledTimer)
251     {
252       mReadScreenReaderEnabledTimer.Stop();
253     }
254   }
255
256   /**
257    * @copydoc Dali::Accessibility::Bridge::Terminate()
258    */
259   void Terminate() override
260   {
261     if(mData)
262     {
263       mData->mCurrentlyHighlightedActor = {};
264       mData->mHighlightActor            = {};
265     }
266     ForceDown();
267     StopTimer();
268     if((NULL != mIdleCallback) && Dali::Adaptor::IsAvailable())
269     {
270       Dali::Adaptor::Get().RemoveIdle(mIdleCallback);
271     }
272     mAccessibilityStatusClient        = {};
273     mDbusServer                       = {};
274     mConnectionPtr                    = {};
275   }
276
277   /**
278    * @copydoc Dali::Accessibility::Bridge::ForceUp()
279    */
280   ForceUpResult ForceUp() override
281   {
282     if(BridgeAccessible::ForceUp() == ForceUpResult::ALREADY_UP)
283     {
284       return ForceUpResult::ALREADY_UP;
285     }
286
287     BridgeObject::RegisterInterfaces();
288     BridgeAccessible::RegisterInterfaces();
289     BridgeComponent::RegisterInterfaces();
290     BridgeCollection::RegisterInterfaces();
291     BridgeAction::RegisterInterfaces();
292     BridgeValue::RegisterInterfaces();
293     BridgeText::RegisterInterfaces();
294     BridgeEditableText::RegisterInterfaces();
295     BridgeSelection::RegisterInterfaces();
296     BridgeApplication::RegisterInterfaces();
297     BridgeHypertext::RegisterInterfaces();
298     BridgeHyperlink::RegisterInterfaces();
299
300     RegisterOnBridge(&mApplication);
301
302     mRegistryClient      = {AtspiDbusNameRegistry, AtspiDbusPathDec, AtspiDbusInterfaceDec, mConnectionPtr};
303     mDirectReadingClient = DBus::DBusClient{DirectReadingDBusName, DirectReadingDBusPath, DirectReadingDBusInterface, mConnectionPtr};
304
305     mDirectReadingClient.addSignal<void(int32_t, std::string)>("ReadingStateChanged", [=](int32_t id, std::string readingState) {
306       auto it = mDirectReadingCallbacks.find(id);
307       if(it != mDirectReadingCallbacks.end())
308       {
309         it->second(readingState);
310         if(readingState != "ReadingPaused" && readingState != "ReadingResumed" && readingState != "ReadingStarted")
311         {
312           mDirectReadingCallbacks.erase(it);
313         }
314       }
315     });
316
317     auto    proxy = DBus::DBusClient{AtspiDbusNameRegistry, AtspiDbusPathRoot, AtspiDbusInterfaceSocket, mConnectionPtr};
318     Address root{"", "root"};
319     auto    res = proxy.method<Address(Address)>("Embed").call(root);
320     if(!res)
321     {
322       LOG() << "Call to Embed failed: " << res.getError().message;
323     }
324     assert(res);
325
326     mApplication.mParent.SetAddress(std::move(std::get<0>(res)));
327
328     mEnabledSignal.Emit();
329
330     if(mIsShown)
331     {
332       auto rootLayer = Dali::Stage::GetCurrent().GetRootLayer();
333       auto window    = Dali::DevelWindow::Get(rootLayer);
334       EmitActivate(window); // Currently, sends a signal that the default window is activated here.
335     }
336
337     return ForceUpResult::JUST_STARTED;
338   }
339
340   /**
341    * @brief Sends a signal to dbus that the window is shown.
342    *
343    * @param[in] window The window to be shown
344    * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
345    */
346   void EmitShown(Dali::Window window)
347   {
348     auto windowAccessible = mApplication.GetWindowAccessible(window);
349     if(windowAccessible)
350     {
351       windowAccessible->EmitShowing(true);
352     }
353   }
354
355   /**
356    * @brief Sends a signal to dbus that the window is hidden.
357    *
358    * @param[in] window The window to be hidden
359    * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
360    */
361   void EmitHidden(Dali::Window window)
362   {
363     auto windowAccessible = mApplication.GetWindowAccessible(window);
364     if(windowAccessible)
365     {
366       windowAccessible->EmitShowing(false);
367     }
368   }
369
370   /**
371    * @brief Sends a signal to dbus that the window is activated.
372    *
373    * @param[in] window The window to be activated
374    * @see BridgeObject::Emit()
375    */
376   void EmitActivate(Dali::Window window)
377   {
378     auto windowAccessible = mApplication.GetWindowAccessible(window);
379     if(windowAccessible)
380     {
381       windowAccessible->Emit(WindowEvent::ACTIVATE, 0);
382     }
383   }
384
385   /**
386    * @brief Sends a signal to dbus that the window is deactivated.
387    *
388    * @param[in] window The window to be deactivated
389    * @see BridgeObject::Emit()
390    */
391   void EmitDeactivate(Dali::Window window)
392   {
393     auto windowAccessible = mApplication.GetWindowAccessible(window);
394     if(windowAccessible)
395     {
396       windowAccessible->Emit(WindowEvent::DEACTIVATE, 0);
397     }
398   }
399
400   /**
401    * @copydoc Dali::Accessibility::Bridge::WindowShown()
402    */
403   void WindowShown(Dali::Window window) override
404   {
405     if(!mIsShown && IsUp())
406     {
407       EmitShown(window);
408     }
409     mIsShown = true;
410   }
411
412   /**
413    * @copydoc Dali::Accessibility::Bridge::WindowHidden()
414    */
415   void WindowHidden(Dali::Window window) override
416   {
417     if(mIsShown && IsUp())
418     {
419       EmitHidden(window);
420     }
421     mIsShown = false;
422   }
423
424   /**
425    * @copydoc Dali::Accessibility::Bridge::WindowFocused()
426    */
427   void WindowFocused(Dali::Window window) override
428   {
429     if(mIsShown && IsUp())
430     {
431       EmitActivate(window);
432     }
433   }
434
435   /**
436    * @copydoc Dali::Accessibility::Bridge::WindowUnfocused()
437    */
438   void WindowUnfocused(Dali::Window window) override
439   {
440     if(mIsShown && IsUp())
441     {
442       EmitDeactivate(window);
443     }
444   }
445
446   /**
447    * @copydoc Dali::Accessibility::Bridge::SuppressScreenReader()
448    */
449   void SuppressScreenReader(bool suppress) override
450   {
451     if(mIsScreenReaderSuppressed == suppress)
452     {
453       return;
454     }
455     mIsScreenReaderSuppressed = suppress;
456     ReadScreenReaderEnabledProperty();
457   }
458
459   bool ReadIsEnabledTimerCallback()
460   {
461     ReadIsEnabledProperty();
462     return false;
463   }
464
465   void ReadIsEnabledProperty()
466   {
467     mAccessibilityStatusClient.property<bool>("IsEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
468       if(!msg)
469       {
470         DALI_LOG_ERROR("Get IsEnabled property error: %s\n", msg.getError().message.c_str());
471         if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
472         {
473           if(!mReadIsEnabledTimer)
474           {
475             mReadIsEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
476             mReadIsEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadIsEnabledTimerCallback);
477           }
478           mReadIsEnabledTimer.Start();
479         }
480         return;
481       }
482       mIsEnabled = std::get<0>(msg);
483       if((!mIsScreenReaderSuppressed && mIsScreenReaderEnabled) || mIsEnabled)
484       {
485         ForceUp();
486       }
487       else
488       {
489         ForceDown();
490       }
491     });
492   }
493
494   void ListenIsEnabledProperty()
495   {
496     mAccessibilityStatusClient.addPropertyChangedEvent<bool>("IsEnabled", [this](bool res) {
497       mIsEnabled = res;
498       if(mIsScreenReaderEnabled || mIsEnabled)
499       {
500         ForceUp();
501       }
502       else
503       {
504         ForceDown();
505       }
506     });
507   }
508
509   bool ReadScreenReaderEnabledTimerCallback()
510   {
511     ReadScreenReaderEnabledProperty();
512     return false;
513   }
514
515   void ReadScreenReaderEnabledProperty()
516   {
517     // can be true because of SuppressScreenReader before init
518     if (!mAccessibilityStatusClient)
519     {
520       return;
521     }
522
523     mAccessibilityStatusClient.property<bool>("ScreenReaderEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
524       if(!msg)
525       {
526         DALI_LOG_ERROR("Get ScreenReaderEnabled property error: %s\n", msg.getError().message.c_str());
527         if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
528         {
529           if(!mReadScreenReaderEnabledTimer)
530           {
531             mReadScreenReaderEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
532             mReadScreenReaderEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadScreenReaderEnabledTimerCallback);
533           }
534           mReadScreenReaderEnabledTimer.Start();
535         }
536         return;
537       }
538       mIsScreenReaderEnabled = std::get<0>(msg);
539       if((!mIsScreenReaderSuppressed && mIsScreenReaderEnabled) || mIsEnabled)
540       {
541         ForceUp();
542       }
543       else
544       {
545         ForceDown();
546       }
547     });
548   }
549
550   void ListenScreenReaderEnabledProperty()
551   {
552     mAccessibilityStatusClient.addPropertyChangedEvent<bool>("ScreenReaderEnabled", [this](bool res) {
553       mIsScreenReaderEnabled = res;
554       if((!mIsScreenReaderSuppressed && mIsScreenReaderEnabled) || mIsEnabled)
555       {
556         ForceUp();
557       }
558       else
559       {
560         ForceDown();
561       }
562     });
563   }
564
565   void ReadAndListenProperties()
566   {
567     ReadIsEnabledProperty();
568     ListenIsEnabledProperty();
569
570     ReadScreenReaderEnabledProperty();
571     ListenScreenReaderEnabledProperty();
572   }
573
574   bool InitializeAccessibilityStatusClient()
575   {
576     mAccessibilityStatusClient = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION};
577
578     if (!mAccessibilityStatusClient)
579     {
580       DALI_LOG_ERROR("Accessibility Status DbusClient is not ready\n");
581       return false;
582     }
583
584     return true;
585   }
586
587   bool InitializeTimerCallback()
588   {
589     if ( InitializeAccessibilityStatusClient() )
590     {
591       ReadAndListenProperties();
592       return false;
593     }
594     return true;
595   }
596
597   bool OnIdleSignal()
598   {
599     if ( InitializeAccessibilityStatusClient() )
600     {
601       ReadAndListenProperties();
602       mIdleCallback = NULL;
603       return false;
604     }
605
606     if(!mInitializeTimer)
607     {
608       mInitializeTimer = Dali::Timer::New(RETRY_INTERVAL);
609       mInitializeTimer.TickSignal().Connect(this, &BridgeImpl::InitializeTimerCallback);
610     }
611     mInitializeTimer.Start();
612
613     mIdleCallback = NULL;
614     return false;
615   }
616
617   /**
618    * @copydoc Dali::Accessibility::Bridge::Initialize()
619    */
620   void Initialize() override
621   {
622     if ( InitializeAccessibilityStatusClient() )
623     {
624       ReadAndListenProperties();
625       return;
626     }
627
628     // Initialize failed. Try it again on Idle
629     if( Dali::Adaptor::IsAvailable() )
630     {
631       Dali::Adaptor& adaptor = Dali::Adaptor::Get();
632       if( NULL == mIdleCallback )
633       {
634         mIdleCallback = MakeCallback( this, &BridgeImpl::OnIdleSignal );
635         adaptor.AddIdle( mIdleCallback, true );
636       }
637     }
638   }
639
640   /**
641    * @copydoc Dali::Accessibility::Bridge::GetScreenReaderEnabled()
642    */
643   bool GetScreenReaderEnabled() override
644   {
645     return mIsScreenReaderEnabled;
646   }
647
648   /**
649    * @copydoc Dali::Accessibility::Bridge::IsEnabled()
650    */
651   bool IsEnabled() override
652   {
653     return mIsEnabled;
654   }
655 }; // BridgeImpl
656
657 namespace // unnamed namespace
658 {
659
660 bool INITIALIZED_BRIDGE = false;
661
662 /**
663  * @brief Creates BridgeImpl instance.
664  *
665  * @return The BridgeImpl instance
666  * @note This method is to check environment variable first. If ATSPI is disable using env, it returns dummy bridge instance.
667  */
668 std::shared_ptr<Bridge> CreateBridge()
669 {
670   INITIALIZED_BRIDGE = true;
671
672   try
673   {
674     /* check environment variable first */
675     const char* envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI);
676     if(envAtspiDisabled && std::atoi(envAtspiDisabled) != 0)
677     {
678       return Dali::Accessibility::DummyBridge::GetInstance();
679     }
680
681     return std::make_shared<BridgeImpl>();
682   }
683   catch(const std::exception&)
684   {
685     DALI_LOG_ERROR("Failed to initialize AT-SPI bridge");
686     return Dali::Accessibility::DummyBridge::GetInstance();
687   }
688 }
689
690 } // unnamed namespace
691
692 // Dali::Accessibility::Bridge class implementation
693
694 std::shared_ptr<Bridge> Bridge::GetCurrentBridge()
695 {
696   static std::shared_ptr<Bridge> bridge;
697
698   if(bridge)
699   {
700     return bridge;
701   }
702   else if(mAutoInitState == AutoInitState::ENABLED)
703   {
704     bridge = CreateBridge();
705
706     /* check environment variable for suppressing screen-reader */
707     const char* envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER);
708     if(envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0)
709     {
710       bridge->SuppressScreenReader(true);
711     }
712
713     return bridge;
714   }
715
716   return Dali::Accessibility::DummyBridge::GetInstance();
717 }
718
719 void Bridge::DisableAutoInit()
720 {
721   if(INITIALIZED_BRIDGE)
722   {
723     DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization");
724   }
725
726   mAutoInitState = AutoInitState::DISABLED;
727 }
728
729 void Bridge::EnableAutoInit()
730 {
731   mAutoInitState = AutoInitState::ENABLED;
732
733   if(INITIALIZED_BRIDGE)
734   {
735     return;
736   }
737
738   auto rootLayer       = Dali::Stage::GetCurrent().GetRootLayer(); // A root layer of the default window.
739   auto window          = Dali::DevelWindow::Get(rootLayer);
740   auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
741
742   auto accessible = Accessibility::Accessible::Get(rootLayer, true);
743
744   auto bridge = Bridge::GetCurrentBridge();
745   bridge->AddTopLevelWindow(accessible);
746   bridge->SetApplicationName(applicationName);
747   bridge->Initialize();
748
749   if(window && window.IsVisible())
750   {
751     bridge->WindowShown(window);
752   }
753 }