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
232 JSViewAbstract::SetDialogProperties(constructorArg, instance->dialogProperties_);
233 JSViewAbstract::SetDialogHoverModeProperties(constructorArg, instance->dialogProperties_);
234 instance->IncRefCount();
235 info.SetReturnValue(AceType::RawPtr(instance));
236 }
237 }
238
DestructorCallback(JSCustomDialogController * controller)239 void JSCustomDialogController::DestructorCallback(JSCustomDialogController* controller)
240 {
241 if (controller != nullptr) {
242 controller->ownerView_ = nullptr;
243 controller->DecRefCount();
244 }
245 }
246
JsOpenDialog(const JSCallbackInfo & info)247 void JSCustomDialogController::JsOpenDialog(const JSCallbackInfo& info)
248 {
249 if (!jsBuilderFunction_) {
250 return;
251 }
252
253 if (this->ownerView_ == nullptr) {
254 return;
255 }
256 auto containerId = this->ownerView_->GetInstanceId();
257 ContainerScope containerScope(containerId);
258 WeakPtr<NG::FrameNode> frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
259 auto pipelineContext = PipelineContext::GetCurrentContext();
260 CHECK_NULL_VOID(pipelineContext);
261
262 auto scopedDelegate = EngineHelper::GetCurrentDelegate();
263 if (!scopedDelegate) {
264 // this case usually means there is no foreground container, need to figure out the reason.
265 return;
266 }
267
268 auto buildFunc = [buildfunc = jsBuilderFunction_, node = frameNode, context = pipelineContext]() {
269 {
270 ACE_SCORING_EVENT("CustomDialog.builder");
271 context->UpdateCurrentActiveNode(node);
272 buildfunc->Execute();
273 }
274 };
275
276 auto cancelTask = ([cancelCallback = jsCancelFunction_, node = frameNode, context = pipelineContext]() {
277 if (cancelCallback) {
278 ACE_SCORING_EVENT("CustomDialog.cancel");
279 context->UpdateCurrentActiveNode(node);
280 cancelCallback->Execute();
281 }
282 });
283
284 auto container = Container::Current();
285 if (container && container->IsScenceBoardWindow() && !dialogProperties_.windowScene.Upgrade()) {
286 dialogProperties_.isScenceBoardDialog = true;
287 auto viewNode = this->ownerView_->GetViewNode();
288 CHECK_NULL_VOID(viewNode);
289 auto parentCustom = AceType::DynamicCast<NG::CustomNode>(viewNode);
290 CHECK_NULL_VOID(parentCustom);
291 auto parent = parentCustom->GetParent();
292 while (parent && parent->GetTag() != V2::WINDOW_SCENE_ETS_TAG) {
293 parent = parent->GetParent();
294 }
295 if (parent) {
296 dialogProperties_.windowScene = parent;
297 }
298 }
299 dialogProperties_.isSysBlurStyle =
300 Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TWELVE) ? true : false;
301 CustomDialogControllerModel::GetInstance()->SetOpenDialog(dialogProperties_, WeakClaim(this), dialogs_, pending_,
302 isShown_, std::move(cancelTask), std::move(buildFunc), dialogComponent_, customDialog_, dialogOperation_);
303 }
304
JsCloseDialog(const JSCallbackInfo & info)305 void JSCustomDialogController::JsCloseDialog(const JSCallbackInfo& info)
306 {
307
308 if (this->ownerView_ == nullptr) {
309 return;
310 }
311 auto containerId = this->ownerView_->GetInstanceId();
312 ContainerScope containerScope(containerId);
313
314 auto scopedDelegate = EngineHelper::GetCurrentDelegate();
315 if (!scopedDelegate) {
316 // this case usually means there is no foreground container, need to figure out the reason.
317 return;
318 }
319
320 WeakPtr<NG::FrameNode> frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
321 auto cancelTask = ([cancelCallback = jsCancelFunction_, node = frameNode]() {
322 if (cancelCallback) {
323 ACE_SCORING_EVENT("CustomDialog.cancel");
324 auto pipelineContext = PipelineContext::GetCurrentContext();
325 CHECK_NULL_VOID(pipelineContext);
326 pipelineContext->UpdateCurrentActiveNode(node);
327 cancelCallback->Execute();
328 }
329 });
330
331 CustomDialogControllerModel::GetInstance()->SetCloseDialog(dialogProperties_, WeakClaim(this), dialogs_, pending_,
332 isShown_, std::move(cancelTask), dialogComponent_, customDialog_, dialogOperation_);
333 }
334
ParseAnimation(const JsiExecutionContext & execContext,const JsiRef<JsiValue> & animationValue,AnimationOption & result)335 bool JSCustomDialogController::ParseAnimation(
336 const JsiExecutionContext& execContext, const JsiRef<JsiValue>& animationValue, AnimationOption& result)
337 {
338 if (animationValue->IsNull() || !animationValue->IsObject()) {
339 return false;
340 }
341
342 JSRef<JSObject> obj = JSRef<JSObject>::Cast(animationValue);
343 // If the attribute does not exist, the default value is used.
344 int32_t duration = obj->GetPropertyValue<int32_t>("duration", DEFAULT_ANIMATION_DURATION);
345 int32_t delay = obj->GetPropertyValue<int32_t>("delay", 0);
346 int32_t iterations = obj->GetPropertyValue<int32_t>("iterations", 1);
347 float tempo = obj->GetPropertyValue<float>("tempo", 1.0);
348 auto finishCallbackType = static_cast<FinishCallbackType>(obj->GetPropertyValue<int32_t>("finishCallbackType", 0));
349 if (NonPositive(tempo)) {
350 tempo = 1.0f;
351 }
352 auto direction = StringToAnimationDirection(obj->GetPropertyValue<std::string>("playMode", "normal"));
353 RefPtr<Curve> curve;
354 JSRef<JSVal> curveArgs = obj->GetProperty("curve");
355 if (curveArgs->IsString()) {
356 curve = CreateCurve(obj->GetPropertyValue<std::string>("curve", "linear"));
357 } else if (curveArgs->IsObject()) {
358 JSRef<JSObject> curveObj = JSRef<JSObject>::Cast(curveArgs);
359 JSRef<JSVal> curveString = curveObj->GetProperty("__curveString");
360 if (!curveString->IsString()) {
361 // Default AnimationOption which is invalid.
362 return false;
363 }
364 curve = CreateCurve(curveString->ToString());
365 } else {
366 curve = Curves::EASE_IN_OUT;
367 }
368 result.SetDuration(duration);
369 result.SetDelay(delay);
370 result.SetIteration(iterations);
371 result.SetTempo(tempo);
372 result.SetAnimationDirection(direction);
373 result.SetCurve(curve);
374 result.SetFinishCallbackType(finishCallbackType);
375
376 JSRef<JSVal> onFinish = obj->GetProperty("onFinish");
377 std::function<void()> onFinishEvent;
378 if (onFinish->IsFunction()) {
379 auto frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
380 auto jsFunc = AceType::MakeRefPtr<JsWeakFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onFinish));
381 onFinishEvent = [execCtx = execContext, func = std::move(jsFunc), node = frameNode]() {
382 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
383 ACE_SCORING_EVENT("CustomDialog.onFinish");
384 auto pipelineContext = PipelineContext::GetCurrentContext();
385 CHECK_NULL_VOID(pipelineContext);
386 pipelineContext->UpdateCurrentActiveNode(node);
387 func->Execute();
388 };
389 result.SetOnFinishEvent(onFinishEvent);
390 }
391 return true;
392 }
393
JSBind(BindingTarget object)394 void JSCustomDialogController::JSBind(BindingTarget object)
395 {
396 JSClass<JSCustomDialogController>::Declare("NativeCustomDialogController");
397 JSClass<JSCustomDialogController>::CustomMethod("open", &JSCustomDialogController::JsOpenDialog);
398 JSClass<JSCustomDialogController>::CustomMethod("close", &JSCustomDialogController::JsCloseDialog);
399 JSClass<JSCustomDialogController>::Bind(
400 object, &JSCustomDialogController::ConstructorCallback, &JSCustomDialogController::DestructorCallback);
401 }
402 } // namespace OHOS::Ace::Framework
403