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