1 // Copyright 2018 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 package org.chromium.chrome.browser.usage_stats; 6 7 import org.chromium.base.Function; 8 import org.chromium.base.Promise; 9 10 import java.util.ArrayList; 11 import java.util.List; 12 13 /** 14 * Class that tracks which sites are currently suspended. 15 */ 16 public class SuspensionTracker { 17 private final UsageStatsBridge mBridge; 18 private final NotificationSuspender mNotificationSuspender; 19 private final Promise<List<String>> mRootPromise; 20 private Promise<Void> mWritePromise; 21 SuspensionTracker(UsageStatsBridge bridge, NotificationSuspender notificationSuspender)22 public SuspensionTracker(UsageStatsBridge bridge, NotificationSuspender notificationSuspender) { 23 mBridge = bridge; 24 mNotificationSuspender = notificationSuspender; 25 mRootPromise = new Promise<>(); 26 mBridge.getAllSuspensions((result) -> { mRootPromise.fulfill(result); }); 27 mWritePromise = Promise.fulfilled(null); 28 } 29 30 /** 31 * Sets the status of <c>fqdns</c> to match <c>suspended</c>. 32 * The returned promise will be fulfilled once persistence succeeds, and rejected if persistence 33 * fails. 34 */ setWebsitesSuspended(List<String> fqdns, boolean suspended)35 public Promise<Void> setWebsitesSuspended(List<String> fqdns, boolean suspended) { 36 Promise<Void> newWritePromise = new Promise<>(); 37 mWritePromise.then((dummyResult) -> { 38 mRootPromise.then((result) -> { 39 // We copy result so that the mutation isn't reflected in result until persistence 40 // succeeds. 41 List<String> resultCopy = new ArrayList<>(result); 42 if (suspended) { 43 UsageStatsMetricsReporter.reportMetricsEvent( 44 UsageStatsMetricsEvent.SUSPEND_SITES); 45 resultCopy.addAll(fqdns); 46 } else { 47 UsageStatsMetricsReporter.reportMetricsEvent( 48 UsageStatsMetricsEvent.UNSUSPEND_SITES); 49 resultCopy.removeAll(fqdns); 50 } 51 52 mBridge.setSuspensions( 53 resultCopy.toArray(new String[resultCopy.size()]), (didSucceed) -> { 54 if (didSucceed) { 55 if (suspended) { 56 result.addAll(fqdns); 57 } else { 58 result.removeAll(fqdns); 59 } 60 mNotificationSuspender.setWebsitesSuspended(fqdns, suspended); 61 newWritePromise.fulfill(null); 62 } else { 63 newWritePromise.reject(); 64 } 65 }); 66 // We need to add a dummy exception handler so that Promise doesn't complain when we 67 // call variants of then() that don't take a single callback. These variants set an 68 // exception handler on the returned promise, so they expect there to be one on the 69 // root promise. 70 }, (e) -> {}); 71 }); 72 73 mWritePromise = newWritePromise; 74 return newWritePromise; 75 } 76 getAllSuspendedWebsites()77 public Promise<List<String>> getAllSuspendedWebsites() { 78 return mRootPromise.then( 79 (Function<List<String>, List<String>>) (result) -> { return result; }); 80 } 81 isWebsiteSuspended(String fqdn)82 public boolean isWebsiteSuspended(String fqdn) { 83 // We special case isWebsiteSuspended to return a value immediately because its only 84 // consumer(PageViewOsberver) only cares about immediate results. 85 if (mRootPromise != null && mRootPromise.isFulfilled()) { 86 return mRootPromise.getResult().contains(fqdn); 87 } 88 89 return false; 90 } 91 }