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