1 /*
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "frameworks/bridge/js_frontend/js_ace_page.h"
17 
18 #include "core/components/focus_collaboration/focus_collaboration_component.h"
19 
20 namespace OHOS::Ace::Framework {
21 
22 #ifdef NG_BUILD
JsAcePage(int32_t pageId,const std::string & url)23 JsAcePage::JsAcePage(int32_t pageId, const std::string& url) : AcePage(pageId), url_(url) {}
24 #else
25 JsAcePage::JsAcePage(int32_t pageId, const RefPtr<DOMDocument>& document, const std::string& url,
26     const WeakPtr<StageElement>& container)
27     : AcePage(pageId), domDoc_(document), url_(url), container_(container),
28       radioGroups_(std::make_shared<JsPageRadioGroups>())
29 {
30     ACE_DCHECK(domDoc_);
31 }
32 #endif
33 
~JsAcePage()34 JsAcePage::~JsAcePage()
35 {
36     LOG_DESTROY();
37 #ifndef NG_BUILD
38     auto pipelineContext = pipelineContext_.Upgrade();
39     if (!pipelineContext) {
40         return;
41     }
42 
43     auto taskExecutor = pipelineContext->GetTaskExecutor();
44     if (!taskExecutor) {
45         LOGE("taskExecutor not exists");
46         return;
47     }
48 
49     auto accessibilityManager = pipelineContext->GetAccessibilityManager();
50     RefPtr<DOMDocument> domDoc;
51     domDoc.Swap(domDoc_);
52     auto weakDom = AceType::WeakClaim(AceType::RawPtr(domDoc));
53     auto weakAcc = AceType::WeakClaim(AceType::RawPtr(accessibilityManager));
54     taskExecutor->PostTask(
55         [weakDom, weakAcc] {
56             auto domDoc = weakDom.Upgrade();
57             auto accessibilityManager = weakAcc.Upgrade();
58             if (domDoc && accessibilityManager) {
59                 accessibilityManager->ClearPageAccessibilityNodes(domDoc->GetRootNodeId());
60             }
61         },
62         TaskExecutor::TaskType::UI, "ArkUIPageClearAccessibilityNodes");
63 
64     // Release Dom and Components in UI thread
65     RefPtr<PageTransitionComponent> pageTransition;
66     pageTransition.Swap(pageTransition_);
67     RefPtr<Component> component;
68     component.Swap(component_);
69     std::shared_ptr<JsPageRadioGroups> radioGroups;
70     radioGroups.swap(radioGroups_);
71 
72     taskExecutor->PostTask(
73         [domDoc, pageTransition, component, radioGroups]() mutable {
74             LOGI("release Dom and Components on UI thread");
75             domDoc.Reset();
76             pageTransition.Reset();
77             component.Reset();
78             radioGroups.reset();
79         },
80         TaskExecutor::TaskType::UI, "ArkUIPageReleaseDomAndComponent");
81 #endif
82 }
83 
BuildPage(const std::string & url)84 RefPtr<PageComponent> JsAcePage::BuildPage(const std::string& url)
85 {
86 #ifdef NG_BUILD
87     return nullptr;
88 #else
89     CHECK_RUN_ON(UI);
90     auto pageId = GetPageId();
91     auto rootStack = domDoc_->GetRootStackComponent();
92     auto rootComposedStack = domDoc_->GetRootComposedStack();
93     auto focusCollaboration = AceType::MakeRefPtr<FocusCollaborationComponent>(true);
94 
95     if (container_.Upgrade()) {
96         if (component_) {
97             return AceType::MakeRefPtr<PageComponent>(pageId, url, component_);
98         } else if (rootComposedStack) {
99             return AceType::MakeRefPtr<PageComponent>(pageId, url, rootComposedStack);
100         }
101     }
102     if (!pageTransition_) {
103         pageTransition_ = AceType::MakeRefPtr<PageTransitionComponent>();
104     }
105     if ((!rootStack || !rootComposedStack) && !component_) {
106         LOGW("Page[%{public}d] can't be loaded. no root component.", pageId);
107         pageTransition_->SetContent(nullptr);
108     } else {
109         if (component_) {
110             focusCollaboration->InsertChild(0, component_);
111         } else if (rootComposedStack) {
112             focusCollaboration->InsertChild(0, rootComposedStack);
113         }
114         pageTransition_->SetContent(focusCollaboration);
115         if ((SystemProperties::GetDeviceType() == DeviceType::TV) && (!pageTransition_->GetIsSetOption())) {
116             pageTransition_->SetSeparation(true);
117             SwapBackgroundDecoration(pageTransition_);
118         }
119     }
120     bool isDeclarative = false;
121     auto context = pipelineContext_.Upgrade();
122     if (context && context->GetIsDeclarative()) {
123         isDeclarative = true;
124     }
125     const std::string& cardComposeId = GetCardId();
126     if (!cardComposeId.empty()) {
127         return AceType::MakeRefPtr<PageComponent>(
128             pageId, url, cardComposeId, isDeclarative ? std::move(pageTransition_) : pageTransition_);
129     }
130     return AceType::MakeRefPtr<PageComponent>(
131         pageId, url, isDeclarative ? std::move(pageTransition_) : pageTransition_);
132 #endif
133 }
134 
GetCardId() const135 std::string JsAcePage::GetCardId() const
136 {
137     std::unique_ptr<JsonValue> argsValue = JsonUtil::ParseJsonString(pageParams_);
138     if (argsValue && argsValue->IsObject()) {
139         // support old JSON structure as { "ref": value}
140         if (!argsValue->GetString(DOM_TRANSITION_CARD_COMPOSEID).empty()) {
141             return argsValue->GetString(DOM_TRANSITION_CARD_COMPOSEID);
142         }
143 
144         // support new JSON structure as { "paramsData": { "ref": value } }
145         const auto& paramsData = argsValue->GetObject(DOM_TRANSITION_CARD_PARAMS);
146         if (paramsData->IsObject() && !paramsData->GetString(DOM_TRANSITION_CARD_COMPOSEID).empty()) {
147             return paramsData->GetString(DOM_TRANSITION_CARD_COMPOSEID);
148         }
149     }
150     return "";
151 }
152 
153 #ifndef NG_BUILD
BuildPagePatch(int32_t nodeId)154 RefPtr<ComposedComponent> JsAcePage::BuildPagePatch(int32_t nodeId)
155 {
156     CHECK_RUN_ON(UI);
157     RefPtr<Component> dirtyComponent = domDoc_->GetComponentById(nodeId);
158     if (!dirtyComponent) {
159         LOGE("Node[%{public}d] can't be reached.", nodeId);
160         return nullptr;
161     }
162 
163     auto composedComponent = AceType::DynamicCast<ComposedComponent>(dirtyComponent);
164     ACE_DCHECK(composedComponent);
165     return composedComponent;
166 }
167 
SwapBackgroundDecoration(const RefPtr<PageTransitionComponent> & transition)168 void JsAcePage::SwapBackgroundDecoration(const RefPtr<PageTransitionComponent>& transition)
169 {
170     CHECK_RUN_ON(UI);
171     if (!transition) {
172         LOGW("swap background decoration failed. transition is null.");
173         return;
174     }
175 
176     auto rootNode = domDoc_->GetDOMNodeById(DOM_ROOT_NODE_ID_BASE + GetPageId());
177     if (!rootNode) {
178         LOGW("swap background decoration failed. root node is null.");
179         return;
180     }
181 
182     auto box = rootNode->GetBoxComponent();
183     if (!box) {
184         LOGW("swap background decoration failed. box is null.");
185         return;
186     }
187 
188     auto decoration = box->GetBackDecoration();
189     if (!decoration) {
190         LOGW("swap background decoration failed. decoration is null.");
191         return;
192     }
193 
194     auto backgroundBox = AceType::MakeRefPtr<BoxComponent>();
195     backgroundBox->SetBackDecoration(decoration);
196     backgroundBox->SetWidth(box->GetWidthDimension().Value(), box->GetWidthDimension().Unit());
197     backgroundBox->SetHeight(box->GetHeightDimension().Value(), box->GetHeightDimension().Unit());
198     backgroundBox->SetFlex(BoxFlex::FLEX_XY);
199     transition->SetBackground(backgroundBox);
200     box->SetBackDecoration(nullptr);
201 }
202 #endif
203 
GetBridgeById(NodeId nodeId)204 RefPtr<BaseCanvasBridge> JsAcePage::GetBridgeById(NodeId nodeId)
205 {
206     std::unique_lock<std::mutex> lock(bridgeMutex_);
207     auto iter = canvasBridges_.find(nodeId);
208     if (iter == canvasBridges_.end()) {
209         LOGE("the canvas is not in the map");
210         return nullptr;
211     }
212     return iter->second;
213 }
214 
GetOffscreenCanvasBridgeById(int32_t bridgeId)215 RefPtr<BaseCanvasBridge> JsAcePage::GetOffscreenCanvasBridgeById(int32_t bridgeId)
216 {
217     auto iter = offscreenCanvasBridges_.find(bridgeId);
218     if (iter == offscreenCanvasBridges_.end()) {
219         LOGE("the canvas is not in the map");
220         return nullptr;
221     }
222     return iter->second;
223 }
224 
GetXComponentBridgeById(NodeId nodeId)225 RefPtr<BaseXComponentBridge> JsAcePage::GetXComponentBridgeById(NodeId nodeId)
226 {
227     auto iter = xcomponentBridges_.find(nodeId);
228     if (iter == xcomponentBridges_.end()) {
229         LOGE("the XComponent is not in the map");
230         return nullptr;
231     }
232     return iter->second;
233 }
234 
GetAnimationBridge(NodeId nodeId)235 RefPtr<BaseAnimationBridge> JsAcePage::GetAnimationBridge(NodeId nodeId)
236 {
237     std::unique_lock<std::mutex> lock(bridgeMutex_);
238     auto bridge = animationBridges_.find(nodeId);
239     if (bridge == animationBridges_.end()) {
240         LOGW("the animation bridge is not in the map, nodeId: %{public}d", nodeId);
241         return nullptr;
242     }
243     return bridge->second;
244 }
245 
RemoveAnimationBridge(NodeId nodeId)246 void JsAcePage::RemoveAnimationBridge(NodeId nodeId)
247 {
248     RefPtr<BaseAnimationBridge> bridge;
249     {
250         std::unique_lock<std::mutex> lock(bridgeMutex_);
251         auto pos = animationBridges_.find(nodeId);
252         if (pos != animationBridges_.end()) {
253             bridge.Swap(pos->second);
254             animationBridges_.erase(pos);
255         }
256     }
257 
258     if (bridge) {
259         auto pipelineContext = pipelineContext_.Upgrade();
260         if (!pipelineContext) {
261             LOGE("pipelineContext is nullptr");
262             return;
263         }
264         auto taskExecutor = pipelineContext->GetTaskExecutor();
265         if (!taskExecutor) {
266             LOGE("taskExecutor is nullptr");
267             return;
268         }
269         taskExecutor->PostSyncTask([&bridge]() { bridge.Reset(); },
270             TaskExecutor::TaskType::JS, "ArkUIReleaseAnimationBridge");
271     }
272 }
273 
AddAnimationBridge(NodeId nodeId,const RefPtr<BaseAnimationBridge> & animationBridge)274 void JsAcePage::AddAnimationBridge(NodeId nodeId, const RefPtr<BaseAnimationBridge>& animationBridge)
275 {
276     if (!animationBridge) {
277         LOGE("AddAnimationBridge failed. Animation bridge is null.");
278         return;
279     }
280     std::unique_lock<std::mutex> lock(bridgeMutex_);
281     animationBridges_[nodeId] = animationBridge;
282 }
283 
AddAnimatorBridge(int32_t bridgeId,const RefPtr<BaseAnimationBridge> & animatorBridge)284 void JsAcePage::AddAnimatorBridge(int32_t bridgeId, const RefPtr<BaseAnimationBridge>& animatorBridge)
285 {
286     if (!animatorBridge) {
287         LOGE("AddAnimationBridge failed. Animation bridge is null.");
288         return;
289     }
290     auto animator = animatorBridge->JsGetAnimator();
291     if (!animator) {
292         LOGE("animator is null");
293         return;
294     }
295     animator->AttachScheduler(pipelineContext_);
296     std::unique_lock<std::mutex> lock(bridgeMutex_);
297     animatorBridges_[bridgeId] = animatorBridge;
298 }
299 
RemoveAnimatorBridge(int32_t bridgeId)300 void JsAcePage::RemoveAnimatorBridge(int32_t bridgeId)
301 {
302     std::unique_lock<std::mutex> lock(bridgeMutex_);
303     animatorBridges_.erase(bridgeId);
304 }
305 
GetAnimatorBridge(int32_t bridgeId)306 RefPtr<BaseAnimationBridge> JsAcePage::GetAnimatorBridge(int32_t bridgeId)
307 {
308     std::unique_lock<std::mutex> lock(bridgeMutex_);
309     auto bridge = animatorBridges_.find(bridgeId);
310     if (bridge == animatorBridges_.end()) {
311         LOGW("the animation bridge is not in the map, nodeId: %{public}d", bridgeId);
312         return nullptr;
313     }
314     return bridge->second;
315 }
316 
AddAnimatorInfo(const std::string & animatorId,const RefPtr<AnimatorInfo> & animatorInfo)317 void JsAcePage::AddAnimatorInfo(const std::string& animatorId, const RefPtr<AnimatorInfo>& animatorInfo)
318 {
319     if (!animatorInfo) {
320         LOGE("AddAnimation failed. Animation is null.");
321         return;
322     }
323     auto animator = animatorInfo->GetAnimator();
324     if (!animator) {
325         LOGE("animator is null");
326         return;
327     }
328     animator->AttachScheduler(pipelineContext_);
329     animatorInfos_[animatorId] = animatorInfo;
330 }
331 
RemoveAnimatorInfo(const std::string & animatorId)332 void JsAcePage::RemoveAnimatorInfo(const std::string& animatorId)
333 {
334     animatorInfos_.erase(animatorId);
335 }
336 
GetAnimatorInfo(const std::string & animatorId)337 RefPtr<AnimatorInfo> JsAcePage::GetAnimatorInfo(const std::string& animatorId)
338 {
339     auto bridge = animatorInfos_.find(animatorId);
340     if (bridge == animatorInfos_.end()) {
341         LOGW("the animation bridge is not in the map, animatorId: %{public}s", animatorId.c_str());
342         return nullptr;
343     }
344     return bridge->second;
345 }
346 
PushCanvasBridge(NodeId nodeId,const RefPtr<BaseCanvasBridge> & bridge)347 void JsAcePage::PushCanvasBridge(NodeId nodeId, const RefPtr<BaseCanvasBridge>& bridge)
348 {
349     if (!bridge) {
350         LOGE("PushCanvasBridge failed. Canvas bridge is null.");
351         return;
352     }
353     std::unique_lock<std::mutex> lock(bridgeMutex_);
354     canvasBridges_[nodeId] = bridge;
355 }
356 
PushOffscreenCanvasBridge(int32_t bridgeId,const RefPtr<BaseCanvasBridge> & bridge)357 void JsAcePage::PushOffscreenCanvasBridge(int32_t bridgeId, const RefPtr<BaseCanvasBridge>& bridge)
358 {
359     offscreenCanvasBridges_[bridgeId] = bridge;
360 }
361 
PushXComponentBridge(NodeId nodeId,const RefPtr<BaseXComponentBridge> & bridge)362 void JsAcePage::PushXComponentBridge(NodeId nodeId, const RefPtr<BaseXComponentBridge>& bridge)
363 {
364     if (!bridge) {
365         LOGE("PushXComponentBridge failed. XComponent bridge is null.");
366         return;
367     }
368     xcomponentBridges_[nodeId] = bridge;
369 }
370 
AddNodeEvent(int32_t nodeId,const std::string & actionType,const std::string & eventAction)371 void JsAcePage::AddNodeEvent(int32_t nodeId, const std::string& actionType, const std::string& eventAction)
372 {
373     std::unique_lock<std::mutex> lock(eventMutex_);
374     nodeEvent_[nodeId][actionType] = eventAction;
375 }
376 
GetNodeEventAction(int32_t nodeId,const std::string & actionType)377 std::string JsAcePage::GetNodeEventAction(int32_t nodeId, const std::string& actionType)
378 {
379     // in error case just use empty string.
380     std::unique_lock<std::mutex> lock(eventMutex_);
381     return nodeEvent_[nodeId][actionType];
382 }
383 
SetRootNode(const RefPtr<NG::UINode> & node)384 void JsAcePage::SetRootNode(const RefPtr<NG::UINode>& node)
385 {
386     pageRootNode_ = node;
387 }
388 
389 #ifndef NG_BUILD
GetRadioGroups()390 std::shared_ptr<JsPageRadioGroups> JsAcePage::GetRadioGroups()
391 {
392     return radioGroups_;
393 }
394 #endif
395 
OnJsEngineDestroy()396 void JsAcePage::OnJsEngineDestroy()
397 {
398     std::unique_lock<std::mutex> lock(bridgeMutex_);
399     for (auto&& [id, bridge] : animationBridges_) {
400         if (bridge) {
401             bridge->OnJsEngineDestroy();
402         }
403     }
404     for (auto&& [id, bridge] : canvasBridges_) {
405         if (bridge) {
406             bridge->OnJsEngineDestroy();
407         }
408     }
409     for (auto&& [id, bridge] : xcomponentBridges_) {
410         if (bridge) {
411             bridge->OnJsEngineDestroy();
412         }
413     }
414     for (auto&& [id, bridge] : animatorBridges_) {
415         if (bridge) {
416             bridge->OnJsEngineDestroy();
417         }
418     }
419     for (auto&& [id, info] : animatorInfos_) {
420         if (info) {
421             info->OnJsEngineDestroy();
422         }
423     }
424 }
425 
426 } // namespace OHOS::Ace::Framework
427