/* * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "frameworks/bridge/js_frontend/js_ace_page.h" #include "core/components/focus_collaboration/focus_collaboration_component.h" namespace OHOS::Ace::Framework { #ifdef NG_BUILD JsAcePage::JsAcePage(int32_t pageId, const std::string& url) : AcePage(pageId), url_(url) {} #else JsAcePage::JsAcePage(int32_t pageId, const RefPtr& document, const std::string& url, const WeakPtr& container) : AcePage(pageId), domDoc_(document), url_(url), container_(container), radioGroups_(std::make_shared()) { ACE_DCHECK(domDoc_); } #endif JsAcePage::~JsAcePage() { LOG_DESTROY(); #ifndef NG_BUILD auto pipelineContext = pipelineContext_.Upgrade(); if (!pipelineContext) { return; } auto taskExecutor = pipelineContext->GetTaskExecutor(); if (!taskExecutor) { LOGE("taskExecutor not exists"); return; } auto accessibilityManager = pipelineContext->GetAccessibilityManager(); RefPtr domDoc; domDoc.Swap(domDoc_); auto weakDom = AceType::WeakClaim(AceType::RawPtr(domDoc)); auto weakAcc = AceType::WeakClaim(AceType::RawPtr(accessibilityManager)); taskExecutor->PostTask( [weakDom, weakAcc] { auto domDoc = weakDom.Upgrade(); auto accessibilityManager = weakAcc.Upgrade(); if (domDoc && accessibilityManager) { accessibilityManager->ClearPageAccessibilityNodes(domDoc->GetRootNodeId()); } }, TaskExecutor::TaskType::UI, "ArkUIPageClearAccessibilityNodes"); // Release Dom and Components in UI thread RefPtr pageTransition; pageTransition.Swap(pageTransition_); RefPtr component; component.Swap(component_); std::shared_ptr radioGroups; radioGroups.swap(radioGroups_); taskExecutor->PostTask( [domDoc, pageTransition, component, radioGroups]() mutable { LOGI("release Dom and Components on UI thread"); domDoc.Reset(); pageTransition.Reset(); component.Reset(); radioGroups.reset(); }, TaskExecutor::TaskType::UI, "ArkUIPageReleaseDomAndComponent"); #endif } RefPtr JsAcePage::BuildPage(const std::string& url) { #ifdef NG_BUILD return nullptr; #else CHECK_RUN_ON(UI); auto pageId = GetPageId(); auto rootStack = domDoc_->GetRootStackComponent(); auto rootComposedStack = domDoc_->GetRootComposedStack(); auto focusCollaboration = AceType::MakeRefPtr(true); if (container_.Upgrade()) { if (component_) { return AceType::MakeRefPtr(pageId, url, component_); } else if (rootComposedStack) { return AceType::MakeRefPtr(pageId, url, rootComposedStack); } } if (!pageTransition_) { pageTransition_ = AceType::MakeRefPtr(); } if ((!rootStack || !rootComposedStack) && !component_) { LOGW("Page[%{public}d] can't be loaded. no root component.", pageId); pageTransition_->SetContent(nullptr); } else { if (component_) { focusCollaboration->InsertChild(0, component_); } else if (rootComposedStack) { focusCollaboration->InsertChild(0, rootComposedStack); } pageTransition_->SetContent(focusCollaboration); if ((SystemProperties::GetDeviceType() == DeviceType::TV) && (!pageTransition_->GetIsSetOption())) { pageTransition_->SetSeparation(true); SwapBackgroundDecoration(pageTransition_); } } bool isDeclarative = false; auto context = pipelineContext_.Upgrade(); if (context && context->GetIsDeclarative()) { isDeclarative = true; } const std::string& cardComposeId = GetCardId(); if (!cardComposeId.empty()) { return AceType::MakeRefPtr( pageId, url, cardComposeId, isDeclarative ? std::move(pageTransition_) : pageTransition_); } return AceType::MakeRefPtr( pageId, url, isDeclarative ? std::move(pageTransition_) : pageTransition_); #endif } std::string JsAcePage::GetCardId() const { std::unique_ptr argsValue = JsonUtil::ParseJsonString(pageParams_); if (argsValue && argsValue->IsObject()) { // support old JSON structure as { "ref": value} if (!argsValue->GetString(DOM_TRANSITION_CARD_COMPOSEID).empty()) { return argsValue->GetString(DOM_TRANSITION_CARD_COMPOSEID); } // support new JSON structure as { "paramsData": { "ref": value } } const auto& paramsData = argsValue->GetObject(DOM_TRANSITION_CARD_PARAMS); if (paramsData->IsObject() && !paramsData->GetString(DOM_TRANSITION_CARD_COMPOSEID).empty()) { return paramsData->GetString(DOM_TRANSITION_CARD_COMPOSEID); } } return ""; } #ifndef NG_BUILD RefPtr JsAcePage::BuildPagePatch(int32_t nodeId) { CHECK_RUN_ON(UI); RefPtr dirtyComponent = domDoc_->GetComponentById(nodeId); if (!dirtyComponent) { LOGE("Node[%{public}d] can't be reached.", nodeId); return nullptr; } auto composedComponent = AceType::DynamicCast(dirtyComponent); ACE_DCHECK(composedComponent); return composedComponent; } void JsAcePage::SwapBackgroundDecoration(const RefPtr& transition) { CHECK_RUN_ON(UI); if (!transition) { LOGW("swap background decoration failed. transition is null."); return; } auto rootNode = domDoc_->GetDOMNodeById(DOM_ROOT_NODE_ID_BASE + GetPageId()); if (!rootNode) { LOGW("swap background decoration failed. root node is null."); return; } auto box = rootNode->GetBoxComponent(); if (!box) { LOGW("swap background decoration failed. box is null."); return; } auto decoration = box->GetBackDecoration(); if (!decoration) { LOGW("swap background decoration failed. decoration is null."); return; } auto backgroundBox = AceType::MakeRefPtr(); backgroundBox->SetBackDecoration(decoration); backgroundBox->SetWidth(box->GetWidthDimension().Value(), box->GetWidthDimension().Unit()); backgroundBox->SetHeight(box->GetHeightDimension().Value(), box->GetHeightDimension().Unit()); backgroundBox->SetFlex(BoxFlex::FLEX_XY); transition->SetBackground(backgroundBox); box->SetBackDecoration(nullptr); } #endif RefPtr JsAcePage::GetBridgeById(NodeId nodeId) { std::unique_lock lock(bridgeMutex_); auto iter = canvasBridges_.find(nodeId); if (iter == canvasBridges_.end()) { LOGE("the canvas is not in the map"); return nullptr; } return iter->second; } RefPtr JsAcePage::GetOffscreenCanvasBridgeById(int32_t bridgeId) { auto iter = offscreenCanvasBridges_.find(bridgeId); if (iter == offscreenCanvasBridges_.end()) { LOGE("the canvas is not in the map"); return nullptr; } return iter->second; } RefPtr JsAcePage::GetXComponentBridgeById(NodeId nodeId) { auto iter = xcomponentBridges_.find(nodeId); if (iter == xcomponentBridges_.end()) { LOGE("the XComponent is not in the map"); return nullptr; } return iter->second; } RefPtr JsAcePage::GetAnimationBridge(NodeId nodeId) { std::unique_lock lock(bridgeMutex_); auto bridge = animationBridges_.find(nodeId); if (bridge == animationBridges_.end()) { LOGW("the animation bridge is not in the map, nodeId: %{public}d", nodeId); return nullptr; } return bridge->second; } void JsAcePage::RemoveAnimationBridge(NodeId nodeId) { RefPtr bridge; { std::unique_lock lock(bridgeMutex_); auto pos = animationBridges_.find(nodeId); if (pos != animationBridges_.end()) { bridge.Swap(pos->second); animationBridges_.erase(pos); } } if (bridge) { auto pipelineContext = pipelineContext_.Upgrade(); if (!pipelineContext) { LOGE("pipelineContext is nullptr"); return; } auto taskExecutor = pipelineContext->GetTaskExecutor(); if (!taskExecutor) { LOGE("taskExecutor is nullptr"); return; } taskExecutor->PostSyncTask([&bridge]() { bridge.Reset(); }, TaskExecutor::TaskType::JS, "ArkUIReleaseAnimationBridge"); } } void JsAcePage::AddAnimationBridge(NodeId nodeId, const RefPtr& animationBridge) { if (!animationBridge) { LOGE("AddAnimationBridge failed. Animation bridge is null."); return; } std::unique_lock lock(bridgeMutex_); animationBridges_[nodeId] = animationBridge; } void JsAcePage::AddAnimatorBridge(int32_t bridgeId, const RefPtr& animatorBridge) { if (!animatorBridge) { LOGE("AddAnimationBridge failed. Animation bridge is null."); return; } auto animator = animatorBridge->JsGetAnimator(); if (!animator) { LOGE("animator is null"); return; } animator->AttachScheduler(pipelineContext_); std::unique_lock lock(bridgeMutex_); animatorBridges_[bridgeId] = animatorBridge; } void JsAcePage::RemoveAnimatorBridge(int32_t bridgeId) { std::unique_lock lock(bridgeMutex_); animatorBridges_.erase(bridgeId); } RefPtr JsAcePage::GetAnimatorBridge(int32_t bridgeId) { std::unique_lock lock(bridgeMutex_); auto bridge = animatorBridges_.find(bridgeId); if (bridge == animatorBridges_.end()) { LOGW("the animation bridge is not in the map, nodeId: %{public}d", bridgeId); return nullptr; } return bridge->second; } void JsAcePage::AddAnimatorInfo(const std::string& animatorId, const RefPtr& animatorInfo) { if (!animatorInfo) { LOGE("AddAnimation failed. Animation is null."); return; } auto animator = animatorInfo->GetAnimator(); if (!animator) { LOGE("animator is null"); return; } animator->AttachScheduler(pipelineContext_); animatorInfos_[animatorId] = animatorInfo; } void JsAcePage::RemoveAnimatorInfo(const std::string& animatorId) { animatorInfos_.erase(animatorId); } RefPtr JsAcePage::GetAnimatorInfo(const std::string& animatorId) { auto bridge = animatorInfos_.find(animatorId); if (bridge == animatorInfos_.end()) { LOGW("the animation bridge is not in the map, animatorId: %{public}s", animatorId.c_str()); return nullptr; } return bridge->second; } void JsAcePage::PushCanvasBridge(NodeId nodeId, const RefPtr& bridge) { if (!bridge) { LOGE("PushCanvasBridge failed. Canvas bridge is null."); return; } std::unique_lock lock(bridgeMutex_); canvasBridges_[nodeId] = bridge; } void JsAcePage::PushOffscreenCanvasBridge(int32_t bridgeId, const RefPtr& bridge) { offscreenCanvasBridges_[bridgeId] = bridge; } void JsAcePage::PushXComponentBridge(NodeId nodeId, const RefPtr& bridge) { if (!bridge) { LOGE("PushXComponentBridge failed. XComponent bridge is null."); return; } xcomponentBridges_[nodeId] = bridge; } void JsAcePage::AddNodeEvent(int32_t nodeId, const std::string& actionType, const std::string& eventAction) { std::unique_lock lock(eventMutex_); nodeEvent_[nodeId][actionType] = eventAction; } std::string JsAcePage::GetNodeEventAction(int32_t nodeId, const std::string& actionType) { // in error case just use empty string. std::unique_lock lock(eventMutex_); return nodeEvent_[nodeId][actionType]; } void JsAcePage::SetRootNode(const RefPtr& node) { pageRootNode_ = node; } #ifndef NG_BUILD std::shared_ptr JsAcePage::GetRadioGroups() { return radioGroups_; } #endif void JsAcePage::OnJsEngineDestroy() { std::unique_lock lock(bridgeMutex_); for (auto&& [id, bridge] : animationBridges_) { if (bridge) { bridge->OnJsEngineDestroy(); } } for (auto&& [id, bridge] : canvasBridges_) { if (bridge) { bridge->OnJsEngineDestroy(); } } for (auto&& [id, bridge] : xcomponentBridges_) { if (bridge) { bridge->OnJsEngineDestroy(); } } for (auto&& [id, bridge] : animatorBridges_) { if (bridge) { bridge->OnJsEngineDestroy(); } } for (auto&& [id, info] : animatorInfos_) { if (info) { info->OnJsEngineDestroy(); } } } } // namespace OHOS::Ace::Framework