1 /*
2 * Copyright (c) 2021-2022 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/declarative_frontend/jsview/dialog/js_custom_dialog_controller.h"
17
18 #include "base/subwindow/subwindow_manager.h"
19 #include "base/utils/system_properties.h"
20 #include "base/utils/utils.h"
21 #include "bridge/declarative_frontend/engine/jsi/jsi_types.h"
22 #include "bridge/declarative_frontend/jsview/models/custom_dialog_controller_model_impl.h"
23 #include "core/common/ace_engine.h"
24 #include "core/common/container.h"
25 #include "core/components_ng/base/view_stack_processor.h"
26 #include "core/components_ng/pattern/dialog/custom_dialog_controller_model_ng.h"
27 #include "core/pipeline_ng/pipeline_context.h"
28 #include "frameworks/bridge/common/utils/engine_helper.h"
29 #include "frameworks/bridge/declarative_frontend/jsview/js_view_abstract.h"
30
31 namespace OHOS::Ace {
32 std::unique_ptr<CustomDialogControllerModel> CustomDialogControllerModel::instance_ = nullptr;
33 std::mutex CustomDialogControllerModel::mutex_;
GetInstance()34 CustomDialogControllerModel* CustomDialogControllerModel::GetInstance()
35 {
36 if (!instance_) {
37 std::lock_guard<std::mutex> lock(mutex_);
38 if (!instance_) {
39 #ifdef NG_BUILD
40 instance_.reset(new NG::CustomDialogControllerModelNG());
41 #else
42 if (Container::IsCurrentUseNewPipeline()) {
43 instance_.reset(new NG::CustomDialogControllerModelNG());
44 } else {
45 instance_.reset(new Framework::CustomDialogControllerModelImpl());
46 }
47 #endif
48 }
49 }
50 return instance_.get();
51 }
52 } // namespace OHOS::Ace
53
54 namespace OHOS::Ace::Framework {
55 namespace {
56 const std::vector<DialogAlignment> DIALOG_ALIGNMENT = { DialogAlignment::TOP, DialogAlignment::CENTER,
57 DialogAlignment::BOTTOM, DialogAlignment::DEFAULT, DialogAlignment::TOP_START, DialogAlignment::TOP_END,
58 DialogAlignment::CENTER_START, DialogAlignment::CENTER_END, DialogAlignment::BOTTOM_START,
59 DialogAlignment::BOTTOM_END };
60 const std::vector<KeyboardAvoidMode> KEYBOARD_AVOID_MODE = { KeyboardAvoidMode::DEFAULT, KeyboardAvoidMode::NONE };
61 constexpr int32_t DEFAULT_ANIMATION_DURATION = 200;
62
63 } // namespace
64
ConstructorCallback(const JSCallbackInfo & info)65 void JSCustomDialogController::ConstructorCallback(const JSCallbackInfo& info)
66 {
67 int argc = info.Length();
68 if (argc > 1 && !info[0]->IsUndefined() && info[0]->IsObject() && !info[1]->IsUndefined() && info[1]->IsObject()) {
69 JSRef<JSObject> constructorArg = JSRef<JSObject>::Cast(info[0]);
70 JSRef<JSObject> ownerObj = JSRef<JSObject>::Cast(info[1]);
71
72 // check if owner object is set
73 JSView* ownerView = ownerObj->Unwrap<JSView>();
74 auto instance = AceType::MakeRefPtr<JSCustomDialogController>(ownerView);
75 if (ownerView == nullptr) {
76 instance->IncRefCount();
77 info.SetReturnValue(AceType::RawPtr(instance));
78 instance = nullptr;
79 return;
80 }
81
82 // Process builder function.
83 JSRef<JSVal> builderCallback = constructorArg->GetProperty("builder");
84 if (!builderCallback->IsUndefined() && builderCallback->IsFunction()) {
85 instance->jsBuilderFunction_ =
86 AceType::MakeRefPtr<JsWeakFunction>(ownerObj, JSRef<JSFunc>::Cast(builderCallback));
87 } else {
88 instance->jsBuilderFunction_ = nullptr;
89 instance->IncRefCount();
90 info.SetReturnValue(AceType::RawPtr(instance));
91 instance = nullptr;
92 return;
93 }
94
95 // Process cancel function.
96 JSRef<JSVal> cancelCallback = constructorArg->GetProperty("cancel");
97 if (!cancelCallback->IsUndefined() && cancelCallback->IsFunction()) {
98 auto frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
99 auto jsCancelFunction = AceType::MakeRefPtr<JsWeakFunction>(ownerObj, JSRef<JSFunc>::Cast(cancelCallback));
100 instance->jsCancelFunction_ = jsCancelFunction;
101
102 auto onCancel = [execCtx = info.GetExecutionContext(), func = std::move(jsCancelFunction),
103 node = frameNode]() {
104 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
105 ACE_SCORING_EVENT("onCancel");
106 auto pipelineContext = PipelineContext::GetCurrentContext();
107 CHECK_NULL_VOID(pipelineContext);
108 pipelineContext->UpdateCurrentActiveNode(node);
109 func->Execute();
110 };
111 instance->dialogProperties_.onCancel = onCancel;
112 }
113
114 std::function<void(const int32_t& info)> onWillDismissFunc = nullptr;
115 JSViewAbstract::ParseDialogCallback(constructorArg, onWillDismissFunc);
116 instance->dialogProperties_.onWillDismiss = onWillDismissFunc;
117
118 // Parses autoCancel.
119 JSRef<JSVal> autoCancelValue = constructorArg->GetProperty("autoCancel");
120 if (autoCancelValue->IsBoolean()) {
121 instance->dialogProperties_.autoCancel = autoCancelValue->ToBoolean();
122 }
123
124 // Parses customStyle.
125 JSRef<JSVal> customStyleValue = constructorArg->GetProperty("customStyle");
126 if (customStyleValue->IsBoolean()) {
127 instance->dialogProperties_.customStyle = customStyleValue->ToBoolean();
128 }
129
130 // Parse alignment
131 auto alignmentValue = constructorArg->GetProperty("alignment");
132 if (alignmentValue->IsNumber()) {
133 auto alignment = alignmentValue->ToNumber<int32_t>();
134 if (alignment >= 0 && alignment <= static_cast<int32_t>(DIALOG_ALIGNMENT.size())) {
135 instance->dialogProperties_.alignment = DIALOG_ALIGNMENT[alignment];
136 }
137 }
138
139 // Parse keyboardAvoidMode
140 auto avoidModeValue = constructorArg->GetProperty("keyboardAvoidMode");
141 if (avoidModeValue->IsNumber()) {
142 auto avoidMode = avoidModeValue->ToNumber<int32_t>();
143 if (avoidMode >= 0 && avoidMode < static_cast<int32_t>(KEYBOARD_AVOID_MODE.size())) {
144 instance->dialogProperties_.keyboardAvoidMode = KEYBOARD_AVOID_MODE[avoidMode];
145 }
146 }
147
148 // Parse offset
149 auto offsetValue = constructorArg->GetProperty("offset");
150 if (offsetValue->IsObject()) {
151 auto offsetObj = JSRef<JSObject>::Cast(offsetValue);
152 CalcDimension dx;
153 auto dxValue = offsetObj->GetProperty("dx");
154 JSViewAbstract::ParseJsDimensionVp(dxValue, dx);
155 CalcDimension dy;
156 auto dyValue = offsetObj->GetProperty("dy");
157 JSViewAbstract::ParseJsDimensionVp(dyValue, dy);
158 dx.ResetInvalidValue();
159 dy.ResetInvalidValue();
160 instance->dialogProperties_.offset = DimensionOffset(dx, dy);
161 }
162
163 // Parses gridCount.
164 auto gridCountValue = constructorArg->GetProperty("gridCount");
165 if (gridCountValue->IsNumber()) {
166 instance->dialogProperties_.gridCount = gridCountValue->ToNumber<int32_t>();
167 }
168
169 // Parse maskColor.
170 auto maskColorValue = constructorArg->GetProperty("maskColor");
171 Color maskColor;
172 if (JSViewAbstract::ParseJsColor(maskColorValue, maskColor)) {
173 instance->dialogProperties_.maskColor = maskColor;
174 }
175
176 // Parse maskRect.
177 auto maskRectValue = constructorArg->GetProperty("maskRect");
178 DimensionRect maskRect;
179 if (JSViewAbstract::ParseJsDimensionRect(maskRectValue, maskRect)) {
180 instance->dialogProperties_.maskRect = maskRect;
181 }
182
183 // Parse backgroundColor.
184 auto backgroundColorValue = constructorArg->GetProperty("backgroundColor");
185 Color backgroundColor;
186 if (JSViewAbstract::ParseJsColor(backgroundColorValue, backgroundColor)) {
187 instance->dialogProperties_.backgroundColor = backgroundColor;
188 }
189
190 // Parse backgroundBlurStyle.
191 auto backgroundBlurStyle = constructorArg->GetProperty("backgroundBlurStyle");
192 if (backgroundBlurStyle->IsNumber()) {
193 auto blurStyle = backgroundBlurStyle->ToNumber<int32_t>();
194 if (blurStyle >= static_cast<int>(BlurStyle::NO_MATERIAL) &&
195 blurStyle <= static_cast<int>(BlurStyle::COMPONENT_ULTRA_THICK)) {
196 instance->dialogProperties_.backgroundBlurStyle = blurStyle;
197 }
198 }
199
200 auto execContext = info.GetExecutionContext();
201 // Parse openAnimation.
202 auto openAnimationValue = constructorArg->GetProperty("openAnimation");
203 AnimationOption openAnimation;
204 if (ParseAnimation(execContext, openAnimationValue, openAnimation)) {
205 instance->dialogProperties_.openAnimation = openAnimation;
206 }
207
208 // Parse closeAnimation.
209 auto closeAnimationValue = constructorArg->GetProperty("closeAnimation");
210 AnimationOption closeAnimation;
211 if (ParseAnimation(execContext, closeAnimationValue, closeAnimation)) {
212 instance->dialogProperties_.closeAnimation = closeAnimation;
213 }
214
215 // Parse showInSubWindowValue.
216 auto showInSubWindowValue = constructorArg->GetProperty("showInSubWindow");
217 if (showInSubWindowValue->IsBoolean()) {
218 #if defined(PREVIEW)
219 LOGW("[Engine Log] Unable to use the SubWindow in the Previewer. Perform this operation on the "
220 "emulator or a real device instead.");
221 #else
222 instance->dialogProperties_.isShowInSubWindow = showInSubWindowValue->ToBoolean();
223 #endif
224 }
225
226 // Parse isModal.
227 auto isModalValue = constructorArg->GetProperty("isModal");
228 if (isModalValue->IsBoolean()) {
229 instance->dialogProperties_.isModal = isModalValue->ToBoolean();
230 }
231 JSViewAbstract::SetDialogProperties(constructorArg, instance->dialogProperties_);
232 instance->IncRefCount();
233 info.SetReturnValue(AceType::RawPtr(instance));
234 }
235 }
236
DestructorCallback(JSCustomDialogController * controller)237 void JSCustomDialogController::DestructorCallback(JSCustomDialogController* controller)
238 {
239 if (controller != nullptr) {
240 controller->ownerView_ = nullptr;
241 controller->DecRefCount();
242 }
243 }
244
JsOpenDialog(const JSCallbackInfo & info)245 void JSCustomDialogController::JsOpenDialog(const JSCallbackInfo& info)
246 {
247 if (!jsBuilderFunction_) {
248 return;
249 }
250
251 if (this->ownerView_ == nullptr) {
252 return;
253 }
254 auto containerId = this->ownerView_->GetInstanceId();
255 ContainerScope containerScope(containerId);
256 WeakPtr<NG::FrameNode> frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
257 auto pipelineContext = PipelineContext::GetCurrentContext();
258 CHECK_NULL_VOID(pipelineContext);
259
260 auto scopedDelegate = EngineHelper::GetCurrentDelegate();
261 if (!scopedDelegate) {
262 // this case usually means there is no foreground container, need to figure out the reason.
263 return;
264 }
265
266 auto buildFunc = [buildfunc = jsBuilderFunction_, node = frameNode, context = pipelineContext]() {
267 {
268 ACE_SCORING_EVENT("CustomDialog.builder");
269 context->UpdateCurrentActiveNode(node);
270 buildfunc->Execute();
271 }
272 };
273
274 auto cancelTask = ([cancelCallback = jsCancelFunction_, node = frameNode, context = pipelineContext]() {
275 if (cancelCallback) {
276 ACE_SCORING_EVENT("CustomDialog.cancel");
277 context->UpdateCurrentActiveNode(node);
278 cancelCallback->Execute();
279 }
280 });
281
282 auto container = Container::Current();
283 if (container && container->IsScenceBoardWindow() && !dialogProperties_.windowScene.Upgrade()) {
284 dialogProperties_.isScenceBoardDialog = true;
285 auto viewNode = this->ownerView_->GetViewNode();
286 CHECK_NULL_VOID(viewNode);
287 auto parentCustom = AceType::DynamicCast<NG::CustomNode>(viewNode);
288 CHECK_NULL_VOID(parentCustom);
289 auto parent = parentCustom->GetParent();
290 while (parent && parent->GetTag() != V2::WINDOW_SCENE_ETS_TAG) {
291 parent = parent->GetParent();
292 }
293 if (parent) {
294 dialogProperties_.windowScene = parent;
295 }
296 }
297 dialogProperties_.isSysBlurStyle =
298 Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TWELVE) ? true : false;
299 CustomDialogControllerModel::GetInstance()->SetOpenDialog(dialogProperties_, WeakClaim(this), dialogs_, pending_,
300 isShown_, std::move(cancelTask), std::move(buildFunc), dialogComponent_, customDialog_, dialogOperation_);
301 }
302
JsCloseDialog(const JSCallbackInfo & info)303 void JSCustomDialogController::JsCloseDialog(const JSCallbackInfo& info)
304 {
305
306 if (this->ownerView_ == nullptr) {
307 return;
308 }
309 auto containerId = this->ownerView_->GetInstanceId();
310 ContainerScope containerScope(containerId);
311
312 auto scopedDelegate = EngineHelper::GetCurrentDelegate();
313 if (!scopedDelegate) {
314 // this case usually means there is no foreground container, need to figure out the reason.
315 return;
316 }
317
318 WeakPtr<NG::FrameNode> frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
319 auto cancelTask = ([cancelCallback = jsCancelFunction_, node = frameNode]() {
320 if (cancelCallback) {
321 ACE_SCORING_EVENT("CustomDialog.cancel");
322 auto pipelineContext = PipelineContext::GetCurrentContext();
323 CHECK_NULL_VOID(pipelineContext);
324 pipelineContext->UpdateCurrentActiveNode(node);
325 cancelCallback->Execute();
326 }
327 });
328
329 CustomDialogControllerModel::GetInstance()->SetCloseDialog(dialogProperties_, WeakClaim(this), dialogs_, pending_,
330 isShown_, std::move(cancelTask), dialogComponent_, customDialog_, dialogOperation_);
331 }
332
ParseAnimation(const JsiExecutionContext & execContext,const JsiRef<JsiValue> & animationValue,AnimationOption & result)333 bool JSCustomDialogController::ParseAnimation(
334 const JsiExecutionContext& execContext, const JsiRef<JsiValue>& animationValue, AnimationOption& result)
335 {
336 if (animationValue->IsNull() || !animationValue->IsObject()) {
337 return false;
338 }
339
340 JSRef<JSObject> obj = JSRef<JSObject>::Cast(animationValue);
341 // If the attribute does not exist, the default value is used.
342 int32_t duration = obj->GetPropertyValue<int32_t>("duration", DEFAULT_ANIMATION_DURATION);
343 int32_t delay = obj->GetPropertyValue<int32_t>("delay", 0);
344 int32_t iterations = obj->GetPropertyValue<int32_t>("iterations", 1);
345 float tempo = obj->GetPropertyValue<float>("tempo", 1.0);
346 auto finishCallbackType = static_cast<FinishCallbackType>(obj->GetPropertyValue<int32_t>("finishCallbackType", 0));
347 if (NonPositive(tempo)) {
348 tempo = 1.0f;
349 }
350 auto direction = StringToAnimationDirection(obj->GetPropertyValue<std::string>("playMode", "normal"));
351 RefPtr<Curve> curve;
352 JSRef<JSVal> curveArgs = obj->GetProperty("curve");
353 if (curveArgs->IsString()) {
354 curve = CreateCurve(obj->GetPropertyValue<std::string>("curve", "linear"));
355 } else if (curveArgs->IsObject()) {
356 JSRef<JSObject> curveObj = JSRef<JSObject>::Cast(curveArgs);
357 JSRef<JSVal> curveString = curveObj->GetProperty("__curveString");
358 if (!curveString->IsString()) {
359 // Default AnimationOption which is invalid.
360 return false;
361 }
362 curve = CreateCurve(curveString->ToString());
363 } else {
364 curve = Curves::EASE_IN_OUT;
365 }
366 result.SetDuration(duration);
367 result.SetDelay(delay);
368 result.SetIteration(iterations);
369 result.SetTempo(tempo);
370 result.SetAnimationDirection(direction);
371 result.SetCurve(curve);
372 result.SetFinishCallbackType(finishCallbackType);
373
374 JSRef<JSVal> onFinish = obj->GetProperty("onFinish");
375 std::function<void()> onFinishEvent;
376 if (onFinish->IsFunction()) {
377 auto frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
378 auto jsFunc = AceType::MakeRefPtr<JsWeakFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onFinish));
379 onFinishEvent = [execCtx = execContext, func = std::move(jsFunc), node = frameNode]() {
380 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
381 ACE_SCORING_EVENT("CustomDialog.onFinish");
382 auto pipelineContext = PipelineContext::GetCurrentContext();
383 CHECK_NULL_VOID(pipelineContext);
384 pipelineContext->UpdateCurrentActiveNode(node);
385 func->Execute();
386 };
387 result.SetOnFinishEvent(onFinishEvent);
388 }
389 return true;
390 }
391
JSBind(BindingTarget object)392 void JSCustomDialogController::JSBind(BindingTarget object)
393 {
394 JSClass<JSCustomDialogController>::Declare("NativeCustomDialogController");
395 JSClass<JSCustomDialogController>::CustomMethod("open", &JSCustomDialogController::JsOpenDialog);
396 JSClass<JSCustomDialogController>::CustomMethod("close", &JSCustomDialogController::JsCloseDialog);
397 JSClass<JSCustomDialogController>::Bind(
398 object, &JSCustomDialogController::ConstructorCallback, &JSCustomDialogController::DestructorCallback);
399 }
400 } // namespace OHOS::Ace::Framework
401