Bug 1883017: Handle resizable buffers in ArrayBufferObject::ensureNonInline. r=sfink
[gecko.git] / browser / modules / LaterRun.sys.mjs
blob505942d617d875053931b05a4639f5587ef30d31
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 const kEnabledPref = "browser.laterrun.enabled";
6 const kPagePrefRoot = "browser.laterrun.pages.";
7 // Number of sessions we've been active in
8 const kSessionCountPref = "browser.laterrun.bookkeeping.sessionCount";
9 // Time the profile was created at in seconds:
10 const kProfileCreationTime = "browser.laterrun.bookkeeping.profileCreationTime";
11 // Time the update was applied at in seconds:
12 const kUpdateAppliedTime = "browser.laterrun.bookkeeping.updateAppliedTime";
14 // After 50 sessions or 1 month since install, assume we will no longer be
15 // interested in showing anything to "new" users
16 const kSelfDestructSessionLimit = 50;
17 const kSelfDestructHoursLimit = 31 * 24;
19 class Page {
20   constructor({
21     pref,
22     minimumHoursSinceInstall,
23     minimumSessionCount,
24     requireBoth,
25     url,
26   }) {
27     this.pref = pref;
28     this.minimumHoursSinceInstall = minimumHoursSinceInstall || 0;
29     this.minimumSessionCount = minimumSessionCount || 1;
30     this.requireBoth = requireBoth || false;
31     this.url = url;
32   }
34   get hasRun() {
35     return Services.prefs.getBoolPref(this.pref + "hasRun", false);
36   }
38   applies(sessionInfo) {
39     if (this.hasRun) {
40       return false;
41     }
42     if (this.requireBoth) {
43       return (
44         sessionInfo.sessionCount >= this.minimumSessionCount &&
45         sessionInfo.hoursSinceInstall >= this.minimumHoursSinceInstall
46       );
47     }
48     return (
49       sessionInfo.sessionCount >= this.minimumSessionCount ||
50       sessionInfo.hoursSinceInstall >= this.minimumHoursSinceInstall
51     );
52   }
55 export let LaterRun = {
56   get ENABLE_REASON_NEW_PROFILE() {
57     return 1;
58   },
59   get ENABLE_REASON_UPDATE_APPLIED() {
60     return 2;
61   },
63   init(reason) {
64     if (!this.enabled) {
65       return;
66     }
68     if (reason == this.ENABLE_REASON_NEW_PROFILE) {
69       // If this is the first run, set the time we were installed
70       if (
71         Services.prefs.getPrefType(kProfileCreationTime) ==
72         Ci.nsIPrefBranch.PREF_INVALID
73       ) {
74         // We need to store seconds in order to fit within int prefs.
75         Services.prefs.setIntPref(
76           kProfileCreationTime,
77           Math.floor(Date.now() / 1000)
78         );
79       }
80       this.sessionCount++;
81     } else if (reason == this.ENABLE_REASON_UPDATE_APPLIED) {
82       Services.prefs.setIntPref(
83         kUpdateAppliedTime,
84         Math.floor(Services.startup.getStartupInfo().start.getTime() / 1000)
85       );
86     }
88     if (
89       this.hoursSinceInstall > kSelfDestructHoursLimit ||
90       this.sessionCount > kSelfDestructSessionLimit
91     ) {
92       this.selfDestruct();
93     }
94   },
96   // The enabled, hoursSinceInstall and sessionCount properties mirror the
97   // preferences system, and are here for convenience.
98   get enabled() {
99     return Services.prefs.getBoolPref(kEnabledPref, false);
100   },
102   enable(reason) {
103     if (!this.enabled) {
104       Services.prefs.setBoolPref(kEnabledPref, true);
105       this.init(reason);
106     }
107   },
109   get hoursSinceInstall() {
110     let installStampSec = Services.prefs.getIntPref(
111       kProfileCreationTime,
112       Date.now() / 1000
113     );
114     return Math.floor((Date.now() / 1000 - installStampSec) / 3600);
115   },
117   get hoursSinceUpdate() {
118     let updateStampSec = Services.prefs.getIntPref(kUpdateAppliedTime, 0);
119     return Math.floor((Date.now() / 1000 - updateStampSec) / 3600);
120   },
122   get sessionCount() {
123     if (this._sessionCount) {
124       return this._sessionCount;
125     }
126     return (this._sessionCount = Services.prefs.getIntPref(
127       kSessionCountPref,
128       0
129     ));
130   },
132   set sessionCount(val) {
133     this._sessionCount = val;
134     Services.prefs.setIntPref(kSessionCountPref, val);
135   },
137   // Because we don't want to keep incrementing this indefinitely for no reason,
138   // we will turn ourselves off after a set amount of time/sessions (see top of
139   // file).
140   selfDestruct() {
141     Services.prefs.setBoolPref(kEnabledPref, false);
142   },
144   // Create an array of Page objects based on the currently set prefs
145   readPages() {
146     // Enumerate all the pages.
147     let allPrefsForPages = Services.prefs.getChildList(kPagePrefRoot);
148     let pageDataStore = new Map();
149     for (let pref of allPrefsForPages) {
150       let [slug, prop] = pref.substring(kPagePrefRoot.length).split(".");
151       if (!pageDataStore.has(slug)) {
152         pageDataStore.set(slug, {
153           pref: pref.substring(0, pref.length - prop.length),
154         });
155       }
156       if (prop == "requireBoth" || prop == "hasRun") {
157         pageDataStore.get(slug)[prop] = Services.prefs.getBoolPref(pref, false);
158       } else if (prop == "url") {
159         pageDataStore.get(slug)[prop] = Services.prefs.getStringPref(pref, "");
160       } else {
161         pageDataStore.get(slug)[prop] = Services.prefs.getIntPref(pref, 0);
162       }
163     }
164     let rv = [];
165     for (let [, pageData] of pageDataStore) {
166       if (pageData.url) {
167         let uri = null;
168         try {
169           let urlString = Services.urlFormatter.formatURL(pageData.url.trim());
170           uri = Services.io.newURI(urlString);
171         } catch (ex) {
172           console.error(
173             "Invalid LaterRun page URL ",
174             pageData.url,
175             " ignored."
176           );
177           continue;
178         }
179         if (!uri.schemeIs("https")) {
180           console.error("Insecure LaterRun page URL ", uri.spec, " ignored.");
181         } else {
182           pageData.url = uri.spec;
183           rv.push(new Page(pageData));
184         }
185       }
186     }
187     return rv;
188   },
190   // Return a URL for display as a 'later run' page if its criteria are matched,
191   // or null otherwise.
192   // NB: will only return one page at a time; if multiple pages match, it's up
193   // to the preference service which one gets shown first, and the next one
194   // will be shown next startup instead.
195   getURL() {
196     if (!this.enabled) {
197       return null;
198     }
199     let pages = this.readPages();
200     let page = pages.find(p => p.applies(this));
201     if (page) {
202       Services.prefs.setBoolPref(page.pref + "hasRun", true);
203       return page.url;
204     }
205     return null;
206   },
209 LaterRun.init();