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