background_sync_controller_impl.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. // Copyright 2015 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. #include "components/background_sync/background_sync_controller_impl.h"
  5. #include "base/containers/contains.h"
  6. #include "base/strings/string_number_conversions.h"
  7. #include "base/strings/string_util.h"
  8. #include "base/time/time.h"
  9. #include "build/build_config.h"
  10. #include "components/content_settings/core/browser/host_content_settings_map.h"
  11. #include "components/content_settings/core/common/content_settings.h"
  12. #include "components/keep_alive_registry/keep_alive_registry.h"
  13. #include "components/variations/variations_associated_data.h"
  14. #include "content/public/browser/background_sync_context.h"
  15. #include "content/public/browser/background_sync_controller.h"
  16. #include "content/public/browser/background_sync_parameters.h"
  17. #include "content/public/browser/background_sync_registration.h"
  18. #include "content/public/browser/browser_context.h"
  19. #include "content/public/browser/navigation_handle.h"
  20. #include "content/public/browser/storage_partition.h"
  21. #include "url/gurl.h"
  22. #include "url/origin.h"
  23. // static
  24. const char BackgroundSyncControllerImpl::kFieldTrialName[] = "BackgroundSync";
  25. const char BackgroundSyncControllerImpl::kDisabledParameterName[] = "disabled";
  26. #if BUILDFLAG(IS_ANDROID)
  27. const char BackgroundSyncControllerImpl::kRelyOnAndroidNetworkDetection[] =
  28. "rely_on_android_network_detection";
  29. #endif
  30. const char BackgroundSyncControllerImpl::kKeepBrowserAwakeParameterName[] =
  31. "keep_browser_awake_till_events_complete";
  32. const char BackgroundSyncControllerImpl::kSkipPermissionsCheckParameterName[] =
  33. "skip_permissions_check_for_testing";
  34. const char BackgroundSyncControllerImpl::kMaxAttemptsParameterName[] =
  35. "max_sync_attempts";
  36. const char BackgroundSyncControllerImpl::
  37. kMaxAttemptsWithNotificationPermissionParameterName[] =
  38. "max_sync_attempts_with_notification_permission";
  39. const char BackgroundSyncControllerImpl::kInitialRetryParameterName[] =
  40. "initial_retry_delay_sec";
  41. const char BackgroundSyncControllerImpl::kRetryDelayFactorParameterName[] =
  42. "retry_delay_factor";
  43. const char BackgroundSyncControllerImpl::kMinSyncRecoveryTimeName[] =
  44. "min_recovery_time_sec";
  45. const char BackgroundSyncControllerImpl::kMaxSyncEventDurationName[] =
  46. "max_sync_event_duration_sec";
  47. const char BackgroundSyncControllerImpl::kMinPeriodicSyncEventsInterval[] =
  48. "min_periodic_sync_events_interval_sec";
  49. BackgroundSyncControllerImpl::BackgroundSyncControllerImpl(
  50. content::BrowserContext* browser_context,
  51. std::unique_ptr<background_sync::BackgroundSyncDelegate> delegate)
  52. : browser_context_(browser_context), delegate_(std::move(delegate)) {
  53. DCHECK(browser_context_);
  54. DCHECK(delegate_);
  55. background_sync_metrics_ =
  56. std::make_unique<BackgroundSyncMetrics>(delegate_.get());
  57. delegate_->GetHostContentSettingsMap()->AddObserver(this);
  58. }
  59. BackgroundSyncControllerImpl::~BackgroundSyncControllerImpl() = default;
  60. void BackgroundSyncControllerImpl::OnContentSettingChanged(
  61. const ContentSettingsPattern& primary_pattern,
  62. const ContentSettingsPattern& secondary_pattern,
  63. ContentSettingsTypeSet content_type_set) {
  64. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  65. if (!content_type_set.Contains(ContentSettingsType::BACKGROUND_SYNC) &&
  66. !content_type_set.Contains(
  67. ContentSettingsType::PERIODIC_BACKGROUND_SYNC)) {
  68. return;
  69. }
  70. std::vector<url::Origin> affected_origins;
  71. for (const auto& origin : periodic_sync_origins_) {
  72. if (!IsContentSettingBlocked(origin))
  73. continue;
  74. auto* storage_partition = browser_context_->GetStoragePartitionForUrl(
  75. origin.GetURL(), /* can_create= */ false);
  76. if (!storage_partition)
  77. continue;
  78. auto* background_sync_context =
  79. storage_partition->GetBackgroundSyncContext();
  80. if (!background_sync_context)
  81. continue;
  82. background_sync_context->UnregisterPeriodicSyncForOrigin(origin);
  83. affected_origins.push_back(origin);
  84. }
  85. // Stop tracking affected origins.
  86. for (const auto& origin : affected_origins) {
  87. periodic_sync_origins_.erase(origin);
  88. }
  89. }
  90. void BackgroundSyncControllerImpl::GetParameterOverrides(
  91. content::BackgroundSyncParameters* parameters) {
  92. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  93. #if BUILDFLAG(IS_ANDROID)
  94. if (delegate_->ShouldDisableBackgroundSync())
  95. parameters->disable = true;
  96. #endif
  97. std::map<std::string, std::string> field_params;
  98. if (!variations::GetVariationParams(kFieldTrialName, &field_params))
  99. return;
  100. if (base::EqualsCaseInsensitiveASCII(field_params[kDisabledParameterName],
  101. "true")) {
  102. parameters->disable = true;
  103. }
  104. if (base::EqualsCaseInsensitiveASCII(
  105. field_params[kKeepBrowserAwakeParameterName], "true")) {
  106. parameters->keep_browser_awake_till_events_complete = true;
  107. }
  108. if (base::EqualsCaseInsensitiveASCII(
  109. field_params[kSkipPermissionsCheckParameterName], "true")) {
  110. parameters->skip_permissions_check_for_testing = true;
  111. }
  112. if (base::Contains(field_params,
  113. kMaxAttemptsWithNotificationPermissionParameterName)) {
  114. int max_attempts;
  115. if (base::StringToInt(
  116. field_params[kMaxAttemptsWithNotificationPermissionParameterName],
  117. &max_attempts)) {
  118. parameters->max_sync_attempts_with_notification_permission = max_attempts;
  119. }
  120. }
  121. if (base::Contains(field_params, kMaxAttemptsParameterName)) {
  122. int max_attempts;
  123. if (base::StringToInt(field_params[kMaxAttemptsParameterName],
  124. &max_attempts)) {
  125. parameters->max_sync_attempts = max_attempts;
  126. }
  127. }
  128. if (base::Contains(field_params, kInitialRetryParameterName)) {
  129. int initial_retry_delay_sec;
  130. if (base::StringToInt(field_params[kInitialRetryParameterName],
  131. &initial_retry_delay_sec)) {
  132. parameters->initial_retry_delay = base::Seconds(initial_retry_delay_sec);
  133. }
  134. }
  135. if (base::Contains(field_params, kRetryDelayFactorParameterName)) {
  136. int retry_delay_factor;
  137. if (base::StringToInt(field_params[kRetryDelayFactorParameterName],
  138. &retry_delay_factor)) {
  139. parameters->retry_delay_factor = retry_delay_factor;
  140. }
  141. }
  142. if (base::Contains(field_params, kMinSyncRecoveryTimeName)) {
  143. int min_sync_recovery_time_sec;
  144. if (base::StringToInt(field_params[kMinSyncRecoveryTimeName],
  145. &min_sync_recovery_time_sec)) {
  146. parameters->min_sync_recovery_time =
  147. base::Seconds(min_sync_recovery_time_sec);
  148. }
  149. }
  150. if (base::Contains(field_params, kMaxSyncEventDurationName)) {
  151. int max_sync_event_duration_sec;
  152. if (base::StringToInt(field_params[kMaxSyncEventDurationName],
  153. &max_sync_event_duration_sec)) {
  154. parameters->max_sync_event_duration =
  155. base::Seconds(max_sync_event_duration_sec);
  156. }
  157. }
  158. if (base::Contains(field_params, kMinPeriodicSyncEventsInterval)) {
  159. int min_periodic_sync_events_interval_sec;
  160. if (base::StringToInt(field_params[kMinPeriodicSyncEventsInterval],
  161. &min_periodic_sync_events_interval_sec)) {
  162. parameters->min_periodic_sync_events_interval =
  163. base::Seconds(min_periodic_sync_events_interval_sec);
  164. }
  165. }
  166. #if BUILDFLAG(IS_ANDROID)
  167. // Check if the delegate explicitly disabled this feature.
  168. if (delegate_->ShouldDisableAndroidNetworkDetection()) {
  169. parameters->rely_on_android_network_detection = false;
  170. } else if (base::Contains(field_params, kRelyOnAndroidNetworkDetection)) {
  171. if (base::EqualsCaseInsensitiveASCII(
  172. field_params[kRelyOnAndroidNetworkDetection], "true")) {
  173. parameters->rely_on_android_network_detection = true;
  174. }
  175. }
  176. #endif
  177. return;
  178. }
  179. void BackgroundSyncControllerImpl::NotifyOneShotBackgroundSyncRegistered(
  180. const url::Origin& origin,
  181. bool can_fire,
  182. bool is_reregistered) {
  183. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  184. background_sync_metrics_->MaybeRecordOneShotSyncRegistrationEvent(
  185. origin, can_fire, is_reregistered);
  186. }
  187. void BackgroundSyncControllerImpl::NotifyPeriodicBackgroundSyncRegistered(
  188. const url::Origin& origin,
  189. int min_interval,
  190. bool is_reregistered) {
  191. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  192. background_sync_metrics_->MaybeRecordPeriodicSyncRegistrationEvent(
  193. origin, min_interval, is_reregistered);
  194. }
  195. void BackgroundSyncControllerImpl::NotifyOneShotBackgroundSyncCompleted(
  196. const url::Origin& origin,
  197. blink::ServiceWorkerStatusCode status_code,
  198. int num_attempts,
  199. int max_attempts) {
  200. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  201. background_sync_metrics_->MaybeRecordOneShotSyncCompletionEvent(
  202. origin, status_code, num_attempts, max_attempts);
  203. }
  204. void BackgroundSyncControllerImpl::NotifyPeriodicBackgroundSyncCompleted(
  205. const url::Origin& origin,
  206. blink::ServiceWorkerStatusCode status_code,
  207. int num_attempts,
  208. int max_attempts) {
  209. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  210. background_sync_metrics_->MaybeRecordPeriodicSyncEventCompletion(
  211. origin, status_code, num_attempts, max_attempts);
  212. }
  213. void BackgroundSyncControllerImpl::ScheduleBrowserWakeUpWithDelay(
  214. blink::mojom::BackgroundSyncType sync_type,
  215. base::TimeDelta delay) {
  216. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  217. if (delegate_->IsProfileOffTheRecord())
  218. return;
  219. #if BUILDFLAG(IS_ANDROID)
  220. delegate_->ScheduleBrowserWakeUpWithDelay(sync_type, delay);
  221. #endif
  222. }
  223. void BackgroundSyncControllerImpl::CancelBrowserWakeup(
  224. blink::mojom::BackgroundSyncType sync_type) {
  225. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  226. if (delegate_->IsProfileOffTheRecord())
  227. return;
  228. #if BUILDFLAG(IS_ANDROID)
  229. delegate_->CancelBrowserWakeup(sync_type);
  230. #endif
  231. }
  232. base::TimeDelta BackgroundSyncControllerImpl::SnapToMaxOriginFrequency(
  233. int64_t min_interval,
  234. int64_t min_gap_for_origin) {
  235. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  236. DCHECK_GE(min_gap_for_origin, 0);
  237. DCHECK_GE(min_interval, 0);
  238. if (min_interval < min_gap_for_origin)
  239. return base::Milliseconds(min_gap_for_origin);
  240. if (min_interval % min_gap_for_origin == 0)
  241. return base::Milliseconds(min_interval);
  242. return base::Milliseconds((min_interval / min_gap_for_origin + 1) *
  243. min_gap_for_origin);
  244. }
  245. base::TimeDelta BackgroundSyncControllerImpl::ApplyMinGapForOrigin(
  246. base::TimeDelta delay,
  247. base::TimeDelta time_till_next_scheduled_event_for_origin,
  248. base::TimeDelta min_gap_for_origin) {
  249. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  250. if (time_till_next_scheduled_event_for_origin.is_max())
  251. return delay;
  252. if (delay <= time_till_next_scheduled_event_for_origin - min_gap_for_origin)
  253. return delay;
  254. if (delay <= time_till_next_scheduled_event_for_origin)
  255. return time_till_next_scheduled_event_for_origin;
  256. if (delay <= time_till_next_scheduled_event_for_origin + min_gap_for_origin)
  257. return time_till_next_scheduled_event_for_origin + min_gap_for_origin;
  258. return delay;
  259. }
  260. bool BackgroundSyncControllerImpl::IsContentSettingBlocked(
  261. const url::Origin& origin) {
  262. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  263. auto* host_content_settings_map = delegate_->GetHostContentSettingsMap();
  264. DCHECK(host_content_settings_map);
  265. auto url = origin.GetURL();
  266. return CONTENT_SETTING_ALLOW != host_content_settings_map->GetContentSetting(
  267. /* primary_url= */ url,
  268. /* secondary_url= */ url,
  269. ContentSettingsType::BACKGROUND_SYNC);
  270. }
  271. void BackgroundSyncControllerImpl::Shutdown() {
  272. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  273. delegate_->GetHostContentSettingsMap()->RemoveObserver(this);
  274. delegate_->Shutdown();
  275. }
  276. base::TimeDelta BackgroundSyncControllerImpl::GetNextEventDelay(
  277. const content::BackgroundSyncRegistration& registration,
  278. content::BackgroundSyncParameters* parameters,
  279. base::TimeDelta time_till_soonest_scheduled_event_for_origin) {
  280. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  281. DCHECK(parameters);
  282. int num_attempts = registration.num_attempts();
  283. if (!num_attempts) {
  284. // First attempt.
  285. switch (registration.sync_type()) {
  286. case blink::mojom::BackgroundSyncType::ONE_SHOT:
  287. return base::TimeDelta();
  288. case blink::mojom::BackgroundSyncType::PERIODIC:
  289. int site_engagement_factor =
  290. delegate_->GetSiteEngagementPenalty(registration.origin().GetURL());
  291. if (!site_engagement_factor)
  292. return base::TimeDelta::Max();
  293. int64_t effective_gap_ms =
  294. site_engagement_factor *
  295. parameters->min_periodic_sync_events_interval.InMilliseconds();
  296. return ApplyMinGapForOrigin(
  297. SnapToMaxOriginFrequency(registration.options()->min_interval,
  298. effective_gap_ms),
  299. time_till_soonest_scheduled_event_for_origin,
  300. parameters->min_periodic_sync_events_interval);
  301. }
  302. }
  303. // After a sync event has been fired.
  304. DCHECK_LT(num_attempts, parameters->max_sync_attempts);
  305. return parameters->initial_retry_delay *
  306. pow(parameters->retry_delay_factor, num_attempts - 1);
  307. }
  308. std::unique_ptr<content::BackgroundSyncController::BackgroundSyncEventKeepAlive>
  309. BackgroundSyncControllerImpl::CreateBackgroundSyncEventKeepAlive() {
  310. #if BUILDFLAG(IS_ANDROID)
  311. // Not needed on Android.
  312. return nullptr;
  313. #else
  314. return delegate_->CreateBackgroundSyncEventKeepAlive();
  315. #endif
  316. }
  317. void BackgroundSyncControllerImpl::NoteSuspendedPeriodicSyncOrigins(
  318. std::set<url::Origin> suspended_origins) {
  319. delegate_->NoteSuspendedPeriodicSyncOrigins(std::move(suspended_origins));
  320. }
  321. void BackgroundSyncControllerImpl::NoteRegisteredPeriodicSyncOrigins(
  322. std::set<url::Origin> registered_origins) {
  323. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  324. for (auto& origin : registered_origins)
  325. periodic_sync_origins_.insert(std::move(origin));
  326. }
  327. void BackgroundSyncControllerImpl::AddToTrackedOrigins(
  328. const url::Origin& origin) {
  329. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  330. periodic_sync_origins_.insert(origin);
  331. }
  332. void BackgroundSyncControllerImpl::RemoveFromTrackedOrigins(
  333. const url::Origin& origin) {
  334. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  335. periodic_sync_origins_.erase(origin);
  336. }