Merge pull request #15130 from calixteman/context_menu
[Editor] Dispatch an event when some global states are changing (bug 1777695)
This commit is contained in:
commit
bde46632d4
@ -67,7 +67,7 @@ class AnnotationEditorLayer {
|
||||
"mac+ctrl+Backspace",
|
||||
"mac+alt+Backspace",
|
||||
],
|
||||
AnnotationEditorLayer.prototype.suppress,
|
||||
AnnotationEditorLayer.prototype.delete,
|
||||
],
|
||||
]);
|
||||
|
||||
@ -128,6 +128,14 @@ class AnnotationEditorLayer {
|
||||
this.setActiveEditor(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the editing state.
|
||||
* @param {boolean} isEditing
|
||||
*/
|
||||
setEditingState(isEditing) {
|
||||
this.#uiManager.setEditingState(isEditing);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mouseover callback.
|
||||
* @param {MouseEvent} event
|
||||
@ -173,8 +181,8 @@ class AnnotationEditorLayer {
|
||||
* Suppress the selected editor or all editors.
|
||||
* @returns {undefined}
|
||||
*/
|
||||
suppress() {
|
||||
this.#uiManager.suppress();
|
||||
delete() {
|
||||
this.#uiManager.delete();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -188,7 +196,7 @@ class AnnotationEditorLayer {
|
||||
* Cut the selected editor.
|
||||
*/
|
||||
cut() {
|
||||
this.#uiManager.cut(this);
|
||||
this.#uiManager.cut();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -196,7 +204,7 @@ class AnnotationEditorLayer {
|
||||
* @returns {undefined}
|
||||
*/
|
||||
paste() {
|
||||
this.#uiManager.paste(this);
|
||||
this.#uiManager.paste();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -218,11 +218,27 @@ class AnnotationEditor {
|
||||
const [tx, ty] = this.getInitialTranslation();
|
||||
this.translate(tx, ty);
|
||||
|
||||
bindEvents(this, this.div, ["dragstart", "focusin", "focusout"]);
|
||||
bindEvents(this, this.div, [
|
||||
"dragstart",
|
||||
"focusin",
|
||||
"focusout",
|
||||
"mousedown",
|
||||
]);
|
||||
|
||||
return this.div;
|
||||
}
|
||||
|
||||
/**
|
||||
* Onmousedown callback.
|
||||
* @param {MouseEvent} event
|
||||
*/
|
||||
mousedown(event) {
|
||||
if (event.button !== 0) {
|
||||
// Avoid to focus this editor because of a non-left click.
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
getRect(tx, ty) {
|
||||
const [parentWidth, parentHeight] = this.parent.viewportBaseDimensions;
|
||||
const [pageWidth, pageHeight] = this.parent.pageDimensions;
|
||||
@ -362,6 +378,11 @@ class AnnotationEditor {
|
||||
* @returns {undefined}
|
||||
*/
|
||||
remove() {
|
||||
if (!this.isEmpty()) {
|
||||
// The editor is removed but it can be back at some point thanks to
|
||||
// undo/redo so we must commit it before.
|
||||
this.commit();
|
||||
}
|
||||
this.parent.remove(this);
|
||||
}
|
||||
|
||||
|
@ -214,6 +214,7 @@ class FreeTextEditor extends AnnotationEditor {
|
||||
|
||||
/** @inheritdoc */
|
||||
enableEditMode() {
|
||||
this.parent.setEditingState(false);
|
||||
this.parent.updateToolbar(AnnotationEditorType.FREETEXT);
|
||||
super.enableEditMode();
|
||||
this.overlayDiv.classList.remove("enabled");
|
||||
@ -223,6 +224,7 @@ class FreeTextEditor extends AnnotationEditor {
|
||||
|
||||
/** @inheritdoc */
|
||||
disableEditMode() {
|
||||
this.parent.setEditingState(true);
|
||||
super.disableEditMode();
|
||||
this.overlayDiv.classList.add("enabled");
|
||||
this.editorDiv.contentEditable = false;
|
||||
@ -245,6 +247,12 @@ class FreeTextEditor extends AnnotationEditor {
|
||||
return this.editorDiv.innerText.trim() === "";
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
remove() {
|
||||
this.parent.setEditingState(true);
|
||||
super.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the text from this editor.
|
||||
* @returns {string}
|
||||
@ -282,7 +290,7 @@ class FreeTextEditor extends AnnotationEditor {
|
||||
commit() {
|
||||
if (!this.#hasAlreadyBeenCommitted) {
|
||||
// This editor has something and it's the first time
|
||||
// it's commited so we can it in the undo/redo stack.
|
||||
// it's commited so we can add it in the undo/redo stack.
|
||||
this.#hasAlreadyBeenCommitted = true;
|
||||
this.parent.addUndoableEditor(this);
|
||||
}
|
||||
|
@ -211,8 +211,12 @@ class InkEditor extends AnnotationEditor {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isEmpty()) {
|
||||
this.commit();
|
||||
}
|
||||
|
||||
// Destroy the canvas.
|
||||
this.canvas.width = this.canvas.heigth = 0;
|
||||
this.canvas.width = this.canvas.height = 0;
|
||||
this.canvas.remove();
|
||||
this.canvas = null;
|
||||
|
||||
@ -258,7 +262,10 @@ class InkEditor extends AnnotationEditor {
|
||||
|
||||
/** @inheritdoc */
|
||||
isEmpty() {
|
||||
return this.paths.length === 0;
|
||||
return (
|
||||
this.paths.length === 0 ||
|
||||
(this.paths.length === 1 && this.paths[0].length === 0)
|
||||
);
|
||||
}
|
||||
|
||||
#getInitialBBox() {
|
||||
@ -415,7 +422,7 @@ class InkEditor extends AnnotationEditor {
|
||||
* @returns {undefined}
|
||||
*/
|
||||
canvasMousedown(event) {
|
||||
if (!this.isInEditMode() || this.#disableEditing) {
|
||||
if (event.button !== 0 || !this.isInEditMode() || this.#disableEditing) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -447,6 +454,9 @@ class InkEditor extends AnnotationEditor {
|
||||
* @returns {undefined}
|
||||
*/
|
||||
canvasMouseup(event) {
|
||||
if (event.button !== 0) {
|
||||
return;
|
||||
}
|
||||
if (this.isInEditMode() && this.currentPath.length !== 0) {
|
||||
event.stopPropagation();
|
||||
this.#endDrawing(event);
|
||||
|
@ -150,6 +150,26 @@ class CommandManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is something to undo.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
hasSomethingToUndo() {
|
||||
return !isNaN(this.#position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is something to redo.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
hasSomethingToRedo() {
|
||||
if (isNaN(this.#position) && this.#start < this.#commands.length) {
|
||||
return true;
|
||||
}
|
||||
const next = (this.#position + 1) % this.#maxSize;
|
||||
return next !== this.#start && next < this.#commands.length;
|
||||
}
|
||||
|
||||
#setCommands(cmds) {
|
||||
if (this.#commands.length < this.#maxSize) {
|
||||
this.#commands.push(cmds);
|
||||
@ -167,6 +187,10 @@ class CommandManager {
|
||||
}
|
||||
this.#commands[this.#position] = cmds;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.#commands = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -279,6 +303,18 @@ class ClipboardManager {
|
||||
paste() {
|
||||
return this.element?.copy() || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the clipboard is empty.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isEmpty() {
|
||||
return this.element === null;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.element = null;
|
||||
}
|
||||
}
|
||||
|
||||
class ColorManager {
|
||||
@ -355,7 +391,7 @@ class AnnotationEditorUIManager {
|
||||
|
||||
#allEditors = new Map();
|
||||
|
||||
#allLayers = new Set();
|
||||
#allLayers = new Map();
|
||||
|
||||
#allowClick = true;
|
||||
|
||||
@ -377,8 +413,69 @@ class AnnotationEditorUIManager {
|
||||
|
||||
#previousActiveEditor = null;
|
||||
|
||||
#boundOnEditingAction = this.onEditingAction.bind(this);
|
||||
|
||||
#previousStates = {
|
||||
isEditing: false,
|
||||
isEmpty: true,
|
||||
hasEmptyClipboard: true,
|
||||
hasSomethingToUndo: false,
|
||||
hasSomethingToRedo: false,
|
||||
hasSelectedEditor: false,
|
||||
};
|
||||
|
||||
constructor(eventBus) {
|
||||
this.#eventBus = eventBus;
|
||||
this.#eventBus._on("editingaction", this.#boundOnEditingAction);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.#eventBus._off("editingaction", this.#boundOnEditingAction);
|
||||
for (const layer of this.#allLayers.values()) {
|
||||
layer.destroy();
|
||||
}
|
||||
this.#allLayers.clear();
|
||||
for (const editor of this.#allEditors.values()) {
|
||||
editor.destroy();
|
||||
}
|
||||
this.#allEditors.clear();
|
||||
this.#activeEditor = null;
|
||||
this.#clipboardManager.destroy();
|
||||
this.#commandManager.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an action for a given name.
|
||||
* For example, the user can click on the "Undo" entry in the context menu
|
||||
* and it'll trigger the undo action.
|
||||
* @param {Object} details
|
||||
*/
|
||||
onEditingAction(details) {
|
||||
if (
|
||||
["undo", "redo", "cut", "copy", "paste", "delete", "selectAll"].includes(
|
||||
details.name
|
||||
)
|
||||
) {
|
||||
this[details.name]();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the different possible states of this manager, e.g. is the clipboard
|
||||
* empty or is there something to undo, ...
|
||||
* @param {Object} details
|
||||
*/
|
||||
#dispatchUpdateStates(details) {
|
||||
const hasChanged = Object.entries(details).some(
|
||||
([key, value]) => this.#previousStates[key] !== value
|
||||
);
|
||||
|
||||
if (hasChanged) {
|
||||
this.#eventBus.dispatch("annotationeditorstateschanged", {
|
||||
source: this,
|
||||
details: Object.assign(this.#previousStates, details),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#dispatchUpdateUI(details) {
|
||||
@ -388,6 +485,29 @@ class AnnotationEditorUIManager {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the editing state.
|
||||
* It can be useful to temporarily disable it when the user is editing a
|
||||
* FreeText annotation.
|
||||
* @param {boolean} isEditing
|
||||
*/
|
||||
setEditingState(isEditing) {
|
||||
if (isEditing) {
|
||||
this.#dispatchUpdateStates({
|
||||
isEditing: this.#mode !== AnnotationEditorType.NONE,
|
||||
isEmpty: this.#isEmpty(),
|
||||
hasSomethingToUndo: this.#commandManager.hasSomethingToUndo(),
|
||||
hasSomethingToRedo: this.#commandManager.hasSomethingToRedo(),
|
||||
hasSelectedEditor: false,
|
||||
hasEmptyClipboard: this.#clipboardManager.isEmpty(),
|
||||
});
|
||||
} else {
|
||||
this.#dispatchUpdateStates({
|
||||
isEditing: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorTypes(types) {
|
||||
this.#editorTypes = types;
|
||||
for (const editorType of this.#editorTypes) {
|
||||
@ -408,7 +528,7 @@ class AnnotationEditorUIManager {
|
||||
* @param {AnnotationEditorLayer} layer
|
||||
*/
|
||||
addLayer(layer) {
|
||||
this.#allLayers.add(layer);
|
||||
this.#allLayers.set(layer.pageIndex, layer);
|
||||
if (this.#isEnabled) {
|
||||
layer.enable();
|
||||
} else {
|
||||
@ -421,7 +541,7 @@ class AnnotationEditorUIManager {
|
||||
* @param {AnnotationEditorLayer} layer
|
||||
*/
|
||||
removeLayer(layer) {
|
||||
this.#allLayers.delete(layer);
|
||||
this.#allLayers.delete(layer.pageIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -431,10 +551,12 @@ class AnnotationEditorUIManager {
|
||||
updateMode(mode) {
|
||||
this.#mode = mode;
|
||||
if (mode === AnnotationEditorType.NONE) {
|
||||
this.setEditingState(false);
|
||||
this.#disableAll();
|
||||
} else {
|
||||
this.setEditingState(true);
|
||||
this.#enableAll();
|
||||
for (const layer of this.#allLayers) {
|
||||
for (const layer of this.#allLayers.values()) {
|
||||
layer.updateMode(mode);
|
||||
}
|
||||
}
|
||||
@ -476,7 +598,7 @@ class AnnotationEditorUIManager {
|
||||
#enableAll() {
|
||||
if (!this.#isEnabled) {
|
||||
this.#isEnabled = true;
|
||||
for (const layer of this.#allLayers) {
|
||||
for (const layer of this.#allLayers.values()) {
|
||||
layer.enable();
|
||||
}
|
||||
}
|
||||
@ -488,7 +610,7 @@ class AnnotationEditorUIManager {
|
||||
#disableAll() {
|
||||
if (this.#isEnabled) {
|
||||
this.#isEnabled = false;
|
||||
for (const layer of this.#allLayers) {
|
||||
for (const layer of this.#allLayers.values()) {
|
||||
layer.disable();
|
||||
}
|
||||
}
|
||||
@ -534,6 +656,19 @@ class AnnotationEditorUIManager {
|
||||
this.#allEditors.delete(editor.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an editor to the layer it belongs to or add it to the global map.
|
||||
* @param {AnnotationEditor} editor
|
||||
*/
|
||||
#addEditorToLayer(editor) {
|
||||
const layer = this.#allLayers.get(editor.pageIndex);
|
||||
if (layer) {
|
||||
layer.addOrRebuild(editor);
|
||||
} else {
|
||||
this.addEditor(editor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the given editor as the active one.
|
||||
* @param {AnnotationEditor} editor
|
||||
@ -548,7 +683,9 @@ class AnnotationEditorUIManager {
|
||||
this.#activeEditor = editor;
|
||||
if (editor) {
|
||||
this.#dispatchUpdateUI(editor.propertiesToUpdate);
|
||||
this.#dispatchUpdateStates({ hasSelectedEditor: true });
|
||||
} else {
|
||||
this.#dispatchUpdateStates({ hasSelectedEditor: false });
|
||||
if (this.#previousActiveEditor) {
|
||||
this.#dispatchUpdateUI(this.#previousActiveEditor.propertiesToUpdate);
|
||||
} else {
|
||||
@ -564,6 +701,11 @@ class AnnotationEditorUIManager {
|
||||
*/
|
||||
undo() {
|
||||
this.#commandManager.undo();
|
||||
this.#dispatchUpdateStates({
|
||||
hasSomethingToUndo: this.#commandManager.hasSomethingToUndo(),
|
||||
hasSomethingToRedo: true,
|
||||
isEmpty: this.#isEmpty(),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -571,6 +713,11 @@ class AnnotationEditorUIManager {
|
||||
*/
|
||||
redo() {
|
||||
this.#commandManager.redo();
|
||||
this.#dispatchUpdateStates({
|
||||
hasSomethingToUndo: true,
|
||||
hasSomethingToRedo: this.#commandManager.hasSomethingToRedo(),
|
||||
isEmpty: this.#isEmpty(),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -579,6 +726,25 @@ class AnnotationEditorUIManager {
|
||||
*/
|
||||
addCommands(params) {
|
||||
this.#commandManager.add(params);
|
||||
this.#dispatchUpdateStates({
|
||||
hasSomethingToUndo: true,
|
||||
hasSomethingToRedo: false,
|
||||
isEmpty: this.#isEmpty(),
|
||||
});
|
||||
}
|
||||
|
||||
#isEmpty() {
|
||||
if (this.#allEditors.size === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.#allEditors.size === 1) {
|
||||
for (const editor of this.#allEditors.values()) {
|
||||
return editor.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -608,10 +774,9 @@ class AnnotationEditorUIManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Suppress some editors from the given layer.
|
||||
* @param {AnnotationEditorLayer} layer
|
||||
* Delete the current editor or all.
|
||||
*/
|
||||
suppress(layer) {
|
||||
delete() {
|
||||
let cmd, undo;
|
||||
if (this.#isAllSelected) {
|
||||
const editors = Array.from(this.#allEditors.values());
|
||||
@ -623,7 +788,7 @@ class AnnotationEditorUIManager {
|
||||
|
||||
undo = () => {
|
||||
for (const editor of editors) {
|
||||
layer.addOrRebuild(editor);
|
||||
this.#addEditorToLayer(editor);
|
||||
}
|
||||
};
|
||||
|
||||
@ -637,7 +802,7 @@ class AnnotationEditorUIManager {
|
||||
editor.remove();
|
||||
};
|
||||
undo = () => {
|
||||
layer.addOrRebuild(editor);
|
||||
this.#addEditorToLayer(editor);
|
||||
};
|
||||
}
|
||||
|
||||
@ -650,14 +815,14 @@ class AnnotationEditorUIManager {
|
||||
copy() {
|
||||
if (this.#activeEditor) {
|
||||
this.#clipboardManager.copy(this.#activeEditor);
|
||||
this.#dispatchUpdateStates({ hasEmptyClipboard: false });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cut the selected editor.
|
||||
* @param {AnnotationEditorLayer}
|
||||
*/
|
||||
cut(layer) {
|
||||
cut() {
|
||||
if (this.#activeEditor) {
|
||||
this.#clipboardManager.copy(this.#activeEditor);
|
||||
const editor = this.#activeEditor;
|
||||
@ -665,7 +830,7 @@ class AnnotationEditorUIManager {
|
||||
editor.remove();
|
||||
};
|
||||
const undo = () => {
|
||||
layer.addOrRebuild(editor);
|
||||
this.#addEditorToLayer(editor);
|
||||
};
|
||||
|
||||
this.addCommands({ cmd, undo, mustExec: true });
|
||||
@ -674,16 +839,16 @@ class AnnotationEditorUIManager {
|
||||
|
||||
/**
|
||||
* Paste a previously copied editor.
|
||||
* @param {AnnotationEditorLayer}
|
||||
* @returns {undefined}
|
||||
*/
|
||||
paste(layer) {
|
||||
paste() {
|
||||
const editor = this.#clipboardManager.paste();
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
// TODO: paste in the current visible layer.
|
||||
const cmd = () => {
|
||||
layer.addOrRebuild(editor);
|
||||
this.#addEditorToLayer(editor);
|
||||
};
|
||||
const undo = () => {
|
||||
editor.remove();
|
||||
@ -700,6 +865,7 @@ class AnnotationEditorUIManager {
|
||||
for (const editor of this.#allEditors.values()) {
|
||||
editor.select();
|
||||
}
|
||||
this.#dispatchUpdateStates({ hasSelectedEditor: true });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -707,9 +873,11 @@ class AnnotationEditorUIManager {
|
||||
*/
|
||||
unselectAll() {
|
||||
this.#isAllSelected = false;
|
||||
|
||||
for (const editor of this.#allEditors.values()) {
|
||||
editor.unselect();
|
||||
}
|
||||
this.#dispatchUpdateStates({ hasSelectedEditor: this.hasActive() });
|
||||
}
|
||||
|
||||
/**
|
||||
|
14
web/app.js
14
web/app.js
@ -186,6 +186,10 @@ class DefaultExternalServices {
|
||||
static get isInAutomation() {
|
||||
return shadow(this, "isInAutomation", false);
|
||||
}
|
||||
|
||||
static updateEditorStates(data) {
|
||||
throw new Error("Not implemented: updateEditorStates");
|
||||
}
|
||||
}
|
||||
|
||||
const PDFViewerApplication = {
|
||||
@ -1954,6 +1958,12 @@ const PDFViewerApplication = {
|
||||
eventBus._on("fileinputchange", webViewerFileInputChange);
|
||||
eventBus._on("openfile", webViewerOpenFile);
|
||||
}
|
||||
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
|
||||
eventBus._on(
|
||||
"annotationeditorstateschanged",
|
||||
webViewerAnnotationEditorStatesChanged
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
bindWindowEvents() {
|
||||
@ -3076,6 +3086,10 @@ function beforeUnload(evt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function webViewerAnnotationEditorStatesChanged(data) {
|
||||
PDFViewerApplication.externalServices.updateEditorStates(data);
|
||||
}
|
||||
|
||||
/* Abstract factory for the print service. */
|
||||
const PDFPrintServiceFactory = {
|
||||
instance: {
|
||||
|
@ -640,6 +640,10 @@ class BaseViewer {
|
||||
if (this._scriptingManager) {
|
||||
this._scriptingManager.setDocument(null);
|
||||
}
|
||||
if (this.#annotationEditorUIManager) {
|
||||
this.#annotationEditorUIManager.destroy();
|
||||
this.#annotationEditorUIManager = null;
|
||||
}
|
||||
}
|
||||
|
||||
this.pdfDocument = pdfDocument;
|
||||
@ -899,7 +903,6 @@ class BaseViewer {
|
||||
}
|
||||
|
||||
_resetView() {
|
||||
this.#annotationEditorUIManager = null;
|
||||
this._pages = [];
|
||||
this._currentPageNumber = 1;
|
||||
this._currentScale = UNKNOWN_SCALE;
|
||||
|
@ -271,6 +271,20 @@ class MozL10n {
|
||||
window.addEventListener("save", handleEvent);
|
||||
})();
|
||||
|
||||
(function listenEditingEvent() {
|
||||
const handleEvent = function ({ detail }) {
|
||||
if (!PDFViewerApplication.initialized) {
|
||||
return;
|
||||
}
|
||||
PDFViewerApplication.eventBus.dispatch("editingaction", {
|
||||
source: window,
|
||||
name: detail.name,
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener("editingaction", handleEvent);
|
||||
})();
|
||||
|
||||
class FirefoxComDataRangeTransport extends PDFDataRangeTransport {
|
||||
requestDataRange(begin, end) {
|
||||
FirefoxCom.request("requestDataRange", { begin, end });
|
||||
@ -384,6 +398,10 @@ class FirefoxExternalServices extends DefaultExternalServices {
|
||||
return new FirefoxPreferences();
|
||||
}
|
||||
|
||||
static updateEditorStates(data) {
|
||||
FirefoxCom.request("updateEditorStates", data);
|
||||
}
|
||||
|
||||
static createL10n(options) {
|
||||
const mozL10n = document.mozL10n;
|
||||
// TODO refactor mozL10n.setExternalLocalizerServices
|
||||
|
Loading…
Reference in New Issue
Block a user