Skip to content

Commit

Permalink
Merge pull request #18573 from calixteman/cancel_ai_requests
Browse files Browse the repository at this point in the history
[Editor] Dispatch changes in prefs enableAltTextModelDownload and enableGuessAltText to the viewer (bug 1912024)
  • Loading branch information
calixteman committed Aug 7, 2024
2 parents 5709b13 + 92ade54 commit fef2853
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 51 deletions.
26 changes: 20 additions & 6 deletions src/display/editor/stamp.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,11 @@ class StampEditor extends AnnotationEditor {
this._uiManager.useNewAltTextFlow &&
this.#bitmap
) {
// The alt-text dialog isn't opened but we still want to guess the alt
// text.
this.mlGuessAltText();
try {
// The alt-text dialog isn't opened but we still want to guess the alt
// text.
this.mlGuessAltText();
} catch {}
}

this.div.focus();
Expand All @@ -155,8 +157,11 @@ class StampEditor extends AnnotationEditor {
}

const { mlManager } = this._uiManager;
if (!mlManager || !(await mlManager.isEnabledFor("altText"))) {
return null;
if (!mlManager) {
throw new Error("No ML.");
}
if (!(await mlManager.isEnabledFor("altText"))) {
throw new Error("ML isn't enabled for alt text.");
}
const { data, width, height } =
imageData ||
Expand All @@ -170,9 +175,18 @@ class StampEditor extends AnnotationEditor {
channels: data.length / (width * height),
},
});
if (!response || response.error || !response.output) {
if (!response) {
throw new Error("No response from the AI service.");
}
if (response.error) {
throw new Error("Error from the AI service.");
}
if (response.cancel) {
return null;
}
if (!response.output) {
throw new Error("No valid response from the AI service.");
}
const altText = response.output;
await this.setGuessedAltText(altText);
if (updateAltTextData && !this.hasAltTextData()) {
Expand Down
4 changes: 1 addition & 3 deletions web/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -404,9 +404,7 @@ const PDFViewerApplication = {
} else {
eventBus = new EventBus();
}
if (this.mlManager) {
this.mlManager.eventBus = eventBus;
}
this.mlManager?.setEventBus(eventBus, this._globalAbortController.signal);
this.eventBus = eventBus;

this.overlayManager = new OverlayManager();
Expand Down
4 changes: 2 additions & 2 deletions web/app_options.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,12 +193,12 @@ const defaultOptions = {
enableAltTextModelDownload: {
/** @type {boolean} */
value: true,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE + OptionKind.EVENT_DISPATCH,
},
enableGuessAltText: {
/** @type {boolean} */
value: true,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE + OptionKind.EVENT_DISPATCH,
},
enableHighlightEditor: {
// We'll probably want to make some experiments before enabling this
Expand Down
94 changes: 81 additions & 13 deletions web/firefoxcom.js
Original file line number Diff line number Diff line change
Expand Up @@ -307,11 +307,15 @@ class FirefoxScripting {
}

class MLManager {
#abortSignal = null;

#enabled = null;

#eventBus = null;

#ready = null;

eventBus = null;
#requestResolvers = null;

hasProgress = false;

Expand All @@ -330,6 +334,32 @@ class MLManager {
this.enableGuessAltText = enableGuessAltText;
}

setEventBus(eventBus, abortSignal) {
this.#eventBus = eventBus;
this.#abortSignal = abortSignal;
eventBus._on(
"enablealttextmodeldownload",
({ value }) => {
if (this.enableAltTextModelDownload === value) {
return;
}
if (value) {
this.downloadModel("altText");
} else {
this.deleteModel("altText");
}
},
abortSignal
);
eventBus._on(
"enableguessalttext",
({ value }) => {
this.toggleService("altText", value);
},
abortSignal
);
}

async isEnabledFor(name) {
return this.enableGuessAltText && !!(await this.#enabled?.get(name));
}
Expand All @@ -339,16 +369,17 @@ class MLManager {
}

async deleteModel(name) {
if (name !== "altText") {
if (name !== "altText" || !this.enableAltTextModelDownload) {
return;
}
this.enableAltTextModelDownload = false;
this.#ready?.delete(name);
this.#enabled?.delete(name);
await Promise.all([
this.toggleService("altText", false),
FirefoxCom.requestAsync("mlDelete", MLManager.#AI_ALT_TEXT_MODEL_NAME),
]);
await this.toggleService("altText", false);
await FirefoxCom.requestAsync(
"mlDelete",
MLManager.#AI_ALT_TEXT_MODEL_NAME
);
}

async loadModel(name) {
Expand All @@ -358,7 +389,7 @@ class MLManager {
}

async downloadModel(name) {
if (name !== "altText") {
if (name !== "altText" || this.enableAltTextModelDownload) {
return null;
}
this.enableAltTextModelDownload = true;
Expand All @@ -369,18 +400,53 @@ class MLManager {
if (data?.name !== "altText") {
return null;
}
const resolvers = (this.#requestResolvers ||= new Set());
const resolver = Promise.withResolvers();
resolvers.add(resolver);

data.service = MLManager.#AI_ALT_TEXT_MODEL_NAME;
return FirefoxCom.requestAsync("mlGuess", data);
const requestPromise = FirefoxCom.requestAsync("mlGuess", data);

requestPromise
.then(response => {
if (resolvers.has(resolver)) {
resolver.resolve(response);
resolvers.delete(resolver);
}
})
.catch(reason => {
if (resolvers.has(resolver)) {
resolver.reject(reason);
resolvers.delete(resolver);
}
});

return resolver.promise;
}

async #cancelAllRequests() {
if (!this.#requestResolvers) {
return;
}
for (const resolver of this.#requestResolvers) {
resolver.resolve({ cancel: true });
}
this.#requestResolvers.clear();
this.#requestResolvers = null;
}

async toggleService(name, enabled) {
if (name !== "altText") {
if (name !== "altText" || this.enableGuessAltText === enabled) {
return;
}

this.enableGuessAltText = enabled;
if (enabled && this.enableAltTextModelDownload) {
await this.#loadAltTextEngine(false);
if (enabled) {
if (this.enableAltTextModelDownload) {
await this.#loadAltTextEngine(false);
}
} else {
this.#cancelAllRequests();
}
}

Expand All @@ -403,7 +469,7 @@ class MLManager {
if (listenToProgress) {
this.hasProgress = true;
const callback = ({ detail }) => {
this.eventBus.dispatch("loadaiengineprogress", {
this.#eventBus.dispatch("loadaiengineprogress", {
source: this,
detail,
});
Expand All @@ -412,7 +478,9 @@ class MLManager {
window.removeEventListener("loadAIEngineProgress", callback);
}
};
window.addEventListener("loadAIEngineProgress", callback);
window.addEventListener("loadAIEngineProgress", callback, {
signal: this.#abortSignal,
});
promise.then(ok => {
if (!ok) {
this.hasProgress = false;
Expand Down
4 changes: 4 additions & 0 deletions web/genericcom.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ class FakeMLManager {
this.enableAltTextModelDownload = enableAltTextModelDownload;
}

setEventBus(eventBus, abortSignal) {
this.eventBus = eventBus;
}

async isEnabledFor(_name) {
return this.enableGuessAltText;
}
Expand Down
81 changes: 54 additions & 27 deletions web/new_alt_text_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ class NewAltTextManager {
this.#toggleDisclaimer();
});

eventBus._on("enableguessalttext", ({ value }) => {
this.#toggleGuessAltText(value, /* isInitial = */ false);
});

this.#overlayManager.register(dialog);
}

Expand Down Expand Up @@ -247,13 +251,12 @@ class NewAltTextManager {
this.#imageData,
/* updateAltTextData = */ false
);
if (altText === null) {
throw new Error("No valid response from the AI service.");
}
this.#guessedAltText = altText;
this.#wasAILoading = this.#isAILoading;
if (this.#isAILoading) {
this.#addAltText(altText);
if (altText) {
this.#guessedAltText = altText;
this.#wasAILoading = this.#isAILoading;
if (this.#isAILoading) {
this.#addAltText(altText);
}
}
} catch (e) {
console.error(e);
Expand Down Expand Up @@ -458,6 +461,8 @@ class ImageAltTextSettings {

#createModelButton;

#downloadModelButton;

#dialog;

#eventBus;
Expand Down Expand Up @@ -486,6 +491,7 @@ class ImageAltTextSettings {
this.#dialog = dialog;
this.#aiModelSettings = aiModelSettings;
this.#createModelButton = createModelButton;
this.#downloadModelButton = downloadModelButton;
this.#showAltTextDialogButton = showAltTextDialogButton;
this.#overlayManager = overlayManager;
this.#eventBus = eventBus;
Expand All @@ -508,40 +514,61 @@ class ImageAltTextSettings {
this.#togglePref.bind(this, "enableNewAltTextWhenAddingImage")
);

deleteModelButton.addEventListener("click", async () => {
await mlManager.deleteModel("altText");
deleteModelButton.addEventListener("click", this.#delete.bind(this, true));
downloadModelButton.addEventListener(
"click",
this.#download.bind(this, true)
);

closeButton.addEventListener("click", this.#finish.bind(this));

aiModelSettings.classList.toggle("download", true);
createModelButton.disabled = true;
createModelButton.setAttribute("aria-pressed", false);
this.#setPref("enableGuessAltText", false);
this.#setPref("enableAltTextModelDownload", false);
eventBus._on("enablealttextmodeldownload", ({ value }) => {
if (value) {
this.#download(false);
} else {
this.#delete(false);
}
});

downloadModelButton.addEventListener("click", async () => {
downloadModelButton.disabled = true;
downloadModelButton.firstChild.setAttribute(
this.#overlayManager.register(dialog);
}

async #download(isFromUI = false) {
if (isFromUI) {
this.#downloadModelButton.disabled = true;
this.#downloadModelButton.firstChild.setAttribute(
"data-l10n-id",
"pdfjs-editor-alt-text-settings-downloading-model-button"
);

await mlManager.downloadModel("altText");
await this.#mlManager.downloadModel("altText");

aiModelSettings.classList.toggle("download", false);
downloadModelButton.firstChild.setAttribute(
this.#downloadModelButton.firstChild.setAttribute(
"data-l10n-id",
"pdfjs-editor-alt-text-settings-download-model-button"
);
createModelButton.disabled = false;
createModelButton.setAttribute("aria-pressed", true);

this.#createModelButton.disabled = false;
this.#setPref("enableGuessAltText", true);
mlManager.toggleService("altText", true);
this.#mlManager.toggleService("altText", true);
this.#setPref("enableAltTextModelDownload", true);
downloadModelButton.disabled = false;
});
this.#downloadModelButton.disabled = false;
}

closeButton.addEventListener("click", this.#finish.bind(this));
this.#overlayManager.register(dialog);
this.#aiModelSettings.classList.toggle("download", false);
this.#createModelButton.setAttribute("aria-pressed", true);
}

async #delete(isFromUI = false) {
if (isFromUI) {
await this.#mlManager.deleteModel("altText");
this.#setPref("enableGuessAltText", false);
this.#setPref("enableAltTextModelDownload", false);
}

this.#aiModelSettings.classList.toggle("download", true);
this.#createModelButton.disabled = true;
this.#createModelButton.setAttribute("aria-pressed", false);
}

async open({ enableGuessAltText, enableNewAltTextWhenAddingImage }) {
Expand Down

0 comments on commit fef2853

Please sign in to comment.