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:
44     void OnDispatchPointerEvent(std::shared_ptr<MMI::PointerEvent>& inputEvent) override
45     {
46         WLOGI("called");
47     }
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_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     return contextObj;
90 }
91 
Create(const std::unique_ptr<AbilityRuntime::Runtime>& runtime)92 JsWindowExtension* JsWindowExtension::Create(const std::unique_ptr<AbilityRuntime::Runtime>& runtime)
93 {
94     WLOGFD("Create runtime");
95     return new JsWindowExtension(static_cast<AbilityRuntime::JsRuntime&>(*runtime));
96 }
97 
JsWindowExtension(AbilityRuntime::JsRuntime& jsRuntime)98 JsWindowExtension::JsWindowExtension(AbilityRuntime::JsRuntime& jsRuntime) : jsRuntime_(jsRuntime) {}
~JsWindowExtension()99 JsWindowExtension::~JsWindowExtension()
100 {
101     WLOGFD("Called");
102     auto context = GetContext();
103     if (context) {
104         context->Unbind();
105     }
106     jsRuntime_.FreeNativeReference(std::move(jsObj_));
107 }
108 
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)109 void JsWindowExtension::Init(const std::shared_ptr<AbilityRuntime::AbilityLocalRecord>& record,
110     const std::shared_ptr<AbilityRuntime::OHOSApplication>& application,
111     std::shared_ptr<AbilityRuntime::AbilityHandler>& handler,
112     const sptr<IRemoteObject>& token)
113 {
114     HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "WindowExtension Init");
115     WindowExtension::Init(record, application, handler, token);
116     std::string srcPath;
117     GetSrcPath(srcPath);
118     if (srcPath.empty()) {
119         WLOGFE("Failed to get srcPath");
120         return;
121     }
122 
123     std::string moduleName(Extension::abilityInfo_->moduleName);
124     moduleName.append("::").append(abilityInfo_->name);
125     WLOGI("JsWindowExtension::Init module:%{public}s,srcPath:%{public}s.", moduleName.c_str(), srcPath.c_str());
126     AbilityRuntime::HandleScope handleScope(jsRuntime_);
127 
128     jsObj_ = jsRuntime_.LoadModule(moduleName, srcPath, abilityInfo_->hapPath,
129         abilityInfo_->compileMode == AbilityRuntime::CompileMode::ES_MODULE);
130     if (jsObj_ == nullptr) {
131         WLOGFE("Failed to get jsObj_");
132         return;
133     }
134     WLOGI("JsWindowExtension::Init ConvertNativeValueTo.");
135     napi_env env = jsRuntime_.GetNapiEnv();
136     napi_value obj = jsObj_->GetNapiValue();
137     if (obj == nullptr) {
138         WLOGFE("Failed to get JsWindowExtension object");
139         return;
140     }
141 
142     BindContext(env, obj);
143 }
144 
BindContext(napi_env env, napi_value obj)145 void JsWindowExtension::BindContext(napi_env env, napi_value obj)
146 {
147     auto context = GetContext();
148     if (context == nullptr) {
149         WLOGFE("Failed to get context");
150         return;
151     }
152 
153     napi_value contextObj = CreateJsWindowExtensionContext(jsRuntime_.GetNapiEnv(), context);
154     if (contextObj == nullptr) {
155         WLOGFE("Failed to get js window extension context");
156         return;
157     }
158     auto shellContextRef = jsRuntime_.LoadSystemModule("application.WindowExtensionContext", &contextObj, 1);
159     contextObj = shellContextRef->GetNapiValue();
160     if (contextObj == nullptr) {
161         WLOGFE("Failed to get context native object");
162         return;
163     }
164     auto workContext = new (std::nothrow) std::weak_ptr<WindowExtensionContext>(context);
165     if (workContext == nullptr) {
166         WLOGFE("Failed to get window extension context");
167         return;
168     }
169     napi_coerce_to_native_binding_object(env, contextObj,
170         AbilityRuntime::DetachCallbackFunc, AttachWindowExtensionContext, workContext, nullptr);
171     WLOGI("JsWindowExtension::Init Bind.");
172     context->Bind(jsRuntime_, shellContextRef.release());
173     WLOGI("JsWindowExtension::SetProperty.");
174     napi_set_named_property(env, obj, "context", contextObj);
175 
176     napi_wrap(env, contextObj, workContext,
177               [](napi_env, void* data, void *) {
178                 WLOGI("Finalizer for weak_ptr extension context is called");
179                 delete static_cast<std::weak_ptr<WindowExtensionContext>*>(data);
180               }, nullptr, nullptr);
181     WLOGI("JsWindowExtension::Init end.");
182 }
183 
GetSrcPath(std::string& srcPath) const184 void JsWindowExtension::GetSrcPath(std::string& srcPath) const
185 {
186     if (!Extension::abilityInfo_) {
187         WLOGFE("abilityInfo_ is nullptr");
188         return;
189     }
190 
191     if (!Extension::abilityInfo_->isModuleJson) {
192         srcPath.append(Extension::abilityInfo_->package);
193         srcPath.append("/assets/js/");
194         if (!Extension::abilityInfo_->srcPath.empty()) {
195             srcPath.append(Extension::abilityInfo_->srcPath);
196         }
197         srcPath.append("/").append(Extension::abilityInfo_->name).append(".abc");
198         return;
199     }
200 
201     if (!Extension::abilityInfo_->srcEntrance.empty()) {
202         srcPath.append(Extension::abilityInfo_->moduleName + "/");
203         srcPath.append(Extension::abilityInfo_->srcEntrance);
204         srcPath.erase(srcPath.rfind('.'));
205         srcPath.append(".abc");
206     }
207 }
208 
OnConnect(const AAFwk::Want& want)209 sptr<IRemoteObject> JsWindowExtension::OnConnect(const AAFwk::Want& want)
210 {
211     HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "WindowExtension OnConnect %s-%s",
212         want.GetElement().GetAbilityName().c_str(), want.GetElement().GetAbilityName().c_str());
213     WLOGI("called.");
214     Extension::OnConnect(want);
215     napi_env env = jsRuntime_.GetNapiEnv();
216     std::unique_ptr<AbilityRuntime::NapiAsyncTask::CompleteCallback> complete =
217         std::make_unique<AbilityRuntime::NapiAsyncTask::CompleteCallback>(
218         [=] (napi_env env, AbilityRuntime::NapiAsyncTask& task, int32_t status) {
219             napi_env napiEnv = jsRuntime_.GetNapiEnv();
220             napi_value napiWant = OHOS::AppExecFwk::WrapWant(napiEnv, want);
221             napi_value argv[] = { napiWant };
222             CallJsMethod("onConnect", argv, AbilityRuntime::ArraySize(argv));
223         }
224     );
225     napi_ref callback = nullptr;
226     std::unique_ptr<AbilityRuntime::NapiAsyncTask::ExecuteCallback> execute = nullptr;
227     AbilityRuntime::NapiAsyncTask::Schedule("JsWindowExtension::OnConnect", env,
228         std::make_unique<AbilityRuntime::NapiAsyncTask>(callback, std::move(execute), std::move(complete)));
229 
230     if (!stub_) {
231         WLOGFE("stub is nullptr.");
232         return nullptr;
233     }
234     WLOGFD("Create stub successfully!");
235     WindowManager::GetInstance().NotifyWindowExtensionVisibilityChange(getprocpid(), getuid(), true);
236     auto context = GetContext();
237     AAFwk::AbilityManagerClient::GetInstance()->ScheduleCommandAbilityWindowDone(
238         context->GetToken(), sessionInfo_, AAFwk::WIN_CMD_FOREGROUND, AAFwk::ABILITY_CMD_FOREGROUND);
239     return stub_->AsObject();
240 }
241 
OnDisconnect(const AAFwk::Want& want)242 void JsWindowExtension::OnDisconnect(const AAFwk::Want& want)
243 {
244     Extension::OnDisconnect(want);
245     napi_env env = jsRuntime_.GetNapiEnv();
246     napi_value napiWant = OHOS::AppExecFwk::WrapWant(env, want);
247     napi_value argv[] = { napiWant };
248     CallJsMethod("onDisconnect", argv, AbilityRuntime::ArraySize(argv));
249     auto window = stub_ != nullptr ? stub_->GetWindow() : nullptr;
250     if (window != nullptr) {
251         window->Destroy();
252         WLOGI("Destroy window.");
253     }
254     WLOGI("called.");
255     WindowManager::GetInstance().NotifyWindowExtensionVisibilityChange(getprocpid(), getuid(), false);
256     auto context = GetContext();
257     AAFwk::AbilityManagerClient::GetInstance()->ScheduleCommandAbilityWindowDone(
258         context->GetToken(), sessionInfo_, AAFwk::WIN_CMD_DESTROY, AAFwk::ABILITY_CMD_DESTROY);
259 }
260 
OnStart(const AAFwk::Want& want, sptr<AAFwk::SessionInfo> sessionInfo)261 void JsWindowExtension::OnStart(const AAFwk::Want& want, sptr<AAFwk::SessionInfo> sessionInfo)
262 {
263     WLOGI("OnStart");
264     HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "WindowExtension OnStart %s-%s",
265         want.GetElement().GetAbilityName().c_str(), want.GetElement().GetAbilityName().c_str());
266     sessionInfo_ = sessionInfo;
267     Extension::OnStart(want);
268 
269     AbilityRuntime::ElementName elementName = want.GetElement();
270     std::string windowName = elementName.GetBundleName() + elementName.GetModuleName() +
271         elementName.GetAbilityName() + std::to_string(extensionCnt_);
272     extensionCnt_++;
273 
274     stub_ = new(std::nothrow) WindowExtensionStubImpl(windowName);
275     WLOGI("JsWindowExtension OnStart begin..");
276     Rect rect { want.GetIntParam(RECT_FORM_KEY_POS_X, 0),
277     want.GetIntParam(RECT_FORM_KEY_POS_Y, 0),
278     want.GetIntParam(RECT_FORM_KEY_WIDTH, 0),
279     want.GetIntParam(RECT_FORM_KEY_HEIGHT, 0) };
280     uint32_t windowId = static_cast<uint32_t>(want.GetIntParam(WINDOW_ID, INVALID_WINDOW_ID));
281     if (stub_ != nullptr) {
282         auto context = GetContext();
283         if (context == nullptr) {
284             WLOGFE("get context failed");
285             return;
286         }
287         sptr<Window> window = stub_->CreateWindow(rect, windowId, context,
288             sessionInfo == nullptr ? nullptr : sessionInfo->sessionToken);
289         if (window == nullptr) {
290             WLOGFE("create window failed");
291             return;
292         }
293         OnWindowCreated();
294         WLOGI("ability context onWindowReady rect x =%{public}d y=%{public}d w=%{public}d h=%{public}d ",
295             rect.posX_, rect.posY_, rect.width_, rect.height_);
296     }
297 }
298 
OnWindowCreated() const299 void JsWindowExtension::OnWindowCreated() const
300 {
301     napi_env env = jsRuntime_.GetNapiEnv();
302     std::unique_ptr<AbilityRuntime::NapiAsyncTask::CompleteCallback> complete =
303         std::make_unique<AbilityRuntime::NapiAsyncTask::CompleteCallback>(
304         [=] (napi_env env, AbilityRuntime::NapiAsyncTask& task, int32_t status) {
305             auto window = stub_->GetWindow();
306             if (window == nullptr) {
307                 WLOGFE("get window failed");
308                 return;
309             }
310             napi_value value = CreateJsWindowObject(env, window);
311             if (value == nullptr) {
312                 WLOGFE("Create js window failed");
313                 return;
314             }
315             napi_value argv[] = { value };
316             CallJsMethod("onWindowReady", argv, AbilityRuntime::ArraySize(argv));
317         }
318     );
319 
320     napi_ref callback = nullptr;
321     std::unique_ptr<AbilityRuntime::NapiAsyncTask::ExecuteCallback> execute = nullptr;
322     AbilityRuntime::NapiAsyncTask::Schedule("JsWindowExtension::OnWindowCreated", env,
323         std::make_unique<AbilityRuntime::NapiAsyncTask>(callback, std::move(execute), std::move(complete)));
324 }
325 
GetType(napi_env env, napi_value value)326 napi_valuetype GetType(napi_env env, napi_value value)
327 {
328     napi_valuetype res = napi_undefined;
329     napi_typeof(env, value, &res);
330     return res;
331 }
332 
CallJsMethod(const char* name, napi_value const * argv, size_t argc) const333 napi_value JsWindowExtension::CallJsMethod(const char* name, napi_value const * argv, size_t argc) const
334 {
335     WLOGI("called (%{public}s), begin", name);
336 
337     if (!jsObj_) {
338         WLOGFW("Not found WindowExtension.js");
339         return nullptr;
340     }
341 
342     AbilityRuntime::HandleScope handleScope(jsRuntime_);
343     napi_env env = jsRuntime_.GetNapiEnv();
344 
345     napi_value value = jsObj_->GetNapiValue();
346     if (value == nullptr) {
347         WLOGFE("Failed to get WindowExtension object");
348         return nullptr;
349     }
350 
351     napi_value method = nullptr;
352     napi_get_named_property(env, value, name, &method);
353     if (method == nullptr || GetType(env, method) != napi_function) {
354         WLOGFE("Failed to get '%{public}s' from WindowExtension object", name);
355         return nullptr;
356     }
357     WLOGI("(%{public}s), success", name);
358     napi_value result = nullptr;
359     napi_call_function(env, value, method, argc, argv, &result);
360     return result;
361 }
362 } // namespace Rosen
363 } // namespace OHOS
364