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