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