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