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