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