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 "nweb_helper.h"
17
18 #include <cstdint>
19 #include <dirent.h>
20 #include <dlfcn.h>
21 #include <memory>
22 #include <refbase.h>
23 #include <surface.h>
24 #include <sys/stat.h>
25 #include <thread>
26 #include <unistd.h>
27 #include <fcntl.h>
28
29 #include "application_context.h"
30 #include "config_policy_utils.h"
31 #include "nweb_adapter_helper.h"
32 #include "nweb_enhance_surface_adapter.h"
33 #include "nweb_log.h"
34 #include "nweb_surface_adapter.h"
35
36 namespace {
37 const uint32_t NWEB_SURFACE_MAX_WIDTH = 7680;
38 const uint32_t NWEB_SURFACE_MAX_HEIGHT = 7680;
39 #if defined(webview_arm64)
40 const std::string RELATIVE_PATH_FOR_MOCK = "libs/arm64";
41 const std::string RELATIVE_PATH_FOR_BUNDLE = "nweb/libs/arm64";
42 #elif defined(webview_x86_64)
43 const std::string RELATIVE_PATH_FOR_MOCK = "libs/x86_64";
44 const std::string RELATIVE_PATH_FOR_BUNDLE = "nweb/libs/x86_64";
45 #else
46 const std::string RELATIVE_PATH_FOR_MOCK = "libs/arm";
47 const std::string RELATIVE_PATH_FOR_BUNDLE = "nweb/libs/arm";
48 #endif
49 const std::string LIB_NAME_WEB_ENGINE = "libweb_engine.so";
50 static bool g_isFirstTimeStartUp = false;
51 const std::string WEB_CONFIG_PATH = "etc/web/web_config.xml";
52 }
53
54 namespace OHOS::NWeb {
Instance()55 NWebHelper &NWebHelper::Instance()
56 {
57 static NWebHelper helper;
58 return helper;
59 }
60
61 #ifdef __MUSL__
LoadLib(bool from_ark)62 bool NWebHelper::LoadLib(bool from_ark)
63 {
64 if (libHandleWebEngine_ != nullptr) {
65 return true;
66 }
67 if (bundlePath_.empty()) {
68 return false;
69 }
70 std::string loadLibPath;
71 if (from_ark) {
72 loadLibPath = bundlePath_ + "/" + RELATIVE_PATH_FOR_BUNDLE;
73 } else {
74 loadLibPath = bundlePath_ + "/" + RELATIVE_PATH_FOR_MOCK;
75 }
76 Dl_namespace dlns;
77 dlns_init(&dlns, "nweb_ns");
78 dlns_create(&dlns, loadLibPath.c_str());
79 void *libHandleWebEngine = dlopen_ns(&dlns, LIB_NAME_WEB_ENGINE.c_str(), RTLD_NOW);
80 if (libHandleWebEngine == nullptr) {
81 WVLOG_E("fail to dlopen %{public}s, errmsg=%{public}s", LIB_NAME_WEB_ENGINE.c_str(), dlerror());
82 return false;
83 }
84 libHandleWebEngine_ = libHandleWebEngine;
85 return true;
86 }
87 #else
LoadLib(bool from_ark)88 bool NWebHelper::LoadLib(bool from_ark)
89 {
90 if (libHandleWebEngine_ != nullptr) {
91 return true;
92 }
93 if (bundlePath_.empty()) {
94 return false;
95 }
96 std::string loadLibPath;
97 if (from_ark) {
98 loadLibPath = bundlePath_ + "/" + RELATIVE_PATH_FOR_BUNDLE;
99 } else {
100 loadLibPath = bundlePath_ + "/" + RELATIVE_PATH_FOR_MOCK;
101 }
102 const std::string libPathWebEngine = loadLibPath + "/" + LIB_NAME_WEB_ENGINE;
103 void *libHandleWebEngine = ::dlopen(libPathWebEngine.c_str(), RTLD_NOW);
104 if (libHandleWebEngine == nullptr) {
105 WVLOG_E("fail to dlopen %{public}s, errmsg=%{public}s", libPathWebEngine.c_str(), dlerror());
106 return false;
107 }
108 libHandleWebEngine_ = libHandleWebEngine;
109 return true;
110 }
111 #endif
112
UnloadLib()113 void NWebHelper::UnloadLib()
114 {
115 if (libHandleWebEngine_ != nullptr) {
116 ::dlclose(libHandleWebEngine_);
117 libHandleWebEngine_ = nullptr;
118 }
119 }
120
DoPreReadLib(const std::string & bundlePath)121 static void DoPreReadLib(const std::string &bundlePath)
122 {
123 WVLOG_I("NWebHelper PreReadLib");
124 std::string libPathWebEngine = bundlePath + "/" + RELATIVE_PATH_FOR_BUNDLE + "/" + LIB_NAME_WEB_ENGINE;
125
126 char tempPath[PATH_MAX] = {0};
127 if (realpath(libPathWebEngine.c_str(), tempPath) == nullptr) {
128 WVLOG_E("path to realpath error");
129 return;
130 }
131
132 struct stat stats;
133 int ret = stat(tempPath, &stats);
134 if (ret < 0) {
135 WVLOG_E("stat web engine library failed, ret = %{public}d", ret);
136 return;
137 }
138
139 static const int SINGLE_READ_SIZE = 5 * 1024 * 1024;
140 char *buf = new (std::nothrow) char[SINGLE_READ_SIZE];
141 if (buf == nullptr) {
142 WVLOG_E("malloc buf failed");
143 return;
144 }
145
146 int fd = open(tempPath, O_RDONLY);
147 if (fd <= 0) {
148 WVLOG_E("open web engine library failed");
149 delete [] buf;
150 return;
151 }
152
153 int readCnt = stats.st_size / SINGLE_READ_SIZE;
154 if (readCnt * SINGLE_READ_SIZE < stats.st_size) {
155 readCnt += 1;
156 }
157
158 for (int i = 0; i < readCnt; i++) {
159 (void)read(fd, buf, SINGLE_READ_SIZE);
160 }
161
162 (void)close(fd);
163 delete [] buf;
164 WVLOG_I("NWebHelper PreReadLib Finish");
165 }
166
TryPreReadLib(bool isFirstTimeStartUpWeb,const std::string & bundlePath)167 void NWebHelper::TryPreReadLib(bool isFirstTimeStartUpWeb, const std::string &bundlePath)
168 {
169 g_isFirstTimeStartUp = isFirstTimeStartUpWeb;
170 if (isFirstTimeStartUpWeb) {
171 WVLOG_I("first time startup, need to wait until the nweb init stage");
172 return;
173 }
174
175 DoPreReadLib(bundlePath);
176 }
177
TryPreReadLibForFirstlyAppStartUp(const std::string & bundlePath)178 static void TryPreReadLibForFirstlyAppStartUp(const std::string &bundlePath)
179 {
180 if (g_isFirstTimeStartUp) {
181 std::thread preReadThread([bundlePath]() {
182 DoPreReadLib(bundlePath);
183 });
184
185 preReadThread.detach();
186 }
187 }
188
Init(bool from_ark)189 bool NWebHelper::Init(bool from_ark)
190 {
191 TryPreReadLibForFirstlyAppStartUp(bundlePath_);
192 return LoadLib(from_ark);
193 }
194
195 using InitializeWebEngine = void (*)(const NWebInitArgs &);
InitAndRun(bool from_ark)196 bool NWebHelper::InitAndRun(bool from_ark) {
197 if (!Init(from_ark)) {
198 return false;
199 }
200
201 WVLOG_I("InitializeWebEngine: load libs and initiallize cef.");
202 if (libHandleWebEngine_ == nullptr) {
203 WVLOG_E("InitializeWebEngine: libHandleWebEngine_ is nullptr");
204 return false;
205 }
206
207 const std::string INITIALIZE_WEB_ENGINE_FUNC_NAME = "InitializeWebEngine";
208 InitializeWebEngine initializeWebEngine =
209 reinterpret_cast<InitializeWebEngine>(dlsym(libHandleWebEngine_,
210 INITIALIZE_WEB_ENGINE_FUNC_NAME.c_str()));
211 if (initializeWebEngine == nullptr) {
212 WVLOG_E("initializeWebEngine: fail to dlsym %{public}s from libohoswebview.so",
213 INITIALIZE_WEB_ENGINE_FUNC_NAME.c_str());
214 return false;
215 }
216
217 OHOS::NWeb::NWebInitArgs initArgs;
218 NWebAdapterHelper::Instance().ParseConfig(initArgs);
219 // obtain bundle path
220 std::shared_ptr<AbilityRuntime::ApplicationContext> ctx =
221 AbilityRuntime::ApplicationContext::GetApplicationContext();
222 if (!ctx) {
223 WVLOG_E("Failed to init cef due to nil application context.");
224 return false;
225 }
226
227 if (ctx->GetBaseDir().empty()) {
228 WVLOG_E("Failed to init cef due to base dir is empty.");
229 return false;
230 }
231
232 initArgs.web_engine_args_to_add.push_back(
233 std::string("--user-data-dir=").append(ctx->GetBaseDir()));
234 initArgs.web_engine_args_to_add.push_back(
235 std::string("--bundle-installation-dir=").append(bundlePath_));
236
237 initializeWebEngine(initArgs);
238 return true;
239 }
240
SetBundlePath(const std::string & path)241 void NWebHelper::SetBundlePath(const std::string &path)
242 {
243 bundlePath_ = path;
244 }
245
~NWebHelper()246 NWebHelper::~NWebHelper()
247 {
248 UnloadLib();
249 }
250
251 using CreateNWebFuncType = void(*)(const NWebCreateInfo &, std::shared_ptr<NWeb> &);
CreateNWeb(const NWebCreateInfo & create_info)252 std::shared_ptr<NWeb> NWebHelper::CreateNWeb(const NWebCreateInfo &create_info)
253 {
254 if (libHandleWebEngine_ == nullptr) {
255 WVLOG_E("libHandleWebEngine_ is nullptr");
256 return nullptr;
257 }
258
259 const std::string CREATE_NWEB_FUNC_NAME = "CreateNWeb";
260 CreateNWebFuncType funcCreateNWeb =
261 reinterpret_cast<CreateNWebFuncType>(dlsym(libHandleWebEngine_, CREATE_NWEB_FUNC_NAME.c_str()));
262 if (funcCreateNWeb == nullptr) {
263 WVLOG_E("fail to dlsym %{public}s from libohoswebview.so", CREATE_NWEB_FUNC_NAME.c_str());
264 return nullptr;
265 }
266 std::shared_ptr<NWeb> nweb;
267 funcCreateNWeb(create_info, nweb);
268 if (nweb == nullptr) {
269 WVLOG_E("fail to create nweb");
270 return nullptr;
271 }
272
273 return nweb;
274 }
275
276 using GetCookieManagerFunc = NWebCookieManager *(*)();
GetCookieManager()277 NWebCookieManager *NWebHelper::GetCookieManager()
278 {
279 if (libHandleWebEngine_ == nullptr) {
280 WVLOG_E("libHandleWebEngine_ is nullptr");
281 return nullptr;
282 }
283
284 const std::string COOKIE_FUNC_NAME = "GetCookieManager";
285 GetCookieManagerFunc cookieFunc =
286 reinterpret_cast<GetCookieManagerFunc>(dlsym(libHandleWebEngine_, COOKIE_FUNC_NAME.c_str()));
287 if (cookieFunc == nullptr) {
288 WVLOG_E("fail to dlsym %{public}s from libohoswebview.so", COOKIE_FUNC_NAME.c_str());
289 return nullptr;
290 }
291 return cookieFunc();
292 }
293
294 using GetNWebFunc = void(*)(int32_t, std::weak_ptr<NWeb> &);
GetNWeb(int32_t nweb_id)295 std::weak_ptr<NWeb> NWebHelper::GetNWeb(int32_t nweb_id)
296 {
297 std::weak_ptr<OHOS::NWeb::NWeb> nweb;
298 if (libHandleWebEngine_ == nullptr) {
299 WVLOG_E("libHandleWebEngine_ is nullptr");
300 return nweb;
301 }
302
303 const std::string GET_NWEB_FUNC_NAME = "GetNWeb";
304 GetNWebFunc getNWebFunc = reinterpret_cast<GetNWebFunc>(dlsym(libHandleWebEngine_, GET_NWEB_FUNC_NAME.c_str()));
305 if (getNWebFunc == nullptr) {
306 WVLOG_E("fail to dlsym %{public}s from libohoswebview.so", GET_NWEB_FUNC_NAME.c_str());
307 return nweb;
308 }
309
310 getNWebFunc(nweb_id, nweb);
311 return nweb;
312 }
313
314 using SetHttpDnsFunc = void (*)(const NWebDOHConfig &);
SetHttpDns(const NWebDOHConfig & config)315 void NWebHelper::SetHttpDns(const NWebDOHConfig &config)
316 {
317 if (libHandleWebEngine_ == nullptr) {
318 WVLOG_E("doh: libHandleNWebAdapter_ is nullptr");
319 return;
320 }
321
322 const std::string SET_HTTP_DNS_FUNC_NAME = "SetHttpDns";
323 SetHttpDnsFunc setHttpDnsFunc =
324 reinterpret_cast<SetHttpDnsFunc>(dlsym(libHandleWebEngine_, SET_HTTP_DNS_FUNC_NAME.c_str()));
325 if (setHttpDnsFunc == nullptr) {
326 WVLOG_E("doh: fail to dlsym %{public}s from libohoswebview.so", SET_HTTP_DNS_FUNC_NAME.c_str());
327 return;
328 }
329
330 setHttpDnsFunc(config);
331 }
332
333 using PrepareForPageLoadFunc = void (*)(std::string, bool, int32_t);
PrepareForPageLoad(std::string url,bool preconnectable,int32_t numSockets)334 void NWebHelper::PrepareForPageLoad(std::string url, bool preconnectable, int32_t numSockets)
335 {
336 if (libHandleWebEngine_ == nullptr) {
337 WVLOG_E("libHandleNWebAdapter_ is nullptr");
338 return;
339 }
340 const std::string PREPARE_FOR_PAGE_LOAD_FUNC_NAME = "PrepareForPageLoad";
341 PrepareForPageLoadFunc prepareForPageLoadFunc =
342 reinterpret_cast<PrepareForPageLoadFunc>(dlsym(libHandleWebEngine_, PREPARE_FOR_PAGE_LOAD_FUNC_NAME.c_str()));
343 if (prepareForPageLoadFunc == nullptr) {
344 WVLOG_E("fail to dlsym %{public}s from libohoswebview.so", PREPARE_FOR_PAGE_LOAD_FUNC_NAME.c_str());
345 return;
346 }
347 prepareForPageLoadFunc(url, preconnectable, numSockets);
348 }
349
350 using GetDataBaseFunc = NWebDataBase *(*)();
GetDataBase()351 NWebDataBase *NWebHelper::GetDataBase()
352 {
353 if (libHandleWebEngine_ == nullptr) {
354 WVLOG_E("libHandleWebEngine_ is nullptr");
355 return nullptr;
356 }
357
358 const std::string DATA_BASE_FUNC_NAME = "GetDataBase";
359 GetDataBaseFunc dataBaseFunc =
360 reinterpret_cast<GetDataBaseFunc>(dlsym(libHandleWebEngine_, DATA_BASE_FUNC_NAME.c_str()));
361 if (dataBaseFunc == nullptr) {
362 WVLOG_E("fail to dlsym %{public}s from libohoswebview.so", DATA_BASE_FUNC_NAME.c_str());
363 return nullptr;
364 }
365 return dataBaseFunc();
366 }
367
368 using GetWebStorageFunc = NWebWebStorage *(*)();
GetWebStorage()369 NWebWebStorage *NWebHelper::GetWebStorage()
370 {
371 if (libHandleWebEngine_ == nullptr) {
372 WVLOG_E("libHandleWebEngine_ is nullptr");
373 return nullptr;
374 }
375 const std::string STORAGE_FUNC_NAME = "GetWebStorage";
376 GetWebStorageFunc storageFunc =
377 reinterpret_cast<GetWebStorageFunc>(dlsym(libHandleWebEngine_, STORAGE_FUNC_NAME.c_str()));
378 if (storageFunc == nullptr) {
379 WVLOG_E("fail to dlsym %{public}s from libohoswebview.so", STORAGE_FUNC_NAME.c_str());
380 return nullptr;
381 }
382 return storageFunc();
383 }
384
Instance()385 NWebAdapterHelper &NWebAdapterHelper::Instance()
386 {
387 static NWebAdapterHelper helper;
388 return helper;
389 }
390
Init(bool from_ark)391 bool NWebAdapterHelper::Init(bool from_ark)
392 {
393 return NWebHelper::Instance().Init(from_ark);
394 }
395
CreateNWeb(sptr<Surface> surface,const NWebInitArgs & initArgs,uint32_t width,uint32_t height)396 std::shared_ptr<NWeb> NWebAdapterHelper::CreateNWeb(
397 sptr<Surface> surface, const NWebInitArgs& initArgs, uint32_t width, uint32_t height)
398 {
399 if (surface == nullptr) {
400 WVLOG_E("fail to create nweb, input surface is nullptr");
401 return nullptr;
402 }
403 if (width > NWEB_SURFACE_MAX_WIDTH || height > NWEB_SURFACE_MAX_HEIGHT) {
404 WVLOG_E("input size %{public}u*%{public}u is invalid.", width, height);
405 return nullptr;
406 }
407 auto createInfo = NWebSurfaceAdapter::Instance().GetCreateInfo(surface, initArgs, width, height);
408 ParseConfig(createInfo.init_args);
409 auto nweb = NWebHelper::Instance().CreateNWeb(createInfo);
410 if (nweb == nullptr) {
411 WVLOG_E("fail to create nweb instance");
412 }
413 return nweb;
414 }
415
CreateNWeb(void * enhanceSurfaceInfo,const NWebInitArgs & initArgs,uint32_t width,uint32_t height)416 std::shared_ptr<NWeb> NWebAdapterHelper::CreateNWeb(void *enhanceSurfaceInfo,
417 const NWebInitArgs &initArgs,
418 uint32_t width,
419 uint32_t height)
420 {
421 if (enhanceSurfaceInfo == nullptr) {
422 WVLOG_E("fail to create nweb, input surface is nullptr");
423 return nullptr;
424 }
425 if (width > NWEB_SURFACE_MAX_WIDTH || height > NWEB_SURFACE_MAX_HEIGHT) {
426 WVLOG_E("input size %{public}u*%{public}u is invalid.", width, height);
427 return nullptr;
428 }
429 auto createInfo = NWebEnhanceSurfaceAdapter::Instance().GetCreateInfo(enhanceSurfaceInfo, initArgs, width, height);
430 auto nweb = NWebHelper::Instance().CreateNWeb(createInfo);
431 if (nweb == nullptr) {
432 WVLOG_E("fail to create nweb instance");
433 }
434 return nweb;
435 }
436
GetConfigPath(const std::string & configFileName)437 std::string NWebAdapterHelper::GetConfigPath(const std::string& configFileName)
438 {
439 char buf[PATH_MAX + 1];
440 char* configPath = GetOneCfgFile(configFileName.c_str(), buf, PATH_MAX + 1);
441 char tmpPath[PATH_MAX + 1] = { 0 };
442 if (!configPath || strlen(configPath) == 0 || strlen(configPath) > PATH_MAX || !realpath(configPath, tmpPath)) {
443 WVLOG_I("can not get customization config file");
444 return "/system/" + configFileName;
445 }
446 return std::string(tmpPath);
447 }
448
ReadConfig(const xmlNodePtr & rootPtr,NWebInitArgs & init_args)449 void NWebAdapterHelper::ReadConfig(const xmlNodePtr& rootPtr, NWebInitArgs& init_args)
450 {
451 for (xmlNodePtr curNodePtr = rootPtr->xmlChildrenNode; curNodePtr != nullptr; curNodePtr = curNodePtr->next) {
452 if (curNodePtr->name == nullptr || curNodePtr->type == XML_COMMENT_NODE) {
453 WVLOG_E("invalid node!");
454 continue;
455 }
456 std::string nodeName = reinterpret_cast<const char*>(curNodePtr->name);
457 for (xmlNodePtr curChildNodePtr = curNodePtr->xmlChildrenNode; curChildNodePtr != nullptr;
458 curChildNodePtr = curChildNodePtr->next) {
459 if (curChildNodePtr->name == nullptr || curChildNodePtr->type == XML_COMMENT_NODE) {
460 WVLOG_E("invalid node!");
461 continue;
462 }
463 std::string childNodeName = reinterpret_cast<const char*>(curChildNodePtr->name);
464 xmlChar* content = xmlNodeGetContent(curChildNodePtr);
465 if (content == nullptr) {
466 WVLOG_E("read xml node error: nodeName:(%{public}s)", curChildNodePtr->name);
467 continue;
468 }
469 std::string contentStr = reinterpret_cast<const char*>(content);
470 xmlFree(content);
471 if (nodeName == std::string("renderConfig") && childNodeName == std::string("renderProcessCount")) {
472 init_args.web_engine_args_to_add.emplace_back(
473 std::string("--renderer-process-limit=") + contentStr);
474 } else if (nodeName == std::string("mediaConfig") &&
475 childNodeName == std::string("backgroundMediaShouldSuspend") && contentStr == std::string("false")) {
476 init_args.web_engine_args_to_add.emplace_back(
477 std::string("--disable-background-media-suspend"));
478 } else if (nodeName == std::string("loadurlSocPerfConfig") &&
479 childNodeName == std::string("loadurlSocPerfParam") && contentStr == std::string("true")) {
480 init_args.web_engine_args_to_add.emplace_back(
481 std::string("--ohos-enable-loadurl-soc-perf"));
482 } else if (nodeName == std::string("mouseWheelSocPerfConfig") &&
483 childNodeName == std::string("mouseWheelSocPerfParam") && contentStr == std::string("true")) {
484 init_args.web_engine_args_to_add.emplace_back(
485 std::string("--ohos-enable-mousewheel-soc-perf"));
486 }
487 }
488 }
489 }
490
ParseConfig(NWebInitArgs & args)491 void NWebAdapterHelper::ParseConfig(NWebInitArgs& args)
492 {
493 auto configFilePath = GetConfigPath(WEB_CONFIG_PATH);
494 xmlDocPtr docPtr = xmlReadFile(configFilePath.c_str(), nullptr, XML_PARSE_NOBLANKS);
495 if (docPtr == nullptr) {
496 WVLOG_E("load xml error!");
497 return;
498 }
499
500 xmlNodePtr rootPtr = xmlDocGetRootElement(docPtr);
501 if (rootPtr == nullptr || rootPtr->name == nullptr ||
502 xmlStrcmp(rootPtr->name, reinterpret_cast<const xmlChar*>("WEB"))) {
503 WVLOG_E("get root element failed!");
504 xmlFreeDoc(docPtr);
505 return;
506 }
507
508 ReadConfig(rootPtr, args);
509 xmlFreeDoc(docPtr);
510 }
511 } // namespace OHOS::NWeb
512