164 lines
4.8 KiB
JavaScript
164 lines
4.8 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 https://mozilla.org/MPL/2.0/. */
|
|
|
|
/* eslint-env webextensions */
|
|
"use strict";
|
|
|
|
/**
|
|
* Display a message to the user in the content page.
|
|
*/
|
|
const displayMessage = async (tabId, message) => {
|
|
await browser.scripting.executeScript({
|
|
target: { tabId },
|
|
func: message => {
|
|
const { altTextModal } = window;
|
|
altTextModal.updateText(message);
|
|
},
|
|
args: [message],
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Ensures the engine is ready. Since there is no way to know whether an engine
|
|
* has been created, and we are limited to just 1 engine per extension, we
|
|
* store a boolean in session storage.
|
|
*/
|
|
const ensureEngineIsReady = async tabId => {
|
|
const { engineCreated } = await browser.storage.session.get({
|
|
engineCreated: false,
|
|
});
|
|
|
|
if (engineCreated) {
|
|
return;
|
|
}
|
|
|
|
const listener = progressData => {
|
|
browser.tabs.sendMessage(tabId, progressData);
|
|
};
|
|
browser.trial.ml.onProgress.addListener(listener);
|
|
|
|
try {
|
|
await displayMessage(tabId, "Initializing...");
|
|
await browser.trial.ml.createEngine({
|
|
modelHub: "mozilla",
|
|
taskName: "image-to-text",
|
|
});
|
|
browser.storage.session.set({ engineCreated: true });
|
|
} catch (err) {
|
|
await displayMessage(tabId, `${err}`);
|
|
} finally {
|
|
browser.trial.ml.onProgress.removeListener(listener);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Initializes the ML engine as well as the content CSS/JS in the page, and
|
|
* then run the inference to generate an alt text for a given image (URL).
|
|
*/
|
|
const generateAltText = async (tabId, imageUrl) => {
|
|
const [{ result: hasAltTextModal }] = await browser.scripting.executeScript({
|
|
target: { tabId },
|
|
func: () => {
|
|
return typeof window.altTextModal !== "undefined";
|
|
},
|
|
});
|
|
|
|
if (!hasAltTextModal) {
|
|
// Inject alt-text-modal.*, which creates the AltTextModal instance.
|
|
await browser.scripting.insertCSS({
|
|
target: { tabId },
|
|
files: ["./alt-text-modal.css"],
|
|
});
|
|
await browser.scripting.executeScript({
|
|
target: { tabId },
|
|
files: ["./alt-text-modal.js"],
|
|
});
|
|
}
|
|
|
|
try {
|
|
// Make sure the engine is ready.
|
|
await ensureEngineIsReady(tabId);
|
|
|
|
// Generate the alt-text for the image.
|
|
await browser.scripting.executeScript({
|
|
target: { tabId },
|
|
func: async imageUrl => {
|
|
const { altTextModal } = window;
|
|
try {
|
|
altTextModal.updateText("Running inference...");
|
|
|
|
const res = await browser.trial.ml.runEngine({
|
|
args: [imageUrl],
|
|
});
|
|
altTextModal.updateText(res[0].generated_text);
|
|
} catch (err) {
|
|
altTextModal.updateText(`${err}`);
|
|
}
|
|
},
|
|
args: [imageUrl],
|
|
});
|
|
} catch (err) {
|
|
console.warn(err);
|
|
}
|
|
};
|
|
|
|
// Currently, Firefox for Android does not support the menus API, so we use a
|
|
// different trigger to provide the alt text generation. On Firefox for
|
|
// desktop, we use the context menu, and on Android, we use a long-press event.
|
|
if ("menus" in browser) {
|
|
// When the menus API is available, we create a new item in the context menu.
|
|
// When this item is clicked, we run the alt-text generation.
|
|
browser.menus.onClicked.addListener((info, tab) => {
|
|
if (info.menuItemId !== "generate-alt-text" || !info.srcUrl) {
|
|
return;
|
|
}
|
|
|
|
generateAltText(tab.id, info.srcUrl);
|
|
});
|
|
} else {
|
|
// When the menus API is not available, we will receive a message from a
|
|
// content script that is injected below. This content script adds a listener
|
|
// to each image in the content page, which will send a message back to the
|
|
// background page when an event is emitted.
|
|
//
|
|
// See also the logic in the `onInstalled` listener.
|
|
browser.runtime.onMessage.addListener((msg, sender) => {
|
|
if (msg.type !== "generate-alt-text") {
|
|
return;
|
|
}
|
|
|
|
generateAltText(sender.tab.id, msg.data.url);
|
|
});
|
|
}
|
|
|
|
browser.runtime.onInstalled.addListener(async () => {
|
|
if ("menus" in browser) {
|
|
await browser.menus.create({
|
|
id: "generate-alt-text",
|
|
title: "✨ Generate Alt Text",
|
|
contexts: ["image"],
|
|
});
|
|
} else {
|
|
const scripts = await browser.scripting.getRegisteredContentScripts();
|
|
if (!scripts.some(script => script.id === "contextmenu-shim")) {
|
|
await browser.scripting.registerContentScripts([
|
|
{
|
|
id: "contextmenu-shim",
|
|
js: ["./contextmenu-shim.js"],
|
|
matches: ["<all_urls>"],
|
|
},
|
|
]);
|
|
}
|
|
}
|
|
|
|
const granted = await browser.permissions.contains({
|
|
permissions: ["trialML"],
|
|
});
|
|
|
|
if (!granted) {
|
|
// `browser.runtime.openOptionsPage()` is not working well on Firefox for
|
|
// Android, see: https://bugzilla.mozilla.org/show_bug.cgi?id=1795449
|
|
browser.tabs.create({ url: browser.runtime.getURL("settings.html") });
|
|
}
|
|
});
|