forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			323 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			323 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* 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/. */
 | 
						|
 | 
						|
/* eslint-env mozilla/remote-page */
 | 
						|
 | 
						|
import { onToggleContainer } from "./helpers.mjs";
 | 
						|
 | 
						|
const { TabsSetupFlowManager } = ChromeUtils.importESModule(
 | 
						|
  "resource:///modules/firefox-view-tabs-setup-manager.sys.mjs"
 | 
						|
);
 | 
						|
 | 
						|
const TOPIC_SETUPSTATE_CHANGED = "firefox-view.setupstate.changed";
 | 
						|
const UI_OPEN_STATE = "browser.tabs.firefox-view.ui-state.tab-pickup.open";
 | 
						|
 | 
						|
class TabPickupContainer extends HTMLDetailsElement {
 | 
						|
  constructor() {
 | 
						|
    super();
 | 
						|
    this.boundObserve = (...args) => this.observe(...args);
 | 
						|
    this._currentSetupStateIndex = -1;
 | 
						|
    this.errorState = null;
 | 
						|
    this.tabListAdded = null;
 | 
						|
  }
 | 
						|
  get setupContainerElem() {
 | 
						|
    return this.querySelector(".sync-setup-container");
 | 
						|
  }
 | 
						|
 | 
						|
  get tabsContainerElem() {
 | 
						|
    return this.querySelector(".synced-tabs-container");
 | 
						|
  }
 | 
						|
 | 
						|
  get tabPickupListElem() {
 | 
						|
    return this.querySelector(".synced-tabs-container tab-pickup-list");
 | 
						|
  }
 | 
						|
 | 
						|
  getWindow() {
 | 
						|
    return this.ownerGlobal.browsingContext.embedderWindowGlobal.browsingContext
 | 
						|
      .window;
 | 
						|
  }
 | 
						|
 | 
						|
  connectedCallback() {
 | 
						|
    this.addEventListener("click", this);
 | 
						|
    this.addEventListener("toggle", this);
 | 
						|
    this.addEventListener("visibilitychange", this);
 | 
						|
    Services.obs.addObserver(this.boundObserve, TOPIC_SETUPSTATE_CHANGED);
 | 
						|
 | 
						|
    for (let elem of this.querySelectorAll("a[data-support-url]")) {
 | 
						|
      elem.href =
 | 
						|
        Services.urlFormatter.formatURLPref("app.support.baseURL") +
 | 
						|
        elem.dataset.supportUrl;
 | 
						|
    }
 | 
						|
 | 
						|
    // we wait until the list shows up before trying to populate it,
 | 
						|
    // when its safe to assume the custom-element's methods will be available
 | 
						|
    this.tabListAdded = this.promiseChildAdded();
 | 
						|
    this.update();
 | 
						|
  }
 | 
						|
 | 
						|
  promiseChildAdded() {
 | 
						|
    return new Promise(resolve => {
 | 
						|
      if (typeof this.tabPickupListElem?.getSyncedTabData == "function") {
 | 
						|
        resolve();
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      this.addEventListener(
 | 
						|
        "list-ready",
 | 
						|
        event => {
 | 
						|
          resolve();
 | 
						|
        },
 | 
						|
        { once: true }
 | 
						|
      );
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  cleanup() {
 | 
						|
    Services.obs.removeObserver(this.boundObserve, TOPIC_SETUPSTATE_CHANGED);
 | 
						|
  }
 | 
						|
 | 
						|
  disconnectedCallback() {
 | 
						|
    this.cleanup();
 | 
						|
  }
 | 
						|
 | 
						|
  handleEvent(event) {
 | 
						|
    if (event.type == "toggle") {
 | 
						|
      onToggleContainer(this);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    if (event.type == "click" && event.target.dataset.action) {
 | 
						|
      switch (event.target.dataset.action) {
 | 
						|
        case "view0-sync-error-action":
 | 
						|
        case "view0-network-offline-action":
 | 
						|
        case "view0-password-locked-action": {
 | 
						|
          TabsSetupFlowManager.tryToClearError();
 | 
						|
          break;
 | 
						|
        }
 | 
						|
        case "view0-signed-out-action":
 | 
						|
        case "view1-primary-action": {
 | 
						|
          TabsSetupFlowManager.openFxASignup(event.target.ownerGlobal);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
        case "view2-primary-action":
 | 
						|
        case "mobile-promo-primary-action": {
 | 
						|
          TabsSetupFlowManager.openFxAPairDevice(event.target.ownerGlobal);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
        case "view3-primary-action": {
 | 
						|
          TabsSetupFlowManager.syncOpenTabs(event.target);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
        case "mobile-promo-dismiss": {
 | 
						|
          TabsSetupFlowManager.dismissMobilePromo(event.target);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
        case "mobile-confirmation-dismiss": {
 | 
						|
          TabsSetupFlowManager.dismissMobileConfirmation(event.target);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
        case "view0-sync-disconnected-action": {
 | 
						|
          const window = event.target.ownerGlobal;
 | 
						|
          const {
 | 
						|
            switchToTabHavingURI,
 | 
						|
          } = window.docShell.chromeEventHandler.ownerGlobal;
 | 
						|
          switchToTabHavingURI(
 | 
						|
            "about:preferences?action=choose-what-to-sync#sync",
 | 
						|
            true,
 | 
						|
            {}
 | 
						|
          );
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
    // Returning to fxview seems like a likely time for a device check
 | 
						|
    if (
 | 
						|
      event.type == "visibilitychange" &&
 | 
						|
      document.visibilityState === "visible"
 | 
						|
    ) {
 | 
						|
      this.update();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  async observe(subject, topic, errorState) {
 | 
						|
    if (topic == TOPIC_SETUPSTATE_CHANGED) {
 | 
						|
      this.update({ errorState });
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  get mobilePromoElem() {
 | 
						|
    return this.querySelector(".promo-box");
 | 
						|
  }
 | 
						|
  get mobileSuccessElem() {
 | 
						|
    return this.querySelector(".confirmation-message-box");
 | 
						|
  }
 | 
						|
 | 
						|
  update({
 | 
						|
    stateIndex = TabsSetupFlowManager.uiStateIndex,
 | 
						|
    showMobilePromo = TabsSetupFlowManager.shouldShowMobilePromo,
 | 
						|
    showMobilePairSuccess = TabsSetupFlowManager.shouldShowMobileConnectedSuccess,
 | 
						|
    errorState = TabsSetupFlowManager.getErrorType(),
 | 
						|
    waitingForTabs = TabsSetupFlowManager.waitingForTabs,
 | 
						|
  } = {}) {
 | 
						|
    let needsRender = false;
 | 
						|
    if (waitingForTabs !== this._waitingForTabs) {
 | 
						|
      this._waitingForTabs = waitingForTabs;
 | 
						|
      needsRender = true;
 | 
						|
    }
 | 
						|
 | 
						|
    if (showMobilePromo !== this._showMobilePromo) {
 | 
						|
      this._showMobilePromo = showMobilePromo;
 | 
						|
      needsRender = true;
 | 
						|
    }
 | 
						|
    if (showMobilePairSuccess !== this._showMobilePairSuccess) {
 | 
						|
      this._showMobilePairSuccess = showMobilePairSuccess;
 | 
						|
      needsRender = true;
 | 
						|
    }
 | 
						|
    if (stateIndex == 4 && this._currentSetupStateIndex !== stateIndex) {
 | 
						|
      // trigger an initial request for the synced tabs list
 | 
						|
      this.tabListAdded.then(() => {
 | 
						|
        this.tabPickupListElem.getSyncedTabData();
 | 
						|
      });
 | 
						|
    }
 | 
						|
    if (stateIndex !== this._currentSetupStateIndex || stateIndex == 0) {
 | 
						|
      this._currentSetupStateIndex = stateIndex;
 | 
						|
      needsRender = true;
 | 
						|
      this.errorState = errorState;
 | 
						|
    }
 | 
						|
    needsRender && this.render();
 | 
						|
  }
 | 
						|
 | 
						|
  generateErrorMessage() {
 | 
						|
    // We map the error state strings to Fluent string IDs so that it's easier
 | 
						|
    // to change strings in the future without having to update all of the
 | 
						|
    // error state strings.
 | 
						|
    const errorStateStringMappings = {
 | 
						|
      "sync-error": {
 | 
						|
        header: "firefoxview-tabpickup-sync-error-header",
 | 
						|
        description: "firefoxview-tabpickup-generic-sync-error-description",
 | 
						|
        buttonLabel: "firefoxview-tabpickup-sync-error-primarybutton",
 | 
						|
      },
 | 
						|
 | 
						|
      "fxa-admin-disabled": {
 | 
						|
        header: "firefoxview-tabpickup-fxa-admin-disabled-header",
 | 
						|
        description: "firefoxview-tabpickup-fxa-admin-disabled-description",
 | 
						|
        // The button is hidden for this errorState, so we don't include the
 | 
						|
        // buttonLabel property.
 | 
						|
      },
 | 
						|
 | 
						|
      "network-offline": {
 | 
						|
        header: "firefoxview-tabpickup-network-offline-header",
 | 
						|
        description: "firefoxview-tabpickup-network-offline-description",
 | 
						|
        buttonLabel: "firefoxview-tabpickup-network-offline-primarybutton",
 | 
						|
      },
 | 
						|
 | 
						|
      "sync-disconnected": {
 | 
						|
        header: "firefoxview-tabpickup-sync-disconnected-header",
 | 
						|
        description: "firefoxview-tabpickup-sync-disconnected-description",
 | 
						|
        buttonLabel: "firefoxview-tabpickup-sync-disconnected-primarybutton",
 | 
						|
      },
 | 
						|
 | 
						|
      "password-locked": {
 | 
						|
        header: "firefoxview-tabpickup-password-locked-header",
 | 
						|
        description: "firefoxview-tabpickup-password-locked-description",
 | 
						|
        buttonLabel: "firefoxview-tabpickup-password-locked-primarybutton",
 | 
						|
        link: {
 | 
						|
          label: "firefoxview-tabpickup-password-locked-link",
 | 
						|
          href:
 | 
						|
            Services.urlFormatter.formatURLPref("app.support.baseURL") +
 | 
						|
            "primary-password-stored-logins",
 | 
						|
        },
 | 
						|
      },
 | 
						|
      "signed-out": {
 | 
						|
        header: "firefoxview-tabpickup-signed-out-header",
 | 
						|
        description: "firefoxview-tabpickup-signed-out-description",
 | 
						|
        buttonLabel: "firefoxview-tabpickup-signed-out-primarybutton",
 | 
						|
      },
 | 
						|
    };
 | 
						|
 | 
						|
    const errorStateHeader = this.querySelector(
 | 
						|
      "#tabpickup-steps-view0-header"
 | 
						|
    );
 | 
						|
    const errorStateDescription = this.querySelector(
 | 
						|
      "#error-state-description"
 | 
						|
    );
 | 
						|
    const errorStateButton = this.querySelector("#error-state-button");
 | 
						|
    const errorStateLink = this.querySelector("#error-state-link");
 | 
						|
    const errorStateProperties = errorStateStringMappings[this.errorState];
 | 
						|
 | 
						|
    document.l10n.setAttributes(errorStateHeader, errorStateProperties.header);
 | 
						|
    document.l10n.setAttributes(
 | 
						|
      errorStateDescription,
 | 
						|
      errorStateProperties.description
 | 
						|
    );
 | 
						|
 | 
						|
    errorStateButton.hidden = this.errorState == "fxa-admin-disabled";
 | 
						|
 | 
						|
    if (this.errorState != "fxa-admin-disabled") {
 | 
						|
      document.l10n.setAttributes(
 | 
						|
        errorStateButton,
 | 
						|
        errorStateProperties.buttonLabel
 | 
						|
      );
 | 
						|
      errorStateButton.setAttribute(
 | 
						|
        "data-action",
 | 
						|
        `view0-${this.errorState}-action`
 | 
						|
      );
 | 
						|
    }
 | 
						|
 | 
						|
    if (errorStateProperties.link) {
 | 
						|
      document.l10n.setAttributes(
 | 
						|
        errorStateLink,
 | 
						|
        errorStateProperties.link.label
 | 
						|
      );
 | 
						|
      errorStateLink.href = errorStateProperties.link.href;
 | 
						|
      errorStateLink.hidden = false;
 | 
						|
    } else {
 | 
						|
      errorStateLink.hidden = true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  render() {
 | 
						|
    if (!this.isConnected) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    let setupElem = this.setupContainerElem;
 | 
						|
    let tabsElem = this.tabsContainerElem;
 | 
						|
    let mobilePromoElem = this.mobilePromoElem;
 | 
						|
    let mobileSuccessElem = this.mobileSuccessElem;
 | 
						|
 | 
						|
    const stateIndex = this._currentSetupStateIndex;
 | 
						|
    const isLoading = this._waitingForTabs;
 | 
						|
 | 
						|
    mobilePromoElem.hidden = !this._showMobilePromo;
 | 
						|
    mobileSuccessElem.hidden = !this._showMobilePairSuccess;
 | 
						|
 | 
						|
    this.open =
 | 
						|
      !TabsSetupFlowManager.isTabSyncSetupComplete ||
 | 
						|
      Services.prefs.getBoolPref(UI_OPEN_STATE, true);
 | 
						|
 | 
						|
    // show/hide either the setup or tab list containers, creating each as necessary
 | 
						|
    if (stateIndex < 4) {
 | 
						|
      tabsElem.hidden = true;
 | 
						|
      setupElem.hidden = false;
 | 
						|
      setupElem.selectedViewName = `sync-setup-view${stateIndex}`;
 | 
						|
 | 
						|
      if (stateIndex == 0 && this.errorState) {
 | 
						|
        this.generateErrorMessage();
 | 
						|
      }
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    setupElem.hidden = true;
 | 
						|
    tabsElem.hidden = false;
 | 
						|
    tabsElem.classList.toggle("loading", isLoading);
 | 
						|
  }
 | 
						|
 | 
						|
  async onReload() {
 | 
						|
    await TabsSetupFlowManager.syncOnPageReload();
 | 
						|
  }
 | 
						|
}
 | 
						|
customElements.define("tab-pickup-container", TabPickupContainer, {
 | 
						|
  extends: "details",
 | 
						|
});
 | 
						|
 | 
						|
export { TabPickupContainer };
 |