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