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