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