1 /*
2  * Copyright (c) 2024 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 <fstream>
17 #include <iostream>
18 #include <securec.h>
19 
20 #include "business_error.h"
21 #include "napi_parse_utils.h"
22 #include "nweb_log.h"
23 #include "web_errors.h"
24 #include "webview_createpdf_execute_callback.h"
25 namespace OHOS::NWeb {
26 using namespace NWebError;
27 const std::string JS_EXT_ARR_CLASS_NAME = "PdfData";
28 thread_local napi_ref g_jsArrExtClassRef;
29 // static
InitJSExcute(napi_env env,napi_value exports)30 void WebviewCreatePDFExecuteCallback::InitJSExcute(napi_env env, napi_value exports)
31 {
32     napi_value jsArrExtClass = nullptr;
33     napi_property_descriptor jsArrExtClsProperties[] = { DECLARE_NAPI_FUNCTION(
34         "pdfArrayBuffer", NapiArrayBufferExt::GetArrayBuffer) };
35     napi_define_class(env, JS_EXT_ARR_CLASS_NAME.c_str(), JS_EXT_ARR_CLASS_NAME.length(),
36         NapiArrayBufferExt::JsConstructor, nullptr, sizeof(jsArrExtClsProperties) / sizeof(jsArrExtClsProperties[0]),
37         jsArrExtClsProperties, &jsArrExtClass);
38     napi_create_reference(env, jsArrExtClass, 1, &g_jsArrExtClassRef);
39     napi_set_named_property(env, exports, JS_EXT_ARR_CLASS_NAME.c_str(), jsArrExtClass);
40 }
41 
ReleaseArrayBufferExecuteParamAndUvWork(ArrayBufferExecuteParam * param,uv_work_t * work)42 void WebviewCreatePDFExecuteCallback::ReleaseArrayBufferExecuteParamAndUvWork(
43     ArrayBufferExecuteParam* param, uv_work_t* work)
44 {
45     if (param != nullptr) {
46         if (param->result_ != nullptr) {
47             delete[] param->result_;
48             param->result_ = nullptr;
49         }
50         delete param;
51         param = nullptr;
52     }
53     if (work != nullptr) {
54         delete work;
55         work = nullptr;
56     }
57 }
58 
OnReceiveValue(const char * value,const long size)59 void WebviewCreatePDFExecuteCallback::OnReceiveValue(const char* value, const long size)
60 {
61     if (value == nullptr || size <= 0) {
62         WVLOG_E("[CreatePDF] value is null or size is invalid");
63         return;
64     }
65     uv_loop_s* loop = nullptr;
66     uv_work_t* work = nullptr;
67 
68     napi_get_uv_event_loop(env_, &loop);
69     if (loop == nullptr) {
70         return;
71     }
72     work = new (std::nothrow) uv_work_t;
73     if (work == nullptr) {
74         return;
75     }
76 
77     ArrayBufferExecuteParam* param = new (std::nothrow) ArrayBufferExecuteParam();
78     if (param == nullptr) {
79         delete work;
80         return;
81     }
82     param->env_ = env_;
83     param->callbackRef_ = callbackRef_;
84     param->deferred_ = deferred_;
85     param->result_ = new (std::nothrow) char[size + 1];
86     if (param->result_ == nullptr) {
87         WVLOG_E("new char failed");
88         ReleaseArrayBufferExecuteParamAndUvWork(param, work);
89         return;
90     }
91     if (memcpy_s(param->result_, size, value, size) != 0) {
92         WVLOG_E("[CreatePDF] memcpy failed");
93         ReleaseArrayBufferExecuteParamAndUvWork(param, work);
94         return;
95     }
96     param->size_ = size;
97 
98     work->data = reinterpret_cast<void*>(param);
99 
100     int ret = uv_queue_work_with_qos(
101         loop, work, [](uv_work_t* work) {}, UvAfterWorkCb, uv_qos_user_initiated);
102     if (ret != 0) {
103         WVLOG_E("[CreatePDF] queue work failed");
104         ReleaseArrayBufferExecuteParamAndUvWork(param, work);
105     }
106 }
107 
UvAfterWorkCb(uv_work_t * work,int status)108 void WebviewCreatePDFExecuteCallback::UvAfterWorkCb(uv_work_t* work, int status)
109 {
110     (void)status;
111     if (!work) {
112         return;
113     }
114     ArrayBufferExecuteParam* param = reinterpret_cast<ArrayBufferExecuteParam*>(work->data);
115     if (!param) {
116         WVLOG_E("[CreatePDF] param is null");
117         ReleaseArrayBufferExecuteParamAndUvWork(param, work);
118         return;
119     }
120     napi_handle_scope scope = nullptr;
121     napi_open_handle_scope(param->env_, &scope);
122     if (scope == nullptr) {
123         WVLOG_E("[CreatePDF] scope is null");
124         ReleaseArrayBufferExecuteParamAndUvWork(param, work);
125         return;
126     }
127     if (param->callbackRef_) {
128         UvAfterWorkCbAsync(param->env_, param->callbackRef_, param->result_, param->size_);
129     } else if (param->deferred_) {
130         UvAfterWorkCbPromise(param->env_, param->deferred_, param->result_, param->size_);
131     }
132 
133     napi_close_handle_scope(param->env_, scope);
134     ReleaseArrayBufferExecuteParamAndUvWork(param, work);
135 }
136 
UvAfterWorkCbAsync(napi_env env,napi_ref callbackRef,const char * result,const long size)137 void WebviewCreatePDFExecuteCallback::UvAfterWorkCbAsync(
138     napi_env env, napi_ref callbackRef, const char* result, const long size)
139 {
140     napi_value setResult[INTEGER_TWO] = { 0 };
141 
142     if (result == nullptr) {
143         setResult[INTEGER_ZERO] = BusinessError::CreateError(env, NWebError::INVALID_RESOURCE);
144         napi_get_null(env, &setResult[INTEGER_ONE]);
145     } else {
146         napi_get_undefined(env, &setResult[INTEGER_ZERO]);
147         napi_value jsArrExt = nullptr;
148         NAPI_CALL_RETURN_VOID(env, napi_get_reference_value(env, g_jsArrExtClassRef, &jsArrExt));
149         NAPI_CALL_RETURN_VOID(env, napi_new_instance(env, jsArrExt, 0, NULL, &setResult[INTEGER_ONE]));
150 
151         WebJsArrayBufferExt* webArrayBufferExt = new (std::nothrow) WebJsArrayBufferExt(result, size);
152         if (webArrayBufferExt == nullptr) {
153             WVLOG_E("new WebJsArrayBufferExt failed.");
154             return;
155         }
156 
157         napi_status status = napi_wrap(
158             env, setResult[INTEGER_ONE], webArrayBufferExt,
159             [](napi_env env, void* data, void* hint) {
160                 WebJsArrayBufferExt* webArrayBufferExt = static_cast<WebJsArrayBufferExt*>(data);
161                 delete webArrayBufferExt;
162                 webArrayBufferExt = nullptr;
163             },
164             nullptr, nullptr);
165         if (status != napi_status::napi_ok) {
166             if (webArrayBufferExt) {
167                 delete webArrayBufferExt;
168                 webArrayBufferExt = nullptr;
169             }
170             WVLOG_E("napi_wrap failed");
171             return;
172         }
173     }
174     napi_value args[INTEGER_TWO] = { setResult[INTEGER_ZERO], setResult[INTEGER_ONE] };
175     napi_value callback = nullptr;
176     napi_value callbackResult = nullptr;
177 
178     napi_get_reference_value(env, callbackRef, &callback);
179     napi_call_function(env, nullptr, callback, INTEGER_TWO, args, &callbackResult);
180     napi_delete_reference(env, callbackRef);
181 }
182 
UvAfterWorkCbPromise(napi_env env,napi_deferred deferred,const char * result,const long size)183 void WebviewCreatePDFExecuteCallback::UvAfterWorkCbPromise(
184     napi_env env, napi_deferred deferred, const char* result, const long size)
185 {
186     napi_value setResult[INTEGER_TWO] = { 0 };
187     setResult[INTEGER_ZERO] = NWebError::BusinessError::CreateError(env, NWebError::INVALID_RESOURCE);
188 
189     napi_value jsArrExt = nullptr;
190     napi_status status = napi_get_reference_value(env, g_jsArrExtClassRef, &jsArrExt);
191     if (status != napi_status::napi_ok) {
192         WVLOG_E("napi_get_reference_value failed.");
193         return;
194     }
195     status = napi_new_instance(env, jsArrExt, 0, NULL, &setResult[INTEGER_ONE]);
196     if (status != napi_status::napi_ok) {
197         WVLOG_E("napi_new_instance failed.");
198         return;
199     }
200     WebJsArrayBufferExt* webArrayBufferExt = new (std::nothrow) WebJsArrayBufferExt(result, size);
201     if (webArrayBufferExt == nullptr) {
202         WVLOG_E("new WebJsArrayBufferExt failed.");
203         return;
204     }
205 
206     status = napi_wrap(
207         env, setResult[INTEGER_ONE], webArrayBufferExt,
208         [](napi_env env, void* data, void* hint) {
209             WebJsArrayBufferExt* webArrayBufferExt = static_cast<WebJsArrayBufferExt*>(data);
210             delete webArrayBufferExt;
211             webArrayBufferExt = nullptr;
212         },
213         nullptr, nullptr);
214     if (status != napi_status::napi_ok) {
215         if (webArrayBufferExt) {
216             delete webArrayBufferExt;
217             webArrayBufferExt = nullptr;
218         }
219         WVLOG_E("napi_wrap failed.");
220         return;
221     }
222 
223     napi_value args[INTEGER_TWO] = { setResult[INTEGER_ZERO], setResult[INTEGER_ONE] };
224     if (result == nullptr) {
225         napi_reject_deferred(env, deferred, args[INTEGER_ZERO]);
226     } else {
227         napi_resolve_deferred(env, deferred, args[INTEGER_ONE]);
228     }
229 }
230 
JsConstructor(napi_env env,napi_callback_info info)231 napi_value NapiArrayBufferExt::JsConstructor(napi_env env, napi_callback_info info)
232 {
233     napi_value thisVar = nullptr;
234     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
235     return thisVar;
236 }
237 
GetArrayBuffer(napi_env env,napi_callback_info info)238 napi_value NapiArrayBufferExt::GetArrayBuffer(napi_env env, napi_callback_info info)
239 {
240     napi_value thisVar = nullptr;
241     napi_value result = nullptr;
242     size_t argc = INTEGER_ONE;
243     napi_value argv[INTEGER_ONE] = { 0 };
244 
245     WebJsArrayBufferExt* webArrayBufferExt = nullptr;
246     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
247     NAPI_CALL(env, napi_unwrap(env, thisVar, (void**)&webArrayBufferExt));
248     if (webArrayBufferExt == nullptr) {
249         WVLOG_E("unwrap webArrayBufferExt failed.");
250         return result;
251     }
252 
253     const char* pdfResult = webArrayBufferExt->GetPDFResult();
254     const long size = webArrayBufferExt->GetPDFSize();
255     if (pdfResult == nullptr || size <= 0) {
256         WVLOG_E("[CreatePDF] invalid PDF result or size");
257         return nullptr;
258     }
259     napi_value arraybuffer = nullptr;
260     void* bufferData = nullptr;
261 
262     napi_status status = napi_create_arraybuffer(env, size, &bufferData, &arraybuffer);
263     if (status != napi_ok) {
264         WVLOG_E("[CreatePDF] create array buffer failed, status: %{public}d", status);
265         return nullptr;
266     }
267     if (bufferData == nullptr) {
268         WVLOG_E("[CreatePDF] bufferData is null after array buffer creation");
269         return nullptr;
270     }
271     if (memcpy_s(bufferData, size, pdfResult, size) != 0) {
272         WVLOG_E("[CreatePDF] memcpy failed");
273         return nullptr;
274     }
275     status = napi_create_typedarray(env, napi_typedarray_type::napi_uint8_array, size, arraybuffer, 0, &result);
276     if (status != napi_ok) {
277         WVLOG_E("[CreatePDF] create typed array failed, status: %{public}d", status);
278         return nullptr;
279     }
280     return result;
281 }
282 
283 } // namespace OHOS::NWeb
284