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