1 /*
2 * Copyright (c) 2021 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 "frameworks/bridge/card_frontend/card_frontend.h"
17
18 #include <memory>
19 #include <vector>
20
21 #include "base/log/event_report.h"
22 #include "base/utils/utils.h"
23 #include "core/common/thread_checker.h"
24 #include "frameworks/bridge/common/utils/utils.h"
25
26 namespace OHOS::Ace {
27 namespace {
28
29 const char MANIFEST_JSON[] = "manifest.json";
30 const char FILE_TYPE_JSON[] = ".json";
31
32 } // namespace
33
~CardFrontend()34 CardFrontend::~CardFrontend()
35 {
36 LOG_DESTROY();
37 }
38
Initialize(FrontendType type,const RefPtr<TaskExecutor> & taskExecutor)39 bool CardFrontend::Initialize(FrontendType type, const RefPtr<TaskExecutor>& taskExecutor)
40 {
41 type_ = type;
42 taskExecutor_ = taskExecutor;
43 delegate_ = AceType::MakeRefPtr<Framework::CardFrontendDelegate>();
44 manifestParser_ = AceType::MakeRefPtr<Framework::ManifestParser>();
45 return true;
46 }
47
Destroy()48 void CardFrontend::Destroy()
49 {
50 CHECK_RUN_ON(JS);
51 TAG_LOGI(AceLogTag::ACE_FORM, "CardFrontend Destroy begin.");
52 parseJsCard_.Reset();
53 delegate_.Reset();
54 eventHandler_.Reset();
55 }
56
AttachPipelineContext(const RefPtr<PipelineBase> & context)57 void CardFrontend::AttachPipelineContext(const RefPtr<PipelineBase>& context)
58 {
59 auto pipelineContext = DynamicCast<PipelineContext>(context);
60 CHECK_NULL_VOID(delegate_);
61 CHECK_NULL_VOID(pipelineContext);
62 eventHandler_ = AceType::MakeRefPtr<CardEventHandler>(delegate_);
63 pipelineContext->RegisterEventHandler(eventHandler_);
64 holder_.Attach(context);
65 delegate_->GetJsAccessibilityManager()->SetPipelineContext(context);
66 delegate_->GetJsAccessibilityManager()->InitializeCallback();
67 }
68
SetAssetManager(const RefPtr<AssetManager> & assetManager)69 void CardFrontend::SetAssetManager(const RefPtr<AssetManager>& assetManager)
70 {
71 assetManager_ = assetManager;
72 }
73
ParseManifest() const74 void CardFrontend::ParseManifest() const
75 {
76 std::call_once(onceFlag_, [this]() {
77 std::string jsonContent;
78 if (!Framework::GetAssetContentImpl(assetManager_, MANIFEST_JSON, jsonContent)) {
79 TAG_LOGW(AceLogTag::ACE_FORM, "RunPage parse manifest.json failed");
80 return;
81 }
82 manifestParser_->Parse(jsonContent);
83 });
84 }
85
RunPage(const std::string & url,const std::string & params)86 UIContentErrorCode CardFrontend::RunPage(const std::string& url, const std::string& params)
87 {
88 std::string urlPath;
89 if (GetFormSrc().empty()) {
90 ParseManifest();
91 if (!url.empty()) {
92 urlPath = manifestParser_->GetRouter()->GetPagePath(url, FILE_TYPE_JSON);
93 }
94 if (urlPath.empty()) {
95 urlPath = manifestParser_->GetRouter()->GetEntry(FILE_TYPE_JSON);
96 }
97 } else {
98 urlPath = GetFormSrcPath(GetFormSrc(), FILE_TYPE_JSON);
99 }
100 if (urlPath.empty()) {
101 TAG_LOGW(AceLogTag::ACE_FORM, "fail to run page due to path url is empty");
102 return UIContentErrorCode::NULL_URL;
103 }
104 taskExecutor_->PostTask(
105 [weak = AceType::WeakClaim(this), urlPath, params] {
106 auto frontend = weak.Upgrade();
107 if (frontend) {
108 frontend->LoadPage(urlPath, params);
109 }
110 },
111 TaskExecutor::TaskType::JS, "ArkUICardFrontendRunPage");
112
113 return UIContentErrorCode::NO_ERRORS;
114 }
115
GetFormSrcPath(const std::string & uri,const std::string & suffix) const116 std::string CardFrontend::GetFormSrcPath(const std::string& uri, const std::string& suffix) const
117 {
118 if (uri.empty()) {
119 return "";
120 }
121 // the case uri is starts with "/" and "/" is the mainPage
122 if (uri.size() != 0) {
123 return uri + suffix;
124 }
125
126 return "";
127 }
128
GetPage(int32_t pageId) const129 RefPtr<AcePage> CardFrontend::GetPage(int32_t pageId) const
130 {
131 CHECK_NULL_RETURN(delegate_, nullptr);
132 return delegate_->GetPage();
133 }
134
GetWindowConfig()135 WindowConfig& CardFrontend::GetWindowConfig()
136 {
137 ParseManifest();
138 if (GetFormSrc().empty()) {
139 if (!manifestParser_) {
140 static WindowConfig windowConfig;
141 TAG_LOGW(AceLogTag::ACE_FORM, "manifestParser is null, return default config");
142 return windowConfig;
143 }
144 return manifestParser_->GetWindowConfig();
145 } else {
146 return GetCardWindowConfig();
147 }
148 }
149
LoadPage(const std::string & urlPath,const std::string & params)150 void CardFrontend::LoadPage(const std::string& urlPath, const std::string& params)
151 {
152 CHECK_RUN_ON(JS);
153 CHECK_NULL_VOID(delegate_);
154 auto page = delegate_->CreatePage(0, urlPath);
155 page->SetPageParams(params);
156 page->SetFlushCallback([weak = WeakClaim(this)](const RefPtr<Framework::JsAcePage>& page) {
157 auto front = weak.Upgrade();
158 if (front) {
159 front->OnPageLoaded(page);
160 }
161 });
162
163 std::string content;
164 if (!Framework::GetAssetContentImpl(assetManager_, urlPath, content)) {
165 TAG_LOGW(AceLogTag::ACE_FORM, "Failed to load page");
166 return;
167 }
168 ParsePage(holder_.Get(), content, params, page);
169 }
170
ParsePage(const RefPtr<PipelineBase> & context,const std::string & pageContent,const std::string & params,const RefPtr<Framework::JsAcePage> & page)171 void CardFrontend::ParsePage(const RefPtr<PipelineBase>& context, const std::string& pageContent,
172 const std::string& params, const RefPtr<Framework::JsAcePage>& page)
173 {
174 CHECK_RUN_ON(JS);
175 auto rootBody = Framework::ParseFileData(pageContent);
176 CHECK_NULL_VOID(rootBody);
177
178 const auto& rootTemplate = rootBody->GetValue("template");
179 parseJsCard_ = AceType::MakeRefPtr<Framework::JsCardParser>(context, assetManager_, std::move(rootBody));
180 if (!parseJsCard_->Initialize()) {
181 TAG_LOGW(AceLogTag::ACE_FORM, "js card parser initialize fail");
182 return;
183 }
184 parseJsCard_->SetColorMode(colorMode_);
185 parseJsCard_->SetDensity(density_);
186 parseJsCard_->LoadImageInfo();
187 parseJsCard_->SetCardHapPath(cardHapPath_);
188 parseJsCard_->CreateDomNode(page, rootTemplate, -1);
189 parseJsCard_->ResetNodeId();
190 page->FlushCommands();
191 if (!params.empty()) {
192 parseJsCard_->UpdatePageData(params, page);
193 }
194 }
195
OnPageLoaded(const RefPtr<Framework::JsAcePage> & page)196 void CardFrontend::OnPageLoaded(const RefPtr<Framework::JsAcePage>& page)
197 {
198 CHECK_RUN_ON(JS);
199 // Pop all JS command and execute them in UI thread.
200 auto jsCommands = std::make_shared<std::vector<RefPtr<Framework::JsCommand>>>();
201 page->PopAllCommands(*jsCommands);
202 page->SetPipelineContext(holder_.Get());
203 taskExecutor_->PostTask(
204 [weak = AceType::WeakClaim(this), page, jsCommands] {
205 auto frontend = weak.Upgrade();
206 CHECK_NULL_VOID(frontend);
207 // Flush all JS commands.
208 for (const auto& command : *jsCommands) {
209 command->Execute(page);
210 }
211
212 auto pipelineContext = AceType::DynamicCast<PipelineContext>(frontend->holder_.Get());
213 CHECK_NULL_VOID(pipelineContext);
214 auto minSdk = frontend->manifestParser_->GetMinPlatformVersion();
215 pipelineContext->SetMinPlatformVersion(minSdk);
216
217 auto document = page->GetDomDocument();
218 if (frontend->pageLoaded_) {
219 page->ClearShowCommand();
220 std::vector<NodeId> dirtyNodes;
221 page->PopAllDirtyNodes(dirtyNodes);
222 if (dirtyNodes.empty()) {
223 return;
224 }
225 auto rootNodeId = dirtyNodes.front();
226 if (rootNodeId == DOM_ROOT_NODE_ID_BASE) {
227 auto patchComponent = page->BuildPagePatch(rootNodeId);
228 if (patchComponent) {
229 pipelineContext->ScheduleUpdate(patchComponent);
230 }
231 }
232 if (document) {
233 // When a component is configured with "position: fixed", there is a proxy node in root tree
234 // instead of the real composed node. So here updates the real composed node.
235 for (int32_t nodeId : document->GetProxyRelatedNodes()) {
236 auto patchComponent = page->BuildPagePatch(nodeId);
237 if (patchComponent) {
238 pipelineContext->ScheduleUpdate(patchComponent);
239 }
240 }
241 }
242 return;
243 }
244
245 // Just clear all dirty nodes.
246 page->ClearAllDirtyNodes();
247 if (document) {
248 document->HandleComponentPostBinding();
249 }
250 if (pipelineContext->GetAccessibilityManager()) {
251 pipelineContext->GetAccessibilityManager()->HandleComponentPostBinding();
252 }
253 if (pipelineContext->CanPushPage()) {
254 pipelineContext->PushPage(page->BuildPage(page->GetUrl()));
255 frontend->pageLoaded_ = true;
256 if (frontend->delegate_) {
257 frontend->delegate_->GetJsAccessibilityManager()->SetRunningPage(page);
258 }
259 }
260 },
261 TaskExecutor::TaskType::UI, "ArkUICardFrontendPageLoaded");
262 taskExecutor_->PostTask(
263 [weak = AceType::WeakClaim(this)] {
264 auto frontend = weak.Upgrade();
265 CHECK_NULL_VOID(frontend);
266 frontend->FireFormVisiableCallback();
267 },
268 TaskExecutor::TaskType::UI, "ArkUICardFrontendFireFormVisiable");
269 }
270
UpdateData(const std::string & dataList)271 void CardFrontend::UpdateData(const std::string& dataList)
272 {
273 taskExecutor_->PostTask(
274 [weak = AceType::WeakClaim(this), dataList] {
275 auto frontend = weak.Upgrade();
276 if (frontend) {
277 frontend->UpdatePageData(dataList);
278 }
279 },
280 TaskExecutor::TaskType::JS, "ArkUICardFrontendUpdatePageData");
281 }
282
UpdatePageData(const std::string & dataList)283 void CardFrontend::UpdatePageData(const std::string& dataList)
284 {
285 CHECK_RUN_ON(JS);
286 if (!delegate_ || !parseJsCard_) {
287 TAG_LOGW(AceLogTag::ACE_FORM, "the delegate or parseJsCard is null");
288 return;
289 }
290 parseJsCard_->UpdatePageData(dataList, delegate_->GetPage());
291 }
292
SetColorMode(ColorMode colorMode)293 void CardFrontend::SetColorMode(ColorMode colorMode)
294 {
295 taskExecutor_->PostTask(
296 [weak = AceType::WeakClaim(this), colorMode]() {
297 auto frontend = weak.Upgrade();
298 if (frontend) {
299 frontend->colorMode_ = colorMode;
300 if (!frontend->delegate_ || !frontend->parseJsCard_) {
301 return;
302 }
303 frontend->parseJsCard_->SetColorMode(frontend->colorMode_);
304 frontend->OnMediaFeatureUpdate();
305 }
306 },
307 TaskExecutor::TaskType::JS, "ArkUICardFrontendSetColorMode");
308 }
309
RebuildAllPages()310 void CardFrontend::RebuildAllPages()
311 {
312 CHECK_NULL_VOID(delegate_);
313 auto page = delegate_->GetPage();
314 taskExecutor_->PostTask(
315 [weakPage = WeakPtr<Framework::JsAcePage>(page)] {
316 auto page = weakPage.Upgrade();
317 CHECK_NULL_VOID(page);
318 auto domDoc = page->GetDomDocument();
319 CHECK_NULL_VOID(domDoc);
320 auto rootNode = domDoc->GetDOMNodeById(domDoc->GetRootNodeId());
321 CHECK_NULL_VOID(rootNode);
322 rootNode->UpdateStyleWithChildren();
323 },
324 TaskExecutor::TaskType::UI, "ArkUICardFrontendRebuildAllPages");
325 }
326
OnSurfaceChanged(int32_t width,int32_t height)327 void CardFrontend::OnSurfaceChanged(int32_t width, int32_t height)
328 {
329 taskExecutor_->PostTask(
330 [weak = AceType::WeakClaim(this), width, height] {
331 auto frontend = weak.Upgrade();
332 if (frontend) {
333 frontend->HandleSurfaceChanged(width, height);
334 }
335 },
336 TaskExecutor::TaskType::JS, "ArkUICardFrontendSurfaceChanged");
337 }
338
HandleSurfaceChanged(int32_t width,int32_t height)339 void CardFrontend::HandleSurfaceChanged(int32_t width, int32_t height)
340 {
341 CHECK_RUN_ON(JS);
342 CHECK_NULL_VOID(parseJsCard_);
343 parseJsCard_->OnSurfaceChanged(width, height);
344 OnMediaFeatureUpdate();
345 }
346
OnMediaFeatureUpdate()347 void CardFrontend::OnMediaFeatureUpdate()
348 {
349 CHECK_RUN_ON(JS);
350 CHECK_NULL_VOID(delegate_);
351 CHECK_NULL_VOID(parseJsCard_);
352 parseJsCard_->UpdateStyle(delegate_->GetPage());
353 }
354 } // namespace OHOS::Ace
355