Release Vulpes OS 2.7-preview.22

This commit is contained in:
OverSu 2026-10-22 20:42:26 +02:00
commit a138f97877
294 changed files with 515786 additions and 187 deletions

View file

@ -1052,6 +1052,20 @@ function getImageSize(blob, callback, error) {
else {
metadata.type = 'jpeg';
}
// Modern Gecko decodes EXIF orientation itself, including
// canvas.drawImage(). Gaia must not rotate those pixels twice.
if (metadata.rotation || metadata.mirrored) {
if (metadata.rotation === 90 || metadata.rotation === 270) {
var width = metadata.width;
metadata.width = metadata.height;
metadata.height = width;
}
metadata.rotation = 0;
metadata.mirrored = false;
// Embedded thumbnails need not carry the parent's EXIF tag.
// Generate a matching preview from the oriented full image.
delete metadata.preview;
}
callback(metadata);
},
error);

View file

@ -0,0 +1,351 @@
/**
* WifiUtils is a utils-box that keeps wifi-operations needed utils.
*
* @module WifiUtils
*/
define(function(require) {
'use strict';
var WifiHelper = require('shared/wifi_helper');
var wifiManager = WifiHelper.getWifiManager();
var WifiUtils = {
/**
* Create an explanatory list item
*
* @memberOf WifiUtils
* @param {String} message
* @returns {HTMLLIElement}
*/
newExplanationItem: function(message) {
var li = document.createElement('li');
li.className = 'explanation';
li.setAttribute('data-l10n-id', message);
return li;
},
/**
* Create a network list item
*
* @memberOf WifiUtils
* @param {Object} options
* @returns {HTMLLIElement}
*/
newListItem: function(options) {
/**
* A Wi-Fi list item has the following HTML structure:
* <li>
* <aside class="pack-end wifi-icon level-[?] [secured]"></aside>
* <a>
* <span> Network SSID </span>
* <small> Network Security </small>
* </a>
* </li>
*/
var network = options.network;
var showNotInRange = options.showNotInRange || false;
var onClick = options.onClick || function() {};
// icon
var icon = document.createElement('aside');
icon.classList.add('pack-end');
icon.classList.add('wifi-icon');
var level = Math.min(Math.floor(network.relSignalStrength / 20), 4);
icon.classList.add('level-' + level);
// ssid
var ssid = document.createElement('span');
ssid.dir = 'auto';
ssid.textContent = network.ssid;
// supported authentication methods
var small = document.createElement('small');
var keys = WifiHelper.getSecurity(network);
var networkNotInRange = (network.known && level === 0);
var hasSecurity = (keys && keys.length);
if (hasSecurity) {
if (showNotInRange && networkNotInRange) {
small.setAttribute('data-l10n-id', 'notInRange');
} else {
document.l10n.setAttributes(small, 'securedBy',
{ capabilities: keys.join(', ') });
}
icon.classList.add('secured');
} else {
if (showNotInRange && networkNotInRange) {
small.setAttribute('data-l10n-id', 'notInRange');
} else {
small.setAttribute('data-l10n-id', 'securityOpen');
}
}
var a = document.createElement('a');
a.appendChild(ssid);
a.appendChild(small);
// create list item
var li = document.createElement('li');
li.dataset.ssid = network.ssid;
li.appendChild(icon);
li.appendChild(a);
// Show connection status
icon.classList.add('wifi-signal');
var networkStatus = WifiHelper.getNetworkStatus(network);
var kActiveStatuses = ['associated', 'connecting', 'connected'];
if (-1 !== kActiveStatuses.indexOf(networkStatus)) {
li.classList.add('active');
if ('connected' === networkStatus) {
icon.classList.add('connected');
} else {
icon.classList.add('connecting');
}
small.setAttribute('data-l10n-id', 'short-status-' + networkStatus);
}
// bind connection callback
li.onclick = function() {
onClick(network);
};
return li;
},
/**
* Change dialog layout based on dialogId and security
*
* @memberOf WifiUtils
* @param {HTMLElement} panel
* @param {String} security
*/
changeDisplay: function(panel, security) {
var eap = panel.querySelector('li.eap select');
var identity = panel.querySelector('input[name=identity]');
var password = panel.querySelector('input[name=password]');
var authPhase2 = panel.querySelector('li.auth-phase2 select');
var certificate = panel.querySelector('li.server-certificate select');
var description =
panel.querySelector('li.server-certificate-description');
if (security === 'WEP' || security === 'WPA-PSK') {
identity.parentNode.style.display = 'none';
password.parentNode.style.display = 'block';
authPhase2.parentNode.parentNode.style.display = 'none';
certificate.parentNode.parentNode.style.display = 'none';
description.style.display = 'none';
} else if (security === 'WPA-EAP') {
if (eap) {
switch (eap.value) {
case 'SIM':
identity.parentNode.style.display = 'none';
password.parentNode.style.display = 'none';
authPhase2.parentNode.parentNode.style.display = 'none';
certificate.parentNode.parentNode.style.display = 'none';
description.style.display = 'none';
break;
case 'PEAP':
case 'TLS':
case 'TTLS':
identity.parentNode.style.display = 'block';
password.parentNode.style.display = 'block';
authPhase2.parentNode.parentNode.style.display = 'block';
certificate.parentNode.parentNode.style.display = 'block';
description.style.display = 'block';
break;
default:
break;
}
}
} else {
identity.parentNode.style.display = 'none';
password.parentNode.style.display = 'none';
}
},
/**
* This is used to help us do some initialization works if the panel
* is auth related.
*
* @memberOf WifiUtils
* @param {String} panel
* @param {Object} network
*/
initializeAuthFields: function(panel, network) {
var ssid = panel.querySelector('input[name=ssid]');
var identity = panel.querySelector('input[name=identity]');
var password = panel.querySelector('input[name=password]');
var showPassword = panel.querySelector('gaia-checkbox[name=show-pwd]');
var eap = panel.querySelector('li.eap select');
var certificate = panel.querySelector('li.server-certificate select');
var submitButton = panel.querySelector('button[type=submit]');
// load needed certificates first
this.loadImportedCertificateOptions(certificate);
identity.value = network.identity || '';
password.type = 'password';
password.value = network.password || '';
showPassword.checked = false;
showPassword.onchange = function() {
password.type = this.checked ? 'text' : 'password';
};
var checkPassword = function() {
var key = WifiHelper.getKeyManagement(network);
var isDisabled = !WifiHelper.isValidInput(
key, password.value, identity.value, eap.value);
if (ssid) {
isDisabled = isDisabled || !WifiHelper.isSSIDValid(ssid.value);
}
submitButton.disabled = isDisabled;
};
eap.onchange = function() {
var key = WifiHelper.getKeyManagement(network);
checkPassword();
WifiUtils.changeDisplay(panel, key);
};
if (ssid) {
ssid.oninput = checkPassword;
}
password.oninput = checkPassword;
identity.oninput = checkPassword;
checkPassword();
},
/**
* This is an inner function that used to inject certificates options
* into select element.
*
* @memberOf WifiUtils
* @param {HTMLSelectElement} select
*/
loadImportedCertificateOptions: function(select) {
if (!wifiManager.getImportedCerts) {
return;
}
var certRequest = wifiManager.getImportedCerts();
certRequest.onsuccess = function() {
var i;
var certList = certRequest.result;
// save the imported server certificates
var certificateList = certList.ServerCert;
// reset the option to be <option value="none">--</option> only
var originLengthOfOptions = select.options.length;
for (i = 0; i < originLengthOfOptions - 1; i++) {
select.remove(1);
}
for (i = 0; i < certificateList.length; i++) {
var option = document.createElement('option');
option.text = certificateList[i];
option.value = certificateList[i];
select.add(option, null);
}
};
certRequest.onerror = function() {
console.warn('getImportedCerts failed');
};
},
/**
* Updates the icon of the given network
*
* @memberOf WifiUtils
* @param {Object} network
* @param {Integer} networkSignal
*/
updateNetworkSignal: function(network, networkSignal) {
if (!network) { return; }
var items = document.querySelectorAll('li[data-ssid]');
var li = Array.prototype.find.call(items, function(item) {
return item.dataset.ssid === network.ssid;
});
// Signal updates can arrive before the scan has populated the list.
var icon = li && li.querySelector('aside');
if (!icon) { return; }
// Clean previous state
icon.className = icon.className.replace(/level-\w*/, '');
var level = Math.min(Math.floor(networkSignal / 20), 4);
icon.classList.add('level-' + level);
},
/**
* Get concated networkKey which can be used as identifier
*
* @memberOf WifiUtils
* @param {Object} network
* @return {String} concated network identifier
*/
getNetworkKey: function(network) {
if (!network) {
return '';
} else {
var key =
network.ssid + '+' + WifiHelper.getSecurity(network).join('+');
return key;
}
},
/**
* Reflect incoming network status on related listItem (show different UI)
*
* @memberOf WifiUtils
* @param {Object} options
* @param {Object} options.listItems - listItems with DOM elements
* @param {Object} options.activeItemDOM - DOM element for active item
* @param {Object} options.network - network object
* @param {Object} options.networkStatus - current status for network
*/
updateListItemStatus: function(options) {
options = options || {};
var listItems = options.listItems;
var activeItemDOM = options.activeItemDOM;
var network = options.network;
var networkStatus = options.networkStatus;
if (!network || !networkStatus || !listItems) {
console.log('Please check passing options for updateListItemStatus');
return;
}
var key = this.getNetworkKey(network);
var listItemDOM = listItems[key];
if (activeItemDOM && activeItemDOM != listItemDOM) {
activeItemDOM.classList.remove('active');
activeItemDOM.querySelector('small').
setAttribute('data-l10n-id', 'short-status-disconnected');
activeItemDOM.querySelector('aside').classList.remove('connecting');
activeItemDOM.querySelector('aside').classList.remove('connected');
}
if (listItemDOM) {
listItemDOM.classList.add('active');
listItemDOM.querySelector('small').
setAttribute('data-l10n-id', 'short-status-' + networkStatus);
if (networkStatus === 'connecting' || networkStatus === 'associated') {
listItemDOM.querySelector('aside').classList.remove('connected');
listItemDOM.querySelector('aside').classList.add('connecting');
} else if (networkStatus === 'connected') {
listItemDOM.querySelector('aside').classList.remove('connecting');
listItemDOM.querySelector('aside').classList.add('connected');
}
}
}
};
return WifiUtils;
});

View file

@ -0,0 +1,119 @@
/* exported getImageSize */
/* global BlobView */
/* global parseJPEGMetadata */
/*
* Determine the pixel dimensions of an image without actually
* decoding the image. Passes an object of metadata to the callback
* function on success or an error message to the error function on
* failure. The metadata object will include type, width and height
* properties. Supported image types are GIF, PNG and JPEG. JPEG
* metadata may also include information about an EXIF preview image.
*
* Because of shortcomings in the way Gecko handles images, the
* Gallery app will crash with an OOM error if it attempts to decode
* and display an image that is too big. Images require 4 bytes per
* pixel, so a 10 megapixel photograph requires 40 megabytes of image
* memory. This function gives the gallery app a way to reject images
* that are too large.
*
* Requires the BlobView class from shared/js/blobview.js and the
* parseJPEGMetadata() function from shared/js/media/jpeg_metadata_parser.js
*/
function getImageSize(blob, callback, error) {
'use strict';
BlobView.get(blob, 0, Math.min(1024, blob.size), function(data) {
// Make sure we are at least 8 bytes long before reading the first 8 bytes
if (data.byteLength <= 8) {
error('corrupt image file');
return;
}
var magic = data.getBinaryText(0, 8);
if (magic.substring(0, 4) === 'GIF8') {
try {
callback({
type: 'gif',
width: data.getUint16(6, true),
height: data.getUint16(8, true)
});
}
catch (e) {
error(e.toString());
}
}
else if (magic.substring(0, 8) === '\x89PNG\r\n\x1A\n') {
try {
callback({
type: 'png',
width: data.getUint32(16, false),
height: data.getUint32(20, false)
});
}
catch (e) {
error(e.toString());
}
}
else if (magic.substring(0, 2) === 'BM' &&
data.getUint32(2, true) === blob.size) {
// This is a BMP file
try {
var width, height;
if (data.getUint16(14, true) === 12) { // check format version
width = data.getUint16(18, true); // 16-bit little endian width
height = data.getUint16(20, true); // 16-bit little endian height
}
else { // newer versions of the format use 32-bit ints
width = data.getUint32(18, true); // 32-bit little endian width
height = data.getUint32(22, true); // 32-bit little endian height
}
callback({
type: 'bmp',
width: width,
height: height
});
}
catch (e) {
error(e.toString());
}
}
else if (magic.substring(0, 2) === '\xFF\xD8') {
parseJPEGMetadata(blob,
function(metadata) {
if (metadata.progressive) {
// If the jpeg parser tells us that this is
// a progressive jpeg, then treat that as a
// distinct image type because pjpegs have
// such different memory requirements than
// regular jpegs.
delete metadata.progressive;
metadata.type = 'pjpeg';
}
else {
metadata.type = 'jpeg';
}
// Modern Gecko decodes EXIF orientation itself, including
// canvas.drawImage(). Gaia must not rotate those pixels twice.
if (metadata.rotation || metadata.mirrored) {
if (metadata.rotation === 90 || metadata.rotation === 270) {
var width = metadata.width;
metadata.width = metadata.height;
metadata.height = width;
}
metadata.rotation = 0;
metadata.mirrored = false;
// Embedded thumbnails need not carry the parent's EXIF tag.
// Generate a matching preview from the oriented full image.
delete metadata.preview;
}
callback(metadata);
},
error);
}
else {
error('unknown image type');
}
});
}

View file

@ -0,0 +1,79 @@
{
"search_providers": {
"yahoo": {
"title": "Yahoo",
"search": {
"url": "https://search.yahoo.com/yhs/search",
"params": {
"p": "{searchTerms}"
}
},
"suggest": {
"url": "https://search.yahoo.com/sugg/ff",
"params": {
"command": "{searchTerms}",
"output": "fxjson"
}
}
},
"ddg": {
"title": "DuckDuckGo",
"search": {
"url": "https://duckduckgo.com/",
"params": {
"q": "{searchTerms}"
}
},
"suggest": {
"url": "https://ac.duckduckgo.com/ac/",
"params": {
"q": "{searchTerms}",
"type": "list"
}
}
},
"google": {
"title": "Google",
"search": {
"url": "https://www.google.com/search",
"params": {
"q": "{searchTerms}"
}
},
"suggest": {
"url": "https://www.google.com/complete/search",
"params": {
"q": "{searchTerms}",
"client": "firefox"
}
}
},
"bing": {
"title": "Bing",
"search": {
"url": "https://www.bing.com/search",
"params": {
"q": "{searchTerms}"
}
},
"suggest": {
"url": "https://www.bing.com/osjson.aspx",
"params": {
"query": "{searchTerms}"
}
}
}
},
"simConfigs": {},
"partnerConfig": {},
"defaultEngines": {
"defaultEngine": "ddg",
"providers": {
"yahoo": {},
"google": {},
"bing": {},
"ddg": {}
}
},
"locales": {}
}

View file

@ -1191,6 +1191,18 @@
ModalDialog.selectOne(title, options, selected);
};
AppChrome.prototype.onShare = function ac_onShare() {
this.hideOverflowMenu();
var url = this._currentURL;
if (!url || !/^https?:\/\//i.test(url)) {
return;
}
return new MozActivity({
name: 'share',
data: { type: 'url', url: url }
});
};
AppChrome.prototype.showWindows = function ac_showWindows() {
window.dispatchEvent(
new CustomEvent('taskmanagershow',

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 830 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB