• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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