1 /*
2  * Copyright (c) 2023 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 #include "core/common/recorder/node_data_cache.h"
16 
17 #include "core/components_ng/pattern/stage/page_pattern.h"
18 
19 namespace OHOS::Ace::Recorder {
20 constexpr int32_t PAGE_URL_SUFFIX_LENGTH = 3;
21 
GetPageUrlByNode(const RefPtr<NG::FrameNode> & node)22 std::string GetPageUrlByNode(const RefPtr<NG::FrameNode>& node)
23 {
24     auto pageNode = node->GetPageNode();
25     CHECK_NULL_RETURN(pageNode, "");
26     auto pagePattern = pageNode->GetPattern<NG::PagePattern>();
27     CHECK_NULL_RETURN(pagePattern, "");
28     return pagePattern->GetPageUrl();
29 }
30 
GetCurrentPageUrl()31 const std::string GetCurrentPageUrl()
32 {
33     CHECK_RUN_ON(UI);
34     auto container = Container::Current();
35     CHECK_NULL_RETURN(container, "");
36     auto frontEnd = container->GetFrontend();
37     CHECK_NULL_RETURN(frontEnd, "");
38     auto pageUrl = frontEnd->GetCurrentPageUrl();
39     // remove .js suffix if exists
40     if (StringUtils::EndWith(pageUrl, ".js")) {
41         return pageUrl.substr(0, pageUrl.length() - PAGE_URL_SUFFIX_LENGTH);
42     }
43     return pageUrl;
44 }
45 
Get()46 NodeDataCache& NodeDataCache::Get()
47 {
48     static NodeDataCache cache;
49     return cache;
50 }
51 
NodeDataCache()52 NodeDataCache::NodeDataCache()
53 {
54     container_ = std::make_shared<NodeDataContainer>();
55     mergedConfig_ = std::make_shared<MergedConfig>();
56 }
57 
OnPageReady()58 void NodeDataCache::OnPageReady()
59 {
60     prePageUrl_ = pageUrl_;
61     pageUrl_ = GetCurrentPageUrl();
62 }
63 
OnPageShow(const std::string & pageUrl)64 void NodeDataCache::OnPageShow(const std::string& pageUrl)
65 {
66     prePageUrl_ = pageUrl_;
67     pageUrl_ = pageUrl;
68 }
69 
OnBeforePagePop(bool destroy)70 void NodeDataCache::OnBeforePagePop(bool destroy)
71 {
72     if (destroy) {
73         Clear(prePageUrl_);
74     } else {
75         Clear(pageUrl_);
76     }
77     shouldCollectFull_ = false;
78     EventRecorder::Get().SetContainerChanged();
79 }
80 
UpdateConfig(std::shared_ptr<MergedConfig> && mergedConfig)81 void NodeDataCache::UpdateConfig(std::shared_ptr<MergedConfig>&& mergedConfig)
82 {
83     std::unique_lock<std::shared_mutex> lock(configMutex_);
84     mergedConfig_ = mergedConfig;
85     shouldCollectFull_ = false;
86 }
87 
PutString(const RefPtr<NG::FrameNode> & node,const std::string & id,const std::string & value)88 bool NodeDataCache::PutString(const RefPtr<NG::FrameNode>& node, const std::string& id, const std::string& value)
89 {
90     if (id.empty() || value.empty() || value.length() > MAX_DATA_LENGTH) {
91         return false;
92     }
93     if (mergedConfig_->shareNodes.empty()) {
94         return false;
95     }
96     auto pageUrl = GetPageUrlByNode(node);
97     if (pageUrl.empty()) {
98         return false;
99     }
100     std::shared_lock<std::shared_mutex> configLock(configMutex_);
101     auto iter = mergedConfig_->shareNodes.find(pageUrl);
102     if (!shouldCollectFull_ && iter == mergedConfig_->shareNodes.end()) {
103         return false;
104     }
105     if (shouldCollectFull_ || iter->second.find(id) != iter->second.end()) {
106         std::unique_lock<std::shared_mutex> cacheLock(cacheMutex_);
107         auto iter = container_->find(pageUrl);
108         if (iter == container_->end()) {
109             auto pageContainer = std::unordered_map<std::string, std::string>();
110             pageContainer.emplace(id, value);
111             container_->emplace(pageUrl, std::move(pageContainer));
112         } else {
113             if (iter->second.size() >= MAX_SIZE_PER_PAGE) {
114                 return false;
115             }
116             iter->second[id] = value;
117         }
118     }
119     return true;
120 }
121 
PutBool(const RefPtr<NG::FrameNode> & node,const std::string & id,bool value)122 bool NodeDataCache::PutBool(const RefPtr<NG::FrameNode>& node, const std::string& id, bool value)
123 {
124     std::string strVal = value ? "true" : "false";
125     return PutString(node, id, strVal);
126 }
127 
PutInt(const RefPtr<NG::FrameNode> & node,const std::string & id,int value)128 bool NodeDataCache::PutInt(const RefPtr<NG::FrameNode>& node, const std::string& id, int value)
129 {
130     return PutString(node, id, std::to_string(value));
131 }
132 
PutStringArray(const RefPtr<NG::FrameNode> & node,const std::string & id,const std::vector<std::string> & value)133 bool NodeDataCache::PutStringArray(
134     const RefPtr<NG::FrameNode>& node, const std::string& id, const std::vector<std::string>& value)
135 {
136     auto jsonArray = JsonUtil::CreateArray(true);
137     for (size_t i = 0; i < value.size(); i++) {
138         jsonArray->Put(std::to_string(i).c_str(), value.at(i).c_str());
139     }
140     return PutString(node, id, jsonArray->ToString());
141 }
142 
PutMultiple(const RefPtr<NG::FrameNode> & node,const std::string & id,const std::string & name,bool value)143 bool NodeDataCache::PutMultiple(
144     const RefPtr<NG::FrameNode>& node, const std::string& id, const std::string& name, bool value)
145 {
146     auto json = JsonUtil::Create(true);
147     json->Put(KEY_TEXT, name.c_str());
148     json->Put(KEY_CHECKED, value);
149     return PutString(node, id, json->ToString());
150 }
151 
PutMultiple(const RefPtr<NG::FrameNode> & node,const std::string & id,const std::string & name,int index)152 bool NodeDataCache::PutMultiple(
153     const RefPtr<NG::FrameNode>& node, const std::string& id, const std::string& name, int index)
154 {
155     auto json = JsonUtil::Create(true);
156     json->Put(KEY_TEXT, name.c_str());
157     json->Put(KEY_INDEX, index);
158     return PutString(node, id, json->ToString());
159 }
160 
PutMultiple(const RefPtr<NG::FrameNode> & node,const std::string & id,const std::string & name,const std::vector<std::string> & value)161 bool NodeDataCache::PutMultiple(const RefPtr<NG::FrameNode>& node, const std::string& id, const std::string& name,
162     const std::vector<std::string>& value)
163 {
164     auto json = JsonUtil::Create(true);
165     json->Put(KEY_TEXT, name.c_str());
166     auto jsonArray = JsonUtil::CreateArray(true);
167     for (size_t i = 0; i < value.size(); i++) {
168         jsonArray->Put(std::to_string(i).c_str(), value.at(i).c_str());
169     }
170     json->Put(KEY_TEXT_ARRAY, jsonArray);
171     return PutString(node, id, json->ToString());
172 }
173 
GetNodeData(const std::string & pageUrl,std::unordered_map<std::string,std::string> & nodes)174 void NodeDataCache::GetNodeData(const std::string& pageUrl, std::unordered_map<std::string, std::string>& nodes)
175 {
176     if (pageUrl.empty()) {
177         return;
178     }
179     std::shared_lock<std::shared_mutex> lock(cacheMutex_);
180     auto iter = container_->find(pageUrl);
181     if (iter == container_->end()) {
182         return;
183     }
184     for (auto nodeIter = nodes.begin(); nodeIter != nodes.end(); nodeIter++) {
185         auto it = iter->second.find(nodeIter->first);
186         if (it != iter->second.end()) {
187             nodes[it->first] = it->second;
188         }
189     }
190 }
191 
Clear(const std::string & pageUrl)192 void NodeDataCache::Clear(const std::string& pageUrl)
193 {
194     if (pageUrl.empty()) {
195         return;
196     }
197     std::unique_lock<std::shared_mutex> lock(cacheMutex_);
198     auto iter = container_->find(pageUrl);
199     if (iter != container_->end()) {
200         container_->erase(iter);
201     }
202 }
203 
Reset()204 void NodeDataCache::Reset()
205 {
206     std::unique_lock<std::shared_mutex> lock(cacheMutex_);
207     container_->clear();
208     pageUrl_ = "";
209     prePageUrl_ = "";
210 }
211 
GetExposureCfg(const std::string & pageUrl,const std::string & inspectId,ExposureCfg & cfg)212 void NodeDataCache::GetExposureCfg(const std::string& pageUrl, const std::string& inspectId, ExposureCfg& cfg)
213 {
214     if (pageUrl.empty()) {
215         return;
216     }
217     std::shared_lock<std::shared_mutex> configLock(configMutex_);
218     auto iter = mergedConfig_->exposureNodes.find(pageUrl);
219     if (iter == mergedConfig_->exposureNodes.end()) {
220         return;
221     }
222     auto cfgIter = iter->second.find({ inspectId, 0.0, 0 });
223     if (cfgIter == iter->second.end()) {
224         return;
225     }
226     cfg.id = cfgIter->id;
227     cfg.ratio = cfgIter->ratio;
228     cfg.duration = cfgIter->duration;
229 }
230 } // namespace OHOS::Ace::Recorder
231