• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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 "frameworks/bridge/declarative_frontend/ng/page_router_manager.h"
17 #include "base/ressched/ressched_report.h"
18 #include "base/perfmonitor/perf_monitor.h"
19 #include "bridge/declarative_frontend/engine/jsi/jsi_declarative_engine.h"
20 #include "core/components_ng/base/frame_node.h"
21 #include "core/components_ng/base/view_advanced_register.h"
22 #include "core/components_ng/pattern/stage/page_node.h"
23 #include "core/components_ng/pattern/stage/page_pattern.h"
24 #include "core/components_ng/pattern/stage/stage_manager.h"
25 #include "core/components_v2/inspector/inspector_constants.h"
26 #include "core/pipeline/base/element_register.h"
27 #include "core/pipeline_ng/pipeline_context.h"
28 
29 namespace OHOS::Ace::NG {
30 namespace {
31 constexpr int32_t INVALID_PAGE_INDEX = -1;
32 constexpr int32_t MAX_ROUTER_STACK_SIZE = 32;
33 } // namespace
34 
PushExtender(const RouterPageInfo & target)35 RefPtr<FrameNode> PageRouterManager::PushExtender(const RouterPageInfo& target)
36 {
37     CHECK_RUN_ON(JS);
38     if (inRouterOpt_) {
39         auto context = PipelineContext::GetCurrentContext();
40         CHECK_NULL_RETURN(context, nullptr);
41         context->PostAsyncEvent(
42             [weak = WeakClaim(this), target]() {
43                 auto router = weak.Upgrade();
44                 CHECK_NULL_VOID(router);
45                 router->Push(target);
46             },
47             "ArkUIPageRouterPush", TaskExecutor::TaskType::JS);
48         return nullptr;
49     }
50     RouterOptScope scope(this);
51     if (target.url.empty()) {
52         TAG_LOGE(AceLogTag::ACE_ROUTER, "push url is empty");
53         return nullptr;
54     }
55     auto context = PipelineContext::GetCurrentContext();
56     CHECK_NULL_RETURN(context, nullptr);
57     auto stageManager = context->GetStageManager();
58     CHECK_NULL_RETURN(stageManager, nullptr);
59     if (GetStackSize() >= MAX_ROUTER_STACK_SIZE && !stageManager->GetForceSplitEnable()) {
60         TAG_LOGW(AceLogTag::ACE_ROUTER, "StartPush exceeds maxStackSize.");
61         if (target.errorCallback != nullptr) {
62             target.errorCallback("The pages are pushed too much.", ERROR_CODE_PAGE_STACK_FULL);
63         }
64         return nullptr;
65     }
66     RouterPageInfo info = target;
67     info.path = info.url + ".js";
68     if (info.path.empty()) {
69         TAG_LOGW(AceLogTag::ACE_ROUTER, "empty path found in StartPush with url: %{public}s", info.url.c_str());
70         if (info.errorCallback != nullptr) {
71             info.errorCallback("The uri of router is not exist.", ERROR_CODE_URI_ERROR);
72         }
73         return nullptr;
74     }
75 
76     CleanPageOverlay();
77     UpdateSrcPage();
78 
79     if (info.routerMode == RouterMode::SINGLE) {
80         auto pageInfo = FindPageInStack(info.url);
81         if (pageInfo.second) {
82             // find page in stack, move postion and update params.
83             auto pagePattern = pageInfo.second->GetPattern<PagePattern>();
84             if (pagePattern) {
85                 pagePattern->FireOnNewParam(info.params);
86             }
87             MovePageToFront(pageInfo.first, pageInfo.second, info, true);
88             return pagePattern->GetHost();
89         }
90         auto index = FindPageInRestoreStack(info.url);
91         if (index != INVALID_PAGE_INDEX) {
92             // find page in restore page, create page, move position and update params.
93             RestorePageWithTarget(index, false, info, RestorePageDestination::TOP);
94             return pageInfo.second;
95         }
96     }
97     auto loadPageSuccess = LoadPageExtender(GenerateNextPageId(), info, true, true, true);
98     if (!loadPageSuccess) {
99         return nullptr;
100     }
101     auto pageNode = pageRouterStack_.back().Upgrade();
102     return pageNode;
103 }
104 
ReplaceExtender(const RouterPageInfo & target,std::function<void ()> && enterFinishCallback)105 RefPtr<FrameNode> PageRouterManager::ReplaceExtender(const RouterPageInfo& target,
106     std::function<void()>&& enterFinishCallback)
107 {
108     CHECK_RUN_ON(JS);
109     if (inRouterOpt_) {
110         auto context = PipelineContext::GetCurrentContext();
111         CHECK_NULL_RETURN(context, nullptr);
112         context->PostAsyncEvent(
113             [weak = WeakClaim(this), target]() {
114                 auto router = weak.Upgrade();
115                 CHECK_NULL_VOID(router);
116                 router->Replace(target);
117             },
118             "ArkUIPageRouterReplace", TaskExecutor::TaskType::JS);
119         return nullptr;
120     }
121     RouterOptScope scope(this);
122     CleanPageOverlay();
123     if (target.url.empty()) {
124         return nullptr;
125     }
126 
127     RouterPageInfo info = target;
128     info.path = info.url + ".js";
129     if (info.path.empty()) {
130         TAG_LOGW(AceLogTag::ACE_ROUTER, "empty path found in StartReplace with url: %{public}s", info.url.c_str());
131         if (info.errorCallback != nullptr) {
132             info.errorCallback("The uri of router is not exist.", ERROR_CODE_URI_ERROR_LITE);
133         }
134         return nullptr;
135     }
136     UpdateSrcPage();
137 
138     auto pipelineContext = PipelineContext::GetCurrentContext();
139     CHECK_NULL_RETURN(pipelineContext, nullptr);
140 #if defined(ENABLE_SPLIT_MODE)
141     auto stageManager = pipelineContext->GetStageManager();
142     CHECK_NULL_RETURN(stageManager, nullptr);
143 #endif
144     TAG_LOGI(AceLogTag::ACE_ROUTER,
145         "router replace in new lifecycle(API version > 11), replace mode: %{public}d, url: %{public}s",
146         static_cast<int32_t>(info.routerMode), info.url.c_str());
147     auto popNode = GetCurrentPageNode();
148     int32_t popIndex = static_cast<int32_t>(pageRouterStack_.size()) - 1;
149     bool findPage = false;
150     if (info.routerMode == RouterMode::SINGLE) {
151         auto pageInfo = FindPageInStack(info.url);
152         // haven't find page by named route's name. Try again with its page path.
153         if (pageInfo.second == nullptr && info.isNamedRouterMode) {
154             std::string pagePath = Framework::JsiDeclarativeEngine::GetPagePath(info.url);
155             pageInfo = FindPageInStack(pagePath);
156         }
157         auto replacePageNode = pageInfo.second;
158         if (pageInfo.first == popIndex) {
159             // replace top self in SINGLE mode, do nothing.
160             CHECK_NULL_RETURN(replacePageNode, nullptr);
161             auto pagePattern = pageInfo.second->GetPattern<PagePattern>();
162             if (pagePattern) {
163                 pagePattern->FireOnNewParam(info.params);
164             }
165             return replacePageNode;
166         }
167         if (replacePageNode) {
168             // find page in stack, move position and update params.
169 #if defined(ENABLE_SPLIT_MODE)
170             stageManager->SetIsNewPageReplacing(true);
171 #endif
172             MovePageToFront(pageInfo.first, replacePageNode, info, false, true, false);
173 #if defined(ENABLE_SPLIT_MODE)
174             stageManager->SetIsNewPageReplacing(false);
175 #endif
176             popIndex = popIndex - 1;
177             findPage = true;
178             auto pagePattern = replacePageNode->GetPattern<PagePattern>();
179             if (pagePattern) {
180                 pagePattern->FireOnNewParam(info.params);
181             }
182         } else {
183             auto index = FindPageInRestoreStack(info.url);
184             if (index != INVALID_PAGE_INDEX) {
185                 // find page in restore page, create page, move position and update params.
186                 RestorePageWithTarget(index, false, info, RestorePageDestination::BELLOW_TOP, false);
187                 return replacePageNode;
188             }
189         }
190     }
191     RefPtr<FrameNode> pageNode = nullptr;
192     if (!findPage) {
193         isNewPageReplacing_ = true;
194 #if defined(ENABLE_SPLIT_MODE)
195         stageManager->SetIsNewPageReplacing(true);
196 #endif
197         bool loadPageSuccess = LoadPageExtender(GenerateNextPageId(), info, false, false);
198         if (loadPageSuccess) {
199             pageNode = pageRouterStack_.back().Upgrade();
200         }
201 #if defined(ENABLE_SPLIT_MODE)
202         stageManager->SetIsNewPageReplacing(false);
203 #endif
204         isNewPageReplacing_ = false;
205     }
206     if (popIndex < 0 || popNode == GetCurrentPageNode()) {
207         return pageNode;
208     }
209     CHECK_NULL_RETURN(popNode, nullptr);
210     auto pagePattern = popNode->GetPattern<PagePattern>();
211     CHECK_NULL_RETURN(pagePattern, nullptr);
212     pagePattern->SetOnNodeDisposeCallback(std::move(enterFinishCallback));
213     auto iter = pageRouterStack_.begin();
214     std::advance(iter, popIndex);
215     auto lastIter = pageRouterStack_.erase(iter);
216     pageRouterStack_.emplace_back(WeakPtr<FrameNode>(AceType::DynamicCast<FrameNode>(popNode)));
217     popNode->MovePosition(GetLastPageIndex());
218     for (auto iter = lastIter; iter != pageRouterStack_.end(); ++iter, ++popIndex) {
219         auto page = iter->Upgrade();
220         if (!page) {
221             continue;
222         }
223         if (page == popNode) {
224             // do not change index of page that will be replaced.
225             continue;
226         }
227         auto pagePattern = page->GetPattern<NG::PagePattern>();
228         pagePattern->GetPageInfo()->SetPageIndex(popIndex + 1);
229     }
230 #if defined(ENABLE_SPLIT_MODE)
231     stageManager->SetIsNewPageReplacing(true);
232 #endif
233     PopPage("", false, false);
234 #if defined(ENABLE_SPLIT_MODE)
235     stageManager->SetIsNewPageReplacing(false);
236 #endif
237     return pageNode;
238 }
239 
RunPageExtender(const RouterPageInfo & target)240 RefPtr<FrameNode> PageRouterManager::RunPageExtender(
241     const RouterPageInfo& target)
242 {
243     PerfMonitor::GetPerfMonitor()->SetAppStartStatus();
244     ACE_SCOPED_TRACE("PageRouterManager::RunPage");
245     CHECK_RUN_ON(JS);
246     RouterPageInfo info = target;
247     info.path = info.url + ".js";
248     auto loadPageSuccess = LoadPageExtender(GenerateNextPageId(), info);
249     if (!loadPageSuccess) {
250         return nullptr;
251     }
252     auto pageNode = pageRouterStack_.back().Upgrade();
253     return pageNode;
254 }
255 
LoadPageExtender(int32_t pageId,const RouterPageInfo & target,bool needHideLast,bool needTransition,bool)256 bool PageRouterManager::LoadPageExtender(
257     int32_t pageId, const RouterPageInfo& target, bool needHideLast, bool needTransition, bool /*isPush*/)
258 {
259     ACE_SCOPED_TRACE_COMMERCIAL("load page: %s(id:%d)", target.url.c_str(), pageId);
260     CHECK_RUN_ON(JS);
261     auto pageNode = CreatePageExtender(pageId, target);
262 
263     if (!pageNode) {
264         TAG_LOGE(AceLogTag::ACE_ROUTER, "failed to create page in LoadPage");
265         return false;
266     }
267 
268     pageRouterStack_.emplace_back(pageNode);
269     if (!OnPageReady(pageNode, needHideLast, needTransition)) {
270         pageRouterStack_.pop_back();
271         TAG_LOGW(AceLogTag::ACE_ROUTER, "LoadPage OnPageReady Failed");
272         return false;
273     }
274     AccessibilityEventType type = AccessibilityEventType::CHANGE;
275     pageNode->OnAccessibilityEvent(type);
276     TAG_LOGI(AceLogTag::ACE_ROUTER, "LoadPage Success");
277     return true;
278 }
279 
CreatePageExtender(int32_t pageId,const RouterPageInfo & target)280 RefPtr<FrameNode> PageRouterManager::CreatePageExtender(int32_t pageId, const RouterPageInfo& target)
281 {
282     ACE_SCOPED_TRACE("PageRouterManager::CreatePage");
283     CHECK_RUN_ON(JS);
284     TAG_LOGI(AceLogTag::ACE_ROUTER,
285         "Page router manager is creating page[%{public}d]: url: %{public}s path: "
286         "%{public}s, recoverable: %{public}s, namedRouter: %{public}s",
287         pageId, target.url.c_str(), target.path.c_str(), (target.recoverable ? "yes" : "no"),
288         (target.isNamedRouterMode ? "yes" : "no"));
289     auto entryPageInfo = AceType::MakeRefPtr<EntryPageInfo>(
290         pageId, target.url, target.path, target.params, target.recoverable, target.isNamedRouterMode);
291     auto pagePattern = ViewAdvancedRegister::GetInstance()->CreatePagePattern(entryPageInfo);
292     std::unordered_map<std::string, std::string> reportData { { "pageUrl", target.url } };
293     ResSchedReportScope reportScope("push_page", reportData);
294     auto pageNode = PageNode::CreatePageNode(ElementRegister::GetInstance()->MakeUniqueId(), pagePattern);
295     pageNode->SetHostPageId(pageId);
296     // !!! must push_back first for UpdateRootComponent
297     pageRouterStack_.emplace_back(pageNode);
298 
299     // record full path info of every pageNode
300     auto pageInfo = pagePattern->GetPageInfo();
301     if (!pageInfo) {
302         pageRouterStack_.pop_back();
303         return nullptr;
304     }
305     auto keyInfo = target.url;
306     if (keyInfo.empty() && manifestParser_) {
307         auto router = manifestParser_->GetRouter();
308         if (router) {
309             keyInfo = router->GetEntry("");
310         }
311     }
312 #if !defined(PREVIEW)
313     if (keyInfo.substr(0, strlen(BUNDLE_TAG)) == BUNDLE_TAG) {
314         // deal with @bundle url
315         // @bundle format: @bundle:bundleName/moduleName/pagePath/fileName(without file extension)
316         // @bundle example: @bundle:com.example.applicationHsp/hsp/ets/mylib/pages/Index
317         // only moduleName and pagePath/fileName is needed: hspmylib/pages/Index
318         size_t bundleEndPos = keyInfo.find('/');
319         size_t moduleStartPos = bundleEndPos + 1;
320         size_t moduleEndPos = keyInfo.find('/', moduleStartPos);
321         std::string moduleName = keyInfo.substr(moduleStartPos, moduleEndPos - moduleStartPos);
322         size_t pageInfoStartPos = keyInfo.find('/', moduleEndPos + 1);
323         keyInfo = keyInfo.substr(pageInfoStartPos + 1);
324         keyInfo = moduleName + keyInfo;
325     }
326 #endif
327     SetPageInfoRouteName(entryPageInfo);
328     auto pagePath = Framework::JsiDeclarativeEngine::GetFullPathInfo(keyInfo);
329     if (pagePath.empty()) {
330         auto container = Container::Current();
331         if (!container) {
332             pageRouterStack_.pop_back();
333             return nullptr;
334         }
335         auto moduleName = container->GetModuleName();
336         keyInfo = moduleName + keyInfo;
337         pagePath = Framework::JsiDeclarativeEngine::GetFullPathInfo(keyInfo);
338     }
339     pageInfo->SetFullPath(pagePath);
340 
341 #if defined(PREVIEW)
342     if (!isComponentPreview_()) {
343 #endif
344         if (target.isNamedRouterMode) {
345             if (manifestParser_) {
346                 manifestParser_->SetPagePath(target.url);
347             } else {
348                 TAG_LOGE(AceLogTag::ACE_ROUTER, "set routeName in manifest failed, manifestParser is null!");
349             }
350         }
351 
352         if (target.errorCallback != nullptr) {
353             target.errorCallback("", ERROR_CODE_NO_ERROR);
354         }
355 #if defined(PREVIEW)
356     }
357 #endif
358 
359     pageRouterStack_.pop_back();
360     return pageNode;
361 }
362 } // namespace OHOS::Ace::NG