2016-02-04 04:04:56 +03:00
|
|
|
/* 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";
|
|
|
|
|
2018-02-23 22:50:01 +03:00
|
|
|
var EXPORTED_SYMBOLS = ["LaterRun"];
|
2016-02-04 04:04:56 +03:00
|
|
|
|
2018-01-30 02:20:18 +03:00
|
|
|
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
2016-02-04 04:04:56 +03:00
|
|
|
|
|
|
|
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() {
|
2017-07-28 22:20:27 +03:00
|
|
|
return Services.prefs.getBoolPref(this.pref + "hasRun", false);
|
2016-02-04 04:04:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2017-07-28 22:20:27 +03:00
|
|
|
if (Services.prefs.getPrefType(kProfileCreationTime) == Ci.nsIPrefBranch.PREF_INVALID) {
|
2016-02-04 04:04:56 +03:00
|
|
|
// We need to store seconds in order to fit within int prefs.
|
2017-07-28 22:20:27 +03:00
|
|
|
Services.prefs.setIntPref(kProfileCreationTime, Math.floor(Date.now() / 1000));
|
2016-02-04 04:04:56 +03:00
|
|
|
}
|
|
|
|
this.sessionCount++;
|
|
|
|
|
|
|
|
if (this.hoursSinceInstall > kSelfDestructHoursLimit ||
|
|
|
|
this.sessionCount > kSelfDestructSessionLimit) {
|
|
|
|
this.selfDestruct();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// The enabled, hoursSinceInstall and sessionCount properties mirror the
|
|
|
|
// preferences system, and are here for convenience.
|
|
|
|
get enabled() {
|
2017-07-28 22:20:27 +03:00
|
|
|
return Services.prefs.getBoolPref(kEnabledPref, false);
|
2016-02-04 04:04:56 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
set enabled(val) {
|
|
|
|
let wasEnabled = this.enabled;
|
2017-07-28 22:20:27 +03:00
|
|
|
Services.prefs.setBoolPref(kEnabledPref, val);
|
2016-02-04 04:04:56 +03:00
|
|
|
if (val && !wasEnabled) {
|
|
|
|
this.init();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
get hoursSinceInstall() {
|
2017-07-28 22:20:27 +03:00
|
|
|
let installStamp = Services.prefs.getIntPref(kProfileCreationTime, Date.now() / 1000);
|
2016-02-04 04:04:56 +03:00
|
|
|
return Math.floor((Date.now() / 1000 - installStamp) / 3600);
|
|
|
|
},
|
|
|
|
|
|
|
|
get sessionCount() {
|
|
|
|
if (this._sessionCount) {
|
|
|
|
return this._sessionCount;
|
|
|
|
}
|
2017-07-28 22:20:27 +03:00
|
|
|
return this._sessionCount = Services.prefs.getIntPref(kSessionCountPref, 0);
|
2016-02-04 04:04:56 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
set sessionCount(val) {
|
|
|
|
this._sessionCount = val;
|
2017-07-28 22:20:27 +03:00
|
|
|
Services.prefs.setIntPref(kSessionCountPref, val);
|
2016-02-04 04:04:56 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
// 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() {
|
2017-07-28 22:20:27 +03:00
|
|
|
Services.prefs.setBoolPref(kEnabledPref, false);
|
2016-02-04 04:04:56 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
// 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)});
|
|
|
|
}
|
|
|
|
if (prop == "requireBoth" || prop == "hasRun") {
|
2017-07-28 22:20:27 +03:00
|
|
|
pageDataStore.get(slug)[prop] = Services.prefs.getBoolPref(pref, false);
|
2016-02-04 04:04:56 +03:00
|
|
|
} else if (prop == "url") {
|
2017-07-28 22:20:27 +03:00
|
|
|
pageDataStore.get(slug)[prop] = Services.prefs.getStringPref(pref, "");
|
|
|
|
} else {
|
|
|
|
pageDataStore.get(slug)[prop] = Services.prefs.getIntPref(pref, 0);
|
2016-02-04 04:04:56 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
let rv = [];
|
|
|
|
for (let [, pageData] of pageDataStore) {
|
|
|
|
if (pageData.url) {
|
|
|
|
let uri = null;
|
|
|
|
try {
|
2016-05-06 11:40:10 +03:00
|
|
|
let urlString = Services.urlFormatter.formatURL(pageData.url.trim());
|
2017-01-09 22:27:25 +03:00
|
|
|
uri = Services.io.newURI(urlString);
|
2016-02-04 04:04:56 +03:00
|
|
|
} catch (ex) {
|
|
|
|
Cu.reportError("Invalid LaterRun page URL " + pageData.url + " ignored.");
|
2016-05-06 11:40:10 +03:00
|
|
|
continue;
|
2016-02-04 04:04:56 +03:00
|
|
|
}
|
|
|
|
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();
|
2016-11-16 06:58:30 +03:00
|
|
|
let page = pages.find(p => p.applies(this));
|
2016-02-04 04:04:56 +03:00
|
|
|
if (page) {
|
|
|
|
Services.prefs.setBoolPref(page.pref + "hasRun", true);
|
|
|
|
return page.url;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
LaterRun.init();
|