Объявление

В связи с наплывом спама и ботов на форуме, регистрация новых пользователей будет приостановлена. О восстановлении регистрации будет сообщено дополнительно

Administrator

№17326Вчера 00:51:22

Andrey_Krropotkin
Участник
 
Группа: Members
Зарегистрирован: 11-11-2011
Сообщений: 499
UA: Firefox 137.0

Re: Custom Buttons

Dumby еще вопрос. Может к этой теме не относится, но и  к UСF то же. Есть такой проект от одного из разработчиков- aminomancer замена панели unified-extensions-button - extensionOptionsPanel.uc.js

скрытый текст

Выделить код

Код:

// ==UserScript==
// @name           Extension Options Panel
// @version        1.9.2
// @author         aminomancer
// @homepageURL    https://github.com/aminomancer/uc.css.js
// @description    This script creates a toolbar button that opens a popup panel where extensions can be configured, disabled, uninstalled, etc. Each extension gets its own button in the panel. Clicking an extension's button leads to a subview where you can jump to the extension's options, disable or enable the extension, uninstall it, configure automatic updates, disable/enable it in private browsing, view its source code in whatever program is associated with `.xpi` files, open the extension's homepage, or copy the extension's ID. The panel can also be opened from the App Menu, using the built-in "Add-ons and themes" button. Since v1.8, themes will also be listed in the panel. Hovering a theme will show a tooltip with a preview/screenshot of the theme, and clicking the theme will toggle it on or off. There are several translation and configuration options directly below.
// @downloadURL    https://cdn.jsdelivr.net/gh/aminomancer/uc.css.js@master/JS/extensionOptionsPanel.uc.js
// @updateURL      https://cdn.jsdelivr.net/gh/aminomancer/uc.css.js@master/JS/extensionOptionsPanel.uc.js
// @license        This Source Code Form is subject to the terms of the Creative Commons Attribution-NonCommercial-ShareAlike International License, v. 4.0. If a copy of the CC BY-NC-SA 4.0 was not distributed with this file, You can obtain one at http://creativecommons.org/licenses/by-nc-sa/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
// ==/UserScript==

var openAddonsMgr = window.BrowserOpenAddonsMgr || window.BrowserAddonUI.openAddonsMgr; // Firefox
            
             
class ExtensionOptionsWidget {
  // user configuration. some of these are prefs that can be changed in
  // about:config or user.js. as for the others, you can directly edit this file
  // and change the value to the right of the colon.
  
  static config = {
    // this script replaces the "Add-ons & Themes" button in the app menu with
    // an "Extensions" button that opens our new panel instead of opening
    // about:addons. set to false if you want to leave this button alone
    "Replace addons button": Services.prefs.getBoolPref(
      "extensionOptionsPanel.replaceAddonsButton",
      true
    ),

    // set to false if you don't want the "Add-on options" title to be displayed
    // at the top of the panel
    "Show header": Services.prefs.getBoolPref(
      "extensionOptionsPanel.showHeader",
      true
    ),

    // show the addon version next to its name in the list
    "Show version": Services.prefs.getBoolPref(
      "extensionOptionsPanel.showVersion",
      true
    ),

    // about:addons shows you when an addon has a warning or error, e.g. it's
    // unsigned or blocked. if this is set to true, we'll show the same
    // information in the panel
    "Show addon messages": Services.prefs.getBoolPref(
      "extensionOptionsPanel.showAddonMessages",
      true
    ),

    // when hovering a theme in the panel, a preview/screenshot of the theme
    // will be displayed in a tooltip, if possible. this depends on the add-on
    // author.
    "Show theme preview tooltips": Services.prefs.getBoolPref(
      "extensionOptionsPanel.showThemePreviewTooltips",
      true
    ),

    // show system extensions?
    "Show hidden extensions": Services.prefs.getBoolPref(
      "extensionOptionsPanel.showHiddenExtensions",
      false
    ),

    // show extensions that you've disabled?
    "Show disabled extensions": Services.prefs.getBoolPref(
      "extensionOptionsPanel.showDisabledExtensions",
      true
    ),

    // show enabled extensions at the top of the list and disabled extensions at
    // the bottom?
    "Show enabled extensions first": Services.prefs.getBoolPref(
      "extensionOptionsPanel.showEnabledExtensionsFirst",
      true
    ),

    // put addon IDs in this list, separated by commas, to exclude them from the
    // list, e.g. ["screenshots@mozilla.org", "dark-theme@mozilla.org"]
    "Addon ID blacklist": JSON.parse(
      Services.prefs.getCharPref("extensionOptionsPanel.addonIDBlacklist", "[]")
    ),

    // if you want to change the button's icon for some reason, you can replace
    // this string with any URL or data URL that leads to an image.
    "Icon URL": `chrome://mozapps/skin/extensions/extension.svg`,

    // localization strings
    l10n: {
      // what should the button's label be when it's in the overflow panel or
      // customization palette?
      "Button label": "Add-ons and themes",

      // what should the button's tooltip be? I use sentence case since that's
      // the convention.
      "Button tooltip": "Расширения и темы",

      // title shown at the top of the panel (when "Show header" is true)
      "Panel title": "Расширения и темы",

      // label for the button that appears when you have no addons installed.
      "Download addons label": "Установить",

      // label for the about:addons button at the bottom of the panel
      "Addons page label": "Управление дополнениями",

      // labels for the addon subview buttons
      "Addon options label": "Настройки",
      "Manage addon label": "Управление",
      "Enable addon label": "Включить",
      "Disable addon label": "Отключить",
      "Uninstall addon label": "Удалить",
      "View source label": "Файл установки",
      "Manage shortcuts label": "Горячие клавиши",
      "Open homepage label": "Открыть дом. страницу",
      "Copy ID label": "Копировать ID",
      "Copy Name": "Копировать имя",
      "Copy Version": "Копировать версию",
      "Copy NameVersion": "Копировать имя и версию",  
      "Copy Inspector": "Инспектор",
      "Automatic updates label": "Автообновления:",
      "On Pin To Toolbar": "Закрепить на панели инструментов",
      

      // labels for the automatic update radio buttons
      autoUpdate: {
        "Default label": "По умолч.",
        "On label": "Вкл",
        "Off label": "Выкл",
      },

      "Run in private windows label": "В приватных окнах:",

      // labels for the run in private windows radio buttons
      runInPrivate: {
        "Allow label": "Разрешить",

        "Don't allow label": "Не разрешать",
      },

      // labels for addon buttons that have a warning or error, e.g. addon
      // automatically disabled because it's on a blocklist or unsigned
      addonMessages: {
        Blocked: "Blocked",

        "Signature required": "Signature required",

        Incompatible: "Incompatible",

        Unverified: "Unverified",

        Insecure: "Insecure",
      },
    },
  };

  /**
   * create a DOM node with given parameters
   * @param {object} aDoc (which doc to create the element in)
   * @param {string} tag (an HTML tag name, like "button" or "p")
   * @param {object} props (an object containing attribute name/value pairs,
   *                       e.g. class: ".bookmark-item")
   * @param {boolean} isHTML (if true, create an HTML element. if omitted or
   *                         false, create a XUL element. generally avoid HTML
   *                         when modding the UI, most UI elements are actually
   *                         XUL elements.)
   * @returns the created DOM node
   */
  create(aDoc, tag, props, isHTML = false) {
    let el = isHTML ? aDoc.createElement(tag) : aDoc.createXULElement(tag);
    for (let prop in props) el.setAttribute(prop, props[prop]);
    return el;
  }

  /**
   * set or remove multiple attributes for a given node
   * @param {object} el (a DOM node)
   * @param {object} props (an object of attribute name/value pairs)
   * @returns the DOM node
   */
  setAttributes(el, props) {
    for (let [name, value] of Object.entries(props)) {
      if (value) el.setAttribute(name, value);
      else el.removeAttribute(name);
    }
  }

  /**
   * make a valid ID for a DOM node based on an extension's ID.
   * @param {string} id (an extension's ID)
   * @returns an ID with crap removed so it can be used in a DOM node's ID.
   */
  makeWidgetId(id) {
    id = id.toLowerCase();
    return id.replace(/[^a-z0-9_-]/g, "_");
  }

  /**
   * for a given addon ID, get the Extension object from the addon policy
   * @param {string} id (an addon's ID)
   * @returns the Extension object
   */
  extensionForAddonId(id) {
    let policy = WebExtensionPolicy.getByID(id);
    return policy && policy.extension;
  }

  /**
   * find out if an addon has a valid signature
   * @param {object} addon (an Addon object, retrieved by AddonManager.getAddonsByTypes)
   * @returns true if signed, false if unsigned or invalid
   */
  isCorrectlySigned(addon) {
    // Add-ons without an "isCorrectlySigned" property are correctly signed as
    // they aren't the correct type for signing.
    return addon.isCorrectlySigned !== false;
  }

  /**
   * find out if an addon has been automatically disabled from the xpi database
   * because it lacked a valid signature and user had xpinstall.signatures.required = true
   * @param {object} addon (an Addon object)
   * @returns true if the addon was auto-disabled
   */
  isDisabledUnsigned(addon) {
    let signingRequired =
      addon.type == "locale"
        ? this.LANGPACKS_REQUIRE_SIGNING
        : this.REQUIRE_SIGNING;
    return signingRequired && !this.isCorrectlySigned(addon);
  }

  /**
   * find an addon's screenshot url. prefer 680x92.
   * @param {object} addon (an Addon object)
   * @returns {string} url
   */
  getScreenshotUrlForAddon(addon) {
    if (addon.id == "default-theme@mozilla.org") {
      return "chrome://mozapps/content/extensions/default-theme/preview.svg";
    }
    const builtInThemePreview = this.BuiltInThemes.previewForBuiltInThemeId(
      addon.id
    );
    if (builtInThemePreview) return builtInThemePreview;
    let { screenshots } = addon;
    if (!screenshots || !screenshots.length) return null;
    let screenshot = screenshots.find(s => s.width === 680 && s.height === 92);
    if (!screenshot) screenshot = screenshots[0];
    return screenshot.url;
  }

  // where panelviews are hiding when we're not looking
  viewCache(doc) {
    return doc.getElementById("appMenu-viewCache");
  }

  constructor() {
    ChromeUtils.defineESModuleGetters(this, {
      ExtensionPermissions:
        "resource://gre/modules/ExtensionPermissions.sys.mjs",
      BuiltInThemes: "resource:///modules/BuiltInThemes.sys.mjs",
    });
    ChromeUtils.defineLazyGetter(this, "extBundle", function () {
      return Services.strings.createBundle(
        "chrome://global/locale/extensions.properties"
      );
    });
    XPCOMUtils.defineLazyPreferenceGetter(
      this,
      "REQUIRE_SIGNING",
      "xpinstall.signatures.required",
      false
    );
    XPCOMUtils.defineLazyPreferenceGetter(
      this,
      "LANGPACKS_REQUIRE_SIGNING",
      "extensions.langpacks.signatures.required",
      false
    );
    this.viewId = "PanelUI-eom";
    this.config = ExtensionOptionsWidget.config;
    let { l10n } = this.config;
    if (
      /^chrome:\/\/browser\/content\/browser.(xul||xhtml)$/i.test(location) &&
      !CustomizableUI.getPlacementOfWidget("eom-button", true)
    ) {
      CustomizableUI.createWidget({
        id: "eom-button",
        viewId: this.viewId,
        type: "view",
        defaultArea: CustomizableUI.AREA_NAVBAR,
        removable: true,
        label: l10n["Button label"],
        tooltiptext: l10n["Button tooltip"],
        // if the button is middle-clicked, open the addons page instead of the panel
        onClick: event => {
          if (event.button == 1) {
            event.target.ownerGlobal.openAddonsMgr(
              "addons://list/extension"
            );
          }
        },
        // create the panelview before the toolbar button
        onBeforeCreated: aDoc => {
          let eop = aDoc.defaultView.extensionOptionsPanel;
          if (!eop) return;
          let view = eop.create(aDoc, "panelview", {
            id: eop.viewId,
            class: "PanelUI-subView cui-widget-panelview",
            flex: "1",
            style: "min-width:30em",
          });
          aDoc.getElementById("appMenu-viewCache").appendChild(view);
          aDoc.defaultView.extensionOptionsPanel.panelview = view;

          if (eop.config["Show header"]) {
            let header = view.appendChild(
              eop.create(aDoc, "vbox", { id: "eom-mainView-panel-header" })
            );
            let heading = header.appendChild(eop.create(aDoc, "label"));
            let label = heading.appendChild(
              eop.create(aDoc, "html:span", {
                id: "eom-mainView-panel-header-span",
                role: "heading",
                "aria-level": "1",
              })
            );
            label.textContent = l10n["Panel title"];
            view.appendChild(aDoc.createXULElement("toolbarseparator"));
          }

          view.appendChild(
            eop.create(aDoc, "vbox", {
              id: `${view.id}-body`,
              class: "panel-subview-body",
            })
          );

          // create the theme preview tooltip
          if (eop.config["Show theme preview tooltips"]) {
            let tooltip = aDoc
              .getElementById("mainPopupSet")
              .appendChild(
                aDoc.defaultView.MozXULElement.parseXULToFragment(
                  `<tooltip id="eom-theme-preview-tooltip" noautohide="true" orient="vertical"><vbox id="eom-theme-preview-box"><html:img id="eom-theme-preview-canvas"></html:img></vbox></tooltip>`
                )
              );
            tooltip.addEventListener("popupshowing", e =>
              eop.onTooltipShowing(e)
            );
          }

          eop.fluentSetup(aDoc).then(() => eop.swapAddonsButton(aDoc));
        },
        // populate the panel before it's shown
        onViewShowing: event => {
          if (
            event.originalTarget ===
            event.target.ownerGlobal.extensionOptionsPanel?.panelview
          ) {
            event.target.ownerGlobal.extensionOptionsPanel.getAddonsAndPopulate(
              event
            );
          }
        },
        // delete the panel if the widget node is destroyed
        onDestroyed: aDoc => {
          let view = aDoc.getElementById(
            aDoc.defaultView.extensionOptionsPanel?.viewId
          );
          if (view) {
            aDoc.defaultView.CustomizableUI.hidePanelForNode(view);
            view.remove();
          }
        },
      });
    }
    this.loadStylesheet(); // load the stylesheet
  }

  // grab localized strings for the extensions button and disabled/enabled extensions headings
  async fluentSetup(aDoc) {
    aDoc.ownerGlobal.MozXULElement.insertFTLIfNeeded(
      "toolkit/about/aboutAddons.ftl"
    );
    let [extensions, themes, enabled, disabled, privateHelp] =
      await aDoc.l10n.formatValues([
        "addon-category-extension",
        "addon-category-theme",
        "extension-enabled-heading",
        "extension-disabled-heading",
        "addon-detail-private-browsing-help",
      ]);
    privateHelp = privateHelp.replace(/\s*\<.*\>$/, "");
    this.aboutAddonsStrings = {
      extensions,
      themes,
      enabled,
      disabled,
      privateHelp,
    };
  }

  /**
   * this script changes the built-in "Add-ons & themes" button in the app menu
   * to open our new panel instead of opening about:addons
   * @param {object} aDoc (the document our widget has been created within)
   */
  swapAddonsButton(aDoc) {
    if (!this.config["Replace addons button"]) return;
    let win = aDoc.defaultView;
    win.PanelUI._initialized ||
      win.PanelUI.init(shouldSuppressPopupNotifications);
    let el = win.PanelUI.mainView.querySelector(
      "#appMenu-extensions-themes-button, #appMenu-addons-button"
    );
    this.setAttributes(el, {
      command: 0,
      key: 0,
      shortcut: 0,
      class: "subviewbutton subviewbutton-nav",
      closemenu: "none",
    });
    el.addEventListener("command", () =>
      win.PanelUI.showSubView("PanelUI-eom", el)
    );
  }

  /**
   * grab all addons and populate the panel with them.
   * @param {object} e (a ViewShowing event)
   */
  async getAddonsAndPopulate(e) {
    let extensions = await AddonManager.getAddonsByTypes(["extension"]);
    let themes = await AddonManager.getAddonsByTypes(["theme"]);
    this.populatePanelBody(e, { extensions, themes });
  }

  /**
   * create everything inside the panel
   * @param {object} e (a ViewShowing event - its target is the panelview node)
   * @param {array} addons (an object containing arrays for different addon
   *                       types e.g. extensions, themes)
   */
  populatePanelBody(e, addons) {
    let prevState;
    let { extensions, themes } = addons;
    let view = e?.target || this.panelview;
    let win = view.ownerGlobal;
    let doc = win.document;
    let body = view.querySelector(".panel-subview-body");
    let { l10n } = this.config;
    let enabledFirst = this.config["Show enabled extensions first"];
    let showVersion = this.config["Show version"];
    let showDisabled = this.config["Show disabled extensions"];
    let blackListArray = this.config["Addon ID blacklist"];

    // clear all the panel items and subviews before rebuilding them.
    while (body.hasChildNodes()) body.firstChild.remove();
    [...this.viewCache(doc).children].forEach(panel => {
      if (panel.id.includes("PanelUI-eom-addon-")) panel.remove();
    });
    let appMenuMultiView = win.PanelMultiView.forNode(PanelUI.multiView);
    if (
      win.PanelMultiView.forNode(view.closest("panelmultiview")) ===
      appMenuMultiView
    ) {
      [...appMenuMultiView._viewStack.children].forEach(panel => {
        if (panel.id !== view.id && panel.id.includes("PanelUI-eom-addon-")) {
          panel.remove();
        }
      });
    }

    // extensions...
    let enabledSubheader = body.appendChild(
      this.create(doc, "h2", { class: "subview-subheader" }, true)
    );
    enabledSubheader.textContent =
      this.aboutAddonsStrings[showDisabled ? "enabled" : "extensions"];
    extensions
      .sort((a, b) => {
        // get sorted by enabled state...
        let ka =
          (enabledFirst ? Number(!a.isActive) : "") + a.name.toLowerCase();
        let kb =
          (enabledFirst ? Number(!b.isActive) : "") + b.name.toLowerCase();
        return ka < kb ? -1 : 1;
      })
      .forEach(addon => {
        // then get excluded if config wills it...
        if (
          !blackListArray.includes(addon.id) &&
          (!addon.hidden || this.config["Show hidden extensions"]) &&
          (!addon.userDisabled || showDisabled)
        ) {
          // then get built into subviewbuttons and corresponding subviews...
          if (
            showDisabled &&
            enabledFirst &&
            prevState &&
            addon.isActive != prevState
          ) {
            body.appendChild(doc.createXULElement("toolbarseparator"));
            let disabledSubheader = body.appendChild(
              this.create(doc, "h2", { class: "subview-subheader" }, true)
            );
            disabledSubheader.textContent = this.aboutAddonsStrings.disabled;
          }
          prevState = addon.isActive;

          let subviewbutton = body.appendChild(
            this.create(doc, "toolbarbutton", {
              label: addon.name + (showVersion ? ` ${addon.version}` : ""),
              class:
                "subviewbutton subviewbutton-iconic subviewbutton-nav eom-addon-button",
              closemenu: "none",
              "addon-type": "extension",
              "data-extensionid": addon.id,
            })
          );
          subviewbutton.addEventListener("command", e =>
            this.showSubView(e, subviewbutton)
          );
          if (!addon.isActive) subviewbutton.classList.add("disabled");
          // set the icon using CSS variables and list-style-image so that user stylesheets can override the icon URL.
          subviewbutton.style.setProperty(
            "--extension-icon",
            `url(${addon.iconURL || this.config["Icon URL"]})`
          );
          subviewbutton._Addon = addon;

          if (this.config["Show addon messages"]) {
            this.setAddonMessage(doc, subviewbutton, addon);
          }
        }
      });

    // themes...
    let themesSeparator = body.appendChild(
      doc.createXULElement("toolbarseparator")
    );
    let themesSubheader = body.appendChild(
      this.create(doc, "h2", { class: "subview-subheader" }, true)
    );
    themesSubheader.textContent = this.aboutAddonsStrings.themes;
    themes.forEach(addon => {
      if (
        !blackListArray.includes(addon.id) &&
        (!addon.hidden || this.config["Show hidden extensions"]) &&
        (!addon.userDisabled || showDisabled)
      ) {
        let subviewbutton = body.appendChild(
          this.create(doc, "toolbarbutton", {
            label: addon.name + (showVersion ? ` ${addon.version}` : ""),
            class: "subviewbutton subviewbutton-iconic eom-addon-button",
            closemenu: "none",
            "addon-type": "theme",
            "data-extensionid": addon.id,
          })
        );
        subviewbutton.addEventListener("command", async e => {
          await addon[addon.userDisabled ? "enable" : "disable"]();
          subviewbutton.parentElement
            .querySelectorAll(`.eom-addon-button[addon-type="theme"]`)
            .forEach(btn => {
              btn.classList[btn._Addon?.isActive ? "remove" : "add"](
                "disabled"
              );
              this.setAddonMessage(doc, btn, btn._Addon);
            });
        });
        if (!addon.isActive) subviewbutton.classList.add("disabled");
        subviewbutton.style.setProperty(
          "--extension-icon",
          `url(${addon.iconURL || this.config["Icon URL"]})`
        );
        subviewbutton._Addon = addon;

        this.setAddonMessage(doc, subviewbutton, addon);
      }
    });

    // if no addons are shown, display a "Download Addons" button that leads to AMO.
    let getAddonsButton = body.appendChild(
      this.create(doc, "toolbarbutton", {
        id: "eom-get-addons-button",
        class: "subviewbutton subviewbutton-iconic",
        label: l10n["Download addons label"],
        image: `data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 68 68" style="border-radius:3px"><path fill="context-fill" fill-opacity="context-fill-opacity" d="M0 0v68h68V0H0zm61.8 49H49.5V32.4c0-5.1-1.7-7-5-7-4 0-5.6 2.9-5.6 6.9v10.2h3.9v6.4H30.5V32.4c0-5.1-1.7-7-5-7-4 0-5.6 2.9-5.6 6.9v10.2h5.6v6.4h-18v-6.4h3.9V26H7.5v-6.4h12.3V24c1.8-3.1 4.8-5 8.9-5 4.2 0 8.1 2 9.5 6.3 1.6-3.9 4.9-6.3 9.5-6.3 5.3 0 10.1 3.2 10.1 10.1v13.5h4V49z"/></svg>`,
      })
    );
    getAddonsButton.addEventListener("command", e =>
      win.switchToTabHavingURI(
        Services.urlFormatter.formatURLPref("extensions.getAddons.link.url"),
        true,
        {
          inBackground: false,
          triggeringPrincipal:
            Services.scriptSecurityManager.getSystemPrincipal(),
        }
      )
    );

    let hasExtensions = !!body.querySelector(
      `.eom-addon-button[addon-type="extension"]`
    );
    let hasThemes = !!body.querySelector(
      `.eom-addon-button[addon-type="theme"]`
    );
    getAddonsButton.hidden = hasExtensions || hasThemes;
    if (!hasExtensions) {
      enabledSubheader.remove();
      themesSeparator.remove();
    }
    if (!hasThemes) {
      themesSubheader.remove();
      themesSeparator.remove();
    }

    // make a footer button that leads to about:addons
    if (view.querySelector("#eom-allAddonsButton")) return;
    view.appendChild(doc.createXULElement("toolbarseparator"));
    let footerButton = view.appendChild(
      this.create(doc, "toolbarbutton", {
        label: l10n["Addons page label"],
        id: "eom-allAddonsButton",
        class: "subviewbutton subviewbutton-iconic panel-subview-footer-button",
        image: this.config["Icon URL"],
        key: "key_openAddons",
        shortcut: win.ShortcutUtils.prettifyShortcut(win.key_openAddons),
      })
    );
    footerButton.addEventListener("command", () =>
      openAddonsMgr("addons://list/extension")
    );
    
    if (view.querySelector("#eom-allAddonsButton1")) return;
     let footerButton1 = view.appendChild (
      this.create(doc, "toolbarbutton", {
        label: "Обновления",
        id: "eom-allAddonsButton1",
        class: "subviewbutton subviewbutton-iconic panel-subview-footer-button",
        image: this.config["Icon URL"],
      })
      );
     footerButton1.addEventListener("command", () =>
     openAddonsMgr("addons://updates/recent")
    ); 
    
    if (view.querySelector("#eom-allAddonsButton2")) return;
     let footerButton2 = view.appendChild (
      this.create(doc, "toolbarbutton", {
        label: "Обновить дополнения",
        id: "ForAddonsUpdates",
        class: "subviewbutton subviewbutton-iconic panel-subview-footer-button",
        image: this.config["Icon URL"],
      })
      );
     footerButton2.addEventListener("command", () =>
     this.checkForAddonsUpdates() 
    ); 
  }


  /**
   * for a given button made for an addon, find out if it has a message
   * (blocked, unverified, etc.) and if so, display it
   * @param {object} doc (the document we're localizing)
   * @param {object} subviewbutton (an addon button in the panel)
   * @param {object} addon (an Addon object)
   */
  async setAddonMessage(doc, subviewbutton, addon) {
    const { l10n } = this.config;
    const { name } = addon;
    const { STATE_BLOCKED, STATE_SOFTBLOCKED } = Ci.nsIBlocklistService;
    const formatString = (type, args) => {
      return new Promise(resolve => {
        doc.l10n
          .formatMessages([{ id: `details-notification-${type}2`, args }])
          .then(msg => resolve(msg[0].value));
      });
    };

    let message = null;
    if (addon.blocklistState === STATE_BLOCKED) {
      message = {
        label: l10n.addonMessages.Blocked,
        detail: await formatString("blocked", { name }),
        type: "error",
      };
    } else if (this.isDisabledUnsigned(addon)) {
      message = {
        label: l10n.addonMessages["Signature Required"],
        detail: await formatString("unsigned-and-disabled", { name }),
        type: "error",
      };
    } else if (
      !addon.isCompatible &&
      (AddonManager.checkCompatibility ||
        addon.blocklistState !== STATE_SOFTBLOCKED)
    ) {
      message = {
        label: l10n.addonMessages.Incompatible,
        detail: await formatString("incompatible", {
          name,
          version: Services.appinfo.version,
        }),
        type: "warning",
      };
    } else if (!this.isCorrectlySigned(addon)) {
      message = {
        label: l10n.addonMessages.Unverified,
        detail: await formatString("unsigned", { name }),
        type: "warning",
      };
    } else if (addon.blocklistState === STATE_SOFTBLOCKED) {
      message = {
        label: l10n.addonMessages.Insecure,
        detail: await formatString("softblocked", { name }),
        type: "warning",
      };
    }
    if (
      this.config["Show theme preview tooltips"] &&
      addon.type === "theme" &&
      (!message || message.type !== "error")
    ) {
      message = message ?? {};
      message.detail = "";
      message.tooltip = "eom-theme-preview-tooltip";
      message.preview = this.getScreenshotUrlForAddon(addon);
      if (addon.isActive) {
        message.label = null;
        message.checked = true;
      }
    }
    if (subviewbutton._addonMessage) {
      subviewbutton.removeAttribute("message-type");
      subviewbutton.removeAttribute("tooltiptext");
      subviewbutton.removeAttribute("tooltip");
      subviewbutton.removeAttribute("enable-checked");
      subviewbutton.querySelector(".eom-message-label")?.remove();
      delete subviewbutton._addonMessage;
    }
    if (message) {
      subviewbutton.setAttribute("message-type", message?.type);
      subviewbutton.setAttribute("tooltiptext", message?.detail);
      if (message.tooltip) {
        subviewbutton.setAttribute("tooltip", message.tooltip);
      }
      if (message.checked) subviewbutton.setAttribute("enable-checked", true);
      if (message.label) {
        subviewbutton.appendChild(
          this.create(document, "h", {
            class: "toolbarbutton-text eom-message-label",
          })
        ).textContent = `(${message.label})`;
      }
    }
    subviewbutton._addonMessage = message;
  }

  /**
   * show the subview for a given extension
   * @param {object} event (a triggering command/click event)
   * @param {object} anchor (the subviewbutton that was clicked —
   *                        dictates the title of the subview)
   */
  showSubView(event, anchor) {
    if (!("_Addon" in anchor)) return;
    this.buildSubView(anchor, anchor._Addon);
    event.target.ownerGlobal.PanelUI?.showSubView(
      `PanelUI-eom-addon-${this.makeWidgetId(anchor._Addon.id)}`,
      anchor,
      event
    );
  }

  /**
   * for a given addon, build a panel subview
   * @param {object} subviewbutton (the button you click to enter the subview,
   *                               corresponding to the addon)
   * @param {object} addon (an addon object provided by the AddonManager,
   *                       with all the data we need)
   */
  buildSubView(subviewbutton, addon) {
    let { l10n } = this.config;
    let win = subviewbutton.ownerGlobal;
    let doc = win.document;
    let view = this.viewCache(doc).appendChild(
      this.create(doc, "panelview", {
        id: `PanelUI-eom-addon-${this.makeWidgetId(addon.id)}`, // turn the extension ID into a DOM node ID
        flex: "1",
        class: "PanelUI-subView cui-widget-panelview",
      })
    );

    // create options button
    let optionsButton = view.appendChild(
      this.create(doc, "toolbarbutton", {
        label: l10n["Addon options label"],
        class: "subviewbutton",
      })
    );
    optionsButton.addEventListener("command", e =>
      this.openAddonOptions(addon, win)
    );

    // manage button, when no options page exists
    let manageButton = view.appendChild(
      this.create(doc, "toolbarbutton", {
        label: l10n["Manage addon label"],
        class: "subviewbutton",
      })
    );
    manageButton.addEventListener("command", e =>
      openAddonsMgr(
        `addons://detail/${encodeURIComponent(addon.id)}`
      )
    );

    // disable button
    let disableButton = view.appendChild(
      this.create(doc, "toolbarbutton", {
        label: addon.userDisabled
          ? l10n["Enable addon label"]
          : l10n["Disable addon label"],
        class: "subviewbutton",
        closemenu: "none",
      })
    );
    disableButton.addEventListener("command", async e => {
      if (addon.userDisabled) {
        await addon.enable();
        disableButton.setAttribute("label", l10n["Disable addon label"]);
      } else {
        await addon.disable();
        disableButton.setAttribute("label", l10n["Enable addon label"]);
      }
      this.getAddonsAndPopulate();
    });



// on Pin To Toolbar
    let onPinToTool = view.appendChild(
      this.create(doc, "toolbarbutton", {
        label: l10n["On Pin To Toolbar"],
        closemenu: "none",
        class: "subviewbutton",
        type: "checkbox",
        checked: "false",
          })
    );
    onPinToTool.addEventListener("command", async ( event) => {
          const extension = this.extensionForAddonId(addon.id);
          const browserAction = extension.apiManager.global.browserActionFor(extension);
          const checkbox = event.target;

     if (browserAction) {
         gUnifiedExtensions.pinToToolbar(
        browserAction.widget?.id, checkbox.getAttribute("checked") == "true"
       );
}
     
    });


    // uninstall button, and so on...
    let uninstallButton = view.appendChild(
      this.create(doc, "toolbarbutton", {
        label: l10n["Uninstall addon label"],
        class: "subviewbutton",
      })
    );
    uninstallButton.addEventListener("command", e => {
      if (
        win.Services.prompt.confirm(
          null,
          null,
          `Delete ${addon.name} permanently?`
        )
      ) {
        addon.pendingOperations & win.AddonManager.PENDING_UNINSTALL
          ? addon.cancelUninstall()
          : addon.uninstall();
      }
    });
    
   let themesSeparator1 = view.appendChild(
      doc.createXULElement("toolbarseparator")
    );
    
    // allow automatic updates radio group
    let updates = view.appendChild(
      this.create(doc, "hbox", {
        id: "eom-allow-auto-updates",
        class: "subviewbutton eom-radio-hbox",
        align: "center",
      })
    );
    let updatesLabel = updates.appendChild(
      this.create(doc, "label", {
        id: "eom-allow-auto-updates-label",
        class: "toolbarbutton-text eom-radio-label",
        flex: 1,
        wrap: true,
      })
    );
    updatesLabel.textContent = l10n["Automatic updates label"];
    let updatesGroup = updates.appendChild(
      this.create(doc, "radiogroup", {
        id: "eom-allow-auto-updates-group",
        class: "eom-radio-group",
        value: addon.applyBackgroundUpdates,
        closemenu: "none",
        orient: "horizontal",
        "aria-labelledby": "eom-allow-auto-updates-label",
      })
    );
    updatesGroup.addEventListener(
      "command",
      e => (addon.applyBackgroundUpdates = e.target.value)
    );
    updatesGroup.appendChild(
      this.create(doc, "radio", {
        label: l10n.autoUpdate["Default label"],
        class: "subviewradio",
        value: 1,
      })
    );
    updatesGroup.appendChild(
      this.create(doc, "radio", {
        label: l10n.autoUpdate["On label"],
        class: "subviewradio",
        value: 2,
      })
    );
    updatesGroup.appendChild(
      this.create(doc, "radio", {
        label: l10n.autoUpdate["Off label"],
        class: "subviewradio",
        value: 0,
      })
    );

    // run in private windows radio group
    let setPrivateState = async (addon, node) => {
      let perms = await this.ExtensionPermissions.get(addon.id);
      let isAllowed = perms.permissions.includes(
        "internal:privateBrowsingAllowed"
      );
      node.permState = isAllowed;
      node.value = isAllowed ? 1 : 0;
    };
    let privateWindows = view.appendChild(
      this.create(doc, "hbox", {
        id: "eom-run-in-private",
        class: "subviewbutton eom-radio-hbox",
        align: "center",
      })
    );
    let privateLabel = privateWindows.appendChild(
      this.create(doc, "label", {
        id: "eom-run-in-private-label",
        class: "toolbarbutton-text eom-radio-label",
        flex: 1,
        wrap: true,
        tooltiptext: this.aboutAddonsStrings.privateHelp,
      })
    );
    privateLabel.textContent = l10n["Run in private windows label"];
    let privateGroup = privateWindows.appendChild(
      this.create(doc, "radiogroup", {
        id: "eom-run-in-private-group",
        class: "eom-radio-group",
        closemenu: "none",
        orient: "horizontal",
        "aria-labelledby": "eom-run-in-private-label",
      })
    );
    privateGroup.addEventListener("command", async () => {
      let extension = this.extensionForAddonId(addon.id);
      await this.ExtensionPermissions[
        privateGroup.permState ? "remove" : "add"
      ](
        addon.id,
        {
          permissions: ["internal:privateBrowsingAllowed"],
          origins: [],
        },
        extension
      );
      setPrivateState(addon, privateGroup);
    });
    privateGroup.appendChild(
      this.create(doc, "radio", {
        label: l10n.runInPrivate["Allow label"],
        class: "subviewradio",
        value: 1,
      })
    );
    privateGroup.appendChild(
      this.create(doc, "radio", {
        label: l10n.runInPrivate["Don't allow label"],
        class: "subviewradio",
        value: 0,
      })
    );
    setPrivateState(addon, privateGroup);


    let themesSeparator2 = view.appendChild(
      doc.createXULElement("toolbarseparator")
    );
    // manage shortcuts
    let shortcutsButton = view.appendChild(
      this.create(doc, "toolbarbutton", {
        label: l10n["Manage shortcuts label"],
        class: "subviewbutton",
      })
    );
    shortcutsButton.addEventListener("command", () =>
      openAddonsMgr("addons://shortcuts/shortcuts")
    );

    let viewSrcButton = view.appendChild(
      this.create(doc, "toolbarbutton", {
        label: l10n["View source label"],
        class: "subviewbutton",
      })
    );
    viewSrcButton.addEventListener("command", () => this.openArchive(addon));

   let viewSrcButton1 = view.appendChild(
      this.create(doc, "toolbarbutton", {
        label: "Папка установки",
        class: "subviewbutton",
      })
    );
    viewSrcButton1.addEventListener("command", () => this.openDir(addon));

    let homePageButton = view.appendChild(
      this.create(doc, "toolbarbutton", {
        label: l10n["Open homepage label"],
        class: "subviewbutton",
      })
    );
    homePageButton.addEventListener("command", () => {
      win.switchToTabHavingURI(addon.homepageURL || addon.supportURL, true, {
        inBackground: false,
        triggeringPrincipal:
          win.Services.scriptSecurityManager.getSystemPrincipal(),
      });
    });

    let copyIdButton = view.appendChild(
      this.create(doc, "toolbarbutton", {
        label: l10n["Copy ID label"],
        class: "subviewbutton",
      })
    );
    copyIdButton.addEventListener("command", () => {
      win.Cc["@mozilla.org/widget/clipboardhelper;1"]
        .getService(win.Ci.nsIClipboardHelper)
        .copyString(addon.id);
      let PMV =
        view.panelMultiView && win.PanelMultiView.forNode(view.panelMultiView);
      if (PMV) {
        let panel = PMV._panel;
        if (panel && PMV._getBoundsWithoutFlushing(panel.anchorNode)?.width) {
          win.CustomHint?.show(panel.anchorNode, "Copied");
        }
      }
    });
    
    let copyNameButton = view.appendChild(
      this.create(doc, "toolbarbutton", {
        label: l10n["Copy Name"],
        class: "subviewbutton",
      })
    );
    copyNameButton.addEventListener("command", () => {
      win.Cc["@mozilla.org/widget/clipboardhelper;1"]
        .getService(win.Ci.nsIClipboardHelper)
        .copyString(addon.name);
      
    });
    
    let copyVersionButton = view.appendChild(
      this.create(doc, "toolbarbutton", {
        label: l10n["Copy Version"],
        class: "subviewbutton",
      })
    );
    copyVersionButton.addEventListener("command", () => {
      win.Cc["@mozilla.org/widget/clipboardhelper;1"]
        .getService(win.Ci.nsIClipboardHelper)
        .copyString(addon.version);
    });

     let copyNameVersionButton1 = view.appendChild(
      this.create(doc, "toolbarbutton", {
        label: l10n["Copy NameVersion"],
        class: "subviewbutton ",
      })
    );
    copyNameVersionButton1.addEventListener("command", () => {
      win.Cc["@mozilla.org/widget/clipboardhelper;1"]
        .getService(win.Ci.nsIClipboardHelper)
        .copyString(addon.name + " " + addon.version);
    });
    
    let InspectorButton = view.appendChild(
      this.create(doc, "toolbarbutton", {
        label: l10n["Copy Inspector"],
        class: "subviewbutton ",
      })
    );
      InspectorButton.addEventListener("command", () => {
      var  url3 = `about:devtools-toolbox?id=${encodeURIComponent(   addon.id    )}&type=extension`;
     gBrowser.fixupAndLoadURIString(url3, { triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal() }); 

    });

    view.addEventListener("ViewShowing", () => {
      optionsButton.hidden = !addon.optionsURL;
      manageButton.hidden = !!addon.optionsURL;
      updates.hidden = !(addon.permissions & win.AddonManager.PERM_CAN_UPGRADE);
      updatesGroup.setAttribute("value", addon.applyBackgroundUpdates);
      privateWindows.hidden = !(
        addon.incognito != "not_allowed" &&
        !!(
          addon.permissions &
          win.AddonManager.PERM_CAN_CHANGE_PRIVATEBROWSING_ACCESS
        )
      );
      setPrivateState(addon, privateGroup);
      shortcutsButton.hidden = !this.extensionForAddonId(addon.id)?.shortcuts
        ?.manifestCommands?.size;
      disableButton.setAttribute(
        "label",
        addon.userDisabled
          ? l10n["Enable addon label"]
          : l10n["Disable addon label"]
      );
      uninstallButton.hidden = viewSrcButton.hidden =
        addon.isSystem || addon.isBuiltin || addon.temporarilyInstalled;
      homePageButton.hidden = !(addon.homepageURL || addon.supportURL);
    });
  }

  /**
   * open a given addon's options page
   * @param {object} addon (an addon object)
   * @param {object} win (the window from which this was invoked)
   */
  openAddonOptions(addon, win) {
    if (!addon.isActive || !addon.optionsURL) return;

    switch (Number(addon.optionsType)) {
      case 5:
        win.BrowserOpenAddonsMgr(
          `addons://detail/${win.encodeURIComponent(addon.id)}/preferences`
        );
        break;
      case 3:
        win.switchToTabHavingURI(addon.optionsURL, true);
        break;
      case 1: {
        let windows = win.Services.wm.getEnumerator(null);
        while (windows.hasMoreElements()) {
          let win2 = windows.getNext();
          if (win2.closed) continue;
          if (win2.document.documentURI == addon.optionsURL) {
            win2.focus();
            return;
          }
        }
        let features = "chrome,titlebar,toolbar,centerscreen";
        if (
          win.Services.prefs.getBoolPref("browser.preferences.instantApply")
        ) {
          features += ",dialog=no";
        }
        win.openDialog(addon.optionsURL, addon.id, features);
      }
    }
  }

  /**
   * open a given addon's source xpi file in the user's associated program, e.g. 7-zip
   * @param {object} addon (an addon object)
   */
  openArchive(addon) {
    let dir = Services.dirsvc.get("ProfD", Ci.nsIFile);
    dir.append("extensions");
    dir.append(`${addon.id}.xpi`);
    dir.launch();
  }

  openDir(addon) {
    let dir = Services.dirsvc.get("ProfD", Ci.nsIFile);
    dir.append("extensions");
    dir.append(`${addon.id}.xpi`);
    dir.reveal();
  }


  onTooltipShowing(e) {
    let anchor = e.target.triggerNode
      ? e.target.triggerNode.closest(".eom-addon-button")
      : null;
    let message = anchor._addonMessage;
    let img = e.target.querySelector("#eom-theme-preview-canvas");
    img.src = message?.preview || "";
    if (!anchor || !message?.preview) {
      e.preventDefault();
      return;
    }
    e.target.setAttribute("position", "after_start");
    e.target.moveToAnchor(anchor, "after_start");
  }

  // generate and load a stylesheet
  loadStylesheet() {
    let sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(
      Ci.nsIStyleSheetService
    );
    let css = /*css*/ `#eom-button {
  list-style-image: url('${this.config["Icon URL"]}');
}
#eom-mainView-panel-header {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: var(--arrowpanel-menuitem-margin-inline);
  min-height: var(--arrowpanel-header-min-height);
}
#eom-mainView-panel-header-span {
  font-weight: 600;
  display: inline-block;
  text-align: center;
  overflow-wrap: break-word;
}
.panel-header ~ #eom-mainView-panel-header,
.panel-header ~ #eom-mainView-panel-header + toolbarseparator {
  display: none;
}
.eom-addon-button {
  list-style-image: var(--extension-icon);
}
#${this.viewId} {
  min-height: 400px;
  min-width: 27em;
}
#${this.viewId} .disabled label {
  opacity: 0.6;
  font-style: italic;
}
#${this.viewId} .eom-message-label {
  opacity: 0.6;
  margin-inline-start: 8px;
  font-style: italic;
}
.eom-addon-button:is(toolbarbutton.subviewbutton) {
  &[message-type="warning"] {
    background-color: var(--eom-warning-bg, hsla(48, 100%, 66%, 0.15));

    &:not([disabled], [open], :active):is(:hover) {
      background-color: var(
        --eom-warning-bg-hover,
        color-mix(in srgb, currentColor 8%, hsla(48, 100%, 66%, 0.18))
      );
    }
    &:not([disabled]):is([open], :hover:active) {
      background-color: var(
        --eom-warning-bg-active,
        color-mix(in srgb, currentColor 15%, hsla(48, 100%, 66%, 0.2))
      );
    }
  }
  &[message-type="error"] {
    background-color: var(--eom-error-bg, hsla(2, 100%, 66%, 0.15));

    &:not([disabled], [open], :active):is(:hover) {
      background-color: var(
        --eom-error-bg-hover,
        color-mix(in srgb, currentColor 8%, hsla(2, 100%, 66%, 0.18))
      );
    }
    &:not([disabled]):is([open], :hover:active) {
      background-color: var(
        --eom-error-bg-active,
        color-mix(in srgb, currentColor 15%, hsla(2, 100%, 66%, 0.2))
      );
    }
  }
}
.eom-radio-hbox {
  padding-block: 4px;
}
.eom-radio-hbox .radio-check {
  margin-block: 0;
}
.eom-radio-hbox label {
  padding-bottom: 1px;
}
.eom-radio-label {
  margin-inline-end: 8px;
}
.eom-radio-hbox .subviewradio {
  margin: 0;
  margin-inline: 2px;
  padding: 0;
  background: none !important;
}
.eom-radio-hbox .radio-label-box {
  margin: 0;
  padding: 0;
}
.eom-radio-label[tooltiptext] {
  cursor: help;
}
.eom-addon-button[enable-checked]::after {
  -moz-context-properties: fill, fill-opacity;
  content: url(chrome://global/skin/icons/check.svg);
  fill: currentColor;
  fill-opacity: 0.6;
  display: block;
  margin-inline-start: 10px;
}
#eom-theme-preview-tooltip {
  appearance: none;
  padding: 0;
  border-radius: var(--arrowpanel-border-radius, 8px);
  overflow: hidden;
  border: 1px solid var(--arrowpanel-border-color);
  background: var(--arrowpanel-border-color);
}
`;
    let uri = makeURI(`data:text/css;charset=UTF=8,${encodeURIComponent(css)}`);
    if (sss.sheetRegistered(uri, sss.AUTHOR_SHEET)) return;
    sss.loadAndRegisterSheet(uri, sss.AUTHOR_SHEET);
  }
}

window.extensionOptionsPanel = new ExtensionOptionsWidget();


Не плохой вариант на замену кнопки от infocatcher. Как выражается автор - это его видения в будущем этой панели, которое придет не скоро. Я обратился к нему . но на письма он никому не отвечет больше 2-х месяцев.
Недостатки:
1. Она не заменяет полностью основную панель - выражается это в том, что если спрятать основную, в какую нибудь панель, которая закрывается, то невозможно установить дополнения, т.к. это системная кнопка. Хотелось чтобы, когда я спрятал основную, расширения устанавливались через эту, если возможно.
2. Я дополнил эту панель несколькоми пунктами, один из них - закрепить на панели инструментов, но надо щелкнуть мышью не один раз, а 2 раза, в чем причина не понятно
3. Не хватает пункта- проверить обновления. Для меня достаточно - проверить на обновления, если есть, то открыть вкладку-последние обновления, если нет: алерт - не найдено.
Плюсы
1. Простота кода и  добавления своих пунктов каких душе угодно
2. Современный вид
Dumby если не сложно, можешь посмотреть пункты недостатки и предложить свое решение

Еще один вопрос при таком построении создании окна
var img = doc.createElementNS(xhtmlns, 'div');
и т.д.
var img1 = doc.createElementNS(xhtmlns, 'input');
    img1.setAttribute ('type', 'button');
    img1.setAttribute('style', 'и т.д.');
    и т.д. остальные setAttribute
Как подключить к стилю input: hover

Добавлено Вчера 01:11:23
mokujin хоть и старый но работает надежно, жалко от него отказываться, просто в том месте, что указал надо внести изменения, а там дальше посмотрим. Хотя  можно и это в этой версии браузера проигнорировать это. Если переключить security.browser_xhtml_csp.enabled то вообще не будет показывать этих ошибок, связанных с oncommand и другими Content-Security-Policy. За исправления спасибо

Отредактировано Andrey_Krropotkin (Вчера 01:17:01)

Отсутствует

 

№17327Вчера 01:49:59

Dobrov
Участник
 
Группа: Members
Зарегистрирован: 04-10-2011
Сообщений: 487
UA: Firefox 128.0

Re: Custom Buttons

Andrey_Krropotkin пишет

замена панели unified-extensions-button

Эта панель мало информативная, на мой взгляд, нужно открывать подменю каждого дополнения.
Кнопка меню с Дополнениями от Vitaliy V. более наглядная, ей проще пользоваться.

Отсутствует

 

Board footer

Powered by PunBB
Modified by Mozilla Russia
Copyright © 2004–2020 Mozilla Russia GitHub mark
Язык отображения форума: [Русский] [English]