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