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 #include "frameworks/bridge/declarative_frontend/jsview/dialog/js_alert_dialog.h"
16
17 #include <sstream>
18 #include <string>
19 #include <vector>
20
21 #include "base/log/ace_scoring_log.h"
22 #include "bridge/declarative_frontend/jsview/models/alert_dialog_model_impl.h"
23 #include "core/common/container.h"
24 #include "core/components_ng/base/view_stack_processor.h"
25 #include "core/components_ng/pattern/dialog/alert_dialog_model_ng.h"
26 #include "frameworks/bridge/common/utils/engine_helper.h"
27 #include "frameworks/bridge/declarative_frontend/engine/functions/js_function.h"
28
29 namespace OHOS::Ace {
30 std::unique_ptr<AlertDialogModel> AlertDialogModel::instance_ = nullptr;
31 std::mutex AlertDialogModel::mutex_;
GetInstance()32 AlertDialogModel* AlertDialogModel::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::AlertDialogModelNG());
39 #else
40 if (Container::IsCurrentUseNewPipeline()) {
41 instance_.reset(new NG::AlertDialogModelNG());
42 } else {
43 instance_.reset(new Framework::AlertDialogModelImpl());
44 }
45 #endif
46 }
47 }
48 return instance_.get();
49 }
50
51 } // namespace OHOS::Ace
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 const std::vector<DialogButtonDirection> DIALOG_BUTTONS_DIRECTION = { DialogButtonDirection::AUTO,
59 DialogButtonDirection::HORIZONTAL, DialogButtonDirection::VERTICAL };
60 } // namespace
61
SetParseStyle(ButtonInfo & buttonInfo,const int32_t styleValue)62 void SetParseStyle(ButtonInfo& buttonInfo, const int32_t styleValue)
63 {
64 if (styleValue >= static_cast<int32_t>(DialogButtonStyle::DEFAULT) &&
65 styleValue <= static_cast<int32_t>(DialogButtonStyle::HIGHTLIGHT)) {
66 buttonInfo.dlgButtonStyle = static_cast<DialogButtonStyle>(styleValue);
67 }
68 }
69
ParseButtonObj(const JSCallbackInfo & args,DialogProperties & properties,JSRef<JSVal> jsVal,const std::string & property)70 void ParseButtonObj(
71 const JSCallbackInfo& args, DialogProperties& properties, JSRef<JSVal> jsVal, const std::string& property)
72 {
73 if (!jsVal->IsObject()) {
74 return;
75 }
76 auto objInner = JSRef<JSObject>::Cast(jsVal);
77 auto value = objInner->GetProperty("value");
78 std::string buttonValue;
79 ButtonInfo buttonInfo;
80 if (JSAlertDialog::ParseJsString(value, buttonValue)) {
81 buttonInfo.text = buttonValue;
82 }
83
84 // Parse enabled
85 auto enabledValue = objInner->GetProperty("enabled");
86 if (enabledValue->IsBoolean()) {
87 buttonInfo.enabled = enabledValue->ToBoolean();
88 }
89
90 // Parse defaultFocus
91 auto defaultFocusValue = objInner->GetProperty("defaultFocus");
92 if (defaultFocusValue->IsBoolean()) {
93 buttonInfo.defaultFocus = defaultFocusValue->ToBoolean();
94 }
95
96 // Parse style
97 auto style = objInner->GetProperty("style");
98 if (style->IsNumber()) {
99 auto styleValue = style->ToNumber<int32_t>();
100 SetParseStyle(buttonInfo, styleValue);
101 }
102
103 auto fontColorValue = objInner->GetProperty("fontColor");
104 Color textColor;
105 if (JSAlertDialog::ParseJsColor(fontColorValue, textColor)) {
106 buttonInfo.textColor = textColor.ColorToString();
107 }
108
109 auto backgroundColorValue = objInner->GetProperty("backgroundColor");
110 Color backgroundColor;
111 if (JSAlertDialog::ParseJsColor(backgroundColorValue, backgroundColor)) {
112 buttonInfo.isBgColorSetted = true;
113 buttonInfo.bgColor = backgroundColor;
114 }
115
116 auto actionValue = objInner->GetProperty("action");
117 if (actionValue->IsFunction()) {
118 WeakPtr<NG::FrameNode> frameNode = NG::ViewStackProcessor::GetInstance()->GetMainFrameNode();
119 auto actionFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(actionValue));
120 auto eventFunc = [execCtx = args.GetExecutionContext(), func = std::move(actionFunc), property,
121 node = frameNode]() {
122 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
123 ACE_SCORING_EVENT("AlertDialog.[" + property + "].onAction");
124 auto pipelineContext = PipelineContext::GetCurrentContextSafely();
125 CHECK_NULL_VOID(pipelineContext);
126 pipelineContext->UpdateCurrentActiveNode(node);
127 func->Execute();
128 };
129 AlertDialogModel::GetInstance()->SetParseButtonObj(eventFunc, buttonInfo, properties, property);
130 }
131
132 if (buttonInfo.IsValid()) {
133 properties.buttons.emplace_back(buttonInfo);
134 }
135 }
136
ParseButtonArray(const JSCallbackInfo & args,DialogProperties & properties,JSRef<JSObject> obj,const std::string & property)137 void ParseButtonArray(
138 const JSCallbackInfo& args, DialogProperties& properties, JSRef<JSObject> obj, const std::string& property)
139 {
140 auto jsVal = obj->GetProperty(property.c_str());
141 if (!jsVal->IsArray()) {
142 return;
143 }
144 JSRef<JSArray> array = JSRef<JSArray>::Cast(jsVal);
145 size_t length = array->Length();
146 if (length <= 0) {
147 return;
148 }
149 for (size_t i = 0; i < length; i++) {
150 JSRef<JSVal> buttonItem = array->GetValueAt(i);
151 if (!buttonItem->IsObject()) {
152 break;
153 }
154 ParseButtonObj(args, properties, buttonItem, property + std::to_string(i));
155 }
156 }
157
Show(const JSCallbackInfo & args)158 void JSAlertDialog::Show(const JSCallbackInfo& args)
159 {
160 auto scopedDelegate = EngineHelper::GetCurrentDelegateSafely();
161 if (!scopedDelegate) {
162 // this case usually means there is no foreground container, need to figure out the reason.
163 LOGE("scopedDelegate is null, please check");
164 return;
165 }
166
167 DialogProperties properties { .type = DialogType::ALERT_DIALOG };
168 if (args[0]->IsObject()) {
169 auto obj = JSRef<JSObject>::Cast(args[0]);
170
171 // Parse title.
172 auto titleValue = obj->GetProperty("title");
173 std::string title;
174 if (ParseJsString(titleValue, title)) {
175 properties.title = title;
176 }
177
178 // Parse subtitle.
179 auto subtitleValue = obj->GetProperty("subtitle");
180 std::string subtitle;
181 if (ParseJsString(subtitleValue, subtitle)) {
182 properties.subtitle = subtitle;
183 }
184
185 // Parses message.
186 auto messageValue = obj->GetProperty("message");
187 std::string message;
188 if (ParseJsString(messageValue, message)) {
189 properties.content = message;
190 }
191
192 // Parses gridCount.
193 auto gridCountValue = obj->GetProperty("gridCount");
194 if (gridCountValue->IsNumber()) {
195 properties.gridCount = gridCountValue->ToNumber<int32_t>();
196 }
197
198 // Parse auto autoCancel.
199 auto autoCancelValue = obj->GetProperty("autoCancel");
200 if (autoCancelValue->IsBoolean()) {
201 properties.autoCancel = autoCancelValue->ToBoolean();
202 }
203
204 // Parse cancel.
205 auto cancelValue = obj->GetProperty("cancel");
206 if (cancelValue->IsFunction()) {
207 WeakPtr<NG::FrameNode> frameNode = NG::ViewStackProcessor::GetInstance()->GetMainFrameNode();
208 auto cancelFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(cancelValue));
209 auto eventFunc = [execCtx = args.GetExecutionContext(), func = std::move(cancelFunc), node = frameNode]() {
210 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
211 ACE_SCORING_EVENT("AlertDialog.property.cancel");
212 auto pipelineContext = PipelineContext::GetCurrentContextSafely();
213 CHECK_NULL_VOID(pipelineContext);
214 pipelineContext->UpdateCurrentActiveNode(node);
215 func->Execute();
216 };
217 AlertDialogModel::GetInstance()->SetOnCancel(eventFunc, properties);
218 }
219
220 if (obj->GetProperty("confirm")->IsObject()) {
221 // Parse confirm.
222 auto objInner = obj->GetProperty("confirm");
223 ParseButtonObj(args, properties, objInner, "confirm");
224 } else if (obj->GetProperty("buttons")->IsArray()) {
225 // Parse buttons array.
226 ParseButtonArray(args, properties, obj, "buttons");
227 } else {
228 // Parse primaryButton and secondaryButton.
229 auto objInner = obj->GetProperty("primaryButton");
230 ParseButtonObj(args, properties, objInner, "primaryButton");
231 objInner = obj->GetProperty("secondaryButton");
232 ParseButtonObj(args, properties, objInner, "secondaryButton");
233 }
234
235 // Parse buttons direction.
236 auto directionValue = obj->GetProperty("buttonDirection");
237 if (directionValue->IsNumber()) {
238 auto buttonDirection = directionValue->ToNumber<int32_t>();
239 if (buttonDirection >= 0 && buttonDirection <= static_cast<int32_t>(DIALOG_BUTTONS_DIRECTION.size())) {
240 properties.buttonDirection = DIALOG_BUTTONS_DIRECTION[buttonDirection];
241 }
242 }
243
244 // Parse alignment
245 auto alignmentValue = obj->GetProperty("alignment");
246 if (alignmentValue->IsNumber()) {
247 auto alignment = alignmentValue->ToNumber<int32_t>();
248 if (alignment >= 0 && alignment <= static_cast<int32_t>(DIALOG_ALIGNMENT.size())) {
249 properties.alignment = DIALOG_ALIGNMENT[alignment];
250 }
251 }
252
253 // Parse offset
254 auto offsetValue = obj->GetProperty("offset");
255 if (offsetValue->IsObject()) {
256 auto offsetObj = JSRef<JSObject>::Cast(offsetValue);
257 CalcDimension dx;
258 auto dxValue = offsetObj->GetProperty("dx");
259 ParseJsDimensionVp(dxValue, dx);
260 CalcDimension dy;
261 auto dyValue = offsetObj->GetProperty("dy");
262 ParseJsDimensionVp(dyValue, dy);
263 properties.offset = DimensionOffset(dx, dy);
264 }
265
266 // Parse maskRect.
267 auto maskRectValue = obj->GetProperty("maskRect");
268 DimensionRect maskRect;
269 if (JSViewAbstract::ParseJsDimensionRect(maskRectValue, maskRect)) {
270 properties.maskRect = maskRect;
271 }
272
273 // Parse showInSubWindowValue.
274 auto showInSubWindowValue = obj->GetProperty("showInSubWindow");
275 if (showInSubWindowValue->IsBoolean()) {
276 LOGI("Parse showInSubWindowValue");
277 properties.isShowInSubWindow = showInSubWindowValue->ToBoolean();
278 }
279
280 // Parse isModal.
281 auto isModalValue = obj->GetProperty("isModal");
282 if (isModalValue->IsBoolean()) {
283 LOGI("Parse isModalValue");
284 properties.isModal = isModalValue->ToBoolean();
285 }
286
287 auto backgroundColorValue = obj->GetProperty("backgroundColor");
288 Color backgroundColor;
289 if (JSViewAbstract::ParseJsColor(backgroundColorValue, backgroundColor)) {
290 properties.backgroundColor = backgroundColor;
291 }
292
293 auto backgroundBlurStyle = obj->GetProperty("backgroundBlurStyle");
294 BlurStyleOption styleOption;
295 if (backgroundBlurStyle->IsNumber()) {
296 auto blurStyle = backgroundBlurStyle->ToNumber<int32_t>();
297 if (blurStyle >= static_cast<int>(BlurStyle::NO_MATERIAL) &&
298 blurStyle <= static_cast<int>(BlurStyle::COMPONENT_ULTRA_THICK)) {
299 properties.backgroundBlurStyle = blurStyle;
300 }
301 }
302 AlertDialogModel::GetInstance()->SetShowDialog(properties);
303 }
304 }
305
JSBind(BindingTarget globalObj)306 void JSAlertDialog::JSBind(BindingTarget globalObj)
307 {
308 JSClass<JSAlertDialog>::Declare("AlertDialog");
309 JSClass<JSAlertDialog>::StaticMethod("show", &JSAlertDialog::Show);
310
311 JSClass<JSAlertDialog>::InheritAndBind<JSViewAbstract>(globalObj);
312 }
313
314 } // namespace OHOS::Ace::Framework
315