EY);
      },
      /**
       * Shows the Primary Password prompt if enabled, or the
       * OS auth dialog otherwise.
       *
       * @param resolve Callback that is called with result of authentication.
       * @param messageId The string ID that corresponds to a string stored in aboutLogins.ftl.
       *                  This string will be displayed only when the OS auth dialog is used.
       * @param reason The reason for requesting reauthentication, used for telemetry.
       */
      async promptForPrimaryPassword(resolve, messageId, reason) {
        gPrimaryPasswordPromise = {
          resolve,
        };

        that.sendAsyncMessage("AboutLogins:PrimaryPasswordRequest", {
          messageId,
          reason,
        });

        return gPrimaryPasswordPromise;
      },
      // Default to enabled just in case a search is attempted before we get a response.
      primaryPasswordEnabled: true,
      passwordRevealVisible: true,
    };
    waivedContent.AboutLoginsUtils = Cu.cloneInto(
      AboutLoginsUtils,
      waivedContent,
      {
        cloneFunctions: true,
      }
    );
  }

  #aboutLoginsImportReportInit() {
    this.sendAsyncMessage("AboutLogins:ImportReportInit");
  }

  #aboutLoginsCopyLoginDetail(detail) {
    lazy.ClipboardHelper.copyString(
      detail,
      this.windowContext,
      lazy.ClipboardHelper.Sensitive
    );
  }

  #aboutLoginsCreateLogin(login) {
    this.sendAsyncMessage("AboutLogins:CreateLogin", {
      login,
    });
  }

  #aboutLoginsDeleteLogin(login) {
    this.sendAsyncMessage("AboutLogins:DeleteLogin", {
      login,
    });
  }

  #aboutLoginsExportPasswords() {
    this.sendAsyncMessage("AboutLogins:ExportPasswords");
  }

  #aboutLoginsGetHelp() {
    this.sendAsyncMessage("AboutLogins:GetHelp");
  }

  #aboutLoginsImportFromBrowser() {
    this.sendAsyncMessage("AboutLogins:ImportFromBrowser");
    recordTelemetryEvent({
      name: "mgmtMenuItemUsedImportFromBrowser",
    });
  }

  #aboutLoginsImportFromFile() {
    this.sendAsyncMessage("AboutLogins:ImportFromFile");
    recordTelemetryEvent({
      name: "mgmtMenuItemUsedImportFromCsv",
    });
  }

  #aboutLoginsOpenPreferences() {
    this.sendAsyncMessage("AboutLogins:OpenPreferences");
    recordTelemetryEvent({
      name: "mgmtMenuItemUsedPreferences",
    });
  }

  #aboutLoginsRecordTelemetryEvent(event) {
    if (event.detail.name.startsWith("openManagement")) {
      let { docShell } = this.browsingContext;
      // Compare to the last time open_management was recorded for the same
      // outerWindowID to not double-count them due to a redirect to remove
      // the entryPoint query param (since replaceState isn't allowed for
      // about:). Don't use performance.now for the tab since you can't
      // compare that number between different tabs and this JSM is shared.
      let now = docShell.now();
      if (
        this.browsingContext.browserId == gLastOpenManagementBrowserId &&
        now - gLastOpenManagementEventTime <
          TELEMETRY_MIN_MS_BETWEEN_OPEN_MANAGEMENT
      ) {
        return;
      }
      gLastOpenManagementEventTime = now;
      gLastOpenManagementBrowserId = this.browsingContext.browserId;
    }
    recordTelemetryEvent(event.detail);
  }

  #aboutLoginsRemoveAllLogins() {
    this.sendAsyncMessage("AboutLogins:RemoveAllLogins");
  }

  #aboutLoginsSortChanged(detail) {
    this.sendAsyncMessage("AboutLogins:SortChanged", detail);
  }

  #aboutLoginsSyncEnable() {
    this.sendAsyncMessage("AboutLogins:SyncEnable");
  }

  #aboutLoginsUpdateLogin(login) {
    this.sendAsyncMessage("AboutLogins:UpdateLogin", {
      login,
    });
  }

  // eslint-disable-next-line consistent-return
  receiveMessage(message) {
    switch (message.name) {
      case "AboutLogins:ImportReportData":
        this.#importReportData(message.data);
        break;
      case "AboutLogins:PrimaryPasswordResponse":
        this.#primaryPasswordResponse(message.data);
        break;
      case "AboutLogins:RemaskPassword":
        this.#remaskPassword(message.data);
        break;
      case "AboutLogins:Setup":
        this.#setup(message.data);
        break;
      case "AboutLogins:WaitForFocus": {
        return new Promise(resolve => {
          if (!this.document.hasFocus()) {
            this.document.ownerGlobal.addEventListener(
              "focus",
              () => {
                resolve();
              },
              { once: true }
            );
          } else {
            resolve();
          }
        });
      }
      default:
        this.#passMessageDataToContent(message);
    }
  }

  #importReportData(data) {
    this.sendToContent("ImportReportData", data);
  }

  #primaryPasswordResponse(data) {
    if (gPrimaryPasswordPromise) {
      gPrimaryPasswordPromise.resolve(data.result);
      recordTelemetryEvent(data.telemetryEvent);
    }
  }

  #remaskPassword(data) {
    this.sendToContent("RemaskPassword", data);
  }

  #setup(data) {
    let utils = Cu.waiveXrays(this.browsingContext.window).AboutLoginsUtils;
    utils.primaryPasswordEnabled = data.primaryPasswordEnabled;
    utils.passwordRevealVisible = data.passwordRevealVisible;
    utils.importVisible = data.importVisible;
    utils.supportBaseURL = Services.urlFormatter.formatURLPref(
      "app.support.baseURL"
    );
    this.sendToContent("Setup", data);
  }

  #passMessageDataToContent(message) {
    this.sendToContent(message.name.replace("AboutLogins:", ""), message.data);
  }

  sendToContent(messageType, detail) {
    let win = this.document.defaultView;
    let message = Object.assign({ messageType }, { value: detail });
    let event = new win.CustomEvent("AboutLoginsChromeToContent", {
      detail: Cu.cloneInto(message, win),
    });
    win.dispatchEvent(event);
  }
}
PK