[Tizen][M34-Merge] Implement favicon database get API
[platform/framework/web/chromium-efl.git] / components / rlz / rlz_tracker.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 // This code glues the RLZ library DLL with Chrome. It allows Chrome to work
6 // with or without the DLL being present. If the DLL is not present the
7 // functions do nothing and just return false.
8
9 #include "components/rlz/rlz_tracker.h"
10
11 #include <algorithm>
12 #include <utility>
13
14 #include "base/bind.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/sequenced_task_runner.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/task_scheduler/post_task.h"
21 #include "base/task_scheduler/task_traits.h"
22 #include "base/trace_event/trace_event.h"
23 #include "build/build_config.h"
24 #include "components/rlz/rlz_tracker_delegate.h"
25
26 #if defined(OS_CHROMEOS)
27 #include "base/syslog_logging.h"
28 #endif
29
30 namespace rlz {
31 namespace {
32
33 // Maximum and minimum delay for financial ping we would allow to be set through
34 // master preferences. Somewhat arbitrary, may need to be adjusted in future.
35 #if defined(OS_CHROMEOS)
36 const base::TimeDelta kMaxInitDelay = base::TimeDelta::FromHours(24);
37 #else
38 const base::TimeDelta kMaxInitDelay = base::TimeDelta::FromSeconds(200);
39 #endif
40 const base::TimeDelta kMinInitDelay = base::TimeDelta::FromSeconds(20);
41
42 void RecordProductEvents(bool first_run,
43                          bool is_google_default_search,
44                          bool is_google_homepage,
45                          bool is_google_in_startpages,
46                          bool already_ran,
47                          bool omnibox_used,
48                          bool homepage_used,
49                          bool app_list_used) {
50   TRACE_EVENT0("RLZ", "RecordProductEvents");
51   // Record the installation of chrome. We call this all the time but the rlz
52   // lib should ignore all but the first one.
53   rlz_lib::RecordProductEvent(rlz_lib::CHROME,
54                               RLZTracker::ChromeOmnibox(),
55                               rlz_lib::INSTALL);
56 #if !defined(OS_IOS)
57   rlz_lib::RecordProductEvent(rlz_lib::CHROME,
58                               RLZTracker::ChromeHomePage(),
59                               rlz_lib::INSTALL);
60   rlz_lib::RecordProductEvent(rlz_lib::CHROME,
61                               RLZTracker::ChromeAppList(),
62                               rlz_lib::INSTALL);
63 #endif  // !defined(OS_IOS)
64
65   if (!already_ran) {
66     // Do the initial event recording if is the first run or if we have an
67     // empty rlz which means we haven't got a chance to do it.
68     char omnibox_rlz[rlz_lib::kMaxRlzLength + 1];
69     if (!rlz_lib::GetAccessPointRlz(RLZTracker::ChromeOmnibox(), omnibox_rlz,
70                                     rlz_lib::kMaxRlzLength)) {
71       omnibox_rlz[0] = 0;
72     }
73
74     // Record if google is the initial search provider and/or home page.
75     if ((first_run || omnibox_rlz[0] == 0) && is_google_default_search) {
76       rlz_lib::RecordProductEvent(rlz_lib::CHROME,
77                                   RLZTracker::ChromeOmnibox(),
78                                   rlz_lib::SET_TO_GOOGLE);
79     }
80
81 #if !defined(OS_IOS)
82     char homepage_rlz[rlz_lib::kMaxRlzLength + 1];
83     if (!rlz_lib::GetAccessPointRlz(RLZTracker::ChromeHomePage(), homepage_rlz,
84                                     rlz_lib::kMaxRlzLength)) {
85       homepage_rlz[0] = 0;
86     }
87
88     if ((first_run || homepage_rlz[0] == 0) &&
89         (is_google_homepage || is_google_in_startpages)) {
90       rlz_lib::RecordProductEvent(rlz_lib::CHROME,
91                                   RLZTracker::ChromeHomePage(),
92                                   rlz_lib::SET_TO_GOOGLE);
93     }
94
95     char app_list_rlz[rlz_lib::kMaxRlzLength + 1];
96     if (!rlz_lib::GetAccessPointRlz(RLZTracker::ChromeAppList(), app_list_rlz,
97                                     rlz_lib::kMaxRlzLength)) {
98       app_list_rlz[0] = 0;
99     }
100
101     // Record if google is the initial search provider and/or home page.
102     if ((first_run || app_list_rlz[0] == 0) && is_google_default_search) {
103       rlz_lib::RecordProductEvent(rlz_lib::CHROME,
104                                   RLZTracker::ChromeAppList(),
105                                   rlz_lib::SET_TO_GOOGLE);
106     }
107 #endif  // !defined(OS_IOS)
108   }
109
110   // Record first user interaction with the omnibox. We call this all the
111   // time but the rlz lib should ingore all but the first one.
112   if (omnibox_used) {
113     rlz_lib::RecordProductEvent(rlz_lib::CHROME,
114                                 RLZTracker::ChromeOmnibox(),
115                                 rlz_lib::FIRST_SEARCH);
116   }
117
118 #if !defined(OS_IOS)
119   // Record first user interaction with the home page. We call this all the
120   // time but the rlz lib should ingore all but the first one.
121   if (homepage_used || is_google_in_startpages) {
122     rlz_lib::RecordProductEvent(rlz_lib::CHROME,
123                                 RLZTracker::ChromeHomePage(),
124                                 rlz_lib::FIRST_SEARCH);
125   }
126
127   // Record first user interaction with the app list. We call this all the
128   // time but the rlz lib should ingore all but the first one.
129   if (app_list_used) {
130     rlz_lib::RecordProductEvent(rlz_lib::CHROME,
131                                 RLZTracker::ChromeAppList(),
132                                 rlz_lib::FIRST_SEARCH);
133   }
134 #endif  // !defined(OS_IOS)
135 }
136
137 bool SendFinancialPing(const std::string& brand,
138                        const base::string16& lang,
139                        const base::string16& referral) {
140   rlz_lib::AccessPoint points[] = {RLZTracker::ChromeOmnibox(),
141 #if !defined(OS_IOS)
142                                    RLZTracker::ChromeHomePage(),
143                                    RLZTracker::ChromeAppList(),
144 #endif
145                                    rlz_lib::NO_ACCESS_POINT};
146   std::string lang_ascii(base::UTF16ToASCII(lang));
147   std::string referral_ascii(base::UTF16ToASCII(referral));
148   std::string product_signature;
149 #if defined(OS_CHROMEOS)
150   product_signature = "chromeos";
151 #else
152   product_signature = "chrome";
153 #endif
154   return rlz_lib::SendFinancialPing(rlz_lib::CHROME, points,
155                                     product_signature.c_str(),
156                                     brand.c_str(), referral_ascii.c_str(),
157                                     lang_ascii.c_str(), false, true);
158 }
159
160 }  // namespace
161
162 RLZTracker* RLZTracker::tracker_ = nullptr;
163
164 // static
165 RLZTracker* RLZTracker::GetInstance() {
166   return tracker_ ? tracker_ : base::Singleton<RLZTracker>::get();
167 }
168
169 RLZTracker::RLZTracker()
170     : first_run_(false),
171       send_ping_immediately_(false),
172       is_google_default_search_(false),
173       is_google_homepage_(false),
174       is_google_in_startpages_(false),
175       already_ran_(false),
176       omnibox_used_(false),
177       homepage_used_(false),
178       app_list_used_(false),
179       min_init_delay_(kMinInitDelay),
180       background_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
181           {base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN, base::MayBlock(),
182            base::TaskPriority::BACKGROUND})) {
183   DETACH_FROM_SEQUENCE(sequence_checker_);
184 }
185
186 RLZTracker::~RLZTracker() {
187 }
188
189 // static
190 void RLZTracker::SetRlzDelegate(std::unique_ptr<RLZTrackerDelegate> delegate) {
191   RLZTracker* tracker = GetInstance();
192   if (!tracker->delegate_) {
193     // RLZTracker::SetRlzDelegate is called at Profile creation time which can
194     // happens multiple time on ChromeOS, so do nothing if the delegate already
195     // exists.
196     tracker->SetDelegate(std::move(delegate));
197   }
198 }
199
200 void RLZTracker::SetDelegate(std::unique_ptr<RLZTrackerDelegate> delegate) {
201   DCHECK(delegate);
202   DCHECK(!delegate_);
203   delegate_ = std::move(delegate);
204 }
205
206 // static
207 bool RLZTracker::InitRlzDelayed(bool first_run,
208                                 bool send_ping_immediately,
209                                 base::TimeDelta delay,
210                                 bool is_google_default_search,
211                                 bool is_google_homepage,
212                                 bool is_google_in_startpages) {
213   return GetInstance()->Init(first_run, send_ping_immediately, delay,
214                              is_google_default_search, is_google_homepage,
215                              is_google_in_startpages);
216 }
217
218 bool RLZTracker::Init(bool first_run,
219                       bool send_ping_immediately,
220                       base::TimeDelta delay,
221                       bool is_google_default_search,
222                       bool is_google_homepage,
223                       bool is_google_in_startpages) {
224   DCHECK(delegate_) << "RLZTracker used before initialization";
225   first_run_ = first_run;
226   is_google_default_search_ = is_google_default_search;
227   is_google_homepage_ = is_google_homepage;
228   is_google_in_startpages_ = is_google_in_startpages;
229   send_ping_immediately_ = send_ping_immediately;
230
231   // Enable zero delays for testing.
232   if (delegate_->ShouldEnableZeroDelayForTesting())
233     EnableZeroDelayForTesting();
234
235   delay = std::min(kMaxInitDelay, std::max(min_init_delay_, delay));
236
237   if (delegate_->GetBrand(&brand_) && !delegate_->IsBrandOrganic(brand_)) {
238     // Register for notifications from the omnibox so that we can record when
239     // the user performs a first search.
240     delegate_->SetOmniboxSearchCallback(
241         base::Bind(&RLZTracker::RecordFirstSearch, base::Unretained(this),
242                    ChromeOmnibox()));
243
244 #if !defined(OS_IOS)
245     // Register for notifications from navigations, to see if the user has used
246     // the home page.
247     delegate_->SetHomepageSearchCallback(
248         base::Bind(&RLZTracker::RecordFirstSearch, base::Unretained(this),
249                    ChromeHomePage()));
250 #endif
251   }
252   delegate_->GetReactivationBrand(&reactivation_brand_);
253
254 #if defined(OS_CHROMEOS)
255   // If the brand is organic, RLZ is essentially disabled.  Write a log to the
256   // console for administrators and QA.
257   if (delegate_->IsBrandOrganic(brand_) &&
258       delegate_->IsBrandOrganic(reactivation_brand_)) {
259     SYSLOG(INFO) << "RLZ is disabled";
260   }
261 #endif
262
263   // Could be null; don't run if so.  RLZ will try again next restart.
264   net::URLRequestContextGetter* context_getter = delegate_->GetRequestContext();
265   if (context_getter) {
266     rlz_lib::SetURLRequestContext(context_getter);
267     ScheduleDelayedInit(delay);
268   }
269
270 #if !defined(OS_IOS)
271   // Prime the RLZ cache for the home page access point so that its avaiable
272   // for the startup page if needed (i.e., when the startup page is set to
273   // the home page).
274   GetAccessPointRlz(ChromeHomePage(), nullptr);
275 #endif  // !defined(OS_IOS)
276
277   return true;
278 }
279
280 void RLZTracker::Cleanup() {
281   rlz_cache_.clear();
282   if (delegate_)
283     delegate_->Cleanup();
284 }
285
286 void RLZTracker::ScheduleDelayedInit(base::TimeDelta delay) {
287   DCHECK(delegate_) << "RLZTracker used before initialization";
288   // The RLZTracker is a singleton object that outlives any runnable tasks
289   // that will be queued up.
290   background_task_runner_->PostDelayedTask(
291       FROM_HERE, base::Bind(&RLZTracker::DelayedInit, base::Unretained(this)),
292       delay);
293 }
294
295 void RLZTracker::DelayedInit() {
296   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
297   DCHECK(delegate_) << "RLZTracker used before initialization";
298   bool schedule_ping = false;
299
300   // For organic brandcodes do not use rlz at all. Empty brandcode usually
301   // means a chromium install. This is ok.
302   if (!delegate_->IsBrandOrganic(brand_)) {
303     RecordProductEvents(first_run_, is_google_default_search_,
304                         is_google_homepage_, is_google_in_startpages_,
305                         already_ran_, omnibox_used_, homepage_used_,
306                         app_list_used_);
307     schedule_ping = true;
308   }
309
310   // If chrome has been reactivated, record the events for this brand
311   // as well.
312   if (!delegate_->IsBrandOrganic(reactivation_brand_)) {
313     rlz_lib::SupplementaryBranding branding(reactivation_brand_.c_str());
314     RecordProductEvents(first_run_, is_google_default_search_,
315                         is_google_homepage_, is_google_in_startpages_,
316                         already_ran_, omnibox_used_, homepage_used_,
317                         app_list_used_);
318     schedule_ping = true;
319   }
320
321   already_ran_ = true;
322
323   if (schedule_ping)
324     ScheduleFinancialPing();
325 }
326
327 void RLZTracker::ScheduleFinancialPing() {
328   DCHECK(delegate_) << "RLZTracker used before initialization";
329   background_task_runner_->PostTask(
330       FROM_HERE, base::Bind(&RLZTracker::PingNowImpl, base::Unretained(this)));
331 }
332
333 void RLZTracker::PingNowImpl() {
334   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
335   DCHECK(delegate_) << "RLZTracker used before initialization";
336   TRACE_EVENT0("RLZ", "RLZTracker::PingNowImpl");
337   base::string16 lang;
338   delegate_->GetLanguage(&lang);
339   if (lang.empty())
340     lang = base::ASCIIToUTF16("en");
341   base::string16 referral;
342   delegate_->GetReferral(&referral);
343
344   if (!delegate_->IsBrandOrganic(brand_) &&
345       SendFinancialPing(brand_, lang, referral)) {
346     delegate_->ClearReferral();
347
348     {
349       base::AutoLock lock(cache_lock_);
350       rlz_cache_.clear();
351     }
352
353     // Prime the RLZ cache for the access points we are interested in.
354     GetAccessPointRlz(RLZTracker::ChromeOmnibox(), nullptr);
355 #if !defined(OS_IOS)
356     GetAccessPointRlz(RLZTracker::ChromeHomePage(), nullptr);
357     GetAccessPointRlz(RLZTracker::ChromeAppList(), nullptr);
358 #endif  // !defined(OS_IOS)
359   }
360
361   if (!delegate_->IsBrandOrganic(reactivation_brand_)) {
362     rlz_lib::SupplementaryBranding branding(reactivation_brand_.c_str());
363     SendFinancialPing(reactivation_brand_, lang, referral);
364   }
365 }
366
367 bool RLZTracker::SendFinancialPing(const std::string& brand,
368                                    const base::string16& lang,
369                                    const base::string16& referral) {
370   return ::rlz::SendFinancialPing(brand, lang, referral);
371 }
372
373 // static
374 bool RLZTracker::RecordProductEvent(rlz_lib::Product product,
375                                     rlz_lib::AccessPoint point,
376                                     rlz_lib::Event event_id) {
377   // This method is called during unit tests while the RLZTracker has not been
378   // initialized, so check for the presence of a delegate and exit if there is
379   // none registered.
380   RLZTracker* tracker = GetInstance();
381   return !tracker->delegate_ ? false : tracker->RecordProductEventImpl(
382                                            product, point, event_id);
383 }
384
385 bool RLZTracker::RecordProductEventImpl(rlz_lib::Product product,
386                                         rlz_lib::AccessPoint point,
387                                         rlz_lib::Event event_id) {
388   DCHECK(delegate_) << "RLZTracker used before initialization";
389   // Make sure we don't access disk outside of the I/O thread.
390   // In such case we repost the task on the right thread and return error.
391   if (ScheduleRecordProductEvent(product, point, event_id))
392     return true;
393
394   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
395
396   bool ret = rlz_lib::RecordProductEvent(product, point, event_id);
397
398   // If chrome has been reactivated, record the event for this brand as well.
399   if (!reactivation_brand_.empty()) {
400     rlz_lib::SupplementaryBranding branding(reactivation_brand_.c_str());
401     ret &= rlz_lib::RecordProductEvent(product, point, event_id);
402   }
403
404   return ret;
405 }
406
407 bool RLZTracker::ScheduleRecordProductEvent(rlz_lib::Product product,
408                                             rlz_lib::AccessPoint point,
409                                             rlz_lib::Event event_id) {
410   DCHECK(delegate_) << "RLZTracker used before initialization";
411   if (!delegate_->IsOnUIThread())
412     return false;
413
414   background_task_runner_->PostTask(
415       FROM_HERE, base::Bind(base::IgnoreResult(&RLZTracker::RecordProductEvent),
416                             product, point, event_id));
417   return true;
418 }
419
420 void RLZTracker::RecordFirstSearch(rlz_lib::AccessPoint point) {
421   DCHECK(delegate_) << "RLZTracker used before initialization";
422   // Make sure we don't access disk outside of the I/O thread.
423   // In such case we repost the task on the right thread and return error.
424   if (ScheduleRecordFirstSearch(point))
425     return;
426
427   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
428
429   bool* record_used = GetAccessPointRecord(point);
430
431   // Try to record event now, else set the flag to try later when we
432   // attempt the ping.
433   if (!RecordProductEvent(rlz_lib::CHROME, point, rlz_lib::FIRST_SEARCH)) {
434     *record_used = true;
435   } else if (send_ping_immediately_ && point == ChromeOmnibox()) {
436     ScheduleDelayedInit(base::TimeDelta());
437   }
438 }
439
440 bool RLZTracker::ScheduleRecordFirstSearch(rlz_lib::AccessPoint point) {
441   DCHECK(delegate_) << "RLZTracker used before initialization";
442   if (!delegate_->IsOnUIThread())
443     return false;
444   background_task_runner_->PostTask(FROM_HERE,
445                                     base::Bind(&RLZTracker::RecordFirstSearch,
446                                                base::Unretained(this), point));
447   return true;
448 }
449
450 bool* RLZTracker::GetAccessPointRecord(rlz_lib::AccessPoint point) {
451   if (point == ChromeOmnibox())
452     return &omnibox_used_;
453 #if !defined(OS_IOS)
454   if (point == ChromeHomePage())
455     return &homepage_used_;
456   if (point == ChromeAppList())
457     return &app_list_used_;
458 #endif  // !defined(OS_IOS)
459   NOTREACHED();
460   return nullptr;
461 }
462
463 // static
464 std::string RLZTracker::GetAccessPointHttpHeader(rlz_lib::AccessPoint point) {
465   TRACE_EVENT0("RLZ", "RLZTracker::GetAccessPointHttpHeader");
466   std::string extra_headers;
467   base::string16 rlz_string;
468   RLZTracker::GetAccessPointRlz(point, &rlz_string);
469   if (!rlz_string.empty()) {
470     return base::StringPrintf("X-Rlz-String: %s\r\n",
471                               base::UTF16ToUTF8(rlz_string).c_str());
472   }
473
474   return extra_headers;
475 }
476
477 // GetAccessPointRlz() caches RLZ strings for all access points. If we had
478 // a successful ping, then we update the cached value.
479 // static
480 bool RLZTracker::GetAccessPointRlz(rlz_lib::AccessPoint point,
481                                    base::string16* rlz) {
482   // This method is called during unit tests while the RLZTracker has not been
483   // initialized, so check for the presence of a delegate and exit if there is
484   // none registered.
485   TRACE_EVENT0("RLZ", "RLZTracker::GetAccessPointRlz");
486   RLZTracker* tracker = GetInstance();
487   return !tracker->delegate_ ? false
488                              : tracker->GetAccessPointRlzImpl(point, rlz);
489 }
490
491 // GetAccessPointRlz() caches RLZ strings for all access points. If we had
492 // a successful ping, then we update the cached value.
493 bool RLZTracker::GetAccessPointRlzImpl(rlz_lib::AccessPoint point,
494                                        base::string16* rlz) {
495   DCHECK(delegate_) << "RLZTracker used before initialization";
496   // If the RLZ string for the specified access point is already cached,
497   // simply return its value.
498   {
499     base::AutoLock lock(cache_lock_);
500     if (rlz_cache_.find(point) != rlz_cache_.end()) {
501       if (rlz)
502         *rlz = rlz_cache_[point];
503       return true;
504     }
505   }
506
507   // Make sure we don't access disk outside of the I/O thread.
508   // In such case we repost the task on the right thread and return error.
509   if (ScheduleGetAccessPointRlz(point))
510     return false;
511
512   char str_rlz[rlz_lib::kMaxRlzLength + 1];
513   if (!rlz_lib::GetAccessPointRlz(point, str_rlz, rlz_lib::kMaxRlzLength))
514     return false;
515
516   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
517
518   base::string16 rlz_local(base::ASCIIToUTF16(str_rlz));
519   if (rlz)
520     *rlz = rlz_local;
521
522   base::AutoLock lock(cache_lock_);
523   rlz_cache_[point] = rlz_local;
524   return true;
525 }
526
527 bool RLZTracker::ScheduleGetAccessPointRlz(rlz_lib::AccessPoint point) {
528   DCHECK(delegate_) << "RLZTracker used before initialization";
529   if (!delegate_->IsOnUIThread())
530     return false;
531
532   base::string16* not_used = nullptr;
533   background_task_runner_->PostTask(
534       FROM_HERE, base::Bind(base::IgnoreResult(&RLZTracker::GetAccessPointRlz),
535                             point, not_used));
536   return true;
537 }
538
539 #if defined(OS_CHROMEOS)
540 // static
541 void RLZTracker::ClearRlzState() {
542   RLZTracker* tracker = GetInstance();
543   if (tracker->delegate_)
544     tracker->ClearRlzStateImpl();
545 }
546
547 void RLZTracker::ClearRlzStateImpl() {
548   DCHECK(delegate_) << "RLZTracker used before initialization";
549   if (ScheduleClearRlzState())
550     return;
551
552   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
553   rlz_lib::ClearAllProductEvents(rlz_lib::CHROME);
554 }
555
556 bool RLZTracker::ScheduleClearRlzState() {
557   DCHECK(delegate_) << "RLZTracker used before initialization";
558   if (!delegate_->IsOnUIThread())
559     return false;
560
561   background_task_runner_->PostTask(
562       FROM_HERE,
563       base::Bind(&RLZTracker::ClearRlzStateImpl, base::Unretained(this)));
564   return true;
565 }
566 #endif
567
568 // static
569 void RLZTracker::CleanupRlz() {
570   GetInstance()->Cleanup();
571   rlz_lib::SetURLRequestContext(nullptr);
572 }
573
574 // static
575 void RLZTracker::EnableZeroDelayForTesting() {
576   GetInstance()->min_init_delay_ = base::TimeDelta();
577 }
578
579 #if !defined(OS_IOS)
580 // static
581 void RLZTracker::RecordAppListSearch() {
582   // This method is called during unit tests while the RLZTracker has not been
583   // initialized, so check for the presence of a delegate and exit if there is
584   // none registered.
585   RLZTracker* tracker = GetInstance();
586   if (tracker->delegate_)
587     tracker->RecordFirstSearch(RLZTracker::ChromeAppList());
588 }
589 #endif
590
591 }  // namespace rlz