1 /*
2  * Copyright (c) 2021-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 "frameworks/bridge/js_frontend/frontend_delegate_impl.h"
17 
18 #include "base/i18n/localization.h"
19 #include "base/log/event_report.h"
20 #include "base/resource/ace_res_config.h"
21 #include "core/components/toast/toast_component.h"
22 
23 namespace OHOS::Ace::Framework {
24 namespace {
25 
26 constexpr int32_t INVALID_PAGE_ID = -1;
27 constexpr int32_t MAX_ROUTER_STACK = 32;
28 constexpr int32_t TOAST_TIME_MAX = 10000;    // ms
29 constexpr int32_t TOAST_TIME_DEFAULT = 1500; // ms
30 constexpr int32_t MAX_PAGE_ID_SIZE = sizeof(uint64_t) * 8;
31 constexpr int32_t NANO_TO_MILLI = 1000000; // nanosecond to millisecond
32 constexpr int32_t TO_MILLI = 1000;         // second to millisecond
33 constexpr int32_t COMPATIBLE_VERSION = 7;
34 constexpr int32_t WEB_FEATURE_VERSION = 6;
35 constexpr int32_t CALLBACK_ERRORCODE_COMPLETE = 2;
36 
37 const char MANIFEST_JSON[] = "manifest.json";
38 const char FILE_TYPE_JSON[] = ".json";
39 const char I18N_FOLDER[] = "i18n/";
40 const char RESOURCES_FOLDER[] = "resources/";
41 const char STYLES_FOLDER[] = "styles/";
42 const char I18N_FILE_SUFFIX[] = "/properties/i18n.json";
43 
44 } // namespace
45 
GenerateNextPageId()46 int32_t FrontendDelegateImpl::GenerateNextPageId()
47 {
48     for (int32_t idx = 0; idx < MAX_PAGE_ID_SIZE; ++idx) {
49         uint64_t bitMask = (1ULL << idx);
50         if ((bitMask & pageIdPool_.fetch_or(bitMask, std::memory_order_relaxed)) == 0) {
51             return idx;
52         }
53     }
54     return INVALID_PAGE_ID;
55 }
56 
RecyclePageId(int32_t pageId)57 void FrontendDelegateImpl::RecyclePageId(int32_t pageId)
58 {
59     if (pageId < 0 && pageId >= MAX_PAGE_ID_SIZE) {
60         return;
61     }
62     uint64_t bitMask = (1ULL << pageId);
63     pageIdPool_.fetch_and(~bitMask, std::memory_order_relaxed);
64 }
65 
FrontendDelegateImpl(const FrontendDelegateImplBuilder & builder)66 FrontendDelegateImpl::FrontendDelegateImpl(const FrontendDelegateImplBuilder& builder)
67     : loadJs_(builder.loadCallback), dispatcherCallback_(builder.transferCallback),
68       asyncEvent_(builder.asyncEventCallback), syncEvent_(builder.syncEventCallback),
69       externalEvent_(builder.externalEventCallback),
70       updatePage_(builder.updatePageCallback), resetStagingPage_(builder.resetStagingPageCallback),
71       destroyPage_(builder.destroyPageCallback), destroyApplication_(builder.destroyApplicationCallback),
72       updateApplicationState_(builder.updateApplicationStateCallback),
73       onStartContinuationCallBack_(builder.onStartContinuationCallBack),
74       onCompleteContinuationCallBack_(builder.onCompleteContinuationCallBack),
75       onRemoteTerminatedCallBack_(builder.onRemoteTerminatedCallBack),
76       onSaveDataCallBack_(builder.onSaveDataCallBack),
77       onRestoreDataCallBack_(builder.onRestoreDataCallBack),
78       timer_(builder.timerCallback), mediaQueryCallback_(builder.mediaQueryCallback),
79       requestAnimationCallback_(builder.requestAnimationCallback), jsCallback_(builder.jsCallback),
80       manifestParser_(AceType::MakeRefPtr<ManifestParser>()),
81       jsAccessibilityManager_(AccessibilityNodeManager::Create()),
82       mediaQueryInfo_(AceType::MakeRefPtr<MediaQueryInfo>()), taskExecutor_(builder.taskExecutor),
83       callNativeHandler_(builder.callNativeHandler)
84 {}
85 
~FrontendDelegateImpl()86 FrontendDelegateImpl::~FrontendDelegateImpl()
87 {
88     CHECK_RUN_ON(JS);
89     LOG_DESTROY();
90 }
91 
ParseManifest()92 void FrontendDelegateImpl::ParseManifest()
93 {
94     std::call_once(onceFlag_, [weak = AceType::WeakClaim(this)]() {
95         std::string jsonContent;
96         auto delegate = weak.Upgrade();
97         if (delegate) {
98             if (!delegate->GetAssetContent(MANIFEST_JSON, jsonContent)) {
99                 LOGE("RunPage parse manifest.json failed");
100                 EventReport::SendFormException(FormExcepType::RUN_PAGE_ERR);
101                 return;
102             }
103             delegate->manifestParser_->Parse(jsonContent);
104             auto task = [delegate]() {
105                 delegate->pipelineContextHolder_.Get(); // Wait until Pipeline Context is attached.
106                 delegate->manifestParser_->GetAppInfo()->ParseI18nJsonInfo();
107             };
108             delegate->taskExecutor_->PostTask(task, TaskExecutor::TaskType::JS, "ArkUIParseI18nJsonInfo");
109         }
110     });
111 }
112 
RunPage(const std::string & url,const std::string & params)113 UIContentErrorCode FrontendDelegateImpl::RunPage(const std::string& url, const std::string& params)
114 {
115     ACE_SCOPED_TRACE("FrontendDelegateImpl::RunPage");
116 
117     auto routerBackCallback = [weak = WeakClaim(this)](const std::string& urlPath) {
118         auto delegate = weak.Upgrade();
119         if (!delegate) {
120             return false;
121         }
122         delegate->Push(urlPath, "");
123         return true;
124     };
125     DelegateClient::GetInstance().RegisterRouterPushCallback(routerBackCallback);
126 
127     auto getWebPageUrlCallback = [weak = WeakClaim(this)](std::string& pageUrl, int32_t& pageId) {
128         auto delegate = weak.Upgrade();
129         if (!delegate) {
130             return false;
131         }
132         pageUrl = delegate->GetRunningPageUrl();
133         pageId = delegate->GetRunningPageId();
134         return true;
135     };
136     DelegateClient::GetInstance().RegisterGetWebPageUrlCallback(getWebPageUrlCallback);
137 
138     auto isPagePathInvalidCallback = [weak = WeakClaim(this)](bool& isPageEmpty) {
139         auto delegate = weak.Upgrade();
140         if (!delegate) {
141             return false;
142         }
143         isPageEmpty = delegate->GetPagePathInvalidFlag();
144         return true;
145     };
146     DelegateClient::GetInstance().RegisterIsPagePathInvalidCallback(isPagePathInvalidCallback);
147 
148     ParseManifest();
149     if (!url.empty()) {
150         mainPagePath_ = manifestParser_->GetRouter()->GetPagePath(url);
151         if (mainPagePath_.empty()) {
152             mainPagePath_ = manifestParser_->GetRouter()->GetEntry();
153         }
154     } else {
155         mainPagePath_ = manifestParser_->GetRouter()->GetEntry();
156     }
157     return LoadPage(GenerateNextPageId(), mainPagePath_, true, params);
158 }
159 
ChangeLocale(const std::string & language,const std::string & countryOrRegion)160 void FrontendDelegateImpl::ChangeLocale(const std::string& language, const std::string& countryOrRegion)
161 {
162     taskExecutor_->PostTask(
163         [language, countryOrRegion]() { AceApplicationInfo::GetInstance().ChangeLocale(language, countryOrRegion); },
164         TaskExecutor::TaskType::PLATFORM, "ArkUIChangeLocale");
165 }
166 
GetI18nData(std::unique_ptr<JsonValue> & json)167 void FrontendDelegateImpl::GetI18nData(std::unique_ptr<JsonValue>& json)
168 {
169     auto data = JsonUtil::CreateArray(true);
170     GetConfigurationCommon(I18N_FOLDER, data);
171     auto i18nData = JsonUtil::Create(true);
172     i18nData->Put("resources", data);
173     json->Put("i18n", i18nData);
174 }
175 
GetResourceConfiguration(std::unique_ptr<JsonValue> & json)176 void FrontendDelegateImpl::GetResourceConfiguration(std::unique_ptr<JsonValue>& json)
177 {
178     auto data = JsonUtil::CreateArray(true);
179     GetConfigurationCommon(RESOURCES_FOLDER, data);
180     json->Put("resourcesConfiguration", data);
181 }
182 
GetConfigurationCommon(const std::string & filePath,std::unique_ptr<JsonValue> & data)183 void FrontendDelegateImpl::GetConfigurationCommon(const std::string& filePath, std::unique_ptr<JsonValue>& data)
184 {
185     std::vector<std::string> files;
186     if (assetManager_) {
187         assetManager_->GetAssetList(filePath, files);
188     }
189 
190     std::vector<std::string> fileNameList;
191     for (const auto& file : files) {
192         if (EndWith(file, FILE_TYPE_JSON) && !StartWith(file, STYLES_FOLDER)) {
193             std::string tmp = file.substr(0, file.size() - (sizeof(FILE_TYPE_JSON) - 1));
194             size_t pos = tmp.find_last_of("/");
195             pos = (pos == std::string::npos) ? 0 : (pos + 1);
196             fileNameList.emplace_back(tmp.substr(pos, tmp.size() - pos));
197         }
198     }
199 
200     std::vector<std::string> priorityFileName;
201     if (filePath.compare(I18N_FOLDER) == 0) {
202         auto localeTag = AceApplicationInfo::GetInstance().GetLocaleTag();
203         priorityFileName = AceResConfig::GetLocaleFallback(localeTag, fileNameList);
204     } else {
205         priorityFileName = AceResConfig::GetResourceFallback(fileNameList);
206     }
207 
208     for (const auto& fileName : priorityFileName) {
209         auto fileFullPath = filePath + fileName + std::string(FILE_TYPE_JSON);
210 #if !defined(PREVIEW)
211         if (filePath.compare(I18N_FOLDER) == 0) {
212             GetAssetFromI18n(fileFullPath, data);
213             continue;
214         }
215 #endif
216         std::string content;
217         if (GetAssetContent(fileFullPath, content)) {
218             auto fileData = ParseFileData(content);
219             if (fileData == nullptr) {
220                 LOGW("parse %{private}s.json i18n content failed", filePath.c_str());
221             } else {
222                 data->Put(fileData);
223             }
224         }
225     }
226 }
227 
GetAssetFromI18n(const std::string & fileFullPath,std::unique_ptr<JsonValue> & data)228 void FrontendDelegateImpl::GetAssetFromI18n(const std::string& fileFullPath, std::unique_ptr<JsonValue>& data)
229 {
230     if (!assetManager_) {
231         return;
232     }
233     auto jsAssetVector = assetManager_->GetAssetFromI18n(fileFullPath);
234     for (auto& jsAsset : jsAssetVector) {
235         auto bufLen = jsAsset->GetSize();
236         auto buffer = jsAsset->GetData();
237         if ((buffer == nullptr) || (bufLen <= 0)) {
238             continue;
239         }
240         std::string content;
241         content.assign(buffer, buffer + bufLen);
242         auto fileData = ParseFileData(content);
243         if (fileData) {
244             data->Put(fileData);
245         }
246     }
247 }
248 
OnJsCallback(const std::string & callbackId,const std::string & data)249 void FrontendDelegateImpl::OnJsCallback(const std::string& callbackId, const std::string& data)
250 {
251     taskExecutor_->PostTask(
252         [weak = AceType::WeakClaim(this), callbackId, args = std::move(data)] {
253             auto delegate = weak.Upgrade();
254             if (delegate) {
255                 delegate->jsCallback_(callbackId, args);
256             }
257         },
258         TaskExecutor::TaskType::JS, "ArkUIHandleJsCallback");
259 }
260 
SetJsMessageDispatcher(const RefPtr<JsMessageDispatcher> & dispatcher) const261 void FrontendDelegateImpl::SetJsMessageDispatcher(const RefPtr<JsMessageDispatcher>& dispatcher) const
262 {
263     taskExecutor_->PostTask([dispatcherCallback = dispatcherCallback_, dispatcher] { dispatcherCallback(dispatcher); },
264         TaskExecutor::TaskType::JS, "ArkUISetJsMessageDispatcher");
265 }
266 
TransferComponentResponseData(int32_t callbackId,int32_t code,std::vector<uint8_t> && data)267 void FrontendDelegateImpl::TransferComponentResponseData(int32_t callbackId, int32_t code, std::vector<uint8_t>&& data)
268 {
269     WeakPtr<PipelineBase> contextWeak(pipelineContextHolder_.Get());
270     taskExecutor_->PostTask(
271         [callbackId, data = std::move(data), contextWeak]() mutable {
272             auto context = contextWeak.Upgrade();
273             if (!context) {
274                 LOGE("context is null");
275             } else if (!context->GetMessageBridge()) {
276                 LOGE("messageBridge is null");
277             } else {
278                 context->GetMessageBridge()->HandleCallback(callbackId, std::move(data));
279             }
280         },
281         TaskExecutor::TaskType::UI, "ArkUITransferComponentResponseData");
282 }
283 
TransferJsResponseData(int32_t callbackId,int32_t code,std::vector<uint8_t> && data) const284 void FrontendDelegateImpl::TransferJsResponseData(int32_t callbackId, int32_t code, std::vector<uint8_t>&& data) const
285 {
286     auto weak = AceType::WeakClaim(AceType::RawPtr(groupJsBridge_));
287     taskExecutor_->PostTask(
288         [callbackId, code, data = std::move(data), weak]() mutable {
289             auto groupJsBridge = weak.Upgrade();
290             if (groupJsBridge) {
291                 groupJsBridge->TriggerModuleJsCallback(callbackId, code, std::move(data));
292             }
293         },
294         TaskExecutor::TaskType::JS, "ArkUITransferJsResponseData");
295 }
296 
297 #if defined(PREVIEW)
TransferJsResponseDataPreview(int32_t callbackId,int32_t code,ResponseData responseData) const298 void FrontendDelegateImpl::TransferJsResponseDataPreview(
299     int32_t callbackId, int32_t code, ResponseData responseData) const
300 {
301     LOGI("JsFrontend TransferJsResponseDataPreview");
302     auto weak = AceType::WeakClaim(AceType::RawPtr(groupJsBridge_));
303     taskExecutor_->PostTask(
304         [callbackId, code, responseData, weak]() mutable {
305             auto groupJsBridge = weak.Upgrade();
306             if (groupJsBridge) {
307                 groupJsBridge->TriggerModuleJsCallbackPreview(callbackId, code, responseData);
308             }
309         },
310         TaskExecutor::TaskType::JS, "ArkUITransferJsResponseData");
311 }
312 #endif
313 
TransferJsPluginGetError(int32_t callbackId,int32_t errorCode,std::string && errorMessage) const314 void FrontendDelegateImpl::TransferJsPluginGetError(
315     int32_t callbackId, int32_t errorCode, std::string&& errorMessage) const
316 {
317     auto weak = AceType::WeakClaim(AceType::RawPtr(groupJsBridge_));
318     taskExecutor_->PostTask(
319         [callbackId, errorCode, errorMessage = std::move(errorMessage), weak]() mutable {
320             auto groupJsBridge = weak.Upgrade();
321             if (groupJsBridge) {
322                 groupJsBridge->TriggerModulePluginGetErrorCallback(callbackId, errorCode, std::move(errorMessage));
323             }
324         },
325         TaskExecutor::TaskType::JS, "ArkUITransferJsPluginGetError");
326 }
327 
TransferJsEventData(int32_t callbackId,int32_t code,std::vector<uint8_t> && data) const328 void FrontendDelegateImpl::TransferJsEventData(int32_t callbackId, int32_t code, std::vector<uint8_t>&& data) const
329 {
330     auto weak = AceType::WeakClaim(AceType::RawPtr(groupJsBridge_));
331     taskExecutor_->PostTask(
332         [callbackId, code, data = std::move(data), weak]() mutable {
333             auto groupJsBridge = weak.Upgrade();
334             if (groupJsBridge) {
335                 groupJsBridge->TriggerEventJsCallback(callbackId, code, std::move(data));
336             }
337         },
338         TaskExecutor::TaskType::JS, "ArkUITransferJsEventData");
339 }
340 
LoadPluginJsCode(std::string && jsCode) const341 void FrontendDelegateImpl::LoadPluginJsCode(std::string&& jsCode) const
342 {
343     auto weak = AceType::WeakClaim(AceType::RawPtr(groupJsBridge_));
344     taskExecutor_->PostTask(
345         [jsCode = std::move(jsCode), weak]() mutable {
346             auto groupJsBridge = weak.Upgrade();
347             if (groupJsBridge) {
348                 groupJsBridge->LoadPluginJsCode(std::move(jsCode));
349             }
350         },
351         TaskExecutor::TaskType::JS, "ArkUILoadPluginJsCode");
352 }
353 
LoadPluginJsByteCode(std::vector<uint8_t> && jsCode,std::vector<int32_t> && jsCodeLen) const354 void FrontendDelegateImpl::LoadPluginJsByteCode(std::vector<uint8_t>&& jsCode, std::vector<int32_t>&& jsCodeLen) const
355 {
356     auto weak = AceType::WeakClaim(AceType::RawPtr(groupJsBridge_));
357     taskExecutor_->PostTask(
358         [jsCode = std::move(jsCode), jsCodeLen = std::move(jsCodeLen), weak]() mutable {
359             auto groupJsBridge = weak.Upgrade();
360             if (groupJsBridge) {
361                 groupJsBridge->LoadPluginJsByteCode(std::move(jsCode), std::move(jsCodeLen));
362             }
363         },
364         TaskExecutor::TaskType::JS, "ArkUILoadPluginJsByteCode");
365 }
366 
OnPageBackPress()367 bool FrontendDelegateImpl::OnPageBackPress()
368 {
369     bool result = FireSyncEvent("_root", std::string("\"clickbackitem\","), std::string(""));
370     return result;
371 }
372 
OnActive()373 void FrontendDelegateImpl::OnActive()
374 {
375     FireAsyncEvent("_root", std::string("\"viewactive\",null,null"), std::string(""));
376 }
377 
OnInactive()378 void FrontendDelegateImpl::OnInactive()
379 {
380     FireAsyncEvent("_root", std::string("\"viewinactive\",null,null"), std::string(""));
381     FireAsyncEvent("_root", std::string("\"viewsuspended\",null,null"), std::string(""));
382 }
383 
OnBackGround()384 void FrontendDelegateImpl::OnBackGround()
385 {
386     OnPageHide();
387 }
388 
OnForeground()389 void FrontendDelegateImpl::OnForeground()
390 {
391     // first page show will be called by push page successfully
392     if (!isFirstNotifyShow_) {
393         OnPageShow();
394     }
395     isFirstNotifyShow_ = false;
396 }
397 
OnStartContinuation()398 bool FrontendDelegateImpl::OnStartContinuation()
399 {
400     bool ret = false;
401     taskExecutor_->PostSyncTask(
402         [weak = AceType::WeakClaim(this), &ret] {
403             auto delegate = weak.Upgrade();
404             if (delegate && delegate->onStartContinuationCallBack_) {
405                 ret = delegate->onStartContinuationCallBack_();
406             }
407         },
408         TaskExecutor::TaskType::JS, "ArkUIStartContinuationCallback");
409     if (!ret) {
410         ret = FireSyncEvent("_root", std::string("\"onStartContinuation\","), std::string(""));
411     }
412     return ret;
413 }
414 
OnCompleteContinuation(int32_t code)415 void FrontendDelegateImpl::OnCompleteContinuation(int32_t code)
416 {
417     taskExecutor_->PostSyncTask(
418         [weak = AceType::WeakClaim(this), code] {
419             auto delegate = weak.Upgrade();
420             if (delegate && delegate->onCompleteContinuationCallBack_) {
421                 delegate->onCompleteContinuationCallBack_(code);
422             }
423         },
424         TaskExecutor::TaskType::JS, "ArkUICompleteContinuationCallback");
425     FireSyncEvent("_root", std::string("\"onCompleteContinuation\","), std::to_string(code));
426 }
427 
OnRemoteTerminated()428 void FrontendDelegateImpl::OnRemoteTerminated()
429 {
430     taskExecutor_->PostSyncTask(
431         [weak = AceType::WeakClaim(this)] {
432             auto delegate = weak.Upgrade();
433             if (delegate && delegate->onRemoteTerminatedCallBack_) {
434                 delegate->onRemoteTerminatedCallBack_();
435             }
436         },
437         TaskExecutor::TaskType::JS, "ArkUIRemoteTerminatedCallback");
438 }
439 
OnSaveData(std::string & data)440 void FrontendDelegateImpl::OnSaveData(std::string& data)
441 {
442     std::string savedData;
443     taskExecutor_->PostSyncTask(
444         [weak = AceType::WeakClaim(this), &savedData] {
445             auto delegate = weak.Upgrade();
446             if (delegate && delegate->onSaveDataCallBack_) {
447                 delegate->onSaveDataCallBack_(savedData);
448             }
449         },
450         TaskExecutor::TaskType::JS, "ArkUISaveDataCallback");
451     if (savedData.empty()) {
452         FireSyncEvent("_root", std::string("\"onSaveData\","), std::string(""), savedData);
453     }
454     std::string pageUri = GetRunningPageUrl();
455     data = std::string("{\"url\":\"").append(pageUri).append("\",\"__remoteData\":").append(savedData).append("}");
456 }
457 
OnRestoreData(const std::string & data)458 bool FrontendDelegateImpl::OnRestoreData(const std::string& data)
459 {
460     bool ret = false;
461     taskExecutor_->PostSyncTask(
462         [weak = AceType::WeakClaim(this), &data, &ret] {
463             auto delegate = weak.Upgrade();
464             if (delegate && delegate->onRestoreDataCallBack_) {
465                 ret = delegate->onRestoreDataCallBack_(data);
466             }
467         },
468         TaskExecutor::TaskType::JS, "ArkUIRestoreDataCallback");
469     FireSyncEvent("_root", std::string("\"onSaveData\","), data);
470     return ret;
471 }
472 
OnNewRequest(const std::string & data)473 void FrontendDelegateImpl::OnNewRequest(const std::string& data)
474 {
475     FireSyncEvent("_root", std::string("\"onNewRequest\","), data);
476 }
477 
CallPopPage()478 void FrontendDelegateImpl::CallPopPage()
479 {
480     std::lock_guard<std::mutex> lock(mutex_);
481     auto& currentPage = pageRouteStack_.back();
482     if (!pageRouteStack_.empty() && currentPage.alertCallback) {
483         backUri_ = "";
484         taskExecutor_->PostTask(
485             [context = AceType::DynamicCast<PipelineContext>(pipelineContextHolder_.Get()),
486                 dialogProperties = pageRouteStack_.back().dialogProperties,
487                 isRightToLeft = AceApplicationInfo::GetInstance().IsRightToLeft()]() {
488                 if (context) {
489                     context->ShowDialog(dialogProperties, isRightToLeft);
490                 }
491             },
492             TaskExecutor::TaskType::UI, "ArkUIShowDialogBeforePopPage");
493     } else {
494         PopPage();
495     }
496 }
497 
ResetStagingPage()498 void FrontendDelegateImpl::ResetStagingPage()
499 {
500     taskExecutor_->PostTask([resetStagingPage = resetStagingPage_] { resetStagingPage(); },
501         TaskExecutor::TaskType::JS, "ArkUIResetStagingPage");
502 }
503 
OnApplicationDestroy(const std::string & packageName)504 void FrontendDelegateImpl::OnApplicationDestroy(const std::string& packageName)
505 {
506     taskExecutor_->PostSyncTask(
507         [destroyApplication = destroyApplication_, packageName] { destroyApplication(packageName); },
508         TaskExecutor::TaskType::JS, "ArkUIApplicationDestroyCallback");
509 }
510 
OnApplicationUpdateState(const std::string & packageName,Frontend::State state)511 void FrontendDelegateImpl::OnApplicationUpdateState(const std::string& packageName, Frontend::State state)
512 {
513     taskExecutor_->PostSyncTask(
514         [updateApplication = updateApplicationState_, packageName, state] { updateApplication(packageName, state); },
515         TaskExecutor::TaskType::JS, "ArkUIApplicationUpdateStateCallback");
516 }
517 
FireAsyncEvent(const std::string & eventId,const std::string & param,const std::string & jsonArgs)518 void FrontendDelegateImpl::FireAsyncEvent(
519     const std::string& eventId, const std::string& param, const std::string& jsonArgs)
520 {
521     std::string args = param;
522     args.append(",null").append(",null"); // callback and dom changes
523     if (!jsonArgs.empty()) {
524         args.append(",").append(jsonArgs); // method args
525     }
526     taskExecutor_->PostTask(
527         [weak = AceType::WeakClaim(this), eventId, args = std::move(args)] {
528             auto delegate = weak.Upgrade();
529             if (delegate) {
530                 delegate->asyncEvent_(eventId, args);
531             }
532         },
533         TaskExecutor::TaskType::JS, "ArkUIFireAsyncEvent");
534 }
535 
FireSyncEvent(const std::string & eventId,const std::string & param,const std::string & jsonArgs)536 bool FrontendDelegateImpl::FireSyncEvent(
537     const std::string& eventId, const std::string& param, const std::string& jsonArgs)
538 {
539     std::string resultStr;
540     FireSyncEvent(eventId, param, jsonArgs, resultStr);
541     return (resultStr == "true");
542 }
543 
FireSyncEvent(const std::string & eventId,const std::string & param,const std::string & jsonArgs,std::string & result)544 void FrontendDelegateImpl::FireSyncEvent(
545     const std::string& eventId, const std::string& param, const std::string& jsonArgs, std::string& result)
546 {
547     int32_t callbackId = callbackCnt_++;
548     std::string args = param;
549     args.append("{\"_callbackId\":\"").append(std::to_string(callbackId)).append("\"}").append(",null");
550     if (!jsonArgs.empty()) {
551         args.append(",").append(jsonArgs); // method args
552     }
553     taskExecutor_->PostSyncTask(
554         [weak = AceType::WeakClaim(this), eventId, args = std::move(args), callbackId, &result] {
555             auto delegate = weak.Upgrade();
556             if (delegate) {
557                 delegate->syncEvent_(eventId, args);
558                 result = delegate->jsCallBackResult_[callbackId];
559                 LOGD("FireSyncEvent eventId: %{public}s, callbackId: %{public}d", eventId.c_str(), callbackId);
560                 delegate->jsCallBackResult_.erase(callbackId);
561             }
562         },
563         TaskExecutor::TaskType::JS, "ArkUIFireSyncEvent");
564 }
565 
FireExternalEvent(const std::string & eventId,const std::string & componentId,const uint32_t nodeId,const bool isDestroy)566 void FrontendDelegateImpl::FireExternalEvent(
567     const std::string& eventId, const std::string& componentId, const uint32_t nodeId, const bool isDestroy)
568 {
569     taskExecutor_->PostSyncTask(
570         [weak = AceType::WeakClaim(this), componentId, nodeId, isDestroy] {
571             auto delegate = weak.Upgrade();
572             if (delegate) {
573                 delegate->externalEvent_(componentId, nodeId, isDestroy);
574             }
575         },
576         TaskExecutor::TaskType::JS, "ArkUIFireExternalEvent");
577 }
578 
FireAccessibilityEvent(const AccessibilityEvent & accessibilityEvent)579 void FrontendDelegateImpl::FireAccessibilityEvent(const AccessibilityEvent& accessibilityEvent)
580 {
581     jsAccessibilityManager_->SendAccessibilityAsyncEvent(accessibilityEvent);
582 }
583 
InitializeAccessibilityCallback()584 void FrontendDelegateImpl::InitializeAccessibilityCallback()
585 {
586     jsAccessibilityManager_->InitializeCallback();
587 }
588 
589 // Start FrontendDelegate overrides.
Push(const std::string & uri,const std::string & params)590 void FrontendDelegateImpl::Push(const std::string& uri, const std::string& params)
591 {
592     Push(uri, params, nullptr);
593 }
594 
PushWithCallback(const std::string & uri,const std::string & params,bool recoverable,const std::function<void (const std::string &,int32_t)> & errorCallback,uint32_t routerMode)595 void FrontendDelegateImpl::PushWithCallback(const std::string& uri, const std::string& params, bool recoverable,
596     const std::function<void(const std::string&, int32_t)>& errorCallback, uint32_t routerMode)
597 {
598     Push(uri, params, errorCallback);
599 }
600 
Push(const std::string & uri,const std::string & params,const std::function<void (const std::string &,int32_t)> & errorCallback)601 void FrontendDelegateImpl::Push(const std::string& uri, const std::string& params,
602     const std::function<void(const std::string&, int32_t)>& errorCallback)
603 {
604     if (uri.empty()) {
605         LOGE("router.Push uri is empty");
606         return;
607     }
608     if (isRouteStackFull_) {
609         LOGE("the router stack has reached its max size, you can't push any more pages.");
610         EventReport::SendPageRouterException(PageRouterExcepType::PAGE_STACK_OVERFLOW_ERR, uri);
611         if (errorCallback != nullptr) {
612             errorCallback("The pages are pushed too much.", ERROR_CODE_PAGE_STACK_FULL);
613         }
614         return;
615     }
616     std::string pagePath = manifestParser_->GetRouter()->GetPagePath(uri);
617     if (!pagePath.empty()) {
618         isPagePathInvalid_ = true;
619         LoadPage(GenerateNextPageId(), pagePath, false, params);
620         if (errorCallback != nullptr) {
621             errorCallback("", ERROR_CODE_NO_ERROR);
622         }
623     } else {
624         isPagePathInvalid_ = false;
625         LOGW("[Engine Log] this uri not support in route push.");
626         if (errorCallback != nullptr) {
627             errorCallback("The uri of router is not exist.", ERROR_CODE_URI_ERROR);
628         }
629     }
630 
631     if (taskExecutor_) {
632         taskExecutor_->PostTask(
633             [context = pipelineContextHolder_.Get(), isPagePathInvalid = isPagePathInvalid_]() {
634                 if (context) {
635                     context->NotifyIsPagePathInvalidDismiss(isPagePathInvalid);
636                 }
637             },
638             TaskExecutor::TaskType::UI, "ArkUINotifyIsPagePathInvalidDismiss");
639     }
640 }
641 
Replace(const std::string & uri,const std::string & params)642 void FrontendDelegateImpl::Replace(const std::string& uri, const std::string& params)
643 {
644     Replace(uri, params, nullptr);
645 }
646 
ReplaceWithCallback(const std::string & uri,const std::string & params,bool recoverable,const std::function<void (const std::string &,int32_t)> & errorCallback,uint32_t routerMode)647 void FrontendDelegateImpl::ReplaceWithCallback(const std::string& uri, const std::string& params, bool recoverable,
648     const std::function<void(const std::string&, int32_t)>& errorCallback, uint32_t routerMode)
649 {
650     Push(uri, params, errorCallback);
651 }
652 
Replace(const std::string & uri,const std::string & params,const std::function<void (const std::string &,int32_t)> & errorCallback)653 void FrontendDelegateImpl::Replace(const std::string& uri, const std::string& params,
654     const std::function<void(const std::string&, int32_t)>& errorCallback)
655 {
656     if (uri.empty()) {
657         LOGE("router.Replace uri is empty");
658         return;
659     }
660 
661     std::string pagePath = manifestParser_->GetRouter()->GetPagePath(uri);
662     if (!pagePath.empty()) {
663         LoadReplacePage(GenerateNextPageId(), pagePath, params);
664         if (errorCallback != nullptr) {
665             errorCallback("", ERROR_CODE_NO_ERROR);
666         }
667     } else {
668         LOGW("[Engine Log] this uri not support in route replace.");
669         if (errorCallback != nullptr) {
670             errorCallback("The uri of router is not exist.", ERROR_CODE_URI_ERROR_LITE);
671         }
672     }
673 }
674 
Back(const std::string & uri,const std::string & params)675 void FrontendDelegateImpl::Back(const std::string& uri, const std::string& params)
676 {
677     auto pipelineContext = pipelineContextHolder_.Get();
678     {
679         std::lock_guard<std::mutex> lock(mutex_);
680         auto& currentPage = pageRouteStack_.back();
681         if (!pageRouteStack_.empty() && currentPage.alertCallback) {
682             backUri_ = uri;
683             backParam_ = params;
684             taskExecutor_->PostTask(
685                 [context = AceType::DynamicCast<PipelineContext>(pipelineContextHolder_.Get()),
686                     dialogProperties = pageRouteStack_.back().dialogProperties,
687                     isRightToLeft = AceApplicationInfo::GetInstance().IsRightToLeft()]() {
688                     if (context) {
689                         context->ShowDialog(dialogProperties, isRightToLeft);
690                     }
691                 },
692                 TaskExecutor::TaskType::UI, "ArkUIShowDialogBeforeBack");
693             return;
694         }
695     }
696     BackImplement(uri, params);
697 }
698 
BackImplement(const std::string & uri,const std::string & params)699 void FrontendDelegateImpl::BackImplement(const std::string& uri, const std::string& params)
700 {
701     if (uri.empty()) {
702         PopPage();
703     } else {
704         std::string pagePath = manifestParser_->GetRouter()->GetPagePath(uri);
705         pageId_ = GetPageIdByUrl(pagePath);
706         if (!pagePath.empty()) {
707             if (!params.empty()) {
708                 std::lock_guard<std::mutex> lock(mutex_);
709                 pageParamMap_[pageId_] = params;
710             }
711             PopToPage(pagePath);
712         } else {
713             LOGW("[Engine Log] this uri not support in route Back.");
714         }
715     }
716 
717     taskExecutor_->PostTask(
718         [context = pipelineContextHolder_.Get()]() {
719             if (context) {
720                 context->NotifyRouterBackDismiss();
721             }
722         },
723         TaskExecutor::TaskType::UI, "ArkUINotifyRouterBackDismiss");
724 }
725 
PostponePageTransition()726 void FrontendDelegateImpl::PostponePageTransition()
727 {
728     std::lock_guard<std::mutex> lock(mutex_);
729     taskExecutor_->PostTask(
730         [context = pipelineContextHolder_.Get()]() {
731             if (context) {
732                 context->PostponePageTransition();
733             }
734         },
735         TaskExecutor::TaskType::UI, "ArkUIPostponePageTransition");
736 }
737 
LaunchPageTransition()738 void FrontendDelegateImpl::LaunchPageTransition()
739 {
740     std::lock_guard<std::mutex> lock(mutex_);
741     taskExecutor_->PostTask(
742         [context = pipelineContextHolder_.Get()]() {
743             if (context) {
744                 context->LaunchPageTransition();
745             }
746         },
747         TaskExecutor::TaskType::UI, "ArkUILaunchPageTransition");
748 }
749 
Clear()750 void FrontendDelegateImpl::Clear()
751 {
752     ClearInvisiblePages();
753 }
754 
GetStackSize() const755 int32_t FrontendDelegateImpl::GetStackSize() const
756 {
757     std::lock_guard<std::mutex> lock(mutex_);
758     return static_cast<int32_t>(pageRouteStack_.size());
759 }
760 
GetState(int32_t & index,std::string & name,std::string & path)761 void FrontendDelegateImpl::GetState(int32_t& index, std::string& name, std::string& path)
762 {
763     std::string url;
764     {
765         std::lock_guard<std::mutex> lock(mutex_);
766         if (pageRouteStack_.empty()) {
767             return;
768         }
769         index = static_cast<int32_t>(pageRouteStack_.size());
770         url = pageRouteStack_.back().url;
771     }
772     auto pos = url.rfind(".js");
773     // url length - (.js) length
774     if (pos == url.length() - 3) {
775         url = url.substr(0, pos);
776     }
777     pos = url.rfind("/");
778     if (pos != std::string::npos) {
779         name = url.substr(pos + 1);
780         path = url.substr(0, pos + 1);
781     }
782 }
783 
GetComponentsCount()784 size_t FrontendDelegateImpl::GetComponentsCount()
785 {
786     std::lock_guard<std::mutex> lock(mutex_);
787     if (pageRouteStack_.empty()) {
788         return 0;
789     }
790     auto itPage = pageMap_.find(pageRouteStack_.back().pageId);
791     if (itPage == pageMap_.end()) {
792         return 0;
793     }
794     auto domDoc = itPage->second->GetDomDocument();
795     if (!domDoc) {
796         return 0;
797     }
798     return domDoc->GetComponentsCount();
799 }
800 
GetParams()801 std::string FrontendDelegateImpl::GetParams()
802 {
803     auto iter = pageParamMap_.find(pageId_);
804     if (iter != pageParamMap_.end()) {
805         return iter->second;
806     } else {
807         return "";
808     }
809 }
810 
TriggerPageUpdate(int32_t pageId,bool directExecute)811 void FrontendDelegateImpl::TriggerPageUpdate(int32_t pageId, bool directExecute)
812 {
813     auto page = GetPage(pageId);
814     if (!page) {
815         return;
816     }
817 
818     auto jsPage = AceType::DynamicCast<Framework::JsAcePage>(page);
819     ACE_DCHECK(jsPage);
820 
821     // Pop all JS command and execute them in UI thread.
822     auto jsCommands = std::make_shared<std::vector<RefPtr<JsCommand>>>();
823     jsPage->PopAllCommands(*jsCommands);
824 
825     auto pipelineContext = AceType::DynamicCast<PipelineContext>(pipelineContextHolder_.Get());
826     WeakPtr<Framework::JsAcePage> jsPageWeak(jsPage);
827     WeakPtr<PipelineContext> contextWeak(pipelineContext);
828     auto updateTask = [weak = AceType::WeakClaim(this), jsPageWeak, contextWeak, jsCommands] {
829         ACE_SCOPED_TRACE("FlushUpdateCommands");
830         auto delegate = weak.Upgrade();
831         auto jsPage = jsPageWeak.Upgrade();
832         auto context = contextWeak.Upgrade();
833         if (!delegate || !jsPage || !context) {
834             LOGE("Page update failed. page or context is null.");
835             EventReport::SendPageRouterException(PageRouterExcepType::UPDATE_PAGE_ERR);
836             return;
837         }
838         bool useLiteStyle = delegate->GetMinPlatformVersion() < COMPATIBLE_VERSION && delegate->IsUseLiteStyle();
839         context->SetUseLiteStyle(useLiteStyle);
840         jsPage->SetUseLiteStyle(useLiteStyle);
841         jsPage->SetUseBoxWrap(delegate->GetMinPlatformVersion() >= WEB_FEATURE_VERSION);
842         // Flush all JS commands.
843         for (const auto& command : *jsCommands) {
844             command->Execute(jsPage);
845         }
846         if (jsPage->GetDomDocument()) {
847             jsPage->GetDomDocument()->HandleComponentPostBinding();
848         }
849         auto accessibilityManager = context->GetAccessibilityManager();
850         if (accessibilityManager) {
851             accessibilityManager->HandleComponentPostBinding();
852         }
853 
854         jsPage->ClearShowCommand();
855         std::vector<NodeId> dirtyNodes;
856         jsPage->PopAllDirtyNodes(dirtyNodes);
857         for (auto nodeId : dirtyNodes) {
858             auto patchComponent = jsPage->BuildPagePatch(nodeId);
859             if (patchComponent) {
860                 context->ScheduleUpdate(patchComponent);
861             }
862         }
863     };
864     auto weakContext = AceType::WeakClaim(AceType::RawPtr(pipelineContext));
865     taskExecutor_->PostTask(
866         [updateTask, weakContext, directExecute]() {
867             auto pipelineContext = weakContext.Upgrade();
868             if (pipelineContext) {
869                 pipelineContext->AddPageUpdateTask(std::move(updateTask), directExecute);
870             }
871         },
872         TaskExecutor::TaskType::UI, "ArkUIAddPageUpdateTask");
873 }
874 
PostJsTask(std::function<void ()> && task,const std::string & name)875 void FrontendDelegateImpl::PostJsTask(std::function<void()>&& task, const std::string& name)
876 {
877     taskExecutor_->PostTask(task, TaskExecutor::TaskType::JS, name);
878 }
879 
PostUITask(std::function<void ()> && task,const std::string & name)880 void FrontendDelegateImpl::PostUITask(std::function<void()>&& task, const std::string& name)
881 {
882     taskExecutor_->PostTask(task, TaskExecutor::TaskType::UI, name);
883 }
884 
GetAppID() const885 const std::string& FrontendDelegateImpl::GetAppID() const
886 {
887     return manifestParser_->GetAppInfo()->GetAppID();
888 }
889 
GetAppName() const890 const std::string& FrontendDelegateImpl::GetAppName() const
891 {
892     return manifestParser_->GetAppInfo()->GetAppName();
893 }
894 
GetVersionName() const895 const std::string& FrontendDelegateImpl::GetVersionName() const
896 {
897     return manifestParser_->GetAppInfo()->GetVersionName();
898 }
899 
GetVersionCode() const900 int32_t FrontendDelegateImpl::GetVersionCode() const
901 {
902     return manifestParser_->GetAppInfo()->GetVersionCode();
903 }
904 
GetWindowConfig()905 WindowConfig& FrontendDelegateImpl::GetWindowConfig()
906 {
907     ParseManifest();
908     return manifestParser_->GetWindowConfig();
909 }
910 
GetMinPlatformVersion()911 int32_t FrontendDelegateImpl::GetMinPlatformVersion()
912 {
913     ParseManifest();
914     return manifestParser_->GetMinPlatformVersion();
915 }
916 
IsUseLiteStyle()917 bool FrontendDelegateImpl::IsUseLiteStyle()
918 {
919     ParseManifest();
920     return manifestParser_->IsUseLiteStyle();
921 }
922 
IsWebFeature()923 bool FrontendDelegateImpl::IsWebFeature()
924 {
925     ParseManifest();
926     return manifestParser_->IsWebFeature();
927 }
928 
MeasureText(MeasureContext context)929 double FrontendDelegateImpl::MeasureText(MeasureContext context)
930 {
931     if (context.isFontSizeUseDefaultUnit && context.fontSize.has_value() &&
932         !AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
933         context.fontSize = Dimension(context.fontSize->Value(), DimensionUnit::VP);
934     }
935     return MeasureUtil::MeasureText(context);
936 }
937 
MeasureTextSize(MeasureContext context)938 Size FrontendDelegateImpl::MeasureTextSize(MeasureContext context)
939 {
940     if (context.isFontSizeUseDefaultUnit && context.fontSize.has_value() &&
941         !AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
942         context.fontSize = Dimension(context.fontSize->Value(), DimensionUnit::VP);
943     }
944     return MeasureUtil::MeasureTextSize(context);
945 }
946 
ShowToast(const NG::ToastInfo & toastInfo)947 void FrontendDelegateImpl::ShowToast(const NG::ToastInfo& toastInfo)
948 {
949     NG::ToastInfo updatedToastInfo = toastInfo;
950     updatedToastInfo.duration = std::clamp(toastInfo.duration, TOAST_TIME_DEFAULT, TOAST_TIME_MAX);
951     updatedToastInfo.isRightToLeft = AceApplicationInfo::GetInstance().IsRightToLeft();
952     auto pipelineContext = AceType::DynamicCast<PipelineContext>(pipelineContextHolder_.Get());
953     auto weak = AceType::WeakClaim(AceType::RawPtr(pipelineContext));
954     taskExecutor_->PostTask(
955         [updatedToastInfo, weak] {
956             ToastComponent::GetInstance().Show(weak.Upgrade(), updatedToastInfo.message, updatedToastInfo.duration,
957                 updatedToastInfo.bottom, updatedToastInfo.isRightToLeft);
958         },
959         TaskExecutor::TaskType::UI, "ArkUIShowToast");
960 }
961 
GetBoundingRectData(NodeId nodeId)962 Rect FrontendDelegateImpl::GetBoundingRectData(NodeId nodeId)
963 {
964     Rect rect;
965     auto task = [context = pipelineContextHolder_.Get(), nodeId, &rect]() {
966         context->GetBoundingRectData(nodeId, rect);
967     };
968     PostSyncTaskToPage(task, "ArkUIGetBoundingRectData");
969     return rect;
970 }
971 
GetInspector(NodeId nodeId)972 std::string FrontendDelegateImpl::GetInspector(NodeId nodeId)
973 {
974     std::string attrs;
975     auto task = [weak = WeakClaim(AceType::RawPtr(jsAccessibilityManager_)), nodeId, &attrs]() {
976         auto accessibilityNodeManager = weak.Upgrade();
977         if (accessibilityNodeManager) {
978             attrs = accessibilityNodeManager->GetInspectorNodeById(nodeId);
979         }
980     };
981     PostSyncTaskToPage(task, "ArkUIGetInspectorNode");
982     return attrs;
983 }
984 
ShowDialog(const std::string & title,const std::string & message,const std::vector<ButtonInfo> & buttons,bool autoCancel,std::function<void (int32_t,int32_t)> && callback,const std::set<std::string> & callbacks)985 void FrontendDelegateImpl::ShowDialog(const std::string& title, const std::string& message,
986     const std::vector<ButtonInfo>& buttons, bool autoCancel, std::function<void(int32_t, int32_t)>&& callback,
987     const std::set<std::string>& callbacks)
988 {
989     if (!taskExecutor_) {
990         LOGE("task executor is null.");
991         return;
992     }
993     std::unordered_map<std::string, EventMarker> callbackMarkers;
994     if (callbacks.find(COMMON_SUCCESS) != callbacks.end()) {
995         auto successEventMarker = BackEndEventManager<void(int32_t)>::GetInstance().GetAvailableMarker();
996         BackEndEventManager<void(int32_t)>::GetInstance().BindBackendEvent(
997             successEventMarker, [callback, taskExecutor = taskExecutor_](int32_t successType) {
998                 taskExecutor->PostTask([callback, successType]() { callback(0, successType); },
999                     TaskExecutor::TaskType::JS, "ArkUIShowDialogSuccessCallback");
1000             });
1001         callbackMarkers.emplace(COMMON_SUCCESS, successEventMarker);
1002     }
1003     if (callbacks.find(COMMON_CANCEL) != callbacks.end()) {
1004         auto cancelEventMarker = BackEndEventManager<void()>::GetInstance().GetAvailableMarker();
1005         BackEndEventManager<void()>::GetInstance().BindBackendEvent(
1006             cancelEventMarker, [callback, taskExecutor = taskExecutor_] {
1007                 taskExecutor->PostTask(
1008                     [callback]() { callback(1, 0); }, TaskExecutor::TaskType::JS, "ArkUIShowDialogCancelCallback");
1009             });
1010         callbackMarkers.emplace(COMMON_CANCEL, cancelEventMarker);
1011     }
1012     if (callbacks.find(COMMON_COMPLETE) != callbacks.end()) {
1013         auto completeEventMarker = BackEndEventManager<void()>::GetInstance().GetAvailableMarker();
1014         BackEndEventManager<void()>::GetInstance().BindBackendEvent(
1015             completeEventMarker, [callback, taskExecutor = taskExecutor_] {
1016                 taskExecutor->PostTask([callback]() { callback(CALLBACK_ERRORCODE_COMPLETE, 0); },
1017                     TaskExecutor::TaskType::JS, "ArkUIShowDialogCompleteCallback");
1018             });
1019         callbackMarkers.emplace(COMMON_COMPLETE, completeEventMarker);
1020     }
1021     DialogProperties dialogProperties = {
1022         .title = title,
1023         .content = message,
1024         .autoCancel = autoCancel,
1025         .buttons = buttons,
1026         .callbacks = std::move(callbackMarkers),
1027     };
1028     WeakPtr<PipelineContext> weak = AceType::DynamicCast<PipelineContext>(pipelineContextHolder_.Get());
1029     taskExecutor_->PostTask(
1030         [weak, dialogProperties, isRightToLeft = AceApplicationInfo::GetInstance().IsRightToLeft()]() {
1031             auto context = weak.Upgrade();
1032             if (context) {
1033                 context->ShowDialog(dialogProperties, isRightToLeft);
1034             }
1035         },
1036         TaskExecutor::TaskType::UI, "ArkUIShowDialog");
1037 }
1038 
ShowActionMenu(const std::string & title,const std::vector<ButtonInfo> & button,std::function<void (int32_t,int32_t)> && callback)1039 void FrontendDelegateImpl::ShowActionMenu(const std::string& title,
1040     const std::vector<ButtonInfo>& button, std::function<void(int32_t, int32_t)>&& callback)
1041 {
1042     if (!taskExecutor_) {
1043         return;
1044     }
1045 
1046     std::unordered_map<std::string, EventMarker> callbackMarkers;
1047 
1048     auto successEventMarker = BackEndEventManager<void(int32_t)>::GetInstance().GetAvailableMarker();
1049     BackEndEventManager<void(int32_t)>::GetInstance().BindBackendEvent(
1050         successEventMarker, [callback, number = button.size(), taskExecutor = taskExecutor_](int32_t successType) {
1051             taskExecutor->PostTask(
1052                 [callback, number, successType]() {
1053                     // if callback index is larger than button's number, cancel button is selected
1054                     if (static_cast<size_t>(successType) == number) {
1055                         callback(1, 0);
1056                     } else {
1057                         callback(0, successType);
1058                     }
1059                 },
1060                 TaskExecutor::TaskType::JS, "ArkUIShowActionMenuSuccess");
1061         });
1062     callbackMarkers.emplace(COMMON_SUCCESS, successEventMarker);
1063 
1064     auto cancelEventMarker = BackEndEventManager<void()>::GetInstance().GetAvailableMarker();
1065     BackEndEventManager<void()>::GetInstance().BindBackendEvent(
1066         cancelEventMarker, [callback, taskExecutor = taskExecutor_] {
1067             taskExecutor->PostTask([callback]() { callback(1, 0); },
1068                 TaskExecutor::TaskType::JS, "ArkUIShowActionMenuCancel");
1069         });
1070     callbackMarkers.emplace(COMMON_CANCEL, cancelEventMarker);
1071 
1072     DialogProperties dialogProperties = {
1073         .title = title,
1074         .autoCancel = true,
1075         .isMenu = true,
1076         .buttons = button,
1077         .callbacks = std::move(callbackMarkers),
1078     };
1079     ButtonInfo buttonInfo = {
1080         .text = Localization::GetInstance()->GetEntryLetters("common.cancel"),
1081         .textColor = "#000000"
1082     };
1083     dialogProperties.buttons.emplace_back(buttonInfo);
1084     WeakPtr<PipelineContext> weak = AceType::DynamicCast<PipelineContext>(pipelineContextHolder_.Get());
1085     taskExecutor_->PostTask(
1086         [weak, dialogProperties, isRightToLeft = AceApplicationInfo::GetInstance().IsRightToLeft()]() {
1087             auto context = weak.Upgrade();
1088             if (context) {
1089                 context->ShowDialog(dialogProperties, isRightToLeft);
1090             }
1091         },
1092         TaskExecutor::TaskType::UI, "ArkUIShowActionMenu");
1093 }
1094 
EnableAlertBeforeBackPage(const std::string & message,std::function<void (int32_t)> && callback)1095 void FrontendDelegateImpl::EnableAlertBeforeBackPage(
1096     const std::string& message, std::function<void(int32_t)>&& callback)
1097 {
1098     if (!taskExecutor_) {
1099         LOGE("task executor is null.");
1100         return;
1101     }
1102 
1103     std::unordered_map<std::string, EventMarker> callbackMarkers;
1104     auto pipelineContext = pipelineContextHolder_.Get();
1105     auto successEventMarker = BackEndEventManager<void(int32_t)>::GetInstance().GetAvailableMarker();
1106     BackEndEventManager<void(int32_t)>::GetInstance().BindBackendEvent(successEventMarker,
1107         [weak = AceType::WeakClaim(this), callback, taskExecutor = taskExecutor_](int32_t successType) {
1108             taskExecutor->PostTask(
1109                 [weak, callback, successType]() {
1110                     callback(successType);
1111                     if (!successType) {
1112                         return;
1113                     }
1114                     auto delegate = weak.Upgrade();
1115                     if (!delegate) {
1116                         return;
1117                     }
1118                     delegate->BackImplement(delegate->backUri_, delegate->backParam_);
1119                 },
1120                 TaskExecutor::TaskType::JS, "ArkUIBackPageSuccessEvent");
1121         });
1122     callbackMarkers.emplace(COMMON_SUCCESS, successEventMarker);
1123 
1124     std::lock_guard<std::mutex> lock(mutex_);
1125     if (pageRouteStack_.empty()) {
1126         return;
1127     }
1128 
1129     auto& currentPage = pageRouteStack_.back();
1130     ClearAlertCallback(currentPage);
1131     currentPage.dialogProperties = {
1132         .content = message,
1133         .autoCancel = false,
1134         .buttons = { { .text = Localization::GetInstance()->GetEntryLetters("common.cancel"), .textColor = "" },
1135             { .text = Localization::GetInstance()->GetEntryLetters("common.ok"), .textColor = "" } },
1136         .callbacks = std::move(callbackMarkers),
1137     };
1138 }
1139 
DisableAlertBeforeBackPage()1140 void FrontendDelegateImpl::DisableAlertBeforeBackPage()
1141 {
1142     std::lock_guard<std::mutex> lock(mutex_);
1143     if (pageRouteStack_.empty()) {
1144         LOGE("page stack is null.");
1145         return;
1146     }
1147     auto& currentPage = pageRouteStack_.back();
1148     ClearAlertCallback(currentPage);
1149 }
1150 
SetCallBackResult(const std::string & callBackId,const std::string & result)1151 void FrontendDelegateImpl::SetCallBackResult(const std::string& callBackId, const std::string& result)
1152 {
1153     jsCallBackResult_.try_emplace(StringToInt(callBackId), result);
1154 }
1155 
WaitTimer(const std::string & callbackId,const std::string & delay,bool isInterval,bool isFirst)1156 void FrontendDelegateImpl::WaitTimer(
1157     const std::string& callbackId, const std::string& delay, bool isInterval, bool isFirst)
1158 {
1159     if (!isFirst) {
1160         auto timeoutTaskIter = timeoutTaskMap_.find(callbackId);
1161         // If not find the callbackId in map, means this timer already was removed,
1162         // no need create a new cancelableTimer again.
1163         if (timeoutTaskIter == timeoutTaskMap_.end()) {
1164             return;
1165         }
1166     }
1167 
1168     int32_t delayTime = StringToInt(delay);
1169     // CancelableCallback class can only be executed once.
1170     CancelableCallback<void()> cancelableTimer;
1171     cancelableTimer.Reset([callbackId, delay, isInterval, call = timer_] { call(callbackId, delay, isInterval); });
1172     auto result = timeoutTaskMap_.try_emplace(callbackId, cancelableTimer);
1173     if (!result.second) {
1174         result.first->second = cancelableTimer;
1175     }
1176     taskExecutor_->PostDelayedTask(cancelableTimer, TaskExecutor::TaskType::JS, delayTime, "ArkUICancelableTimer");
1177 }
1178 
ClearTimer(const std::string & callbackId)1179 void FrontendDelegateImpl::ClearTimer(const std::string& callbackId)
1180 {
1181     auto timeoutTaskIter = timeoutTaskMap_.find(callbackId);
1182     if (timeoutTaskIter != timeoutTaskMap_.end()) {
1183         timeoutTaskIter->second.Cancel();
1184         timeoutTaskMap_.erase(timeoutTaskIter);
1185     } else {
1186         LOGW("ClearTimer callbackId not found");
1187     }
1188 }
1189 
PostSyncTaskToPage(std::function<void ()> && task,const std::string & name)1190 void FrontendDelegateImpl::PostSyncTaskToPage(std::function<void()>&& task, const std::string& name)
1191 {
1192     pipelineContextHolder_.Get(); // Wait until Pipeline Context is attached.
1193     TriggerPageUpdate(GetRunningPageId(), true);
1194     taskExecutor_->PostSyncTask(task, TaskExecutor::TaskType::UI, name);
1195 }
1196 
AddTaskObserver(std::function<void ()> && task)1197 void FrontendDelegateImpl::AddTaskObserver(std::function<void()>&& task)
1198 {
1199     taskExecutor_->AddTaskObserver(std::move(task));
1200 }
1201 
RemoveTaskObserver()1202 void FrontendDelegateImpl::RemoveTaskObserver()
1203 {
1204     taskExecutor_->RemoveTaskObserver();
1205 }
1206 
GetAssetContent(const std::string & url,std::string & content)1207 bool FrontendDelegateImpl::GetAssetContent(const std::string& url, std::string& content)
1208 {
1209     return GetAssetContentImpl(assetManager_, url, content);
1210 }
1211 
GetAssetContent(const std::string & url,std::vector<uint8_t> & content)1212 bool FrontendDelegateImpl::GetAssetContent(const std::string& url, std::vector<uint8_t>& content)
1213 {
1214     return GetAssetContentImpl(assetManager_, url, content);
1215 }
1216 
GetAssetPath(const std::string & url)1217 std::string FrontendDelegateImpl::GetAssetPath(const std::string& url)
1218 {
1219     return GetAssetPathImpl(assetManager_, url);
1220 }
1221 
LoadPage(int32_t pageId,const std::string & url,bool isMainPage,const std::string & params)1222 UIContentErrorCode FrontendDelegateImpl::LoadPage(
1223     int32_t pageId, const std::string& url, bool isMainPage, const std::string& params)
1224 {
1225     {
1226         std::lock_guard<std::mutex> lock(mutex_);
1227         pageId_ = pageId;
1228         pageParamMap_[pageId] = params;
1229     }
1230     if (pageId == INVALID_PAGE_ID) {
1231         LOGE("FrontendDelegateImpl, invalid page id");
1232         EventReport::SendPageRouterException(PageRouterExcepType::LOAD_PAGE_ERR, url);
1233         return UIContentErrorCode::INVALID_PAGE_ID;
1234     }
1235     if (isStagingPageExist_) {
1236         LOGE("FrontendDelegateImpl, load page failed, waiting for current page loading finish.");
1237         RecyclePageId(pageId);
1238         return UIContentErrorCode::STAGING_PAGE_EXIST;
1239     }
1240     isStagingPageExist_ = true;
1241     auto document = AceType::MakeRefPtr<DOMDocument>(pageId);
1242     auto page = AceType::MakeRefPtr<JsAcePage>(pageId, document, url);
1243     page->SetPageParams(params);
1244     page->SetFlushCallback([weak = AceType::WeakClaim(this), isMainPage](const RefPtr<JsAcePage>& acePage) {
1245         auto delegate = weak.Upgrade();
1246         if (delegate && acePage) {
1247             delegate->FlushPageCommand(acePage, acePage->GetUrl(), isMainPage);
1248         }
1249     });
1250     taskExecutor_->PostTask(
1251         [weak = AceType::WeakClaim(this), page, url, isMainPage] {
1252             auto delegate = weak.Upgrade();
1253             if (!delegate) {
1254                 LOGE("the delegate context is nullptr");
1255                 return;
1256             }
1257             delegate->loadJs_(url, page, isMainPage);
1258             page->FlushCommands();
1259             // just make sure the pipelineContext is created.
1260             auto pipelineContext = delegate->pipelineContextHolder_.Get();
1261             if (!pipelineContext) {
1262                 LOGE("the pipeline context is nullptr");
1263                 return;
1264             }
1265             if (delegate->GetMinPlatformVersion() > 0) {
1266                 pipelineContext->SetMinPlatformVersion(delegate->GetMinPlatformVersion());
1267             }
1268             delegate->taskExecutor_->PostTask(
1269                 [weak, page] {
1270                     auto delegate = weak.Upgrade();
1271                     if (delegate && delegate->pipelineContextHolder_.Get()) {
1272                         auto context = AceType::DynamicCast<PipelineContext>(delegate->pipelineContextHolder_.Get());
1273                         if (context) {
1274                             context->FlushFocus();
1275                         }
1276                     }
1277                     if (page->GetDomDocument()) {
1278                         page->GetDomDocument()->HandlePageLoadFinish();
1279                     }
1280                 },
1281                 TaskExecutor::TaskType::UI, "ArkUIPageLoadFinish");
1282         },
1283         TaskExecutor::TaskType::JS, "ArkUILoadJsPage");
1284 
1285     return UIContentErrorCode::NO_ERRORS;
1286 }
1287 
OnSurfaceChanged()1288 void FrontendDelegateImpl::OnSurfaceChanged()
1289 {
1290     if (mediaQueryInfo_->GetIsInit()) {
1291         mediaQueryInfo_->SetIsInit(false);
1292     }
1293     mediaQueryInfo_->EnsureListenerIdValid();
1294     OnMediaQueryUpdate();
1295 }
1296 
OnLayoutCompleted(const std::string & componentId)1297 void FrontendDelegateImpl::OnLayoutCompleted(const std::string& componentId) {}
1298 
OnDrawCompleted(const std::string & componentId)1299 void FrontendDelegateImpl::OnDrawCompleted(const std::string& componentId) {}
1300 
OnMediaQueryUpdate(bool isSynchronous)1301 void FrontendDelegateImpl::OnMediaQueryUpdate(bool isSynchronous)
1302 {
1303     if (mediaQueryInfo_->GetIsInit()) {
1304         return;
1305     }
1306 
1307     taskExecutor_->PostTask(
1308         [weak = AceType::WeakClaim(this)] {
1309             auto delegate = weak.Upgrade();
1310             if (!delegate) {
1311                 return;
1312             }
1313             const auto& info = delegate->mediaQueryInfo_->GetMediaQueryInfo();
1314             // request css mediaquery
1315             std::string param("\"viewsizechanged\",");
1316             param.append(info);
1317             delegate->asyncEvent_("_root", param);
1318 
1319             // request js mediaquery
1320             const auto& listenerId = delegate->mediaQueryInfo_->GetListenerId();
1321             delegate->mediaQueryCallback_(listenerId, info);
1322             delegate->mediaQueryInfo_->ResetListenerId();
1323         },
1324         TaskExecutor::TaskType::JS, "ArkUIMediaQueryUpdate");
1325 }
1326 
OnPageReady(const RefPtr<JsAcePage> & page,const std::string & url,bool isMainPage)1327 void FrontendDelegateImpl::OnPageReady(const RefPtr<JsAcePage>& page, const std::string& url, bool isMainPage)
1328 {
1329     LOGI("OnPageReady url = %{private}s", url.c_str());
1330     // Pop all JS command and execute them in UI thread.
1331     auto jsCommands = std::make_shared<std::vector<RefPtr<JsCommand>>>();
1332     page->PopAllCommands(*jsCommands);
1333 
1334     auto pipelineContext = pipelineContextHolder_.Get();
1335     page->SetPipelineContext(pipelineContext);
1336     taskExecutor_->PostTask(
1337         [weak = AceType::WeakClaim(this), page, url, jsCommands, isMainPage] {
1338             auto delegate = weak.Upgrade();
1339             if (!delegate) {
1340                 return;
1341             }
1342             delegate->SetCurrentReadyPage(page);
1343             auto pipelineContext = AceType::DynamicCast<PipelineContext>(delegate->pipelineContextHolder_.Get());
1344             CHECK_NULL_VOID(pipelineContext);
1345             bool useLiteStyle = delegate->GetMinPlatformVersion() < COMPATIBLE_VERSION && delegate->IsUseLiteStyle();
1346             pipelineContext->SetUseLiteStyle(useLiteStyle);
1347             page->SetUseLiteStyle(useLiteStyle);
1348             page->SetUseBoxWrap(delegate->GetMinPlatformVersion() >= WEB_FEATURE_VERSION);
1349             // Flush all JS commands.
1350             for (const auto& command : *jsCommands) {
1351                 command->Execute(page);
1352             }
1353             // Just clear all dirty nodes.
1354             page->ClearAllDirtyNodes();
1355             if (page->GetDomDocument()) {
1356                 page->GetDomDocument()->HandleComponentPostBinding();
1357             }
1358             if (pipelineContext->GetAccessibilityManager()) {
1359                 pipelineContext->GetAccessibilityManager()->HandleComponentPostBinding();
1360             }
1361             if (pipelineContext->CanPushPage()) {
1362                 if (!isMainPage) {
1363                     delegate->OnPageHide();
1364                 }
1365                 pipelineContext->RemovePageTransitionListener(delegate->pageTransitionListenerId_);
1366                 delegate->pageTransitionListenerId_ = pipelineContext->AddPageTransitionListener(
1367                     [weak, page](
1368                         const TransitionEvent& event, const WeakPtr<PageElement>& in, const WeakPtr<PageElement>& out) {
1369                         auto delegate = weak.Upgrade();
1370                         if (delegate) {
1371                             delegate->PushPageTransitionListener(event, page);
1372                         }
1373                     });
1374                 pipelineContext->PushPage(page->BuildPage(url));
1375                 delegate->OnPushPageSuccess(page, url);
1376                 delegate->SetCurrentPage(page->GetPageId());
1377                 delegate->OnMediaQueryUpdate();
1378             } else {
1379                 // This page has been loaded but become useless now, the corresponding js instance
1380                 // must be destroyed to avoid memory leak.
1381                 delegate->OnPageDestroy(page->GetPageId());
1382                 delegate->ResetStagingPage();
1383             }
1384             delegate->isStagingPageExist_ = false;
1385         },
1386         TaskExecutor::TaskType::UI, "ArkUIPageReady");
1387 }
1388 
PushPageTransitionListener(const TransitionEvent & event,const RefPtr<JsAcePage> & page)1389 void FrontendDelegateImpl::PushPageTransitionListener(
1390     const TransitionEvent& event, const RefPtr<JsAcePage>& page)
1391 {
1392     if (event == TransitionEvent::PUSH_END) {
1393         OnPageShow();
1394     }
1395 }
1396 
FlushPageCommand(const RefPtr<JsAcePage> & page,const std::string & url,bool isMainPage)1397 void FrontendDelegateImpl::FlushPageCommand(const RefPtr<JsAcePage>& page, const std::string& url, bool isMainPage)
1398 {
1399     if (!page) {
1400         return;
1401     }
1402     LOGI("FlushPageCommand FragmentCount(%{public}d)", page->FragmentCount());
1403     if (page->FragmentCount() == 1) {
1404         OnPageReady(page, url, isMainPage);
1405     } else {
1406         TriggerPageUpdate(page->GetPageId());
1407     }
1408 }
1409 
AddPageLocked(const RefPtr<JsAcePage> & page)1410 void FrontendDelegateImpl::AddPageLocked(const RefPtr<JsAcePage>& page)
1411 {
1412     auto result = pageMap_.try_emplace(page->GetPageId(), page);
1413     if (!result.second) {
1414         LOGW("the page has already in the map");
1415     }
1416 }
1417 
SetCurrentPage(int32_t pageId)1418 void FrontendDelegateImpl::SetCurrentPage(int32_t pageId)
1419 {
1420     auto page = GetPage(pageId);
1421     if (page != nullptr) {
1422         jsAccessibilityManager_->SetRunningPage(page);
1423         taskExecutor_->PostTask(
1424             [updatePage = updatePage_, page] { updatePage(page); }, TaskExecutor::TaskType::JS, "ArkUISetCurrentPage");
1425     } else {
1426         LOGE("FrontendDelegateImpl SetCurrentPage page is null.");
1427     }
1428 }
1429 
OnPushPageSuccess(const RefPtr<JsAcePage> & page,const std::string & url)1430 void FrontendDelegateImpl::OnPushPageSuccess(const RefPtr<JsAcePage>& page, const std::string& url)
1431 {
1432     std::lock_guard<std::mutex> lock(mutex_);
1433     AddPageLocked(page);
1434     pageRouteStack_.emplace_back(PageInfo { page->GetPageId(), url });
1435     if (pageRouteStack_.size() >= MAX_ROUTER_STACK) {
1436         isRouteStackFull_ = true;
1437         EventReport::SendPageRouterException(PageRouterExcepType::PAGE_STACK_OVERFLOW_ERR, page->GetUrl());
1438     }
1439     auto pipelineContext = pipelineContextHolder_.Get();
1440     if (pipelineContext) {
1441         pipelineContext->onRouterChange(url);
1442         pipelineContext->NotifyPopPageSuccessDismiss(url, pageRouteStack_.back().pageId);
1443     }
1444     LOGI("OnPushPageSuccess size=%{private}zu,pageId=%{private}d,url=%{private}s", pageRouteStack_.size(),
1445         pageRouteStack_.back().pageId, pageRouteStack_.back().url.c_str());
1446 }
1447 
OnPopToPageSuccess(const std::string & url)1448 void FrontendDelegateImpl::OnPopToPageSuccess(const std::string& url)
1449 {
1450     std::lock_guard<std::mutex> lock(mutex_);
1451     while (!pageRouteStack_.empty()) {
1452         if (pageRouteStack_.back().url == url) {
1453             break;
1454         }
1455         OnPageDestroy(pageRouteStack_.back().pageId);
1456         pageMap_.erase(pageRouteStack_.back().pageId);
1457         pageParamMap_.erase(pageRouteStack_.back().pageId);
1458         ClearAlertCallback(pageRouteStack_.back());
1459         pageRouteStack_.pop_back();
1460     }
1461     if (isRouteStackFull_) {
1462         isRouteStackFull_ = false;
1463     }
1464     auto pipelineContext = pipelineContextHolder_.Get();
1465     if (pipelineContext) {
1466         pipelineContext->onRouterChange(url);
1467         pipelineContext->NotifyPopPageSuccessDismiss(url, pageRouteStack_.back().pageId);
1468     }
1469 }
1470 
PopToPage(const std::string & url)1471 void FrontendDelegateImpl::PopToPage(const std::string& url)
1472 {
1473     taskExecutor_->PostTask(
1474         [weak = AceType::WeakClaim(this), url] {
1475             auto delegate = weak.Upgrade();
1476             if (!delegate) {
1477                 return;
1478             }
1479             auto pageId = delegate->GetPageIdByUrl(url);
1480             if (pageId == INVALID_PAGE_ID) {
1481                 return;
1482             }
1483             auto pipelineContext = AceType::DynamicCast<PipelineContext>(delegate->pipelineContextHolder_.Get());
1484             CHECK_NULL_VOID(pipelineContext);
1485             if (!pipelineContext->CanPopPage()) {
1486                 delegate->ResetStagingPage();
1487                 return;
1488             }
1489             delegate->OnPageHide();
1490             pipelineContext->RemovePageTransitionListener(delegate->pageTransitionListenerId_);
1491             delegate->pageTransitionListenerId_ = pipelineContext->AddPageTransitionListener(
1492                 [weak, url, pageId](
1493                     const TransitionEvent& event, const WeakPtr<PageElement>& in, const WeakPtr<PageElement>& out) {
1494                     auto delegate = weak.Upgrade();
1495                     if (delegate) {
1496                         delegate->PopToPageTransitionListener(event, url, pageId);
1497                     }
1498                 });
1499             pipelineContext->PopToPage(pageId);
1500         },
1501         TaskExecutor::TaskType::UI, "ArkUIPopToPage");
1502 }
1503 
PopToPageTransitionListener(const TransitionEvent & event,const std::string & url,int32_t pageId)1504 void FrontendDelegateImpl::PopToPageTransitionListener(
1505     const TransitionEvent& event, const std::string& url, int32_t pageId)
1506 {
1507     if (event == TransitionEvent::POP_END) {
1508         OnPopToPageSuccess(url);
1509         SetCurrentPage(pageId);
1510         OnPageShow();
1511         OnMediaQueryUpdate();
1512     }
1513 }
1514 
OnPopPageSuccess()1515 int32_t FrontendDelegateImpl::OnPopPageSuccess()
1516 {
1517     std::lock_guard<std::mutex> lock(mutex_);
1518     pageMap_.erase(pageRouteStack_.back().pageId);
1519     pageParamMap_.erase(pageRouteStack_.back().pageId);
1520     ClearAlertCallback(pageRouteStack_.back());
1521     pageRouteStack_.pop_back();
1522     if (isRouteStackFull_) {
1523         isRouteStackFull_ = false;
1524     }
1525     if (!pageRouteStack_.empty()) {
1526         auto context = pipelineContextHolder_.Get();
1527         if (context) {
1528             context->NotifyPopPageSuccessDismiss(pageRouteStack_.back().url, pageRouteStack_.back().pageId);
1529             context->onRouterChange(pageRouteStack_.back().url);
1530         }
1531 
1532         return pageRouteStack_.back().pageId;
1533     }
1534     return INVALID_PAGE_ID;
1535 }
1536 
PopPage()1537 void FrontendDelegateImpl::PopPage()
1538 {
1539     taskExecutor_->PostTask(
1540         [weak = AceType::WeakClaim(this)] {
1541             auto delegate = weak.Upgrade();
1542             if (!delegate) {
1543                 return;
1544             }
1545             auto pipelineContext = AceType::DynamicCast<PipelineContext>(delegate->pipelineContextHolder_.Get());
1546             CHECK_NULL_VOID(pipelineContext);
1547             if (delegate->GetStackSize() == 1) {
1548                 if (delegate->disallowPopLastPage_) {
1549                     LOGW("Not allow back because this is the last page!");
1550                     return;
1551                 }
1552                 delegate->OnPageHide();
1553                 delegate->OnPageDestroy(delegate->GetRunningPageId());
1554                 delegate->OnPopPageSuccess();
1555                 pipelineContext->Finish();
1556                 return;
1557             }
1558             if (!pipelineContext->CanPopPage()) {
1559                 delegate->ResetStagingPage();
1560                 return;
1561             }
1562             delegate->OnPageHide();
1563             pipelineContext->RemovePageTransitionListener(delegate->pageTransitionListenerId_);
1564             delegate->pageTransitionListenerId_ = pipelineContext->AddPageTransitionListener(
1565                 [weak, destroyPageId = delegate->GetRunningPageId()](
1566                     const TransitionEvent& event, const WeakPtr<PageElement>& in, const WeakPtr<PageElement>& out) {
1567                     auto delegate = weak.Upgrade();
1568                     if (delegate) {
1569                         delegate->PopPageTransitionListener(event, destroyPageId);
1570                     }
1571                 });
1572             pipelineContext->PopPage();
1573         },
1574         TaskExecutor::TaskType::UI, "ArkUIPopPage");
1575 }
1576 
PopPageTransitionListener(const TransitionEvent & event,int32_t destroyPageId)1577 void FrontendDelegateImpl::PopPageTransitionListener(const TransitionEvent& event, int32_t destroyPageId)
1578 {
1579     if (event == TransitionEvent::POP_END) {
1580         OnPageDestroy(destroyPageId);
1581         auto pageId = OnPopPageSuccess();
1582         SetCurrentPage(pageId);
1583         OnPageShow();
1584         OnMediaQueryUpdate();
1585     }
1586 }
1587 
OnClearInvisiblePagesSuccess()1588 int32_t FrontendDelegateImpl::OnClearInvisiblePagesSuccess()
1589 {
1590     std::lock_guard<std::mutex> lock(mutex_);
1591     PageInfo pageInfo = std::move(pageRouteStack_.back());
1592     pageRouteStack_.pop_back();
1593     for (const auto& info : pageRouteStack_) {
1594         ClearAlertCallback(info);
1595         OnPageDestroy(info.pageId);
1596         pageMap_.erase(info.pageId);
1597         pageParamMap_.erase(info.pageId);
1598     }
1599     pageRouteStack_.clear();
1600     int32_t resPageId = pageInfo.pageId;
1601     pageRouteStack_.emplace_back(std::move(pageInfo));
1602     if (isRouteStackFull_) {
1603         isRouteStackFull_ = false;
1604     }
1605     return resPageId;
1606 }
1607 
ClearInvisiblePages()1608 void FrontendDelegateImpl::ClearInvisiblePages()
1609 {
1610     std::lock_guard<std::mutex> lock(mutex_);
1611     // Execute invisible pages' OnJsEngineDestroy to release JsValue
1612     for (auto pageRouteIter = pageRouteStack_.cbegin(); pageRouteIter != pageRouteStack_.cend() - 1; ++pageRouteIter) {
1613         const auto& info = *pageRouteIter;
1614         auto iter = pageMap_.find(info.pageId);
1615         if (iter != pageMap_.end()) {
1616             auto page = iter->second;
1617             if (page) {
1618                 page->OnJsEngineDestroy();
1619             }
1620         }
1621     }
1622 
1623     taskExecutor_->PostTask(
1624         [weak = AceType::WeakClaim(this)] {
1625             auto delegate = weak.Upgrade();
1626             if (!delegate) {
1627                 return;
1628             }
1629             auto pipelineContext = AceType::DynamicCast<PipelineContext>(delegate->pipelineContextHolder_.Get());
1630             CHECK_NULL_VOID(pipelineContext);
1631             if (pipelineContext->ClearInvisiblePages()) {
1632                 auto pageId = delegate->OnClearInvisiblePagesSuccess();
1633                 delegate->SetCurrentPage(pageId);
1634             }
1635         },
1636         TaskExecutor::TaskType::UI, "ArkUIClearInvisiblePages");
1637 }
1638 
OnReplacePageSuccess(const RefPtr<JsAcePage> & page,const std::string & url)1639 void FrontendDelegateImpl::OnReplacePageSuccess(const RefPtr<JsAcePage>& page, const std::string& url)
1640 {
1641     if (!page) {
1642         return;
1643     }
1644     std::lock_guard<std::mutex> lock(mutex_);
1645     AddPageLocked(page);
1646     if (!pageRouteStack_.empty()) {
1647         pageMap_.erase(pageRouteStack_.back().pageId);
1648         pageParamMap_.erase(pageRouteStack_.back().pageId);
1649         ClearAlertCallback(pageRouteStack_.back());
1650         pageRouteStack_.pop_back();
1651     }
1652     pageRouteStack_.emplace_back(PageInfo { page->GetPageId(), url });
1653     auto pipelineContext = pipelineContextHolder_.Get();
1654     if (pipelineContext) {
1655         pipelineContext->onRouterChange(url);
1656         pipelineContext->NotifyPopPageSuccessDismiss(url, pageRouteStack_.back().pageId);
1657     }
1658 }
1659 
ReplacePage(const RefPtr<JsAcePage> & page,const std::string & url)1660 void FrontendDelegateImpl::ReplacePage(const RefPtr<JsAcePage>& page, const std::string& url)
1661 {
1662     LOGI("ReplacePage url = %{private}s", url.c_str());
1663     // Pop all JS command and execute them in UI thread.
1664     auto jsCommands = std::make_shared<std::vector<RefPtr<JsCommand>>>();
1665     page->PopAllCommands(*jsCommands);
1666 
1667     auto pipelineContext = pipelineContextHolder_.Get();
1668     page->SetPipelineContext(pipelineContext);
1669     taskExecutor_->PostTask(
1670         [weak = AceType::WeakClaim(this), page, url, jsCommands] {
1671             auto delegate = weak.Upgrade();
1672             if (!delegate) {
1673                 return;
1674             }
1675             delegate->SetCurrentReadyPage(page);
1676             auto pipelineContext = AceType::DynamicCast<PipelineContext>(delegate->pipelineContextHolder_.Get());
1677             CHECK_NULL_VOID(pipelineContext);
1678             bool useLiteStyle = delegate->GetMinPlatformVersion() < COMPATIBLE_VERSION && delegate->IsUseLiteStyle();
1679             pipelineContext->SetUseLiteStyle(useLiteStyle);
1680             page->SetUseLiteStyle(useLiteStyle);
1681             page->SetUseBoxWrap(delegate->GetMinPlatformVersion() >= WEB_FEATURE_VERSION);
1682             // Flush all JS commands.
1683             for (const auto& command : *jsCommands) {
1684                 command->Execute(page);
1685             }
1686             // Just clear all dirty nodes.
1687             page->ClearAllDirtyNodes();
1688             page->GetDomDocument()->HandleComponentPostBinding();
1689             if (pipelineContext->GetAccessibilityManager()) {
1690                 pipelineContext->GetAccessibilityManager()->HandleComponentPostBinding();
1691             }
1692             if (pipelineContext->CanReplacePage()) {
1693                 delegate->OnPageHide();
1694                 delegate->OnPageDestroy(delegate->GetRunningPageId());
1695                 pipelineContext->ReplacePage(page->BuildPage(url));
1696                 delegate->OnReplacePageSuccess(page, url);
1697                 delegate->SetCurrentPage(page->GetPageId());
1698                 delegate->OnPageShow();
1699                 delegate->OnMediaQueryUpdate();
1700             } else {
1701                 // This page has been loaded but become useless now, the corresponding js instance
1702                 // must be destroyed to avoid memory leak.
1703                 delegate->OnPageDestroy(page->GetPageId());
1704                 delegate->ResetStagingPage();
1705             }
1706             delegate->isStagingPageExist_ = false;
1707         },
1708         TaskExecutor::TaskType::UI, "ArkUIReplacePage");
1709 }
1710 
LoadReplacePage(int32_t pageId,const std::string & url,const std::string & params)1711 void FrontendDelegateImpl::LoadReplacePage(int32_t pageId, const std::string& url, const std::string& params)
1712 {
1713     {
1714         std::lock_guard<std::mutex> lock(mutex_);
1715         pageId_ = pageId;
1716         pageParamMap_[pageId] = params;
1717     }
1718     if (pageId == INVALID_PAGE_ID) {
1719         LOGE("FrontendDelegateImpl, invalid page id");
1720         EventReport::SendPageRouterException(PageRouterExcepType::REPLACE_PAGE_ERR, url);
1721         return;
1722     }
1723     if (isStagingPageExist_) {
1724         LOGE("FrontendDelegateImpl, replace page failed, waiting for current page loading finish.");
1725         EventReport::SendPageRouterException(PageRouterExcepType::REPLACE_PAGE_ERR, url);
1726         return;
1727     }
1728     isStagingPageExist_ = true;
1729     auto document = AceType::MakeRefPtr<DOMDocument>(pageId);
1730     auto page = AceType::MakeRefPtr<JsAcePage>(pageId, document, url);
1731     page->SetPageParams(params);
1732     taskExecutor_->PostTask(
1733         [page, url, weak = AceType::WeakClaim(this)] {
1734             auto delegate = weak.Upgrade();
1735             if (delegate) {
1736                 delegate->loadJs_(url, page, false);
1737                 delegate->ReplacePage(page, url);
1738             }
1739         },
1740         TaskExecutor::TaskType::JS, "ArkUILoadReplacePage");
1741 }
1742 
RebuildAllPages()1743 void FrontendDelegateImpl::RebuildAllPages()
1744 {
1745     std::unordered_map<int32_t, RefPtr<JsAcePage>> pages;
1746     {
1747         std::lock_guard<std::mutex> lock(mutex_);
1748         pages.insert(pageMap_.begin(), pageMap_.end());
1749     }
1750     for (const auto& [pageId, page] : pages) {
1751         taskExecutor_->PostTask(
1752             [weakPage = WeakPtr<JsAcePage>(page)] {
1753                 auto page = weakPage.Upgrade();
1754                 if (!page) {
1755                     return;
1756                 }
1757                 auto domDoc = page->GetDomDocument();
1758                 if (!domDoc) {
1759                     return;
1760                 }
1761                 auto rootNode = domDoc->GetDOMNodeById(domDoc->GetRootNodeId());
1762                 if (!rootNode) {
1763                     return;
1764                 }
1765                 rootNode->UpdateStyleWithChildren();
1766             },
1767             TaskExecutor::TaskType::UI, "ArkUIRebuildPage");
1768     }
1769 }
1770 
OnPageShow()1771 void FrontendDelegateImpl::OnPageShow()
1772 {
1773     FireAsyncEvent("_root", std::string("\"viewappear\",null,null"), std::string(""));
1774 }
1775 
OnPageHide()1776 void FrontendDelegateImpl::OnPageHide()
1777 {
1778     FireAsyncEvent("_root", std::string("\"viewdisappear\",null,null"), std::string(""));
1779 }
1780 
OnConfigurationUpdated(const std::string & configurationData)1781 void FrontendDelegateImpl::OnConfigurationUpdated(const std::string& configurationData)
1782 {
1783     FireSyncEvent("_root", std::string("\"onConfigurationUpdated\","), configurationData);
1784 }
1785 
ClearAlertCallback(PageInfo pageInfo)1786 void FrontendDelegateImpl::ClearAlertCallback(PageInfo pageInfo)
1787 {
1788     if (pageInfo.alertCallback) {
1789         // notify to clear js reference
1790         pageInfo.alertCallback(static_cast<int32_t>(AlertState::RECOVERY));
1791         pageInfo.alertCallback = nullptr;
1792     }
1793 }
1794 
OnPageDestroy(int32_t pageId)1795 void FrontendDelegateImpl::OnPageDestroy(int32_t pageId)
1796 {
1797     taskExecutor_->PostTask(
1798         [weak = AceType::WeakClaim(this), pageId] {
1799             auto delegate = weak.Upgrade();
1800             if (delegate) {
1801                 auto iter = delegate->pageMap_.find(pageId);
1802                 if (iter != delegate->pageMap_.end()) {
1803                     auto page = iter->second;
1804                     if (page) {
1805                         page->OnJsEngineDestroy();
1806                     }
1807                 }
1808                 delegate->destroyPage_(pageId);
1809                 delegate->RecyclePageId(pageId);
1810             }
1811         },
1812         TaskExecutor::TaskType::JS, "ArkUIPageDestroy");
1813 }
1814 
GetRunningPageId() const1815 int32_t FrontendDelegateImpl::GetRunningPageId() const
1816 {
1817     std::lock_guard<std::mutex> lock(mutex_);
1818     if (pageRouteStack_.empty()) {
1819         return INVALID_PAGE_ID;
1820     }
1821     return pageRouteStack_.back().pageId;
1822 }
1823 
GetRunningPageUrl() const1824 std::string FrontendDelegateImpl::GetRunningPageUrl() const
1825 {
1826     std::lock_guard<std::mutex> lock(mutex_);
1827     if (pageRouteStack_.empty()) {
1828         return std::string();
1829     }
1830     const auto& pageUrl = pageRouteStack_.back().url;
1831     auto pos = pageUrl.rfind(".js");
1832     // url length - (.js) length
1833     if (pos == pageUrl.length() - 3) {
1834         return pageUrl.substr(0, pos);
1835     }
1836     return pageUrl;
1837 }
1838 
GetPageIdByUrl(const std::string & url)1839 int32_t FrontendDelegateImpl::GetPageIdByUrl(const std::string& url)
1840 {
1841     std::lock_guard<std::mutex> lock(mutex_);
1842     auto pageIter = std::find_if(std::rbegin(pageRouteStack_), std::rend(pageRouteStack_),
1843         [&url](const PageInfo& pageRoute) { return url == pageRoute.url; });
1844     if (pageIter != std::rend(pageRouteStack_)) {
1845         return pageIter->pageId;
1846     }
1847     return INVALID_PAGE_ID;
1848 }
1849 
GetPage(int32_t pageId) const1850 RefPtr<JsAcePage> FrontendDelegateImpl::GetPage(int32_t pageId) const
1851 {
1852     std::lock_guard<std::mutex> lock(mutex_);
1853     auto itPage = pageMap_.find(pageId);
1854     if (itPage == pageMap_.end()) {
1855         LOGE("the page is not in the map");
1856         return nullptr;
1857     }
1858     return itPage->second;
1859 }
1860 
RegisterFont(const std::string & familyName,const std::string & familySrc,const std::string & bundleName,const std::string & moduleName)1861 void FrontendDelegateImpl::RegisterFont(const std::string& familyName, const std::string& familySrc,
1862     const std::string& bundleName, const std::string& moduleName)
1863 {
1864     pipelineContextHolder_.Get()->RegisterFont(familyName, familySrc, bundleName, moduleName);
1865 }
1866 
GetSystemFontList(std::vector<std::string> & fontList)1867 void FrontendDelegateImpl::GetSystemFontList(std::vector<std::string>& fontList)
1868 {
1869     pipelineContextHolder_.Get()->GetSystemFontList(fontList);
1870 }
1871 
GetSystemFont(const std::string & fontName,FontInfo & fontInfo)1872 bool FrontendDelegateImpl::GetSystemFont(const std::string& fontName, FontInfo& fontInfo)
1873 {
1874     return pipelineContextHolder_.Get()->GetSystemFont(fontName, fontInfo);
1875 }
1876 
GetUIFontConfig(FontConfigJsonInfo & fontConfigJsonInfo)1877 void FrontendDelegateImpl::GetUIFontConfig(FontConfigJsonInfo& fontConfigJsonInfo)
1878 {
1879     pipelineContextHolder_.Get()->GetUIFontConfig(fontConfigJsonInfo);
1880 }
1881 
HandleImage(const std::string & src,std::function<void (bool,int32_t,int32_t)> && callback)1882 void FrontendDelegateImpl::HandleImage(const std::string& src, std::function<void(bool, int32_t, int32_t)>&& callback)
1883 {
1884     if (src.empty() || !callback) {
1885         return;
1886     }
1887     auto loadCallback = [jsCallback = std::move(callback), taskExecutor = taskExecutor_](
1888                             bool success, int32_t width, int32_t height) {
1889         taskExecutor->PostTask(
1890             [callback = std::move(jsCallback), success, width, height] { callback(success, width, height); },
1891             TaskExecutor::TaskType::JS, "ArkUIHandleImageCallback");
1892     };
1893     pipelineContextHolder_.Get()->TryLoadImageInfo(src, std::move(loadCallback));
1894 }
1895 
RequestAnimationFrame(const std::string & callbackId)1896 void FrontendDelegateImpl::RequestAnimationFrame(const std::string& callbackId)
1897 {
1898     CancelableCallback<void()> cancelableTask;
1899     cancelableTask.Reset([callbackId, call = requestAnimationCallback_, weak = AceType::WeakClaim(this)] {
1900         auto delegate = weak.Upgrade();
1901         if (delegate && call) {
1902             call(callbackId, delegate->GetSystemRealTime());
1903         }
1904     });
1905     animationFrameTaskMap_.try_emplace(callbackId, cancelableTask);
1906     animationFrameTaskIds_.emplace(callbackId);
1907 }
1908 
GetSystemRealTime()1909 uint64_t FrontendDelegateImpl::GetSystemRealTime()
1910 {
1911     struct timespec ts;
1912     clock_gettime(CLOCK_REALTIME, &ts);
1913     return ts.tv_sec * TO_MILLI + ts.tv_nsec / NANO_TO_MILLI;
1914 }
1915 
CancelAnimationFrame(const std::string & callbackId)1916 void FrontendDelegateImpl::CancelAnimationFrame(const std::string& callbackId)
1917 {
1918     auto animationTaskIter = animationFrameTaskMap_.find(callbackId);
1919     if (animationTaskIter != animationFrameTaskMap_.end()) {
1920         animationTaskIter->second.Cancel();
1921         animationFrameTaskMap_.erase(animationTaskIter);
1922     } else {
1923         LOGW("cancelAnimationFrame callbackId not found");
1924     }
1925 }
1926 
FlushAnimationTasks()1927 void FrontendDelegateImpl::FlushAnimationTasks()
1928 {
1929     while (!animationFrameTaskIds_.empty()) {
1930         const auto& callbackId = animationFrameTaskIds_.front();
1931         if (!callbackId.empty()) {
1932             auto taskIter = animationFrameTaskMap_.find(callbackId);
1933             if (taskIter != animationFrameTaskMap_.end()) {
1934                 taskExecutor_->PostTask(taskIter->second, TaskExecutor::TaskType::JS, "ArkUIFlushAnimationTask");
1935             }
1936         }
1937         animationFrameTaskIds_.pop();
1938     }
1939 
1940     auto pageId = GetRunningPageId();
1941     auto page = GetPage(pageId);
1942     if (!page) {
1943         return;
1944     }
1945     auto jsPage = AceType::DynamicCast<Framework::JsAcePage>(page);
1946     if (jsPage && jsPage->GetCommandSize() > 0) {
1947         TriggerPageUpdate(pageId);
1948     }
1949 }
1950 
GetAnimationJsTask()1951 SingleTaskExecutor FrontendDelegateImpl::GetAnimationJsTask()
1952 {
1953     return SingleTaskExecutor::Make(taskExecutor_, TaskExecutor::TaskType::JS);
1954 }
1955 
GetUiTask()1956 SingleTaskExecutor FrontendDelegateImpl::GetUiTask()
1957 {
1958     return SingleTaskExecutor::Make(taskExecutor_, TaskExecutor::TaskType::UI);
1959 }
1960 
AttachPipelineContext(const RefPtr<PipelineBase> & context)1961 void FrontendDelegateImpl::AttachPipelineContext(const RefPtr<PipelineBase>& context)
1962 {
1963     context->SetOnPageShow([weak = AceType::WeakClaim(this)] {
1964         auto delegate = weak.Upgrade();
1965         if (delegate) {
1966             delegate->OnPageShow();
1967         }
1968     });
1969     context->SetAnimationCallback([weak = AceType::WeakClaim(this)] {
1970         auto delegate = weak.Upgrade();
1971         if (delegate) {
1972             delegate->FlushAnimationTasks();
1973         }
1974     });
1975     pipelineContextHolder_.Attach(context);
1976     jsAccessibilityManager_->SetPipelineContext(context);
1977     jsAccessibilityManager_->InitializeCallback();
1978 }
1979 
GetPipelineContext()1980 RefPtr<PipelineBase> FrontendDelegateImpl::GetPipelineContext()
1981 {
1982     return pipelineContextHolder_.Get();
1983 }
1984 
SetColorMode(ColorMode colorMode)1985 void FrontendDelegateImpl::SetColorMode(ColorMode colorMode)
1986 {
1987     mediaQueryInfo_->EnsureListenerIdValid();
1988     OnMediaQueryUpdate();
1989 }
1990 
LoadResourceConfiguration(std::map<std::string,std::string> & mediaResourceFileMap,std::unique_ptr<JsonValue> & currentResourceData)1991 void FrontendDelegateImpl::LoadResourceConfiguration(std::map<std::string, std::string>& mediaResourceFileMap,
1992     std::unique_ptr<JsonValue>& currentResourceData)
1993 {
1994     std::vector<std::string> files;
1995     if (assetManager_) {
1996         assetManager_->GetAssetList(RESOURCES_FOLDER, files);
1997     }
1998 
1999     std::set<std::string> resourceFolderName;
2000     for (const auto& file : files) {
2001         resourceFolderName.insert(file.substr(0, file.find_first_of("/")));
2002     }
2003 
2004     std::vector<std::string> sortedResourceFolderPath =
2005         AceResConfig::GetDeclarativeResourceFallback(resourceFolderName);
2006     for (const auto& folderName : sortedResourceFolderPath) {
2007         auto fileFullPath = std::string(RESOURCES_FOLDER) + folderName + std::string(I18N_FILE_SUFFIX);
2008         std::string content;
2009         if (GetAssetContent(fileFullPath, content)) {
2010             auto fileData = ParseFileData(content);
2011             if (fileData == nullptr) {
2012                 LOGW("parse %{private}s i18n content failed", fileFullPath.c_str());
2013             } else {
2014                 currentResourceData->Put(fileData);
2015             }
2016         }
2017     }
2018 
2019     std::set<std::string> mediaFileName;
2020     for (const auto& file : files) {
2021         auto mediaPathName = file.substr(file.find_first_of("/"));
2022         std::regex mediaPattern("^\\/media\\/\\w*(\\.jpg|\\.png|\\.gif|\\.svg|\\.webp|\\.bmp)$");
2023         std::smatch result;
2024         if (std::regex_match(mediaPathName, result, mediaPattern)) {
2025             mediaFileName.insert(mediaPathName.substr(mediaPathName.find_first_of("/")));
2026         }
2027     }
2028 
2029     auto currentResTag = AceResConfig::GetCurrentDeviceResTag();
2030     auto currentResolutionTag = currentResTag.substr(currentResTag.find_last_of("-") + 1);
2031     for (auto folderName : sortedResourceFolderPath) {
2032         for (auto fileName : mediaFileName) {
2033             if (mediaResourceFileMap.find(fileName) != mediaResourceFileMap.end()) {
2034                 continue;
2035             }
2036             auto fullFileName = folderName + fileName;
2037             if (std::find(files.begin(), files.end(), fullFileName) != files.end()) {
2038                 mediaResourceFileMap.emplace(fileName.substr(fileName.find_last_of("/") + 1),
2039                     std::string(RESOURCES_FOLDER).append(fullFileName));
2040             }
2041         }
2042         if (mediaResourceFileMap.size() == mediaFileName.size()) {
2043             break;
2044         }
2045     }
2046 }
2047 
PushJsCallbackToRenderNode(NodeId id,double ratio,std::function<void (bool,double)> && callback)2048 void FrontendDelegateImpl::PushJsCallbackToRenderNode(NodeId id, double ratio,
2049     std::function<void(bool, double)>&& callback)
2050 {
2051     auto visibleCallback = [jsCallback = std::move(callback), executor = taskExecutor_](bool visible, double ratio) {
2052         executor->PostTask(
2053             [task = std::move(jsCallback), visible, ratio] {
2054                 if (task) {
2055                     task(visible, ratio);
2056                 }
2057             },
2058             TaskExecutor::TaskType::JS, "ArkUIJsVisibleCallback");
2059     };
2060     auto uiPushTask = [id, ratio, visibleCallback,
2061                           pipeline = AceType::DynamicCast<PipelineContext>(pipelineContextHolder_.Get())]() {
2062         if (pipeline) {
2063             pipeline->PushVisibleCallback(id, ratio, visibleCallback);
2064         }
2065     };
2066     taskExecutor_->PostTask(uiPushTask, TaskExecutor::TaskType::UI, "ArkUIPushVisibleCallback");
2067 }
2068 
CallNativeHandler(const std::string & event,const std::string & params)2069 void FrontendDelegateImpl::CallNativeHandler(const std::string& event, const std::string& params)
2070 {
2071     if (callNativeHandler_ != nullptr) {
2072         callNativeHandler_(event, params);
2073     }
2074 }
2075 
2076 } // namespace OHOS::Ace::Framework
2077