• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 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 "core/components_ng/pattern/stage/stage_manager.h"
17 
18 #include <unordered_map>
19 
20 #include "base/geometry/ng/size_t.h"
21 #include "base/log/ace_checker.h"
22 #include "base/log/ace_performance_check.h"
23 #include "base/perfmonitor/perf_monitor.h"
24 #include "base/perfmonitor/perf_constants.h"
25 #include "base/memory/referenced.h"
26 #include "base/utils/time_util.h"
27 #include "base/utils/utils.h"
28 #include "core/animation/page_transition_common.h"
29 #include "core/common/container.h"
30 #include "core/components/common/layout/constants.h"
31 #include "core/components_ng/base/frame_node.h"
32 #include "core/components_ng/base/ui_node.h"
33 #include "core/components_ng/manager/shared_overlay/shared_overlay_manager.h"
34 #include "core/components_ng/pattern/overlay/overlay_manager.h"
35 #include "core/components_ng/pattern/stage/page_pattern.h"
36 #include "core/components_ng/pattern/stage/stage_pattern.h"
37 #include "core/components_ng/property/property.h"
38 #include "core/components_v2/inspector/inspector_constants.h"
39 #include "core/pipeline_ng/pipeline_context.h"
40 #include "core/pipeline_ng/ui_task_scheduler.h"
41 
42 namespace OHOS::Ace::NG {
43 
44 namespace {
FirePageTransition(const RefPtr<FrameNode> & page,PageTransitionType transitionType)45 void FirePageTransition(const RefPtr<FrameNode>& page, PageTransitionType transitionType)
46 {
47     CHECK_NULL_VOID(page);
48     auto pagePattern = page->GetPattern<PagePattern>();
49     CHECK_NULL_VOID(pagePattern);
50     page->GetEventHub<EventHub>()->SetEnabled(false);
51     pagePattern->SetPageInTransition(true);
52     if (transitionType == PageTransitionType::EXIT_PUSH || transitionType == PageTransitionType::EXIT_POP) {
53         pagePattern->TriggerPageTransition(
54             transitionType, [weak = WeakPtr<FrameNode>(page), transitionType, instanceId = Container::CurrentId()]() {
55                 ContainerScope scope(instanceId);
56                 LOGI("pageTransition exit finish");
57                 auto context = PipelineContext::GetCurrentContext();
58                 CHECK_NULL_VOID(context);
59                 auto taskExecutor = context->GetTaskExecutor();
60                 CHECK_NULL_VOID(taskExecutor);
61                 taskExecutor->PostSyncTask(
62                     [weak, weakContext = WeakPtr<PipelineContext>(context), transitionType]() {
63                         auto page = weak.Upgrade();
64                         CHECK_NULL_VOID(page);
65                         auto context = weakContext.Upgrade();
66                         CHECK_NULL_VOID(context);
67                         auto pageFocusHub = page->GetFocusHub();
68                         CHECK_NULL_VOID(pageFocusHub);
69                         pageFocusHub->SetParentFocusable(false);
70                         pageFocusHub->LostFocus();
71                         if (transitionType == PageTransitionType::EXIT_POP && page->GetParent()) {
72                             auto stageNode = page->GetParent();
73                             stageNode->RemoveChild(page);
74                             stageNode->RebuildRenderContextTree();
75                             context->RequestFrame();
76                             return;
77                         }
78                         page->GetEventHub<EventHub>()->SetEnabled(true);
79                         auto pattern = page->GetPattern<PagePattern>();
80                         CHECK_NULL_VOID(pattern);
81                         pattern->SetPageInTransition(false);
82                         pattern->ProcessHideState();
83                         context->MarkNeedFlushMouseEvent();
84                     },
85                     TaskExecutor::TaskType::UI);
86             });
87         return;
88     }
89     PerfMonitor::GetPerfMonitor()->Start(PerfConstants::ABILITY_OR_PAGE_SWITCH, PerfActionType::LAST_UP, "NA");
90     pagePattern->TriggerPageTransition(
91         transitionType, [weak = WeakPtr<FrameNode>(page), instanceId = Container::CurrentId()]() {
92             ContainerScope scope(instanceId);
93             PerfMonitor::GetPerfMonitor()->End(PerfConstants::ABILITY_OR_PAGE_SWITCH, false);
94             LOGI("pageTransition in finish");
95             auto page = weak.Upgrade();
96             CHECK_NULL_VOID(page);
97             page->GetEventHub<EventHub>()->SetEnabled(true);
98             auto pattern = page->GetPattern<PagePattern>();
99             CHECK_NULL_VOID(pattern);
100             pattern->SetPageInTransition(false);
101 
102             auto pageFocusHub = page->GetFocusHub();
103             CHECK_NULL_VOID(pageFocusHub);
104             pageFocusHub->SetParentFocusable(true);
105             pageFocusHub->RequestFocusWithDefaultFocusFirstly();
106             auto context = PipelineContext::GetCurrentContext();
107             CHECK_NULL_VOID(context);
108             context->MarkNeedFlushMouseEvent();
109         });
110 }
111 } // namespace
112 
StartTransition(const RefPtr<FrameNode> & srcPage,const RefPtr<FrameNode> & destPage,RouteType type)113 void StageManager::StartTransition(const RefPtr<FrameNode>& srcPage, const RefPtr<FrameNode>& destPage, RouteType type)
114 {
115     auto pipeline = PipelineContext::GetCurrentContext();
116     CHECK_NULL_VOID(pipeline);
117     auto sharedManager = pipeline->GetSharedOverlayManager();
118     CHECK_NULL_VOID(sharedManager);
119     sharedManager->StartSharedTransition(srcPage, destPage);
120     srcPageNode_ = srcPage;
121     destPageNode_ = destPage;
122     if (type == RouteType::PUSH) {
123         FirePageTransition(srcPage, PageTransitionType::EXIT_PUSH);
124         FirePageTransition(destPage, PageTransitionType::ENTER_PUSH);
125     } else if (type == RouteType::POP) {
126         FirePageTransition(srcPage, PageTransitionType::EXIT_POP);
127         FirePageTransition(destPage, PageTransitionType::ENTER_POP);
128     }
129 }
130 
StageManager(const RefPtr<FrameNode> & stage)131 StageManager::StageManager(const RefPtr<FrameNode>& stage) : stageNode_(stage)
132 {
133     CHECK_NULL_VOID(stageNode_);
134     stagePattern_ = DynamicCast<StagePattern>(stageNode_->GetPattern());
135 }
136 
StopPageTransition()137 void StageManager::StopPageTransition()
138 {
139     auto srcNode = srcPageNode_.Upgrade();
140     if (srcNode) {
141         auto pattern = srcNode->GetPattern<PagePattern>();
142         pattern->StopPageTransition();
143         srcPageNode_ = nullptr;
144     }
145     auto destNode = destPageNode_.Upgrade();
146     if (destNode) {
147         auto pattern = destNode->GetPattern<PagePattern>();
148         pattern->StopPageTransition();
149         destPageNode_ = nullptr;
150     }
151 }
152 
PushPage(const RefPtr<FrameNode> & node,bool needHideLast,bool needTransition)153 bool StageManager::PushPage(const RefPtr<FrameNode>& node, bool needHideLast, bool needTransition)
154 {
155     CHECK_NULL_RETURN(stageNode_, false);
156     CHECK_NULL_RETURN(node, false);
157     int64_t startTime = GetSysTimestamp();
158     auto pipeline = AceType::DynamicCast<NG::PipelineContext>(PipelineBase::GetCurrentContext());
159     CHECK_NULL_RETURN(pipeline, false);
160     StopPageTransition();
161 
162     const auto& children = stageNode_->GetChildren();
163     RefPtr<FrameNode> outPageNode;
164     needTransition &= !children.empty();
165     if (needTransition) {
166         pipeline->FlushPipelineImmediately();
167     }
168     if (!children.empty() && needHideLast) {
169         FirePageHide(children.back(), needTransition ? PageTransitionType::EXIT_PUSH : PageTransitionType::NONE);
170         outPageNode = AceType::DynamicCast<FrameNode>(children.back());
171     }
172     auto rect = stageNode_->GetGeometryNode()->GetFrameRect();
173     rect.SetOffset({});
174     node->GetRenderContext()->SyncGeometryProperties(rect);
175     // mount to parent and mark build render tree.
176     node->MountToParent(stageNode_);
177     // then build the total child.
178     node->Build();
179     stageNode_->RebuildRenderContextTree();
180     FirePageShow(node, needTransition ? PageTransitionType::ENTER_PUSH : PageTransitionType::NONE);
181 
182     auto pagePattern = node->GetPattern<PagePattern>();
183     CHECK_NULL_RETURN(pagePattern, false);
184     stagePattern_->currentPageIndex_ = pagePattern->GetPageInfo()->GetPageId();
185     if (AceChecker::IsPerformanceCheckEnabled()) {
186         // After completing layout tasks at all nodes on the page, perform performance testing and management
187         pipeline->AddAfterLayoutTask([weakStage = WeakClaim(this), weakNode = WeakPtr<FrameNode>(node), startTime]() {
188             auto stage = weakStage.Upgrade();
189             CHECK_NULL_VOID(stage);
190             auto pageNode = weakNode.Upgrade();
191             int64_t endTime = GetSysTimestamp();
192             stage->PerformanceCheck(pageNode, endTime - startTime);
193         });
194     }
195     if (needTransition) {
196         pipeline->AddAfterLayoutTask([weakStage = WeakClaim(this), weakIn = WeakPtr<FrameNode>(node),
197                                          weakOut = WeakPtr<FrameNode>(outPageNode)]() {
198             auto stage = weakStage.Upgrade();
199             CHECK_NULL_VOID(stage);
200             auto inPageNode = weakIn.Upgrade();
201             auto outPageNode = weakOut.Upgrade();
202             stage->StartTransition(outPageNode, inPageNode, RouteType::PUSH);
203         });
204     }
205 
206     // flush layout task.
207     if (!stageNode_->GetGeometryNode()->GetMarginFrameSize().IsPositive()) {
208         // in first load case, wait for window size.
209         LOGI("waiting for window size");
210         return true;
211     }
212     stageNode_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
213     node->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
214 
215     return true;
216 }
217 
PerformanceCheck(const RefPtr<FrameNode> & pageNode,int64_t vsyncTimeout)218 void StageManager::PerformanceCheck(const RefPtr<FrameNode>& pageNode, int64_t vsyncTimeout)
219 {
220     CHECK_NULL_VOID_NOLOG(pageNode);
221     PerformanceCheckNodeMap nodeMap;
222     pageNode->GetPerformanceCheckData(nodeMap);
223     AceScopedPerformanceCheck::RecordPerformanceCheckData(nodeMap, vsyncTimeout);
224 }
225 
PopPage(bool needShowNext,bool needTransition)226 bool StageManager::PopPage(bool needShowNext, bool needTransition)
227 {
228     auto pipeline = PipelineContext::GetCurrentContext();
229     CHECK_NULL_RETURN(pipeline, false);
230     CHECK_NULL_RETURN(stageNode_, false);
231     StopPageTransition();
232     const auto& children = stageNode_->GetChildren();
233     if (children.empty()) {
234         LOGE("fail to pop page due to children is null");
235         return false;
236     }
237     auto pageNode = children.back();
238     const size_t transitionPageSize = 2;
239     needTransition &= (children.size() >= transitionPageSize);
240     if (needTransition) {
241         pipeline->FlushPipelineImmediately();
242     }
243     FirePageHide(pageNode, needTransition ? PageTransitionType::EXIT_POP : PageTransitionType::NONE);
244 
245     RefPtr<FrameNode> inPageNode;
246     if (needShowNext && children.size() >= transitionPageSize) {
247         auto newPageNode = *(++children.rbegin());
248         FirePageShow(newPageNode, needTransition ? PageTransitionType::ENTER_POP : PageTransitionType::NONE);
249         inPageNode = AceType::DynamicCast<FrameNode>(newPageNode);
250     }
251 
252     auto outPageNode = AceType::DynamicCast<FrameNode>(pageNode);
253     if (needTransition) {
254         StartTransition(outPageNode, inPageNode, RouteType::POP);
255         inPageNode->OnAccessibilityEvent(AccessibilityEventType::CHANGE);
256         return true;
257     }
258     stageNode_->RemoveChild(pageNode);
259     pageNode->SetChildrenInDestroying();
260     stageNode_->RebuildRenderContextTree();
261     pipeline->RequestFrame();
262     return true;
263 }
264 
PopPageToIndex(int32_t index,bool needShowNext,bool needTransition)265 bool StageManager::PopPageToIndex(int32_t index, bool needShowNext, bool needTransition)
266 {
267     auto pipeline = PipelineContext::GetCurrentContext();
268     CHECK_NULL_RETURN(pipeline, false);
269     CHECK_NULL_RETURN(stageNode_, false);
270     StopPageTransition();
271     const auto& children = stageNode_->GetChildren();
272     if (children.empty()) {
273         LOGE("fail to pop page due to children is null");
274         return false;
275     }
276     int32_t popSize = static_cast<int32_t>(children.size()) - index - 1;
277     if (popSize < 0) {
278         LOGE("fail to pop page due to index is out of range");
279         return false;
280     }
281     if (popSize == 0) {
282         LOGD("already here");
283         return true;
284     }
285 
286     if (needTransition) {
287         pipeline->FlushPipelineImmediately();
288     }
289     bool firstPageTransition = true;
290     auto outPageNode = AceType::DynamicCast<FrameNode>(children.back());
291     auto iter = children.rbegin();
292     for (int32_t current = 0; current < popSize; ++current) {
293         auto pageNode = *iter;
294         FirePageHide(
295             pageNode, firstPageTransition && needTransition ? PageTransitionType::EXIT_POP : PageTransitionType::NONE);
296         firstPageTransition = false;
297         ++iter;
298     }
299 
300     RefPtr<FrameNode> inPageNode;
301     if (needShowNext) {
302         const auto& newPageNode = *iter;
303         FirePageShow(newPageNode, needTransition ? PageTransitionType::ENTER_POP : PageTransitionType::NONE);
304         inPageNode = AceType::DynamicCast<FrameNode>(newPageNode);
305     }
306 
307     if (needTransition) {
308         // from the penultimate node, (popSize - 1) nodes are deleted.
309         // the last node will be deleted after pageTransition
310         LOGI("PopPageToIndex, before pageTransition, to index:%{public}d, children size:%{public}zu, "
311              "stage children size:%{public}zu",
312             index, children.size(), stageNode_->GetChildren().size());
313         for (int32_t current = 1; current < popSize; ++current) {
314             auto pageNode = *(++children.rbegin());
315             stageNode_->RemoveChild(pageNode);
316         }
317         stageNode_->RebuildRenderContextTree();
318         StartTransition(outPageNode, inPageNode, RouteType::POP);
319         return true;
320     }
321     for (int32_t current = 0; current < popSize; ++current) {
322         auto pageNode = children.back();
323         stageNode_->RemoveChild(pageNode);
324     }
325     stageNode_->RebuildRenderContextTree();
326     pipeline->RequestFrame();
327     return true;
328 }
329 
CleanPageStack()330 bool StageManager::CleanPageStack()
331 {
332     auto pipeline = PipelineContext::GetCurrentContext();
333     CHECK_NULL_RETURN(pipeline, false);
334     CHECK_NULL_RETURN(stageNode_, false);
335     const auto& children = stageNode_->GetChildren();
336     if (children.size() <= 1) {
337         LOGE("fail to clean page stack due to children size is illegal");
338         return false;
339     }
340     auto popSize = static_cast<int32_t>(children.size() - 1);
341     for (int32_t count = 1; count <= popSize; ++count) {
342         auto pageNode = children.front();
343         // mark pageNode child as destroying
344         pageNode->SetChildrenInDestroying();
345         stageNode_->RemoveChild(pageNode);
346     }
347     stageNode_->RebuildRenderContextTree();
348     pipeline->RequestFrame();
349     return true;
350 }
351 
MovePageToFront(const RefPtr<FrameNode> & node,bool needHideLast,bool needTransition)352 bool StageManager::MovePageToFront(const RefPtr<FrameNode>& node, bool needHideLast, bool needTransition)
353 {
354     auto pipeline = PipelineContext::GetCurrentContext();
355     CHECK_NULL_RETURN(pipeline, false);
356     CHECK_NULL_RETURN(stageNode_, false);
357     StopPageTransition();
358     const auto& children = stageNode_->GetChildren();
359     if (children.empty()) {
360         LOGE("child is empty");
361         return false;
362     }
363     const auto& lastPage = children.back();
364     if (lastPage == node) {
365         LOGD("page already on the top");
366         return true;
367     }
368     if (needTransition) {
369         pipeline->FlushPipelineImmediately();
370     }
371     if (needHideLast) {
372         FirePageHide(lastPage, needTransition ? PageTransitionType::EXIT_PUSH : PageTransitionType::NONE);
373     }
374     node->MovePosition(static_cast<int32_t>(stageNode_->GetChildren().size() - 1));
375     node->GetRenderContext()->ResetPageTransitionEffect();
376     FirePageShow(node, needTransition ? PageTransitionType::ENTER_PUSH : PageTransitionType::NONE);
377 
378     stageNode_->RebuildRenderContextTree();
379     if (needTransition) {
380         auto outPageNode = AceType::DynamicCast<FrameNode>(lastPage);
381         StartTransition(outPageNode, node, RouteType::PUSH);
382     }
383     pipeline->RequestFrame();
384     return true;
385 }
386 
FirePageHide(const RefPtr<UINode> & node,PageTransitionType transitionType)387 void StageManager::FirePageHide(const RefPtr<UINode>& node, PageTransitionType transitionType)
388 {
389     auto pageNode = DynamicCast<FrameNode>(node);
390     CHECK_NULL_VOID(pageNode);
391     auto pagePattern = pageNode->GetPattern<PagePattern>();
392     CHECK_NULL_VOID(pagePattern);
393     pagePattern->OnHide();
394     if (transitionType == PageTransitionType::NONE) {
395         // If there is a page transition, this function should execute after page transition,
396         // otherwise the page will not be visible
397         pagePattern->ProcessHideState();
398     }
399 
400     auto pageFocusHub = pageNode->GetFocusHub();
401     CHECK_NULL_VOID(pageFocusHub);
402     pageFocusHub->SetParentFocusable(false);
403     pageFocusHub->LostFocus();
404 
405     auto context = PipelineContext::GetCurrentContext();
406     CHECK_NULL_VOID_NOLOG(context);
407     context->MarkNeedFlushMouseEvent();
408 }
409 
FirePageShow(const RefPtr<UINode> & node,PageTransitionType transitionType)410 void StageManager::FirePageShow(const RefPtr<UINode>& node, PageTransitionType transitionType)
411 {
412     auto pageNode = DynamicCast<FrameNode>(node);
413     CHECK_NULL_VOID(pageNode);
414     auto layoutProperty = pageNode->GetLayoutProperty();
415     auto pageFocusHub = pageNode->GetFocusHub();
416     CHECK_NULL_VOID(pageFocusHub);
417     pageFocusHub->SetParentFocusable(true);
418     pageFocusHub->RequestFocusWithDefaultFocusFirstly();
419 
420     auto pagePattern = pageNode->GetPattern<PagePattern>();
421     CHECK_NULL_VOID(pagePattern);
422     pagePattern->OnShow();
423     // With or without a page transition, we need to make the coming page visible first
424     pagePattern->ProcessShowState();
425 
426     auto context = PipelineContext::GetCurrentContext();
427     CHECK_NULL_VOID_NOLOG(context);
428     context->MarkNeedFlushMouseEvent();
429 #ifdef UICAST_COMPONENT_SUPPORTED
430     do {
431         auto container = Container::Current();
432         CHECK_NULL_BREAK(container);
433         auto distributedUI = container->GetDistributedUI();
434         CHECK_NULL_BREAK(distributedUI);
435         distributedUI->OnPageChanged(node->GetPageId());
436     } while (false);
437 #endif
438 }
439 
GetLastPage()440 RefPtr<FrameNode> StageManager::GetLastPage()
441 {
442     CHECK_NULL_RETURN(stageNode_, nullptr);
443     const auto& children = stageNode_->GetChildren();
444     if (children.empty()) {
445         LOGE("fail to return page due to children is null");
446         return nullptr;
447     }
448     return DynamicCast<FrameNode>(children.back());
449 }
450 
GetPageById(int32_t pageId)451 RefPtr<FrameNode> StageManager::GetPageById(int32_t pageId)
452 {
453     CHECK_NULL_RETURN(stageNode_, nullptr);
454     const auto& children = stageNode_->GetChildren();
455     for (const auto& child : children) {
456         if (child->GetPageId() == pageId) {
457             return DynamicCast<FrameNode>(child);
458         }
459     }
460     LOGD("UITree page not found. %{public}d", pageId);
461     return nullptr;
462 }
463 
ReloadStage()464 void StageManager::ReloadStage()
465 {
466     CHECK_NULL_VOID(stageNode_);
467     const auto& children = stageNode_->GetChildren();
468     for (const auto& child : children) {
469         auto frameNode = DynamicCast<FrameNode>(child);
470         if (!frameNode) {
471             continue;
472         }
473         auto pagePattern = frameNode->GetPattern<PagePattern>();
474         if (!pagePattern) {
475             continue;
476         }
477         pagePattern->ReloadPage();
478     }
479 }
480 
481 } // namespace OHOS::Ace::NG
482