tor-browser

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

firefox-view-synced-tabs-error-handler.sys.mjs (6668B)


      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 /**
      6 * This module exports the SyncedTabsErrorHandler singleton, which handles
      7 * error states for synced tabs.
      8 */
      9 
     10 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
     11 
     12 const lazy = {};
     13 
     14 ChromeUtils.defineESModuleGetters(lazy, {
     15  UIState: "resource://services-sync/UIState.sys.mjs",
     16 });
     17 
     18 ChromeUtils.defineLazyGetter(lazy, "syncUtils", () => {
     19  return ChromeUtils.importESModule("resource://services-sync/util.sys.mjs")
     20    .Utils;
     21 });
     22 
     23 XPCOMUtils.defineLazyServiceGetter(
     24  lazy,
     25  "gNetworkLinkService",
     26  "@mozilla.org/network/network-link-service;1",
     27  Ci.nsINetworkLinkService
     28 );
     29 
     30 const FXA_ENABLED = "identity.fxaccounts.enabled";
     31 const NETWORK_STATUS_CHANGED = "network:offline-status-changed";
     32 const SYNC_SERVICE_ERROR = "weave:service:sync:error";
     33 const SYNC_SERVICE_FINISHED = "weave:service:sync:finish";
     34 const TOPIC_DEVICESTATE_CHANGED = "firefox-view.devicestate.changed";
     35 
     36 const ErrorType = Object.freeze({
     37  SYNC_ERROR: "sync-error",
     38  FXA_ADMIN_DISABLED: "fxa-admin-disabled",
     39  NETWORK_OFFLINE: "network-offline",
     40  SYNC_DISCONNECTED: "sync-disconnected",
     41  PASSWORD_LOCKED: "password-locked",
     42  SIGNED_OUT: "signed-out",
     43 });
     44 
     45 export const SyncedTabsErrorHandler = {
     46  init() {
     47    this.networkIsOnline =
     48      lazy.gNetworkLinkService.linkStatusKnown &&
     49      lazy.gNetworkLinkService.isLinkUp;
     50    this.syncIsConnected = lazy.UIState.get().syncEnabled;
     51    this.syncIsWorking = true;
     52 
     53    Services.obs.addObserver(this, NETWORK_STATUS_CHANGED);
     54    Services.obs.addObserver(this, lazy.UIState.ON_UPDATE);
     55    Services.obs.addObserver(this, SYNC_SERVICE_ERROR);
     56    Services.obs.addObserver(this, SYNC_SERVICE_FINISHED);
     57    Services.obs.addObserver(this, TOPIC_DEVICESTATE_CHANGED);
     58 
     59    return this;
     60  },
     61 
     62  get fxaSignedIn() {
     63    let { UIState } = lazy;
     64    let syncState = UIState.get();
     65    return (
     66      UIState.isReady() &&
     67      syncState.status === UIState.STATUS_SIGNED_IN &&
     68      // syncEnabled just checks the "services.sync.username" pref has a value
     69      syncState.syncEnabled
     70    );
     71  },
     72 
     73  getErrorType() {
     74    // this ordering is important for dealing with multiple errors at once
     75    const errorStates = {
     76      [ErrorType.NETWORK_OFFLINE]: !this.networkIsOnline,
     77      [ErrorType.FXA_ADMIN_DISABLED]: Services.prefs.prefIsLocked(FXA_ENABLED),
     78      [ErrorType.PASSWORD_LOCKED]: this.isPrimaryPasswordLocked,
     79      [ErrorType.SIGNED_OUT]:
     80        lazy.UIState.get().status === lazy.UIState.STATUS_LOGIN_FAILED,
     81      [ErrorType.SYNC_DISCONNECTED]: !this.syncIsConnected,
     82      [ErrorType.SYNC_ERROR]: !this.syncIsWorking && !this.syncHasWorked,
     83    };
     84 
     85    for (let [type, value] of Object.entries(errorStates)) {
     86      if (value) {
     87        return type;
     88      }
     89    }
     90    return null;
     91  },
     92 
     93  getFluentStringsForErrorType(type) {
     94    return Object.freeze(this._errorStateStringMappings[type]);
     95  },
     96 
     97  get isPrimaryPasswordLocked() {
     98    return lazy.syncUtils.mpLocked();
     99  },
    100 
    101  isSyncReady() {
    102    const fxaStatus = lazy.UIState.get().status;
    103    return (
    104      this.networkIsOnline &&
    105      (this.syncIsWorking || this.syncHasWorked) &&
    106      !Services.prefs.prefIsLocked(FXA_ENABLED) &&
    107      // it's an error for sync to not be connected if we are signed-in,
    108      // or for sync to be connected if the FxA status is "login_failed",
    109      // which can happen if a user updates their password on another device
    110      ((!this.syncIsConnected && fxaStatus !== lazy.UIState.STATUS_SIGNED_IN) ||
    111        (this.syncIsConnected &&
    112          fxaStatus !== lazy.UIState.STATUS_LOGIN_FAILED)) &&
    113      // We treat a locked primary password as an error if we are signed-in.
    114      // If the user dismisses the prompt to unlock, they can use the "Try again" button to prompt again
    115      (!this.isPrimaryPasswordLocked || !this.fxaSignedIn)
    116    );
    117  },
    118 
    119  observe(_, topic, data) {
    120    switch (topic) {
    121      case NETWORK_STATUS_CHANGED:
    122        this.networkIsOnline = data == "online";
    123        break;
    124      case lazy.UIState.ON_UPDATE:
    125        this.syncIsConnected = lazy.UIState.get().syncEnabled;
    126        break;
    127      case SYNC_SERVICE_ERROR:
    128        if (lazy.UIState.get().status == lazy.UIState.STATUS_SIGNED_IN) {
    129          this.syncIsWorking = false;
    130        }
    131        break;
    132      case SYNC_SERVICE_FINISHED:
    133        if (!this.syncIsWorking) {
    134          this.syncIsWorking = true;
    135          this.syncHasWorked = true;
    136        }
    137        break;
    138      case TOPIC_DEVICESTATE_CHANGED:
    139        this.syncHasWorked = false;
    140    }
    141  },
    142 
    143  ErrorType,
    144 
    145  // We map the error state strings to Fluent string IDs so that it's easier
    146  // to change strings in the future without having to update all of the
    147  // error state strings.
    148  _errorStateStringMappings: {
    149    [ErrorType.SYNC_ERROR]: {
    150      header: "firefoxview-tabpickup-sync-error-header",
    151      description: "firefoxview-tabpickup-generic-sync-error-description",
    152      buttonLabel: "firefoxview-tabpickup-sync-error-primarybutton",
    153    },
    154    [ErrorType.FXA_ADMIN_DISABLED]: {
    155      header: "firefoxview-tabpickup-fxa-admin-disabled-header",
    156      description: "firefoxview-tabpickup-fxa-disabled-by-policy-description",
    157      // The button is hidden for this errorState, so we don't include the
    158      // buttonLabel property.
    159    },
    160    [ErrorType.NETWORK_OFFLINE]: {
    161      header: "firefoxview-tabpickup-network-offline-header",
    162      description: "firefoxview-tabpickup-network-offline-description",
    163      buttonLabel: "firefoxview-tabpickup-network-offline-primarybutton",
    164    },
    165    [ErrorType.SYNC_DISCONNECTED]: {
    166      header: "firefoxview-tabpickup-sync-disconnected-header",
    167      description: "firefoxview-tabpickup-sync-disconnected-description",
    168      buttonLabel: "firefoxview-tabpickup-sync-disconnected-primarybutton",
    169    },
    170    [ErrorType.PASSWORD_LOCKED]: {
    171      header: "firefoxview-tabpickup-password-locked-header",
    172      description: "firefoxview-tabpickup-password-locked-description",
    173      buttonLabel: "firefoxview-tabpickup-password-locked-primarybutton",
    174      link: {
    175        label: "firefoxview-tabpickup-password-locked-link",
    176        href:
    177          Services.urlFormatter.formatURLPref("app.support.baseURL") +
    178          "primary-password-stored-logins",
    179      },
    180    },
    181    [ErrorType.SIGNED_OUT]: {
    182      header: "firefoxview-tabpickup-signed-out-header",
    183      description: "firefoxview-tabpickup-signed-out-description2",
    184      buttonLabel: "firefoxview-tabpickup-signed-out-primarybutton",
    185    },
    186  },
    187 }.init();