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