Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,143 @@
define(function(require) {
'use strict';
var ManifestHelper = require('shared/manifest_helper');
var AddonManager = require('modules/addon_manager');
const PREFERRED_ICON_SIZE = 64 * (window.devicePixelRatio || 1);
/**
* This module renders the information of an addon to the UI elements.
*
* @class AddonDetails
* @requires module:shared/manifest_helper
* @requires module:modules/addon_manager
* @returns {AddonDetails}
*/
function AddonDetails(elements) {
this._boundUpdateEnabledState = this._updateEnabledState.bind(this);
this._elements = elements;
this._curApp = null;
}
/**
* Update the enabled state of an addon.
*
* @access private
* @memberOf AddonDetails.prototype
* @param {Boolean} newState
*/
AddonDetails.prototype._updateEnabledState = function _update(newState) {
this._elements.enabledState
.setAttribute('data-l10n-id', newState ? 'enabled' : 'disabled');
};
/**
* Render details of an app to the UI elements.
*
* @access public
* @memberOf AddonDetails.prototype
* @param {Object} app Add-on to render
*/
AddonDetails.prototype.render = function render(app) {
if (this._curApp !== null) {
this._curApp.unobserve('enabled', this._boundUpdateEnabledState);
}
this._curApp = app;
if (this._curApp) {
this._curApp.observe('enabled', this._boundUpdateEnabledState);
this._updateEnabledState(this._curApp.enabled);
}
var l10n = document.l10n;
var manifest =
new ManifestHelper(app.instance.manifest || app.instance.updateManifest);
this.updateNames(manifest);
// Put an icon next to the description
navigator.mozApps.mgmt.getIcon(app, PREFERRED_ICON_SIZE).then((blob) => {
this._elements.icon.src = URL.createObjectURL(blob);
}).catch(() => {
this._elements.icon.src = '../style/images/default.png';
});
// Display the add-on version
if (manifest.version) {
this._elements.version.textContent = 'v' + manifest.version;
}
// Display the add-on description if there is one
if (manifest.description) {
// If we have a description, the ManifestHelper class returned the
// best localized version from the manifest file.
this._elements.description.removeAttribute('data-l10n-id');
this._elements.description.textContent = manifest.description;
} else {
// If there is no description, then display "no description" or similar.
this._elements.description
.setAttribute('data-l10n-id', 'addon-no-description');
}
// Display the add-on developer name if there is one
var developerName = manifest.developer && manifest.developer.name;
l10n.setAttributes(this._elements.developer, 'addon-developer-name', {
developerName: developerName || ''
});
// Old add-on format had 'customizations' field, it is now moved to
// 'content_scripts' field
var oldAddonFormat = !!manifest.customizations;
if (oldAddonFormat) {
l10n.setAttributes(this._elements.obsoleteStatusInfo,
AddonManager.canDelete(app) ? 'addon-obsolete-can-delete' :
'addon-obsolete');
}
this._elements.obsoleteStatus.hidden = !oldAddonFormat;
// Get the list of targeted apps
this._elements.targetsList.textContent = ''; // Clear any old content here
AddonManager.getAddonTargets(app).then((targets) => {
var names = [];
targets.forEach(function(target) {
var manifest =
new ManifestHelper(target.manifest || target.updateManifest);
names.push(manifest.displayName);
});
names.sort();
if (names.length < 1) {
this._elements.targetsList
.setAttribute('data-l10n-id', 'addon-no-targets');
} else {
this._elements.targetsList.textContent = names.join(', ');
}
});
};
/**
* Render app names where appropriate.
*
* @access public
* @memberOf AddonDetails.prototype
* @param {JSON} manifest
*/
AddonDetails.prototype.updateNames = function _updateNames(manifest) {
// Display the name of the add-on in the panel header
var appnameArgs = { appName: manifest.name };
var l10n = document.l10n;
l10n.setAttributes(
this._elements.header, 'addon-details-header1', appnameArgs);
l10n.setAttributes(
this._elements.name, 'addon-details-header1', appnameArgs);
// Put text description for an icon
l10n.setAttributes(
this._elements.icon, 'accessibility-app-icon', appnameArgs);
};
return function ctor_addon_details(panel) {
return new AddonDetails(panel);
};
});

View file

@ -0,0 +1,130 @@
define(function(require) {
'use strict';
var SettingsPanel = require('modules/settings_panel');
var AddonManager = require('modules/addon_manager');
var AddonDetails = require('panels/addon_details/addon_details');
var SettingsService = require('modules/settings_service');
var Toaster = require('shared/toaster');
return function ctor_addon_details_panel() {
return SettingsPanel({
onInit: function(panel) {
this._elements = {
body: panel.querySelector('.addon-details-body'),
header: panel.querySelector('.addon-details-header'),
gaiaHeader: panel.querySelector('gaia-header'),
enabledState: panel.querySelector('.addon-enabled details'),
name: panel.querySelector('.addon-details-name'),
icon: panel.querySelector('.addon-details-icon'),
version: panel.querySelector('.addon-details-version'),
description: panel.querySelector('.addon-description-text'),
developer: panel.querySelector('.addon-developer'),
targetsList: panel.querySelector('.addon-targets'),
toggle: panel.querySelector('.addon-enabled'),
deleteButton: panel.querySelector('.addon-delete'),
obsoleteStatus: panel.querySelector('.addon-obsolete-status'),
obsoleteStatusInfo: panel.querySelector('.addon-obsolete-info')
};
this._details = AddonDetails(this._elements);
this._boundOnAppEnabledChange = this._onAppEnabledChange.bind(this);
// Hook up the enable/disable toggle button
this._elements.toggle.onchange = this._onToggleChange.bind(this);
// Hook up the delete button
this._elements.deleteButton.onclick = this._onDelete.bind(this);
},
onBeforeShow: function(panel, options) {
return Promise.resolve((() => {
if (options.addon) {
return options.addon;
} else if (options.manifestURL) {
return AddonManager.findAddonByManifestURL(options.manifestURL);
}
})()).then((addon) => {
this._curAddon = addon;
// set initial state
if (this._curAddon) {
this._details.render(this._curAddon);
this._elements.toggle.checked =
AddonManager.isEnabled(this._curAddon);
this._curAddon.observe('enabled', this._boundOnAppEnabledChange);
this._elements.deleteButton.disabled =
!AddonManager.canDelete(this._curAddon);
this._elements.body.hidden = false;
} else {
console.error('No valid add-on');
this._elements.body.hidden = true;
}
this._elements.gaiaHeader.hidden = false;
});
},
onHide: function() {
// Scroll back to the top
this._elements.body.scrollTop = 0;
if (this._curAddon) {
this._curAddon.unobserve('enabled', this._boundOnAppEnabledChange);
this._curAddon = null;
}
},
_showStepsRequired(type) {
// If an addon injected a script then we have to restart any
// affected apps. And if those apps are system apps, then we
// need to reboot the device.
if (type === 'reboot') {
// XXX We could do a confirm dialog here asking the user if
// they want to go ahead and reboot the device now.
Toaster.showToast({
messageL10nId: 'addon-reboot-required',
latency: 3000,
useTransition: true
});
} else if (type === 'restart') {
Toaster.showToast({
messageL10nId: 'addon-restart-apps-to-disable',
latency: 3000,
useTransition: true
});
}
},
_onAppEnabledChange: function(enabled) {
this._elements.toggle.checked = enabled;
if (!enabled) {
AddonManager.getAddonDisableType(this._curAddon).then(
this._showStepsRequired);
}
},
_onToggleChange: function() {
if (this._elements.toggle.checked) {
AddonManager.enableAddon(this._curAddon);
} else {
AddonManager.disableAddon(this._curAddon);
}
},
_onDelete: function() {
this._elements.deleteButton.disabled = true; // don't delete it twice!
var disableType;
AddonManager.getAddonDisableType(this._curAddon).then(type => {
disableType = type;
return AddonManager.deleteAddon(this._curAddon);
}).then(() => {
this._showStepsRequired(disableType);
SettingsService.navigate('addons'); /* go back*/
}).catch(reason => {
console.error('Addon deletion failed:', reason);
// If the user cancelled deletion, we need to reenable the button
this._elements.deleteButton.disabled = false;
});
}
});
};
});