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_window_extension.h"
17
18 #include <hitrace_meter.h>
19 #include <napi_common_want.h>
20 #include <native_engine/native_reference.h>
21 #include <native_engine/native_value.h>
22 #include <js_extension_context.h>
23 #include <js_runtime_utils.h>
24
25 #include "ability_manager_client.h"
26 #include "js_window.h"
27 #include "js_window_extension_context.h"
28 #include "ui_extension_window_command.h"
29 #include "window_extension_connection.h"
30 #include "window_manager_hilog.h"
31 #include "wm_common.h"
32 #include "wm_common_inner.h"
33 #include "window_manager.h"
34
35 namespace OHOS {
36 namespace Rosen {
37 namespace {
38 constexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_WINDOW, "JSWindowExtension"};
39 }
40 int JsWindowExtension::extensionCnt_ = 0;
41
42 class DispatchInputEventListener : public IDispatchInputEventListener {
43 public:
OnDispatchPointerEvent(std::shared_ptr<MMI::PointerEvent> & inputEvent)44 void OnDispatchPointerEvent(std::shared_ptr<MMI::PointerEvent>& inputEvent) override
45 {
46 WLOGI("called");
47 }
OnDispatchKeyEvent(std::shared_ptr<MMI::KeyEvent> & keyEvent)48 void OnDispatchKeyEvent(std::shared_ptr<MMI::KeyEvent>& keyEvent) override
49 {
50 WLOGI("called");
51 }
52 };
53
AttachWindowExtensionContext(napi_env env,void * value,void *)54 napi_value AttachWindowExtensionContext(napi_env env, void* value, void *)
55 {
56 WLOGI("AttachWindowExtensionContext");
57 if (value == nullptr) {
58 WLOGFE("invalid parameter.");
59 return nullptr;
60 }
61 auto ptr = reinterpret_cast<std::weak_ptr<WindowExtensionContext> *>(value)->lock();
62 if (ptr == nullptr) {
63 WLOGFE("invalid context.");
64 return nullptr;
65 }
66 napi_value object = CreateJsWindowExtensionContext(env, ptr);
67 if (object == nullptr) {
68 WLOGFE("Failed to get js window extension context");
69 return nullptr;
70 }
71 auto contextObj = AbilityRuntime::JsRuntime::LoadSystemModuleByEngine(env,
72 "application.WindowExtensionContext", &object, 1)->GetNapiValue();
73 if (contextObj == nullptr) {
74 WLOGFE("Failed to get context native object");
75 return nullptr;
76 }
77 napi_coerce_to_native_binding_object(env, contextObj,
78 AbilityRuntime::DetachCallbackFunc, AttachWindowExtensionContext, value, nullptr);
79 auto workContext = new (std::nothrow) std::weak_ptr<WindowExtensionContext>(ptr);
80 if (workContext == nullptr) {
81 WLOGFE("Failed to get window extension context");
82 return nullptr;
83 }
84 napi_status status = napi_wrap(env, contextObj, workContext,
85 [](napi_env, void* data, void *) {
86 WLOGI("Finalizer for weak_ptr service extension context is called");
87 delete static_cast<std::weak_ptr<WindowExtensionContext> *>(data);
88 }, nullptr, nullptr);
89 if (status != napi_ok) {
90 WLOGFE("Failed to call napi_wrap");
91 delete workContext;
92 return nullptr;
93 }
94 return contextObj;
95 }
96
Create(const std::unique_ptr<AbilityRuntime::Runtime> & runtime)97 JsWindowExtension* JsWindowExtension::Create(const std::unique_ptr<AbilityRuntime::Runtime>& runtime)
98 {
99 WLOGFD("Create runtime");
100 return new JsWindowExtension(static_cast<AbilityRuntime::JsRuntime&>(*runtime));
101 }
102
JsWindowExtension(AbilityRuntime::JsRuntime & jsRuntime)103 JsWindowExtension::JsWindowExtension(AbilityRuntime::JsRuntime& jsRuntime) : jsRuntime_(jsRuntime) {}
~JsWindowExtension()104 JsWindowExtension::~JsWindowExtension()
105 {
106 WLOGFD("Called");
107 auto context = GetContext();
108 if (context) {
109 context->Unbind();
110 }
111 jsRuntime_.FreeNativeReference(std::move(jsObj_));
112 }
113
Init(const std::shared_ptr<AbilityRuntime::AbilityLocalRecord> & record,const std::shared_ptr<AbilityRuntime::OHOSApplication> & application,std::shared_ptr<AbilityRuntime::AbilityHandler> & handler,const sptr<IRemoteObject> & token)114 void JsWindowExtension::Init(const std::shared_ptr<AbilityRuntime::AbilityLocalRecord>& record,
115 const std::shared_ptr<AbilityRuntime::OHOSApplication>& application,
116 std::shared_ptr<AbilityRuntime::AbilityHandler>& handler,
117 const sptr<IRemoteObject>& token)
118 {
119 HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "WindowExtension Init");
120 WindowExtension::Init(record, application, handler, token);
121 std::string srcPath;
122 GetSrcPath(srcPath);
123 if (srcPath.empty()) {
124 WLOGFE("Failed to get srcPath");
125 return;
126 }
127
128 std::string moduleName(Extension::abilityInfo_->moduleName);
129 moduleName.append("::").append(abilityInfo_->name);
130 WLOGI("JsWindowExtension::Init module:%{public}s,srcPath:%{public}s.", moduleName.c_str(), srcPath.c_str());
131 AbilityRuntime::HandleScope handleScope(jsRuntime_);
132
133 jsObj_ = jsRuntime_.LoadModule(moduleName, srcPath, abilityInfo_->hapPath,
134 abilityInfo_->compileMode == AbilityRuntime::CompileMode::ES_MODULE);
135 if (jsObj_ == nullptr) {
136 WLOGFE("Failed to get jsObj_");
137 return;
138 }
139 WLOGI("JsWindowExtension::Init ConvertNativeValueTo.");
140 napi_env env = jsRuntime_.GetNapiEnv();
141 napi_value obj = jsObj_->GetNapiValue();
142 if (obj == nullptr) {
143 WLOGFE("Failed to get JsWindowExtension object");
144 return;
145 }
146
147 BindContext(env, obj);
148 }
149
BindContext(napi_env env,napi_value obj)150 void JsWindowExtension::BindContext(napi_env env, napi_value obj)
151 {
152 auto context = GetContext();
153 if (context == nullptr) {
154 WLOGFE("Failed to get context");
155 return;
156 }
157
158 napi_value contextObj = CreateJsWindowExtensionContext(jsRuntime_.GetNapiEnv(), context);
159 if (contextObj == nullptr) {
160 WLOGFE("Failed to get js window extension context");
161 return;
162 }
163 auto shellContextRef = jsRuntime_.LoadSystemModule("application.WindowExtensionContext", &contextObj, 1);
164 contextObj = shellContextRef->GetNapiValue();
165 if (contextObj == nullptr) {
166 WLOGFE("Failed to get context native object");
167 return;
168 }
169 auto workContext = new (std::nothrow) std::weak_ptr<WindowExtensionContext>(context);
170 if (workContext == nullptr) {
171 WLOGFE("Failed to get window extension context");
172 return;
173 }
174 napi_coerce_to_native_binding_object(env, contextObj,
175 AbilityRuntime::DetachCallbackFunc, AttachWindowExtensionContext, workContext, nullptr);
176 WLOGI("JsWindowExtension::Init Bind.");
177 context->Bind(jsRuntime_, shellContextRef.release());
178 WLOGI("JsWindowExtension::SetProperty.");
179 napi_set_named_property(env, obj, "context", contextObj);
180
181 napi_status status = napi_wrap(env, contextObj, workContext,
182 [](napi_env, void* data, void *) {
183 WLOGI("Finalizer for weak_ptr extension context is called");
184 delete static_cast<std::weak_ptr<WindowExtensionContext>*>(data);
185 }, nullptr, nullptr);
186 if (status != napi_ok) {
187 WLOGFE("Failed to call napi_wrap");
188 delete workContext;
189 return;
190 }
191 WLOGI("JsWindowExtension::Init end.");
192 }
193
GetSrcPath(std::string & srcPath) const194 void JsWindowExtension::GetSrcPath(std::string& srcPath) const
195 {
196 if (!Extension::abilityInfo_) {
197 WLOGFE("abilityInfo_ is nullptr");
198 return;
199 }
200
201 if (!Extension::abilityInfo_->isModuleJson) {
202 srcPath.append(Extension::abilityInfo_->package);
203 srcPath.append("/assets/js/");
204 if (!Extension::abilityInfo_->srcPath.empty()) {
205 srcPath.append(Extension::abilityInfo_->srcPath);
206 }
207 srcPath.append("/").append(Extension::abilityInfo_->name).append(".abc");
208 return;
209 }
210
211 if (!Extension::abilityInfo_->srcEntrance.empty()) {
212 srcPath.append(Extension::abilityInfo_->moduleName + "/");
213 srcPath.append(Extension::abilityInfo_->srcEntrance);
214 srcPath.erase(srcPath.rfind('.'));
215 srcPath.append(".abc");
216 }
217 }
218
OnConnect(const AAFwk::Want & want)219 sptr<IRemoteObject> JsWindowExtension::OnConnect(const AAFwk::Want& want)
220 {
221 HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "WindowExtension OnConnect %s-%s",
222 want.GetElement().GetAbilityName().c_str(), want.GetElement().GetAbilityName().c_str());
223 WLOGI("called.");
224 Extension::OnConnect(want);
225 napi_env env = jsRuntime_.GetNapiEnv();
226 std::unique_ptr<AbilityRuntime::NapiAsyncTask::CompleteCallback> complete =
227 std::make_unique<AbilityRuntime::NapiAsyncTask::CompleteCallback>(
228 [=] (napi_env env, AbilityRuntime::NapiAsyncTask& task, int32_t status) {
229 napi_env napiEnv = jsRuntime_.GetNapiEnv();
230 napi_value napiWant = OHOS::AppExecFwk::WrapWant(napiEnv, want);
231 napi_value argv[] = { napiWant };
232 CallJsMethod("onConnect", argv, AbilityRuntime::ArraySize(argv));
233 }
234 );
235 napi_ref callback = nullptr;
236 std::unique_ptr<AbilityRuntime::NapiAsyncTask::ExecuteCallback> execute = nullptr;
237 AbilityRuntime::NapiAsyncTask::Schedule("JsWindowExtension::OnConnect", env,
238 std::make_unique<AbilityRuntime::NapiAsyncTask>(callback, std::move(execute), std::move(complete)));
239
240 if (!stub_) {
241 WLOGFE("stub is nullptr.");
242 return nullptr;
243 }
244 WLOGFD("Create stub successfully!");
245 WindowManager::GetInstance().NotifyWindowExtensionVisibilityChange(getprocpid(), getuid(), true);
246 auto context = GetContext();
247 AAFwk::AbilityManagerClient::GetInstance()->ScheduleCommandAbilityWindowDone(
248 context->GetToken(), sessionInfo_, AAFwk::WIN_CMD_FOREGROUND, AAFwk::ABILITY_CMD_FOREGROUND);
249 return stub_->AsObject();
250 }
251
OnDisconnect(const AAFwk::Want & want)252 void JsWindowExtension::OnDisconnect(const AAFwk::Want& want)
253 {
254 Extension::OnDisconnect(want);
255 napi_env env = jsRuntime_.GetNapiEnv();
256 napi_value napiWant = OHOS::AppExecFwk::WrapWant(env, want);
257 napi_value argv[] = { napiWant };
258 CallJsMethod("onDisconnect", argv, AbilityRuntime::ArraySize(argv));
259 auto window = stub_ != nullptr ? stub_->GetWindow() : nullptr;
260 if (window != nullptr) {
261 window->Destroy();
262 WLOGI("Destroy window.");
263 }
264 WLOGI("called.");
265 WindowManager::GetInstance().NotifyWindowExtensionVisibilityChange(getprocpid(), getuid(), false);
266 auto context = GetContext();
267 AAFwk::AbilityManagerClient::GetInstance()->ScheduleCommandAbilityWindowDone(
268 context->GetToken(), sessionInfo_, AAFwk::WIN_CMD_DESTROY, AAFwk::ABILITY_CMD_DESTROY);
269 }
270
OnStart(const AAFwk::Want & want,sptr<AAFwk::SessionInfo> sessionInfo)271 void JsWindowExtension::OnStart(const AAFwk::Want& want, sptr<AAFwk::SessionInfo> sessionInfo)
272 {
273 WLOGI("OnStart");
274 HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "WindowExtension OnStart %s-%s",
275 want.GetElement().GetAbilityName().c_str(), want.GetElement().GetAbilityName().c_str());
276 sessionInfo_ = sessionInfo;
277 Extension::OnStart(want);
278
279 AbilityRuntime::ElementName elementName = want.GetElement();
280 std::string windowName = elementName.GetBundleName() + elementName.GetModuleName() +
281 elementName.GetAbilityName() + std::to_string(extensionCnt_);
282 extensionCnt_++;
283
284 stub_ = new(std::nothrow) WindowExtensionStubImpl(windowName);
285 WLOGI("JsWindowExtension OnStart begin..");
286 Rect rect { want.GetIntParam(RECT_FORM_KEY_POS_X, 0),
287 want.GetIntParam(RECT_FORM_KEY_POS_Y, 0),
288 want.GetIntParam(RECT_FORM_KEY_WIDTH, 0),
289 want.GetIntParam(RECT_FORM_KEY_HEIGHT, 0) };
290 uint32_t windowId = static_cast<uint32_t>(want.GetIntParam(WINDOW_ID, INVALID_WINDOW_ID));
291 if (stub_ != nullptr) {
292 auto context = GetContext();
293 if (context == nullptr) {
294 WLOGFE("get context failed");
295 return;
296 }
297 sptr<Window> window = stub_->CreateWindow(rect, windowId, context,
298 sessionInfo == nullptr ? nullptr : sessionInfo->sessionToken);
299 if (window == nullptr) {
300 WLOGFE("create window failed");
301 return;
302 }
303 OnWindowCreated();
304 WLOGI("ability context onWindowReady rect x =%{public}d y=%{public}d w=%{public}d h=%{public}d ",
305 rect.posX_, rect.posY_, rect.width_, rect.height_);
306 }
307 }
308
OnWindowCreated() const309 void JsWindowExtension::OnWindowCreated() const
310 {
311 napi_env env = jsRuntime_.GetNapiEnv();
312 std::unique_ptr<AbilityRuntime::NapiAsyncTask::CompleteCallback> complete =
313 std::make_unique<AbilityRuntime::NapiAsyncTask::CompleteCallback>(
314 [=] (napi_env env, AbilityRuntime::NapiAsyncTask& task, int32_t status) {
315 auto window = stub_->GetWindow();
316 if (window == nullptr) {
317 WLOGFE("get window failed");
318 return;
319 }
320 napi_value value = CreateJsWindowObject(env, window);
321 if (value == nullptr) {
322 WLOGFE("Create js window failed");
323 return;
324 }
325 napi_value argv[] = { value };
326 CallJsMethod("onWindowReady", argv, AbilityRuntime::ArraySize(argv));
327 }
328 );
329
330 napi_ref callback = nullptr;
331 std::unique_ptr<AbilityRuntime::NapiAsyncTask::ExecuteCallback> execute = nullptr;
332 AbilityRuntime::NapiAsyncTask::Schedule("JsWindowExtension::OnWindowCreated", env,
333 std::make_unique<AbilityRuntime::NapiAsyncTask>(callback, std::move(execute), std::move(complete)));
334 }
335
GetType(napi_env env,napi_value value)336 napi_valuetype GetType(napi_env env, napi_value value)
337 {
338 napi_valuetype res = napi_undefined;
339 napi_typeof(env, value, &res);
340 return res;
341 }
342
CallJsMethod(const char * name,napi_value const * argv,size_t argc) const343 napi_value JsWindowExtension::CallJsMethod(const char* name, napi_value const * argv, size_t argc) const
344 {
345 WLOGI("called (%{public}s), begin", name);
346
347 if (!jsObj_) {
348 WLOGFW("Not found WindowExtension.js");
349 return nullptr;
350 }
351
352 AbilityRuntime::HandleScope handleScope(jsRuntime_);
353 napi_env env = jsRuntime_.GetNapiEnv();
354
355 napi_value value = jsObj_->GetNapiValue();
356 if (value == nullptr) {
357 WLOGFE("Failed to get WindowExtension object");
358 return nullptr;
359 }
360
361 napi_value method = nullptr;
362 napi_get_named_property(env, value, name, &method);
363 if (method == nullptr || GetType(env, method) != napi_function) {
364 WLOGFE("Failed to get '%{public}s' from WindowExtension object", name);
365 return nullptr;
366 }
367 WLOGI("(%{public}s), success", name);
368 napi_value result = nullptr;
369 napi_call_function(env, value, method, argc, argv, &result);
370 return result;
371 }
372 } // namespace Rosen
373 } // namespace OHOS
374