Dumby
Спасибо, проблем не видно. Даже ПКМ по пункту работает.
#context_autoreloadTab:not([checked]) > menupopup > :first-child { display: none !important; }
заменил на
#context_autoreloadTab:not([checked]) > .menu-iconic-left > image { fill: gray !important; -moz-context-properties: fill !important; list-style-image: url("chrome://browser/skin/reload.svg") !important; }
а то текст съезжает влево при неактой перезагрузке.
Отсутствует
Dumby
В 99 здесь скачет...
https://ibb.co/PrMgt9f
Zoom -по умолчанию больше 100% и все , тогда скачет...
Да, и надписи в СВ , типа ini неровные
Отредактировано ВВП (26-04-2022 19:41:01)
Отсутствует
Zoom -по умолчанию больше 100% и все , тогда скачет...
Во, так вижу.
Надо бы найти регрессора и отписаться Infocatcher'у, но времени нет.
Предположительный хотфикс: добавить после y = e.screenY;
… if(this.fxVersion >= 99) { var zoom = e.view.browsingContext.fullZoom; x *= zoom; y *= zoom; }
надписи в СВ , типа ini неровные
Это, надеюсь, сугубо в твоих стилях надо поискать почему неровные.
Отсутствует
Dumby
Скрипт подрихтовал,норм. Кнопки в СВ в скине есть Tab и мешало ...сделал так
.tab-middle.box-inherit > .tab-text { padding: 1px 6px 2px 8px!important; } [data-l10n-id="editor-settingsAccelkey"]{margin-top: 10px !important;} #accelkey {margin-left: 12px !important; margin-top: 10px !important;}
Отредактировано ВВП (02-05-2022 14:58:35)
Отсутствует
Custom Buttons 0.0.7.0.0.25, paxmod и bootstrap в zip-папке.
И, код запускатора с поправкой на это.
// (async xp => { var imprt, ids = [ "custombuttons@xsms.org", ]; if (Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).inSafeMode) return; if (Cr.NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) var {XPIInternal} = (imprt = url => Cu.import(url, {}))(xp); else { // Fx 101+ var g = Cu.getGlobalForObject(Cu), te = new g.TextEncoder(); var imp = g.ChromeUtils.import, {XPIInternal} = imp(xp); var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); var rph = ios.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler); imprt = (url, id) => { var subst = te.encode(id).join(""); rph.setSubstitution(subst, ios.newURI(url)); return imp(`resource://${subst}/`); } } var load = async (file, id) => { var rootURI = XPIInternal.getURIForResourceInFile(file, ""); imprt(rootURI.resolve("startup.jsm"), id).start(rootURI); } var proto = XPIInternal.BootstrapScope.prototype; var func = proto._beforeCallBootstrapMethod; proto._beforeCallBootstrapMethod = () => { proto._beforeCallBootstrapMethod = func; for(var {id, loader, file} of XPIInternal.XPIStates.enabledAddons()) ids.includes(id) && !loader && load(file, id); } })("resource://gre/modules/addons/XPIProvider.jsm");
Отсутствует
Т. е. перед установкой …25-fx-bootstrap отредактировать config.js на №16402?
Сейчас, с custom_buttons-0.0.7.0.0.24-fx-bootstrap.xpi:
// https://forum.mozilla-russia.org/viewtopic.php?pid=798621#p798621 try {(nsvo => { var g = Cu.getGlobalForObject(nsvo), o = g.Object, {freeze} = o, NEW; o.freeze = obj => { if (Components.stack.caller.filename != "resource://gre/modules/AppConstants.jsm") return freeze(obj); obj.MOZ_REQUIRE_SIGNING = false; if ((NEW = "MOZ_ALLOW_ADDON_SIDELOAD" in obj)) lockPref("extensions.experiments.enabled", true); else obj.MOZ_ALLOW_LEGACY_EXTENSIONS = true, lockPref("extensions.legacy.enabled", true); return (o.freeze = freeze)(obj); } lockPref("xpinstall.signatures.required", false); lockPref("extensions.langpacks.signatures.required", false); nsvo = Cu.import("resource://gre/modules/addons/XPIInstall.jsm", {}); var shouldVerify = nsvo.shouldVerifySignedState; if (shouldVerify.length == 1) nsvo.shouldVerifySignedState = addon => !addon.id && shouldVerify(addon); else { var {Services} = g.ChromeUtils.import("resource://gre/modules/Services.jsm"); var subst = "pkg-proto-patch-tmp-script"; var rph = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler); var func = async proto => Object.assign(proto, eval( `({${proto.verifySignedState}})`.replace("(!", "(addonId || !") )); var code = encodeURIComponent(`(${func})(Package.prototype);`); rph.setSubstitution(subst, Services.io.newURI("data:," + code)); Services.scriptloader.loadSubScript(`resource://${subst}/`, nsvo); rph.setSubstitution(subst, null); } if (NEW) nsvo.XPIDatabase.isDisabledLegacy = () => false; })( "permitCPOWsInScope" in Cu ? Cu.import("resource://gre/modules/WebRequestCommon.jsm", {}) : Cu );} catch(ex) {Cu.reportError(ex);} // bootstrap-loader.js https://forum.mozilla-russia.org/viewtopic.php?pid=795196#p795196 try {(ios => { var subst = "bootstrap-loader-config-script"; ios.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler).setSubstitution(subst, ios.newURI(String.raw` data:,/%2A%2A%2A RDFDataSource.jsm %2A%2A%2A/%0A%0Aconst NS_XML = "http://www.w3.org/XML/1998/namespace";%0Aconst NS_XMLNS = "http://www.w3.org/2000/xmlns/";%0Aconst NS_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns%23";%0Aconst NS_NC = "http://home.netscape.com/NC-rdf%23";%0A%0Afunction isElement(obj) {%0A return Element.isInstance(obj);%0A}%0Afunction isText(obj) {%0A return obj && typeof obj == "object" && ChromeUtils.getClassName(obj) == "Text";%0A}%0A%0A/%2A%2A%0A %2A Returns either an rdf namespaced attribute or an un-namespaced attribute%0A %2A value. Returns null if neither exists,%0A %2A/%0Afunction getRDFAttribute(element, name) {%0A if (element.hasAttributeNS(NS_RDF, name))%0A return element.getAttributeNS(NS_RDF, name);%0A if (element.hasAttribute(name))%0A return element.getAttribute(name);%0A return undefined;%0A}%0A%0A/%2A%2A%0A %2A Represents an assertion in the datasource%0A %2A/%0Aclass RDFAssertion {%0A constructor(subject, predicate, object) {%0A // The subject on this assertion, an RDFSubject%0A this._subject = subject;%0A // The predicate, a string%0A this._predicate = predicate;%0A // The object, an RDFNode%0A this._object = object;%0A // The datasource this assertion exists in%0A this._ds = this._subject._ds;%0A // Marks that _DOMnode is the subject's element%0A this._isSubjectElement = false;%0A // The DOM node that represents this assertion. Could be a property element,%0A // a property attribute or the subject's element for rdf:type%0A this._DOMNode = null;%0A }%0A%0A getPredicate() {%0A return this._predicate;%0A }%0A%0A getObject() {%0A return this._object;%0A }%0A}%0A%0Aclass RDFNode {%0A equals(rdfnode) {%0A return (rdfnode.constructor === this.constructor &&%0A rdfnode._value == this._value);%0A }%0A}%0A%0A/%2A%2A%0A %2A A simple literal value%0A %2A/%0Aclass RDFLiteral extends RDFNode {%0A constructor(value) {%0A super();%0A this._value = value;%0A }%0A%0A getValue() {%0A return this._value;%0A }%0A}%0A%0A/%2A%2A%0A %2A This is an RDF node that can be a subject so a resource or a blank node%0A %2A/%0Aclass RDFSubject extends RDFNode {%0A constructor(ds) {%0A super();%0A // A lookup of the assertions with this as the subject. Keyed on predicate%0A this._assertions = {};%0A // A lookup of the assertions with this as the object. Keyed on predicate%0A this._backwards = {};%0A // The datasource this subject belongs to%0A this._ds = ds;%0A // The DOM elements in the document that represent this subject. Array of Element%0A this._elements = [];%0A }%0A%0A /%2A%2A%0A %2A Parses the given Element from the DOM document%0A %2A/%0A /%2A eslint-disable complexity %2A/%0A _parseElement(element) {%0A this._elements.push(element);%0A%0A // There might be an inferred rdf:type assertion in the element name%0A if (element.namespaceURI != NS_RDF ||%0A element.localName != "Description") {%0A var assertion = new RDFAssertion(this, RDF_R("type"),%0A this._ds.getResource(element.namespaceURI + element.localName));%0A assertion._DOMnode = element;%0A assertion._isSubjectElement = true;%0A this._addAssertion(assertion);%0A }%0A%0A // Certain attributes can be literal properties%0A for (let attr of element.attributes) {%0A if (attr.namespaceURI == NS_XML || attr.namespaceURI == NS_XMLNS ||%0A attr.nodeName == "xmlns")%0A continue;%0A if ((attr.namespaceURI == NS_RDF || !attr.namespaceURI) &&%0A (["nodeID", "about", "resource", "ID", "parseType"].includes(attr.localName)))%0A continue;%0A var object = null;%0A if (attr.namespaceURI == NS_RDF) {%0A if (attr.localName == "type")%0A object = this._ds.getResource(attr.nodeValue);%0A }%0A if (!object)%0A object = new RDFLiteral(attr.nodeValue);%0A assertion = new RDFAssertion(this, attr.namespaceURI + attr.localName, object);%0A assertion._DOMnode = attr;%0A this._addAssertion(assertion);%0A }%0A%0A var child = element.firstChild;%0A element.listCounter = 1;%0A while (child) {%0A if (isElement(child)) {%0A object = null;%0A var predicate = child.namespaceURI + child.localName;%0A if (child.namespaceURI == NS_RDF) {%0A if (child.localName == "li") {%0A predicate = RDF_R(%60_%24{element.listCounter}%60);%0A element.listCounter++;%0A }%0A }%0A%0A // Check for and bail out on unknown attributes on the property element%0A for (let attr of child.attributes) {%0A // Ignore XML namespaced attributes%0A if (attr.namespaceURI == NS_XML)%0A continue;%0A // These are reserved by XML for future use%0A if (attr.localName.substring(0, 3).toLowerCase() == "xml")%0A continue;%0A // We can handle these RDF attributes%0A if ((!attr.namespaceURI || attr.namespaceURI == NS_RDF) &&%0A ["resource", "nodeID"].includes(attr.localName))%0A continue;%0A // This is a special attribute we handle for compatibility with Mozilla RDF%0A if (attr.namespaceURI == NS_NC &&%0A attr.localName == "parseType")%0A continue;%0A }%0A%0A var parseType = child.getAttributeNS(NS_NC, "parseType");%0A%0A var resource = getRDFAttribute(child, "resource");%0A var nodeID = getRDFAttribute(child, "nodeID");%0A%0A if (resource !== undefined) {%0A var base = Services.io.newURI(element.baseURI);%0A object = this._ds.getResource(base.resolve(resource));%0A } else if (nodeID !== undefined) {%0A object = this._ds.getBlankNode(nodeID);%0A } else {%0A var hasText = false;%0A var childElement = null;%0A var subchild = child.firstChild;%0A while (subchild) {%0A if (isText(subchild) && /\S/.test(subchild.nodeValue)) {%0A hasText = true;%0A } else if (isElement(subchild)) {%0A childElement = subchild;%0A }%0A subchild = subchild.nextSibling;%0A }%0A%0A if (childElement) {%0A object = this._ds._getSubjectForElement(childElement);%0A object._parseElement(childElement);%0A } else%0A object = new RDFLiteral(child.textContent);%0A }%0A%0A assertion = new RDFAssertion(this, predicate, object);%0A this._addAssertion(assertion);%0A assertion._DOMnode = child;%0A }%0A child = child.nextSibling;%0A }%0A }%0A /%2A eslint-enable complexity %2A/%0A%0A /%2A%2A%0A %2A Adds a new assertion to the internal hashes. Should be called for every%0A %2A new assertion parsed or created programmatically.%0A %2A/%0A _addAssertion(assertion) {%0A var predicate = assertion.getPredicate();%0A if (predicate in this._assertions)%0A this._assertions[predicate].push(assertion);%0A else%0A this._assertions[predicate] = [ assertion ];%0A%0A var object = assertion.getObject();%0A if (object instanceof RDFSubject) {%0A // Create reverse assertion%0A if (predicate in object._backwards)%0A object._backwards[predicate].push(assertion);%0A else%0A object._backwards[predicate] = [ assertion ];%0A }%0A }%0A%0A /%2A%2A%0A %2A Returns all objects in assertions with this subject and the given predicate.%0A %2A/%0A getObjects(predicate) {%0A if (predicate in this._assertions)%0A return Array.from(this._assertions[predicate],%0A i => i.getObject());%0A%0A return [];%0A }%0A%0A /%2A%2A%0A %2A Retrieves the first property value for the given predicate.%0A %2A/%0A getProperty(predicate) {%0A if (predicate in this._assertions)%0A return this._assertions[predicate][0].getObject();%0A return null;%0A }%0A}%0A%0A/%2A%2A%0A %2A Creates a new RDFResource for the datasource. Private.%0A %2A/%0Aclass RDFResource extends RDFSubject {%0A constructor(ds, uri) {%0A super(ds);%0A // This is the uri that the resource represents.%0A this._uri = uri;%0A }%0A}%0A%0A/%2A%2A%0A %2A Creates a new blank node. Private.%0A %2A/%0Aclass RDFBlankNode extends RDFSubject {%0A constructor(ds, nodeID) {%0A super(ds);%0A // The nodeID of this node. May be null if there is no ID.%0A this._nodeID = nodeID;%0A }%0A%0A /%2A%2A%0A %2A Sets attributes on the DOM element to mark it as representing this node%0A %2A/%0A _applyToElement(element) {%0A if (!this._nodeID)%0A return;%0A if (USE_RDFNS_ATTR) {%0A var prefix = this._ds._resolvePrefix(element, RDF_R("nodeID"));%0A element.setAttributeNS(prefix.namespaceURI, prefix.qname, this._nodeID);%0A } else {%0A element.setAttribute("nodeID", this._nodeID);%0A }%0A }%0A%0A /%2A%2A%0A %2A Creates a new Element in the document for holding assertions about this%0A %2A subject. The URI controls what tagname to use.%0A %2A/%0A _createNewElement(uri) {%0A // If there are already nodes representing this in the document then we need%0A // a nodeID to match them%0A if (!this._nodeID && this._elements.length > 0) {%0A this._ds._createNodeID(this);%0A for (let element of this._elements)%0A this._applyToElement(element);%0A }%0A%0A return super._createNewElement.call(uri);%0A }%0A%0A /%2A%2A%0A %2A Adds a reference to this node to the given property Element.%0A %2A/%0A _addReferenceToElement(element) {%0A if (this._elements.length > 0 && !this._nodeID) {%0A // In document elsewhere already%0A // Create a node ID and update the other nodes referencing%0A this._ds._createNodeID(this);%0A for (let element of this._elements)%0A this._applyToElement(element);%0A }%0A%0A if (this._nodeID) {%0A if (USE_RDFNS_ATTR) {%0A let prefix = this._ds._resolvePrefix(element, RDF_R("nodeID"));%0A element.setAttributeNS(prefix.namespaceURI, prefix.qname, this._nodeID);%0A } else {%0A element.setAttribute("nodeID", this._nodeID);%0A }%0A } else {%0A // Add the empty blank node, this is generally right since further%0A // assertions will be added to fill this out%0A var newelement = this._ds._addElement(element, RDF_R("Description"));%0A newelement.listCounter = 1;%0A this._elements.push(newelement);%0A }%0A }%0A%0A /%2A%2A%0A %2A Removes any reference to this node from the given property Element.%0A %2A/%0A _removeReferenceFromElement(element) {%0A if (element.hasAttributeNS(NS_RDF, "nodeID"))%0A element.removeAttributeNS(NS_RDF, "nodeID");%0A if (element.hasAttribute("nodeID"))%0A element.removeAttribute("nodeID");%0A }%0A%0A getNodeID() {%0A return this._nodeID;%0A }%0A}%0A%0A/%2A%2A%0A %2A Creates a new RDFDataSource from the given document. The document will be%0A %2A changed as assertions are added and removed to the RDF. Pass a null document%0A %2A to start with an empty graph.%0A %2A/%0Aclass RDFDataSource {%0A constructor(document) {%0A // All known resources, indexed on URI%0A this._resources = {};%0A // All blank nodes%0A this._allBlankNodes = [];%0A%0A // The underlying DOM document for this datasource%0A this._document = document;%0A this._parseDocument();%0A }%0A%0A static loadFromString(text) {%0A let parser = new DOMParser();%0A let document = parser.parseFromString(text, "application/xml");%0A%0A return new this(document);%0A }%0A%0A /%2A%2A%0A %2A Returns an rdf subject for the given DOM Element. If the subject has not%0A %2A been seen before a new one is created.%0A %2A/%0A _getSubjectForElement(element) {%0A var about = getRDFAttribute(element, "about");%0A%0A if (about !== undefined) {%0A let base = Services.io.newURI(element.baseURI);%0A return this.getResource(base.resolve(about));%0A }%0A return this.getBlankNode(null);%0A }%0A%0A /%2A%2A%0A %2A Parses the document for subjects at the top level.%0A %2A/%0A _parseDocument() {%0A var domnode = this._document.documentElement.firstChild;%0A while (domnode) {%0A if (isElement(domnode)) {%0A var subject = this._getSubjectForElement(domnode);%0A subject._parseElement(domnode);%0A }%0A domnode = domnode.nextSibling;%0A }%0A }%0A%0A /%2A%2A%0A %2A Gets a blank node. nodeID may be null and if so a new blank node is created.%0A %2A If a nodeID is given then the blank node with that ID is returned or created.%0A %2A/%0A getBlankNode(nodeID) {%0A var rdfnode = new RDFBlankNode(this, nodeID);%0A this._allBlankNodes.push(rdfnode);%0A return rdfnode;%0A }%0A%0A /%2A%2A%0A %2A Gets the resource for the URI. The resource is created if it has not been%0A %2A used already.%0A %2A/%0A getResource(uri) {%0A if (uri in this._resources)%0A return this._resources[uri];%0A%0A var resource = new RDFResource(this, uri);%0A this._resources[uri] = resource;%0A return resource;%0A }%0A}%0A%0A%0A/%2A%2A%2A RDFManifestConverter.jsm %2A%2A%2A/%0A%0Aconst RDFURI_INSTALL_MANIFEST_ROOT = "urn:mozilla:install-manifest";%0A%0Afunction EM_R(aProperty) {%0A return %60http://www.mozilla.org/2004/em-rdf%23%24{aProperty}%60;%0A}%0A%0Afunction getValue(literal) {%0A return literal && literal.getValue();%0A}%0A%0Afunction getProperty(resource, property) {%0A return getValue(resource.getProperty(EM_R(property)));%0A}%0A%0Aclass Manifest {%0A constructor(ds) {%0A this.ds = ds;%0A }%0A%0A static loadFromString(text) {%0A return new this(RDFDataSource.loadFromString(text));%0A }%0A}%0A%0Aclass InstallRDF extends Manifest {%0A _readProps(source, obj, props) {%0A for (let prop of props) {%0A let val = getProperty(source, prop);%0A if (val != null) {%0A obj[prop] = val;%0A }%0A }%0A }%0A%0A _readArrayProp(source, obj, prop, target, decode = getValue) {%0A let result = Array.from(source.getObjects(EM_R(prop)),%0A target => decode(target));%0A if (result.length) {%0A obj[target] = result;%0A }%0A }%0A%0A _readArrayProps(source, obj, props, decode = getValue) {%0A for (let [prop, target] of Object.entries(props)) {%0A this._readArrayProp(source, obj, prop, target, decode);%0A }%0A }%0A%0A _readLocaleStrings(source, obj) {%0A this._readProps(source, obj, ["name", "description", "creator", "homepageURL"]);%0A this._readArrayProps(source, obj, {%0A locale: "locales",%0A developer: "developers",%0A translator: "translators",%0A contributor: "contributors",%0A });%0A }%0A%0A decode() {%0A let root = this.ds.getResource(RDFURI_INSTALL_MANIFEST_ROOT);%0A let result = {};%0A%0A let props = ["id", "version", "type", "updateURL", "optionsURL",%0A "optionsType", "aboutURL", "iconURL",%0A "bootstrap", "unpack", "strictCompatibility"];%0A this._readProps(root, result, props);%0A%0A let decodeTargetApplication = source => {%0A let app = {};%0A this._readProps(source, app, ["id", "minVersion", "maxVersion"]);%0A return app;%0A };%0A%0A let decodeLocale = source => {%0A let localized = {};%0A this._readLocaleStrings(source, localized);%0A return localized;%0A };%0A%0A this._readLocaleStrings(root, result);%0A%0A this._readArrayProps(root, result, {"targetPlatform": "targetPlatforms"});%0A this._readArrayProps(root, result, {"targetApplication": "targetApplications"},%0A decodeTargetApplication);%0A this._readArrayProps(root, result, {"localized": "localized"},%0A decodeLocale);%0A this._readArrayProps(root, result, {"dependency": "dependencies"},%0A source => getProperty(source, "id"));%0A%0A return result;%0A }%0A}%0A%0A%0A/%2A%2A%2A BootstrapLoader.jsm %2A%2A%2A/%0A%0Avar {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");%0Avar {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");%0A%0AXPCOMUtils.defineLazyModuleGetters(this, {%0A ConsoleAPI: "resource://gre/modules/Console.jsm",%0A Blocklist: "resource://gre/modules/Blocklist.jsm",%0A XPIProvider: "resource://gre/modules/addons/XPIProvider.jsm"%0A});%0A%0Avar OPTIONS_TYPE_DIALOG = 1;%0A%0AServices.obs.addObserver(doc => {%0A if (doc.location.protocol + doc.location.pathname === 'about:addons' ||%0A doc.location.protocol + doc.location.pathname === 'chrome:/content/extensions/aboutaddons.html') {%0A const win = doc.defaultView;%0A let handleEvent_orig = win.customElements.get('addon-card').prototype.handleEvent;%0A win.customElements.get('addon-card').prototype.handleEvent = function (e) {%0A if (e.type === 'click' &&%0A e.target.getAttribute('action') === 'preferences' &&%0A this.addon.optionsType == OPTIONS_TYPE_DIALOG) {%0A var windows = Services.wm.getEnumerator(null);%0A while (windows.hasMoreElements()) {%0A var win2 = windows.getNext();%0A if (win2.closed) {%0A continue;%0A }%0A if (win2.document.documentURI == this.addon.optionsURL) {%0A win2.focus();%0A return;%0A }%0A }%0A var features = 'chrome,titlebar,toolbar,centerscreen';%0A var instantApply = Services.prefs.getBoolPref('browser.preferences.instantApply');%0A features += instantApply ? ',dialog=no' : '';%0A win.docShell.rootTreeItem.domWindow.openDialog(this.addon.optionsURL, this.addon.id, features); %0A } else {%0A handleEvent_orig.apply(this, arguments);%0A }%0A }%0A let update_orig = win.customElements.get('addon-options').prototype.update;%0A win.customElements.get('addon-options').prototype.update = function (card, addon) {%0A update_orig.apply(this, arguments);%0A if (addon.optionsType == OPTIONS_TYPE_DIALOG)%0A this.querySelector('panel-item[data-l10n-id="preferences-addon-button"]').hidden = false;%0A }%0A }%0A}, 'chrome-document-loaded');%0A%0Aconst {AddonManager} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");%0Aconst {XPIDatabase, AddonInternal} = ChromeUtils.import("resource://gre/modules/addons/XPIDatabase.jsm");%0A%0ACu.import("resource://gre/modules/addons/XPIDatabase.jsm", {}).defineAddonWrapperProperty("optionsType", function optionsType() {%0A if (!this.isActive) {%0A return null;%0A }%0A%0A let addon = this.__AddonInternal__;%0A let hasOptionsURL = !!this.optionsURL;%0A%0A if (addon.optionsType) {%0A switch (parseInt(addon.optionsType, 10)) {%0A case OPTIONS_TYPE_DIALOG:%0A case AddonManager.OPTIONS_TYPE_TAB:%0A case AddonManager.OPTIONS_TYPE_INLINE_BROWSER:%0A return hasOptionsURL ? addon.optionsType : null;%0A }%0A return null;%0A }%0A%0A return null;%0A});%0A%0AXPIDatabase.isDisabledLegacy = () => false;%0A%0AXPCOMUtils.defineLazyGetter(this, 'BOOTSTRAP_REASONS', () => XPIProvider.BOOTSTRAP_REASONS);%0A%0Aconst {Log} = ChromeUtils.import('resource://gre/modules/Log.jsm');%0Avar logger = Log.repository.getLogger('addons.bootstrap');%0A%0A/%2A%2A%0A %2A Valid IDs fit this pattern.%0A %2A/%0Avar gIDTest = /^(\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}|[a-z0-9-\._]%2A\@[a-z0-9-\._]+)$/i;%0A%0A// Properties that exist in the install manifest%0Aconst PROP_METADATA = ['id', 'version', 'type', 'internalName', 'updateURL',%0A 'optionsURL', 'optionsType', 'aboutURL', 'iconURL'];%0Aconst PROP_LOCALE_SINGLE = ['name', 'description', 'creator', 'homepageURL'];%0Aconst PROP_LOCALE_MULTI = ['developers', 'translators', 'contributors'];%0A%0A// Map new string type identifiers to old style nsIUpdateItem types.%0A// Retired values:%0A// 32 = multipackage xpi file%0A// 8 = locale%0A// 256 = apiextension%0A// 128 = experiment%0A// theme = 4%0Aconst TYPES = {%0A extension: 2,%0A dictionary: 64,%0A};%0A%0Aconst COMPATIBLE_BY_DEFAULT_TYPES = {%0A extension: true,%0A dictionary: true,%0A};%0A%0Aconst hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty);%0A%0Afunction isXPI(filename) {%0A let ext = filename.slice(-4).toLowerCase();%0A return ext === '.xpi' || ext === '.zip';%0A}%0A%0A/%2A%2A%0A %2A Gets an nsIURI for a file within another file, either a directory or an XPI%0A %2A file. If aFile is a directory then this will return a file: URI, if it is an%0A %2A XPI file then it will return a jar: URI.%0A %2A%0A %2A @param {nsIFile} aFile%0A %2A The file containing the resources, must be either a directory or an%0A %2A XPI file%0A %2A @param {string} aPath%0A %2A The path to find the resource at, '/' separated. If aPath is empty%0A %2A then the uri to the root of the contained files will be returned%0A %2A @returns {nsIURI}%0A %2A An nsIURI pointing at the resource%0A %2A/%0Afunction getURIForResourceInFile(aFile, aPath) {%0A if (!isXPI(aFile.leafName)) {%0A let resource = aFile.clone();%0A if (aPath)%0A aPath.split('/').forEach(part => resource.append(part));%0A%0A return Services.io.newFileURI(resource);%0A }%0A%0A return buildJarURI(aFile, aPath);%0A}%0A%0A/%2A%2A%0A %2A Creates a jar: URI for a file inside a ZIP file.%0A %2A%0A %2A @param {nsIFile} aJarfile%0A %2A The ZIP file as an nsIFile%0A %2A @param {string} aPath%0A %2A The path inside the ZIP file%0A %2A @returns {nsIURI}%0A %2A An nsIURI for the file%0A %2A/%0Afunction buildJarURI(aJarfile, aPath) {%0A let uri = Services.io.newFileURI(aJarfile);%0A uri = 'jar:' + uri.spec + '!/' + aPath;%0A return Services.io.newURI(uri);%0A}%0A%0Avar BootstrapLoader = {%0A name: 'bootstrap',%0A manifestFile: 'install.rdf',%0A async loadManifest(pkg) {%0A /%2A%2A%0A %2A Reads locale properties from either the main install manifest root or%0A %2A an em:localized section in the install manifest.%0A %2A%0A %2A @param {Object} aSource%0A %2A The resource to read the properties from.%0A %2A @param {boolean} isDefault%0A %2A True if the locale is to be read from the main install manifest%0A %2A root%0A %2A @param {string[]} aSeenLocales%0A %2A An array of locale names already seen for this install manifest.%0A %2A Any locale names seen as a part of this function will be added to%0A %2A this array%0A %2A @returns {Object}%0A %2A an object containing the locale properties%0A %2A/%0A function readLocale(aSource, isDefault, aSeenLocales) {%0A let locale = {};%0A if (!isDefault) {%0A locale.locales = [];%0A for (let localeName of aSource.locales || []) {%0A if (!localeName) {%0A logger.warn('Ignoring empty locale in localized properties');%0A continue;%0A }%0A if (aSeenLocales.includes(localeName)) {%0A logger.warn('Ignoring duplicate locale in localized properties');%0A continue;%0A }%0A aSeenLocales.push(localeName);%0A locale.locales.push(localeName);%0A }%0A%0A if (locale.locales.length == 0) {%0A logger.warn('Ignoring localized properties with no listed locales');%0A return null;%0A }%0A }%0A%0A for (let prop of [...PROP_LOCALE_SINGLE, ...PROP_LOCALE_MULTI]) {%0A if (hasOwnProperty(aSource, prop)) {%0A locale[prop] = aSource[prop];%0A }%0A }%0A%0A return locale;%0A }%0A%0A let manifestData = await pkg.readString('install.rdf');%0A let manifest = InstallRDF.loadFromString(manifestData).decode();%0A%0A let addon = new AddonInternal();%0A for (let prop of PROP_METADATA) {%0A if (hasOwnProperty(manifest, prop)) {%0A addon[prop] = manifest[prop];%0A }%0A }%0A%0A if (!addon.type) {%0A addon.type = 'extension';%0A } else {%0A let type = addon.type;%0A addon.type = null;%0A for (let name in TYPES) {%0A if (TYPES[name] == type) {%0A addon.type = name;%0A break;%0A }%0A }%0A }%0A%0A if (!(addon.type in TYPES))%0A throw new Error('Install manifest specifies unknown type: ' + addon.type);%0A%0A if (!addon.id)%0A throw new Error('No ID in install manifest');%0A if (!gIDTest.test(addon.id))%0A throw new Error('Illegal add-on ID ' + addon.id);%0A if (!addon.version)%0A throw new Error('No version in install manifest');%0A%0A addon.strictCompatibility = (!(addon.type in COMPATIBLE_BY_DEFAULT_TYPES) ||%0A manifest.strictCompatibility == 'true');%0A%0A // Only read these properties for extensions.%0A if (addon.type == 'extension') {%0A if (manifest.bootstrap != 'true') {%0A throw new Error('Non-restartless extensions no longer supported');%0A }%0A%0A if (addon.optionsType &&%0A addon.optionsType != OPTIONS_TYPE_DIALOG &&%0A addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE_BROWSER &&%0A addon.optionsType != AddonManager.OPTIONS_TYPE_TAB) {%0A throw new Error('Install manifest specifies unknown optionsType: ' + addon.optionsType);%0A }%0A%0A if (addon.optionsType)%0A addon.optionsType = parseInt(addon.optionsType);%0A }%0A%0A addon.defaultLocale = readLocale(manifest, true);%0A%0A let seenLocales = [];%0A addon.locales = [];%0A for (let localeData of manifest.localized || []) {%0A let locale = readLocale(localeData, false, seenLocales);%0A if (locale)%0A addon.locales.push(locale);%0A }%0A%0A let dependencies = new Set(manifest.dependencies);%0A addon.dependencies = Object.freeze(Array.from(dependencies));%0A%0A let seenApplications = [];%0A addon.targetApplications = [];%0A for (let targetApp of manifest.targetApplications || []) {%0A if (!targetApp.id || !targetApp.minVersion ||%0A !targetApp.maxVersion) {%0A logger.warn('Ignoring invalid targetApplication entry in install manifest');%0A continue;%0A }%0A if (seenApplications.includes(targetApp.id)) {%0A logger.warn('Ignoring duplicate targetApplication entry for ' + targetApp.id +%0A ' in install manifest');%0A continue;%0A }%0A seenApplications.push(targetApp.id);%0A addon.targetApplications.push(targetApp);%0A }%0A%0A // Note that we don't need to check for duplicate targetPlatform entries since%0A // the RDF service coalesces them for us.%0A addon.targetPlatforms = [];%0A for (let targetPlatform of manifest.targetPlatforms || []) {%0A let platform = {%0A os: null,%0A abi: null,%0A };%0A%0A let pos = targetPlatform.indexOf('_');%0A if (pos != -1) {%0A platform.os = targetPlatform.substring(0, pos);%0A platform.abi = targetPlatform.substring(pos + 1);%0A } else {%0A platform.os = targetPlatform;%0A }%0A%0A addon.targetPlatforms.push(platform);%0A }%0A%0A addon.userDisabled = false;%0A addon.softDisabled = addon.blocklistState == Blocklist.STATE_SOFTBLOCKED;%0A addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;%0A%0A addon.userPermissions = null;%0A%0A addon.icons = {};%0A if (await pkg.hasResource('icon.png')) {%0A addon.icons[32] = 'icon.png';%0A addon.icons[48] = 'icon.png';%0A }%0A%0A if (await pkg.hasResource('icon64.png')) {%0A addon.icons[64] = 'icon64.png';%0A }%0A%0A return addon;%0A },%0A%0A loadScope(addon) {%0A let file = addon.file || addon._sourceBundle;%0A let uri = getURIForResourceInFile(file, 'bootstrap.js').spec;%0A let principal = Services.scriptSecurityManager.getSystemPrincipal();%0A%0A let sandbox = new Cu.Sandbox(principal, {%0A sandboxName: uri,%0A addonId: addon.id,%0A wantGlobalProperties: ['ChromeUtils'],%0A metadata: { addonID: addon.id, URI: uri },%0A });%0A%0A try {%0A Object.assign(sandbox, BOOTSTRAP_REASONS);%0A%0A XPCOMUtils.defineLazyGetter(sandbox, 'console', () =>%0A new ConsoleAPI({ consoleID: %60addon/%24{addon.id}%60 }));%0A%0A Services.scriptloader.loadSubScript(uri, sandbox);%0A } catch (e) {%0A logger.warn(%60Error loading bootstrap.js for %24{addon.id}%60, e);%0A }%0A%0A function findMethod(name) {%0A if (sandbox[name]) {%0A return sandbox[name];%0A }%0A%0A try {%0A let method = Cu.evalInSandbox(name, sandbox);%0A return method;%0A } catch (err) { }%0A%0A return () => {%0A logger.warn(%60Add-on %24{addon.id} is missing bootstrap method %24{name}%60);%0A };%0A }%0A%0A let install = findMethod('install');%0A let uninstall = findMethod('uninstall');%0A let startup = findMethod('startup');%0A let shutdown = findMethod('shutdown');%0A%0A return {%0A install(...args) {%0A install(...args);%0A // Forget any cached files we might've had from this extension.%0A Services.obs.notifyObservers(null, 'startupcache-invalidate');%0A },%0A%0A uninstall(...args) {%0A uninstall(...args);%0A // Forget any cached files we might've had from this extension.%0A Services.obs.notifyObservers(null, 'startupcache-invalidate');%0A },%0A%0A startup(...args) {%0A if (addon.type == 'extension') {%0A logger.debug(%60Registering manifest for %24{file.path}\n%60);%0A Components.manager.addBootstrappedManifestLocation(file);%0A }%0A return startup(...args);%0A },%0A%0A shutdown(data, reason) {%0A try {%0A return shutdown(data, reason);%0A } catch (err) {%0A throw err;%0A } finally {%0A if (reason != BOOTSTRAP_REASONS.APP_SHUTDOWN) {%0A logger.debug(%60Removing manifest for %24{file.path}\n%60);%0A Components.manager.removeBootstrappedManifestLocation(file);%0A }%0A }%0A },%0A };%0A },%0A};%0A%0AAddonManager.addExternalExtensionLoader(BootstrapLoader);%0A%0Aif (AddonManager.isReady) {%0A AddonManager.getAllAddons().then(addons => {%0A addons.forEach(addon => {%0A if (addon.type == 'extension' && !addon.isWebExtension && !addon.userDisabled) {%0A addon.reload();%0A };%0A });%0A });%0A}%0A%0AObject.defineProperty(%0A AddonInternal.prototype,%0A "providesUpdatesSecurely",%0A {enumerable: true, value: true}%0A);%0A `.trim())); Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader) .loadSubScript("resource://" + subst, new Cu.Sandbox(Cu.getObjectPrincipal(this), {wantGlobalProperties: ["ChromeUtils", "DOMParser", "Element", "fetch"]})); })(Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService));} catch(ex) {Cu.reportError(ex);} // https://forum.mozilla-russia.org/viewtopic.php?pid=780458#p780458 try {({ ids: [ "custombuttons@xsms.org", ], init(xrt) { if (xrt.inSafeMode) return; Cu.import("resource://gre/modules/addons/XPIProvider.jsm", this); var load = async file => { var rootURI = this.XPIInternal.getURIForResourceInFile(file, ""); Cu.import(rootURI.resolve("startup.jsm"), {}).start(rootURI); } var proto = this.XPIInternal.BootstrapScope.prototype; var func = proto._beforeCallBootstrapMethod; proto._beforeCallBootstrapMethod = () => { proto._beforeCallBootstrapMethod = func; for(var addon of this.XPIInternal.XPIStates.enabledAddons()) this.ids.includes(addon.id) && !addon.loader && load(addon.file); } } }).init(Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime));} catch(ex) {Cu.reportError(ex);}
Отсутствует
Т. е. перед установкой …25-fx-bootstrap отредактировать config.js на №16402?
Сейчас, с custom_buttons-0.0.7.0.0.24-fx-bootstrap.xpi
Не-не.
Если используется bootstrap, то запускатор не нужен,
bootstrap-loader это уже, в том числе, и запускатор.
Но можно и отредактировать, на всякий случай.
А затем закомментировать код запускатора как неиспользуемый.
В приведённом config.js — это третий фрагмент.
Первый — антиподписячий, второй — bootstrap-loader,
а третий — он, вот его и заменить.
Отсутствует
Как избавиться от красного ?
Ну, например
@-moz-document regexp("moz-extension://\.{36}/options.html\.*") { div#firefox-bug.row.alert.alert-warning { display: none !important; } }
Отсутствует
Если используется bootstrap, то запускатор не нужен,
bootstrap-loader это уже, в том числе, и запускатор.
А как этот bootstrap устанавливать, у меня при попытке на [firefox]100 его воткнуть, пишет, что повреждено?
Отсутствует
А как этот bootstrap устанавливать
ну попробуйте такой вариант...
// https://forum.mozilla-russia.org/viewtopic.php?pid=798621#p798621 try {(nsvo => { var g = Cu.getGlobalForObject(nsvo), o = g.Object, {freeze} = o, NEW; o.freeze = obj => { if (Components.stack.caller.filename != "resource://gre/modules/AppConstants.jsm") return freeze(obj); obj.MOZ_REQUIRE_SIGNING = false; if ((NEW = "MOZ_ALLOW_ADDON_SIDELOAD" in obj)) lockPref("extensions.experiments.enabled", true); else obj.MOZ_ALLOW_LEGACY_EXTENSIONS = true, lockPref("extensions.legacy.enabled", true); return (o.freeze = freeze)(obj); } lockPref("xpinstall.signatures.required", false); lockPref("extensions.langpacks.signatures.required", false); nsvo = Cu.import("resource://gre/modules/addons/XPIInstall.jsm", {}); var shouldVerify = nsvo.shouldVerifySignedState; if (shouldVerify.length == 1) nsvo.shouldVerifySignedState = addon => !addon.id && shouldVerify(addon); else { var {Services} = g.ChromeUtils.import("resource://gre/modules/Services.jsm"); var subst = "pkg-proto-patch-tmp-script"; var rph = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler); var func = async proto => Object.assign(proto, eval( `({${proto.verifySignedState}})`.replace("(!", "(addonId || !") )); var code = encodeURIComponent(`(${func})(Package.prototype);`); rph.setSubstitution(subst, Services.io.newURI("data:," + code)); Services.scriptloader.loadSubScript(`resource://${subst}/`, nsvo); rph.setSubstitution(subst, null); } if (NEW) nsvo.XPIDatabase.isDisabledLegacy = () => false; })( "permitCPOWsInScope" in Cu ? Cu.import("resource://gre/modules/WebRequestCommon.jsm", {}) : Cu );} catch(ex) {Cu.reportError(ex);} // bootstrap-loader.js https://forum.mozilla-russia.org/viewtopic.php?pid=795196#p795196 try {(ios => { var subst = "bootstrap-loader-config-script"; ios.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler).setSubstitution(subst, ios.newURI(String.raw` data:,/%2A%2A%2A RDFDataSource.jsm %2A%2A%2A/%0A%0Aconst NS_XML = "http://www.w3.org/XML/1998/namespace";%0Aconst NS_XMLNS = "http://www.w3.org/2000/xmlns/";%0Aconst NS_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns%23";%0Aconst NS_NC = "http://home.netscape.com/NC-rdf%23";%0A%0Afunction isElement(obj) {%0A return Element.isInstance(obj);%0A}%0Afunction isText(obj) {%0A return obj && typeof obj == "object" && ChromeUtils.getClassName(obj) == "Text";%0A}%0A%0A/%2A%2A%0A %2A Returns either an rdf namespaced attribute or an un-namespaced attribute%0A %2A value. Returns null if neither exists,%0A %2A/%0Afunction getRDFAttribute(element, name) {%0A if (element.hasAttributeNS(NS_RDF, name))%0A return element.getAttributeNS(NS_RDF, name);%0A if (element.hasAttribute(name))%0A return element.getAttribute(name);%0A return undefined;%0A}%0A%0A/%2A%2A%0A %2A Represents an assertion in the datasource%0A %2A/%0Aclass RDFAssertion {%0A constructor(subject, predicate, object) {%0A // The subject on this assertion, an RDFSubject%0A this._subject = subject;%0A // The predicate, a string%0A this._predicate = predicate;%0A // The object, an RDFNode%0A this._object = object;%0A // The datasource this assertion exists in%0A this._ds = this._subject._ds;%0A // Marks that _DOMnode is the subject's element%0A this._isSubjectElement = false;%0A // The DOM node that represents this assertion. Could be a property element,%0A // a property attribute or the subject's element for rdf:type%0A this._DOMNode = null;%0A }%0A%0A getPredicate() {%0A return this._predicate;%0A }%0A%0A getObject() {%0A return this._object;%0A }%0A}%0A%0Aclass RDFNode {%0A equals(rdfnode) {%0A return (rdfnode.constructor === this.constructor &&%0A rdfnode._value == this._value);%0A }%0A}%0A%0A/%2A%2A%0A %2A A simple literal value%0A %2A/%0Aclass RDFLiteral extends RDFNode {%0A constructor(value) {%0A super();%0A this._value = value;%0A }%0A%0A getValue() {%0A return this._value;%0A }%0A}%0A%0A/%2A%2A%0A %2A This is an RDF node that can be a subject so a resource or a blank node%0A %2A/%0Aclass RDFSubject extends RDFNode {%0A constructor(ds) {%0A super();%0A // A lookup of the assertions with this as the subject. Keyed on predicate%0A this._assertions = {};%0A // A lookup of the assertions with this as the object. Keyed on predicate%0A this._backwards = {};%0A // The datasource this subject belongs to%0A this._ds = ds;%0A // The DOM elements in the document that represent this subject. Array of Element%0A this._elements = [];%0A }%0A%0A /%2A%2A%0A %2A Parses the given Element from the DOM document%0A %2A/%0A /%2A eslint-disable complexity %2A/%0A _parseElement(element) {%0A this._elements.push(element);%0A%0A // There might be an inferred rdf:type assertion in the element name%0A if (element.namespaceURI != NS_RDF ||%0A element.localName != "Description") {%0A var assertion = new RDFAssertion(this, RDF_R("type"),%0A this._ds.getResource(element.namespaceURI + element.localName));%0A assertion._DOMnode = element;%0A assertion._isSubjectElement = true;%0A this._addAssertion(assertion);%0A }%0A%0A // Certain attributes can be literal properties%0A for (let attr of element.attributes) {%0A if (attr.namespaceURI == NS_XML || attr.namespaceURI == NS_XMLNS ||%0A attr.nodeName == "xmlns")%0A continue;%0A if ((attr.namespaceURI == NS_RDF || !attr.namespaceURI) &&%0A (["nodeID", "about", "resource", "ID", "parseType"].includes(attr.localName)))%0A continue;%0A var object = null;%0A if (attr.namespaceURI == NS_RDF) {%0A if (attr.localName == "type")%0A object = this._ds.getResource(attr.nodeValue);%0A }%0A if (!object)%0A object = new RDFLiteral(attr.nodeValue);%0A assertion = new RDFAssertion(this, attr.namespaceURI + attr.localName, object);%0A assertion._DOMnode = attr;%0A this._addAssertion(assertion);%0A }%0A%0A var child = element.firstChild;%0A element.listCounter = 1;%0A while (child) {%0A if (isElement(child)) {%0A object = null;%0A var predicate = child.namespaceURI + child.localName;%0A if (child.namespaceURI == NS_RDF) {%0A if (child.localName == "li") {%0A predicate = RDF_R(%60_%24{element.listCounter}%60);%0A element.listCounter++;%0A }%0A }%0A%0A // Check for and bail out on unknown attributes on the property element%0A for (let attr of child.attributes) {%0A // Ignore XML namespaced attributes%0A if (attr.namespaceURI == NS_XML)%0A continue;%0A // These are reserved by XML for future use%0A if (attr.localName.substring(0, 3).toLowerCase() == "xml")%0A continue;%0A // We can handle these RDF attributes%0A if ((!attr.namespaceURI || attr.namespaceURI == NS_RDF) &&%0A ["resource", "nodeID"].includes(attr.localName))%0A continue;%0A // This is a special attribute we handle for compatibility with Mozilla RDF%0A if (attr.namespaceURI == NS_NC &&%0A attr.localName == "parseType")%0A continue;%0A }%0A%0A var parseType = child.getAttributeNS(NS_NC, "parseType");%0A%0A var resource = getRDFAttribute(child, "resource");%0A var nodeID = getRDFAttribute(child, "nodeID");%0A%0A if (resource !== undefined) {%0A var base = Services.io.newURI(element.baseURI);%0A object = this._ds.getResource(base.resolve(resource));%0A } else if (nodeID !== undefined) {%0A object = this._ds.getBlankNode(nodeID);%0A } else {%0A var hasText = false;%0A var childElement = null;%0A var subchild = child.firstChild;%0A while (subchild) {%0A if (isText(subchild) && /\S/.test(subchild.nodeValue)) {%0A hasText = true;%0A } else if (isElement(subchild)) {%0A childElement = subchild;%0A }%0A subchild = subchild.nextSibling;%0A }%0A%0A if (childElement) {%0A object = this._ds._getSubjectForElement(childElement);%0A object._parseElement(childElement);%0A } else%0A object = new RDFLiteral(child.textContent);%0A }%0A%0A assertion = new RDFAssertion(this, predicate, object);%0A this._addAssertion(assertion);%0A assertion._DOMnode = child;%0A }%0A child = child.nextSibling;%0A }%0A }%0A /%2A eslint-enable complexity %2A/%0A%0A /%2A%2A%0A %2A Adds a new assertion to the internal hashes. Should be called for every%0A %2A new assertion parsed or created programmatically.%0A %2A/%0A _addAssertion(assertion) {%0A var predicate = assertion.getPredicate();%0A if (predicate in this._assertions)%0A this._assertions[predicate].push(assertion);%0A else%0A this._assertions[predicate] = [ assertion ];%0A%0A var object = assertion.getObject();%0A if (object instanceof RDFSubject) {%0A // Create reverse assertion%0A if (predicate in object._backwards)%0A object._backwards[predicate].push(assertion);%0A else%0A object._backwards[predicate] = [ assertion ];%0A }%0A }%0A%0A /%2A%2A%0A %2A Returns all objects in assertions with this subject and the given predicate.%0A %2A/%0A getObjects(predicate) {%0A if (predicate in this._assertions)%0A return Array.from(this._assertions[predicate],%0A i => i.getObject());%0A%0A return [];%0A }%0A%0A /%2A%2A%0A %2A Retrieves the first property value for the given predicate.%0A %2A/%0A getProperty(predicate) {%0A if (predicate in this._assertions)%0A return this._assertions[predicate][0].getObject();%0A return null;%0A }%0A}%0A%0A/%2A%2A%0A %2A Creates a new RDFResource for the datasource. Private.%0A %2A/%0Aclass RDFResource extends RDFSubject {%0A constructor(ds, uri) {%0A super(ds);%0A // This is the uri that the resource represents.%0A this._uri = uri;%0A }%0A}%0A%0A/%2A%2A%0A %2A Creates a new blank node. Private.%0A %2A/%0Aclass RDFBlankNode extends RDFSubject {%0A constructor(ds, nodeID) {%0A super(ds);%0A // The nodeID of this node. May be null if there is no ID.%0A this._nodeID = nodeID;%0A }%0A%0A /%2A%2A%0A %2A Sets attributes on the DOM element to mark it as representing this node%0A %2A/%0A _applyToElement(element) {%0A if (!this._nodeID)%0A return;%0A if (USE_RDFNS_ATTR) {%0A var prefix = this._ds._resolvePrefix(element, RDF_R("nodeID"));%0A element.setAttributeNS(prefix.namespaceURI, prefix.qname, this._nodeID);%0A } else {%0A element.setAttribute("nodeID", this._nodeID);%0A }%0A }%0A%0A /%2A%2A%0A %2A Creates a new Element in the document for holding assertions about this%0A %2A subject. The URI controls what tagname to use.%0A %2A/%0A _createNewElement(uri) {%0A // If there are already nodes representing this in the document then we need%0A // a nodeID to match them%0A if (!this._nodeID && this._elements.length > 0) {%0A this._ds._createNodeID(this);%0A for (let element of this._elements)%0A this._applyToElement(element);%0A }%0A%0A return super._createNewElement.call(uri);%0A }%0A%0A /%2A%2A%0A %2A Adds a reference to this node to the given property Element.%0A %2A/%0A _addReferenceToElement(element) {%0A if (this._elements.length > 0 && !this._nodeID) {%0A // In document elsewhere already%0A // Create a node ID and update the other nodes referencing%0A this._ds._createNodeID(this);%0A for (let element of this._elements)%0A this._applyToElement(element);%0A }%0A%0A if (this._nodeID) {%0A if (USE_RDFNS_ATTR) {%0A let prefix = this._ds._resolvePrefix(element, RDF_R("nodeID"));%0A element.setAttributeNS(prefix.namespaceURI, prefix.qname, this._nodeID);%0A } else {%0A element.setAttribute("nodeID", this._nodeID);%0A }%0A } else {%0A // Add the empty blank node, this is generally right since further%0A // assertions will be added to fill this out%0A var newelement = this._ds._addElement(element, RDF_R("Description"));%0A newelement.listCounter = 1;%0A this._elements.push(newelement);%0A }%0A }%0A%0A /%2A%2A%0A %2A Removes any reference to this node from the given property Element.%0A %2A/%0A _removeReferenceFromElement(element) {%0A if (element.hasAttributeNS(NS_RDF, "nodeID"))%0A element.removeAttributeNS(NS_RDF, "nodeID");%0A if (element.hasAttribute("nodeID"))%0A element.removeAttribute("nodeID");%0A }%0A%0A getNodeID() {%0A return this._nodeID;%0A }%0A}%0A%0A/%2A%2A%0A %2A Creates a new RDFDataSource from the given document. The document will be%0A %2A changed as assertions are added and removed to the RDF. Pass a null document%0A %2A to start with an empty graph.%0A %2A/%0Aclass RDFDataSource {%0A constructor(document) {%0A // All known resources, indexed on URI%0A this._resources = {};%0A // All blank nodes%0A this._allBlankNodes = [];%0A%0A // The underlying DOM document for this datasource%0A this._document = document;%0A this._parseDocument();%0A }%0A%0A static loadFromString(text) {%0A let parser = new DOMParser();%0A let document = parser.parseFromString(text, "application/xml");%0A%0A return new this(document);%0A }%0A%0A /%2A%2A%0A %2A Returns an rdf subject for the given DOM Element. If the subject has not%0A %2A been seen before a new one is created.%0A %2A/%0A _getSubjectForElement(element) {%0A var about = getRDFAttribute(element, "about");%0A%0A if (about !== undefined) {%0A let base = Services.io.newURI(element.baseURI);%0A return this.getResource(base.resolve(about));%0A }%0A return this.getBlankNode(null);%0A }%0A%0A /%2A%2A%0A %2A Parses the document for subjects at the top level.%0A %2A/%0A _parseDocument() {%0A var domnode = this._document.documentElement.firstChild;%0A while (domnode) {%0A if (isElement(domnode)) {%0A var subject = this._getSubjectForElement(domnode);%0A subject._parseElement(domnode);%0A }%0A domnode = domnode.nextSibling;%0A }%0A }%0A%0A /%2A%2A%0A %2A Gets a blank node. nodeID may be null and if so a new blank node is created.%0A %2A If a nodeID is given then the blank node with that ID is returned or created.%0A %2A/%0A getBlankNode(nodeID) {%0A var rdfnode = new RDFBlankNode(this, nodeID);%0A this._allBlankNodes.push(rdfnode);%0A return rdfnode;%0A }%0A%0A /%2A%2A%0A %2A Gets the resource for the URI. The resource is created if it has not been%0A %2A used already.%0A %2A/%0A getResource(uri) {%0A if (uri in this._resources)%0A return this._resources[uri];%0A%0A var resource = new RDFResource(this, uri);%0A this._resources[uri] = resource;%0A return resource;%0A }%0A}%0A%0A%0A/%2A%2A%2A RDFManifestConverter.jsm %2A%2A%2A/%0A%0Aconst RDFURI_INSTALL_MANIFEST_ROOT = "urn:mozilla:install-manifest";%0A%0Afunction EM_R(aProperty) {%0A return %60http://www.mozilla.org/2004/em-rdf%23%24{aProperty}%60;%0A}%0A%0Afunction getValue(literal) {%0A return literal && literal.getValue();%0A}%0A%0Afunction getProperty(resource, property) {%0A return getValue(resource.getProperty(EM_R(property)));%0A}%0A%0Aclass Manifest {%0A constructor(ds) {%0A this.ds = ds;%0A }%0A%0A static loadFromString(text) {%0A return new this(RDFDataSource.loadFromString(text));%0A }%0A}%0A%0Aclass InstallRDF extends Manifest {%0A _readProps(source, obj, props) {%0A for (let prop of props) {%0A let val = getProperty(source, prop);%0A if (val != null) {%0A obj[prop] = val;%0A }%0A }%0A }%0A%0A _readArrayProp(source, obj, prop, target, decode = getValue) {%0A let result = Array.from(source.getObjects(EM_R(prop)),%0A target => decode(target));%0A if (result.length) {%0A obj[target] = result;%0A }%0A }%0A%0A _readArrayProps(source, obj, props, decode = getValue) {%0A for (let [prop, target] of Object.entries(props)) {%0A this._readArrayProp(source, obj, prop, target, decode);%0A }%0A }%0A%0A _readLocaleStrings(source, obj) {%0A this._readProps(source, obj, ["name", "description", "creator", "homepageURL"]);%0A this._readArrayProps(source, obj, {%0A locale: "locales",%0A developer: "developers",%0A translator: "translators",%0A contributor: "contributors",%0A });%0A }%0A%0A decode() {%0A let root = this.ds.getResource(RDFURI_INSTALL_MANIFEST_ROOT);%0A let result = {};%0A%0A let props = ["id", "version", "type", "updateURL", "optionsURL",%0A "optionsType", "aboutURL", "iconURL",%0A "bootstrap", "unpack", "strictCompatibility"];%0A this._readProps(root, result, props);%0A%0A let decodeTargetApplication = source => {%0A let app = {};%0A this._readProps(source, app, ["id", "minVersion", "maxVersion"]);%0A return app;%0A };%0A%0A let decodeLocale = source => {%0A let localized = {};%0A this._readLocaleStrings(source, localized);%0A return localized;%0A };%0A%0A this._readLocaleStrings(root, result);%0A%0A this._readArrayProps(root, result, {"targetPlatform": "targetPlatforms"});%0A this._readArrayProps(root, result, {"targetApplication": "targetApplications"},%0A decodeTargetApplication);%0A this._readArrayProps(root, result, {"localized": "localized"},%0A decodeLocale);%0A this._readArrayProps(root, result, {"dependency": "dependencies"},%0A source => getProperty(source, "id"));%0A%0A return result;%0A }%0A}%0A%0A%0A/%2A%2A%2A BootstrapLoader.jsm %2A%2A%2A/%0A%0Avar {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");%0Avar {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");%0A%0AXPCOMUtils.defineLazyModuleGetters(this, {%0A ConsoleAPI: "resource://gre/modules/Console.jsm",%0A Blocklist: "resource://gre/modules/Blocklist.jsm",%0A XPIProvider: "resource://gre/modules/addons/XPIProvider.jsm"%0A});%0A%0Avar OPTIONS_TYPE_DIALOG = 1;%0A%0AServices.obs.addObserver(doc => {%0A if (doc.location.protocol + doc.location.pathname === 'about:addons' ||%0A doc.location.protocol + doc.location.pathname === 'chrome:/content/extensions/aboutaddons.html') {%0A const win = doc.defaultView;%0A let handleEvent_orig = win.customElements.get('addon-card').prototype.handleEvent;%0A win.customElements.get('addon-card').prototype.handleEvent = function (e) {%0A if (e.type === 'click' &&%0A e.target.getAttribute('action') === 'preferences' &&%0A this.addon.optionsType == OPTIONS_TYPE_DIALOG) {%0A var windows = Services.wm.getEnumerator(null);%0A while (windows.hasMoreElements()) {%0A var win2 = windows.getNext();%0A if (win2.closed) {%0A continue;%0A }%0A if (win2.document.documentURI == this.addon.optionsURL) {%0A win2.focus();%0A return;%0A }%0A }%0A var features = 'chrome,titlebar,toolbar,centerscreen';%0A var instantApply = Services.prefs.getBoolPref('browser.preferences.instantApply');%0A features += instantApply ? ',dialog=no' : '';%0A win.docShell.rootTreeItem.domWindow.openDialog(this.addon.optionsURL, this.addon.id, features); %0A } else {%0A handleEvent_orig.apply(this, arguments);%0A }%0A }%0A let update_orig = win.customElements.get('addon-options').prototype.update;%0A win.customElements.get('addon-options').prototype.update = function (card, addon) {%0A update_orig.apply(this, arguments);%0A if (addon.optionsType == OPTIONS_TYPE_DIALOG)%0A this.querySelector('panel-item[data-l10n-id="preferences-addon-button"]').hidden = false;%0A }%0A }%0A}, 'chrome-document-loaded');%0A%0Aconst {AddonManager} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");%0Aconst {XPIDatabase, AddonInternal} = ChromeUtils.import("resource://gre/modules/addons/XPIDatabase.jsm");%0A%0ACu.import("resource://gre/modules/addons/XPIDatabase.jsm", {}).defineAddonWrapperProperty("optionsType", function optionsType() {%0A if (!this.isActive) {%0A return null;%0A }%0A%0A let addon = this.__AddonInternal__;%0A let hasOptionsURL = !!this.optionsURL;%0A%0A if (addon.optionsType) {%0A switch (parseInt(addon.optionsType, 10)) {%0A case OPTIONS_TYPE_DIALOG:%0A case AddonManager.OPTIONS_TYPE_TAB:%0A case AddonManager.OPTIONS_TYPE_INLINE_BROWSER:%0A return hasOptionsURL ? addon.optionsType : null;%0A }%0A return null;%0A }%0A%0A return null;%0A});%0A%0AXPIDatabase.isDisabledLegacy = () => false;%0A%0AXPCOMUtils.defineLazyGetter(this, 'BOOTSTRAP_REASONS', () => XPIProvider.BOOTSTRAP_REASONS);%0A%0Aconst {Log} = ChromeUtils.import('resource://gre/modules/Log.jsm');%0Avar logger = Log.repository.getLogger('addons.bootstrap');%0A%0A/%2A%2A%0A %2A Valid IDs fit this pattern.%0A %2A/%0Avar gIDTest = /^(\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}|[a-z0-9-\._]%2A\@[a-z0-9-\._]+)$/i;%0A%0A// Properties that exist in the install manifest%0Aconst PROP_METADATA = ['id', 'version', 'type', 'internalName', 'updateURL',%0A 'optionsURL', 'optionsType', 'aboutURL', 'iconURL'];%0Aconst PROP_LOCALE_SINGLE = ['name', 'description', 'creator', 'homepageURL'];%0Aconst PROP_LOCALE_MULTI = ['developers', 'translators', 'contributors'];%0A%0A// Map new string type identifiers to old style nsIUpdateItem types.%0A// Retired values:%0A// 32 = multipackage xpi file%0A// 8 = locale%0A// 256 = apiextension%0A// 128 = experiment%0A// theme = 4%0Aconst TYPES = {%0A extension: 2,%0A dictionary: 64,%0A};%0A%0Aconst COMPATIBLE_BY_DEFAULT_TYPES = {%0A extension: true,%0A dictionary: true,%0A};%0A%0Aconst hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty);%0A%0Afunction isXPI(filename) {%0A let ext = filename.slice(-4).toLowerCase();%0A return ext === '.xpi' || ext === '.zip';%0A}%0A%0A/%2A%2A%0A %2A Gets an nsIURI for a file within another file, either a directory or an XPI%0A %2A file. If aFile is a directory then this will return a file: URI, if it is an%0A %2A XPI file then it will return a jar: URI.%0A %2A%0A %2A @param {nsIFile} aFile%0A %2A The file containing the resources, must be either a directory or an%0A %2A XPI file%0A %2A @param {string} aPath%0A %2A The path to find the resource at, '/' separated. If aPath is empty%0A %2A then the uri to the root of the contained files will be returned%0A %2A @returns {nsIURI}%0A %2A An nsIURI pointing at the resource%0A %2A/%0Afunction getURIForResourceInFile(aFile, aPath) {%0A if (!isXPI(aFile.leafName)) {%0A let resource = aFile.clone();%0A if (aPath)%0A aPath.split('/').forEach(part => resource.append(part));%0A%0A return Services.io.newFileURI(resource);%0A }%0A%0A return buildJarURI(aFile, aPath);%0A}%0A%0A/%2A%2A%0A %2A Creates a jar: URI for a file inside a ZIP file.%0A %2A%0A %2A @param {nsIFile} aJarfile%0A %2A The ZIP file as an nsIFile%0A %2A @param {string} aPath%0A %2A The path inside the ZIP file%0A %2A @returns {nsIURI}%0A %2A An nsIURI for the file%0A %2A/%0Afunction buildJarURI(aJarfile, aPath) {%0A let uri = Services.io.newFileURI(aJarfile);%0A uri = 'jar:' + uri.spec + '!/' + aPath;%0A return Services.io.newURI(uri);%0A}%0A%0Avar BootstrapLoader = {%0A name: 'bootstrap',%0A manifestFile: 'install.rdf',%0A async loadManifest(pkg) {%0A /%2A%2A%0A %2A Reads locale properties from either the main install manifest root or%0A %2A an em:localized section in the install manifest.%0A %2A%0A %2A @param {Object} aSource%0A %2A The resource to read the properties from.%0A %2A @param {boolean} isDefault%0A %2A True if the locale is to be read from the main install manifest%0A %2A root%0A %2A @param {string[]} aSeenLocales%0A %2A An array of locale names already seen for this install manifest.%0A %2A Any locale names seen as a part of this function will be added to%0A %2A this array%0A %2A @returns {Object}%0A %2A an object containing the locale properties%0A %2A/%0A function readLocale(aSource, isDefault, aSeenLocales) {%0A let locale = {};%0A if (!isDefault) {%0A locale.locales = [];%0A for (let localeName of aSource.locales || []) {%0A if (!localeName) {%0A logger.warn('Ignoring empty locale in localized properties');%0A continue;%0A }%0A if (aSeenLocales.includes(localeName)) {%0A logger.warn('Ignoring duplicate locale in localized properties');%0A continue;%0A }%0A aSeenLocales.push(localeName);%0A locale.locales.push(localeName);%0A }%0A%0A if (locale.locales.length == 0) {%0A logger.warn('Ignoring localized properties with no listed locales');%0A return null;%0A }%0A }%0A%0A for (let prop of [...PROP_LOCALE_SINGLE, ...PROP_LOCALE_MULTI]) {%0A if (hasOwnProperty(aSource, prop)) {%0A locale[prop] = aSource[prop];%0A }%0A }%0A%0A return locale;%0A }%0A%0A let manifestData = await pkg.readString('install.rdf');%0A let manifest = InstallRDF.loadFromString(manifestData).decode();%0A%0A let addon = new AddonInternal();%0A for (let prop of PROP_METADATA) {%0A if (hasOwnProperty(manifest, prop)) {%0A addon[prop] = manifest[prop];%0A }%0A }%0A%0A if (!addon.type) {%0A addon.type = 'extension';%0A } else {%0A let type = addon.type;%0A addon.type = null;%0A for (let name in TYPES) {%0A if (TYPES[name] == type) {%0A addon.type = name;%0A break;%0A }%0A }%0A }%0A%0A if (!(addon.type in TYPES))%0A throw new Error('Install manifest specifies unknown type: ' + addon.type);%0A%0A if (!addon.id)%0A throw new Error('No ID in install manifest');%0A if (!gIDTest.test(addon.id))%0A throw new Error('Illegal add-on ID ' + addon.id);%0A if (!addon.version)%0A throw new Error('No version in install manifest');%0A%0A addon.strictCompatibility = (!(addon.type in COMPATIBLE_BY_DEFAULT_TYPES) ||%0A manifest.strictCompatibility == 'true');%0A%0A // Only read these properties for extensions.%0A if (addon.type == 'extension') {%0A if (manifest.bootstrap != 'true') {%0A throw new Error('Non-restartless extensions no longer supported');%0A }%0A%0A if (addon.optionsType &&%0A addon.optionsType != OPTIONS_TYPE_DIALOG &&%0A addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE_BROWSER &&%0A addon.optionsType != AddonManager.OPTIONS_TYPE_TAB) {%0A throw new Error('Install manifest specifies unknown optionsType: ' + addon.optionsType);%0A }%0A%0A if (addon.optionsType)%0A addon.optionsType = parseInt(addon.optionsType);%0A }%0A%0A addon.defaultLocale = readLocale(manifest, true);%0A%0A let seenLocales = [];%0A addon.locales = [];%0A for (let localeData of manifest.localized || []) {%0A let locale = readLocale(localeData, false, seenLocales);%0A if (locale)%0A addon.locales.push(locale);%0A }%0A%0A let dependencies = new Set(manifest.dependencies);%0A addon.dependencies = Object.freeze(Array.from(dependencies));%0A%0A let seenApplications = [];%0A addon.targetApplications = [];%0A for (let targetApp of manifest.targetApplications || []) {%0A if (!targetApp.id || !targetApp.minVersion ||%0A !targetApp.maxVersion) {%0A logger.warn('Ignoring invalid targetApplication entry in install manifest');%0A continue;%0A }%0A if (seenApplications.includes(targetApp.id)) {%0A logger.warn('Ignoring duplicate targetApplication entry for ' + targetApp.id +%0A ' in install manifest');%0A continue;%0A }%0A seenApplications.push(targetApp.id);%0A addon.targetApplications.push(targetApp);%0A }%0A%0A // Note that we don't need to check for duplicate targetPlatform entries since%0A // the RDF service coalesces them for us.%0A addon.targetPlatforms = [];%0A for (let targetPlatform of manifest.targetPlatforms || []) {%0A let platform = {%0A os: null,%0A abi: null,%0A };%0A%0A let pos = targetPlatform.indexOf('_');%0A if (pos != -1) {%0A platform.os = targetPlatform.substring(0, pos);%0A platform.abi = targetPlatform.substring(pos + 1);%0A } else {%0A platform.os = targetPlatform;%0A }%0A%0A addon.targetPlatforms.push(platform);%0A }%0A%0A addon.userDisabled = false;%0A addon.softDisabled = addon.blocklistState == Blocklist.STATE_SOFTBLOCKED;%0A addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;%0A%0A addon.userPermissions = null;%0A%0A addon.icons = {};%0A if (await pkg.hasResource('icon.png')) {%0A addon.icons[32] = 'icon.png';%0A addon.icons[48] = 'icon.png';%0A }%0A%0A if (await pkg.hasResource('icon64.png')) {%0A addon.icons[64] = 'icon64.png';%0A }%0A%0A return addon;%0A },%0A%0A loadScope(addon) {%0A let file = addon.file || addon._sourceBundle;%0A let uri = getURIForResourceInFile(file, 'bootstrap.js').spec;%0A let principal = Services.scriptSecurityManager.getSystemPrincipal();%0A%0A let sandbox = new Cu.Sandbox(principal, {%0A sandboxName: uri,%0A addonId: addon.id,%0A wantGlobalProperties: ['ChromeUtils'],%0A metadata: { addonID: addon.id, URI: uri },%0A });%0A%0A try {%0A Object.assign(sandbox, BOOTSTRAP_REASONS);%0A%0A XPCOMUtils.defineLazyGetter(sandbox, 'console', () =>%0A new ConsoleAPI({ consoleID: %60addon/%24{addon.id}%60 }));%0A%0A Services.scriptloader.loadSubScript(uri, sandbox);%0A } catch (e) {%0A logger.warn(%60Error loading bootstrap.js for %24{addon.id}%60, e);%0A }%0A%0A function findMethod(name) {%0A if (sandbox[name]) {%0A return sandbox[name];%0A }%0A%0A try {%0A let method = Cu.evalInSandbox(name, sandbox);%0A return method;%0A } catch (err) { }%0A%0A return () => {%0A logger.warn(%60Add-on %24{addon.id} is missing bootstrap method %24{name}%60);%0A };%0A }%0A%0A let install = findMethod('install');%0A let uninstall = findMethod('uninstall');%0A let startup = findMethod('startup');%0A let shutdown = findMethod('shutdown');%0A%0A return {%0A install(...args) {%0A install(...args);%0A // Forget any cached files we might've had from this extension.%0A Services.obs.notifyObservers(null, 'startupcache-invalidate');%0A },%0A%0A uninstall(...args) {%0A uninstall(...args);%0A // Forget any cached files we might've had from this extension.%0A Services.obs.notifyObservers(null, 'startupcache-invalidate');%0A },%0A%0A startup(...args) {%0A if (addon.type == 'extension') {%0A logger.debug(%60Registering manifest for %24{file.path}\n%60);%0A Components.manager.addBootstrappedManifestLocation(file);%0A }%0A return startup(...args);%0A },%0A%0A shutdown(data, reason) {%0A try {%0A return shutdown(data, reason);%0A } catch (err) {%0A throw err;%0A } finally {%0A if (reason != BOOTSTRAP_REASONS.APP_SHUTDOWN) {%0A logger.debug(%60Removing manifest for %24{file.path}\n%60);%0A Components.manager.removeBootstrappedManifestLocation(file);%0A }%0A }%0A },%0A };%0A },%0A};%0A%0AAddonManager.addExternalExtensionLoader(BootstrapLoader);%0A%0Aif (AddonManager.isReady) {%0A AddonManager.getAllAddons().then(addons => {%0A addons.forEach(addon => {%0A if (addon.type == 'extension' && !addon.isWebExtension && !addon.userDisabled) {%0A addon.reload();%0A };%0A });%0A });%0A}%0A%0AObject.defineProperty(%0A AddonInternal.prototype,%0A "providesUpdatesSecurely",%0A {enumerable: true, value: true}%0A);%0A `.trim())); Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader) .loadSubScript("resource://" + subst, new Cu.Sandbox(Cu.getObjectPrincipal(this), {wantGlobalProperties: ["ChromeUtils", "DOMParser", "Element", "fetch"]})); })(Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService));} catch(ex) {Cu.reportError(ex);} // https://forum.mozilla-russia.org/viewtopic.php?pid=799413#p799413 try {(async xp => { var imprt, ids = [ "custombuttons@xsms.org", ]; if (Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).inSafeMode) return; if (Cr.NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) var {XPIInternal} = (imprt = url => Cu.import(url, {}))(xp); else { // Fx 101+ var g = Cu.getGlobalForObject(Cu), te = new g.TextEncoder(); var imp = g.ChromeUtils.import, {XPIInternal} = imp(xp); var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); var rph = ios.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler); imprt = (url, id) => { var subst = te.encode(id).join(""); rph.setSubstitution(subst, ios.newURI(url)); return imp(`resource://${subst}/`); } } var load = async (file, id) => { var rootURI = XPIInternal.getURIForResourceInFile(file, ""); imprt(rootURI.resolve("startup.jsm"), id).start(rootURI); } var proto = XPIInternal.BootstrapScope.prototype; var func = proto._beforeCallBootstrapMethod; proto._beforeCallBootstrapMethod = () => { proto._beforeCallBootstrapMethod = func; for(var {id, loader, file} of XPIInternal.XPIStates.enabledAddons()) ids.includes(id) && !loader && load(file, id); } })("resource://gre/modules/addons/XPIProvider.jsm");} catch(ex) {Cu.reportError(ex);} // try { const { Services } = Cu.import('resource://gre/modules/Services.jsm'); Cu.import(Services.io.newFileURI(Services.dirsvc.get('ProfD', Ci.nsIFile)).spec + 'chrome/utils/boot.jsm'); } catch(ex) {Cu.reportError(ex);}
Жизнь иногда такое выкидывает, что хочется подобрать...
Отсутствует
А как этот bootstrap устанавливать
А зачем?
bootstrap — это для тех, у кого уже подключён
какой-нибудь bootstrap-loader, соответственно,
установлены другие bootstrap расширения, то есть,
получается, что paxmod поставить можно, но как бы никчему, избыточно.
А подключать bootstrap-loader только,
чтобы поставить bootstrap CB — как-то странно.
И, отвечая на вопрос, если bootstrap-loader подключён и в порядке,
то устанавливать точно так же, как другие расширения и WebExtensions.
На about:addons кнопка-шестерёнка, пункт «Установить дополнение из файла…».
Ещё, вроде, можно перетащить xpi в лису мышью.
Отсутствует
А зачем?
Дык просто любопытство одолело, почему не ставится.
bootstrap — это для тех, у кого уже подключён
какой-нибудь bootstrap-loader, соответственно,
установлены другие bootstrap расширения, то есть,
получается, что paxmod поставить можно, но как бы никчему, избыточно.А подключать bootstrap-loader только,
чтобы поставить bootstrap CB — как-то странно.
Спасибо. Вот теперь всё доходчиво и понятно. И спасибо большое за CB
Отредактировано voqabuhe (03-05-2022 23:30:48)
Отсутствует
Вставить то вставил
Понимаешь хоть, как это со стороны выглядит?
Отсюда не видно что и как вставил.
Хочешь чтобы я в кофейную гущу пальцем ткнул?
Хорошо, допустим притащил добро из старой версии,
в том числе и кустомэлементщину, включая
chrome://global/content/elements/panel.js
А там используется XULPopupElement.isAnchored, которого больше нет.
Так что пробуй в panel.js заменить this.isAnchored на event.isAnchored
Заменить все, там их несколько.
Отсутствует
В кнопке открыть в плеере "Л: Видео в плеер\nП: Видео из Clipboard" при ЛКМ видео добавляется в плейлист. Если убрать строку 62 "args.push("/add");", то видео будет сразу воспроизводится в плеере. А как сделать, чтобы вместо ненужного ПКМ "Видео из Clipboard" сразу запускалось воспроизведение, а по ЛКМ осталось добавление в плейлист? Т.е ЛКМ работало со строкой "args.push("/add");" а ПКМ без неё.
(func => { var sysPlayerName = "Pot Player"; var path = "R:\\PotPlayer\\PotPlayerMini.exe"; var videoMoved = "Видео перенесено в " + sysPlayerName; var noFound = "Не найдено видео на странице, доступное для переноса в " + sysPlayerName; this.label = "Открыть видео в " + sysPlayerName; this.tooltipText = "Л: Видео в плеер\nП: Видео из Clipboard"; this._handleClick = () => { var msgName = _id + ":Player"; var listener = ({data}) => data ? run([data]) : notify(); messageManager.addMessageListener(msgName, listener); addDestructor(() => messageManager.removeMessageListener(msgName, listener)); var url = "data:charset=utf-8," + encodeURIComponent( `(${func})()`.replace("MSG_NAME", msgName) .replace("VIDEO_MOVED", encodeURIComponent(videoMoved)) .replace("CONFIRM", encodeURIComponent("Открыть ссылку в плеере ?")) ); (this._handleClick = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))(); } this.onauxclick = e => e.button != 1 || gShowPopup(this); this.oncontextmenu = e => { if (e.ctrlKey || e.shiftKey || e.altKey) return; e.preventDefault(); custombuttons.confirmBox(null, "Запустить плеер из буфера обмена ?", "Да", "Нет") && run([gClipboard.read(), "/play"]); } var popup = document.getElementById("contentAreaContextMenu"); addEventListener("popupshowing", { get hidden() { return !(gContextMenu.onLink || gContextMenu.onVideo || gContextMenu.onPlainTextLink); }, handleEvent() { if (this.hidden) return; var menuitem = document.createXULElement("menuitem"); for(var args of Object.entries({ image: self.image, oncommand: "play()", class: "menuitem-iconic", label: "Открыть в " + sysPlayerName })) menuitem.setAttribute(...args); menuitem.play = () => play(gContextMenu.linkURL || gContextMenu.mediaURL); document.getElementById("context-savelink").before(menuitem); addDestructor(() => menuitem.remove()); this.handleEvent = e => { if (e.target == popup) menuitem.hidden = this.hidden; } } }, false, popup || 1); var play = link => custombuttons.confirmBox(null, "Открыть ссылку в плеере ?", "Да", "Отмена") && run([link]); var run = args => { var file = FileUtils.File(path); (run = args => { if (!file.exists()) return custombuttons.alertBox("File not exists!", path); var process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess); process.init(file); args.push("/add"); process.runwAsync(args, args.length); })(args); } var notify = () => { var name = _id + "-noFound"; var as = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService); (notify = () => setTimeout(as.closeAlert, 1150, name, as.showAlertNotification( "chrome://global/skin/icons/question-48.png", "", noFound, false, "", null, name )))(); } })(() => { var found, videoMoved, SEND = msg => { found = true; if (!msg || Cc["@mozilla.org/embedcomp/prompt-service;1"] .getService(Ci.nsIPromptService) .confirm(content, null, decodeURIComponent("CONFIRM")) ) { if (msg) videoMoved = decodeURIComponent("VIDEO_MOVED"); sendAsyncMessage("MSG_NAME", msg); } else return true; } var YoutubeID = /(?:youtube(?:-nocookie)?\.com\/(?:[^\/\n\s]+\/\S+\/|(?:v|e(?:mbed)?)\/|\S*?[?&]v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})(?:\W|$)/; var tmp = '', tmpp = '', innerA = '<div style="display:block!important;color:#00ff00!important;width:250px!important;font:bold 16px serif!important;z-index:999!important;opacity:1!important;visibility: visible!important;', innerB = 'left:5px!important;position:absolute!important;height:auto!important;box-sizing:border-box!important;padding:5px!important;margin:5px!important;', //stopPl = "javascript:(function(){v=document.getElementById('movie_player');if(v){v.stopVideo()}else{v=document.getElementsByTagName('video');if(v){v[0].src='';try{v[0].load()}catch(e){}};}})();", ytIMGouter = function (ytID) { return '<div width="100%"><br /><a target="_blank" href="https://www.youtube.com/watch?v=' + ytID + '"><img src="https://i.ytimg.com/vi/' + ytID + '/hqdefault.jpg"></a><br />' + innerA + 'background-color:black!important;position:relative!important;bottom:20px!important;"> ' + videoMoved + '</div><br /></div><br />' }, handlWin = function (currentWin) { tmp = ''; var elem = currentWin.document.getElementsByTagName('video'), currLoc = currentWin.location; if (elem.length > 0) { if (currLoc.hostname.indexOf('youtu') != -1 && (tmp = currLoc.toString().match(YoutubeID)) && tmp[1].length == 11) { if (SEND('https://www.youtube.com/watch?v=' + tmp[1])) return; videoMovedbox = currentWin.document.createElement('videoMoved'); videoMovedbox.innerHTML = innerA + innerB + 'top:-15px!important;"><b>' + videoMoved + '</b></div>'; //loadURI(stopPl); (function(d){var v=d.getElementById('movie_player');if(v){try{v.stopVideo()}catch{}} else{v=d.getElementsByTagName('video');if(v[0]){v[0].src='';try{v[0].load()}catch{}};}})(currentWin.document); currentWin.document.getElementById('eow-title').appendChild(videoMovedbox); return true; }; for (i = 0; i < elem.length; i++) { if (((tmp = getSrc(elem[i].parentNode, currLoc)) && tmp.length > 2) || (i == 0 && currentWin.document.body.innerHTML.substring(0, 7) == '<video ' && (tmp = currLoc.toString()))) { if (SEND(tmp)) return; videoMovedbox = currentWin.document.createElement('videoMoved'); videoMovedbox.innerHTML = innerA + innerB + 'top:20px!important;background-color:black!important;">' + videoMoved + '</div>'; if (currLoc.hostname == 'www.youtube.com') { elem[i].parentNode.parentNode.appendChild(videoMovedbox); } else { elem[i].parentNode.appendChild(videoMovedbox); }; elem[i].src = ''; try { elem[i].load() } catch (e) {}; return true; } } }; currentWin._elems = currentWin.document.getElementsByTagName('iframe'); if (currentWin._elems.length > 0) { for (currentWin._iCounter = 0; currentWin._iCounter < currentWin._elems.length; currentWin._iCounter++) { if ((currentWin._elems[currentWin._iCounter].src.indexOf('youtube.com') > -1) && (tmp = currentWin._elems[currentWin._iCounter].src.match(YoutubeID)) && (tmp[1].length == 11)) { if (SEND('https://www.youtube.com/watch?v=' + tmp[1])) return; currentWin._elems[currentWin._iCounter].outerHTML = ytIMGouter(tmp[1]); return true; }; if (currentWin._elems[currentWin._iCounter].clientWidth > 80 && currentWin._elems[currentWin._iCounter].clientHeight > 40 && handlWin(currentWin._elems[currentWin._iCounter].contentWindow)) return true; } }; elem = currentWin.document.getElementsByTagName('object'); currLoc = currentWin.location; if (elem.length == 0) { elem = currentWin.document.getElementsByTagName('embed') }; if (elem.length > 0) { for (i = 0; i < elem.length; i++) { if (elem[i].innerHTML.indexOf('youtu') != -1 && (tmp = elem[i].innerHTML.match(YoutubeID)) && tmp[1].length == 11) { if (SEND('https://www.youtube.com/watch?v=' + tmp[1])) return; elem[i].outerHTML = ytIMGouter(tmp[1]); return true; } else { if (elem[i].clientWidth > 80 && elem[i].clientHeight > 40) { if (((tmp = getSrc(elem[i].parentNode, currLoc)) || (tmp = getLink(elem[i], currLoc))) && tmp.length > 2) { if (SEND(tmp)) return; elem[i].outerHTML = innerA + 'background-color:black!important;bottom:20px!important;"> ' + videoMoved + '</div>'; return true; }; }; } }; }; return false; }; function restProtHost(lnkR, curLoc) { if (lnkR.length == 0) return ''; let tr = lnkR.replace(/^:\/\//, curLoc.protocol + "//"); if (!tr.match(/^https?:\/\//i)) { lnkR = tr.replace(/^\/+/, ''); if (lnkR.split('/')[0].split('?')[0].split('#')[0].toLowerCase().match(/^(?:[-a-z\d]+\.)+[a-z\d]{2,6}$/)) { tr = curLoc.protocol + '//' + lnkR; } else { tr = curLoc.protocol + '//' + curLoc.host + "/" + lnkR; } }; return tr; }; function getSrc(vobj, currentLoc) { var t = '', tt = ''; if ((((t = vobj.innerHTML.match(/<video.*?\ssrc=(?:(?:'([^']*)')|(?:"([^"]*)")|([^\s]*))/i)) && (t) && (tt = t[1] || t[2] || t[3]) && tt.indexOf('blob:') == -1) || ((t = vobj.innerHTML.match(/<source.*?\ssrc=(?:(?:'([^']*)')|(?:"([^"]*)")|([^\s]*)).*?\stype=['"]?video\//i)) && (t) && (tt = t[1] || t[2] || t[3]))) && tt.length > 2 && tt.indexOf('blob:') == -1) { if (tt.indexOf(".mp4/?") == -1) { tt = tt.replace(/&/g, "&") }; t = restProtHost(tt, currentLoc); return t; }; return ''; }; function getLink(obj, curLocation) { if (!obj || !obj.tagName) return ''; q = obj.tagName.toLowerCase(); var getParam = function (e, n) { var v = '', r = new RegExp('^(' + n + ')$', 'i'), param = e.getElementsByTagName('param'); for (var igp = 0, p; p = param[igp]; igp++) { if (p.hasAttribute('name') && p.getAttribute('name').match(r)) { v = p.getAttribute('value'); break }; }; return v; }; var restPath = function (f, s) { return (f.substring(0, 4) == 'http') ? f : s.replace(/[#?].*$/, '').replace(/[^\/]*$/, f) }; function videoLinkExtract(fl) { alert(fl); var linkArr = [], outLinks = [], jj = 0, lba = '', lbb = '', decodeURL = gBrowser.currentURI.spec; { try { return decodeURIComponent(s) } catch (e) { return unescape(s) } }; for (var ij = 0; ij < 3; ij++) { lba = lba + String.fromCharCode(parseInt((Math.random() * 15 + 1) + '', 10)); lbb = lbb + String.fromCharCode(parseInt((Math.random() * 15 + 16) + '', 10)); }; function pushWithMerit(lnk) { var merit = -11; if (lnk.match(/^https?:\/\//i)) merit = merit + 40; if (outLinks.length == 0) merit = merit + 1; if (lnk.match(/^\//)) merit = merit + 7; if (lnk.match(/^\/\//)) merit = merit + 30; if (lnk.match(/240p([^a-z]|$)/i)) merit = merit + 1; if (lnk.match(/[^a-z]240([^a-z0-9]|$)/i)) merit = merit + 1; if (lnk.match(/360p([^a-z]|$)/i)) merit = merit + 3; if (lnk.match(/[^a-z]360([^a-z0-9]|$)/i)) merit = merit + 3; if (lnk.match(/480p([^a-z]|$)/i)) merit = merit + 5; if (lnk.match(/[^a-z]480([^a-z0-9]|$)/i)) merit = merit + 5; if (lnk.match(/720p([^a-z]|$)/i)) merit = merit + 7; if (lnk.match(/[^a-z]720([^a-z0-9]|$)/i)) merit = merit + 7; if (lnk.match(/\.mp4([^a-z]|$)/i)) merit = merit + 8; if (lnk.match(/_hd([^a-z]|$)/i)) merit = merit + 6; if (lnk.match(/\.(jpg|xml)([^a-z]|$)/i)) merit = merit - 40; if (merit > 0) outLinks.push(merit + lba + lnk); Services.console.logStringMessage('merit:' + merit + ' lnk->' + lnk); }; linkArr.push(fl); while (linkArr.length > jj && jj < 30) { var testPaths = []; testPaths = linkArr[jj].split(/(\.(?:flv|mp4|m3u8))/i); if (testPaths[testPaths.length - 1] == '') testPaths.pop(); for (k = 1; k < testPaths.length; k = k + 2) { if (testPaths[k - 1].indexOf(lba) > -1) { pref = testPaths[k - 1]; } else { var testAboutDom = testPaths[k - 1].toLowerCase().split(/(https?:\/\/)/); if (testAboutDom[testAboutDom.length - 1] == '') testAboutDom.pop(); var pTest = testAboutDom[testAboutDom.length - 1].split(/(\?[^\?]*?&)/); if (pTest.length > 2) { pTest.pop(); pTest.pop(); }; testAboutDom[testAboutDom.length - 1] = pTest.join(''); pref = testPaths[k - 1].substring(testAboutDom.join('').lastIndexOf("&") + 1); }; t2 = pref.lastIndexOf(lbb); if (t2 > -1) { pref = pref.substring(t2 + 3); } else { t2 = pref.lastIndexOf('{"'); if (t2 > -1) pref = pref.substring(t2 + 2); t2 = pref.lastIndexOf('["'); if (t2 > -1) pref = pref.substring(t2 + 2); t2 = pref.lastIndexOf(',"'); if (t2 > -1) pref = pref.substring(t2 + 2); t2 = pref.toLowerCase().lastIndexOf('"http://'); if (t2 > -1) pref = pref.substring(t2 + 1); t2 = pref.toLowerCase().lastIndexOf('"https://'); if (t2 > -1) pref = pref.substring(t2 + 1); t2 = pref.toLowerCase().lastIndexOf(',http://'); if (t2 > -1) pref = pref.substring(t2 + 1); t2 = pref.toLowerCase().lastIndexOf(',https://'); if (t2 > -1) pref = pref.substring(t2 + 1); t2 = pref.toLowerCase().lastIndexOf(';http'); if (t2 > -1) pref = pref.substring(t2 + 1); t2 = pref.toLowerCase().lastIndexOf('*https://'); if (t2 > -1) pref = pref.substring(t2 + 1); t2 = pref.toLowerCase().lastIndexOf(' or '); if (t2 > -1) pref = pref.substring(t2 + 4); pref = pref.substring(pref.split('/')[0].toLowerCase().split('%2f')[0].lastIndexOf('=') + 1); } if (pref.length > 0) { if (pref.split('?')[0].toLowerCase().match(/%[2-3][0-9a-f]/)) { t2 = pref.indexOf('"') if (t2 > -1) pref = pref.substring(t2 + 1); suff = testPaths[k + 1] ? testPaths[k + 1].split('&')[0].split('"')[0].split(';')[0].split(/,http/i)[0] : ''; if ((suff != testPaths[k + 1]) || (testPaths.length < k + 3)) { if (testPaths.length > k + 1) { testPaths[k + 1] = ((pref == testPaths[k - 1]) ? '' : '&') + testPaths[k + 1].substr(suff.length) }; t2 = pref.lastIndexOf(lba); if (t2 > -1) pref = pref.substring(t2 + 3) linkArr.push(decodeURL(pref + testPaths[k] + suff)); } else { testPaths[k + 1] = (pref == testPaths[k - 1] ? '' : lbb) + pref + testPaths[k] + suff } } else { suff = testPaths[k + 1] ? testPaths[k + 1].split(';')[0].split('"]')[0].split('"}')[0].split('",')[0].split(/,https?:\/\//i)[0].split('*https://')[0].split(' or ')[0] : ''; t2 = suff.indexOf('&'); if ((t2 > -1) && (pref != testPaths[k - 1])) { if (t2 == 0) suff = ''; if (suff.charAt(0) != '?') suff = suff.split(/(&[^&]+=https?:\/\/)/i)[0]; }; if ((suff != testPaths[k + 1]) || (testPaths.length < k + 3)) { if (testPaths.length > k + 1) { testPaths[k + 1] = ((pref == testPaths[k - 1]) ? '' : '&') + testPaths[k + 1].substr(suff.length) }; t2 = pref.lastIndexOf(lba); if (t2 > -1) pref = pref.substring(t2 + 3); pushWithMerit(pref + testPaths[k] + suff); } else { testPaths[k + 1] = lba + (pref == testPaths[k - 1] ? '' : lbb) + pref + testPaths[k] + suff } } } }; jj = jj + 1; }; if (outLinks.length == 0) return ''; function srt(a, b) { a = parseInt(a.substr(0, a.indexOf(lba)), 10); b = parseInt(b.substr(0, b.indexOf(lba)), 10); if (a < b) return 1; if (a > b) return -1; return 0 }; outLinks.sort(srt); outLinks[0] = outLinks[0].substr(outLinks[0].indexOf(lba) + 3) if (outLinks[0].indexOf('_hq.mp4/?time=') > 0) outLinks[0] = outLinks[0].replace(/&/g, '&'); return outLinks[0]; }; if (!ol) return ''; //ol = ol.replace(/^:?\/\//, curLocation.protocol + "//"); //return restPath(ol, src); return restProtHost(ol, curLocation); }; try {handlWin(content);} finally {found || SEND();} }); var style = custombutton.buttonGetHelp(self).replace(/id/g, _id); var uri = makeURI('data:text/css,'+ encodeURIComponent(style)); var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService); sss.loadAndRegisterSheet(uri, 0);
Отредактировано manuk (10-05-2022 11:38:32)
Отсутствует
Т.е ЛКМ работало со строкой "args.push("/add");" а ПКМ без неё.
Самое простое — по тайминг-флагу.
… var rmb, reset = () => rmb = false; this.oncontextmenu = e => { if (e.ctrlKey || e.shiftKey || e.altKey) return; e.preventDefault(); //custombuttons.confirmBox(null, "Запустить плеер из буфера обмена ?", "Да", "Нет") // && run([gClipboard.read(), "/play"]); rmb = true; setTimeout(reset, 500); this._handleClick(); }
Отсутствует
Dumby, заменил на ваши строки своими кривыми ручонками, да не так, наверно. По ЛКМ и ПКМ одинаково добавляет в плейлист.
(func => { var sysPlayerName = "Pot Player"; var path = "R:\\PotPlayer\\PotPlayerMini.exe"; var videoMoved = "Видео перенесено в " + sysPlayerName; var noFound = "Не найдено видео на странице, доступное для переноса в " + sysPlayerName; this.label = "Открыть видео в " + sysPlayerName; this.tooltipText = "Л: Видео в плейлист"; this._handleClick = () => { var msgName = _id + ":Player"; var listener = ({data}) => data ? run([data]) : notify(); messageManager.addMessageListener(msgName, listener); addDestructor(() => messageManager.removeMessageListener(msgName, listener)); var url = "data:charset=utf-8," + encodeURIComponent( `(${func})()`.replace("MSG_NAME", msgName) .replace("VIDEO_MOVED", encodeURIComponent(videoMoved)) .replace("CONFIRM", encodeURIComponent("Открыть ссылку в плеере ?")) ); (this._handleClick = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))(); } var rmb, reset = () => rmb = false; this.oncontextmenu = e => { if (e.ctrlKey || e.shiftKey || e.altKey) return; e.preventDefault(); //custombuttons.confirmBox(null, "Запустить плеер из буфера обмена ?", "Да", "Нет") // && run([gClipboard.read(), "/play"]); rmb = true; setTimeout(reset, 500); this._handleClick(); } var popup = document.getElementById("contentAreaContextMenu"); addEventListener("popupshowing", { get hidden() { return !(gContextMenu.onLink || gContextMenu.onVideo || gContextMenu.onPlainTextLink); }, handleEvent() { if (this.hidden) return; var menuitem = document.createXULElement("menuitem"); for(var args of Object.entries({ image: self.image, oncommand: "play()", class: "menuitem-iconic", label: " В плейлист " + sysPlayerName })) menuitem.setAttribute(...args); menuitem.play = () => play(gContextMenu.linkURL || gContextMenu.mediaURL); document.getElementById("context-savelink").before(menuitem); addDestructor(() => menuitem.remove()); this.handleEvent = e => { if (e.target == popup) menuitem.hidden = this.hidden; } } }, false, popup || 1); var play = link => custombuttons.confirmBox(null, "Открыть ссылку в плеере ?", "Да", "Отмена") && run([link]); var run = args => { var file = FileUtils.File(path); (run = args => { if (!file.exists()) return custombuttons.alertBox("File not exists!", path); var process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess); process.init(file); //args.push("/add"); rmb || args.push("/add"); process.runwAsync(args, args.length); })(args); } var notify = () => { var name = _id + "-noFound"; var as = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService); (notify = () => setTimeout(as.closeAlert, 1150, name, as.showAlertNotification( "chrome://global/skin/icons/question-48.png", "", noFound, false, "", null, name )))(); } })(() => { var found, videoMoved, SEND = msg => { found = true; if (!msg || Cc["@mozilla.org/embedcomp/prompt-service;1"] .getService(Ci.nsIPromptService) .confirm(content, null, decodeURIComponent("CONFIRM")) ) { if (msg) videoMoved = decodeURIComponent("VIDEO_MOVED"); sendAsyncMessage("MSG_NAME", msg); } else return true; } var YoutubeID = /(?:youtube(?:-nocookie)?\.com\/(?:[^\/\n\s]+\/\S+\/|(?:v|e(?:mbed)?)\/|\S*?[?&]v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})(?:\W|$)/; var tmp = '', tmpp = '', innerA = '<div style="display:block!important;color:#00ff00!important;width:250px!important;font:bold 16px serif!important;z-index:999!important;opacity:1!important;visibility: visible!important;', innerB = 'left:5px!important;position:absolute!important;height:auto!important;box-sizing:border-box!important;padding:5px!important;margin:5px!important;', //stopPl = "javascript:(function(){v=document.getElementById('movie_player');if(v){v.stopVideo()}else{v=document.getElementsByTagName('video');if(v){v[0].src='';try{v[0].load()}catch(e){}};}})();", ytIMGouter = function (ytID) { return '<div width="100%"><br /><a target="_blank" href="https://www.youtube.com/watch?v=' + ytID + '"><img src="https://i.ytimg.com/vi/' + ytID + '/hqdefault.jpg"></a><br />' + innerA + 'background-color:black!important;position:relative!important;bottom:20px!important;"> ' + videoMoved + '</div><br /></div><br />' }, handlWin = function (currentWin) { tmp = ''; var elem = currentWin.document.getElementsByTagName('video'), currLoc = currentWin.location; if (elem.length > 0) { if (currLoc.hostname.indexOf('youtu') != -1 && (tmp = currLoc.toString().match(YoutubeID)) && tmp[1].length == 11) { if (SEND('https://www.youtube.com/watch?v=' + tmp[1])) return; videoMovedbox = currentWin.document.createElement('videoMoved'); videoMovedbox.innerHTML = innerA + innerB + 'top:-15px!important;"><b>' + videoMoved + '</b></div>'; //loadURI(stopPl); (function(d){var v=d.getElementById('movie_player');if(v){try{v.stopVideo()}catch{}} else{v=d.getElementsByTagName('video');if(v[0]){v[0].src='';try{v[0].load()}catch{}};}})(currentWin.document); currentWin.document.getElementById('eow-title').appendChild(videoMovedbox); return true; }; for (i = 0; i < elem.length; i++) { if (((tmp = getSrc(elem[i].parentNode, currLoc)) && tmp.length > 2) || (i == 0 && currentWin.document.body.innerHTML.substring(0, 7) == '<video ' && (tmp = currLoc.toString()))) { if (SEND(tmp)) return; videoMovedbox = currentWin.document.createElement('videoMoved'); videoMovedbox.innerHTML = innerA + innerB + 'top:20px!important;background-color:black!important;">' + videoMoved + '</div>'; if (currLoc.hostname == 'www.youtube.com') { elem[i].parentNode.parentNode.appendChild(videoMovedbox); } else { elem[i].parentNode.appendChild(videoMovedbox); }; elem[i].src = ''; try { elem[i].load() } catch (e) {}; return true; } } }; currentWin._elems = currentWin.document.getElementsByTagName('iframe'); if (currentWin._elems.length > 0) { for (currentWin._iCounter = 0; currentWin._iCounter < currentWin._elems.length; currentWin._iCounter++) { if ((currentWin._elems[currentWin._iCounter].src.indexOf('youtube.com') > -1) && (tmp = currentWin._elems[currentWin._iCounter].src.match(YoutubeID)) && (tmp[1].length == 11)) { if (SEND('https://www.youtube.com/watch?v=' + tmp[1])) return; currentWin._elems[currentWin._iCounter].outerHTML = ytIMGouter(tmp[1]); return true; }; if (currentWin._elems[currentWin._iCounter].clientWidth > 80 && currentWin._elems[currentWin._iCounter].clientHeight > 40 && handlWin(currentWin._elems[currentWin._iCounter].contentWindow)) return true; } }; elem = currentWin.document.getElementsByTagName('object'); currLoc = currentWin.location; if (elem.length == 0) { elem = currentWin.document.getElementsByTagName('embed') }; if (elem.length > 0) { for (i = 0; i < elem.length; i++) { if (elem[i].innerHTML.indexOf('youtu') != -1 && (tmp = elem[i].innerHTML.match(YoutubeID)) && tmp[1].length == 11) { if (SEND('https://www.youtube.com/watch?v=' + tmp[1])) return; elem[i].outerHTML = ytIMGouter(tmp[1]); return true; } else { if (elem[i].clientWidth > 80 && elem[i].clientHeight > 40) { if (((tmp = getSrc(elem[i].parentNode, currLoc)) || (tmp = getLink(elem[i], currLoc))) && tmp.length > 2) { if (SEND(tmp)) return; elem[i].outerHTML = innerA + 'background-color:black!important;bottom:20px!important;"> ' + videoMoved + '</div>'; return true; }; }; } }; }; return false; }; function restProtHost(lnkR, curLoc) { if (lnkR.length == 0) return ''; let tr = lnkR.replace(/^:\/\//, curLoc.protocol + "//"); if (!tr.match(/^https?:\/\//i)) { lnkR = tr.replace(/^\/+/, ''); if (lnkR.split('/')[0].split('?')[0].split('#')[0].toLowerCase().match(/^(?:[-a-z\d]+\.)+[a-z\d]{2,6}$/)) { tr = curLoc.protocol + '//' + lnkR; } else { tr = curLoc.protocol + '//' + curLoc.host + "/" + lnkR; } }; return tr; }; function getSrc(vobj, currentLoc) { var t = '', tt = ''; if ((((t = vobj.innerHTML.match(/<video.*?\ssrc=(?:(?:'([^']*)')|(?:"([^"]*)")|([^\s]*))/i)) && (t) && (tt = t[1] || t[2] || t[3]) && tt.indexOf('blob:') == -1) || ((t = vobj.innerHTML.match(/<source.*?\ssrc=(?:(?:'([^']*)')|(?:"([^"]*)")|([^\s]*)).*?\stype=['"]?video\//i)) && (t) && (tt = t[1] || t[2] || t[3]))) && tt.length > 2 && tt.indexOf('blob:') == -1) { if (tt.indexOf(".mp4/?") == -1) { tt = tt.replace(/&/g, "&") }; t = restProtHost(tt, currentLoc); return t; }; return ''; }; function getLink(obj, curLocation) { if (!obj || !obj.tagName) return ''; q = obj.tagName.toLowerCase(); var getParam = function (e, n) { var v = '', r = new RegExp('^(' + n + ')$', 'i'), param = e.getElementsByTagName('param'); for (var igp = 0, p; p = param[igp]; igp++) { if (p.hasAttribute('name') && p.getAttribute('name').match(r)) { v = p.getAttribute('value'); break }; }; return v; }; var restPath = function (f, s) { return (f.substring(0, 4) == 'http') ? f : s.replace(/[#?].*$/, '').replace(/[^\/]*$/, f) }; function videoLinkExtract(fl) { alert(fl); var linkArr = [], outLinks = [], jj = 0, lba = '', lbb = '', decodeURL = gBrowser.currentURI.spec; { try { return decodeURIComponent(s) } catch (e) { return unescape(s) } }; for (var ij = 0; ij < 3; ij++) { lba = lba + String.fromCharCode(parseInt((Math.random() * 15 + 1) + '', 10)); lbb = lbb + String.fromCharCode(parseInt((Math.random() * 15 + 16) + '', 10)); }; function pushWithMerit(lnk) { var merit = -11; if (lnk.match(/^https?:\/\//i)) merit = merit + 40; if (outLinks.length == 0) merit = merit + 1; if (lnk.match(/^\//)) merit = merit + 7; if (lnk.match(/^\/\//)) merit = merit + 30; if (lnk.match(/240p([^a-z]|$)/i)) merit = merit + 1; if (lnk.match(/[^a-z]240([^a-z0-9]|$)/i)) merit = merit + 1; if (lnk.match(/360p([^a-z]|$)/i)) merit = merit + 3; if (lnk.match(/[^a-z]360([^a-z0-9]|$)/i)) merit = merit + 3; if (lnk.match(/480p([^a-z]|$)/i)) merit = merit + 5; if (lnk.match(/[^a-z]480([^a-z0-9]|$)/i)) merit = merit + 5; if (lnk.match(/720p([^a-z]|$)/i)) merit = merit + 7; if (lnk.match(/[^a-z]720([^a-z0-9]|$)/i)) merit = merit + 7; if (lnk.match(/\.mp4([^a-z]|$)/i)) merit = merit + 8; if (lnk.match(/_hd([^a-z]|$)/i)) merit = merit + 6; if (lnk.match(/\.(jpg|xml)([^a-z]|$)/i)) merit = merit - 40; if (merit > 0) outLinks.push(merit + lba + lnk); Services.console.logStringMessage('merit:' + merit + ' lnk->' + lnk); }; linkArr.push(fl); while (linkArr.length > jj && jj < 30) { var testPaths = []; testPaths = linkArr[jj].split(/(\.(?:flv|mp4|m3u8))/i); if (testPaths[testPaths.length - 1] == '') testPaths.pop(); for (k = 1; k < testPaths.length; k = k + 2) { if (testPaths[k - 1].indexOf(lba) > -1) { pref = testPaths[k - 1]; } else { var testAboutDom = testPaths[k - 1].toLowerCase().split(/(https?:\/\/)/); if (testAboutDom[testAboutDom.length - 1] == '') testAboutDom.pop(); var pTest = testAboutDom[testAboutDom.length - 1].split(/(\?[^\?]*?&)/); if (pTest.length > 2) { pTest.pop(); pTest.pop(); }; testAboutDom[testAboutDom.length - 1] = pTest.join(''); pref = testPaths[k - 1].substring(testAboutDom.join('').lastIndexOf("&") + 1); }; t2 = pref.lastIndexOf(lbb); if (t2 > -1) { pref = pref.substring(t2 + 3); } else { t2 = pref.lastIndexOf('{"'); if (t2 > -1) pref = pref.substring(t2 + 2); t2 = pref.lastIndexOf('["'); if (t2 > -1) pref = pref.substring(t2 + 2); t2 = pref.lastIndexOf(',"'); if (t2 > -1) pref = pref.substring(t2 + 2); t2 = pref.toLowerCase().lastIndexOf('"http://'); if (t2 > -1) pref = pref.substring(t2 + 1); t2 = pref.toLowerCase().lastIndexOf('"https://'); if (t2 > -1) pref = pref.substring(t2 + 1); t2 = pref.toLowerCase().lastIndexOf(',http://'); if (t2 > -1) pref = pref.substring(t2 + 1); t2 = pref.toLowerCase().lastIndexOf(',https://'); if (t2 > -1) pref = pref.substring(t2 + 1); t2 = pref.toLowerCase().lastIndexOf(';http'); if (t2 > -1) pref = pref.substring(t2 + 1); t2 = pref.toLowerCase().lastIndexOf('*https://'); if (t2 > -1) pref = pref.substring(t2 + 1); t2 = pref.toLowerCase().lastIndexOf(' or '); if (t2 > -1) pref = pref.substring(t2 + 4); pref = pref.substring(pref.split('/')[0].toLowerCase().split('%2f')[0].lastIndexOf('=') + 1); } if (pref.length > 0) { if (pref.split('?')[0].toLowerCase().match(/%[2-3][0-9a-f]/)) { t2 = pref.indexOf('"') if (t2 > -1) pref = pref.substring(t2 + 1); suff = testPaths[k + 1] ? testPaths[k + 1].split('&')[0].split('"')[0].split(';')[0].split(/,http/i)[0] : ''; if ((suff != testPaths[k + 1]) || (testPaths.length < k + 3)) { if (testPaths.length > k + 1) { testPaths[k + 1] = ((pref == testPaths[k - 1]) ? '' : '&') + testPaths[k + 1].substr(suff.length) }; t2 = pref.lastIndexOf(lba); if (t2 > -1) pref = pref.substring(t2 + 3) linkArr.push(decodeURL(pref + testPaths[k] + suff)); } else { testPaths[k + 1] = (pref == testPaths[k - 1] ? '' : lbb) + pref + testPaths[k] + suff } } else { suff = testPaths[k + 1] ? testPaths[k + 1].split(';')[0].split('"]')[0].split('"}')[0].split('",')[0].split(/,https?:\/\//i)[0].split('*https://')[0].split(' or ')[0] : ''; t2 = suff.indexOf('&'); if ((t2 > -1) && (pref != testPaths[k - 1])) { if (t2 == 0) suff = ''; if (suff.charAt(0) != '?') suff = suff.split(/(&[^&]+=https?:\/\/)/i)[0]; }; if ((suff != testPaths[k + 1]) || (testPaths.length < k + 3)) { if (testPaths.length > k + 1) { testPaths[k + 1] = ((pref == testPaths[k - 1]) ? '' : '&') + testPaths[k + 1].substr(suff.length) }; t2 = pref.lastIndexOf(lba); if (t2 > -1) pref = pref.substring(t2 + 3); pushWithMerit(pref + testPaths[k] + suff); } else { testPaths[k + 1] = lba + (pref == testPaths[k - 1] ? '' : lbb) + pref + testPaths[k] + suff } } } }; jj = jj + 1; }; if (outLinks.length == 0) return ''; function srt(a, b) { a = parseInt(a.substr(0, a.indexOf(lba)), 10); b = parseInt(b.substr(0, b.indexOf(lba)), 10); if (a < b) return 1; if (a > b) return -1; return 0 }; outLinks.sort(srt); outLinks[0] = outLinks[0].substr(outLinks[0].indexOf(lba) + 3) if (outLinks[0].indexOf('_hq.mp4/?time=') > 0) outLinks[0] = outLinks[0].replace(/&/g, '&'); return outLinks[0]; }; if (!ol) return ''; //ol = ol.replace(/^:?\/\//, curLocation.protocol + "//"); //return restPath(ol, src); return restProtHost(ol, curLocation); }; try {handlWin(content);} finally {found || SEND();} }); var style = custombutton.buttonGetHelp(self).replace(/id/g, _id); var uri = makeURI('data:text/css,'+ encodeURIComponent(style)); var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService); sss.loadAndRegisterSheet(uri, 0);
Отсутствует