DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (5350524bb654)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
/* 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();