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