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