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