[Tizen][AT-SPI] do not keep window in ApplicationAccessible
[platform/core/uifw/dali-adaptor.git] / dali / internal / accessibility / bridge / bridge-base.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 #include <dali/internal/accessibility/bridge/bridge-base.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/common/stage.h>
23 #include <atomic>
24 #include <cstdlib>
25 #include <memory>
26
27 // INTERNAL INCLUDES
28 #include <dali/public-api/adaptor-framework/timer.h>
29
30 using namespace Dali::Accessibility;
31
32 static Dali::Timer tickTimer;
33
34 BridgeBase::BridgeBase()
35 {
36 }
37
38 BridgeBase::~BridgeBase()
39 {
40   mApplication.mChildren.clear();
41 }
42
43 void BridgeBase::AddFilteredEvent(FilteredEvents kind, Dali::Accessibility::Accessible* obj, float delay, std::function<void()> functor)
44 {
45   if(delay < 0)
46   {
47     delay = 0;
48   }
49
50   auto it = mFilteredEvents.insert({{kind, obj}, {static_cast<unsigned int>(delay * 10), {}}});
51   if(it.second)
52   {
53     functor();
54   }
55   else
56   {
57     it.first->second.second = std::move(functor);
58   }
59
60   if(!tickTimer)
61   {
62     tickTimer = Dali::Timer::New(100);
63     tickTimer.TickSignal().Connect(this, &BridgeBase::TickFilteredEvents);
64   }
65 }
66
67 bool BridgeBase::TickFilteredEvents()
68 {
69   for(auto it = mFilteredEvents.begin(); it != mFilteredEvents.end();)
70   {
71     if(it->second.first)
72     {
73       --it->second.first;
74     }
75     else
76     {
77       if(it->second.second)
78       {
79         it->second.second();
80         it->second.second = {};
81       }
82       else
83       {
84         it = mFilteredEvents.erase(it);
85         continue;
86       }
87     }
88     ++it;
89   }
90   return !mFilteredEvents.empty();
91 }
92
93 void BridgeBase::UpdateRegisteredEvents()
94 {
95   using ReturnType = std::vector<std::tuple<std::string, std::string>>;
96   mRegistry.method<DBus::ValueOrError<ReturnType>()>("GetRegisteredEvents").asyncCall([this](DBus::ValueOrError<ReturnType> msg) {
97     if(!msg)
98     {
99       LOG() << "Get registered events failed";
100       return;
101     }
102
103     IsBoundsChangedEventAllowed = false;
104
105     ReturnType values = std::get<ReturnType>(msg.getValues());
106     for(long unsigned int i = 0; i < values.size(); i++)
107     {
108       if(!std::get<1>(values[i]).compare("Object:BoundsChanged"))
109       {
110         IsBoundsChangedEventAllowed = true;
111       }
112     }
113   });
114 }
115
116 BridgeBase::ForceUpResult BridgeBase::ForceUp()
117 {
118   if(Bridge::ForceUp() == ForceUpResult::ALREADY_UP)
119   {
120     return ForceUpResult::ALREADY_UP;
121   }
122   auto proxy = DBus::DBusClient{dbusLocators::atspi::BUS, dbusLocators::atspi::OBJ_PATH, dbusLocators::atspi::BUS_INTERFACE, DBus::ConnectionType::SESSION};
123   auto addr  = proxy.method<std::string()>(dbusLocators::atspi::GET_ADDRESS).call();
124
125   if(!addr)
126   {
127     throw std::domain_error{std::string("failed at call '") + dbusLocators::atspi::GET_ADDRESS + "': " + addr.getError().message};
128   }
129
130   mConnectionPtr  = DBusWrapper::Installed()->eldbus_address_connection_get_impl(std::get<0>(addr));
131   mData->mBusName = DBus::getConnectionName(mConnectionPtr);
132   mDbusServer     = {mConnectionPtr};
133
134   {
135     DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceCache};
136     AddFunctionToInterface(desc, "GetItems", &BridgeBase::GetItems);
137     mDbusServer.addInterface(AtspiDbusPathCache, desc);
138   }
139   {
140     DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceApplication};
141     AddGetSetPropertyToInterface(desc, "Id", &BridgeBase::GetId, &BridgeBase::SetId);
142     mDbusServer.addInterface(AtspiPath, desc);
143   }
144
145   mRegistry = {AtspiDbusNameRegistry, AtspiDbusPathRegistry, AtspiDbusInterfaceRegistry, mConnectionPtr};
146
147   UpdateRegisteredEvents();
148
149   mRegistry.addSignal<void(void)>("EventListenerRegistered", [this](void) {
150     UpdateRegisteredEvents();
151   });
152
153   mRegistry.addSignal<void(void)>("EventListenerDeregistered", [this](void) {
154     UpdateRegisteredEvents();
155   });
156
157   return ForceUpResult::JUST_STARTED;
158 }
159
160 void BridgeBase::ForceDown()
161 {
162   Bridge::ForceDown();
163   mRegistry      = {};
164   mDbusServer    = {};
165   mConnectionPtr = {};
166 }
167
168 const std::string& BridgeBase::GetBusName() const
169 {
170   static std::string empty;
171   return mData ? mData->mBusName : empty;
172 }
173
174 Accessible* BridgeBase::FindByPath(const std::string& name) const
175 {
176   try
177   {
178     return Find(name);
179   }
180   catch(std::domain_error&)
181   {
182     return nullptr;
183   }
184 }
185
186 void BridgeBase::AddPopup(Accessible* object)
187 {
188   if(std::find(mPopups.begin(), mPopups.end(), object) != mPopups.end())
189   {
190     return;
191   }
192   mPopups.push_back(object);
193   if(IsUp())
194   {
195     object->Emit(WindowEvent::ACTIVATE, 0);
196   }
197 }
198
199 void BridgeBase::RemovePopup(Accessible* object)
200 {
201   auto it = std::find(mPopups.begin(), mPopups.end(), object);
202   if(it == mPopups.end())
203   {
204     return;
205   }
206   mPopups.erase(it);
207
208   if(IsUp())
209   {
210     object->Emit(WindowEvent::DEACTIVATE, 0);
211     if(mPopups.empty())
212     {
213       mApplication.mChildren.back()->Emit(WindowEvent::ACTIVATE, 0);
214     }
215     else
216     {
217       mPopups.back()->Emit(WindowEvent::ACTIVATE, 0);
218     }
219   }
220 }
221
222 void BridgeBase::AddTopLevelWindow(Accessible* windowAccessible)
223 {
224   if(windowAccessible->GetInternalActor() == nullptr)
225   {
226     return;
227   }
228
229   // Prevent adding the default window twice.
230   if(!mApplication.mChildren.empty() &&
231      mApplication.mChildren[0]->GetInternalActor() == windowAccessible->GetInternalActor())
232   {
233     return;
234   }
235
236   // Adds Window to a list of Windows.
237   mApplication.mChildren.push_back(windowAccessible);
238   SetIsOnRootLevel(windowAccessible);
239 }
240
241 void BridgeBase::RemoveTopLevelWindow(Accessible* windowAccessible)
242 {
243   for(auto i = 0u; i < mApplication.mChildren.size(); ++i)
244   {
245     if(mApplication.mChildren[i] == windowAccessible)
246     {
247       mApplication.mChildren.erase(mApplication.mChildren.begin() + i);
248       break;
249     }
250   }
251 }
252
253 std::string BridgeBase::StripPrefix(const std::string& path)
254 {
255   auto size = strlen(AtspiPath);
256   return path.substr(size + 1);
257 }
258
259 Accessible* BridgeBase::Find(const std::string& path) const
260 {
261   if(path == "root")
262   {
263     return &mApplication;
264   }
265
266   void* accessible;
267   std::istringstream tmp{path};
268   if(!(tmp >> accessible))
269   {
270     throw std::domain_error{"invalid path '" + path + "'"};
271   }
272
273   auto it = mData->mKnownObjects.find(static_cast<Accessible*>(accessible));
274   if(it == mData->mKnownObjects.end())
275   {
276     throw std::domain_error{"unknown object '" + path + "'"};
277   }
278
279   return static_cast<Accessible*>(accessible);
280 }
281
282 Accessible* BridgeBase::Find(const Address& ptr) const
283 {
284   assert(ptr.GetBus() == mData->mBusName);
285   return Find(ptr.GetPath());
286 }
287
288 Accessible* BridgeBase::FindSelf() const
289 {
290   auto path  = DBus::DBusServer::getCurrentObjectPath();
291   auto size = strlen(AtspiPath);
292   if(path.size() <= size)
293   {
294     throw std::domain_error{"invalid path '" + path + "'"};
295   }
296   if(path.substr(0, size) != AtspiPath)
297   {
298     throw std::domain_error{"invalid path '" + path + "'"};
299   }
300   if(path[size] != '/')
301   {
302     throw std::domain_error{"invalid path '" + path + "'"};
303   }
304   return Find(StripPrefix(path));
305 }
306
307 void BridgeBase::SetId(int id)
308 {
309   this->mId = id;
310 }
311
312 int BridgeBase::GetId()
313 {
314   return this->mId;
315 }
316
317 auto BridgeBase::GetItems() -> DBus::ValueOrError<std::vector<CacheElementType>>
318 {
319   auto root = &mApplication;
320
321   std::vector<CacheElementType> res;
322
323   std::function<void(Accessible*)> proc =
324     [&](Accessible* item) {
325       res.emplace_back(std::move(CreateCacheElement(root)));
326       for(auto i = 0u; i < item->GetChildCount(); ++i)
327       {
328         proc(item->GetChildAtIndex(i));
329       }
330     };
331
332   return res;
333 }
334
335 auto BridgeBase::CreateCacheElement(Accessible* item) -> CacheElementType
336 {
337   if(!item)
338   {
339     return {};
340   }
341
342   auto root   = &mApplication;
343   auto parent = item->GetParent();
344
345   std::vector<Address> children;
346   for(auto i = 0u; i < item->GetChildCount(); ++i)
347   {
348     children.emplace_back(item->GetChildAtIndex(i)->GetAddress());
349   }
350
351   return std::make_tuple(
352     item->GetAddress(),
353     root->GetAddress(),
354     parent ? parent->GetAddress() : Address{},
355     children,
356     item->GetInterfaces(),
357     item->GetName(),
358     item->GetRole(),
359     item->GetDescription(),
360     item->GetStates().GetRawData());
361 }