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_transition_controller.h"
17 #include "js_runtime_utils.h"
18 #include "window.h"
19 #include "window_helper.h"
20 #include "window_manager_hilog.h"
21 #include "window_option.h"
22 #include "js_window.h"
23 #include "permission.h"
24
25 namespace OHOS {
26 namespace Rosen {
27 using namespace AbilityRuntime;
28 namespace {
29 constexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_WINDOW, "JsTransitionController"};
30 }
31
32 static int g_jsTransCxtCtorCnt = 0;
33 static int g_jsTransCxtDtorCnt = 0;
34 static int g_jsTransCtrlCtorCnt = 0;
35 static int g_jsTransCtrlDtorCnt = 0;
36
JsTransitionContext(sptr<Window> window,bool isShownTransContext)37 JsTransitionContext::JsTransitionContext(sptr<Window> window, bool isShownTransContext)
38 : windowToken_(window), isShownTransContext_(isShownTransContext)
39 {
40 WLOGI("[NAPI] JsTransitionContext constructorCnt: %{public}d", ++g_jsTransCxtCtorCnt);
41 }
42
~JsTransitionContext()43 JsTransitionContext::~JsTransitionContext()
44 {
45 WLOGI("[NAPI] ~JsTransitionContext deConstructorCnt: %{public}d", ++g_jsTransCxtDtorCnt);
46 }
47
Finalizer(napi_env env,void * data,void * hint)48 void JsTransitionContext::Finalizer(napi_env env, void* data, void* hint)
49 {
50 WLOGI("[NAPI]JsTransitionContext::Finalizer");
51 std::unique_ptr<JsTransitionContext>(static_cast<JsTransitionContext*>(data));
52 }
53
CompleteTransition(napi_env env,napi_callback_info info)54 napi_value JsTransitionContext::CompleteTransition(napi_env env, napi_callback_info info)
55 {
56 WLOGI("[NAPI]CompleteTransition");
57 JsTransitionContext* me = CheckParamsAndGetThis<JsTransitionContext>(env, info);
58 return (me != nullptr) ? me->OnCompleteTransition(env, info) : nullptr;
59 }
60
OnCompleteTransition(napi_env env,napi_callback_info info)61 napi_value JsTransitionContext::OnCompleteTransition(napi_env env, napi_callback_info info)
62 {
63 if (!Permission::IsSystemCalling()) {
64 TLOGE(WmsLogTag::WMS_SYSTEM, "not system app, permission denied!");
65 return NapiThrowError(env, WmErrorCode::WM_ERROR_NOT_SYSTEM_APP);
66 }
67
68 size_t argc = 4;
69 napi_value argv[4] = {nullptr};
70 napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
71 if (argc < 1) {
72 return NapiThrowError(env, WmErrorCode::WM_ERROR_INVALID_PARAM);
73 }
74 bool transitionCompleted = false;
75 if (!ConvertFromJsValue(env, argv[0], transitionCompleted)) {
76 return NapiThrowError(env, WmErrorCode::WM_ERROR_INVALID_PARAM);
77 }
78
79 WMError ret = WMError::WM_OK;
80 auto window = windowToken_.promote();
81 if (window == nullptr) {
82 return NapiThrowError(env, WmErrorCode::WM_ERROR_STATE_ABNORMALLY);
83 }
84 auto state = window->GetWindowState();
85 if (!isShownTransContext_) {
86 if (state != WindowState::STATE_HIDDEN) {
87 WLOGI("[NAPI]Window [%{public}u, %{public}s] Hidden context called but window is not hidden: %{public}u",
88 window->GetWindowId(), window->GetWindowName().c_str(), static_cast<uint32_t>(state));
89 return NapiGetUndefined(env);
90 }
91 window->UpdateSurfaceNodeAfterCustomAnimation(false); // remove from rs tree after animation
92 if (!transitionCompleted) {
93 ret = window->Show(); // hide aborted
94 }
95 } else {
96 if (state != WindowState::STATE_SHOWN) {
97 WLOGI("[NAPI]Window [%{public}u, %{public}s] shown context called but window is not shown: %{public}u",
98 window->GetWindowId(), window->GetWindowName().c_str(), static_cast<uint32_t>(state));
99 return NapiGetUndefined(env);
100 }
101 if (!transitionCompleted) {
102 ret = window->Hide(); // show aborted
103 }
104 }
105 if (ret != WMError::WM_OK) {
106 return NapiThrowError(env, WM_JS_TO_ERROR_CODE_MAP.at(ret));
107 }
108 WLOGI("[NAPI]Window [%{public}u, %{public}s] CompleteTransition %{public}d end",
109 window->GetWindowId(), window->GetWindowName().c_str(), transitionCompleted);
110 return NapiGetUndefined(env);
111 }
112
CreateJsTransitionContextObject(napi_env env,std::shared_ptr<NativeReference> jsWin,sptr<Window> window,bool isShownTransContext)113 static napi_value CreateJsTransitionContextObject(napi_env env, std::shared_ptr<NativeReference> jsWin,
114 sptr<Window> window, bool isShownTransContext)
115 {
116 WLOGI("[NAPI]CreateJsTransitionContextObject");
117 napi_value objValue = nullptr;
118 napi_create_object(env, &objValue);
119 if (objValue == nullptr) {
120 WLOGFE("[NAPI]Failed to get object");
121 return nullptr;
122 }
123 std::unique_ptr<JsTransitionContext> jsTransContext = std::make_unique<JsTransitionContext>(window,
124 isShownTransContext);
125 napi_wrap(env, objValue, jsTransContext.release(), JsTransitionContext::Finalizer, nullptr, nullptr);
126 if (jsWin != nullptr && jsWin->GetNapiValue() != nullptr) {
127 napi_set_named_property(env, objValue, "toWindow", jsWin->GetNapiValue());
128 } else {
129 WLOGFE("[NAPI]jsWin is nullptr");
130 return nullptr;
131 }
132
133 const char *moduleName = "JsTransitionContext";
134 BindNativeFunction(env, objValue, "completeTransition", moduleName, JsTransitionContext::CompleteTransition);
135 return objValue;
136 }
137
JsTransitionController(napi_env env,std::shared_ptr<NativeReference> jsWin,sptr<Window> window)138 JsTransitionController::JsTransitionController(napi_env env, std::shared_ptr<NativeReference> jsWin,
139 sptr<Window> window)
140 : env_(env), jsWin_(jsWin), windowToken_(window), weakRef_(wptr<JsTransitionController> (this))
141 {
142 WLOGI("[NAPI] JsTransitionController constructorCnt: %{public}d", ++g_jsTransCtrlCtorCnt);
143 }
144
~JsTransitionController()145 JsTransitionController::~JsTransitionController()
146 {
147 WLOGI("[NAPI] ~JsTransitionController deConstructorCnt: %{public}d", ++g_jsTransCtrlDtorCnt);
148 }
149
AnimationForShown()150 void JsTransitionController::AnimationForShown()
151 {
152 WLOGI("[NAPI]AnimationForShown");
153 std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback> (
154 [self = weakRef_](napi_env, NapiAsyncTask&, int32_t) {
155 auto thisController = self.promote();
156 if (thisController == nullptr) {
157 WLOGFE("this transition Controller is null!");
158 return;
159 }
160 HandleScope handleScope(thisController->env_);
161 auto jsWin = thisController->jsWin_.lock();
162 auto window = thisController->windowToken_.promote();
163 if (jsWin == nullptr || window == nullptr) {
164 WLOGFE("native window or jsWindow is null!");
165 return;
166 }
167 auto state = window->GetWindowState();
168 if (state != WindowState::STATE_SHOWN) {
169 WLOGFE("animation shown configuration for state %{public}u not support!", static_cast<uint32_t>(state));
170 return;
171 }
172 napi_value jsTransContextObj = CreateJsTransitionContextObject(
173 thisController->env_, jsWin, window, true);
174 if (jsTransContextObj == nullptr) {
175 return;
176 }
177 napi_value argv[] = { jsTransContextObj };
178 thisController->CallJsMethod("animationForShown", argv, ArraySize(argv));
179 // add to rs tree before animation
180 window->UpdateSurfaceNodeAfterCustomAnimation(true);
181 }
182 );
183
184 napi_ref callback = nullptr;
185 std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
186 NapiAsyncTask::Schedule("JsTransitionController::AnimationForShown", env_,
187 std::make_unique<NapiAsyncTask>(callback, std::move(execute), std::move(complete)));
188 }
189
AnimationForHidden()190 void JsTransitionController::AnimationForHidden()
191 {
192 WLOGI("[NAPI]AnimationForHidden");
193 std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback> (
194 [self = weakRef_](napi_env, NapiAsyncTask&, int32_t) {
195 auto thisController = self.promote();
196 if (thisController == nullptr) {
197 WLOGFE("this transition Controller is null!");
198 return;
199 }
200 HandleScope handleScope(thisController->env_);
201 auto jsWin = thisController->jsWin_.lock();
202 auto window = thisController->windowToken_.promote();
203 if (jsWin == nullptr || window == nullptr) {
204 WLOGFE("native window or jsWindow is null!");
205 return;
206 }
207 auto state = window->GetWindowState();
208 if (state != WindowState::STATE_HIDDEN) {
209 WLOGFE("animation hidden configuration for state %{public}u not support!",
210 static_cast<uint32_t>(state));
211 return;
212 }
213 napi_value jsTransContextObj = CreateJsTransitionContextObject(
214 thisController->env_, jsWin, window, false);
215 if (jsTransContextObj == nullptr) {
216 return;
217 }
218 napi_value argv[] = { jsTransContextObj };
219 thisController->CallJsMethod("animationForHidden", argv, ArraySize(argv));
220 }
221 );
222 napi_ref callback = nullptr;
223 std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
224 NapiAsyncTask::Schedule("JsTransitionController::AnimationForHidden", env_,
225 std::make_unique<NapiAsyncTask>(callback, std::move(execute), std::move(complete)));
226 }
227
CallJsMethod(const std::string & methodName,napi_value const * argv,size_t argc)228 void JsTransitionController::CallJsMethod(const std::string& methodName, napi_value const * argv, size_t argc)
229 {
230 WLOGI("Call js function:%{public}s.", methodName.c_str());
231 auto self = jsTransControllerObj_.lock();
232 if (self == nullptr) {
233 WLOGFE("JsController is null!");
234 return;
235 }
236 auto jsControllerObj = self->GetNapiValue();
237 if (jsControllerObj == nullptr) {
238 WLOGFE("JsControllerObj is null!");
239 return;
240 }
241 napi_value method = nullptr;
242 napi_get_named_property(env_, jsControllerObj, methodName.c_str(), &method);
243 if (method == nullptr || GetType(env_, method) == napi_undefined) {
244 WLOGFE("Failed to get %{public}s from object", methodName.c_str());
245 return;
246 }
247 napi_call_function(env_, jsControllerObj, method, argc, argv, nullptr);
248 }
249
SetJsController(std::shared_ptr<NativeReference> jsVal)250 void JsTransitionController::SetJsController(std::shared_ptr<NativeReference> jsVal)
251 {
252 jsTransControllerObj_ = jsVal;
253 }
254 }
255 }