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