• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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