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