1 /*
2 * Copyright (c) 2021-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 "core/components/form/sub_container.h"
17
18 #include "ashmem.h"
19
20 #include "adapter/ohos/entrance/utils.h"
21 #include "base/utils/utils.h"
22 #include "core/common/container_scope.h"
23 #include "core/components/theme/theme_manager_impl.h"
24 #include "core/components_ng/pattern/form/form_layout_property.h"
25 #include "frameworks/core/common/asset_manager_impl.h"
26 #include "frameworks/core/common/task_executor_impl.h"
27 #include "frameworks/core/components/form/form_element.h"
28 #include "frameworks/core/components/form/form_window.h"
29 #include "frameworks/core/components/form/render_form.h"
30
31 namespace OHOS::Ace {
32 namespace {
33
34 const int32_t THEME_ID_DEFAULT = 117440515;
35
36 } // namespace
37
~SubContainer()38 SubContainer::~SubContainer()
39 {
40 Destroy();
41 }
42
Initialize()43 void SubContainer::Initialize()
44 {
45 auto formPattern = formPattern_.Upgrade();
46 if (formPattern && !formPattern->IsJsCard()) {
47 LOGI("ETS card do not require the creation of a separate TaskExecutor");
48 return;
49 }
50
51 if (!outSidePipelineContext_.Upgrade()) {
52 LOGE("no pipeline context for create form component container.");
53 return;
54 }
55
56 auto executor = outSidePipelineContext_.Upgrade()->GetTaskExecutor();
57 if (!executor) {
58 LOGE("could not got main pipeline executor");
59 return;
60 }
61
62 auto taskExecutor = AceType::DynamicCast<TaskExecutorImpl>(executor);
63 if (!taskExecutor) {
64 LOGE("main pipeline context executor is not flutter taskexecutor");
65 return;
66 }
67 taskExecutor_ = Referenced::MakeRefPtr<TaskExecutorImpl>(taskExecutor);
68 }
69
Destroy()70 void SubContainer::Destroy()
71 {
72 if (!pipelineContext_) {
73 LOGE("no context find for inner card");
74 return;
75 }
76
77 if (!taskExecutor_) {
78 LOGE("no taskExecutor find for inner card");
79 return;
80 }
81
82 auto outPipelineContext = outSidePipelineContext_.Upgrade();
83 if (outPipelineContext) {
84 outPipelineContext->RemoveTouchPipeline(WeakPtr<PipelineBase>(pipelineContext_));
85 }
86
87 assetManager_.Reset();
88 pipelineContext_.Reset();
89 }
90
UpdateRootElementSize()91 void SubContainer::UpdateRootElementSize()
92 {
93 Dimension rootWidth = 0.0_vp;
94 Dimension rootHeight = 0.0_vp;
95 if (Container::IsCurrentUseNewPipeline()) {
96 auto form = formPattern_.Upgrade();
97 CHECK_NULL_VOID(form);
98 auto layoutProperty = form->GetLayoutProperty<NG::FormLayoutProperty>();
99 CHECK_NULL_VOID(layoutProperty);
100 auto formInfo = layoutProperty->GetRequestFormInfo();
101 if (formInfo.has_value()) {
102 rootWidth = formInfo->width;
103 rootHeight = formInfo->height;
104 }
105 } else {
106 auto formComponent = AceType::DynamicCast<FormComponent>(formComponent_);
107 if (formComponent) {
108 rootWidth = formComponent->GetWidth();
109 rootHeight = formComponent->GetHeight();
110 }
111 }
112
113 if (rootWidht_ == rootWidth && rootHeight_ == rootHeight) {
114 LOGE("size not changed, should not change");
115 return;
116 }
117
118 surfaceWidth_ = outSidePipelineContext_.Upgrade()->NormalizeToPx(rootWidth);
119 surfaceHeight_ = outSidePipelineContext_.Upgrade()->NormalizeToPx(rootHeight);
120 if (pipelineContext_) {
121 pipelineContext_->SetRootSize(density_, rootWidth.Value(), rootHeight.Value());
122 }
123 }
124
UpdateSurfaceSize()125 void SubContainer::UpdateSurfaceSize()
126 {
127 if (!taskExecutor_) {
128 LOGE("update surface size fail could not post task to ui thread");
129 return;
130 }
131 auto weakContext = AceType::WeakClaim(AceType::RawPtr(pipelineContext_));
132 taskExecutor_->PostTask(
133 [weakContext, surfaceWidth = surfaceWidth_, surfaceHeight = surfaceHeight_]() {
134 auto context = weakContext.Upgrade();
135 if (context == nullptr) {
136 LOGE("context is nullptr");
137 return;
138 }
139 if (NearZero(surfaceWidth) && NearZero(surfaceHeight)) {
140 LOGE("surface is zero, should not update");
141 return;
142 }
143 context->OnSurfaceChanged(surfaceWidth, surfaceHeight);
144 },
145 TaskExecutor::TaskType::UI, "ArkUIFormUpdateSurfaceSize");
146 }
147
UpdateSurfaceSizeWithAnimathion()148 void SubContainer::UpdateSurfaceSizeWithAnimathion()
149 {
150 CHECK_NULL_VOID(pipelineContext_);
151 pipelineContext_->OnSurfaceChanged(surfaceWidth_, surfaceHeight_);
152 pipelineContext_->FlushPipelineImmediately();
153 }
154
RunCard(int64_t formId,const std::string & path,const std::string & module,const std::string & data,const std::map<std::string,sptr<AppExecFwk::FormAshmem>> & imageDataMap,const std::string & formSrc,const FrontendType & cardType,const FrontendType & uiSyntax)155 void SubContainer::RunCard(int64_t formId, const std::string& path, const std::string& module, const std::string& data,
156 const std::map<std::string, sptr<AppExecFwk::FormAshmem>>& imageDataMap, const std::string& formSrc,
157 const FrontendType& cardType, const FrontendType& uiSyntax)
158 {
159 LOGI("SubContainer::RunCard RunCard!!! path = %{public}s formSrc = %{public}s", path.c_str(), formSrc.c_str());
160 if ((formId == runningCardId_) && (uiSyntax == FrontendType::ETS_CARD)) {
161 LOGE("the card is showing, no need run again");
162 return;
163 }
164
165 runningCardId_ = formId;
166 if (onFormAcquiredCallback_) {
167 onFormAcquiredCallback_(formId);
168 }
169 if (uiSyntax == FrontendType::ETS_CARD) {
170 // ArkTSCard: 确认Acquired事件时序
171 LOGI("Run Card in FRS");
172 uiSyntax_ = FrontendType::ETS_CARD;
173 return;
174 }
175
176 cardType_ = cardType;
177 if (cardType_ != FrontendType::ETS_CARD && cardType_ != FrontendType::JS_CARD) {
178 LOGE("Run Card failed, card type unknown");
179 return;
180 }
181
182 InitFrontend(path, module, imageDataMap, formSrc, cardType_);
183
184 if (cardType_ == FrontendType::ETS_CARD) { // ETS Card : API9 only support NG-Host & NG-eTSCard
185 if (Container::IsCurrentUseNewPipeline()) {
186 auto pattern = formPattern_.Upgrade();
187 CHECK_NULL_VOID(pattern);
188 auto pipelineContext = DynamicCast<NG::PipelineContext>(pipelineContext_);
189 if (!pipelineContext) {
190 LOGE("RunCard failed, pipeline context is nullptr");
191 return;
192 }
193 pipelineContext->SetDrawDelegate(pattern->GetDrawDelegate());
194 frontend_->RunPage("", data);
195 return;
196 } else {
197 LOGE("ETS Card not support old pipeline");
198 return;
199 }
200 } else if (cardType_ == FrontendType::JS_CARD) {
201 RunJSCard(data);
202 } else {
203 LOGE("SubContainer::RunCard card type error");
204 }
205 }
206
RunJSCard(const std::string & data)207 void SubContainer::RunJSCard(const std::string& data)
208 {
209 // JS Card : API9 only support Old Pipeline JSCard, Host can be NG or Old
210 if (Container::IsCurrentUseNewPipeline()) {
211 auto pattern = formPattern_.Upgrade();
212 CHECK_NULL_VOID(pattern);
213 pipelineContext_->SetDrawDelegate(pattern->GetDrawDelegate());
214 frontend_->RunPage("", data);
215 if (onFormLoadCallback_) {
216 onFormLoadCallback_();
217 }
218 return;
219 }
220
221 auto form = AceType::DynamicCast<FormElement>(GetFormElement().Upgrade());
222 if (!form) {
223 LOGE("set draw delegate could not get form element");
224 return;
225 }
226 auto renderNode = form->GetRenderNode();
227 if (!renderNode) {
228 LOGE("set draw delegate could not get render node");
229 return;
230 }
231 auto formRender = AceType::DynamicCast<RenderForm>(renderNode);
232 if (!formRender) {
233 LOGE("set draw delegate could not get render form");
234 return;
235 }
236 pipelineContext_->SetDrawDelegate(formRender->GetDrawDelegate());
237 frontend_->RunPage("", data);
238 }
239
RunSameCard()240 void SubContainer::RunSameCard()
241 {
242 LOGI("SubContainer::RunSameCard ");
243 if (onFormAcquiredCallback_) {
244 onFormAcquiredCallback_(runningCardId_);
245 }
246 auto pattern = formPattern_.Upgrade();
247 CHECK_NULL_VOID(pattern);
248 auto pipelineContext = DynamicCast<PipelineContext>(pipelineContext_);
249 CHECK_NULL_VOID(pipelineContext);
250 UpdateRootElementSize();
251 pipelineContext_->OnSurfaceChanged(surfaceWidth_, surfaceHeight_);
252 auto delegeta = pattern->GetDrawDelegate();
253 pipelineContext->SetDrawDelegate(std::move(delegeta));
254 pipelineContext->MarkForcedRefresh();
255 pipelineContext_->FlushPipelineImmediately();
256 onFormVisibleCallback_();
257 }
258
ProcessSharedImage(const std::map<std::string,sptr<AppExecFwk::FormAshmem>> imageDataMap)259 void SubContainer::ProcessSharedImage(const std::map<std::string, sptr<AppExecFwk::FormAshmem>> imageDataMap)
260 {
261 std::vector<std::string> picNameArray;
262 std::vector<int> fileDescriptorArray;
263 std::vector<int> byteLenArray;
264 if (!imageDataMap.empty()) {
265 for (auto& imageData : imageDataMap) {
266 if (!imageData.second) {
267 LOGI("the point of FormAshmem about %{private}s is null, continue", imageData.first.c_str());
268 continue;
269 }
270 picNameArray.push_back(imageData.first);
271 fileDescriptorArray.push_back(imageData.second->GetAshmemFd());
272 byteLenArray.push_back(imageData.second->GetAshmemSize());
273 }
274 GetNamesOfSharedImage(picNameArray);
275 UpdateSharedImage(picNameArray, byteLenArray, fileDescriptorArray);
276 }
277 }
278
GetNamesOfSharedImage(std::vector<std::string> & picNameArray)279 void SubContainer::GetNamesOfSharedImage(std::vector<std::string>& picNameArray)
280 {
281 if (picNameArray.empty()) {
282 LOGE("picNameArray is null!");
283 return;
284 }
285 auto pipelineCtx = DynamicCast<PipelineContext>(GetPipelineContext());
286 if (!pipelineCtx) {
287 LOGE("pipeline context is null!");
288 return;
289 }
290 auto sharedImageManager = pipelineCtx->GetOrCreateSharedImageManager();
291 auto nameSize = picNameArray.size();
292 for (uint32_t i = 0; i < nameSize; i++) {
293 // get name of picture
294 auto name = picNameArray[i];
295 sharedImageManager->AddPictureNamesToReloadMap(std::move(name));
296 }
297 }
298
UpdateSharedImage(std::vector<std::string> & picNameArray,std::vector<int32_t> & byteLenArray,std::vector<int> & fileDescriptorArray)299 void SubContainer::UpdateSharedImage(
300 std::vector<std::string>& picNameArray, std::vector<int32_t>& byteLenArray, std::vector<int>& fileDescriptorArray)
301 {
302 auto pipelineCtx = GetPipelineContext();
303 if (!pipelineCtx) {
304 LOGE("pipeline context is null! when try UpdateSharedImage");
305 return;
306 }
307 if (picNameArray.empty() || byteLenArray.empty() || fileDescriptorArray.empty()) {
308 LOGE("array is null! when try UpdateSharedImage");
309 return;
310 }
311 auto nameArraySize = picNameArray.size();
312 if (nameArraySize != byteLenArray.size()) {
313 LOGE("nameArraySize does not equal to fileDescriptorArraySize, please check!");
314 return;
315 }
316 if (nameArraySize != fileDescriptorArray.size()) {
317 LOGE("nameArraySize does not equal to fileDescriptorArraySize, please check!");
318 return;
319 }
320 // now it can be assured that all three arrays are of the same size
321
322 std::string picNameCopy;
323 for (uint32_t i = 0; i < nameArraySize; i++) {
324 // get name of picture
325 auto picName = picNameArray[i];
326 // save a copy of picName and ReleaseStringUTFChars immediately to avoid memory leak
327 picNameCopy = picName;
328
329 // get fd ID
330 auto fd = fileDescriptorArray[i];
331
332 auto newFd = dup(fd);
333 if (newFd < 0) {
334 LOGE("dup fd fail, fail reason: %{public}s, fd: %{public}d, picName: %{private}s, length: %{public}d",
335 strerror(errno), fd, picNameCopy.c_str(), byteLenArray[i]);
336 continue;
337 }
338
339 auto ashmem = Ashmem(newFd, byteLenArray[i]);
340 GetImageDataFromAshmem(picNameCopy, ashmem, pipelineCtx, byteLenArray[i]);
341 ashmem.UnmapAshmem();
342 ashmem.CloseAshmem();
343 }
344 }
345
GetImageDataFromAshmem(const std::string & picName,Ashmem & ashmem,const RefPtr<PipelineBase> & pipelineContext,int len)346 void SubContainer::GetImageDataFromAshmem(
347 const std::string& picName, Ashmem& ashmem, const RefPtr<PipelineBase>& pipelineContext, int len)
348 {
349 bool ret = ashmem.MapReadOnlyAshmem();
350 // if any exception causes a [return] before [AddSharedImage], the memory image will not show because [RenderImage]
351 // will never be notified to start loading.
352 if (!ret) {
353 LOGE("MapReadOnlyAshmem fail, fail reason: %{public}s, picName: %{private}s, length: %{public}d, "
354 "fd: %{public}d",
355 strerror(errno), picName.c_str(), len, ashmem.GetAshmemFd());
356 return;
357 }
358 const uint8_t* imageData = reinterpret_cast<const uint8_t*>(ashmem.ReadFromAshmem(len, 0));
359 if (imageData == nullptr) {
360 LOGE("imageData is nullptr, errno is: %{public}s, picName: %{private}s, length: %{public}d, fd: %{public}d",
361 strerror(errno), picName.c_str(), len, ashmem.GetAshmemFd());
362 return;
363 }
364 auto context = DynamicCast<PipelineContext>(pipelineContext);
365 CHECK_NULL_VOID(context);
366 RefPtr<SharedImageManager> sharedImageManager = context->GetOrCreateSharedImageManager();
367 if (sharedImageManager) {
368 // read image data from shared memory and save a copy to sharedImageManager
369 sharedImageManager->AddSharedImage(picName, std::vector<uint8_t>(imageData, imageData + len));
370 }
371 }
372
UpdateCard(const std::string & content,const std::map<std::string,sptr<AppExecFwk::FormAshmem>> & imageDataMap)373 void SubContainer::UpdateCard(
374 const std::string& content, const std::map<std::string, sptr<AppExecFwk::FormAshmem>>& imageDataMap)
375 {
376 if (!frontend_) {
377 LOGE("update card fial due to could not find card front end");
378 return;
379 }
380 if (allowUpdate_) {
381 frontend_->UpdateData(std::move(content));
382 ProcessSharedImage(imageDataMap);
383 }
384 }
385
UpdateConfiguration()386 void SubContainer::UpdateConfiguration()
387 {
388 if (frontend_) {
389 frontend_->OnMediaFeatureUpdate();
390 }
391 }
392
Dump(const std::vector<std::string> & params)393 bool SubContainer::Dump(const std::vector<std::string>& params)
394 {
395 if (pipelineContext_) {
396 pipelineContext_->Dump(params);
397 return true;
398 }
399 return false;
400 }
401
InitFrontend(const std::string & path,const std::string & module,const std::map<std::string,sptr<AppExecFwk::FormAshmem>> & imageDataMap,const std::string & formSrc,const FrontendType & cardType)402 void SubContainer::InitFrontend(const std::string &path, const std::string &module,
403 const std::map<std::string, sptr<AppExecFwk::FormAshmem>> &imageDataMap, const std::string &formSrc,
404 const FrontendType &cardType)
405 {
406 cardType_ = cardType;
407 if (cardType_ == FrontendType::ETS_CARD) {
408 frontend_ = AceType::MakeRefPtr<CardFrontendDeclarative>();
409 onFormVisibleCallback_();
410 } else if (cardType_ == FrontendType::JS_CARD) {
411 frontend_ = AceType::MakeRefPtr<CardFrontend>();
412 frontend_->AddFormVisiableCallback(onFormVisibleCallback_);
413 }
414 frontend_->Initialize(cardType_, taskExecutor_);
415 frontend_->ResetPageLoadState();
416 LOGI("run card path:%{private}s, module:%{private}s", path.c_str(), module.c_str());
417
418 auto assetManager = GetAssetManager(path, module);
419
420 if (formSrc.compare(0, 2, "./") == 0) { // 2:length of "./"
421 frontend_->SetFormSrc(formSrc.substr(2)); // 2:length of "./"
422 } else {
423 frontend_->SetFormSrc(formSrc);
424 }
425 LOGI("RunCard formSrc = %{public}s", formSrc.c_str());
426 frontend_->SetCardWindowConfig(GetWindowConfig());
427
428 auto &&window = std::make_unique<FormWindow>(outSidePipelineContext_);
429 window->SetFormWindowId(nodeId_);
430 window->SetId(instanceId_);
431 windowId_ = nodeId_;
432 if (cardType_ == FrontendType::ETS_CARD) { // ETS Card : API9 only support New Pipeline
433 pipelineContext_ = AceType::MakeRefPtr<NG::PipelineContext>(
434 std::move(window), taskExecutor_, assetManager_, nullptr, frontend_, instanceId_);
435 } else { // JS Card : API9 only support Old Pipeline
436 pipelineContext_ = AceType::MakeRefPtr<PipelineContext>(
437 std::move(window), taskExecutor_, assetManager_, nullptr, frontend_, instanceId_);
438 }
439
440 InitCardThemeManager(path, instanceId_, module, imageDataMap, assetManager);
441
442 InitPipelineContext();
443
444 frontend_->AttachPipelineContext(pipelineContext_);
445 frontend_->SetLoadCardCallBack(outSidePipelineContext_);
446 frontend_->SetRunningCardId(nodeId_);
447 frontend_->SetDensity(density_);
448 UpdateSurfaceSize();
449 }
450
InitPipelineContext()451 void SubContainer::InitPipelineContext()
452 {
453 auto&& actionEventHandler = [weak = WeakClaim(this)](const std::string& action) {
454 auto container = weak.Upgrade();
455 CHECK_NULL_VOID(container);
456
457 if (Container::IsCurrentUseNewPipeline()) {
458 auto form = container->GetFormPattern();
459 CHECK_NULL_VOID(form);
460 form->OnActionEvent(action);
461 } else {
462 auto form = AceType::DynamicCast<FormElement>(container->GetFormElement().Upgrade());
463 CHECK_NULL_VOID(form);
464 form->OnActionEvent(action);
465 }
466 };
467 pipelineContext_->SetActionEventHandler(actionEventHandler);
468
469 auto weakContext = AceType::WeakClaim(AceType::RawPtr(pipelineContext_));
470 taskExecutor_->PostTask(
471 [weakContext]() {
472 auto context = weakContext.Upgrade();
473 if (context == nullptr) {
474 LOGE("RunCard PostTask Task failed, context is nullptr");
475 return;
476 }
477 context->SetupRootElement();
478 },
479 TaskExecutor::TaskType::UI, "ArkUIFormSetupRootElement");
480 }
481
GetAssetManager(const std::string & path,const std::string & module)482 RefPtr<AssetManager> SubContainer::GetAssetManager(const std::string &path, const std::string &module)
483 {
484 RefPtr<AssetManagerImpl> assetManagerImpl = Referenced::MakeRefPtr<AssetManagerImpl>();
485 std::vector<std::string> basePaths;
486 basePaths.push_back("assets/js/" + module + "/");
487 basePaths.emplace_back("assets/js/share/");
488 basePaths.emplace_back("");
489 basePaths.emplace_back("js/");
490 basePaths.emplace_back("ets/");
491 if (assetManagerImpl) {
492 frontend_->SetAssetManager(assetManagerImpl);
493 assetManager_ = assetManagerImpl;
494 auto assetProvider = CreateAssetProviderImpl(path, basePaths);
495 if (assetProvider) {
496 LOGI("push card asset provider to queue.");
497 assetManagerImpl->PushBack(std::move(assetProvider));
498 }
499 }
500 return assetManagerImpl;
501 }
502
InitCardThemeManager(const std::string & path,int32_t instanceId,const std::string & module,const std::map<std::string,sptr<AppExecFwk::FormAshmem>> & imageDataMap,RefPtr<AssetManager> assetManager)503 void SubContainer::InitCardThemeManager(const std::string &path, int32_t instanceId, const std::string &module,
504 const std::map<std::string, sptr<AppExecFwk::FormAshmem>> &imageDataMap, RefPtr<AssetManager> assetManager)
505 {
506 ContainerScope scope(instanceId_);
507 density_ = outSidePipelineContext_.Upgrade()->GetDensity();
508 auto eventManager = outSidePipelineContext_.Upgrade()->GetEventManager();
509 pipelineContext_->SetEventManager(eventManager);
510 ProcessSharedImage(imageDataMap);
511 UpdateRootElementSize();
512 pipelineContext_->SetIsJsCard(true); // JSCard & eTSCard both use this flag
513 if (cardType_ == FrontendType::ETS_CARD) {
514 pipelineContext_->SetIsFormRender(true); // only eTSCard use this flag
515 }
516
517 ResourceInfo cardResourceInfo;
518 ResourceConfiguration resConfig;
519 resConfig.SetDensity(density_);
520 cardResourceInfo.SetThemeId(THEME_ID_DEFAULT);
521 cardResourceInfo.SetPackagePath(path);
522 cardResourceInfo.SetResourceConfiguration(resConfig);
523 auto cardThemeManager = pipelineContext_->GetThemeManager();
524 if (!cardThemeManager) {
525 cardThemeManager = AceType::MakeRefPtr<ThemeManagerImpl>();
526 pipelineContext_->SetThemeManager(cardThemeManager);
527 }
528 if (cardThemeManager) {
529 // Init resource, load theme map, do not parse yet.
530 cardThemeManager->InitResource(cardResourceInfo);
531 cardThemeManager->LoadSystemTheme(cardResourceInfo.GetThemeId());
532 auto weakTheme = AceType::WeakClaim(AceType::RawPtr(cardThemeManager));
533 auto weakAsset = AceType::WeakClaim(AceType::RawPtr(assetManager));
534 taskExecutor_->PostTask(
535 [weakTheme, weakAsset]() {
536 auto themeManager = weakTheme.Upgrade();
537 if (themeManager == nullptr) {
538 LOGE("themeManager or aceView is null!");
539 return;
540 }
541 themeManager->ParseSystemTheme();
542 themeManager->SetColorScheme(ColorScheme::SCHEME_LIGHT);
543 themeManager->LoadCustomTheme(weakAsset.Upgrade());
544 },
545 TaskExecutor::TaskType::UI, "ArkUIFormLoadTheme");
546 }
547 }
548
549 } // namespace OHOS::Ace
550