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