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