• 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 std::string KEY_PAGE_TRANSITION_PROPERTY = "pageTransitionProperty";
IterativeAddToSharedMap(const RefPtr<UINode> & node,SharedTransitionMap & map)35 void IterativeAddToSharedMap(const RefPtr<UINode>& node, SharedTransitionMap& map)
36 {
37     const auto& children = node->GetChildren();
38     for (const auto& child : children) {
39         auto frameChild = AceType::DynamicCast<FrameNode>(child);
40         if (!frameChild) {
41             IterativeAddToSharedMap(child, map);
42             continue;
43         }
44         auto id = frameChild->GetRenderContext()->GetShareId();
45         if (!id.empty()) {
46             map[id] = frameChild;
47         }
48         IterativeAddToSharedMap(frameChild, map);
49     }
50 }
51 } // namespace
52 
OnAttachToFrameNode()53 void PagePattern::OnAttachToFrameNode()
54 {
55     auto host = GetHost();
56     CHECK_NULL_VOID(host);
57     MeasureType measureType = MeasureType::MATCH_PARENT;
58     auto container = Container::Current();
59     if (container && container->IsDynamicRender()) {
60         measureType = MeasureType::MATCH_CONTENT;
61     }
62     host->GetLayoutProperty()->UpdateMeasureType(measureType);
63     host->GetLayoutProperty()->UpdateAlignment(Alignment::TOP_LEFT);
64 }
65 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & wrapper,const DirtySwapConfig &)66 bool PagePattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& wrapper, const DirtySwapConfig& /* config */)
67 {
68     if (isFirstLoad_) {
69         isFirstLoad_ = false;
70         if (firstBuildCallback_) {
71             firstBuildCallback_();
72             firstBuildCallback_ = nullptr;
73         }
74     }
75     if (dynamicPageSizeCallback_) {
76         auto node = wrapper->GetGeometryNode();
77         CHECK_NULL_RETURN(node, false);
78         dynamicPageSizeCallback_(node->GetFrameSize());
79     }
80     return false;
81 }
82 
TriggerPageTransition(PageTransitionType type,const std::function<void ()> & onFinish)83 bool PagePattern::TriggerPageTransition(PageTransitionType type, const std::function<void()>& onFinish)
84 {
85     auto host = GetHost();
86     CHECK_NULL_RETURN(host, false);
87     auto renderContext = host->GetRenderContext();
88     CHECK_NULL_RETURN(renderContext, false);
89     if (pageTransitionFunc_) {
90         pageTransitionFunc_();
91     }
92     auto effect = FindPageTransitionEffect(type);
93     pageTransitionFinish_ = std::make_shared<std::function<void()>>(onFinish);
94     auto wrappedOnFinish = [weak = WeakClaim(this), sharedFinish = pageTransitionFinish_]() {
95         auto pattern = weak.Upgrade();
96         CHECK_NULL_VOID(pattern);
97         auto host = pattern->GetHost();
98         CHECK_NULL_VOID(host);
99         if (sharedFinish == pattern->pageTransitionFinish_) {
100             // ensure this is exactly the finish callback saved in pagePattern,
101             // otherwise means new pageTransition started
102             pattern->FirePageTransitionFinish();
103             host->DeleteAnimatablePropertyFloat(KEY_PAGE_TRANSITION_PROPERTY);
104         }
105     };
106     if (effect && effect->GetUserCallback()) {
107         RouteType routeType = (type == PageTransitionType::ENTER_POP || type == PageTransitionType::EXIT_POP)
108                                   ? RouteType::POP
109                                   : RouteType::PUSH;
110         host->CreateAnimatablePropertyFloat(KEY_PAGE_TRANSITION_PROPERTY, 0.0f,
111             [routeType, handler = effect->GetUserCallback()](const float& progress) { handler(routeType, progress); });
112         auto handler = effect->GetUserCallback();
113         handler(routeType, 0.0f);
114         AnimationOption option(effect->GetCurve(), effect->GetDuration());
115         option.SetDelay(effect->GetDelay());
116         AnimationUtils::OpenImplicitAnimation(option, option.GetCurve(), wrappedOnFinish);
117         host->UpdateAnimatablePropertyFloat(KEY_PAGE_TRANSITION_PROPERTY, 1.0f);
118         AnimationUtils::CloseImplicitAnimation();
119         return renderContext->TriggerPageTransition(type, nullptr);
120     }
121     return renderContext->TriggerPageTransition(type, wrappedOnFinish);
122 }
123 
ProcessAutoSave()124 void PagePattern::ProcessAutoSave()
125 {
126     auto host = GetHost();
127     CHECK_NULL_VOID(host);
128     if (!host->NeedRequestAutoSave()) {
129         return;
130     }
131     auto container = Container::Current();
132     CHECK_NULL_VOID(container);
133     container->RequestAutoSave(host);
134 }
135 
ProcessHideState()136 void PagePattern::ProcessHideState()
137 {
138     auto host = GetHost();
139     CHECK_NULL_VOID(host);
140     host->SetActive(false);
141     host->OnVisibleChange(false);
142     host->GetLayoutProperty()->UpdateVisibility(VisibleType::INVISIBLE);
143     auto parent = host->GetAncestorNodeOfFrame();
144     CHECK_NULL_VOID(parent);
145     parent->MarkNeedSyncRenderTree();
146     parent->RebuildRenderContextTree();
147 }
148 
ProcessShowState()149 void PagePattern::ProcessShowState()
150 {
151     auto host = GetHost();
152     CHECK_NULL_VOID(host);
153     host->SetActive(true);
154     host->OnVisibleChange(true);
155     host->GetLayoutProperty()->UpdateVisibility(VisibleType::VISIBLE);
156     auto parent = host->GetAncestorNodeOfFrame();
157     CHECK_NULL_VOID(parent);
158     auto context = NG::PipelineContext::GetCurrentContext();
159     CHECK_NULL_VOID(context);
160     auto manager = context->GetSafeAreaManager();
161     if (manager) {
162         auto safeArea = manager->GetSafeArea();
163         auto parentGlobalOffset = host->GetParentGlobalOffsetDuringLayout();
164         auto geometryNode = host->GetGeometryNode();
165         auto frame = geometryNode->GetFrameRect() + parentGlobalOffset;
166         // if page's frameRect not fit current safeArea, need layout page again
167         if (!NearEqual(frame.GetY(), safeArea.top_.end)) {
168             host->MarkDirtyNode(manager->KeyboardSafeAreaEnabled() ? PROPERTY_UPDATE_LAYOUT : PROPERTY_UPDATE_MEASURE);
169         }
170         if (!NearEqual(frame.GetY() + frame.Height(), safeArea.bottom_.start)) {
171             host->MarkDirtyNode(manager->KeyboardSafeAreaEnabled() ? PROPERTY_UPDATE_LAYOUT : PROPERTY_UPDATE_MEASURE);
172         }
173     }
174     parent->MarkNeedSyncRenderTree();
175     parent->RebuildRenderContextTree();
176 }
177 
OnAttachToMainTree()178 void PagePattern::OnAttachToMainTree()
179 {
180     std::string url = GetPageInfo()->GetPageUrl();
181     int32_t index = EngineHelper::GetCurrentDelegate()->GetIndexByUrl(url);
182     GetPageInfo()->SetPageIndex(index);
183     state_ = RouterPageState::ABOUT_TO_APPEAR;
184     UIObserverHandler::GetInstance().NotifyRouterPageStateChange(GetPageInfo(), state_);
185 }
186 
OnDetachFromMainTree()187 void PagePattern::OnDetachFromMainTree()
188 {
189     state_ = RouterPageState::ABOUT_TO_DISAPPEAR;
190     UIObserverHandler::GetInstance().NotifyRouterPageStateChange(GetPageInfo(), state_);
191 }
192 
OnShow()193 void PagePattern::OnShow()
194 {
195     // Do not invoke onPageShow unless the initialRender function has been executed.
196     CHECK_NULL_VOID(isRenderDone_);
197     CHECK_NULL_VOID(!isOnShow_);
198     auto context = NG::PipelineContext::GetCurrentContext();
199     CHECK_NULL_VOID(context);
200     if (pageInfo_) {
201         context->FirePageChanged(pageInfo_->GetPageId(), true);
202     }
203     auto container = Container::Current();
204     if (!container || !container->WindowIsShow()) {
205         LOGW("no need to trigger onPageShow callback when not in the foreground");
206         return;
207     }
208     auto host = GetHost();
209     CHECK_NULL_VOID(host);
210     host->SetJSViewActive(true);
211     isOnShow_ = true;
212     state_ = RouterPageState::ON_PAGE_SHOW;
213     UIObserverHandler::GetInstance().NotifyRouterPageStateChange(GetPageInfo(), state_);
214     JankFrameReport::GetInstance().StartRecord(pageInfo_->GetPageUrl());
215     PerfMonitor::GetPerfMonitor()->SetPageUrl(pageInfo_->GetPageUrl());
216     auto pageUrlChecker = container->GetPageUrlChecker();
217     if (pageUrlChecker != nullptr) {
218         pageUrlChecker->NotifyPageShow(pageInfo_->GetPageUrl());
219     }
220     if (onPageShow_) {
221         onPageShow_();
222     }
223     if (Recorder::EventRecorder::Get().IsPageRecordEnable()) {
224         std::string param;
225         auto entryPageInfo = DynamicCast<EntryPageInfo>(pageInfo_);
226         if (entryPageInfo) {
227             param = entryPageInfo->GetPageParams();
228             entryPageInfo->SetShowTime(GetCurrentTimestamp());
229         }
230         Recorder::EventRecorder::Get().OnPageShow(pageInfo_->GetPageUrl(), param);
231     }
232 }
233 
OnHide()234 void PagePattern::OnHide()
235 {
236     CHECK_NULL_VOID(isOnShow_);
237     JankFrameReport::GetInstance().FlushRecord();
238     auto context = NG::PipelineContext::GetCurrentContext();
239     CHECK_NULL_VOID(context);
240     if (pageInfo_) {
241         context->FirePageChanged(pageInfo_->GetPageId(), false);
242     }
243     auto host = GetHost();
244     CHECK_NULL_VOID(host);
245     host->SetJSViewActive(false);
246     isOnShow_ = false;
247     state_ = RouterPageState::ON_PAGE_HIDE;
248     UIObserverHandler::GetInstance().NotifyRouterPageStateChange(GetPageInfo(), state_);
249     auto container = Container::Current();
250     if (container) {
251         auto pageUrlChecker = container->GetPageUrlChecker();
252         // ArkTSCard container no SetPageUrlChecker
253         if (pageUrlChecker != nullptr) {
254             pageUrlChecker->NotifyPageHide(pageInfo_->GetPageUrl());
255         }
256     }
257     if (onPageHide_) {
258         onPageHide_();
259     }
260     if (Recorder::EventRecorder::Get().IsPageRecordEnable()) {
261         auto entryPageInfo = DynamicCast<EntryPageInfo>(pageInfo_);
262         int64_t duration = 0;
263         if (entryPageInfo && entryPageInfo->GetShowTime() > 0) {
264             duration = GetCurrentTimestamp() - entryPageInfo->GetShowTime();
265         }
266         Recorder::EventRecorder::Get().OnPageHide(pageInfo_->GetPageUrl(), duration);
267     }
268 }
269 
OnBackPressed()270 bool PagePattern::OnBackPressed()
271 {
272     if (isPageInTransition_) {
273         return true;
274     }
275     // if in page transition, do not set to ON_BACK_PRESS
276     state_ = RouterPageState::ON_BACK_PRESS;
277     UIObserverHandler::GetInstance().NotifyRouterPageStateChange(GetPageInfo(), state_);
278     if (onBackPressed_) {
279         return onBackPressed_();
280     }
281     return false;
282 }
283 
BuildSharedTransitionMap()284 void PagePattern::BuildSharedTransitionMap()
285 {
286     auto host = GetHost();
287     CHECK_NULL_VOID(host);
288     sharedTransitionMap_.clear();
289     IterativeAddToSharedMap(host, sharedTransitionMap_);
290 }
291 
ReloadPage()292 void PagePattern::ReloadPage()
293 {
294     auto host = GetHost();
295     CHECK_NULL_VOID(host);
296     auto customNode = DynamicCast<CustomNodeBase>(host->GetFirstChild());
297     CHECK_NULL_VOID(customNode);
298     customNode->FireReloadFunction(true);
299 }
300 
FindPageTransitionEffect(PageTransitionType type)301 RefPtr<PageTransitionEffect> PagePattern::FindPageTransitionEffect(PageTransitionType type)
302 {
303     RefPtr<PageTransitionEffect> result;
304     for (auto iter = pageTransitionEffects_.rbegin(); iter != pageTransitionEffects_.rend(); ++iter) {
305         auto effect = *iter;
306         if (effect->CanFit(type)) {
307             result = effect;
308             break;
309         }
310     }
311     return result;
312 }
313 
ClearPageTransitionEffect()314 void PagePattern::ClearPageTransitionEffect()
315 {
316     pageTransitionEffects_.clear();
317 }
318 
GetTopTransition() const319 RefPtr<PageTransitionEffect> PagePattern::GetTopTransition() const
320 {
321     return pageTransitionEffects_.empty() ? nullptr : pageTransitionEffects_.back();
322 }
323 
AddPageTransition(const RefPtr<PageTransitionEffect> & effect)324 void PagePattern::AddPageTransition(const RefPtr<PageTransitionEffect>& effect)
325 {
326     pageTransitionEffects_.emplace_back(effect);
327 }
328 
AddJsAnimator(const std::string & animatorId,const RefPtr<Framework::AnimatorInfo> & animatorInfo)329 void PagePattern::AddJsAnimator(const std::string& animatorId, const RefPtr<Framework::AnimatorInfo>& animatorInfo)
330 {
331     CHECK_NULL_VOID(animatorInfo);
332     auto animator = animatorInfo->GetAnimator();
333     CHECK_NULL_VOID(animator);
334     animator->AttachScheduler(PipelineContext::GetCurrentContext());
335     jsAnimatorMap_[animatorId] = animatorInfo;
336 }
337 
GetJsAnimator(const std::string & animatorId)338 RefPtr<Framework::AnimatorInfo> PagePattern::GetJsAnimator(const std::string& animatorId)
339 {
340     auto iter = jsAnimatorMap_.find(animatorId);
341     if (iter != jsAnimatorMap_.end()) {
342         return iter->second;
343     }
344     return nullptr;
345 }
346 
SetFirstBuildCallback(std::function<void ()> && buildCallback)347 void PagePattern::SetFirstBuildCallback(std::function<void()>&& buildCallback)
348 {
349     if (isFirstLoad_) {
350         firstBuildCallback_ = std::move(buildCallback);
351     } else if (buildCallback) {
352         buildCallback();
353     }
354 }
355 
FirePageTransitionFinish()356 void PagePattern::FirePageTransitionFinish()
357 {
358     if (pageTransitionFinish_) {
359         auto onFinish = *pageTransitionFinish_;
360         pageTransitionFinish_ = nullptr;
361         if (onFinish) {
362             onFinish();
363         }
364     }
365 }
366 
StopPageTransition()367 void PagePattern::StopPageTransition()
368 {
369     auto host = GetHost();
370     CHECK_NULL_VOID(host);
371     auto property = host->GetAnimatablePropertyFloat(KEY_PAGE_TRANSITION_PROPERTY);
372     if (property) {
373         FirePageTransitionFinish();
374         return;
375     }
376     AnimationOption option(Curves::LINEAR, 0);
377     AnimationUtils::Animate(
378         option, [host]() { host->UpdateAnimatablePropertyFloat(KEY_PAGE_TRANSITION_PROPERTY, 0.0f); },
379         nullptr);
380     host->DeleteAnimatablePropertyFloat(KEY_PAGE_TRANSITION_PROPERTY);
381     FirePageTransitionFinish();
382 }
383 
BeforeCreateLayoutWrapper()384 void PagePattern::BeforeCreateLayoutWrapper()
385 {
386     auto pipeline = PipelineContext::GetCurrentContext();
387     CHECK_NULL_VOID(pipeline);
388     // SafeArea already applied to AppBar (AtomicServicePattern)
389     if (pipeline->GetInstallationFree()) {
390         return;
391     }
392     ContentRootPattern::BeforeCreateLayoutWrapper();
393 }
394 
AvoidKeyboard() const395 bool PagePattern::AvoidKeyboard() const
396 {
397     auto pipeline = PipelineContext::GetCurrentContext();
398     CHECK_NULL_RETURN(pipeline, false);
399     return pipeline->GetSafeAreaManager()->KeyboardSafeAreaEnabled();
400 }
401 } // namespace OHOS::Ace::NG
402