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();