tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

AboutWelcomeParent.sys.mjs (11049B)


      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
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 const lazy = {};
      6 
      7 ChromeUtils.defineESModuleGetters(lazy, {
      8  AboutWelcomeDefaults:
      9    "resource:///modules/aboutwelcome/AboutWelcomeDefaults.sys.mjs",
     10  AboutWelcomeTelemetry:
     11    "resource:///modules/aboutwelcome/AboutWelcomeTelemetry.sys.mjs",
     12  AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
     13  AWScreenUtils: "resource:///modules/aboutwelcome/AWScreenUtils.sys.mjs",
     14  BackupService: "resource:///modules/backup/BackupService.sys.mjs",
     15  BrowserUtils: "resource://gre/modules/BrowserUtils.sys.mjs",
     16  BuiltInThemes: "resource:///modules/BuiltInThemes.sys.mjs",
     17  FxAccounts: "resource://gre/modules/FxAccounts.sys.mjs",
     18  LangPackMatcher: "resource://gre/modules/LangPackMatcher.sys.mjs",
     19  ShellService: "moz-src:///browser/components/shell/ShellService.sys.mjs",
     20  SpecialMessageActions:
     21    "resource://messaging-system/lib/SpecialMessageActions.sys.mjs",
     22 });
     23 
     24 ChromeUtils.defineLazyGetter(lazy, "log", () => {
     25  const { Logger } = ChromeUtils.importESModule(
     26    "resource://messaging-system/lib/Logger.sys.mjs"
     27  );
     28  return new Logger("AboutWelcomeParent");
     29 });
     30 
     31 ChromeUtils.defineLazyGetter(
     32  lazy,
     33  "Telemetry",
     34  () => new lazy.AboutWelcomeTelemetry()
     35 );
     36 
     37 const DID_SEE_ABOUT_WELCOME_PREF = "trailhead.firstrun.didSeeAboutWelcome";
     38 const DID_HANDLE_CAMAPAIGN_ACTION_PREF =
     39  "trailhead.firstrun.didHandleCampaignAction";
     40 const AWTerminate = {
     41  WINDOW_CLOSED: "welcome-window-closed",
     42  TAB_CLOSED: "welcome-tab-closed",
     43  APP_SHUT_DOWN: "app-shut-down",
     44  ADDRESS_BAR_NAVIGATED: "address-bar-navigated",
     45 };
     46 const LIGHT_WEIGHT_THEMES = {
     47  AUTOMATIC: "default-theme@mozilla.org",
     48  DARK: "firefox-compact-dark@mozilla.org",
     49  LIGHT: "firefox-compact-light@mozilla.org",
     50  ALPENGLOW: "firefox-alpenglow@mozilla.org",
     51 };
     52 
     53 class AboutWelcomeObserver {
     54  constructor() {
     55    Services.obs.addObserver(this, "quit-application");
     56 
     57    this.win = Services.focus.activeWindow;
     58    if (!this.win) {
     59      return;
     60    }
     61 
     62    this.terminateReason = AWTerminate.ADDRESS_BAR_NAVIGATED;
     63 
     64    this.onWindowClose = () => {
     65      this.terminateReason = AWTerminate.WINDOW_CLOSED;
     66    };
     67 
     68    this.onTabClose = () => {
     69      this.terminateReason = AWTerminate.TAB_CLOSED;
     70    };
     71 
     72    this.win.addEventListener("TabClose", this.onTabClose, { once: true });
     73    this.win.addEventListener("unload", this.onWindowClose, { once: true });
     74  }
     75 
     76  observe(aSubject, aTopic) {
     77    switch (aTopic) {
     78      case "quit-application":
     79        this.terminateReason = AWTerminate.APP_SHUT_DOWN;
     80        break;
     81    }
     82  }
     83 
     84  // Added for testing
     85  get AWTerminate() {
     86    return AWTerminate;
     87  }
     88 
     89  stop() {
     90    lazy.log.debug(`Terminate reason is ${this.terminateReason}`);
     91    // Clear the entrypoint pref
     92    Services.prefs.clearUserPref("browser.aboutwelcome.entrypoint");
     93    Services.obs.removeObserver(this, "quit-application");
     94    if (!this.win) {
     95      return;
     96    }
     97    this.win.removeEventListener("TabClose", this.onTabClose);
     98    this.win.removeEventListener("unload", this.onWindowClose);
     99    this.win = null;
    100  }
    101 }
    102 
    103 export class AboutWelcomeParent extends JSWindowActorParent {
    104  constructor() {
    105    super();
    106    this.startAboutWelcomeObserver();
    107  }
    108 
    109  startAboutWelcomeObserver() {
    110    this.AboutWelcomeObserver = new AboutWelcomeObserver();
    111  }
    112 
    113  // Static methods that calls into ShellService to check
    114  // if Firefox is pinned or already default
    115  static async doesAppNeedPin() {
    116    return (
    117      (await lazy.ShellService.doesAppNeedPin()) ||
    118      (await lazy.ShellService.doesAppNeedStartMenuPin())
    119    );
    120  }
    121 
    122  static isDefaultBrowser() {
    123    return lazy.ShellService.isDefaultBrowser();
    124  }
    125 
    126  didDestroy() {
    127    if (this.AboutWelcomeObserver) {
    128      this.AboutWelcomeObserver.stop();
    129    }
    130    this.RegionHomeObserver?.stop();
    131 
    132    lazy.Telemetry.sendTelemetry({
    133      event: "SESSION_END",
    134      event_context: {
    135        reason: this.AboutWelcomeObserver.terminateReason,
    136        page: "about:welcome",
    137      },
    138      message_id: this.AWMessageId,
    139    });
    140  }
    141 
    142  /**
    143   * Handle messages from AboutWelcomeChild.sys.mjs
    144   *
    145   * @param {string} type
    146   * @param {any=} data
    147   * @param {Browser} the xul:browser rendering the page
    148   */
    149  async onContentMessage(type, data, browser) {
    150    lazy.log.debug(`Received content event: ${type}`);
    151    switch (type) {
    152      case "AWPage:SET_WELCOME_MESSAGE_SEEN":
    153        this.AWMessageId = data;
    154        try {
    155          Services.prefs.setBoolPref(DID_SEE_ABOUT_WELCOME_PREF, true);
    156        } catch (e) {
    157          lazy.log.debug(`Fails to set ${DID_SEE_ABOUT_WELCOME_PREF}.`);
    158        }
    159        break;
    160      case "AWPage:SPECIAL_ACTION":
    161        return lazy.SpecialMessageActions.handleAction(data, browser);
    162      case "AWPage:FXA_METRICS_FLOW_URI":
    163        return lazy.FxAccounts.config.promiseMetricsFlowURI("aboutwelcome");
    164      case "AWPage:TELEMETRY_EVENT":
    165        lazy.Telemetry.sendTelemetry(data);
    166        break;
    167      case "AWPage:GET_ATTRIBUTION_DATA": {
    168        let attributionData =
    169          await lazy.AboutWelcomeDefaults.getAttributionContent();
    170        return attributionData;
    171      }
    172      case "AWPage:ENSURE_ADDON_INSTALLED":
    173        return new Promise(resolve => {
    174          let listener = {
    175            onInstallEnded(install, addon) {
    176              if (addon.id === data) {
    177                lazy.AddonManager.removeInstallListener(listener);
    178                resolve("complete");
    179              }
    180            },
    181            onInstallCancelled() {
    182              lazy.AddonManager.removeInstallListener(listener);
    183              resolve("install cancelled");
    184            },
    185            onDownloadCancelled() {
    186              lazy.AddonManager.removeInstallListener(listener);
    187              resolve("install cancelled");
    188            },
    189            onInstallFailed() {
    190              lazy.AddonManager.removeInstallListener(listener);
    191              resolve("install failed");
    192            },
    193          };
    194          lazy.AddonManager.addInstallListener(listener);
    195        });
    196      case "AWPage:GET_INSTALLED_ADDONS":
    197        return lazy.AddonManager.getActiveAddons().then(response =>
    198          response.addons.map(addon => addon.id)
    199        );
    200      case "AWPage:GET_ADDON_DETAILS": {
    201        let addonDetails =
    202          await lazy.AboutWelcomeDefaults.getAddonFromRepository(data);
    203 
    204        return {
    205          addonId: addonDetails.id,
    206          label: addonDetails.name,
    207          icon: addonDetails.iconURL,
    208          type: addonDetails.type,
    209          screenshots: addonDetails.screenshots,
    210          url: addonDetails.url,
    211        };
    212      }
    213      case "AWPage:SELECT_THEME":
    214        await lazy.BuiltInThemes.ensureBuiltInThemes();
    215        return lazy.AddonManager.getAddonByID(LIGHT_WEIGHT_THEMES[data]).then(
    216          addon => addon.enable()
    217        );
    218      case "AWPage:GET_SELECTED_THEME": {
    219        let themes = await lazy.AddonManager.getAddonsByTypes(["theme"]);
    220        let activeTheme = themes.find(addon => addon.isActive);
    221        // Store the current theme ID so user can restore their previous theme.
    222        if (activeTheme?.id) {
    223          LIGHT_WEIGHT_THEMES.AUTOMATIC = activeTheme.id;
    224        }
    225        // convert this to the short form name that the front end code
    226        // expects
    227        let themeShortName = Object.keys(LIGHT_WEIGHT_THEMES).find(
    228          key => LIGHT_WEIGHT_THEMES[key] === activeTheme?.id
    229        );
    230        return themeShortName?.toLowerCase();
    231      }
    232      case "AWPage:DOES_APP_NEED_PIN":
    233        return AboutWelcomeParent.doesAppNeedPin();
    234      case "AWPage:NEED_DEFAULT":
    235        // Only need to set default if we're supposed to check and not default.
    236        return (
    237          Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser") &&
    238          !AboutWelcomeParent.isDefaultBrowser()
    239        );
    240      case "AWPage:WAIT_FOR_MIGRATION_CLOSE":
    241        // Support multiples types of migration: 1) content modal 2) old
    242        // migration modal 3) standalone content modal
    243        return new Promise(resolve => {
    244          const topics = [
    245            "MigrationWizard:Closed",
    246            "MigrationWizard:Destroyed",
    247          ];
    248          const observer = () => {
    249            topics.forEach(t => Services.obs.removeObserver(observer, t));
    250            resolve();
    251          };
    252          topics.forEach(t => Services.obs.addObserver(observer, t));
    253        });
    254      case "AWPage:GET_APP_AND_SYSTEM_LOCALE_INFO":
    255        return lazy.LangPackMatcher.getAppAndSystemLocaleInfo();
    256      case "AWPage:EVALUATE_SCREEN_TARGETING":
    257        return lazy.AWScreenUtils.evaluateTargetingAndRemoveScreens(data);
    258      case "AWPage:ADD_SCREEN_IMPRESSION":
    259        return lazy.AWScreenUtils.addScreenImpression(data);
    260      case "AWPage:EVALUATE_ATTRIBUTE_TARGETING":
    261        return lazy.AWScreenUtils.evaluateScreenTargeting(data);
    262      case "AWPage:NEGOTIATE_LANGPACK":
    263        return lazy.LangPackMatcher.negotiateLangPackForLanguageMismatch(data);
    264      case "AWPage:ENSURE_LANG_PACK_INSTALLED":
    265        return lazy.LangPackMatcher.ensureLangPackInstalled(data);
    266      case "AWPage:SET_REQUESTED_LOCALES":
    267        return lazy.LangPackMatcher.setRequestedAppLocales(data);
    268      case "AWPage:SEND_TO_DEVICE_EMAILS_SUPPORTED": {
    269        return lazy.BrowserUtils.sendToDeviceEmailsSupported();
    270      }
    271      case "AWPage:GET_UNHANDLED_CAMPAIGN_ACTION": {
    272        if (
    273          !Services.prefs.getBoolPref(DID_HANDLE_CAMAPAIGN_ACTION_PREF, false)
    274        ) {
    275          return lazy.AWScreenUtils.getUnhandledCampaignAction();
    276        }
    277        break;
    278      }
    279      case "AWPage:HANDLE_CAMPAIGN_ACTION": {
    280        if (
    281          !Services.prefs.getBoolPref(DID_HANDLE_CAMAPAIGN_ACTION_PREF, false)
    282        ) {
    283          lazy.SpecialMessageActions.handleAction({ type: data }, browser);
    284          try {
    285            Services.prefs.setBoolPref(DID_HANDLE_CAMAPAIGN_ACTION_PREF, true);
    286          } catch (e) {
    287            lazy.log.debug(`Fails to set ${DID_HANDLE_CAMAPAIGN_ACTION_PREF}.`);
    288          }
    289          return true;
    290        }
    291        break;
    292      }
    293      case "AWPage:BACKUP_FIND_WELL_KNOWN": {
    294        // Ask the BackupService to probe default locations.
    295        let bs;
    296        try {
    297          bs = lazy.BackupService.get();
    298        } catch {
    299          bs = lazy.BackupService.init();
    300        }
    301        return bs.findBackupsInWellKnownLocations(data);
    302      }
    303      default:
    304        lazy.log.debug(`Unexpected event ${type} was not handled.`);
    305    }
    306 
    307    return undefined;
    308  }
    309 
    310  /**
    311   * @param {{name: string, data?: any}} message
    312   * @override
    313   */
    314  receiveMessage(message) {
    315    const { name, data } = message;
    316    let browser;
    317 
    318    if (this.manager.rootFrameLoader) {
    319      browser = this.manager.rootFrameLoader.ownerElement;
    320      return this.onContentMessage(name, data, browser);
    321    }
    322 
    323    lazy.log.warn(`Not handling ${name} because the browser doesn't exist.`);
    324    return null;
    325  }
    326 }