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