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_inputmethod_extension.h"
17
18 #include "ability_info.h"
19 #include "global.h"
20 #include "input_method_ability.h"
21 #include "inputmethod_trace.h"
22 #include "js_inputmethod_extension_context.h"
23 #include "js_runtime.h"
24 #include "js_runtime_utils.h"
25 #include "napi/native_api.h"
26 #include "napi/native_node_api.h"
27 #include "napi_common_util.h"
28 #include "napi_common_want.h"
29 #include "napi_remote_object.h"
30
31 namespace OHOS {
32 namespace AbilityRuntime {
33 namespace {
34 constexpr size_t ARGC_ONE = 1;
35 constexpr size_t ARGC_TWO = 2;
36 } // namespace
37 JsInputMethodExtension *JsInputMethodExtension::jsInputMethodExtension = nullptr;
38 using namespace OHOS::AppExecFwk;
39 using namespace OHOS::MiscServices;
40
AttachInputMethodExtensionContext(NativeEngine * engine,void * value,void *)41 NativeValue *AttachInputMethodExtensionContext(NativeEngine *engine, void *value, void *)
42 {
43 IMSA_HILOGI("AttachInputMethodExtensionContext");
44 if (value == nullptr) {
45 IMSA_HILOGW("invalid parameter.");
46 return nullptr;
47 }
48 auto ptr = reinterpret_cast<std::weak_ptr<InputMethodExtensionContext> *>(value)->lock();
49 if (ptr == nullptr) {
50 IMSA_HILOGW("invalid context.");
51 return nullptr;
52 }
53 NativeValue *object = CreateJsInputMethodExtensionContext(*engine, ptr);
54 auto contextObj = JsRuntime::LoadSystemModuleByEngine(engine, "InputMethodExtensionContext", &object, 1)->Get();
55 NativeObject *nObject = ConvertNativeValueTo<NativeObject>(contextObj);
56 nObject->ConvertToNativeBindingObject(
57 engine, DetachCallbackFunc, AttachInputMethodExtensionContext, value, nullptr);
58 auto workContext = new (std::nothrow) std::weak_ptr<InputMethodExtensionContext>(ptr);
59 nObject->SetNativePointer(
60 workContext,
61 [](NativeEngine *, void *data, void *) {
62 IMSA_HILOGI("Finalizer for weak_ptr input method extension context is called");
63 delete static_cast<std::weak_ptr<InputMethodExtensionContext> *>(data);
64 },
65 nullptr);
66 return contextObj;
67 }
68
Create(const std::unique_ptr<Runtime> & runtime)69 JsInputMethodExtension *JsInputMethodExtension::Create(const std::unique_ptr<Runtime> &runtime)
70 {
71 IMSA_HILOGI("JsInputMethodExtension Create");
72 jsInputMethodExtension = new JsInputMethodExtension(static_cast<JsRuntime &>(*runtime));
73 return jsInputMethodExtension;
74 }
75
JsInputMethodExtension(JsRuntime & jsRuntime)76 JsInputMethodExtension::JsInputMethodExtension(JsRuntime &jsRuntime) : jsRuntime_(jsRuntime)
77 {
78 }
79
~JsInputMethodExtension()80 JsInputMethodExtension::~JsInputMethodExtension()
81 {
82 jsRuntime_.FreeNativeReference(std::move(jsObj_));
83 }
84
Init(const std::shared_ptr<AbilityLocalRecord> & record,const std::shared_ptr<OHOSApplication> & application,std::shared_ptr<AbilityHandler> & handler,const sptr<IRemoteObject> & token)85 void JsInputMethodExtension::Init(const std::shared_ptr<AbilityLocalRecord> &record,
86 const std::shared_ptr<OHOSApplication> &application, std::shared_ptr<AbilityHandler> &handler,
87 const sptr<IRemoteObject> &token)
88 {
89 IMSA_HILOGI("JsInputMethodExtension Init");
90 InputMethodExtension::Init(record, application, handler, token);
91 std::string srcPath;
92 GetSrcPath(srcPath);
93 if (srcPath.empty()) {
94 IMSA_HILOGE("Failed to get srcPath");
95 return;
96 }
97
98 std::string moduleName(Extension::abilityInfo_->moduleName);
99 moduleName.append("::").append(abilityInfo_->name);
100 IMSA_HILOGI(
101 "JsInputMethodExtension::Init module:%{public}s,srcPath:%{public}s.", moduleName.c_str(), srcPath.c_str());
102 HandleScope handleScope(jsRuntime_);
103 auto &engine = jsRuntime_.GetNativeEngine();
104
105 jsObj_ = jsRuntime_.LoadModule(
106 moduleName, srcPath, abilityInfo_->hapPath, abilityInfo_->compileMode == CompileMode::ES_MODULE);
107 if (jsObj_ == nullptr) {
108 IMSA_HILOGE("Failed to get jsObj_");
109 return;
110 }
111 IMSA_HILOGI("JsInputMethodExtension::Init ConvertNativeValueTo.");
112 NativeObject *obj = ConvertNativeValueTo<NativeObject>(jsObj_->Get());
113 if (obj == nullptr) {
114 IMSA_HILOGE("Failed to get JsInputMethodExtension object");
115 return;
116 }
117 BindContext(engine, obj);
118 IMSA_HILOGI("JsInputMethodExtension::Init end.");
119 }
120
BindContext(NativeEngine & engine,NativeObject * obj)121 void JsInputMethodExtension::BindContext(NativeEngine &engine, NativeObject *obj)
122 {
123 IMSA_HILOGI("JsInputMethodExtension::BindContext");
124 auto context = GetContext();
125 if (context == nullptr) {
126 IMSA_HILOGE("Failed to get context");
127 return;
128 }
129 IMSA_HILOGI("JsInputMethodExtension::Init CreateJsInputMethodExtensionContext.");
130 NativeValue *contextObj = CreateJsInputMethodExtensionContext(engine, context);
131 auto shellContextRef = jsRuntime_.LoadSystemModule("InputMethodExtensionContext", &contextObj, ARGC_ONE);
132 contextObj = shellContextRef->Get();
133 auto nativeObj = ConvertNativeValueTo<NativeObject>(contextObj);
134 if (nativeObj == nullptr) {
135 IMSA_HILOGE("Failed to get input method extension native object");
136 return;
137 }
138 auto workContext = new (std::nothrow) std::weak_ptr<InputMethodExtensionContext>(context);
139 nativeObj->ConvertToNativeBindingObject(
140 &engine, DetachCallbackFunc, AttachInputMethodExtensionContext, workContext, nullptr);
141 IMSA_HILOGI("JsInputMethodExtension::Init Bind.");
142 context->Bind(jsRuntime_, shellContextRef.release());
143 IMSA_HILOGI("JsInputMethodExtension::SetProperty.");
144 obj->SetProperty("context", contextObj);
145 nativeObj->SetNativePointer(
146 workContext,
147 [](NativeEngine *, void *data, void *) {
148 IMSA_HILOGI("Finalizer for weak_ptr input method extension context is called");
149 delete static_cast<std::weak_ptr<InputMethodExtensionContext> *>(data);
150 },
151 nullptr);
152 }
153
OnStart(const AAFwk::Want & want)154 void JsInputMethodExtension::OnStart(const AAFwk::Want &want)
155 {
156 StartAsync("OnStart", static_cast<int32_t>(TraceTaskId::ONSTART_EXTENSION));
157 StartAsync("Extension::OnStart", static_cast<int32_t>(TraceTaskId::ONSTART_MIDDLE_EXTENSION));
158 Extension::OnStart(want);
159 FinishAsync("Extension::OnStart", static_cast<int32_t>(TraceTaskId::ONSTART_MIDDLE_EXTENSION));
160 IMSA_HILOGI("JsInputMethodExtension OnStart begin..");
161 HandleScope handleScope(jsRuntime_);
162 NativeEngine *nativeEngine = &jsRuntime_.GetNativeEngine();
163 napi_value napiWant = OHOS::AppExecFwk::WrapWant(reinterpret_cast<napi_env>(nativeEngine), want);
164 NativeValue *nativeWant = reinterpret_cast<NativeValue *>(napiWant);
165 NativeValue *argv[] = { nativeWant };
166 StartAsync("onCreate", static_cast<int32_t>(TraceTaskId::ONCREATE_EXTENSION));
167 CallObjectMethod("onCreate", argv, ARGC_ONE);
168 FinishAsync("onCreate", static_cast<int32_t>(TraceTaskId::ONCREATE_EXTENSION));
169 InputMethodAbility::GetInstance()->OnImeReady();
170 auto ret = InputMethodAbility::GetInstance()->SetCoreAndAgent();
171 IMSA_HILOGI("ime bind imf ret: %{public}d", ret);
172 FinishAsync("OnStart", static_cast<int32_t>(TraceTaskId::ONSTART_EXTENSION));
173 }
174
OnStop()175 void JsInputMethodExtension::OnStop()
176 {
177 InputMethodExtension::OnStop();
178 IMSA_HILOGI("JsInputMethodExtension OnStop begin.");
179 CallObjectMethod("onDestroy");
180 bool ret = ConnectionManager::GetInstance().DisconnectCaller(GetContext()->GetToken());
181 if (ret) {
182 IMSA_HILOGI("The input method extension connection is not disconnected.");
183 }
184 IMSA_HILOGI("JsInputMethodExtension %{public}s end.", __func__);
185 }
186
OnConnect(const AAFwk::Want & want)187 sptr<IRemoteObject> JsInputMethodExtension::OnConnect(const AAFwk::Want &want)
188 {
189 IMSA_HILOGI("JsInputMethodExtension OnConnect begin.");
190 StartAsync("OnConnect", static_cast<int32_t>(TraceTaskId::ONCONNECT_EXTENSION));
191 StartAsync("Extension::OnConnect", static_cast<int32_t>(TraceTaskId::ONCONNECT_MIDDLE_EXTENSION));
192 Extension::OnConnect(want);
193 FinishAsync("Extension::OnConnect", static_cast<int32_t>(TraceTaskId::ONCONNECT_MIDDLE_EXTENSION));
194 IMSA_HILOGI("%{public}s begin.", __func__);
195 HandleScope handleScope(jsRuntime_);
196 NativeEngine *nativeEngine = &jsRuntime_.GetNativeEngine();
197 napi_value napiWant = OHOS::AppExecFwk::WrapWant(reinterpret_cast<napi_env>(nativeEngine), want);
198 NativeValue *nativeWant = reinterpret_cast<NativeValue *>(napiWant);
199 NativeValue *argv[] = { nativeWant };
200 if (jsObj_ == nullptr) {
201 IMSA_HILOGE("Not found InputMethodExtension.js");
202 return nullptr;
203 }
204
205 NativeValue *value = jsObj_->Get();
206 NativeObject *obj = ConvertNativeValueTo<NativeObject>(value);
207 if (obj == nullptr) {
208 IMSA_HILOGE("Failed to get InputMethodExtension object");
209 return nullptr;
210 }
211
212 NativeValue *method = obj->GetProperty("onConnect");
213 if (method == nullptr) {
214 IMSA_HILOGE("Failed to get onConnect from InputMethodExtension object");
215 return nullptr;
216 }
217 IMSA_HILOGI("JsInputMethodExtension::CallFunction onConnect, success");
218 NativeValue *remoteNative = nativeEngine->CallFunction(value, method, argv, ARGC_ONE);
219 if (remoteNative == nullptr) {
220 IMSA_HILOGE("remoteNative nullptr.");
221 }
222 auto remoteObj = NAPI_ohos_rpc_getNativeRemoteObject(
223 reinterpret_cast<napi_env>(nativeEngine), reinterpret_cast<napi_value>(remoteNative));
224 if (remoteObj == nullptr) {
225 IMSA_HILOGE("remoteObj nullptr.");
226 }
227 FinishAsync("OnConnect", static_cast<int32_t>(TraceTaskId::ONCONNECT_EXTENSION));
228 return remoteObj;
229 }
230
OnDisconnect(const AAFwk::Want & want)231 void JsInputMethodExtension::OnDisconnect(const AAFwk::Want &want)
232 {
233 IMSA_HILOGI("JsInputMethodExtension OnDisconnect begin.");
234 Extension::OnDisconnect(want);
235 IMSA_HILOGI("%{public}s begin.", __func__);
236 HandleScope handleScope(jsRuntime_);
237 NativeEngine *nativeEngine = &jsRuntime_.GetNativeEngine();
238 napi_value napiWant = OHOS::AppExecFwk::WrapWant(reinterpret_cast<napi_env>(nativeEngine), want);
239 NativeValue *nativeWant = reinterpret_cast<NativeValue *>(napiWant);
240 NativeValue *argv[] = { nativeWant };
241 if (jsObj_ == nullptr) {
242 IMSA_HILOGE("Not found InputMethodExtension.js");
243 return;
244 }
245
246 NativeValue *value = jsObj_->Get();
247 NativeObject *obj = ConvertNativeValueTo<NativeObject>(value);
248 if (obj == nullptr) {
249 IMSA_HILOGE("Failed to get InputMethodExtension object");
250 return;
251 }
252
253 NativeValue *method = obj->GetProperty("onDisconnect");
254 if (method == nullptr) {
255 IMSA_HILOGE("Failed to get onDisconnect from InputMethodExtension object");
256 return;
257 }
258 nativeEngine->CallFunction(value, method, argv, ARGC_ONE);
259 IMSA_HILOGI("%{public}s end.", __func__);
260 }
261
OnCommand(const AAFwk::Want & want,bool restart,int startId)262 void JsInputMethodExtension::OnCommand(const AAFwk::Want &want, bool restart, int startId)
263 {
264 IMSA_HILOGI("JsInputMethodExtension OnCommand begin.");
265 Extension::OnCommand(want, restart, startId);
266 IMSA_HILOGI(
267 "%{public}s begin restart=%{public}s,startId=%{public}d.", __func__, restart ? "true" : "false", startId);
268 HandleScope handleScope(jsRuntime_);
269 NativeEngine *nativeEngine = &jsRuntime_.GetNativeEngine();
270 napi_value napiWant = OHOS::AppExecFwk::WrapWant(reinterpret_cast<napi_env>(nativeEngine), want);
271 NativeValue *nativeWant = reinterpret_cast<NativeValue *>(napiWant);
272 napi_value napiStartId = nullptr;
273 napi_create_int32(reinterpret_cast<napi_env>(nativeEngine), startId, &napiStartId);
274 NativeValue *nativeStartId = reinterpret_cast<NativeValue *>(napiStartId);
275 NativeValue *argv[] = { nativeWant, nativeStartId };
276 CallObjectMethod("onRequest", argv, ARGC_TWO);
277 IMSA_HILOGI("%{public}s end.", __func__);
278 }
279
CallObjectMethod(const char * name,NativeValue * const * argv,size_t argc)280 NativeValue *JsInputMethodExtension::CallObjectMethod(const char *name, NativeValue *const *argv, size_t argc)
281 {
282 IMSA_HILOGI("JsInputMethodExtension::CallObjectMethod(%{public}s), begin", name);
283
284 if (jsObj_ == nullptr) {
285 IMSA_HILOGW("Not found InputMethodExtension.js");
286 return nullptr;
287 }
288
289 HandleScope handleScope(jsRuntime_);
290 auto &nativeEngine = jsRuntime_.GetNativeEngine();
291
292 NativeValue *value = jsObj_->Get();
293 NativeObject *obj = ConvertNativeValueTo<NativeObject>(value);
294 if (obj == nullptr) {
295 IMSA_HILOGE("Failed to get InputMethodExtension object");
296 return nullptr;
297 }
298
299 NativeValue *method = obj->GetProperty(name);
300 if (method == nullptr) {
301 IMSA_HILOGE("Failed to get '%{public}s' from InputMethodExtension object", name);
302 return nullptr;
303 }
304 IMSA_HILOGI("JsInputMethodExtension::CallFunction(%{public}s), success", name);
305 return nativeEngine.CallFunction(value, method, argv, argc);
306 }
307
GetSrcPath(std::string & srcPath)308 void JsInputMethodExtension::GetSrcPath(std::string &srcPath)
309 {
310 IMSA_HILOGI("JsInputMethodExtension GetSrcPath begin.");
311 if (!Extension::abilityInfo_->isModuleJson) {
312 /* temporary compatibility api8 + config.json */
313 srcPath.append(Extension::abilityInfo_->package);
314 srcPath.append("/assets/js/");
315 if (!Extension::abilityInfo_->srcPath.empty()) {
316 srcPath.append(Extension::abilityInfo_->srcPath);
317 }
318 srcPath.append("/").append(Extension::abilityInfo_->name).append(".abc");
319 return;
320 }
321
322 if (!Extension::abilityInfo_->srcEntrance.empty()) {
323 srcPath.append(Extension::abilityInfo_->moduleName + "/");
324 srcPath.append(Extension::abilityInfo_->srcEntrance);
325 srcPath.erase(srcPath.rfind('.'));
326 srcPath.append(".abc");
327 }
328 }
329 } // namespace AbilityRuntime
330 } // namespace OHOS
331