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 "core/common/thread_checker.h"
23 #include "frameworks/bridge/common/utils/utils.h"
24
25 namespace OHOS::Ace {
26 namespace {
27
28 const char MANIFEST_JSON[] = "manifest.json";
29 const char FILE_TYPE_JSON[] = ".json";
30
31 } // namespace
32
~CardFrontend()33 CardFrontend::~CardFrontend()
34 {
35 LOG_DESTROY();
36 }
37
Initialize(FrontendType type,const RefPtr<TaskExecutor> & taskExecutor)38 bool CardFrontend::Initialize(FrontendType type, const RefPtr<TaskExecutor>& taskExecutor)
39 {
40 type_ = type;
41 taskExecutor_ = taskExecutor;
42 delegate_ = AceType::MakeRefPtr<Framework::CardFrontendDelegate>();
43 manifestParser_ = AceType::MakeRefPtr<Framework::ManifestParser>();
44 return true;
45 }
46
Destroy()47 void CardFrontend::Destroy()
48 {
49 CHECK_RUN_ON(JS);
50 LOGI("CardFrontend Destroy begin.");
51 parseJsCard_.Reset();
52 delegate_.Reset();
53 eventHandler_.Reset();
54 LOGI("CardFrontend Destroy end.");
55 }
56
AttachPipelineContext(const RefPtr<PipelineContext> & context)57 void CardFrontend::AttachPipelineContext(const RefPtr<PipelineContext>& context)
58 {
59 if (!delegate_) {
60 return;
61 }
62 eventHandler_ = AceType::MakeRefPtr<CardEventHandler>(delegate_);
63 context->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 LOGE("RunPage parse manifest.json failed");
80 EventReport::SendFormException(FormExcepType::RUN_PAGE_ERR);
81 return;
82 }
83 manifestParser_->Parse(jsonContent);
84 });
85 }
86
RunPage(int32_t pageId,const std::string & url,const std::string & params)87 void CardFrontend::RunPage(int32_t pageId, const std::string& url, const std::string& params)
88 {
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 if (!delegate_) {
135 return nullptr;
136 }
137 return delegate_->GetPage();
138 }
139
GetWindowConfig()140 WindowConfig& CardFrontend::GetWindowConfig()
141 {
142 ParseManifest();
143 return manifestParser_->GetWindowConfig();
144 }
145
LoadPage(const std::string & urlPath,const std::string & params)146 void CardFrontend::LoadPage(const std::string& urlPath, const std::string& params)
147 {
148 CHECK_RUN_ON(JS);
149 if (!delegate_) {
150 return;
151 }
152 auto page = delegate_->CreatePage(0, urlPath);
153 page->SetPageParams(params);
154 page->SetFlushCallback([weak = WeakClaim(this)](const RefPtr<Framework::JsAcePage>& page) {
155 auto front = weak.Upgrade();
156 if (front) {
157 front->OnPageLoaded(page);
158 }
159 });
160
161 std::string content;
162 if (!Framework::GetAssetContentImpl(assetManager_, urlPath, content)) {
163 LOGE("Failed to load page");
164 EventReport::SendFormException(FormExcepType::LOAD_PAGE_ERR);
165 return;
166 }
167 ParsePage(holder_.Get(), content, params, page);
168 }
169
ParsePage(const RefPtr<PipelineContext> & context,const std::string & pageContent,const std::string & params,const RefPtr<Framework::JsAcePage> & page)170 void CardFrontend::ParsePage(const RefPtr<PipelineContext>& context, const std::string& pageContent,
171 const std::string& params, const RefPtr<Framework::JsAcePage>& page)
172 {
173 CHECK_RUN_ON(JS);
174 auto rootBody = Framework::ParseFileData(pageContent);
175 if (!rootBody) {
176 LOGE("parse index json error");
177 return;
178 }
179
180 const auto& rootTemplate = rootBody->GetValue("template");
181 parseJsCard_ = AceType::MakeRefPtr<Framework::JsCardParser>(context, assetManager_, std::move(rootBody));
182 if (!parseJsCard_->Initialize()) {
183 LOGE("js card parser initialize fail");
184 return;
185 }
186 parseJsCard_->SetColorMode(colorMode_);
187 parseJsCard_->SetDensity(density_);
188 parseJsCard_->LoadImageInfo();
189 parseJsCard_->SetCardHapPath(cardHapPath_);
190 parseJsCard_->CreateDomNode(page, rootTemplate, -1);
191 parseJsCard_->ResetNodeId();
192 page->FlushCommands();
193 if (!params.empty()) {
194 parseJsCard_->UpdatePageData(params, page);
195 }
196 }
197
OnPageLoaded(const RefPtr<Framework::JsAcePage> & page)198 void CardFrontend::OnPageLoaded(const RefPtr<Framework::JsAcePage>& page)
199 {
200 CHECK_RUN_ON(JS);
201 // Pop all JS command and execute them in UI thread.
202 auto jsCommands = std::make_shared<std::vector<RefPtr<Framework::JsCommand>>>();
203 page->PopAllCommands(*jsCommands);
204 page->SetPipelineContext(holder_.Get());
205 taskExecutor_->PostTask(
206 [weak = AceType::WeakClaim(this), page, jsCommands] {
207 auto frontend = weak.Upgrade();
208 if (!frontend) {
209 return;
210 }
211 // Flush all JS commands.
212 for (const auto& command : *jsCommands) {
213 command->Execute(page);
214 }
215
216 auto pipelineContext = frontend->holder_.Get();
217 auto minSdk = frontend->manifestParser_->GetMinPlatformVersion();
218 pipelineContext->SetMinPlatformVersion(minSdk);
219
220 auto document = page->GetDomDocument();
221 if (frontend->pageLoaded_) {
222 page->ClearShowCommand();
223 std::vector<NodeId> dirtyNodes;
224 page->PopAllDirtyNodes(dirtyNodes);
225 if (dirtyNodes.empty()) {
226 return;
227 }
228 auto rootNodeId = dirtyNodes.front();
229 if (rootNodeId == DOM_ROOT_NODE_ID_BASE) {
230 auto patchComponent = page->BuildPagePatch(rootNodeId);
231 if (patchComponent) {
232 pipelineContext->ScheduleUpdate(patchComponent);
233 }
234 }
235 if (document) {
236 // When a component is configured with "position: fixed", there is a proxy node in root tree
237 // instead of the real composed node. So here updates the real composed node.
238 for (int32_t nodeId : document->GetProxyRelatedNodes()) {
239 auto patchComponent = page->BuildPagePatch(nodeId);
240 if (patchComponent) {
241 pipelineContext->ScheduleUpdate(patchComponent);
242 }
243 }
244 }
245 return;
246 }
247
248 // Just clear all dirty nodes.
249 page->ClearAllDirtyNodes();
250 if (document) {
251 document->HandleComponentPostBinding();
252 }
253 if (pipelineContext->GetAccessibilityManager()) {
254 pipelineContext->GetAccessibilityManager()->HandleComponentPostBinding();
255 }
256 if (pipelineContext->CanPushPage()) {
257 pipelineContext->PushPage(page->BuildPage(page->GetUrl()));
258 frontend->pageLoaded_ = true;
259 if (frontend->delegate_) {
260 frontend->delegate_->GetJsAccessibilityManager()->SetRunningPage(page);
261 }
262 }
263 },
264 TaskExecutor::TaskType::UI);
265 }
266
UpdateData(const std::string & dataList)267 void CardFrontend::UpdateData(const std::string& dataList)
268 {
269 taskExecutor_->PostTask(
270 [weak = AceType::WeakClaim(this), dataList] {
271 auto frontend = weak.Upgrade();
272 if (frontend) {
273 frontend->UpdatePageData(dataList);
274 }
275 },
276 TaskExecutor::TaskType::JS);
277 }
278
UpdatePageData(const std::string & dataList)279 void CardFrontend::UpdatePageData(const std::string& dataList)
280 {
281 CHECK_RUN_ON(JS);
282 if (!delegate_ || !parseJsCard_) {
283 LOGE("the delegate or parseJsCard is null");
284 EventReport::SendFormException(FormExcepType::UPDATE_PAGE_ERR);
285 return;
286 }
287 parseJsCard_->UpdatePageData(dataList, delegate_->GetPage());
288 }
289
SetColorMode(ColorMode colorMode)290 void CardFrontend::SetColorMode(ColorMode colorMode)
291 {
292 taskExecutor_->PostTask(
293 [weak = AceType::WeakClaim(this), colorMode]() {
294 auto frontend = weak.Upgrade();
295 if (frontend) {
296 frontend->colorMode_ = colorMode;
297 if (!frontend->delegate_ || !frontend->parseJsCard_) {
298 LOGI("the delegate is null");
299 return;
300 }
301 frontend->parseJsCard_->SetColorMode(frontend->colorMode_);
302 frontend->OnMediaFeatureUpdate();
303 } else {
304 LOGE("card frontend is nullptr");
305 }
306 },
307 TaskExecutor::TaskType::JS);
308 }
309
RebuildAllPages()310 void CardFrontend::RebuildAllPages()
311 {
312 if (!delegate_) {
313 LOGI("the delegate is null");
314 return;
315 }
316 auto page = delegate_->GetPage();
317 taskExecutor_->PostTask(
318 [weakPage = WeakPtr<Framework::JsAcePage>(page)] {
319 auto page = weakPage.Upgrade();
320 if (!page) {
321 return;
322 }
323 auto domDoc = page->GetDomDocument();
324 if (!domDoc) {
325 return;
326 }
327 auto rootNode = domDoc->GetDOMNodeById(domDoc->GetRootNodeId());
328 if (!rootNode) {
329 return;
330 }
331 rootNode->UpdateStyleWithChildren();
332 },
333 TaskExecutor::TaskType::UI);
334 }
335
OnSurfaceChanged(int32_t width,int32_t height)336 void CardFrontend::OnSurfaceChanged(int32_t width, int32_t height)
337 {
338 taskExecutor_->PostTask(
339 [weak = AceType::WeakClaim(this), width, height] {
340 auto frontend = weak.Upgrade();
341 if (frontend) {
342 frontend->HandleSurfaceChanged(width, height);
343 }
344 },
345 TaskExecutor::TaskType::JS);
346 }
347
HandleSurfaceChanged(int32_t width,int32_t height)348 void CardFrontend::HandleSurfaceChanged(int32_t width, int32_t height)
349 {
350 CHECK_RUN_ON(JS);
351 if (!parseJsCard_) {
352 LOGE("the parser is null");
353 return;
354 }
355 parseJsCard_->OnSurfaceChanged(width, height);
356 OnMediaFeatureUpdate();
357 }
358
OnMediaFeatureUpdate()359 void CardFrontend::OnMediaFeatureUpdate()
360 {
361 CHECK_RUN_ON(JS);
362 if (!delegate_ || !parseJsCard_) {
363 LOGE("the delegate or parser is null");
364 return;
365 }
366 parseJsCard_->UpdateStyle(delegate_->GetPage());
367 }
368
369 } // namespace OHOS::Ace
370