1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 15:53:27 +00:00

PM-19378 remove v1 notification bar path (#13973)

* PM-19378
- Remove NotificationBarAddLoginImproments flag
- Remove divergent paths in various files
- Fix tests to reflect new singular path

* PM-19378
- Remove applyRedesign from relevant files
- Update styling to reflect changes

* remove notification-bar

* edit styling for to accodmmodate v3 in iframe
This commit is contained in:
Daniel Riera
2025-04-02 09:30:47 -04:00
committed by GitHub
parent f13bc2766b
commit 9080a5a024
13 changed files with 147 additions and 1351 deletions

View File

@@ -1,8 +1,6 @@
import { mock, MockProxy } from "jest-mock-extended";
import { BehaviorSubject } from "rxjs";
import { CLEAR_NOTIFICATION_LOGIN_DATA_DURATION } from "@bitwarden/common/autofill/constants";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { ServerConfig } from "@bitwarden/common/platform/abstractions/config/server-config";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { EnvironmentServerConfigData } from "@bitwarden/common/platform/models/data/server-config.data";
@@ -25,8 +23,6 @@ import { OverlayNotificationsBackground } from "./overlay-notifications.backgrou
describe("OverlayNotificationsBackground", () => {
let logService: MockProxy<LogService>;
let getFeatureFlagMock$: BehaviorSubject<boolean>;
let configService: MockProxy<ConfigService>;
let notificationBackground: NotificationBackground;
let getEnableChangedPasswordPromptSpy: jest.SpyInstance;
let getEnableAddedLoginPromptSpy: jest.SpyInstance;
@@ -35,10 +31,6 @@ describe("OverlayNotificationsBackground", () => {
beforeEach(async () => {
jest.useFakeTimers();
logService = mock<LogService>();
getFeatureFlagMock$ = new BehaviorSubject(true);
configService = mock<ConfigService>({
getFeatureFlag$: jest.fn().mockReturnValue(getFeatureFlagMock$),
});
notificationBackground = mock<NotificationBackground>();
getEnableChangedPasswordPromptSpy = jest
.spyOn(notificationBackground, "getEnableChangedPasswordPrompt")
@@ -48,10 +40,8 @@ describe("OverlayNotificationsBackground", () => {
.mockResolvedValue(true);
overlayNotificationsBackground = new OverlayNotificationsBackground(
logService,
configService,
notificationBackground,
);
configService.getFeatureFlag.mockResolvedValue(true);
await overlayNotificationsBackground.init();
});
@@ -60,27 +50,6 @@ describe("OverlayNotificationsBackground", () => {
jest.clearAllTimers();
});
describe("feature flag behavior", () => {
let runtimeRemoveListenerSpy: jest.SpyInstance;
beforeEach(() => {
runtimeRemoveListenerSpy = jest.spyOn(chrome.runtime.onMessage, "removeListener");
});
it("removes the extension listeners if the current flag value is set to `false`", () => {
getFeatureFlagMock$.next(false);
expect(runtimeRemoveListenerSpy).toHaveBeenCalled();
});
it("ignores the feature flag change if the previous flag value is equal to the current flag value", () => {
getFeatureFlagMock$.next(false);
getFeatureFlagMock$.next(false);
expect(runtimeRemoveListenerSpy).toHaveBeenCalledTimes(1);
});
});
describe("setting up the form submission listeners", () => {
let fields: MockProxy<AutofillField>[];
let details: MockProxy<AutofillPageDetails>;

View File

@@ -1,11 +1,8 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { startWith, Subject, Subscription, switchMap, timer } from "rxjs";
import { pairwise } from "rxjs/operators";
import { Subject, switchMap, timer } from "rxjs";
import { CLEAR_NOTIFICATION_LOGIN_DATA_DURATION } from "@bitwarden/common/autofill/constants";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { BrowserApi } from "../../platform/browser/browser-api";
@@ -26,7 +23,6 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg
private websiteOriginsWithFields: WebsiteOriginsWithFields = new Map();
private activeFormSubmissionRequests: ActiveFormSubmissionRequests = new Set();
private modifyLoginCipherFormData: ModifyLoginCipherFormDataForTab = new Map();
private featureFlagState$: Subscription;
private clearLoginCipherFormDataSubject: Subject<void> = new Subject();
private notificationFallbackTimeout: number | NodeJS.Timeout | null;
private readonly formSubmissionRequestMethods: Set<string> = new Set(["POST", "PUT", "PATCH"]);
@@ -38,7 +34,6 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg
constructor(
private logService: LogService,
private configService: ConfigService,
private notificationBackground: NotificationBackground,
) {}
@@ -46,35 +41,13 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg
* Initialize the overlay notifications background service.
*/
async init() {
this.featureFlagState$ = this.configService
.getFeatureFlag$(FeatureFlag.NotificationBarAddLoginImprovements)
.pipe(startWith(undefined), pairwise())
.subscribe(([prev, current]) => this.handleInitFeatureFlagChange(prev, current));
this.setupExtensionListeners();
this.clearLoginCipherFormDataSubject
.pipe(switchMap(() => timer(CLEAR_NOTIFICATION_LOGIN_DATA_DURATION)))
.subscribe(() => this.modifyLoginCipherFormData.clear());
}
/**
* Handles enabling/disabling the extension listeners that trigger the
* overlay notifications based on the feature flag state.
*
* @param previousValue - The previous value of the feature flag
* @param currentValue - The current value of the feature flag
*/
private handleInitFeatureFlagChange = (previousValue: boolean, currentValue: boolean) => {
if (previousValue === currentValue) {
return;
}
if (currentValue) {
this.setupExtensionListeners();
return;
}
this.removeExtensionListeners();
};
/**
* Handles the response from the content script with the page details. Triggers an initialization
* of the add login or change password notification if the conditions are met.
@@ -520,15 +493,6 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg
chrome.tabs.onUpdated.addListener(this.handleTabUpdated);
}
/**
* Removes the listeners for the extension messages and the tab events.
*/
private removeExtensionListeners() {
BrowserApi.removeListener(chrome.runtime.onMessage, this.handleExtensionMessage);
chrome.tabs.onRemoved.removeListener(this.handleTabRemoved);
chrome.tabs.onUpdated.removeListener(this.handleTabUpdated);
}
/**
* Handles messages that are sent to the extension background.
*

File diff suppressed because it is too large Load Diff

View File

@@ -12,7 +12,6 @@ const NotificationTypes = {
type NotificationType = (typeof NotificationTypes)[keyof typeof NotificationTypes];
type NotificationBarIframeInitData = {
applyRedesign?: boolean;
ciphers?: NotificationCipherData[];
folders?: FolderView[];
importType?: string;

View File

@@ -7,7 +7,7 @@
<body>
<div id="notification-bar-outer-wrapper" class="outer-wrapper">
<div class="logo">
<div class="logo-wrapper">
<a href="https://vault.bitwarden.com" target="_blank" id="logo-link" rel="noreferrer">
<img id="logo" alt="Bitwarden" />
</a>

View File

@@ -1,8 +1,8 @@
@import "../shared/styles/variables";
body {
padding: 0;
margin: 0;
padding: 0;
height: 100%;
font-size: 14px;
line-height: 16px;
@@ -14,16 +14,30 @@ body {
}
}
img {
margin: 0;
padding: 0;
border: 0;
}
button,
select {
font-size: $font-size-base;
font-family: $font-family-sans-serif;
}
.outer-wrapper {
padding: 0 10px;
border-bottom: 2px solid transparent;
display: grid;
grid-template-columns: 24px auto 55px;
grid-column-gap: 10px;
box-sizing: border-box;
display: block;
position: relative;
padding: 8px;
min-height: 42px;
border: 1px solid transparent;
border-bottom: 2px solid transparent;
border-radius: 4px;
box-sizing: border-box;
@include themify($themes) {
border-color: themed("borderColor");
border-bottom-color: themed("primaryColor");
}
@@ -50,20 +64,34 @@ body {
align-self: center;
}
img {
border: 0;
margin: 0;
padding: 0;
}
#logo {
width: 24px;
height: 24px;
display: block;
}
.logo-wrapper {
position: absolute;
top: 8px;
left: 10px;
overflow: hidden;
}
#close-button {
display: flex;
align-items: center;
justify-content: center;
width: 30px;
height: 30px;
margin-right: 10px;
padding: 0;
&:hover {
@include themify($themes) {
border-color: rgba(themed("textColor"), 0.2);
background-color: rgba(themed("textColor"), 0.2);
}
}
}
#close {
@@ -78,15 +106,79 @@ img {
}
}
#close-button:hover {
.notification-close {
position: absolute;
top: 6px;
right: 6px;
}
#content .inner-wrapper {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
.notification-body {
width: 100%;
padding: 4px 38px 24px 42px;
font-weight: 400;
}
.notification-actions {
display: flex;
width: 100%;
align-items: stretch;
justify-content: flex-end;
#never-save {
margin-right: auto;
padding: 0;
font-size: 16px;
font-weight: 500;
letter-spacing: 0.5px;
}
#select-folder {
width: 125px;
margin-right: 6px;
font-size: 12px;
appearance: none;
background-repeat: no-repeat;
background-position: center right 4px;
background-size: 16px;
@include themify($themes) {
border-color: rgba(themed("textColor"), 0.2);
background-color: rgba(themed("textColor"), 0.2);
color: themed("mutedTextColor");
border-color: themed("mutedTextColor");
}
&:not([disabled]) {
display: block;
}
}
.primary,
.secondary {
font-size: 12px;
}
.secondary {
margin-right: 6px;
border-width: 1px;
}
.primary {
margin-right: 2px;
}
&.success-message,
&.error-message {
padding: 4px 36px 6px 42px;
}
}
}
button {
padding: 0.35rem 15px;
padding: 4px 8px;
border-radius: $border-radius;
border: 1px solid transparent;
cursor: pointer;
@@ -133,14 +225,13 @@ button.neutral {
text-decoration: underline;
@include themify($themes) {
background-color: transparent;
color: darken(themed("primaryColor"), 6%);
}
}
}
select {
padding: 0.35rem;
padding: 4px 6px;
border: 1px solid #000000;
border-radius: $border-radius;
@@ -151,16 +242,9 @@ select {
}
}
select,
button {
font-size: $font-size-base;
font-family: $font-family-sans-serif;
}
.success-message {
display: flex;
align-items: center;
align-content: center;
justify-content: center;
@include themify($themes) {
@@ -184,6 +268,13 @@ button {
}
}
.success-event,
.error-event {
.notification-body {
display: none;
}
}
@media screen and (max-width: 768px) {
#select-folder {
display: none;
@@ -196,131 +287,8 @@ button {
}
}
.notification-bar-redesign {
button {
padding: 4px 8px;
}
select {
padding: 4px 6px;
}
.outer-wrapper {
display: block;
position: relative;
padding: 8px;
border-top: 1px solid transparent;
border-left: 1px solid transparent;
border-right: 1px solid transparent;
border-radius: 4px;
@include themify($themes) {
border-top-color: themed("borderColor");
border-left-color: themed("borderColor");
border-right-color: themed("borderColor");
}
}
.logo {
position: absolute;
top: 8px;
left: 10px;
overflow: hidden;
}
.notification-close {
position: absolute;
top: 6px;
right: 6px;
#close-button {
display: flex;
align-content: center;
align-items: center;
justify-content: center;
width: 30px;
height: 30px;
padding: 0;
margin: 0;
}
}
#content .inner-wrapper {
display: flex;
align-items: flex-start;
align-content: flex-start;
flex-wrap: wrap;
.notification-body {
width: 100%;
padding: 4px 38px 24px 42px;
font-weight: 400;
}
.notification-actions {
display: flex;
align-items: stretch;
align-content: stretch;
justify-content: flex-end;
width: 100%;
#never-save {
margin-right: auto;
padding: 0;
font-size: 16px;
font-weight: 500;
letter-spacing: 0.5px;
}
#select-folder {
width: 125px;
margin-right: 6px;
font-size: 12px;
appearance: none;
background-size: 16px;
background-position: center right 4px;
background-repeat: no-repeat;
@include themify($themes) {
color: themed("mutedTextColor");
border-color: themed("mutedTextColor");
}
&:not([disabled]) {
display: block;
}
}
.primary,
.secondary {
font-size: 12px;
}
.secondary {
margin-right: 6px;
border-width: 1px;
}
.primary {
margin-right: 2px;
}
&.success-message,
&.error-message {
padding: 4px 36px 6px 42px;
}
}
}
.success-event,
.error-event {
.notification-body {
display: none;
}
}
}
.theme_light {
.notification-bar-redesign #content .inner-wrapper {
#content .inner-wrapper {
#select-folder {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHhtbG5zOnhsaW5rPSdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rJyB3aWR0aD0nMTYnIGhlaWdodD0nMTYnIGZpbGw9J25vbmUnPjxwYXRoIHN0cm9rZT0nIzIxMjUyOScgZD0nbTUgNiAzIDMgMy0zJy8+PC9zdmc+");
}
@@ -328,7 +296,7 @@ button {
}
.theme_dark {
.notification-bar-redesign #content .inner-wrapper {
#content .inner-wrapper {
#select-folder {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxNicgaGVpZ2h0PScxNicgZmlsbD0nbm9uZSc+PHBhdGggc3Ryb2tlPScjZmZmZmZmJyBkPSdtNSA2IDMgMyAzLTMnLz48L3N2Zz4=");
}

View File

@@ -507,10 +507,6 @@ function setNotificationBarTheme() {
const theme = getTheme(globalThis, notificationBarIframeInitData.theme);
document.documentElement.classList.add(`theme_${theme}`);
if (notificationBarIframeInitData.applyRedesign) {
document.body.classList.add("notification-bar-redesign");
}
}
function postMessageToParent(message: NotificationBarWindowMessage) {

View File

@@ -17,6 +17,7 @@ export class OverlayNotificationsContentService
private notificationBarIframeElement: HTMLIFrameElement | null = null;
private currentNotificationBarType: string | null = null;
private removeTabFromNotificationQueueTypes = new Set(["add", "change"]);
private notificationRefreshFlag: boolean;
private notificationBarElementStyles: Partial<CSSStyleDeclaration> = {
height: "82px",
width: "430px",
@@ -54,6 +55,9 @@ export class OverlayNotificationsContentService
constructor() {
void sendExtensionMessage("checkNotificationQueue");
void sendExtensionMessage("notificationRefreshFlagValue").then((notificationRefreshFlag) => {
this.notificationRefreshFlag = !!notificationRefreshFlag;
});
}
/**
@@ -84,7 +88,6 @@ export class OverlayNotificationsContentService
theme: typeData.theme,
removeIndividualVault: typeData.removeIndividualVault,
importType: typeData.importType,
applyRedesign: true,
launchTimestamp: typeData.launchTimestamp,
};
@@ -192,7 +195,13 @@ export class OverlayNotificationsContentService
{ transform: "translateX(0)", opacity: "1" },
true,
);
setElementStyles(this.notificationBarElement, { boxShadow: "2px 4px 6px 0px #0000001A" }, true);
if (!this.notificationRefreshFlag) {
setElementStyles(
this.notificationBarElement,
{ boxShadow: "2px 4px 6px 0px #0000001A" },
true,
);
}
this.notificationBarIframeElement.removeEventListener(
EVENTS.LOAD,
this.handleNotificationBarIframeOnLoad,
@@ -206,7 +215,13 @@ export class OverlayNotificationsContentService
if (this.notificationBarIframeElement) {
this.notificationBarElement = globalThis.document.createElement("div");
this.notificationBarElement.id = "bit-notification-bar";
setElementStyles(this.notificationBarElement, this.notificationBarElementStyles, true);
if (this.notificationRefreshFlag) {
setElementStyles(this.notificationBarElement, { height: "400px", right: "0" }, true);
}
this.notificationBarElement.appendChild(this.notificationBarIframeElement);
}
}

View File

@@ -380,7 +380,7 @@ describe("AutofillService", () => {
const autofillOverlayMenuBootstrapScript = "bootstrap-autofill-overlay-menu.js";
const autofillOverlayNotificationsBootstrapScript =
"bootstrap-autofill-overlay-notifications.js";
const defaultAutofillScripts = ["autofiller.js", "notificationBar.js", "contextMenuHandler.js"];
const defaultAutofillScripts = ["autofiller.js", "contextMenuHandler.js"];
const defaultExecuteScriptOptions = { runAt: "document_start" };
let tabMock: chrome.tabs.Tab;
let sender: chrome.runtime.MessageSender;
@@ -400,13 +400,9 @@ describe("AutofillService", () => {
});
it("accepts an extension message sender and injects the autofill scripts into the tab of the sender", async () => {
configService.getFeatureFlag.mockImplementation(async (_feature) => {
if (_feature === FeatureFlag.NotificationBarAddLoginImprovements) {
return false as FeatureFlagValueType<any>;
}
enableChangedPasswordPromptMock$.next(false);
enableAddedLoginPromptMock$.next(false);
return true as FeatureFlagValueType<any>;
});
await autofillService.injectAutofillScripts(sender.tab, sender.frameId, true);
[autofillOverlayMenuBootstrapScript, ...defaultAutofillScripts].forEach((scriptName) => {
@@ -457,25 +453,12 @@ describe("AutofillService", () => {
});
});
it("will inject the bootstrap-autofill script if the user does not have the autofill overlay enabled", async () => {
it("will inject the overlay script if the user does not have the autofill overlay enabled", async () => {
jest
.spyOn(autofillService, "getInlineMenuVisibility")
.mockResolvedValue(AutofillOverlayVisibility.Off);
configService.getFeatureFlag.mockImplementation(async (_feature) => {
if (_feature === FeatureFlag.NotificationBarAddLoginImprovements) {
return false as FeatureFlagValueType<any>;
}
return true as FeatureFlagValueType<any>;
});
await autofillService.injectAutofillScripts(sender.tab, sender.frameId);
expect(BrowserApi.executeScriptInTab).toHaveBeenCalledWith(tabMock.id, {
file: `content/${autofillBootstrapScript}`,
frameId: sender.frameId,
...defaultExecuteScriptOptions,
});
expect(BrowserApi.executeScriptInTab).not.toHaveBeenCalledWith(tabMock.id, {
file: `content/${autofillOverlayBootstrapScript}`,
frameId: sender.frameId,

View File

@@ -236,13 +236,8 @@ export default class AutofillService implements AutofillServiceInterface {
const authStatus = await firstValueFrom(this.authService.activeAccountStatus$);
const accountIsUnlocked = authStatus === AuthenticationStatus.Unlocked;
let autoFillOnPageLoadIsEnabled = false;
const addLoginImprovementsFlagActive = await this.configService.getFeatureFlag(
FeatureFlag.NotificationBarAddLoginImprovements,
);
const injectedScripts = [
await this.getBootstrapAutofillContentScript(activeAccount, addLoginImprovementsFlagActive),
];
const injectedScripts = [await this.getBootstrapAutofillContentScript(activeAccount)];
if (activeAccount && accountIsUnlocked) {
autoFillOnPageLoadIsEnabled = await this.getAutofillOnPageLoad();
@@ -259,10 +254,6 @@ export default class AutofillService implements AutofillServiceInterface {
});
}
if (!addLoginImprovementsFlagActive) {
injectedScripts.push("notificationBar.js");
}
injectedScripts.push("contextMenuHandler.js");
for (const injectedScript of injectedScripts) {
@@ -283,11 +274,9 @@ export default class AutofillService implements AutofillServiceInterface {
* enabled.
*
* @param activeAccount - The active account
* @param addLoginImprovementsFlagActive - Whether the add login improvements feature flag is active
*/
private async getBootstrapAutofillContentScript(
activeAccount: { id: UserId | undefined } & AccountInfo,
addLoginImprovementsFlagActive = false,
): Promise<string> {
let inlineMenuVisibility: InlineMenuVisibilitySetting = AutofillOverlayVisibility.Off;
@@ -310,8 +299,7 @@ export default class AutofillService implements AutofillServiceInterface {
const enableAddedLoginPrompt = await firstValueFrom(
this.userNotificationSettingsService.enableAddedLoginPrompt$,
);
const isNotificationBarEnabled =
addLoginImprovementsFlagActive && (enableChangedPasswordPrompt || enableAddedLoginPrompt);
const isNotificationBarEnabled = enableChangedPasswordPrompt || enableAddedLoginPrompt;
if (!inlineMenuVisibility && !isNotificationBarEnabled) {
return "bootstrap-autofill.js";

View File

@@ -1195,7 +1195,6 @@ export default class MainBackground {
this.overlayNotificationsBackground = new OverlayNotificationsBackground(
this.logService,
this.configService,
this.notificationBackground,
);

View File

@@ -201,7 +201,6 @@ const mainConfig = {
"./src/autofill/deprecated/content/bootstrap-legacy-autofill-overlay.ts",
"content/autofiller": "./src/autofill/content/autofiller.ts",
"content/auto-submit-login": "./src/autofill/content/auto-submit-login.ts",
"content/notificationBar": "./src/autofill/content/notification-bar.ts",
"content/contextMenuHandler": "./src/autofill/content/context-menu-handler.ts",
"content/content-message-handler": "./src/autofill/content/content-message-handler.ts",
"content/fido2-content-script": "./src/autofill/fido2/content/fido2-content-script.ts",

View File

@@ -25,7 +25,6 @@ export enum FeatureFlag {
GenerateIdentityFillScriptRefactor = "generate-identity-fill-script-refactor",
IdpAutoSubmitLogin = "idp-auto-submit-login",
InlineMenuPositioningImprovements = "inline-menu-positioning-improvements",
NotificationBarAddLoginImprovements = "notification-bar-add-login-improvements",
NotificationRefresh = "notification-refresh",
UseTreeWalkerApiForPageDetailsCollection = "use-tree-walker-api-for-page-details-collection",
MacOsNativeCredentialSync = "macos-native-credential-sync",
@@ -86,7 +85,6 @@ export const DefaultFeatureFlagValue = {
[FeatureFlag.GenerateIdentityFillScriptRefactor]: FALSE,
[FeatureFlag.IdpAutoSubmitLogin]: FALSE,
[FeatureFlag.InlineMenuPositioningImprovements]: FALSE,
[FeatureFlag.NotificationBarAddLoginImprovements]: FALSE,
[FeatureFlag.NotificationRefresh]: FALSE,
[FeatureFlag.UseTreeWalkerApiForPageDetailsCollection]: FALSE,
[FeatureFlag.MacOsNativeCredentialSync]: FALSE,