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