• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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/page_pattern.h"
17 
18 #include "base/log/jank_frame_report.h"
19 #include "base/log/log_wrapper.h"
20 #include "base/perfmonitor/perf_monitor.h"
21 #include "base/utils/time_util.h"
22 #include "base/utils/utils.h"
23 #include "core/animation/animator.h"
24 #include "core/common/container.h"
25 #include "core/common/recorder/event_recorder.h"
26 #include "core/components/common/properties/alignment.h"
27 #include "core/pipeline_ng/pipeline_context.h"
28 #include "core/components_ng/base/observer_handler.h"
29 #include "bridge/common/utils/engine_helper.h"
30 
31 namespace OHOS::Ace::NG {
32 
33 namespace {
34 constexpr int32_t INVALID_PAGE_INDEX = -1;
35 std::string KEY_PAGE_TRANSITION_PROPERTY = "pageTransitionProperty";
IterativeAddToSharedMap(const RefPtr<UINode> & node,SharedTransitionMap & map)36 void IterativeAddToSharedMap(const RefPtr<UINode>& node, SharedTransitionMap& map)
37 {
38     const auto& children = node->GetChildren();
39     for (const auto& child : children) {
40         auto frameChild = AceType::DynamicCast<FrameNode>(child);
41         if (!frameChild) {
42             IterativeAddToSharedMap(child, map);
43             continue;
44         }
45         auto id = frameChild->GetRenderContext()->GetShareId();
46         if (!id.empty()) {
47             map[id] = frameChild;
48         }
49         IterativeAddToSharedMap(frameChild, map);
50     }
51 }
52 } // namespace
53 
OnAttachToFrameNode()54 void PagePattern::OnAttachToFrameNode()
55 {
56     auto host = GetHost();
57     CHECK_NULL_VOID(host);
58     MeasureType measureType = MeasureType::MATCH_PARENT;
59     auto container = Container::Current();
60     if (container && container->IsDynamicRender()) {
61         measureType = MeasureType::MATCH_CONTENT;
62     }
63     host->GetLayoutProperty()->UpdateMeasureType(measureType);
64     host->GetLayoutProperty()->UpdateAlignment(Alignment::TOP_LEFT);
65 }
66 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & wrapper,const DirtySwapConfig &)67 bool PagePattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& wrapper, const DirtySwapConfig& /* config */)
68 {
69     if (isFirstLoad_) {
70         isFirstLoad_ = false;
71         if (firstBuildCallback_) {
72             firstBuildCallback_();
73             firstBuildCallback_ = nullptr;
74         }
75     }
76     return false;
77 }
78 
BeforeSyncGeometryProperties(const DirtySwapConfig & config)79 void PagePattern::BeforeSyncGeometryProperties(const DirtySwapConfig& config)
80 {
81     if (config.skipLayout || config.skipMeasure) {
82         return;
83     }
84     CHECK_NULL_VOID(dynamicPageSizeCallback_);
85     auto host = GetHost();
86     CHECK_NULL_VOID(host);
87     auto node = host->GetGeometryNode();
88     CHECK_NULL_VOID(node);
89     dynamicPageSizeCallback_(node->GetFrameSize());
90 }
91 
TriggerPageTransition(PageTransitionType type,const std::function<void ()> & onFinish)92 bool PagePattern::TriggerPageTransition(PageTransitionType type, const std::function<void()>& onFinish)
93 {
94     auto host = GetHost();
95     CHECK_NULL_RETURN(host, false);
96     auto renderContext = host->GetRenderContext();
97     CHECK_NULL_RETURN(renderContext, false);
98     if (pageTransitionFunc_) {
99         pageTransitionFunc_();
100     }
101     auto effect = FindPageTransitionEffect(type);
102     pageTransitionFinish_ = std::make_shared<std::function<void()>>(onFinish);
103     auto wrappedOnFinish = [weak = WeakClaim(this), sharedFinish = pageTransitionFinish_]() {
104         auto pattern = weak.Upgrade();
105         CHECK_NULL_VOID(pattern);
106         auto host = pattern->GetHost();
107         CHECK_NULL_VOID(host);
108         if (sharedFinish == pattern->pageTransitionFinish_) {
109             // ensure this is exactly the finish callback saved in pagePattern,
110             // otherwise means new pageTransition started
111             pattern->FirePageTransitionFinish();
112             host->DeleteAnimatablePropertyFloat(KEY_PAGE_TRANSITION_PROPERTY);
113         }
114     };
115     if (effect && effect->GetUserCallback()) {
116         RouteType routeType = (type == PageTransitionType::ENTER_POP || type == PageTransitionType::EXIT_POP)
117                                   ? RouteType::POP
118                                   : RouteType::PUSH;
119         host->CreateAnimatablePropertyFloat(KEY_PAGE_TRANSITION_PROPERTY, 0.0f,
120             [routeType, handler = effect->GetUserCallback()](const float& progress) { handler(routeType, progress); });
121         auto handler = effect->GetUserCallback();
122         handler(routeType, 0.0f);
123         AnimationOption option(effect->GetCurve(), effect->GetDuration());
124         option.SetDelay(effect->GetDelay());
125         AnimationUtils::OpenImplicitAnimation(option, option.GetCurve(), wrappedOnFinish);
126         host->UpdateAnimatablePropertyFloat(KEY_PAGE_TRANSITION_PROPERTY, 1.0f);
127         AnimationUtils::CloseImplicitAnimation();
128         return renderContext->TriggerPageTransition(type, nullptr);
129     }
130     return renderContext->TriggerPageTransition(type, wrappedOnFinish);
131 }
132 
ProcessAutoSave(const std::function<void ()> & onFinish,const std::function<void ()> & onUIExtNodeBindingCompleted)133 bool PagePattern::ProcessAutoSave(const std::function<void()>& onFinish,
134     const std::function<void()>& onUIExtNodeBindingCompleted)
135 {
136     auto host = GetHost();
137     CHECK_NULL_RETURN(host, false);
138     if (!host->NeedRequestAutoSave()) {
139         return false;
140     }
141     auto container = Container::Current();
142     CHECK_NULL_RETURN(container, false);
143     return container->RequestAutoSave(host, onFinish, onUIExtNodeBindingCompleted);
144 }
145 
ProcessHideState()146 void PagePattern::ProcessHideState()
147 {
148     auto host = GetHost();
149     CHECK_NULL_VOID(host);
150     host->SetActive(false);
151     host->NotifyVisibleChange(VisibleType::VISIBLE, VisibleType::INVISIBLE);
152     host->GetLayoutProperty()->UpdateVisibility(VisibleType::INVISIBLE);
153     auto parent = host->GetAncestorNodeOfFrame();
154     CHECK_NULL_VOID(parent);
155     parent->MarkNeedSyncRenderTree();
156     parent->RebuildRenderContextTree();
157 }
158 
ProcessShowState()159 void PagePattern::ProcessShowState()
160 {
161     auto host = GetHost();
162     CHECK_NULL_VOID(host);
163     host->SetActive(true);
164     host->NotifyVisibleChange(VisibleType::INVISIBLE, VisibleType::VISIBLE);
165     host->GetLayoutProperty()->UpdateVisibility(VisibleType::VISIBLE);
166     auto parent = host->GetAncestorNodeOfFrame();
167     CHECK_NULL_VOID(parent);
168     auto context = NG::PipelineContext::GetCurrentContext();
169     CHECK_NULL_VOID(context);
170     auto manager = context->GetSafeAreaManager();
171     if (manager) {
172         auto safeArea = manager->GetSafeArea();
173         auto parentGlobalOffset = host->GetParentGlobalOffsetDuringLayout();
174         auto frame = host->GetPaintRectWithTransform() + parentGlobalOffset;
175         // if page's frameRect not fit current safeArea, need layout page again
176         if (!NearEqual(frame.GetY(), safeArea.top_.end)) {
177             host->MarkDirtyNode(manager->KeyboardSafeAreaEnabled() ? PROPERTY_UPDATE_LAYOUT : PROPERTY_UPDATE_MEASURE);
178         }
179         if (!NearEqual(frame.GetY() + frame.Height(), safeArea.bottom_.start)) {
180             host->MarkDirtyNode(manager->KeyboardSafeAreaEnabled() ? PROPERTY_UPDATE_LAYOUT : PROPERTY_UPDATE_MEASURE);
181         }
182     }
183     parent->MarkNeedSyncRenderTree();
184     parent->RebuildRenderContextTree();
185 }
186 
OnAttachToMainTree()187 void PagePattern::OnAttachToMainTree()
188 {
189 #if defined(ENABLE_SPLIT_MODE)
190     if (!needFireObserver_) {
191         return;
192     }
193 #endif
194     int32_t index = INVALID_PAGE_INDEX;
195     auto delegate = EngineHelper::GetCurrentDelegate();
196     if (delegate) {
197         index = delegate->GetStackSize();
198         GetPageInfo()->SetPageIndex(index);
199     }
200     state_ = RouterPageState::ABOUT_TO_APPEAR;
201     UIObserverHandler::GetInstance().NotifyRouterPageStateChange(GetPageInfo(), state_);
202 }
203 
OnDetachFromMainTree()204 void PagePattern::OnDetachFromMainTree()
205 {
206 #if defined(ENABLE_SPLIT_MODE)
207     if (!needFireObserver_) {
208         return;
209     }
210 #endif
211     state_ = RouterPageState::ABOUT_TO_DISAPPEAR;
212     UIObserverHandler::GetInstance().NotifyRouterPageStateChange(GetPageInfo(), state_);
213 }
214 
OnShow()215 void PagePattern::OnShow()
216 {
217     // Do not invoke onPageShow unless the initialRender function has been executed.
218     CHECK_NULL_VOID(isRenderDone_);
219     CHECK_NULL_VOID(!isOnShow_);
220     auto context = NG::PipelineContext::GetCurrentContext();
221     CHECK_NULL_VOID(context);
222     auto container = Container::Current();
223     if (!container || !container->WindowIsShow()) {
224         LOGW("no need to trigger onPageShow callback when not in the foreground");
225         return;
226     }
227     std::string bundleName = container->GetBundleName();
228     NotifyPerfMonitorPageMsg(pageInfo_->GetFullPath(), bundleName);
229     if (pageInfo_) {
230         context->FirePageChanged(pageInfo_->GetPageId(), true);
231     }
232     UpdatePageParam();
233     isOnShow_ = true;
234 #if defined(ENABLE_SPLIT_MODE)
235     if (needFireObserver_) {
236         state_ = RouterPageState::ON_PAGE_SHOW;
237         UIObserverHandler::GetInstance().NotifyRouterPageStateChange(GetPageInfo(), state_);
238     }
239 #else
240     state_ = RouterPageState::ON_PAGE_SHOW;
241     UIObserverHandler::GetInstance().NotifyRouterPageStateChange(GetPageInfo(), state_);
242 #endif
243     JankFrameReport::GetInstance().StartRecord(pageInfo_->GetFullPath());
244     auto pageUrlChecker = container->GetPageUrlChecker();
245     if (pageUrlChecker != nullptr) {
246         pageUrlChecker->NotifyPageShow(pageInfo_->GetPageUrl());
247     }
248     if (visibilityChangeCallback_) {
249         visibilityChangeCallback_(true);
250     }
251     if (onPageShow_) {
252         onPageShow_();
253     }
254     if (!onHiddenChange_.empty()) {
255         FireOnHiddenChange(true);
256     }
257     if (Recorder::EventRecorder::Get().IsPageRecordEnable()) {
258         std::string param;
259         auto entryPageInfo = DynamicCast<EntryPageInfo>(pageInfo_);
260         if (entryPageInfo) {
261             param = Recorder::EventRecorder::Get().IsPageParamRecordEnable() ? entryPageInfo->GetPageParams() : "";
262             entryPageInfo->SetShowTime(GetCurrentTimestamp());
263         }
264         Recorder::EventRecorder::Get().OnPageShow(pageInfo_->GetPageUrl(), param);
265     }
266 }
267 
OnHide()268 void PagePattern::OnHide()
269 {
270     CHECK_NULL_VOID(isOnShow_);
271     JankFrameReport::GetInstance().FlushRecord();
272     auto context = NG::PipelineContext::GetCurrentContext();
273     CHECK_NULL_VOID(context);
274     if (pageInfo_) {
275         context->FirePageChanged(pageInfo_->GetPageId(), false);
276     }
277     auto host = GetHost();
278     CHECK_NULL_VOID(host);
279     host->SetJSViewActive(false);
280     isOnShow_ = false;
281     host->SetAccessibilityVisible(false);
282 #if defined(ENABLE_SPLIT_MODE)
283     if (needFireObserver_) {
284         state_ = RouterPageState::ON_PAGE_HIDE;
285         UIObserverHandler::GetInstance().NotifyRouterPageStateChange(GetPageInfo(), state_);
286     }
287 #else
288     state_ = RouterPageState::ON_PAGE_HIDE;
289     UIObserverHandler::GetInstance().NotifyRouterPageStateChange(GetPageInfo(), state_);
290 #endif
291     auto container = Container::Current();
292     if (container) {
293         auto pageUrlChecker = container->GetPageUrlChecker();
294         // ArkTSCard container no SetPageUrlChecker
295         if (pageUrlChecker != nullptr) {
296             pageUrlChecker->NotifyPageHide(pageInfo_->GetPageUrl());
297         }
298     }
299     if (visibilityChangeCallback_) {
300         visibilityChangeCallback_(false);
301     }
302     if (onPageHide_) {
303         onPageHide_();
304     }
305     if (!onHiddenChange_.empty()) {
306         FireOnHiddenChange(false);
307     }
308     if (Recorder::EventRecorder::Get().IsPageRecordEnable()) {
309         auto entryPageInfo = DynamicCast<EntryPageInfo>(pageInfo_);
310         int64_t duration = 0;
311         if (entryPageInfo && entryPageInfo->GetShowTime() > 0) {
312             duration = GetCurrentTimestamp() - entryPageInfo->GetShowTime();
313         }
314         Recorder::EventRecorder::Get().OnPageHide(pageInfo_->GetPageUrl(), duration);
315     }
316 }
317 
OnBackPressed()318 bool PagePattern::OnBackPressed()
319 {
320     if (RemoveOverlay()) {
321         TAG_LOGI(AceLogTag::ACE_OVERLAY, "page removes it's overlay when on backpressed");
322         return true;
323     }
324     if (isPageInTransition_) {
325         TAG_LOGI(AceLogTag::ACE_ROUTER, "page is in transition");
326         return true;
327     }
328     // if in page transition, do not set to ON_BACK_PRESS
329 #if defined(ENABLE_SPLIT_MODE)
330     if (needFireObserver_) {
331         state_ = RouterPageState::ON_BACK_PRESS;
332         UIObserverHandler::GetInstance().NotifyRouterPageStateChange(GetPageInfo(), state_);
333     }
334 #else
335     state_ = RouterPageState::ON_BACK_PRESS;
336     UIObserverHandler::GetInstance().NotifyRouterPageStateChange(GetPageInfo(), state_);
337 #endif
338     if (onBackPressed_) {
339         return onBackPressed_();
340     }
341     return false;
342 }
343 
BuildSharedTransitionMap()344 void PagePattern::BuildSharedTransitionMap()
345 {
346     auto host = GetHost();
347     CHECK_NULL_VOID(host);
348     sharedTransitionMap_.clear();
349     IterativeAddToSharedMap(host, sharedTransitionMap_);
350 }
351 
ReloadPage()352 void PagePattern::ReloadPage()
353 {
354     auto host = GetHost();
355     CHECK_NULL_VOID(host);
356     auto customNode = DynamicCast<CustomNodeBase>(host->GetFirstChild());
357     CHECK_NULL_VOID(customNode);
358     customNode->FireReloadFunction(true);
359 }
360 
FindPageTransitionEffect(PageTransitionType type)361 RefPtr<PageTransitionEffect> PagePattern::FindPageTransitionEffect(PageTransitionType type)
362 {
363     RefPtr<PageTransitionEffect> result;
364     for (auto iter = pageTransitionEffects_.rbegin(); iter != pageTransitionEffects_.rend(); ++iter) {
365         auto effect = *iter;
366         if (effect->CanFit(type)) {
367             result = effect;
368             break;
369         }
370     }
371     return result;
372 }
373 
ClearPageTransitionEffect()374 void PagePattern::ClearPageTransitionEffect()
375 {
376     pageTransitionEffects_.clear();
377 }
378 
GetTopTransition() const379 RefPtr<PageTransitionEffect> PagePattern::GetTopTransition() const
380 {
381     return pageTransitionEffects_.empty() ? nullptr : pageTransitionEffects_.back();
382 }
383 
AddPageTransition(const RefPtr<PageTransitionEffect> & effect)384 void PagePattern::AddPageTransition(const RefPtr<PageTransitionEffect>& effect)
385 {
386     pageTransitionEffects_.emplace_back(effect);
387 }
388 
AddJsAnimator(const std::string & animatorId,const RefPtr<Framework::AnimatorInfo> & animatorInfo)389 void PagePattern::AddJsAnimator(const std::string& animatorId, const RefPtr<Framework::AnimatorInfo>& animatorInfo)
390 {
391     CHECK_NULL_VOID(animatorInfo);
392     auto animator = animatorInfo->GetAnimator();
393     CHECK_NULL_VOID(animator);
394     animator->AttachScheduler(PipelineContext::GetCurrentContext());
395     jsAnimatorMap_[animatorId] = animatorInfo;
396 }
397 
GetJsAnimator(const std::string & animatorId)398 RefPtr<Framework::AnimatorInfo> PagePattern::GetJsAnimator(const std::string& animatorId)
399 {
400     auto iter = jsAnimatorMap_.find(animatorId);
401     if (iter != jsAnimatorMap_.end()) {
402         return iter->second;
403     }
404     return nullptr;
405 }
406 
SetFirstBuildCallback(std::function<void ()> && buildCallback)407 void PagePattern::SetFirstBuildCallback(std::function<void()>&& buildCallback)
408 {
409     if (isFirstLoad_) {
410         firstBuildCallback_ = std::move(buildCallback);
411     } else if (buildCallback) {
412         buildCallback();
413     }
414 }
415 
FirePageTransitionFinish()416 void PagePattern::FirePageTransitionFinish()
417 {
418     if (pageTransitionFinish_) {
419         auto onFinish = *pageTransitionFinish_;
420         pageTransitionFinish_ = nullptr;
421         if (onFinish) {
422             onFinish();
423         }
424     }
425 }
426 
StopPageTransition()427 void PagePattern::StopPageTransition()
428 {
429     auto host = GetHost();
430     CHECK_NULL_VOID(host);
431     auto property = host->GetAnimatablePropertyFloat(KEY_PAGE_TRANSITION_PROPERTY);
432     if (property) {
433         FirePageTransitionFinish();
434         return;
435     }
436     AnimationOption option(Curves::LINEAR, 0);
437     AnimationUtils::Animate(
438         option, [host]() { host->UpdateAnimatablePropertyFloat(KEY_PAGE_TRANSITION_PROPERTY, 0.0f); },
439         nullptr);
440     host->DeleteAnimatablePropertyFloat(KEY_PAGE_TRANSITION_PROPERTY);
441     FirePageTransitionFinish();
442 }
443 
BeforeCreateLayoutWrapper()444 void PagePattern::BeforeCreateLayoutWrapper()
445 {
446     auto pipeline = PipelineContext::GetCurrentContext();
447     CHECK_NULL_VOID(pipeline);
448     // SafeArea already applied to AppBar (AtomicServicePattern)
449     if (pipeline->GetInstallationFree()) {
450         auto host = GetHost();
451         CHECK_NULL_VOID(host);
452         ACE_SCOPED_TRACE("[%s][self:%d] SafeArea already applied to AppBar", host->GetTag().c_str(), host->GetId());
453         return;
454     }
455     ContentRootPattern::BeforeCreateLayoutWrapper();
456     auto host = GetHost();
457     CHECK_NULL_VOID(host);
458     auto&& insets = host->GetLayoutProperty()->GetSafeAreaInsets();
459     CHECK_NULL_VOID(insets);
460     auto manager = pipeline->GetSafeAreaManager();
461     CHECK_NULL_VOID(manager);
462     ACE_SCOPED_TRACE("[%s][self:%d] safeAreaInsets: AvoidKeyboard %d, AvoidTop %d, AvoidCutout "
463                      "%d, AvoidBottom %d insets %s isIgnore: %d, isNeedAvoidWindow %d, "
464                      "isFullScreen %d",
465         host->GetTag().c_str(), host->GetId(), AvoidKeyboard(), AvoidTop(), AvoidCutout(), AvoidBottom(),
466         insets->ToString().c_str(), manager->IsIgnoreAsfeArea(), manager->IsNeedAvoidWindow(), manager->IsFullScreen());
467 }
468 
AvoidKeyboard() const469 bool PagePattern::AvoidKeyboard() const
470 {
471     auto pipeline = PipelineContext::GetCurrentContext();
472     CHECK_NULL_RETURN(pipeline, false);
473     return pipeline->GetSafeAreaManager()->KeyboardSafeAreaEnabled();
474 }
475 
RemoveOverlay()476 bool PagePattern::RemoveOverlay()
477 {
478     CHECK_NULL_RETURN(overlayManager_, false);
479     CHECK_NULL_RETURN(!overlayManager_->IsModalEmpty(), false);
480     auto pipeline = PipelineContext::GetCurrentContext();
481     CHECK_NULL_RETURN(pipeline, false);
482     auto taskExecutor = pipeline->GetTaskExecutor();
483     CHECK_NULL_RETURN(taskExecutor, false);
484     return overlayManager_->RemoveOverlay(true);
485 }
486 
NotifyPerfMonitorPageMsg(const std::string & pageUrl,const std::string & bundleName)487 void PagePattern::NotifyPerfMonitorPageMsg(const std::string& pageUrl, const std::string& bundleName)
488 {
489     if (PerfMonitor::GetPerfMonitor() != nullptr) {
490         PerfMonitor::GetPerfMonitor()->SetPageUrl(pageUrl);
491         // The page contains only page url but not the page name
492         PerfMonitor::GetPerfMonitor()->SetPageName("");
493         PerfMonitor::GetPerfMonitor()->ReportPageShowMsg(pageUrl, bundleName, "");
494     }
495 }
496 
MarkDirtyOverlay()497 void PagePattern::MarkDirtyOverlay()
498 {
499     CHECK_NULL_VOID(overlayManager_);
500     overlayManager_->MarkDirtyOverlay();
501 }
502 } // namespace OHOS::Ace::NG
503