• 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 #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