You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
172 lines
5.6 KiB
172 lines
5.6 KiB
/* This Source Code Form is subject to the terms of the Mozilla Public |
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
|
|
"use strict"; |
|
|
|
const { classes: Cc, interfaces: Ci, utils: Cu } = Components; |
|
|
|
this.EXPORTED_SYMBOLS = ["LaterRun"]; |
|
|
|
Cu.import("resource://gre/modules/Preferences.jsm"); |
|
Cu.import("resource://gre/modules/Services.jsm"); |
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
XPCOMUtils.defineLazyModuleGetter(this, "setInterval", "resource://gre/modules/Timer.jsm"); |
|
XPCOMUtils.defineLazyModuleGetter(this, "clearInterval", "resource://gre/modules/Timer.jsm"); |
|
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow", "resource://gre/modules/RecentWindow.jsm"); |
|
|
|
const kEnabledPref = "browser.laterrun.enabled"; |
|
const kPagePrefRoot = "browser.laterrun.pages."; |
|
// Number of sessions we've been active in |
|
const kSessionCountPref = "browser.laterrun.bookkeeping.sessionCount"; |
|
// Time the profile was created at: |
|
const kProfileCreationTime = "browser.laterrun.bookkeeping.profileCreationTime"; |
|
|
|
// After 50 sessions or 1 month since install, assume we will no longer be |
|
// interested in showing anything to "new" users |
|
const kSelfDestructSessionLimit = 50; |
|
const kSelfDestructHoursLimit = 31 * 24; |
|
|
|
class Page { |
|
constructor({pref, minimumHoursSinceInstall, minimumSessionCount, requireBoth, url}) { |
|
this.pref = pref; |
|
this.minimumHoursSinceInstall = minimumHoursSinceInstall || 0; |
|
this.minimumSessionCount = minimumSessionCount || 1; |
|
this.requireBoth = requireBoth || false; |
|
this.url = url; |
|
} |
|
|
|
get hasRun() { |
|
return Preferences.get(this.pref + "hasRun", false); |
|
} |
|
|
|
applies(sessionInfo) { |
|
if (this.hasRun) { |
|
return false; |
|
} |
|
if (this.requireBoth) { |
|
return sessionInfo.sessionCount >= this.minimumSessionCount && |
|
sessionInfo.hoursSinceInstall >= this.minimumHoursSinceInstall; |
|
} |
|
return sessionInfo.sessionCount >= this.minimumSessionCount || |
|
sessionInfo.hoursSinceInstall >= this.minimumHoursSinceInstall; |
|
} |
|
} |
|
|
|
let LaterRun = { |
|
init() { |
|
if (!this.enabled) { |
|
return; |
|
} |
|
// If this is the first run, set the time we were installed |
|
if (!Preferences.has(kProfileCreationTime)) { |
|
// We need to store seconds in order to fit within int prefs. |
|
Preferences.set(kProfileCreationTime, Math.floor(Date.now() / 1000)); |
|
} |
|
this.sessionCount++; |
|
|
|
if (this.hoursSinceInstall > kSelfDestructHoursLimit || |
|
this.sessionCount > kSelfDestructSessionLimit) { |
|
this.selfDestruct(); |
|
return; |
|
} |
|
}, |
|
|
|
// The enabled, hoursSinceInstall and sessionCount properties mirror the |
|
// preferences system, and are here for convenience. |
|
get enabled() { |
|
return Preferences.get(kEnabledPref, false); |
|
}, |
|
|
|
set enabled(val) { |
|
let wasEnabled = this.enabled; |
|
Preferences.set(kEnabledPref, val); |
|
if (val && !wasEnabled) { |
|
this.init(); |
|
} |
|
}, |
|
|
|
get hoursSinceInstall() { |
|
let installStamp = Preferences.get(kProfileCreationTime, Date.now() / 1000); |
|
return Math.floor((Date.now() / 1000 - installStamp) / 3600); |
|
}, |
|
|
|
get sessionCount() { |
|
if (this._sessionCount) { |
|
return this._sessionCount; |
|
} |
|
return this._sessionCount = Preferences.get(kSessionCountPref, 0); |
|
}, |
|
|
|
set sessionCount(val) { |
|
this._sessionCount = val; |
|
Preferences.set(kSessionCountPref, val); |
|
}, |
|
|
|
// Because we don't want to keep incrementing this indefinitely for no reason, |
|
// we will turn ourselves off after a set amount of time/sessions (see top of |
|
// file). |
|
selfDestruct() { |
|
Preferences.set(kEnabledPref, false); |
|
}, |
|
|
|
// Create an array of Page objects based on the currently set prefs |
|
readPages() { |
|
// Enumerate all the pages. |
|
let allPrefsForPages = Services.prefs.getChildList(kPagePrefRoot); |
|
let pageDataStore = new Map(); |
|
for (let pref of allPrefsForPages) { |
|
let [slug, prop] = pref.substring(kPagePrefRoot.length).split("."); |
|
if (!pageDataStore.has(slug)) { |
|
pageDataStore.set(slug, {pref: pref.substring(0, pref.length - prop.length)}); |
|
} |
|
let defaultPrefValue = 0; |
|
if (prop == "requireBoth" || prop == "hasRun") { |
|
defaultPrefValue = false; |
|
} else if (prop == "url") { |
|
defaultPrefValue = ""; |
|
} |
|
pageDataStore.get(slug)[prop] = Preferences.get(pref, defaultPrefValue); |
|
} |
|
let rv = []; |
|
for (let [, pageData] of pageDataStore) { |
|
if (pageData.url) { |
|
let uri = null; |
|
try { |
|
let urlString = Services.urlFormatter.formatURL(pageData.url.trim()); |
|
uri = Services.io.newURI(urlString, null, null); |
|
} catch (ex) { |
|
Cu.reportError("Invalid LaterRun page URL " + pageData.url + " ignored."); |
|
continue; |
|
} |
|
if (!uri.schemeIs("https")) { |
|
Cu.reportError("Insecure LaterRun page URL " + uri.spec + " ignored."); |
|
} else { |
|
pageData.url = uri.spec; |
|
rv.push(new Page(pageData)); |
|
} |
|
} |
|
} |
|
return rv; |
|
}, |
|
|
|
// Return a URL for display as a 'later run' page if its criteria are matched, |
|
// or null otherwise. |
|
// NB: will only return one page at a time; if multiple pages match, it's up |
|
// to the preference service which one gets shown first, and the next one |
|
// will be shown next startup instead. |
|
getURL() { |
|
if (!this.enabled) { |
|
return null; |
|
} |
|
let pages = this.readPages(); |
|
let page = pages.find(page => page.applies(this)); |
|
if (page) { |
|
Services.prefs.setBoolPref(page.pref + "hasRun", true); |
|
return page.url; |
|
} |
|
return null; |
|
}, |
|
}; |
|
|
|
LaterRun.init();
|
|
|