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
91 std::string urlPath;
92 if (GetFormSrc().empty()) {
93 ParseManifest();
94 if (!url.empty()) {
95 urlPath = manifestParser_->GetRouter()->GetPagePath(url, FILE_TYPE_JSON);
96 }
97 if (urlPath.empty()) {
98 urlPath = manifestParser_->GetRouter()->GetEntry(FILE_TYPE_JSON);
99 }
100 } else {
101 urlPath = GetFormSrcPath(GetFormSrc(), FILE_TYPE_JSON);
102 }
103 if (urlPath.empty()) {
104 LOGE("fail to run page due to path url is empty");
105 EventReport::SendFormException(FormExcepType::RUN_PAGE_ERR);
106 return;
107 }
108 taskExecutor_->PostTask(
109 [weak = AceType::WeakClaim(this), urlPath, params] {
110 auto frontend = weak.Upgrade();
111 if (frontend) {
112 frontend->LoadPage(urlPath, params);
113 }
114 },
115 TaskExecutor::TaskType::JS);
116 }
117
GetFormSrcPath(const std::string & uri,const std::string & suffix) const118 std::string CardFrontend::GetFormSrcPath(const std::string& uri, const std::string& suffix) const
119 {
120 if (uri.empty()) {
121 LOGW("page uri is empty");
122 return "";
123 }
124 // the case uri is starts with "/" and "/" is the mainPage
125 if (uri.size() != 0) {
126 return uri + suffix;
127 }
128
129 LOGE("can't find this page %{private}s path", uri.c_str());
130 return "";
131 }
132
GetPage(int32_t pageId) const133 RefPtr<AcePage> CardFrontend::GetPage(int32_t pageId) const
134 {
135 CHECK_NULL_RETURN_NOLOG(delegate_, nullptr);
136 return delegate_->GetPage();
137 }
138
GetWindowConfig()139 WindowConfig& CardFrontend::GetWindowConfig()
140 {
141 ParseManifest();
142 if (GetFormSrc().empty()) {
143 return manifestParser_->GetWindowConfig();
144 } else {
145 return GetCardWindowConfig();
146 }
147 }
148
LoadPage(const std::string & urlPath,const std::string & params)149 void CardFrontend::LoadPage(const std::string& urlPath, const std::string& params)
150 {
151 CHECK_RUN_ON(JS);
152 CHECK_NULL_VOID_NOLOG(delegate_);
153 auto page = delegate_->CreatePage(0, urlPath);
154 page->SetPageParams(params);
155 page->SetFlushCallback([weak = WeakClaim(this)](const RefPtr<Framework::JsAcePage>& page) {
156 auto front = weak.Upgrade();
157 if (front) {
158 front->OnPageLoaded(page);
159 }
160 });
161
162 std::string content;
163 if (!Framework::GetAssetContentImpl(assetManager_, urlPath, content)) {
164 LOGE("Failed to load page");
165 EventReport::SendFormException(FormExcepType::LOAD_PAGE_ERR);
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 LOGE("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_NOLOG(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);
262 }
263
UpdateData(const std::string & dataList)264 void CardFrontend::UpdateData(const std::string& dataList)
265 {
266 taskExecutor_->PostTask(
267 [weak = AceType::WeakClaim(this), dataList] {
268 auto frontend = weak.Upgrade();
269 if (frontend) {
270 frontend->UpdatePageData(dataList);
271 }
272 },
273 TaskExecutor::TaskType::JS);
274 }
275
UpdatePageData(const std::string & dataList)276 void CardFrontend::UpdatePageData(const std::string& dataList)
277 {
278 CHECK_RUN_ON(JS);
279 if (!delegate_ || !parseJsCard_) {
280 LOGE("the delegate or parseJsCard is null");
281 EventReport::SendFormException(FormExcepType::UPDATE_PAGE_ERR);
282 return;
283 }
284 parseJsCard_->UpdatePageData(dataList, delegate_->GetPage());
285 }
286
SetColorMode(ColorMode colorMode)287 void CardFrontend::SetColorMode(ColorMode colorMode)
288 {
289 taskExecutor_->PostTask(
290 [weak = AceType::WeakClaim(this), colorMode]() {
291 auto frontend = weak.Upgrade();
292 if (frontend) {
293 frontend->colorMode_ = colorMode;
294 if (!frontend->delegate_ || !frontend->parseJsCard_) {
295 LOGI("the delegate is null");
296 return;
297 }
298 frontend->parseJsCard_->SetColorMode(frontend->colorMode_);
299 frontend->OnMediaFeatureUpdate();
300 } else {
301 LOGE("card frontend is nullptr");
302 }
303 },
304 TaskExecutor::TaskType::JS);
305 }
306
RebuildAllPages()307 void CardFrontend::RebuildAllPages()
308 {
309 CHECK_NULL_VOID(delegate_);
310 auto page = delegate_->GetPage();
311 taskExecutor_->PostTask(
312 [weakPage = WeakPtr<Framework::JsAcePage>(page)] {
313 auto page = weakPage.Upgrade();
314 CHECK_NULL_VOID_NOLOG(page);
315 auto domDoc = page->GetDomDocument();
316 CHECK_NULL_VOID_NOLOG(domDoc);
317 auto rootNode = domDoc->GetDOMNodeById(domDoc->GetRootNodeId());
318 CHECK_NULL_VOID_NOLOG(rootNode);
319 rootNode->UpdateStyleWithChildren();
320 },
321 TaskExecutor::TaskType::UI);
322 }
323
OnSurfaceChanged(int32_t width,int32_t height)324 void CardFrontend::OnSurfaceChanged(int32_t width, int32_t height)
325 {
326 taskExecutor_->PostTask(
327 [weak = AceType::WeakClaim(this), width, height] {
328 auto frontend = weak.Upgrade();
329 if (frontend) {
330 frontend->HandleSurfaceChanged(width, height);
331 }
332 },
333 TaskExecutor::TaskType::JS);
334 }
335
HandleSurfaceChanged(int32_t width,int32_t height)336 void CardFrontend::HandleSurfaceChanged(int32_t width, int32_t height)
337 {
338 CHECK_RUN_ON(JS);
339 CHECK_NULL_VOID(parseJsCard_);
340 parseJsCard_->OnSurfaceChanged(width, height);
341 OnMediaFeatureUpdate();
342 }
343
OnMediaFeatureUpdate()344 void CardFrontend::OnMediaFeatureUpdate()
345 {
346 CHECK_RUN_ON(JS);
347 CHECK_NULL_VOID(delegate_);
348 CHECK_NULL_VOID(parseJsCard_);
349 parseJsCard_->UpdateStyle(delegate_->GetPage());
350 }
351
352 } // namespace OHOS::Ace
353