1 /*
2 * Copyright (c) 2024 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 "bridge/cj_frontend/frontend/cj_frontend_abstract.h"
17
18 #include "securec.h"
19
20 #include "base/subwindow/subwindow_manager.h"
21 #include "bridge/cj_frontend/frontend/cj_frontend_loader.h"
22 #include "bridge/cj_frontend/runtime/cj_runtime_delegate.h"
23 #include "bridge/common/accessibility/accessibility_node_manager.h"
24 #include "core/common/font_manager.h"
25 #include "core/components/dialog/dialog_theme.h"
26 #include "core/pipeline_ng/pipeline_context.h"
27
28 using namespace OHOS::Ace::NG;
29 using namespace OHOS::Ace;
30
31 namespace OHOS::Ace {
32 namespace {
33 constexpr int32_t TOAST_TIME_MAX = 10000; // ms
34 constexpr int32_t TOAST_TIME_DEFAULT = 1500; // ms
35 constexpr int32_t CALLBACK_ERRORCODE_CANCEL = 1;
36 constexpr int32_t CALLBACK_DATACODE_ZERO = 0;
37
38 // helper function to run OverlayManager task
39 // ensures that the task runs in subwindow instead of main Window
MainWindowOverlay(std::function<void (RefPtr<NG::OverlayManager>)> && task,const std::string & name)40 void MainWindowOverlay(std::function<void(RefPtr<NG::OverlayManager>)>&& task, const std::string& name)
41 {
42 auto currentId = Container::CurrentId();
43 ContainerScope scope(currentId);
44 auto context = NG::PipelineContext::GetCurrentContext();
45 CHECK_NULL_VOID(context);
46 auto overlayManager = context->GetOverlayManager();
47 context->GetTaskExecutor()->PostTask(
48 [task = std::move(task), weak = WeakPtr<NG::OverlayManager>(overlayManager)] {
49 auto overlayManager = weak.Upgrade();
50 task(overlayManager);
51 },
52 TaskExecutor::TaskType::UI, name);
53 }
54 } // namespace
55
56 #if defined(PREVIEW)
TransferJsResponseDataPreview(int32_t callbackId,int32_t code,ResponseData responseData) const57 void CJFrontendAbstract::TransferJsResponseDataPreview(
58 int32_t callbackId, int32_t code, ResponseData responseData) const
59 {}
60 #endif
61
~CJFrontendAbstract()62 CJFrontendAbstract::~CJFrontendAbstract()
63 {
64 LOGD("CJFrontendAbstract destroyed.");
65 }
66
Initialize(FrontendType type,const RefPtr<OHOS::Ace::TaskExecutor> & taskExecutor)67 bool CJFrontendAbstract::Initialize(FrontendType type, const RefPtr<OHOS::Ace::TaskExecutor>& taskExecutor)
68 {
69 if (type != FrontendType::DECLARATIVE_CJ) {
70 LOGE("CJFrontendAbstract Initialize failed, FrontendType only accept DECLARATIVE_CJ");
71 return false;
72 }
73 LOGD("CJFrontendAbstract initialize begin.");
74 taskExecutor_ = taskExecutor;
75 manifestParser_ = AceType::MakeRefPtr<Framework::ManifestParser>();
76 accessibilityManager_ = Framework::AccessibilityNodeManager::Create();
77
78 type_ = type;
79 InternalInitialize();
80 if (!pageRouterManager_) {
81 LOGE("InternalInitialize must initialize pageRouterManager_");
82 return false;
83 }
84 return true;
85 }
86
FlushReload()87 void CJFrontendAbstract::FlushReload()
88 {
89 if (!Container::IsCurrentUseNewPipeline()) {
90 LOGW("not support old pipeline");
91 return;
92 }
93 pageRouterManager_->FlushReload();
94 }
95
RebuildAllPages()96 void CJFrontendAbstract::RebuildAllPages()
97 {
98 CHECK_NULL_VOID(pageRouterManager_);
99 auto url = pageRouterManager_->GetCurrentPageUrl();
100 pageRouterManager_->Clear();
101 pageRouterManager_->RunPage(url, "");
102 }
103
NavigatePage(uint8_t type,const PageTarget & target,const std::string & params)104 void CJFrontendAbstract::NavigatePage(uint8_t type, const PageTarget& target, const std::string& params)
105 {
106 switch (static_cast<NavigatorType>(type)) {
107 case NavigatorType::PUSH:
108 PushPage(target.url, params);
109 break;
110 case NavigatorType::REPLACE:
111 ReplacePage(target.url, params);
112 break;
113 case NavigatorType::BACK:
114 Back(target.url, params);
115 break;
116 default:
117 LOGE("Navigator type is invalid!");
118 Back(target.url, params);
119 }
120 }
121
OnBackPressed()122 bool CJFrontendAbstract::OnBackPressed()
123 {
124 return pageRouterManager_->PopWithExitCheck();
125 }
126
OnShow()127 void CJFrontendAbstract::OnShow()
128 {
129 foregroundFrontend_ = true;
130 pageRouterManager_->OnShowCurrent();
131 }
132
OnHide()133 void CJFrontendAbstract::OnHide()
134 {
135 foregroundFrontend_ = false;
136 pageRouterManager_->OnHideCurrent();
137 }
138
Destroy()139 void CJFrontendAbstract::Destroy()
140 {
141 LOGD("CJFrontendAbstract Destroy begin.");
142 }
143
CheckLoadAppLibrary()144 bool CJFrontendAbstract::CheckLoadAppLibrary()
145 {
146 return Framework::CJRuntimeDelegate::GetInstance()->CheckLoadCJLibrary();
147 }
148
AttachPipelineContext(const RefPtr<PipelineBase> & context)149 void CJFrontendAbstract::AttachPipelineContext(const RefPtr<PipelineBase>& context)
150 {
151 pipelineContextHolder_.Attach(context);
152 auto jsAccessibility = AceType::DynamicCast<Framework::AccessibilityNodeManager>(accessibilityManager_);
153 jsAccessibility->SetPipelineContext(context);
154 jsAccessibility->InitializeCallback();
155 }
156
SetAssetManager(const RefPtr<AssetManager> & assetManager)157 void CJFrontendAbstract::SetAssetManager(const RefPtr<AssetManager>& assetManager)
158 {
159 assetManager_ = assetManager;
160 }
161
RunPage(const std::string & url,const std::string & params)162 UIContentErrorCode CJFrontendAbstract::RunPage(const std::string& url, const std::string& params)
163 {
164 LOGI("CJFrontendAbstract::RunPage start: %{public}s", url.c_str());
165 if (!isStageModel_) {
166 if (!CheckLoadAppLibrary()) {
167 TAG_LOGW(AceLogTag::ACE_FORM, "fail to run page due to path url is empty");
168 return UIContentErrorCode::NULL_URL;
169 }
170 }
171 InternalRunPage(url, params);
172 return UIContentErrorCode::NO_ERRORS;
173 }
174
ReplacePage(const std::string & url,const std::string & params)175 void CJFrontendAbstract::ReplacePage(const std::string& url, const std::string& params)
176 {
177 pageRouterManager_->Replace({ url }, params);
178 }
179
PushPage(const std::string & url,const std::string & params)180 void CJFrontendAbstract::PushPage(const std::string& url, const std::string& params)
181 {
182 pageRouterManager_->Push({ url }, params);
183 }
184
Back(const std::string & uri,const std::string & params)185 void CJFrontendAbstract::Back(const std::string& uri, const std::string& params)
186 {
187 pageRouterManager_->BackWithTarget({ uri }, params);
188 }
189
CallRouterBack()190 void CJFrontendAbstract::CallRouterBack()
191 {
192 pageRouterManager_->Pop();
193 }
194
InternalRunPage(const std::string & url,const std::string & params)195 void CJFrontendAbstract::InternalRunPage(const std::string& url, const std::string& params)
196 {
197 LOGI("InternalRunPage %{public}s", url.c_str());
198 taskExecutor_->PostTask(
199 [pageRouterManager = pageRouterManager_, url, params] { pageRouterManager->RunPage(url, params); },
200 TaskExecutor::TaskType::UI, "CJFrontendInternalRunPage");
201 }
202
MeasureText(const MeasureContext & context)203 double CJFrontendAbstract::MeasureText(const MeasureContext& context)
204 {
205 return MeasureUtil::MeasureText(context);
206 }
207
MeasureTextSize(const MeasureContext & context)208 Size CJFrontendAbstract::MeasureTextSize(const MeasureContext& context)
209 {
210 LOGI("CJFrontendAbstract::MeasureTextSize start");
211 return MeasureUtil::MeasureTextSize(context);
212 }
213
ShowToast(const std::string & message,int32_t duration,const std::string & bottom,const NG::ToastShowMode & showMode)214 void CJFrontendAbstract::ShowToast(
215 const std::string& message, int32_t duration, const std::string& bottom, const NG::ToastShowMode& showMode)
216 {
217 int32_t durationTime = std::clamp(duration, TOAST_TIME_DEFAULT, TOAST_TIME_MAX);
218 bool isRightToLeft = AceApplicationInfo::GetInstance().IsRightToLeft();
219 auto task = [durationTime, message, bottom, isRightToLeft, showMode, containerId = Container::CurrentId()](
220 const RefPtr<NG::OverlayManager>& overlayManager) {
221 CHECK_NULL_VOID(overlayManager);
222 ContainerScope scope(containerId);
223 auto toastInfo = NG::ToastInfo { .message = message,
224 .duration = durationTime,
225 .bottom = bottom,
226 .isRightToLeft = isRightToLeft,
227 .showMode = showMode,
228 .alignment = -1,
229 .offset = std::nullopt };
230 overlayManager->ShowToast(toastInfo, nullptr);
231 };
232 MainWindowOverlay(std::move(task), "ArkUIOverlayShowToast");
233 }
234
ShowDialog(const std::string & title,const std::string & message,const std::vector<ButtonInfo> & buttons,std::function<void (int32_t,int32_t)> && callback,const std::set<std::string> & callbacks)235 void CJFrontendAbstract::ShowDialog(const std::string& title, const std::string& message,
236 const std::vector<ButtonInfo>& buttons, std::function<void(int32_t, int32_t)>&& callback,
237 const std::set<std::string>& callbacks)
238 {
239 DialogProperties dialogProperties = { .title = title, .content = message, .buttons = buttons };
240 ShowDialogInner(dialogProperties, std::move(callback), callbacks);
241 }
242
Replace(const std::string & url,const std::string & params,CJPageRouterAbstract::RouterMode modeValue)243 void CJFrontendAbstract::Replace(const std::string& url, const std::string& params,
244 CJPageRouterAbstract::RouterMode modeValue)
245 {
246 pageRouterManager_->Replace({ url }, params, modeValue);
247 }
248
ShowDialogInner(DialogProperties & dialogProperties,std::function<void (int32_t,int32_t)> && callback,const std::set<std::string> & callbacks)249 void CJFrontendAbstract::ShowDialogInner(DialogProperties& dialogProperties,
250 std::function<void(int32_t, int32_t)>&& callback, const std::set<std::string>& callbacks)
251 {
252 auto pipelineContext = pipelineContextHolder_.Get();
253 LOGI("Dialog IsCurrentUseNewPipeline.");
254 dialogProperties.onCancel = [callback, taskExecutor = taskExecutor_] {
255 taskExecutor->PostTask([callback]() { callback(CALLBACK_ERRORCODE_CANCEL, CALLBACK_DATACODE_ZERO); },
256 TaskExecutor::TaskType::JS, "CJFroentendShowDialogInner");
257 };
258 dialogProperties.onSuccess = std::move(callback);
259 auto task = [dialogProperties](const RefPtr<NG::OverlayManager>& overlayManager) {
260 CHECK_NULL_VOID(overlayManager);
261 RefPtr<NG::FrameNode> dialog;
262 LOGI("Begin to show dialog ");
263 if (dialogProperties.isShowInSubWindow) {
264 dialog = SubwindowManager::GetInstance()->ShowDialogNG(dialogProperties, nullptr);
265 CHECK_NULL_VOID(dialog);
266 if (dialogProperties.isModal) {
267 DialogProperties maskarg;
268 maskarg.isMask = true;
269 maskarg.autoCancel = dialogProperties.autoCancel;
270 auto mask = overlayManager->ShowDialog(maskarg, nullptr, false);
271 CHECK_NULL_VOID(mask);
272 overlayManager->SetMaskNodeId(dialog->GetId(), mask->GetId());
273 }
274 } else {
275 dialog = overlayManager->ShowDialog(
276 dialogProperties, nullptr, AceApplicationInfo::GetInstance().IsRightToLeft());
277 CHECK_NULL_VOID(dialog);
278 }
279 };
280 MainWindowOverlay(std::move(task), "ArkUIShowDialogInner");
281 }
282
ShowActionMenu(const std::string & title,const std::vector<ButtonInfo> & button,std::function<void (int32_t,int32_t)> && callback)283 void CJFrontendAbstract::ShowActionMenu(
284 const std::string& title, const std::vector<ButtonInfo>& button, std::function<void(int32_t, int32_t)>&& callback)
285 {
286 DialogProperties dialogProperties = {
287 .title = title,
288 .autoCancel = true,
289 .isMenu = true,
290 .buttons = button,
291 };
292 ShowActionMenuInner(dialogProperties, button, std::move(callback));
293 }
294
ShowActionMenuInner(DialogProperties & dialogProperties,const std::vector<ButtonInfo> & button,std::function<void (int32_t,int32_t)> && callback)295 void CJFrontendAbstract::ShowActionMenuInner(DialogProperties& dialogProperties, const std::vector<ButtonInfo>& button,
296 std::function<void(int32_t, int32_t)>&& callback)
297 {
298 auto pipeline = PipelineBase::GetCurrentContext();
299 CHECK_NULL_VOID(pipeline);
300 auto theme = pipeline->GetTheme<DialogTheme>();
301 CHECK_NULL_VOID(theme);
302 ButtonInfo buttonInfo = { .text = theme->GetCancelText(), .textColor = "" };
303 dialogProperties.buttons.emplace_back(buttonInfo);
304 dialogProperties.onCancel = [callback, taskExecutor = taskExecutor_] {
305 taskExecutor->PostTask([callback]() { callback(CALLBACK_ERRORCODE_CANCEL, CALLBACK_DATACODE_ZERO); },
306 TaskExecutor::TaskType::JS, "CJFroentendShowActionMenuInnerOnCancel");
307 };
308 dialogProperties.onSuccess = std::move(callback);
309 auto context = DynamicCast<NG::PipelineContext>(pipelineContextHolder_.Get());
310 auto overlayManager = context ? context->GetOverlayManager() : nullptr;
311 taskExecutor_->PostTask(
312 [dialogProperties, weak = WeakPtr<NG::OverlayManager>(overlayManager)] {
313 auto overlayManager = weak.Upgrade();
314 CHECK_NULL_VOID(overlayManager);
315 RefPtr<NG::FrameNode> dialog;
316 if (dialogProperties.isShowInSubWindow) {
317 dialog = SubwindowManager::GetInstance()->ShowDialogNG(dialogProperties, nullptr);
318 CHECK_NULL_VOID(dialog);
319 if (dialogProperties.isModal) {
320 DialogProperties maskarg;
321 maskarg.autoCancel = dialogProperties.autoCancel;
322 maskarg.isMask = true;
323 auto mask = overlayManager->ShowDialog(maskarg, nullptr, false);
324 CHECK_NULL_VOID(mask);
325 overlayManager->SetMaskNodeId(dialog->GetId(), mask->GetId());
326 }
327 } else {
328 dialog = overlayManager->ShowDialog(
329 dialogProperties, nullptr, AceApplicationInfo::GetInstance().IsRightToLeft());
330 CHECK_NULL_VOID(dialog);
331 }
332 },
333 TaskExecutor::TaskType::UI, "CJFroentendShowActionMenuInner");
334 }
335
OpenCustomDialog(const PromptDialogAttr & dialogAttr,std::function<void (int32_t)> && callback)336 void CJFrontendAbstract::OpenCustomDialog(const PromptDialogAttr& dialogAttr, std::function<void(int32_t)>&& callback)
337 {
338 DialogProperties dialogProperties = { .onWillDismiss = dialogAttr.customOnWillDismiss,
339 .isShowInSubWindow = dialogAttr.showInSubWindow,
340 .isModal = dialogAttr.isModal,
341 .isSysBlurStyle = false,
342 .customBuilder = dialogAttr.customBuilder,
343 .maskRect = dialogAttr.maskRect };
344 #if defined(PREVIEW)
345 if (dialogProperties.isShowInSubWindow) {
346 LOGW("[Engine Log] Unable to use the SubWindow in the Previewer. Perform this operation on the "
347 "emulator or a real device instead.");
348 dialogProperties.isShowInSubWindow = false;
349 }
350 #endif
351 if (dialogAttr.alignment.has_value()) {
352 dialogProperties.alignment = dialogAttr.alignment.value();
353 }
354 if (dialogAttr.offset.has_value()) {
355 dialogProperties.offset = dialogAttr.offset.value();
356 }
357 if (!Container::IsCurrentUseNewPipeline()) {
358 LOGW("not support old pipeline");
359 return;
360 }
361 LOGI("Dialog IsCurrentUseNewPipeline.");
362 auto task = [dialogAttr, dialogProperties, callback](const RefPtr<NG::OverlayManager>& overlayManager) mutable {
363 CHECK_NULL_VOID(overlayManager);
364 LOGI("Begin to open custom dialog ");
365 if (dialogProperties.isShowInSubWindow) {
366 SubwindowManager::GetInstance()->OpenCustomDialogNG(dialogProperties, std::move(callback));
367 if (dialogProperties.isModal) {
368 LOGW("temporary not support isShowInSubWindow and isModal");
369 }
370 } else {
371 overlayManager->OpenCustomDialog(dialogProperties, std::move(callback));
372 }
373 };
374 MainWindowOverlay(std::move(task), "ArkUIOpenCustomDialog");
375 return;
376 }
377
CloseCustomDialog(int32_t id)378 void CJFrontendAbstract::CloseCustomDialog(int32_t id)
379 {
380 auto task = [id](const RefPtr<NG::OverlayManager>& overlayManager) {
381 CHECK_NULL_VOID(overlayManager);
382 LOGI("begin to close custom dialog.");
383 overlayManager->CloseCustomDialog(id);
384 SubwindowManager::GetInstance()->CloseCustomDialogNG(id);
385 };
386 MainWindowOverlay(std::move(task), "ArkUICloseCustomDialog");
387 return;
388 }
389
RegisterFont(const std::string & familyName,const std::string & familySrc,const std::string & bundleName,const std::string & moduleName)390 void CJFrontendAbstract::RegisterFont(const std::string& familyName, const std::string& familySrc,
391 const std::string& bundleName, const std::string& moduleName)
392 {
393 pipelineContextHolder_.Get()->RegisterFont(familyName, familySrc, bundleName, moduleName);
394 }
395
GetSystemFontList()396 VectorStringHandle CJFrontendAbstract::GetSystemFontList()
397 {
398 auto fontList = new std::vector<std::string>;
399 pipelineContextHolder_.Get()->GetSystemFontList(*fontList);
400 return fontList;
401 }
402
GetSystemFont(const std::string & fontName)403 NativeOptionFontInfo CJFrontendAbstract::GetSystemFont(const std::string& fontName)
404 {
405 FontInfo fontInfo;
406 if (!pipelineContextHolder_.Get()->GetSystemFont(fontName, fontInfo)) {
407 return NativeOptionFontInfo { .hasValue = false, .info = nullptr };
408 }
409 return NativeOptionFontInfo { .hasValue = true,
410 .info = new NativeFontInfo { .path = fontInfo.path.c_str(),
411 .postScriptName = fontInfo.postScriptName.c_str(),
412 .fullName = fontInfo.fullName.c_str(),
413 .family = fontInfo.family.c_str(),
414 .subfamily = fontInfo.subfamily.c_str(),
415 .weight = fontInfo.weight,
416 .width = fontInfo.width,
417 .italic = fontInfo.italic,
418 .monoSpace = fontInfo.monoSpace,
419 .symbolic = fontInfo.symbolic } };
420 }
BackIndex(int32_t index,const std::string & params)421 void CJFrontendAbstract::BackIndex(int32_t index, const std::string& params)
422 {
423 pageRouterManager_->BackWithIndex(index, params);
424 }
425
Clear()426 void CJFrontendAbstract::Clear()
427 {
428 pageRouterManager_->Clear();
429 }
430
GetLength()431 int32_t CJFrontendAbstract::GetLength()
432 {
433 return pageRouterManager_->GetStackSize();
434 }
435
SetShowAlertBeforeBackPage(const char * msg,std::function<void (int32_t)> && callback)436 void CJFrontendAbstract::SetShowAlertBeforeBackPage(const char* msg, std::function<void(int32_t)>&& callback)
437 {
438 pageRouterManager_->EnableAlertBeforeBackPage(msg, callback);
439 }
440
SetHideAlertBeforeBackPage()441 void CJFrontendAbstract::SetHideAlertBeforeBackPage()
442 {
443 pageRouterManager_->DisableAlertBeforeBackPage();
444 }
445
CopyStr(const std::string & str)446 static char* CopyStr(const std::string& str)
447 {
448 char* newStr = new (std::nothrow) char[str.length() + 1];
449 if (newStr == nullptr) {
450 return nullptr;
451 }
452
453 int err = strcpy_s(newStr, str.length() + 1, str.c_str());
454 if (err != 0) {
455 delete[] newStr;
456 return nullptr;
457 }
458
459 return newStr;
460 }
461
GetState(CJPageRouterAbstract::RouterState * info)462 void CJFrontendAbstract::GetState(CJPageRouterAbstract::RouterState* info)
463 {
464 std::string name_str = "";
465 std::string path_str = "";
466 std::string params_str = "";
467 pageRouterManager_->GetState(info->index, name_str, path_str, params_str);
468 info->name = CopyStr(name_str);
469 info->path = CopyStr(path_str);
470 info->params = CopyStr(params_str);
471 }
472
GetStateByIndex(CJPageRouterAbstract::RouterState * info)473 void CJFrontendAbstract::GetStateByIndex(CJPageRouterAbstract::RouterState* info)
474 {
475 std::string name_str = "";
476 std::string path_str = "";
477 std::string params_str = "";
478 pageRouterManager_->GetStateByIndex(info->index, name_str, path_str, params_str);
479 info->name = CopyStr(name_str);
480 info->path = CopyStr(path_str);
481 info->params = CopyStr(params_str);
482 }
483
GetStateByUrl(const char * url)484 CJPageRouterAbstract::RouterStateList CJFrontendAbstract::GetStateByUrl(const char* url)
485 {
486 CJPageRouterAbstract::RouterStateList result;
487 std::vector<CJPageRouterAbstract::RouterState> states = pageRouterManager_->GetStateByUrl(url);
488 if (states.empty()) {
489 return result;
490 }
491 auto stateslist = new CJPageRouterAbstract::RouterState[states.size()];
492 size_t idx = 0;
493 for (auto state : states) {
494 stateslist[idx].index = state.index;
495 stateslist[idx].name = state.name;
496 stateslist[idx].path = state.path;
497 stateslist[idx].params = state.params;
498 idx++;
499 }
500 result.array = stateslist;
501 result.size = static_cast<int64_t>(states.size());
502 result.free = CJPageRouterAbstract::RouterStateListFree;
503 return result;
504 }
PushPageWithCallback(const std::string & url,const std::string & params,CJPageRouterAbstract::RouterMode & mode,std::function<void (int32_t)> && callback)505 void CJFrontendAbstract::PushPageWithCallback(const std::string& url, const std::string& params,
506 CJPageRouterAbstract::RouterMode& mode, std::function<void(int32_t)>&& callback)
507 {
508 pageRouterManager_->PushPageWithCallback({ url, mode, "", callback }, params);
509 }
510
ReplacePageWithCallback(const std::string & url,const std::string & params,CJPageRouterAbstract::RouterMode & mode,std::function<void (int32_t)> && callback)511 void CJFrontendAbstract::ReplacePageWithCallback(const std::string& url, const std::string& params,
512 CJPageRouterAbstract::RouterMode& mode, std::function<void(int32_t)>&& callback)
513 {
514 pageRouterManager_->ReplacePageWithCallback({ url, mode, "", callback }, params);
515 }
516
517 } // namespace OHOS::Ace
518