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