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_) {
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) {
178         frontend_ = AceType::MakeRefPtr<CardFrontendDeclarative>();
179         onFormVisibleCallback_();
180     } else if (cardType_ == FrontendType::JS_CARD) {
181         frontend_ = AceType::MakeRefPtr<CardFrontend>();
182         frontend_->AddFormVisiableCallback(onFormVisibleCallback_);
183     } else {
184         LOGE("Run Card failed, card type unknown");
185         return;
186     }
187     frontend_->Initialize(cardType_, taskExecutor_);
188     frontend_->ResetPageLoadState();
189     LOGI("run card path:%{private}s, module:%{private}s, data:%{private}s", path.c_str(), module.c_str(), data.c_str());
190     RefPtr<AssetManagerImpl> assetManagerImpl = Referenced::MakeRefPtr<AssetManagerImpl>();
191     std::vector<std::string> basePaths;
192     basePaths.push_back("assets/js/" + module + "/");
193     basePaths.emplace_back("assets/js/share/");
194     basePaths.emplace_back("");
195     basePaths.emplace_back("js/");
196     basePaths.emplace_back("ets/");
197     if (assetManagerImpl) {
198         frontend_->SetAssetManager(assetManagerImpl);
199         assetManager_ = assetManagerImpl;
200         auto assetProvider = CreateAssetProviderImpl(path, basePaths);
201         if (assetProvider) {
202             LOGI("push card asset provider to queue.");
203             assetManagerImpl->PushBack(std::move(assetProvider));
204         }
205     }
206     if (formSrc.compare(0, 2, "./") == 0) {       // 2:length of "./"
207         frontend_->SetFormSrc(formSrc.substr(2)); // 2:length of "./"
208     } else {
209         frontend_->SetFormSrc(formSrc);
210     }
211     LOGI("RunCard formSrc = %{public}s", formSrc.c_str());
212     frontend_->SetCardWindowConfig(GetWindowConfig());
213     auto&& window = std::make_unique<FormWindow>(outSidePipelineContext_);
214     window->SetFormWindowId(nodeId_);
215     window->SetId(instanceId_);
216     windowId_ = nodeId_;
217     if (cardType_ == FrontendType::ETS_CARD) { // ETS Card : API9 only support New Pipeline
218         pipelineContext_ = AceType::MakeRefPtr<NG::PipelineContext>(
219             std::move(window), taskExecutor_, assetManager_, nullptr, frontend_, instanceId_);
220     } else { // JS Card : API9 only support Old Pipeline
221         pipelineContext_ = AceType::MakeRefPtr<PipelineContext>(
222             std::move(window), taskExecutor_, assetManager_, nullptr, frontend_, instanceId_);
223     }
224     ContainerScope scope(instanceId_);
225     density_ = outSidePipelineContext_.Upgrade()->GetDensity();
226     auto eventManager = outSidePipelineContext_.Upgrade()->GetEventManager();
227     pipelineContext_->SetEventManager(eventManager);
228     ProcessSharedImage(imageDataMap);
229     UpdateRootElementSize();
230     pipelineContext_->SetIsJsCard(true); // JSCard & eTSCard both use this flag
231     if (cardType_ == FrontendType::ETS_CARD) {
232         pipelineContext_->SetIsFormRender(true); // only eTSCard use this flag
233     }
234 
235     ResourceInfo cardResourceInfo;
236     ResourceConfiguration resConfig;
237     resConfig.SetDensity(density_);
238     cardResourceInfo.SetThemeId(THEME_ID_DEFAULT);
239     cardResourceInfo.SetPackagePath(path);
240     cardResourceInfo.SetResourceConfiguration(resConfig);
241     auto cardThemeManager = pipelineContext_->GetThemeManager();
242     if (!cardThemeManager) {
243         cardThemeManager = AceType::MakeRefPtr<ThemeManagerImpl>();
244         pipelineContext_->SetThemeManager(cardThemeManager);
245     }
246     if (cardThemeManager) {
247         // Init resource, load theme map, do not parse yet.
248         cardThemeManager->InitResource(cardResourceInfo);
249         cardThemeManager->LoadSystemTheme(cardResourceInfo.GetThemeId());
250         auto weakTheme = AceType::WeakClaim(AceType::RawPtr(cardThemeManager));
251         auto weakAsset = AceType::WeakClaim(AceType::RawPtr(assetManagerImpl));
252         taskExecutor_->PostTask(
253             [weakTheme, weakAsset]() {
254                 auto themeManager = weakTheme.Upgrade();
255                 if (themeManager == nullptr) {
256                     LOGE("themeManager or aceView is null!");
257                     return;
258                 }
259                 themeManager->ParseSystemTheme();
260                 themeManager->SetColorScheme(ColorScheme::SCHEME_LIGHT);
261                 themeManager->LoadCustomTheme(weakAsset.Upgrade());
262             },
263             TaskExecutor::TaskType::UI, "ArkUIFormLoadTheme");
264     }
265 
266     auto&& actionEventHandler = [weak = WeakClaim(this)](const std::string& action) {
267         auto container = weak.Upgrade();
268         CHECK_NULL_VOID(container);
269 
270         if (Container::IsCurrentUseNewPipeline()) {
271             auto form = container->GetFormPattern();
272             CHECK_NULL_VOID(form);
273             form->OnActionEvent(action);
274         } else {
275             auto form = AceType::DynamicCast<FormElement>(container->GetFormElement().Upgrade());
276             CHECK_NULL_VOID(form);
277             form->OnActionEvent(action);
278         }
279     };
280     pipelineContext_->SetActionEventHandler(actionEventHandler);
281 
282     auto weakContext = AceType::WeakClaim(AceType::RawPtr(pipelineContext_));
283     taskExecutor_->PostTask(
284         [weakContext]() {
285             auto context = weakContext.Upgrade();
286             if (context == nullptr) {
287                 LOGE("RunCard PostTask Task failed, context is nullptr");
288                 return;
289             }
290             context->SetupRootElement();
291         },
292         TaskExecutor::TaskType::UI, "ArkUIFormSetupRootElement");
293 
294     frontend_->AttachPipelineContext(pipelineContext_);
295     frontend_->SetLoadCardCallBack(outSidePipelineContext_);
296     frontend_->SetRunningCardId(nodeId_);
297     frontend_->SetDensity(density_);
298     UpdateSurfaceSize();
299 
300     if (cardType_ == FrontendType::ETS_CARD) { // ETS Card : API9 only support NG-Host & NG-eTSCard
301         if (Container::IsCurrentUseNewPipeline()) {
302             auto pattern = formPattern_.Upgrade();
303             CHECK_NULL_VOID(pattern);
304             auto pipelineContext = DynamicCast<NG::PipelineContext>(pipelineContext_);
305             if (!pipelineContext) {
306                 LOGE("RunCard failed, pipeline context is nullptr");
307                 return;
308             }
309             pipelineContext->SetDrawDelegate(pattern->GetDrawDelegate());
310             frontend_->RunPage("", data);
311             return;
312         } else {
313             LOGE("ETS Card not support old pipeline");
314             return;
315         }
316     } else if (cardType_ == FrontendType::JS_CARD) {
317         // JS Card : API9 only support Old Pipeline JSCard, Host can be NG or Old
318         if (Container::IsCurrentUseNewPipeline()) {
319             auto pattern = formPattern_.Upgrade();
320             CHECK_NULL_VOID(pattern);
321             pipelineContext_->SetDrawDelegate(pattern->GetDrawDelegate());
322             frontend_->RunPage("", data);
323             if (onFormLoadCallback_) {
324                 onFormLoadCallback_();
325             }
326             return;
327         }
328 
329         auto form = AceType::DynamicCast<FormElement>(GetFormElement().Upgrade());
330         if (!form) {
331             LOGE("set draw delegate could not get form element");
332             return;
333         }
334         auto renderNode = form->GetRenderNode();
335         if (!renderNode) {
336             LOGE("set draw delegate could not get render node");
337             return;
338         }
339         auto formRender = AceType::DynamicCast<RenderForm>(renderNode);
340         if (!formRender) {
341             LOGE("set draw delegate could not get render form");
342             return;
343         }
344         pipelineContext_->SetDrawDelegate(formRender->GetDrawDelegate());
345 
346         frontend_->RunPage("", data);
347     } else {
348         LOGE("SubContainer::RunCard card type error");
349     }
350 }
351 
RunSameCard()352 void SubContainer::RunSameCard()
353 {
354     LOGI("SubContainer::RunSameCard ");
355     if (onFormAcquiredCallback_) {
356         onFormAcquiredCallback_(runningCardId_);
357     }
358     auto pattern = formPattern_.Upgrade();
359     CHECK_NULL_VOID(pattern);
360     auto pipelineContext = DynamicCast<PipelineContext>(pipelineContext_);
361     CHECK_NULL_VOID(pipelineContext);
362     UpdateRootElementSize();
363     pipelineContext_->OnSurfaceChanged(surfaceWidth_, surfaceHeight_);
364     auto delegeta = pattern->GetDrawDelegate();
365     pipelineContext->SetDrawDelegate(std::move(delegeta));
366     pipelineContext->MarkForcedRefresh();
367     pipelineContext_->FlushPipelineImmediately();
368     onFormVisibleCallback_();
369 }
370 
ProcessSharedImage(const std::map<std::string,sptr<AppExecFwk::FormAshmem>> imageDataMap)371 void SubContainer::ProcessSharedImage(const std::map<std::string, sptr<AppExecFwk::FormAshmem>> imageDataMap)
372 {
373     std::vector<std::string> picNameArray;
374     std::vector<int> fileDescriptorArray;
375     std::vector<int> byteLenArray;
376     if (!imageDataMap.empty()) {
377         for (auto& imageData : imageDataMap) {
378             if (!imageData.second) {
379                 LOGI("the point of FormAshmem about %{private}s is null, continue", imageData.first.c_str());
380                 continue;
381             }
382             picNameArray.push_back(imageData.first);
383             fileDescriptorArray.push_back(imageData.second->GetAshmemFd());
384             byteLenArray.push_back(imageData.second->GetAshmemSize());
385         }
386         GetNamesOfSharedImage(picNameArray);
387         UpdateSharedImage(picNameArray, byteLenArray, fileDescriptorArray);
388     }
389 }
390 
GetNamesOfSharedImage(std::vector<std::string> & picNameArray)391 void SubContainer::GetNamesOfSharedImage(std::vector<std::string>& picNameArray)
392 {
393     if (picNameArray.empty()) {
394         LOGE("picNameArray is null!");
395         return;
396     }
397     auto pipelineCtx = DynamicCast<PipelineContext>(GetPipelineContext());
398     if (!pipelineCtx) {
399         LOGE("pipeline context is null!");
400         return;
401     }
402     auto sharedImageManager = pipelineCtx->GetOrCreateSharedImageManager();
403     auto nameSize = picNameArray.size();
404     for (uint32_t i = 0; i < nameSize; i++) {
405         // get name of picture
406         auto name = picNameArray[i];
407         sharedImageManager->AddPictureNamesToReloadMap(std::move(name));
408     }
409 }
410 
UpdateSharedImage(std::vector<std::string> & picNameArray,std::vector<int32_t> & byteLenArray,std::vector<int> & fileDescriptorArray)411 void SubContainer::UpdateSharedImage(
412     std::vector<std::string>& picNameArray, std::vector<int32_t>& byteLenArray, std::vector<int>& fileDescriptorArray)
413 {
414     auto pipelineCtx = GetPipelineContext();
415     if (!pipelineCtx) {
416         LOGE("pipeline context is null! when try UpdateSharedImage");
417         return;
418     }
419     if (picNameArray.empty() || byteLenArray.empty() || fileDescriptorArray.empty()) {
420         LOGE("array is null! when try UpdateSharedImage");
421         return;
422     }
423     auto nameArraySize = picNameArray.size();
424     if (nameArraySize != byteLenArray.size()) {
425         LOGE("nameArraySize does not equal to fileDescriptorArraySize, please check!");
426         return;
427     }
428     if (nameArraySize != fileDescriptorArray.size()) {
429         LOGE("nameArraySize does not equal to fileDescriptorArraySize, please check!");
430         return;
431     }
432     // now it can be assured that all three arrays are of the same size
433 
434     std::string picNameCopy;
435     for (uint32_t i = 0; i < nameArraySize; i++) {
436         // get name of picture
437         auto picName = picNameArray[i];
438         // save a copy of picName and ReleaseStringUTFChars immediately to avoid memory leak
439         picNameCopy = picName;
440 
441         // get fd ID
442         auto fd = fileDescriptorArray[i];
443 
444         auto newFd = dup(fd);
445         if (newFd < 0) {
446             LOGE("dup fd fail, fail reason: %{public}s, fd: %{public}d, picName: %{private}s, length: %{public}d",
447                 strerror(errno), fd, picNameCopy.c_str(), byteLenArray[i]);
448             continue;
449         }
450 
451         auto ashmem = Ashmem(newFd, byteLenArray[i]);
452         GetImageDataFromAshmem(picNameCopy, ashmem, pipelineCtx, byteLenArray[i]);
453         ashmem.UnmapAshmem();
454         ashmem.CloseAshmem();
455     }
456 }
457 
GetImageDataFromAshmem(const std::string & picName,Ashmem & ashmem,const RefPtr<PipelineBase> & pipelineContext,int len)458 void SubContainer::GetImageDataFromAshmem(
459     const std::string& picName, Ashmem& ashmem, const RefPtr<PipelineBase>& pipelineContext, int len)
460 {
461     bool ret = ashmem.MapReadOnlyAshmem();
462     // if any exception causes a [return] before [AddSharedImage], the memory image will not show because [RenderImage]
463     // will never be notified to start loading.
464     if (!ret) {
465         LOGE("MapReadOnlyAshmem fail, fail reason: %{public}s, picName: %{private}s, length: %{public}d, "
466              "fd: %{public}d",
467             strerror(errno), picName.c_str(), len, ashmem.GetAshmemFd());
468         return;
469     }
470     const uint8_t* imageData = reinterpret_cast<const uint8_t*>(ashmem.ReadFromAshmem(len, 0));
471     if (imageData == nullptr) {
472         LOGE("imageData is nullptr, errno is: %{public}s, picName: %{private}s, length: %{public}d, fd: %{public}d",
473             strerror(errno), picName.c_str(), len, ashmem.GetAshmemFd());
474         return;
475     }
476     auto context = DynamicCast<PipelineContext>(pipelineContext);
477     CHECK_NULL_VOID(context);
478     RefPtr<SharedImageManager> sharedImageManager = context->GetOrCreateSharedImageManager();
479     if (sharedImageManager) {
480         // read image data from shared memory and save a copy to sharedImageManager
481         sharedImageManager->AddSharedImage(picName, std::vector<uint8_t>(imageData, imageData + len));
482     }
483 }
484 
UpdateCard(const std::string & content,const std::map<std::string,sptr<AppExecFwk::FormAshmem>> & imageDataMap)485 void SubContainer::UpdateCard(
486     const std::string& content, const std::map<std::string, sptr<AppExecFwk::FormAshmem>>& imageDataMap)
487 {
488     if (!frontend_) {
489         LOGE("update card fial due to could not find card front end");
490         return;
491     }
492     if (allowUpdate_) {
493         frontend_->UpdateData(std::move(content));
494         ProcessSharedImage(imageDataMap);
495     }
496 }
497 
UpdateConfiguration()498 void SubContainer::UpdateConfiguration()
499 {
500     if (frontend_) {
501         frontend_->OnMediaFeatureUpdate();
502     }
503 }
504 
Dump(const std::vector<std::string> & params)505 bool SubContainer::Dump(const std::vector<std::string>& params)
506 {
507     if (pipelineContext_) {
508         pipelineContext_->Dump(params);
509         return true;
510     }
511     return false;
512 }
513 } // namespace OHOS::Ace
514