• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/event_recorder.h"
16 
17 #include <cstdint>
18 #include <optional>
19 #include <vector>
20 
21 #include "core/common/container.h"
22 #include "core/common/recorder/event_controller.h"
23 #include "core/common/recorder/event_definition.h"
24 #include "core/common/recorder/node_data_cache.h"
25 #include "core/pipeline_ng/pipeline_context.h"
26 #include "ui/base/utils/utils.h"
27 
28 namespace OHOS::Ace::Recorder {
29 namespace {
30 constexpr char IGNORE_WINDOW_NAME[] = "$HA_FLOAT_WINDOW$";
31 
FillExtraTextIfNeed(EventType eventType,EventParamsBuilder & builder,const RefPtr<NG::FrameNode> & host)32 void FillExtraTextIfNeed(EventType eventType, EventParamsBuilder& builder, const RefPtr<NG::FrameNode>& host)
33 {
34     if (eventType != EventType::CLICK || !builder.GetValue(KEY_TEXT).empty()) {
35         return;
36     }
37     if (!EventRecorder::Get().IsRecordEnable(Recorder::EventCategory::CATEGORY_PARENT_TEXT)) {
38         return;
39     }
40     if (!host->GetChildren().empty()) {
41         return;
42     }
43     auto parent = host->GetParentFrameNode();
44     CHECK_NULL_VOID(parent);
45     auto property = parent->GetAccessibilityProperty<NG::AccessibilityProperty>();
46     CHECK_NULL_VOID(property);
47     builder.SetExtra(KEY_EXTRA_TEXT, property->GetGroupText(true));
48 }
49 
GetNavDstNameByNode(const RefPtr<NG::FrameNode> & host)50 std::string GetNavDstNameByNode(const RefPtr<NG::FrameNode>& host)
51 {
52     auto context = host->GetContext();
53     CHECK_NULL_RETURN(context, "");
54     return context->GetCurrentPageNameCallback();
55 }
56 } // namespace
57 
IsCacheAvaliable()58 bool IsCacheAvaliable()
59 {
60     return EventRecorder::Get().IsComponentRecordEnable() && !NodeDataCache::Get().IsShareNodeEmpty();
61 }
62 
EventParamsBuilder()63 EventParamsBuilder::EventParamsBuilder()
64 {
65     params_ = std::make_shared<std::unordered_map<std::string, std::string>>();
66 }
67 
SetEventType(EventType eventType)68 EventParamsBuilder& EventParamsBuilder::SetEventType(EventType eventType)
69 {
70     eventType_ = eventType;
71     return *this;
72 }
73 
SetEventCategory(EventCategory category)74 EventParamsBuilder& EventParamsBuilder::SetEventCategory(EventCategory category)
75 {
76     category_ = category;
77     return *this;
78 }
79 
SetId(const std::string & id)80 EventParamsBuilder& EventParamsBuilder::SetId(const std::string& id)
81 {
82     if (!id.empty()) {
83         params_->emplace(KEY_ID, id);
84     }
85     return *this;
86 }
87 
SetType(const std::string & type)88 EventParamsBuilder& EventParamsBuilder::SetType(const std::string& type)
89 {
90     params_->emplace(KEY_TYPE, type);
91     return *this;
92 }
93 
SetDescription(const std::string & desc)94 EventParamsBuilder& EventParamsBuilder::SetDescription(const std::string& desc)
95 {
96     if (!desc.empty()) {
97         params_->emplace(KEY_DESCRIPTION, desc);
98     }
99     return *this;
100 }
101 
SetNavDst(const std::string & dstName)102 EventParamsBuilder& EventParamsBuilder::SetNavDst(const std::string& dstName)
103 {
104     if (!dstName.empty()) {
105         params_->emplace(KEY_NAV_DST, dstName);
106     }
107     return *this;
108 }
109 
SetPageUrl(const std::string & pageUrl)110 EventParamsBuilder& EventParamsBuilder::SetPageUrl(const std::string& pageUrl)
111 {
112     if (!pageUrl.empty()) {
113         params_->emplace(KEY_PAGE, pageUrl);
114     }
115     return *this;
116 }
117 
SetText(const std::string & value)118 EventParamsBuilder& EventParamsBuilder::SetText(const std::string& value)
119 {
120     if (!value.empty()) {
121         params_->emplace(KEY_TEXT, value);
122     }
123     return *this;
124 }
125 
SetChecked(bool value)126 EventParamsBuilder& EventParamsBuilder::SetChecked(bool value)
127 {
128     std::string strVal = value ? "true" : "false";
129     params_->emplace(KEY_CHECKED, strVal);
130     return *this;
131 }
132 
SetIndex(int value)133 EventParamsBuilder& EventParamsBuilder::SetIndex(int value)
134 {
135     params_->emplace(KEY_INDEX, std::to_string(value));
136     return *this;
137 }
138 
SetTextArray(const std::vector<std::string> & value)139 EventParamsBuilder& EventParamsBuilder::SetTextArray(const std::vector<std::string>& value)
140 {
141     auto jsonArray = JsonUtil::CreateArray(true);
142     for (size_t i = 0; i < value.size(); i++) {
143         jsonArray->Put(std::to_string(i).c_str(), value.at(i).c_str());
144     }
145     params_->emplace(KEY_TEXT_ARRAY, jsonArray->ToString());
146     return *this;
147 }
148 
SetHost(const RefPtr<NG::FrameNode> & node)149 EventParamsBuilder& EventParamsBuilder::SetHost(const RefPtr<NG::FrameNode>& node)
150 {
151     if (!node) {
152         return *this;
153     }
154     ContainerScope scope(Container::CurrentIdSafely());
155     if (EventRecorder::Get().IsRecordEnable(EventCategory::CATEGORY_RECT)) {
156         auto rect = node->GetTransformRectRelativeToWindow().ToBounds();
157         params_->emplace(Recorder::KEY_NODE_RECT, std::move(rect));
158     }
159     params_->emplace(KEY_ACE_ID, std::to_string(node->GetId()));
160     params_->emplace("accessilityId", std::to_string(node->GetAccessibilityId()));
161     SetPageUrl(GetPageUrlByNode(node));
162     SetNavDst(GetNavDstNameByNode(node));
163     FillExtraTextIfNeed(eventType_, *this, node);
164     auto parent = node->GetParent();
165     if (parent) {
166         auto index = parent->GetFrameNodeIndex(node);
167         params_->emplace("nodeIndex", std::to_string(index));
168     }
169     return *this;
170 }
171 
SetExtra(const std::string & key,const std::string & value)172 EventParamsBuilder& EventParamsBuilder::SetExtra(const std::string& key, const std::string& value)
173 {
174     if (!key.empty() && !value.empty()) {
175         params_->emplace(key, value);
176     }
177     return *this;
178 }
179 
build()180 std::shared_ptr<std::unordered_map<std::string, std::string>> EventParamsBuilder::build()
181 {
182     auto current = Container::Current();
183     if (current) {
184         params_->emplace(KEY_MOUDLE_NAME, current->GetModuleName());
185     }
186     params_->emplace(KEY_ABILITY_NAME, AceApplicationInfo::GetInstance().GetAbilityName());
187     return params_;
188 }
189 
GetEventType() const190 EventType EventParamsBuilder::GetEventType() const
191 {
192     return eventType_;
193 }
194 
GetEventCategory() const195 EventCategory EventParamsBuilder::GetEventCategory() const
196 {
197     return category_;
198 }
199 
GetValue(const std::string & key) const200 std::string EventParamsBuilder::GetValue(const std::string& key) const
201 {
202     auto iter = params_->find(key);
203     if (iter != params_->end()) {
204         return iter->second;
205     }
206     return "";
207 }
208 
ToString() const209 std::string EventParamsBuilder::ToString() const
210 {
211     std::stringstream ss;
212     ss << "{";
213     if (eventType_ != EventType::INVALID) {
214         ss << "eventType:" << eventType_ << ", ";
215     }
216     for (auto&& it : *params_) {
217         ss << it.first << ":" << it.second << ", ";
218     }
219     ss << "}";
220     return ss.str();
221 }
222 
MapToString(const std::shared_ptr<std::unordered_map<std::string,std::string>> & input)223 std::string MapToString(const std::shared_ptr<std::unordered_map<std::string, std::string>>& input)
224 {
225     if (!input) {
226         return "";
227     }
228     std::stringstream ss;
229     ss << "{";
230     for (auto it = input->begin(); it != input->end(); it++) {
231         ss << it->first << ":" << it->second << ", ";
232     }
233     ss << "}";
234     return ss.str();
235 }
236 
Get()237 EventRecorder& EventRecorder::Get()
238 {
239     static EventRecorder eventRecorder;
240     return eventRecorder;
241 }
242 
EventRecorder()243 EventRecorder::EventRecorder()
244 {
245     eventSwitch_.resize(static_cast<int32_t>(EventCategory::CATEGORY_END), false);
246     eventSwitch_[static_cast<int32_t>(EventCategory::CATEGORY_PAGE)] = true;
247     globalSwitch_.resize(static_cast<int32_t>(EventCategory::CATEGORY_END), true);
248 }
249 
UpdateEventSwitch(const std::vector<bool> & eventSwitch)250 void EventRecorder::UpdateEventSwitch(const std::vector<bool>& eventSwitch)
251 {
252     std::unique_lock<std::shared_mutex> lock(switchLock_);
253     eventSwitch_ = eventSwitch;
254 }
255 
UpdateGlobalEventSwitch(const std::vector<bool> & eventSwitch)256 void EventRecorder::UpdateGlobalEventSwitch(const std::vector<bool>& eventSwitch)
257 {
258     std::unique_lock<std::shared_mutex> lock(switchLock_);
259     globalSwitch_ = eventSwitch;
260 }
261 
UpdateWebIdentifier(const std::unordered_map<std::string,std::string> & identifierMap)262 void EventRecorder::UpdateWebIdentifier(const std::unordered_map<std::string, std::string>& identifierMap)
263 {
264     std::unique_lock<std::shared_mutex> lock(switchLock_);
265     webIdentifierMap_ = identifierMap;
266 }
267 
IsPageRecordEnable() const268 bool EventRecorder::IsPageRecordEnable() const
269 {
270     std::shared_lock<std::shared_mutex> lock(switchLock_);
271     int32_t index = static_cast<int32_t>(EventCategory::CATEGORY_PAGE);
272     return globalSwitch_[index] && eventSwitch_[index];
273 }
274 
IsPageParamRecordEnable() const275 bool EventRecorder::IsPageParamRecordEnable() const
276 {
277     std::shared_lock<std::shared_mutex> lock(switchLock_);
278     int32_t index = static_cast<int32_t>(EventCategory::CATEGORY_PAGE_PARAM);
279     return globalSwitch_[index] && eventSwitch_[index];
280 }
281 
IsExposureRecordEnable() const282 bool EventRecorder::IsExposureRecordEnable() const
283 {
284     std::shared_lock<std::shared_mutex> lock(switchLock_);
285     int32_t index = static_cast<int32_t>(EventCategory::CATEGORY_EXPOSURE);
286     return globalSwitch_[index] && eventSwitch_[index];
287 }
288 
IsComponentRecordEnable() const289 bool EventRecorder::IsComponentRecordEnable() const
290 {
291     std::shared_lock<std::shared_mutex> lock(switchLock_);
292     int32_t index = static_cast<int32_t>(EventCategory::CATEGORY_COMPONENT);
293     return globalSwitch_[index] && eventSwitch_[index];
294 }
295 
IsRecordEnable(EventCategory category) const296 bool EventRecorder::IsRecordEnable(EventCategory category) const
297 {
298     std::shared_lock<std::shared_mutex> lock(switchLock_);
299     int32_t index = static_cast<int32_t>(category);
300     return globalSwitch_[index] && eventSwitch_[index];
301 }
302 
SetContainerInfo(const std::string & windowName,int32_t id,bool foreground)303 void EventRecorder::SetContainerInfo(const std::string& windowName, int32_t id, bool foreground)
304 {
305     if (windowName == IGNORE_WINDOW_NAME) {
306         return;
307     }
308     if (foreground) {
309         containerId_ = id;
310     }
311 }
312 
SetFocusContainerInfo(const std::string & windowName,int32_t id)313 void EventRecorder::SetFocusContainerInfo(const std::string& windowName, int32_t id)
314 {
315     if (windowName == IGNORE_WINDOW_NAME) {
316         return;
317     }
318     focusContainerId_ = id;
319 }
320 
GetContainerId(bool isFocus)321 int32_t EventRecorder::GetContainerId(bool isFocus)
322 {
323     if (isFocus) {
324         return focusContainerId_;
325     } else {
326         return containerId_;
327     }
328 }
329 
GetPageUrl()330 const std::string& EventRecorder::GetPageUrl()
331 {
332     auto pageUrl = GetPageUrlByContainerId(focusContainerId_);
333     if (!pageUrl.empty()) {
334         pageUrl_ = pageUrl;
335     }
336     return pageUrl_;
337 }
338 
GetNavDstName() const339 const std::string& EventRecorder::GetNavDstName() const
340 {
341     return navDstName_;
342 }
343 
FillWebJsCode(std::optional<WebJsItem> & scriptItems) const344 void EventRecorder::FillWebJsCode(std::optional<WebJsItem>& scriptItems) const
345 {
346     if (!IsRecordEnable(EventCategory::CATEGORY_WEB)) {
347         return;
348     }
349     auto codeList = EventController::Get().GetWebJsCodeList();
350     if (codeList.empty()) {
351         return;
352     }
353     std::vector<std::string> scriptRules = { "*" };
354     if (scriptItems.has_value()) {
355         for (const auto& code : codeList) {
356             scriptItems->emplace(std::make_pair(code, scriptRules));
357         }
358     } else {
359         WebJsItem webJsItems;
360         for (const auto& code : codeList) {
361             webJsItems.emplace(std::make_pair(code, scriptRules));
362         }
363         scriptItems = std::make_optional<WebJsItem>(webJsItems);
364     }
365 }
366 
IsMessageValid(const std::string & webCategory,const std::string & identifier)367 bool EventRecorder::IsMessageValid(const std::string& webCategory, const std::string& identifier)
368 {
369     std::shared_lock<std::shared_mutex> lock(switchLock_);
370     auto iter = webIdentifierMap_.find(webCategory);
371     if (iter == webIdentifierMap_.end()) {
372         return false;
373     }
374     return iter->second == identifier;
375 }
376 
NotifyEventCacheEnd()377 void EventRecorder::NotifyEventCacheEnd() {}
378 
OnPageShow(const std::string & pageUrl,const std::string & param,const std::string & name)379 void EventRecorder::OnPageShow(const std::string& pageUrl, const std::string& param, const std::string& name)
380 {
381     pageUrl_ = pageUrl;
382     NodeDataCache::Get().OnPageShow(pageUrl);
383     Recorder::EventParamsBuilder builder;
384     builder.SetType(std::to_string(PageEventType::ROUTER_PAGE))
385         .SetPageUrl(pageUrl)
386         .SetExtra(KEY_NAME, name)
387         .SetExtra(Recorder::KEY_PAGE_PARAM, param);
388     EventController::Get().NotifyEvent(
389         EventCategory::CATEGORY_PAGE, static_cast<int32_t>(EventType::PAGE_SHOW), std::move(builder.build()));
390 }
391 
OnPageHide(const std::string & pageUrl,const int64_t duration,const std::string & name)392 void EventRecorder::OnPageHide(const std::string& pageUrl, const int64_t duration, const std::string& name)
393 {
394     Recorder::EventParamsBuilder builder;
395     builder.SetType(std::to_string(PageEventType::ROUTER_PAGE))
396         .SetPageUrl(pageUrl)
397         .SetExtra(KEY_NAME, name)
398         .SetExtra(KEY_DURATION, std::to_string(duration));
399     EventController::Get().NotifyEvent(
400         EventCategory::CATEGORY_PAGE, static_cast<int32_t>(EventType::PAGE_HIDE), std::move(builder.build()));
401 }
402 
OnClick(EventParamsBuilder && builder)403 void EventRecorder::OnClick(EventParamsBuilder&& builder)
404 {
405     if (!taskExecutor_) {
406         auto container = Container::Current();
407         CHECK_NULL_VOID(container);
408         taskExecutor_ = container->GetTaskExecutor();
409     }
410     CHECK_NULL_VOID(taskExecutor_);
411     if (builder.GetValue(KEY_PAGE).empty()) {
412         builder.SetPageUrl(GetPageUrl());
413     }
414     auto params = builder.build();
415     taskExecutor_->PostTask(
416         [taskExecutor = taskExecutor_, params]() {
417             EventController::Get().NotifyEvent(
418                 EventCategory::CATEGORY_COMPONENT, static_cast<int32_t>(EventType::CLICK), std::move(params));
419         },
420         TaskExecutor::TaskType::UI, "ArkUINotifyClickEvent");
421 }
422 
OnChange(EventParamsBuilder && builder)423 void EventRecorder::OnChange(EventParamsBuilder&& builder)
424 {
425     if (builder.GetValue(KEY_PAGE).empty()) {
426         builder.SetPageUrl(GetPageUrl());
427     }
428     auto params = builder.build();
429     EventController::Get().NotifyEvent(
430         EventCategory::CATEGORY_COMPONENT, static_cast<int32_t>(EventType::CHANGE), std::move(params));
431 }
432 
OnEvent(EventParamsBuilder && builder)433 void EventRecorder::OnEvent(EventParamsBuilder&& builder)
434 {
435     if (builder.GetValue(KEY_PAGE).empty()) {
436         builder.SetPageUrl(GetPageUrl());
437     }
438     auto eventType = builder.GetEventType();
439     auto params = builder.build();
440     EventController::Get().NotifyEvent(builder.GetEventCategory(), static_cast<int32_t>(eventType), std::move(params));
441 }
442 
OnNavDstShow(EventParamsBuilder && builder)443 void EventRecorder::OnNavDstShow(EventParamsBuilder&& builder)
444 {
445     navDstName_ = builder.GetValue(KEY_NAV_DST);
446     navShowTime_ = GetCurrentTimestamp();
447     builder.SetPageUrl(GetPageUrl());
448     builder.SetType(std::to_string(PageEventType::NAV_PAGE));
449     auto params = builder.build();
450     EventController::Get().NotifyEvent(
451         EventCategory::CATEGORY_PAGE, static_cast<int32_t>(EventType::PAGE_SHOW), std::move(params));
452 }
453 
OnNavDstHide(EventParamsBuilder && builder)454 void EventRecorder::OnNavDstHide(EventParamsBuilder&& builder)
455 {
456     if (builder.GetValue(KEY_NAV_DST) == navDstName_) {
457         navDstName_ = "";
458         if (navShowTime_ > 0) {
459             int64_t duration = GetCurrentTimestamp() - navShowTime_;
460             builder.SetExtra(KEY_DURATION, std::to_string(duration));
461             navShowTime_ = 0;
462         }
463     }
464     builder.SetPageUrl(GetPageUrl());
465     builder.SetType(std::to_string(PageEventType::NAV_PAGE));
466     auto params = builder.build();
467     EventController::Get().NotifyEvent(
468         EventCategory::CATEGORY_PAGE, static_cast<int32_t>(EventType::PAGE_HIDE), std::move(params));
469 }
470 
OnExposure(EventParamsBuilder && builder)471 void EventRecorder::OnExposure(EventParamsBuilder&& builder)
472 {
473     auto params = builder.build();
474     EventController::Get().NotifyEvent(
475         EventCategory::CATEGORY_EXPOSURE, static_cast<int32_t>(EventType::EXPOSURE), std::move(params));
476 }
477 
OnWebEvent(const RefPtr<NG::FrameNode> & node,const std::vector<std::string> & params)478 void EventRecorder::OnWebEvent(const RefPtr<NG::FrameNode>& node, const std::vector<std::string>& params)
479 {
480     CHECK_NULL_VOID(node);
481     if (params.empty()) {
482         return;
483     }
484     if (params.size() == WEB_PARAM_SIZE) {
485         if (!IsRecordEnable(EventCategory::CATEGORY_WEB)) {
486             return;
487         }
488         if (!IsMessageValid(params[WEB_PARAM_INDEX_CATEGORY], params[WEB_PARAM_INDEX_IDENTIFIER])) {
489             return;
490         }
491         EventParamsBuilder builder;
492         builder.SetId(node->GetInspectorIdValue(""))
493             .SetType(node->GetHostTag())
494             .SetEventType(EventType::WEB_ACTION)
495             .SetEventCategory(EventCategory::CATEGORY_WEB)
496             .SetExtra(KEY_WEB_CATEGORY, params[WEB_PARAM_INDEX_CATEGORY])
497             .SetText(params[WEB_PARAM_INDEX_CONTENT])
498             .SetHost(node)
499             .SetDescription(node->GetAutoEventParamValue(""));
500         OnEvent(std::move(builder));
501     }
502 }
503 
OnAttachWeb(const RefPtr<NG::FrameNode> & node)504 void EventRecorder::OnAttachWeb(const RefPtr<NG::FrameNode>& node)
505 {
506     CHECK_NULL_VOID(node);
507     weakNodeCache_[node->GetId()] = Referenced::WeakClaim(Referenced::RawPtr(node));
508 }
509 
OnDetachWeb(int32_t nodeId)510 void EventRecorder::OnDetachWeb(int32_t nodeId)
511 {
512     weakNodeCache_.erase(nodeId);
513 }
514 } // namespace OHOS::Ace::Recorder
515