diff --git a/accessibility-checker-extension/src/ts/background/backgroundController.ts b/accessibility-checker-extension/src/ts/background/backgroundController.ts index 7b0f5a944..5744bda6f 100644 --- a/accessibility-checker-extension/src/ts/background/backgroundController.ts +++ b/accessibility-checker-extension/src/ts/background/backgroundController.ts @@ -15,7 +15,7 @@ *****************************************************************************/ import { getDevtoolsController } from "../devtools/devtoolsController"; -import { IArchiveDefinition, IMessage, IReport, IRuleset, ISettings } from "../interfaces/interfaces"; +import { IArchiveDefinition, IMessage, IReport, IRuleset, ISessionState, ISettings } from "../interfaces/interfaces"; import { CommonMessaging } from "../messaging/commonMessaging"; import { Controller, eControllerType, ListenerType } from "../messaging/controller"; import Config from "../util/config"; @@ -143,6 +143,56 @@ class BackgroundController extends Controller { } ///// Settings related functions ///////////////////////////////////////// + /** + * Global state for the extension + */ + public async getSessionState() : Promise { + let retVal = (await this.hook("getSessionState", null, async () => { + let retVal = await new Promise((resolve, _reject) => { + (chrome.storage as any).session.get("SESSION_STATE", async function (result: { SESSION_STATE?: ISessionState}) { + result.SESSION_STATE = result.SESSION_STATE || { + tabStoredCount: {} + } + result.SESSION_STATE.tabStoredCount = result.SESSION_STATE.tabStoredCount || {}; + resolve(result.SESSION_STATE as ISessionState); + }); + }) + return retVal; + }))!; + return retVal; + } + + /** + * Set settings for the extension + */ + public async setSessionState(sessionState: ISessionState) : Promise { + return this.hook("setSessionState", sessionState, async () => { + await new Promise((resolve, _reject) => { + (chrome.storage as any).session.set({ "SESSION_STATE": sessionState }, async function () { + resolve(sessionState!); + }); + }); + this.notifyEventListeners("BG_onSessionState", -1, sessionState); + return sessionState; + }); + } + + + /** + * Set stored scan count + */ + public async setStoredScanCount(info: { tabId: number, count: number }) : Promise { + return this.hook("setStoredScanCount", info, async () => { + let { tabId, count }: {tabId: number, count: number } = info; + let sessionState = await this.getSessionState(); + if (count === 0) { + delete sessionState.tabStoredCount[tabId]; + } else { + sessionState.tabStoredCount[tabId] = count; + } + return await this.setSessionState(sessionState); + }); + } /** * Get settings for the extension @@ -180,6 +230,9 @@ class BackgroundController extends Controller { this.addEventListener(listener, `BG_onSettings`); } + public async addSessionStateListener(listener: ListenerType) { + this.addEventListener(listener, `BG_onSessionState`); + } /** * Get the archive definitions */ @@ -219,7 +272,6 @@ class BackgroundController extends Controller { (async () => { let settings = await this.getSettings(); getDevtoolsController(false, "remote", senderTabId).setScanningState("running"); - console.info(`[INFO]: Scanning using archive ${settings.selected_archive.id} and guideline ${settings.selected_ruleset.id}`); let report : IReport = await myExecuteScript2(senderTabId, (settings: ISettings) => { let checker = new (window).aceIBMa.Checker(); if (Object.keys(checker.engine.nlsMap).length === 0) { @@ -342,6 +394,13 @@ class BackgroundController extends Controller { const listenMsgs : { [ msgId: string ] : (msgBody: IMessage, senderTabId?: number) => Promise }= { + "BG_getSessionState": async () => self.getSessionState(), + "BG_setSessionState": async (msgBody) => { + return self.setSessionState(msgBody.content); + }, + "BG_setStoredScanCount": async (msgBody) => { + return self.setStoredScanCount(msgBody.content); + }, "BG_getSettings": async () => self.getSettings(), "BG_getArchives": async () => self.getArchives(), "BG_getTabId": async (_a, senderTabId) => self.getTabId(senderTabId), diff --git a/accessibility-checker-extension/src/ts/devtools/devtoolsController.ts b/accessibility-checker-extension/src/ts/devtools/devtoolsController.ts index 05050a74c..0c3b7a19d 100644 --- a/accessibility-checker-extension/src/ts/devtools/devtoolsController.ts +++ b/accessibility-checker-extension/src/ts/devtools/devtoolsController.ts @@ -87,6 +87,7 @@ export class DevtoolsController extends Controller { return await this.hook("setStoredReportsMeta", updateMetaArr, async () => { if (updateMetaArr.length === 0) { devtoolsState!.storedReports = []; + getBGController().setStoredScanCount({ tabId: this.ctrlDest.tabId, count: 0}); this.notifyEventListeners("DT_onStoredReportsMeta", this.ctrlDest.tabId, await this.getStoredReportsMeta()); } else { let misMatch = false; @@ -113,6 +114,7 @@ export class DevtoolsController extends Controller { if (!misMatch) { devtoolsState!.storedReports = newReports; let data = await this.getStoredReportsMeta(); + getBGController().setStoredScanCount({ tabId: this.ctrlDest.tabId, count: data.length}); this.notifyEventListeners("DT_onStoredReportsMeta", this.ctrlDest.tabId, data); } } @@ -139,6 +141,7 @@ export class DevtoolsController extends Controller { public async clearStoredReports() : Promise { return this.hook("clearStoredReports", null, async () => { devtoolsState!.storedReports = []; + getBGController().setStoredScanCount({ tabId: this.ctrlDest.tabId, count: 0}); this.notifyEventListeners("DT_onStoredReportsMeta", this.ctrlDest.tabId, await this.getStoredReportsMeta()); }); } @@ -213,7 +216,9 @@ export class DevtoolsController extends Controller { return new Promise((resolve, _reject) => { setTimeout(async () => { this.notifyEventListeners("DT_onReport", this.ctrlDest.tabId, report); - this.notifyEventListeners("DT_onStoredReportsMeta", this.ctrlDest.tabId, await this.getStoredReportsMeta()); + let storedReportsMeta = await this.getStoredReportsMeta(); + getBGController().setStoredScanCount({ tabId: this.ctrlDest.tabId, count: storedReportsMeta.length}); + this.notifyEventListeners("DT_onStoredReportsMeta", this.ctrlDest.tabId, storedReportsMeta); resolve(); }, 0) }); diff --git a/accessibility-checker-extension/src/ts/interfaces/interfaces.ts b/accessibility-checker-extension/src/ts/interfaces/interfaces.ts index d0b9bef2c..489846686 100644 --- a/accessibility-checker-extension/src/ts/interfaces/interfaces.ts +++ b/accessibility-checker-extension/src/ts/interfaces/interfaces.ts @@ -46,6 +46,12 @@ export interface ISettings { tabStopFirstTime: boolean } +export interface ISessionState { + tabStoredCount: { + [tabId: number]: number + } +} + export type MsgDestType = { type: "contentScript" tabId: number diff --git a/accessibility-checker-extension/src/ts/options/OptionsApp.tsx b/accessibility-checker-extension/src/ts/options/OptionsApp.tsx index 292eb0b9a..16ad6f0be 100644 --- a/accessibility-checker-extension/src/ts/options/OptionsApp.tsx +++ b/accessibility-checker-extension/src/ts/options/OptionsApp.tsx @@ -17,11 +17,10 @@ limitations under the License. *****************************************************************************/ import React from "react"; - +import ReactDOM from 'react-dom'; import { IArchiveDefinition, IPolicyDefinition, ISettings } from "../interfaces/interfaces"; import { getBGController } from "../background/backgroundController"; import { DocPage } from "../docs/components/DocPage"; -// import { BrowserDetection } from "../util/browserDetection"; import { Button, @@ -43,6 +42,7 @@ import { } from "@carbon/react/icons"; import "./option.scss"; +import { getDevtoolsController } from "../devtools/devtoolsController"; interface OptionsAppState { lastSettings?: ISettings @@ -57,6 +57,10 @@ interface OptionsAppState { tabStopOutlines: boolean; tabStopAlerts: boolean; tabStopFirstTime: boolean; + // Change Ruleset while there are stored scans + storedScansExist: boolean; + modalDeploymentWithScans: boolean; + modalGuidelinesWithScans: boolean; savePending: number; } @@ -75,13 +79,16 @@ export class OptionsApp extends React.Component<{}, OptionsAppState> { tabStopOutlines: false, tabStopAlerts: true, tabStopFirstTime: true, + // Change Ruleset while there are stored scans + storedScansExist: false, + modalDeploymentWithScans: false, + modalGuidelinesWithScans: false, savePending: 0 }; async componentDidMount() { let self = this; let settings = await bgController.getSettings(); - // console.log("***", settings); let archives = await bgController.getArchives(); let selected_archive: IArchiveDefinition | null = null; let rulesets: IPolicyDefinition[] | null = null; @@ -92,6 +99,7 @@ export class OptionsApp extends React.Component<{}, OptionsAppState> { let tabStopAlerts: boolean = true; let tabStopFirstTime: boolean = true; + let storedScansExist = await this.existStoredScans(); selected_archive = settings.selected_archive; rulesets = selected_archive.rulesets.default; @@ -126,7 +134,11 @@ export class OptionsApp extends React.Component<{}, OptionsAppState> { selected_ruleset: this.getGuideline(selected_archive, selectedRulesetId!), tabStopLines: tabStopLines, tabStopOutlines: tabStopOutlines, tabStopAlerts: tabStopAlerts, tabStopFirstTime: tabStopFirstTime, + storedScansExist: storedScansExist, }); + + + bgController.addSettingsListener(async (newSettings) => { let newState : any = { lastSettings: newSettings @@ -141,6 +153,35 @@ export class OptionsApp extends React.Component<{}, OptionsAppState> { }); } + setModalDeploymentWithScans() { + this.setState({modalDeploymentWithScans: true}); + } + + setModalGuidelinesWithScans() { + this.setState({modalGuidelinesWithScans: true}); + } + + async existStoredScans() { + let tabStoredScans = (await getBGController().getSessionState()).tabStoredCount; + let existScans = false; + for (const tabId in tabStoredScans) { + if (tabStoredScans[tabId] > 0) { + existScans = true; + } + } + return existScans; + } + + async clearStoredScans() { + let tabStoredScans = (await getBGController().getSessionState()).tabStoredCount; + for (const tabId in tabStoredScans) { + if (tabStoredScans[tabId] > 0) { + getDevtoolsController(false, "remote", parseInt(tabId)).clearStoredReports(); + } + } + this.setState({storedScansExist: false}); + } + /** * Return the archive definition corresponding to the 'latest' id * @param archives @@ -350,6 +391,10 @@ export class OptionsApp extends React.Component<{}, OptionsAppState> { + {/* JCH - Need to check if there are scans, storedReportsCount > 0 + but we need to make a state and set it in componentDidMount + */} + {!this.state.selected_archive && } {this.state.selected_archive && <> { titleText="" type="default" selectedItem={selected_archive} - onChange={this.onSelectArchive.bind(this)} + onChange={async (evt: any) => { + await this.onSelectArchive(evt); + if (this.state.storedScansExist) { + this.setModalDeploymentWithScans(); + } + }} /> } - {

Preview rules: Try an experimental preview of possible future rule set

For details on rule set changes between deployments, see Release notes

-
+ , document.body)} + + {typeof document === 'undefined' ? null : ReactDOM.createPortal( { + this.setState({ modalDeploymentWithScans: false }); + this.setState({ selected_archive: this.state.lastSettings?.selected_archive! }); + }).bind(this)} + onRequestSubmit={(() => { + this.clearStoredScans(); + this.setState({ modalDeploymentWithScans: false }); + }).bind(this) + } + > +

Changing the rule set deployment dates will delete any currently stored scans.

+
, document.body)} + {/**** Select ruleset / policy */}
@@ -417,11 +487,16 @@ export class OptionsApp extends React.Component<{}, OptionsAppState> { titleText="" type="default" selectedItem={selected_ruleset} - onChange={this.onSelectGuideline.bind(this)} + onChange={async (evt: any) => { + await this.onSelectGuideline(evt); + if (this.state.storedScansExist) { + this.setModalGuidelinesWithScans(); + } + }} /> } - {

IBM Accessibility: Rules for WCAG 2.1 AA plus additional IBM requirements

WCAG 2.1 (A, AA): This is the current W3C recommendation. Content that conforms to WCAG 2.1 also conforms to WCAG 2.0

WCAG 2.0 (A, AA): Referenced by US Section 508, but not the latest W3C recommendation

-
+ , document.body)}
+ {typeof document === 'undefined' ? null : ReactDOM.createPortal( { + this.setState({ modalGuidelinesWithScans: false }); + this.setState({ selected_ruleset: this.getGuideline(this.state.lastSettings?.selected_archive!, this.state.lastSettings?.selected_ruleset.id!) }); + }).bind(this)} + onRequestSubmit={(() => { + this.clearStoredScans(); + this.setState({ modalGuidelinesWithScans: false }); + }).bind(this) + } + > +

Changing the rule set deployment dates will delete any currently stored scans.

+
, document.body)} +

Keyboard checker mode

@@ -448,7 +541,6 @@ export class OptionsApp extends React.Component<{}, OptionsAppState> { checked={this.state.tabStopLines} //@ts-ignore onChange={(value: any, id: any) => { - // console.log("lines checkbox id.checked = ",id.checked); this.setState({ tabStopLines: id.checked }); }} @@ -459,7 +551,6 @@ export class OptionsApp extends React.Component<{}, OptionsAppState> { checked={this.state.tabStopOutlines} //@ts-ignore onChange={(value: any, id: any) => { - // console.log("lines checkbox id.checked = ",id.checked); this.setState({ tabStopOutlines: id.checked }); }} />