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