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_print_extension.h"
17 
18 #include "ability_info.h"
19 #include "iprint_extension_callback.h"
20 #include "js_print_callback.h"
21 #include "js_print_extension_context.h"
22 #include "js_runtime.h"
23 #include "js_runtime_utils.h"
24 #include "napi/native_api.h"
25 #include "napi/native_node_api.h"
26 #include "napi_common_util.h"
27 #include "napi_common_want.h"
28 #include "napi_print_utils.h"
29 #include "napi_remote_object.h"
30 #include "print_log.h"
31 #include "print_manager_client.h"
32 #include "printer_capability.h"
33 #include "print_job_helper.h"
34 #include "print_utils.h"
35 
36 namespace OHOS {
37 namespace AbilityRuntime {
38 JsPrintExtension *JsPrintExtension::jsExtension_ = nullptr;
39 std::mutex JsPrintExtension::mtx;
40 using namespace OHOS::AppExecFwk;
41 using namespace OHOS::Print;
42 
Create(const std::unique_ptr<Runtime> & runtime)43 JsPrintExtension *JsPrintExtension::Create(const std::unique_ptr<Runtime> &runtime)
44 {
45     PRINT_HILOGD("jws JsPrintExtension begin Create");
46     jsExtension_ = new JsPrintExtension(static_cast<JsRuntime &>(*runtime));
47     return jsExtension_;
48 }
49 
JsPrintExtension(JsRuntime & jsRuntime)50 JsPrintExtension::JsPrintExtension(JsRuntime &jsRuntime) : jsRuntime_(jsRuntime),
51     extensionId_("") {}
~JsPrintExtension()52 JsPrintExtension::~JsPrintExtension()
53 {
54     std::lock_guard<std::mutex> lock(mtx);
55     if (jsExtension_ != nullptr) {
56         jsExtension_ = nullptr;
57     }
58     if (jsObj_) {
59         jsRuntime_.FreeNativeReference(std::move(jsObj_));
60     }
61     PRINT_HILOGI("JsPrintExtension destroyed");
62 }
63 
64 
Init(const std::shared_ptr<AbilityLocalRecord> & record,const std::shared_ptr<OHOSApplication> & application,std::shared_ptr<AbilityHandler> & handler,const sptr<IRemoteObject> & token)65 void JsPrintExtension::Init(const std::shared_ptr<AbilityLocalRecord> &record,
66     const std::shared_ptr<OHOSApplication> &application, std::shared_ptr<AbilityHandler> &handler,
67     const sptr<IRemoteObject> &token)
68 {
69     PRINT_HILOGD("jws JsPrintExtension begin Init");
70     PrintExtension::Init(record, application, handler, token);
71 
72     if (!InitExtensionObj(jsRuntime_)) {
73         PRINT_HILOGE("Failed to init extension object");
74         return;
75     }
76 
77     napi_value obj = jsObj_->GetNapiValue();
78     if (obj == nullptr) {
79         PRINT_HILOGE("Failed to get JsPrintExtension object");
80         return;
81     }
82 
83     if (!InitContextObj(jsRuntime_, obj, extensionId_)) {
84         PRINT_HILOGE("Failed to init extension context object");
85         return;
86     }
87     PRINT_HILOGD("JsPrintExtension::Init end.");
88 }
89 
InitExtensionObj(JsRuntime & jsRuntime)90 bool JsPrintExtension::InitExtensionObj(JsRuntime &jsRuntime)
91 {
92     std::string srcPath = "";
93     GetSrcPath(srcPath);
94     if (srcPath.empty()) {
95         PRINT_HILOGE("Failed to get srcPath");
96         return false;
97     }
98 
99     std::string moduleName(abilityInfo_->moduleName);
100     moduleName.append("::").append(abilityInfo_->name);
101     PRINT_HILOGD("Init module:%{public}s,srcPath:%{public}s.", moduleName.c_str(), srcPath.c_str());
102     HandleScope handleScope(jsRuntime_);
103 
104     jsObj_ = jsRuntime.LoadModule(moduleName, srcPath, abilityInfo_->hapPath,
105         abilityInfo_->compileMode == CompileMode::ES_MODULE);
106     if (jsObj_ == nullptr) {
107         PRINT_HILOGE("Failed to get jsObj_");
108         return false;
109     }
110     return true;
111 }
112 
InitContextObj(JsRuntime & jsRuntime,napi_value & extObj,std::string & extensionId)113 bool JsPrintExtension::InitContextObj(JsRuntime &jsRuntime, napi_value &extObj, std::string &extensionId)
114 {
115     auto context = GetContext();
116     if (context == nullptr) {
117         PRINT_HILOGE("Failed to get context");
118         return false;
119     }
120     PRINT_HILOGD("CreateJsPrintExtensionContext.");
121     napi_env engine = jsRuntime.GetNapiEnv();
122     napi_value contextObj = CreateJsPrintExtensionContext(engine, context, extensionId);
123     auto shellContextRef = jsRuntime.LoadSystemModule("PrintExtensionContext", &contextObj, NapiPrintUtils::ARGC_ONE);
124     if (shellContextRef == nullptr) {
125         PRINT_HILOGE("Failed to load print extension context ref");
126         return false;
127     }
128     contextObj = shellContextRef->GetNapiValue();
129     if (contextObj == nullptr) {
130         PRINT_HILOGE("Failed to get Print extension native object");
131         return false;
132     }
133     PRINT_HILOGD("JsPrintExtension::Init Bind.");
134     context->Bind(jsRuntime, shellContextRef.release());
135     PRINT_HILOGD("JsPrintExtension::napi_set_named_property.");
136     napi_set_named_property(engine, extObj, "context", contextObj);
137 
138     napi_wrap(engine, contextObj, new std::weak_ptr<AbilityRuntime::Context>(context),
139         [](napi_env, void *data, void *) {
140             PRINT_HILOGD("Finalizer for weak_ptr Print extension context is called");
141             delete static_cast<std::weak_ptr<AbilityRuntime::Context> *>(data);
142         },
143         nullptr, nullptr);
144     return true;
145 }
146 
OnStart(const AAFwk::Want & want)147 void JsPrintExtension::OnStart(const AAFwk::Want &want)
148 {
149     Extension::OnStart(want);
150     PRINT_HILOGD("jws JsPrintExtension OnStart begin..");
151     HandleScope handleScope(jsRuntime_);
152     napi_env nativeEngine = jsRuntime_.GetNapiEnv();
153     napi_value nativeWant = OHOS::AppExecFwk::WrapWant(nativeEngine, want);
154     napi_value argv[] = { nativeWant };
155     CallObjectMethod("onCreate", argv, NapiPrintUtils::ARGC_ONE);
156     RegisterCb();
157     PrintManagerClient::GetInstance()->LoadExtSuccess(extensionId_);
158     PRINT_HILOGD("%{public}s end.", __func__);
159 }
160 
RegisterCb()161 void JsPrintExtension::RegisterCb()
162 {
163     RegisterDiscoveryCb();
164     RegisterConnectionCb();
165     RegisterPrintJobCb();
166     RegisterPreviewCb();
167     RegisterQueryCapCb();
168     RegisterExtensionCb();
169 }
170 
OnStop()171 void JsPrintExtension::OnStop()
172 {
173     PrintExtension::OnStop();
174     PRINT_HILOGD("jws JsPrintExtension OnStop begin.");
175     auto context = GetContext();
176     if (context == nullptr) {
177         PRINT_HILOGE("Failed to get context");
178         return;
179     }
180     bool ret = ConnectionManager::GetInstance().DisconnectCaller(context->GetToken());
181     if (ret) {
182         PRINT_HILOGD("The Print extension connection is not disconnected.");
183     }
184     PRINT_HILOGD("%{public}s end.", __func__);
185 }
186 
OnConnect(const AAFwk::Want & want)187 sptr<IRemoteObject> JsPrintExtension::OnConnect(const AAFwk::Want &want)
188 {
189     PRINT_HILOGD("jws JsPrintExtension OnConnect begin.");
190     Extension::OnConnect(want);
191     PRINT_HILOGD("%{public}s begin.", __func__);
192     HandleScope handleScope(jsRuntime_);
193     napi_env nativeEngine = jsRuntime_.GetNapiEnv();
194     napi_value nativeWant = OHOS::AppExecFwk::WrapWant(nativeEngine, want);
195     napi_value argv[] = { nativeWant };
196     if (!jsObj_) {
197         PRINT_HILOGW("Not found PrintExtension.js");
198         return nullptr;
199     }
200 
201     napi_value obj = jsObj_->GetNapiValue();
202     if (obj == nullptr) {
203         PRINT_HILOGE("Failed to get PrintExtension object");
204         return nullptr;
205     }
206 
207     napi_value method = nullptr;
208     napi_get_named_property(nativeEngine, obj, "onConnect", &method);
209     if (method == nullptr) {
210         PRINT_HILOGE("Failed to get onConnect from PrintExtension object");
211         return nullptr;
212     }
213     PRINT_HILOGD("JsPrintExtension::napi_call_function onConnect, success");
214     napi_value remoteNative = nullptr;
215     napi_call_function(nativeEngine, obj, method, NapiPrintUtils::ARGC_ONE, argv, &remoteNative);
216     if (remoteNative == nullptr) {
217         PRINT_HILOGE("remoteNative nullptr.");
218     }
219     auto remoteObj = NAPI_ohos_rpc_getNativeRemoteObject(nativeEngine, remoteNative);
220     if (remoteObj == nullptr) {
221         PRINT_HILOGE("remoteObj nullptr.");
222     }
223     return remoteObj;
224 }
225 
OnDisconnect(const AAFwk::Want & want)226 void JsPrintExtension::OnDisconnect(const AAFwk::Want &want)
227 {
228     PRINT_HILOGD("jws JsPrintExtension OnDisconnect begin.");
229     Extension::OnDisconnect(want);
230     PRINT_HILOGD("%{public}s begin.", __func__);
231     HandleScope handleScope(jsRuntime_);
232     napi_env nativeEngine = jsRuntime_.GetNapiEnv();
233     napi_value nativeWant = OHOS::AppExecFwk::WrapWant(nativeEngine, want);
234     napi_value argv[] = { nativeWant };
235     if (!jsObj_) {
236         PRINT_HILOGW("Not found PrintExtension.js");
237         return;
238     }
239 
240     napi_value obj = jsObj_->GetNapiValue();
241     if (obj == nullptr) {
242         PRINT_HILOGE("Failed to get PrintExtension object");
243         return;
244     }
245 
246     napi_value method = nullptr;
247     napi_get_named_property(nativeEngine, obj, "onDisconnect", &method);
248     if (method == nullptr) {
249         PRINT_HILOGE("Failed to get onDisconnect from PrintExtension object");
250         return;
251     }
252     napi_value callResult = nullptr;
253     napi_call_function(nativeEngine, obj, method, NapiPrintUtils::ARGC_ONE, argv, &callResult);
254     PRINT_HILOGD("%{public}s end.", __func__);
255 }
256 
OnCommand(const AAFwk::Want & want,bool restart,int startId)257 void JsPrintExtension::OnCommand(const AAFwk::Want &want, bool restart, int startId)
258 {
259     PRINT_HILOGD("jws JsPrintExtension OnCommand begin.");
260     Extension::OnCommand(want, restart, startId);
261     PRINT_HILOGD("begin restart=%{public}s,startId=%{public}d.", restart ? "true" : "false", startId);
262     if (startId <= 1) {
263         PRINT_HILOGD("%{public}s ignore.", __func__);
264         return;
265     }
266     HandleScope handleScope(jsRuntime_);
267     napi_env nativeEngine = jsRuntime_.GetNapiEnv();
268     napi_value nativeWant = OHOS::AppExecFwk::WrapWant(nativeEngine, want);
269     napi_value argv[] = { nativeWant };
270     CallObjectMethod("onCreate", argv, NapiPrintUtils::ARGC_ONE);
271     RegisterCb();
272     PrintManagerClient::GetInstance()->LoadExtSuccess(extensionId_);
273     PRINT_HILOGD("%{public}s end.", __func__);
274 }
275 
CallObjectMethod(const char * name,napi_value const * argv,size_t argc)276 napi_value JsPrintExtension::CallObjectMethod(const char *name, napi_value const *argv, size_t argc)
277 {
278     PRINT_HILOGD("jws JsPrintExtension::CallObjectMethod(%{public}s), begin", name);
279 
280     if (!jsObj_) {
281         PRINT_HILOGW("Not found PrintExtension.js");
282         return nullptr;
283     }
284 
285     HandleScope handleScope(jsRuntime_);
286     napi_env nativeEngine = jsRuntime_.GetNapiEnv();
287 
288     napi_value obj = jsObj_->GetNapiValue();
289     if (obj == nullptr) {
290         PRINT_HILOGE("Failed to get PrintExtension object");
291         return nullptr;
292     }
293 
294     napi_value method = nullptr;
295     napi_get_named_property(nativeEngine, obj, name, &method);
296     if (method == nullptr) {
297         PRINT_HILOGE("Failed to get '%{public}s' from PrintExtension object", name);
298         return nullptr;
299     }
300     PRINT_HILOGD("JsPrintExtension::napi_call_function(%{public}s), success", name);
301     napi_value callResult = nullptr;
302     napi_call_function(nativeEngine, obj, method, argc, argv, &callResult);
303     return callResult;
304 }
305 
GetSrcPath(std::string & srcPath)306 void JsPrintExtension::GetSrcPath(std::string &srcPath)
307 {
308     PRINT_HILOGD("jws JsPrintExtension GetSrcPath begin.");
309     if (!Extension::abilityInfo_->isModuleJson) {
310         /* temporary compatibility api8 + config.json */
311         srcPath.append(Extension::abilityInfo_->package);
312         srcPath.append("/assets/js/");
313         if (!Extension::abilityInfo_->srcPath.empty()) {
314             srcPath.append(Extension::abilityInfo_->srcPath);
315         }
316         srcPath.append("/").append(Extension::abilityInfo_->name).append(".abc");
317         return;
318     }
319 
320     if (!Extension::abilityInfo_->srcEntrance.empty()) {
321         srcPath.append(Extension::abilityInfo_->moduleName + "/");
322         srcPath.append(Extension::abilityInfo_->srcEntrance);
323         srcPath.erase(srcPath.rfind('.'));
324         srcPath.append(".abc");
325     }
326 }
327 
Callback(std::string funcName)328 bool JsPrintExtension::Callback(std::string funcName)
329 {
330     PRINT_HILOGD("call %{public}s", funcName.c_str());
331     std::lock_guard<std::mutex> lock(mtx);
332     if (JsPrintExtension::jsExtension_ == nullptr) {
333         return false;
334     }
335     napi_env env = (JsPrintExtension::jsExtension_->jsRuntime_).GetNapiEnv();
336     WorkParam *workParam = new (std::nothrow) WorkParam(env, funcName);
337     if (workParam == nullptr) {
338         return false;
339     }
340     uv_after_work_cb afterCallback = [](uv_work_t *work, int32_t status) {
341         WorkParam *param = reinterpret_cast<WorkParam *>(work->data);
342         if (param == nullptr) {
343             delete work;
344             return;
345         }
346         napi_handle_scope scope = nullptr;
347         napi_open_handle_scope(param->env, &scope);
348         if (scope == nullptr) {
349             delete param;
350             delete work;
351             return;
352         }
353         napi_value arg[] = { 0 };
354         std::lock_guard<std::mutex> lock(mtx);
355         if (JsPrintExtension::jsExtension_ != nullptr) {
356             JsPrintExtension::jsExtension_->CallObjectMethod(param->funcName.c_str(), arg, NapiPrintUtils::ARGC_ZERO);
357         }
358         napi_close_handle_scope(param->env, scope);
359         delete param;
360         delete work;
361     };
362     bool ret = JsPrintCallback::Call(env, workParam, afterCallback);
363     if (!ret) {
364         delete workParam;
365         workParam = nullptr;
366         PRINT_HILOGE("Callback fail, delete param");
367         return false;
368     }
369     return true;
370 }
371 
Callback(const std::string funcName,const std::string & printerId)372 bool JsPrintExtension::Callback(const std::string funcName, const std::string &printerId)
373 {
374     PRINT_HILOGD("call %{public}s", funcName.c_str());
375     std::lock_guard<std::mutex> lock(mtx);
376     if (JsPrintExtension::jsExtension_ == nullptr) {
377         return false;
378     }
379     napi_env env = (JsPrintExtension::jsExtension_->jsRuntime_).GetNapiEnv();
380     WorkParam *workParam = new (std::nothrow) WorkParam(env, funcName);
381     if (workParam == nullptr) {
382         return false;
383     }
384     workParam->printerId = printerId;
385     uv_after_work_cb afterCallback = [](uv_work_t *work, int32_t status) {
386         WorkParam *param = reinterpret_cast<WorkParam *>(work->data);
387         if (param == nullptr) {
388             delete work;
389             return;
390         }
391         napi_handle_scope scope = nullptr;
392         napi_open_handle_scope(param->env, &scope);
393         if (scope == nullptr) {
394             delete param;
395             delete work;
396             return;
397         }
398         napi_value id = OHOS::AppExecFwk::WrapStringToJS(param->env, param->printerId);
399         napi_value arg[] = { id };
400         std::lock_guard<std::mutex> lock(mtx);
401         if (JsPrintExtension::jsExtension_ != nullptr) {
402             JsPrintExtension::jsExtension_->CallObjectMethod(param->funcName.c_str(), arg, NapiPrintUtils::ARGC_ONE);
403         }
404         napi_close_handle_scope(param->env, scope);
405         delete param;
406         delete work;
407     };
408     bool ret = JsPrintCallback::Call(env, workParam, afterCallback);
409     if (!ret) {
410         delete workParam;
411         workParam = nullptr;
412         PRINT_HILOGE("Callback fail, delete param");
413         return false;
414     }
415     return true;
416 }
417 
Callback(const std::string funcName,const Print::PrintJob & job)418 bool JsPrintExtension::Callback(const std::string funcName, const Print::PrintJob &job)
419 {
420     PRINT_HILOGD("call %{public}s", funcName.c_str());
421     std::lock_guard<std::mutex> lock(mtx);
422     if (JsPrintExtension::jsExtension_ == nullptr) {
423         return false;
424     }
425     napi_env env = (JsPrintExtension::jsExtension_->jsRuntime_).GetNapiEnv();
426     WorkParam *workParam = new (std::nothrow) WorkParam(env, funcName);
427     if (workParam == nullptr) {
428         return false;
429     }
430     workParam->job = job;
431     uv_after_work_cb afterCallback = [](uv_work_t *work, int32_t status) {
432         WorkParam *param = reinterpret_cast<WorkParam *>(work->data);
433         if (param == nullptr) {
434             delete work;
435             return;
436         }
437         napi_handle_scope scope = nullptr;
438         napi_open_handle_scope(param->env, &scope);
439         if (scope == nullptr) {
440             delete param;
441             delete work;
442             return;
443         }
444         napi_value jobObject = PrintJobHelper::MakeJsObject(param->env, param->job);
445         napi_value arg[] = { jobObject };
446         std::lock_guard<std::mutex> lock(mtx);
447         if (JsPrintExtension::jsExtension_ != nullptr) {
448             JsPrintExtension::jsExtension_->CallObjectMethod(param->funcName.c_str(), arg, NapiPrintUtils::ARGC_ONE);
449         }
450         napi_close_handle_scope(param->env, scope);
451         delete param;
452         delete work;
453     };
454     bool ret = JsPrintCallback::Call(env, workParam, afterCallback);
455     if (!ret) {
456         delete workParam;
457         workParam = nullptr;
458         PRINT_HILOGE("Callback fail, delete param");
459         return false;
460     }
461     return true;
462 }
463 
RegisterDiscoveryCb()464 void JsPrintExtension::RegisterDiscoveryCb()
465 {
466     PRINT_HILOGD("Register Print Extension Callback");
467     PrintManagerClient::GetInstance()->RegisterExtCallback(extensionId_, PRINT_EXTCB_START_DISCOVERY,
468         []() -> bool {
469             if (JsPrintExtension::jsExtension_ == nullptr) {
470                 return false;
471             }
472             return JsPrintExtension::jsExtension_->Callback("onStartDiscoverPrinter");
473     });
474     PrintManagerClient::GetInstance()->RegisterExtCallback(extensionId_, PRINT_EXTCB_STOP_DISCOVERY,
475         []() -> bool {
476             if (JsPrintExtension::jsExtension_ == nullptr) {
477                 return false;
478             }
479             return JsPrintExtension::jsExtension_->Callback("onStopDiscoverPrinter");
480     });
481 }
482 
RegisterConnectionCb()483 void JsPrintExtension::RegisterConnectionCb()
484 {
485     PrintManagerClient::GetInstance()->RegisterExtCallback(extensionId_, PRINT_EXTCB_CONNECT_PRINTER,
486         [](const std::string &printId) -> bool {
487             if (JsPrintExtension::jsExtension_ == nullptr) {
488                 return false;
489             }
490             std::string realPrinterId = PrintUtils::GetLocalId(printId, jsExtension_->extensionId_);
491             return JsPrintExtension::jsExtension_->Callback("onConnectPrinter", realPrinterId);
492     });
493     PrintManagerClient::GetInstance()->RegisterExtCallback(extensionId_, PRINT_EXTCB_DISCONNECT_PRINTER,
494         [](const std::string &printId) -> bool {
495             if (JsPrintExtension::jsExtension_ == nullptr) {
496                 return false;
497             }
498             std::string realPrinterId = PrintUtils::GetLocalId(printId, jsExtension_->extensionId_);
499             return JsPrintExtension::jsExtension_->Callback("onDisconnectPrinter", realPrinterId);
500     });
501 }
502 
RegisterPrintJobCb()503 void JsPrintExtension::RegisterPrintJobCb()
504 {
505     PrintManagerClient::GetInstance()->RegisterExtCallback(extensionId_, PRINT_EXTCB_START_PRINT,
506         [](const PrintJob &job) -> bool {
507             if (JsPrintExtension::jsExtension_ == nullptr) {
508                 return false;
509             }
510             return JsPrintExtension::jsExtension_->Callback("onStartPrintJob", job);
511     });
512     PrintManagerClient::GetInstance()->RegisterExtCallback(extensionId_, PRINT_EXTCB_CANCEL_PRINT,
513         [](const PrintJob &job) -> bool {
514             if (JsPrintExtension::jsExtension_ == nullptr) {
515                 return false;
516             }
517             return JsPrintExtension::jsExtension_->Callback("onCancelPrintJob", job);
518     });
519 }
520 
RegisterPreviewCb()521 void JsPrintExtension::RegisterPreviewCb()
522 {
523     PrintManagerClient::GetInstance()->RegisterExtCallback(extensionId_, PRINT_EXTCB_REQUEST_PREVIEW,
524         [](const PrintJob &job) -> bool {
525             if (JsPrintExtension::jsExtension_ == nullptr) {
526                 return false;
527             }
528             return JsPrintExtension::jsExtension_->Callback("onRequestPreview", job);
529     });
530 }
531 
RegisterQueryCapCb()532 void JsPrintExtension::RegisterQueryCapCb()
533 {
534     PrintManagerClient::GetInstance()->RegisterExtCallback(extensionId_, PRINT_EXTCB_REQUEST_CAP,
535         [](const std::string &printId) -> bool {
536             if (JsPrintExtension::jsExtension_ == nullptr) {
537                 return false;
538             }
539             std::string realPrinterId = PrintUtils::GetLocalId(printId, jsExtension_->extensionId_);
540             return JsPrintExtension::jsExtension_->Callback("onRequestPrinterCapability", realPrinterId);
541     });
542 }
543 
RegisterExtensionCb()544 void JsPrintExtension::RegisterExtensionCb()
545 {
546     PrintManagerClient::GetInstance()->RegisterExtCallback(extensionId_, PRINT_EXTCB_DESTROY_EXTENSION,
547         []() -> bool {
548             if (JsPrintExtension::jsExtension_ == nullptr) {
549                 return false;
550             }
551             JsPrintExtension::jsExtension_->OnStop();
552             return true;
553     });
554 }
555 } // namespace AbilityRuntime
556 } // namespace OHOS
557