Release Vulpes OS 2.7-preview.24

This commit is contained in:
OverSu 2026-11-04 12:52:20 +01:00
commit 97ef99f827
135 changed files with 7717 additions and 2524 deletions

View file

@ -475,20 +475,25 @@
}
}
// Set the .icon property before saving for
// backwards compatibility with verticalhome
// Set the .icon property before saving for backwards compatibility with
// verticalhome. A missing favicon must not prevent pinning the site.
var saveSite = function(icon) {
siteObject.icon = icon || '';
BookmarksDatabase.put(siteObject, siteObject.id)
.then(() => {
this.app.debug('pinSite: ' + siteObject.id);
this.systemBanner.show('site-pinned-to-home-screen');
})
.catch((error) => {
console.error('pinSite, Failed to pin site: ' + error);
});
}.bind(this);
IconsHelper.getIcon(siteObject.url, null,
{icons: this.app.favicons}, siteObject).then(icon => {
siteObject.icon = icon;
BookmarksDatabase.put(siteObject, siteObject.id)
.then(() => {
this.app.debug('pinSite: ' + siteObject.id);
this.systemBanner.show('site-pinned-to-home-screen');
})
.catch((error) => {
console.error('pinSite, Failed to pin site: ' + error);
});
});
{icons: this.app.favicons}, siteObject).then(saveSite, function(error) {
this.app.debug('pinSite: using no favicon: ' + error);
saveSite('');
}.bind(this));
};
/**
@ -986,12 +991,14 @@
return;
}
// Enable/disable the bookmark option
BookmarksDatabase.get(this._currentURL).then(function resolve(result) {
// start_url is the bookmark key for installed web apps, while _currentURL
// may be a later page in the app. Check the canonical key first.
var siteUrl = this.getSiteUrl();
BookmarksDatabase.get(siteUrl).then(function resolve(result) {
this.addToHomeButton.hidden = !!result;
}.bind(this),
function reject() {
this.addToHomeButton.hidden = true;
this.addToHomeButton.hidden = false;
}.bind(this));
};

View file

@ -242,76 +242,29 @@
div.dataset.zIndexLevel = 'poweroff-splash';
div.id = 'poweroff-splash';
this.poweroffLogoLoader = new LogoLoader(this._poweroff);
// Vulpes uses Gaia's three-ring shutdown animation without a carrier
// image or video layered over it.
var inner;
for (var i = 1; i <= 3; i++) {
var outer = document.createElement('span');
outer.className = 'poweroff-ring';
outer.id = 'poweroff-ring-' + i;
div.appendChild(outer);
inner = document.createElement('span');
outer.appendChild(inner);
}
this.poweroffLogoLoader.onload = function(elem) {
// Perform customized animation.
div.appendChild(elem);
div.className = 'step1';
if (elem.tagName.toLowerCase() == 'video' && !elem.ended) {
elem.onended = function() {
elem.classList.add('hide');
// XXX workaround of bug 831747
// Unload the video. This releases the video decoding hardware
// so other apps can use it.
elem.removeAttribute('src');
elem.load();
};
elem.play();
} else {
div.addEventListener('animationend', function() {
elem.classList.add('hide');
if (elem.tagName.toLowerCase() == 'video') {
// XXX workaround of bug 831747
// Unload the video. This releases the video decoding hardware
// so other apps can use it.
elem.removeAttribute('src');
elem.load();
}
});
div.className = 'step1';
var nextAnimation = function nextAnimation(e) {
if (e.target == div) {
div.className = 'step2';
}
elem.addEventListener('transitionend', function() {
if (e.target == inner) {
self._actualPowerOff(reboot);
});
document.getElementById('screen').appendChild(div);
};
this.poweroffLogoLoader.onnotfound = function() {
// - Perform OS default animation.
// The overall animation ends when the inner span of the bottom ring
// is animated, so we store it for detecting.
var inner;
for (var i = 1; i <= 3; i++) {
var outer = document.createElement('span');
outer.className = 'poweroff-ring';
outer.id = 'poweroff-ring-' + i;
div.appendChild(outer);
inner = document.createElement('span');
outer.appendChild(inner);
}
div.className = 'step1';
var nextAnimation = function nextAnimation(e) {
// Switch to next class
if (e.target == div) {
div.className = 'step2';
}
if (e.target != inner) {
return;
}
self._actualPowerOff(reboot);
};
div.addEventListener('animationend', nextAnimation);
document.getElementById('screen').appendChild(div);
};
div.addEventListener('animationend', nextAnimation);
document.getElementById('screen').appendChild(div);
},
/**

View file

@ -265,7 +265,8 @@
// until it works. It may fail at the moment the screenchange
// event has been fired, so we may need to try it several times.
var tryLockOrientation = () => {
if (screen.mozLockOrientation('portrait-primary')) {
// Allow the lock screen to follow the physical display orientation.
if (screen.mozLockOrientation('any')) {
if (!this.orientationLockID) {
throw new Error('No orientation ID. This function should only' +
'be invoked as a interval callback');

View file

@ -346,13 +346,26 @@
case this.fullApp:
// XXX: This should be replaced probably by Web Activities
var host = document.location.host;
var domain = host.replace(/(^[\w\d]+\.)?([\w\d]+\.[a-z]+)/, '$2');
var protocol = document.location.protocol + '//';
applications.getByManifestURL(protocol + 'settings.' +
domain + '/manifest.webapp').launch();
UtilityTray.hide();
var settingsURL = new URL(document.location.href);
if (settingsURL.hostname.indexOf('system.') !== 0) {
console.error('Quick settings: cannot resolve Settings app host');
return;
}
settingsURL.hostname = 'settings.' +
settingsURL.hostname.slice('system.'.length);
settingsURL.pathname = '/manifest.webapp';
settingsURL.search = '';
settingsURL.hash = '';
applications.waitForReady().then(function() {
var settingsApp = applications.getByManifestURL(
settingsURL.href);
if (!settingsApp) {
console.error('Quick settings: Settings app is not installed');
return;
}
settingsApp.launch();
UtilityTray.hide();
});
break;
}
break;

View file

@ -187,6 +187,15 @@
},
hasPermission: function wf_hasPermission(app, permission) {
// The embedded Gaia browser is the trusted system web client. Newer
// Gecko builds no longer expose mozPermissionSettings to this code, so
// honour its declared window permission and keep popup navigation in
// Gaia instead of letting Gecko open a desktop browser window.
if (app && app.manifest && app.manifest.role === 'search' &&
permission === 'open-remote-window' && app.manifest.permissions &&
app.manifest.permissions['open-remote-window']) {
return true;
}
var mozPerms = navigator.mozPermissionSettings;
if (!mozPerms) {
return false;

View file

@ -70,7 +70,7 @@
text-overflow: ellipsis;
}
#app-install-dialog p {
font-size: 1.6rem;
font-size: 1.4rem;
font-weight: 400;
line-height: normal;
border-top: none;
@ -79,7 +79,7 @@
margin-top: 0;
}
#app-install-dialog span {
font-size: 1.6rem;
font-size: 1.4rem;
font-weight: 400;
line-height: normal;
color: inherit;

View file

@ -57,6 +57,6 @@
#os-logo { background:#071d35; display:flex; flex-direction:column; align-items:center; justify-content:center; }
#os-logo::before { content:""; width:min(42vw,190px); aspect-ratio:1; flex:none; background:url('/shared/resources/branding/vulpes-symbol.png') center / contain no-repeat; animation:vulpes-boot 2s ease-in-out infinite alternate; }
#os-logo::after { content:"VULPES\A Firefox OS Community Project"; white-space:pre-line; padding:12px 22px; color:#f5f8fa; font:500 20px/1.8 FiraSans,sans-serif; text-align:center; }
#poweroff-splash { background:#071d35 url('/shared/resources/branding/vulpes-symbol.png') center / 190px 190px no-repeat; }
#poweroff-splash { background:#071d35; }
@keyframes vulpes-boot { from {opacity:.75} to {opacity:1} }
@media (prefers-reduced-motion: reduce) { #os-logo::before {animation:none} }

View file

@ -11,7 +11,7 @@ requireApp('system/shared/test/unit/mocks/mock_navigator_moz_settings.js');
requireApp('system/shared/test/unit/mocks/mock_navigator_moz_power.js');
suite('CoverScreen >', function() {
var settingsCore, realSettings, subject, realMozPower;
var settingsCore, realSettings, subject, realMozPower, fakeScreen;
const DEFAULT_VIDEO_ON = '/resources/power/carrier_power_on.mp4';
const DEFAULT_VIDEO_OFF = '/resources/power/carrier_power_off.mp4';
const DEFAULT_IMAGE_ON = '/resources/power/carrier_power_on.png';
@ -113,6 +113,9 @@ suite('CoverScreen >', function() {
var fakeElement = document.createElement('div');
if (id === 'poweroff-splash') {
return null;
} else if (id === 'screen') {
fakeScreen = fakeElement;
return fakeScreen;
} else {
return fakeElement.cloneNode(true);
}
@ -135,10 +138,9 @@ suite('CoverScreen >', function() {
test('restart requested', function() {
var stub = this.sinon.stub(navigator.mozPower, 'reboot');
subject.poweroff(true);
var element = document.createElement('div');
var transitionStub = this.sinon.stub(element, 'addEventListener');
subject.poweroffLogoLoader.onload(element);
transitionStub.getCall(0).args[1]();
fakeScreen.lastChild.dispatchEvent(new Event('animationend'));
fakeScreen.lastChild.querySelector('#poweroff-ring-3 > span')
.dispatchEvent(new Event('animationend', { bubbles: true }));
assert.ok(stub.calledOnce);
});
@ -151,11 +153,9 @@ suite('CoverScreen >', function() {
test('poweroff requested', function() {
var stub = this.sinon.stub(navigator.mozPower, 'powerOff');
subject.poweroff(false);
var element = document.createElement('div');
var transitionStub = this.sinon.stub(element, 'addEventListener');
subject.poweroffLogoLoader.onload(element);
transitionStub.getCall(0).args[1]();
fakeScreen.lastChild.dispatchEvent(new Event('animationend'));
fakeScreen.lastChild.querySelector('#poweroff-ring-3 > span')
.dispatchEvent(new Event('animationend', { bubbles: true }));
assert.ok(stub.calledOnce);
});