1 /*
2 * Copyright (c) 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 "js_ui_appearance.h"
17
18 #include "ui_appearance_log.h"
19
20 namespace OHOS {
21 namespace ArkUi::UiAppearance {
22 namespace {
23 static constexpr size_t ARGC_WITH_ONE = 1;
24 static constexpr size_t ARGC_WITH_TWO = 2;
25 const std::string PERMISSION_ERR_MSG =
26 "An attempt was made to update configuration forbidden by permission: ohos.permission.UPDATE_CONFIGURATION.";
27 const std::string INVALID_ARG_MSG = "The type of 'mode' must be DarkMode.";
28
29 static const std::unordered_map<int32_t, std::string> ERROR_CODE_TO_MSG {
30 { UiAppearanceAbilityInterface::ErrCode::PERMISSION_ERR, "Permission denied. " },
31 { UiAppearanceAbilityInterface::ErrCode::INVALID_ARG, "Parameter error. " },
32 { UiAppearanceAbilityInterface::ErrCode::SYS_ERR, "Internal error. " },
33 };
34
NapiThrow(napi_env env,const std::string & message,int32_t errCode)35 void NapiThrow(napi_env env, const std::string& message, int32_t errCode)
36 {
37 napi_value code = nullptr;
38 std::string strCode = std::to_string(errCode);
39 napi_create_string_utf8(env, strCode.c_str(), strCode.length(), &code);
40
41 napi_value msg = nullptr;
42 auto iter = ERROR_CODE_TO_MSG.find(errCode);
43 std::string strMsg = (iter != ERROR_CODE_TO_MSG.end() ? iter->second : "") + message;
44 JS_HILOG_INFO("napi throw errCode %{public}d, strMsg %{public}s", errCode, strMsg.c_str());
45 napi_create_string_utf8(env, strMsg.c_str(), strMsg.length(), &msg);
46
47 napi_value error = nullptr;
48 napi_create_error(env, code, msg, &error);
49 napi_throw(env, error);
50 }
51 } // namespace
52
OnExecute(napi_env env,void * data)53 void JsUiAppearance::OnExecute(napi_env env, void* data)
54 {
55 JS_HILOG_INFO("OnExecute begin.");
56 AsyncContext* asyncContext = static_cast<AsyncContext*>(data);
57 if (asyncContext == nullptr) {
58 NapiThrow(env, "asyncContext is null.", UiAppearanceAbilityInterface::ErrCode::SYS_ERR);
59 return;
60 }
61 auto resCode = UiAppearanceAbilityClient::GetInstance()->SetDarkMode(asyncContext->mode);
62 asyncContext->status = static_cast<UiAppearanceAbilityInterface::ErrCode>(resCode);
63 if (asyncContext->status == UiAppearanceAbilityInterface::ErrCode::PERMISSION_ERR) {
64 asyncContext->errMsg = PERMISSION_ERR_MSG;
65 } else if (asyncContext->status == UiAppearanceAbilityInterface::ErrCode::INVALID_ARG) {
66 asyncContext->errMsg = INVALID_ARG_MSG;
67 } else {
68 asyncContext->errMsg = "";
69 }
70 }
71
OnComplete(napi_env env,napi_status status,void * data)72 void JsUiAppearance::OnComplete(napi_env env, napi_status status, void* data)
73 {
74 JS_HILOG_INFO("OnComplete begin.");
75 napi_handle_scope scope = nullptr;
76 napi_open_handle_scope(env, &scope);
77 if (scope == nullptr) {
78 NapiThrow(env, "open handle scope failed.", UiAppearanceAbilityInterface::ErrCode::SYS_ERR);
79 return;
80 }
81 AsyncContext* asyncContext = static_cast<AsyncContext*>(data);
82 if (asyncContext == nullptr) {
83 NapiThrow(env, "asyncContext is null.", UiAppearanceAbilityInterface::ErrCode::SYS_ERR);
84 return;
85 }
86
87 if (asyncContext->status == UiAppearanceAbilityInterface::ErrCode::SUCCEEDED) {
88 napi_value result = nullptr;
89 napi_get_null(env, &result);
90 if (asyncContext->deferred) { // promise
91 napi_resolve_deferred(env, asyncContext->deferred, result);
92 } else { // AsyncCallback
93 napi_value callback = nullptr;
94 napi_get_reference_value(env, asyncContext->callbackRef, &callback);
95 napi_value ret;
96 napi_call_function(env, nullptr, callback, 1, &result, &ret);
97 }
98 } else {
99 napi_value code = nullptr;
100 std::string strCode = std::to_string(asyncContext->status);
101 napi_create_string_utf8(env, strCode.c_str(), strCode.length(), &code);
102
103 napi_value msg = nullptr;
104 auto iter = ERROR_CODE_TO_MSG.find(asyncContext->status);
105 std::string strMsg = (iter != ERROR_CODE_TO_MSG.end() ? iter->second : "") + asyncContext->errMsg;
106 JS_HILOG_INFO("napi throw errCode %{public}d, strMsg %{public}s", asyncContext->status, strMsg.c_str());
107 napi_create_string_utf8(env, strMsg.c_str(), strMsg.length(), &msg);
108
109 napi_value error = nullptr;
110 napi_create_error(env, code, msg, &error);
111 if (asyncContext->deferred) { // promise
112 napi_reject_deferred(env, asyncContext->deferred, error);
113 } else { // AsyncCallback
114 napi_value callback = nullptr;
115 napi_get_reference_value(env, asyncContext->callbackRef, &callback);
116 napi_value ret;
117 napi_call_function(env, nullptr, callback, 1, &error, &ret);
118 }
119 }
120 napi_delete_async_work(env, asyncContext->work);
121 delete asyncContext;
122 napi_close_handle_scope(env, scope);
123 }
124
CheckArgs(napi_env env,size_t argc,napi_value * argv)125 napi_status JsUiAppearance::CheckArgs(napi_env env, size_t argc, napi_value* argv)
126 {
127 if (argc != ARGC_WITH_ONE && argc != ARGC_WITH_TWO) {
128 NapiThrow(
129 env, "the number of parameters can only be 1 or 2.", UiAppearanceAbilityInterface::ErrCode::INVALID_ARG);
130 return napi_invalid_arg;
131 }
132
133 napi_valuetype valueType = napi_undefined;
134 switch (argc) {
135 case ARGC_WITH_TWO:
136 napi_typeof(env, argv[1], &valueType);
137 if (valueType != napi_function) {
138 NapiThrow(env, "the second parameter must be a function.",
139 UiAppearanceAbilityInterface::ErrCode::INVALID_ARG);
140 return napi_invalid_arg;
141 }
142 [[fallthrough]];
143 case ARGC_WITH_ONE:
144 napi_typeof(env, argv[0], &valueType);
145 if (valueType != napi_number) {
146 NapiThrow(
147 env, "the first parameter must be DarkMode.", UiAppearanceAbilityInterface::ErrCode::INVALID_ARG);
148 return napi_invalid_arg;
149 }
150 break;
151 default:
152 return napi_invalid_arg;
153 }
154 return napi_ok;
155 }
156
ConvertJsDarkMode2Enum(int32_t jsVal)157 UiAppearanceAbilityInterface::DarkMode JsUiAppearance::ConvertJsDarkMode2Enum(int32_t jsVal)
158 {
159 switch (jsVal) {
160 case 0:
161 return UiAppearanceAbilityInterface::DarkMode::ALWAYS_DARK;
162 case 1:
163 return UiAppearanceAbilityInterface::DarkMode::ALWAYS_LIGHT;
164 default:
165 return UiAppearanceAbilityInterface::DarkMode::UNKNOWN;
166 }
167 }
168
JSSetDarkMode(napi_env env,napi_callback_info info)169 static napi_value JSSetDarkMode(napi_env env, napi_callback_info info)
170 {
171 JS_HILOG_INFO("JSSetDarkMode begin.");
172
173 size_t argc = ARGC_WITH_TWO;
174 napi_value argv[ARGC_WITH_TWO] = { 0 };
175 napi_status napiStatus = napi_ok;
176 napi_value result = nullptr;
177 napi_get_undefined(env, &result);
178
179 napiStatus = napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
180 if (napiStatus != napi_ok) {
181 NapiThrow(env, "get callback info failed.", UiAppearanceAbilityInterface::ErrCode::INVALID_ARG);
182 return result;
183 }
184 napiStatus = JsUiAppearance::CheckArgs(env, argc, argv);
185 if (napiStatus != napi_ok) {
186 NapiThrow(env, "parameter parsing error.", UiAppearanceAbilityInterface::ErrCode::INVALID_ARG);
187 return result;
188 }
189
190 auto asyncContext = new (std::nothrow) AsyncContext();
191 if (asyncContext == nullptr) {
192 NapiThrow(env, "create AsyncContext failed.", UiAppearanceAbilityInterface::ErrCode::SYS_ERR);
193 return result;
194 }
195
196 napi_get_value_int32(env, argv[0], &asyncContext->jsSetArg);
197 asyncContext->mode = JsUiAppearance::ConvertJsDarkMode2Enum(asyncContext->jsSetArg);
198 if (argc == ARGC_WITH_TWO) {
199 napi_create_reference(env, argv[1], 1, &asyncContext->callbackRef);
200 }
201 if (asyncContext->callbackRef == nullptr) {
202 napi_create_promise(env, &asyncContext->deferred, &result);
203 }
204
205 napi_value resource = nullptr;
206 napi_create_string_utf8(env, "JSSetDarkMode", NAPI_AUTO_LENGTH, &resource);
207 napi_create_async_work(env, nullptr, resource, JsUiAppearance::OnExecute, JsUiAppearance::OnComplete,
208 reinterpret_cast<void*>(asyncContext), &asyncContext->work);
209 napi_queue_async_work(env, asyncContext->work);
210
211 return result;
212 }
213
JSGetDarkMode(napi_env env,napi_callback_info info)214 static napi_value JSGetDarkMode(napi_env env, napi_callback_info info)
215 {
216 JS_HILOG_INFO("JSGetDarkMode begin.");
217
218 napi_value result = nullptr;
219 napi_get_undefined(env, &result);
220 size_t argc = 0;
221 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr));
222 if (argc != 0) {
223 NapiThrow(env, "requires no parameter.", UiAppearanceAbilityInterface::ErrCode::INVALID_ARG);
224 return result;
225 }
226
227 auto mode = UiAppearanceAbilityClient::GetInstance()->GetDarkMode();
228 if (mode == UiAppearanceAbilityInterface::ErrCode::SYS_ERR) {
229 NapiThrow(env, "get dark-mode failed.", UiAppearanceAbilityInterface::ErrCode::SYS_ERR);
230 return result;
231 }
232 if (mode == UiAppearanceAbilityInterface::ErrCode::PERMISSION_ERR) {
233 NapiThrow(env,
234 "An attempt was made to get configuration forbidden by permission: ohos.permission.UPDATE_CONFIGURATION.",
235 UiAppearanceAbilityInterface::ErrCode::PERMISSION_ERR);
236 return result;
237 }
238 NAPI_CALL(env, napi_create_int32(env, mode, &result));
239 return result;
240 }
241
242 EXTERN_C_START
UiAppearanceExports(napi_env env,napi_value exports)243 static napi_value UiAppearanceExports(napi_env env, napi_value exports)
244 {
245 napi_value DarkMode = nullptr;
246 napi_value alwaysDark = nullptr;
247 napi_value alwaysLight = nullptr;
248 NAPI_CALL(env, napi_create_int32(env, 0, &alwaysDark));
249 NAPI_CALL(env, napi_create_int32(env, 1, &alwaysLight));
250 NAPI_CALL(env, napi_create_object(env, &DarkMode));
251 NAPI_CALL(env, napi_set_named_property(env, DarkMode, "ALWAYS_DARK", alwaysDark));
252 NAPI_CALL(env, napi_set_named_property(env, DarkMode, "ALWAYS_LIGHT", alwaysLight));
253 napi_property_descriptor properties[] = {
254 DECLARE_NAPI_FUNCTION("setDarkMode", JSSetDarkMode),
255 DECLARE_NAPI_FUNCTION("getDarkMode", JSGetDarkMode),
256 DECLARE_NAPI_STATIC_PROPERTY("DarkMode", DarkMode),
257 };
258 NAPI_CALL(env, napi_define_properties(env, exports, sizeof(properties) / sizeof(properties[0]), properties));
259 return exports;
260 }
261 EXTERN_C_END
262
263 static napi_module ui_appearance_module = {
264 .nm_version = 1,
265 .nm_flags = 0,
266 .nm_filename = nullptr,
267 .nm_register_func = UiAppearanceExports,
268 .nm_modname = "uiAppearance", // relative to the module name while import.
269 .nm_priv = nullptr,
270 .reserved = { 0 },
271 };
272
UiAppearanceRegister()273 extern "C" __attribute__((constructor)) void UiAppearanceRegister()
274 {
275 napi_module_register(&ui_appearance_module);
276 }
277 } // namespace ArkUi::UiAppearance
278 } // namespace OHOS
279