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