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