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