* 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/accessibility/accessibility_manager.h"
#include "core/components/focus_collaboration/focus_collaboration_component.h"
#include "core/components_ng/base/ui_node.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<DOMDocument>& document, const std::string& url,
const WeakPtr<StageElement>& container)
: AcePage(pageId), domDoc_(document), url_(url), container_(container),
radioGroups_(std::make_shared<JsPageRadioGroups>())
{
ACE_DCHECK(domDoc_);
}
#endif
JsAcePage::~JsAcePage()
{
LOGI("Page destroyed");
#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<DOMDocument> 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");
RefPtr<PageTransitionComponent> pageTransition;
pageTransition.Swap(pageTransition_);
RefPtr<Component> component;
component.Swap(component_);
std::shared_ptr<JsPageRadioGroups> 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<PageComponent> 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<FocusCollaborationComponent>(true);
if (container_.Upgrade()) {
if (component_) {
return AceType::MakeRefPtr<PageComponent>(pageId, url, component_);
} else if (rootComposedStack) {
return AceType::MakeRefPtr<PageComponent>(pageId, url, rootComposedStack);
}
}
if (!pageTransition_) {
pageTransition_ = AceType::MakeRefPtr<PageTransitionComponent>();
}
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<PageComponent>(
pageId, url, cardComposeId, isDeclarative ? std::move(pageTransition_) : pageTransition_);
}
return AceType::MakeRefPtr<PageComponent>(
pageId, url, isDeclarative ? std::move(pageTransition_) : pageTransition_);
#endif
}
std::string JsAcePage::GetCardId() const
{
std::unique_ptr<JsonValue> argsValue = JsonUtil::ParseJsonString(pageParams_);
if (argsValue && argsValue->IsObject()) {
if (!argsValue->GetString(DOM_TRANSITION_CARD_COMPOSEID).empty()) {
return argsValue->GetString(DOM_TRANSITION_CARD_COMPOSEID);
}
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<ComposedComponent> JsAcePage::BuildPagePatch(int32_t nodeId)
{
CHECK_RUN_ON(UI);
RefPtr<Component> dirtyComponent = domDoc_->GetComponentById(nodeId);
if (!dirtyComponent) {
LOGE("Node[%{public}d] can't be reached.", nodeId);
return nullptr;
}
auto composedComponent = AceType::DynamicCast<ComposedComponent>(dirtyComponent);
ACE_DCHECK(composedComponent);
return composedComponent;
}
void JsAcePage::SwapBackgroundDecoration(const RefPtr<PageTransitionComponent>& 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<BoxComponent>();
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<BaseCanvasBridge> JsAcePage::GetBridgeById(NodeId nodeId)
{
std::unique_lock<std::mutex> 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<BaseCanvasBridge> 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<BaseXComponentBridge> 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<BaseAnimationBridge> JsAcePage::GetAnimationBridge(NodeId nodeId)
{
std::unique_lock<std::mutex> 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<BaseAnimationBridge> bridge;
{
std::unique_lock<std::mutex> 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<BaseAnimationBridge>& animationBridge)
{
if (!animationBridge) {
LOGE("AddAnimationBridge failed. Animation bridge is null.");
return;
}
std::unique_lock<std::mutex> lock(bridgeMutex_);
animationBridges_[nodeId] = animationBridge;
}
void JsAcePage::AddAnimatorBridge(int32_t bridgeId, const RefPtr<BaseAnimationBridge>& 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<std::mutex> lock(bridgeMutex_);
animatorBridges_[bridgeId] = animatorBridge;
}
void JsAcePage::RemoveAnimatorBridge(int32_t bridgeId)
{
std::unique_lock<std::mutex> lock(bridgeMutex_);
animatorBridges_.erase(bridgeId);
}
RefPtr<BaseAnimationBridge> JsAcePage::GetAnimatorBridge(int32_t bridgeId)
{
std::unique_lock<std::mutex> 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>& 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<AnimatorInfo> 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<BaseCanvasBridge>& bridge)
{
if (!bridge) {
LOGE("PushCanvasBridge failed. Canvas bridge is null.");
return;
}
std::unique_lock<std::mutex> lock(bridgeMutex_);
canvasBridges_[nodeId] = bridge;
}
void JsAcePage::PushOffscreenCanvasBridge(int32_t bridgeId, const RefPtr<BaseCanvasBridge>& bridge)
{
offscreenCanvasBridges_[bridgeId] = bridge;
}
void JsAcePage::PushXComponentBridge(NodeId nodeId, const RefPtr<BaseXComponentBridge>& 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<std::mutex> lock(eventMutex_);
nodeEvent_[nodeId][actionType] = eventAction;
}
std::string JsAcePage::GetNodeEventAction(int32_t nodeId, const std::string& actionType)
{
std::unique_lock<std::mutex> lock(eventMutex_);
return nodeEvent_[nodeId][actionType];
}
void JsAcePage::SetRootNode(const RefPtr<NG::UINode>& node)
{
pageRootNode_ = node;
}
#ifndef NG_BUILD
std::shared_ptr<JsPageRadioGroups> JsAcePage::GetRadioGroups()
{
return radioGroups_;
}
#endif
void JsAcePage::OnJsEngineDestroy()
{
std::unique_lock<std::mutex> 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();
}
}
}
}