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