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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,42 @@
(async()=>{
const frame=document.getElementById('catalogue'),message=document.getElementById('message'),dialog=document.getElementById('confirm');
let attempt=0,busy=false,prepared=null,installed=null,language=await VulpesCompat.call('locale.current');
const fr=()=>/^fr(?:-|$)/i.test(language);
const text=(f,e)=>fr()?f:e;
function labels(){document.getElementById('browse').textContent=text('Ouvrir le catalogue','Open catalogue');document.getElementById('open').textContent=text('Ouvrir','Open');document.getElementById('install').textContent=text('Installer','Install');document.getElementById('cancel').textContent=text('Annuler','Cancel');}
function loadFailed(){status(text('Catalogue inaccessible. Vérifiez la connexion Internet, puis réessayez.','Catalogue unavailable. Check your Internet connection, then try again.'));document.getElementById('open').hidden=true;}
frame.addEventListener('mozbrowsererror',loadFailed);
frame.addEventListener('mozbrowserclose',()=>{
if(busy)return;
status(text('Le catalogue est fermé. Vous pouvez le rouvrir ci-dessous.',
'The catalogue is closed. You can reopen it below.'));
document.getElementById('open').hidden=true;
});
let catalogueUrl='';
function browse(reload=false){labels();message.hidden=true;dialog.hidden=true;frame.style.visibility='visible';const next='https://vulpes-os.org/marketplace/?mode=app&lang='+(fr()?'fr':'en');if(reload||!catalogueUrl){catalogueUrl=next;frame.src=next;}}
function status(value){frame.style.visibility='hidden';message.hidden=false;document.getElementById('status').textContent=value;}
async function prepare(url){
if(busy)return;const current=++attempt;busy=true;prepared=null;installed=null;document.getElementById('open').hidden=true;
status(text('Vérification de l’application…','Checking application…'));
try {const value=await VulpesCompat.call('marketplace.prepare',{url});if(current!==attempt)return;prepared=value;
document.getElementById('title').textContent=prepared.name;
document.getElementById('question').textContent=text('Installer cette application archivée ?','Install this archived application?')+' ('+(prepared.bytes/1048576).toFixed(1)+' Mio)';
document.getElementById('permissions').textContent=text('Elle sera exécutée comme une application web, sans accès aux fonctions système. Certains anciens services peuvent ne plus fonctionner.','It will run as a web application without system access. Some old services may no longer work.');
message.hidden=true;dialog.hidden=false;
}catch(error){if(current!==attempt)return;status(error.message==='PRIVILEGED_APP_UNSUPPORTED'?
text('Cette application demande des API privilégiées qui ne sont pas disponibles dans Vulpes OS.',
'This app requires privileged APIs that are not available in Vulpes OS.'):
text('Installation impossible : ','Cannot install: ')+error.message);busy=false;}
}
dialog.addEventListener('submit',async event=>{event.preventDefault();if(!prepared)return;const token=prepared.token;prepared=null;dialog.hidden=true;status(text('Installation…','Installing…'));
try {installed=await VulpesCompat.call('marketplace.install',{token});status(text('Application ajoutée à l’accueil.','Application added to Home.'));document.getElementById('open').hidden=false;}
catch(error){status(text('Installation impossible : ','Cannot install: ')+error.message);}finally{busy=false;}
});
const cancel=()=>{attempt++;prepared=null;busy=false;VulpesCompat.call('marketplace.cancel').catch(console.error);browse();};
document.getElementById('cancel').onclick=cancel;
document.getElementById('browse').onclick=cancel;
document.getElementById('open').onclick=()=>installed && VulpesCompat.call('apps.launch',{manifestURL:installed.manifestURL});
addEventListener('languagechange',()=>{language=navigator.language;if(!busy)browse(true);});
browse();
setInterval(async()=>{if(busy)return;try{const url=await VulpesCompat.call('marketplace.poll');if(url)await prepare(url);}catch(error){console.error(error);}},750);
})();

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -0,0 +1,366 @@
define('modules/navigator/mozPermissionSettings',[],function(){'use strict';return window.navigator.mozPermissionSettings;});define('panels/app_permissions_list/app_permissions_list',["require", "modules/settings_service", "shared/manifest_helper", "modules/apps_cache", "modules/navigator/mozApps", "modules/navigator/mozPermissionSettings"],function(require) {
'use strict';
var SettingsService = require('modules/settings_service');
var ManifestHelper = require('shared/manifest_helper');
var AppsCache = require('modules/apps_cache');
var mozApps = require('modules/navigator/mozApps');
var mozPerms = require('modules/navigator/mozPermissionSettings');
var PermissionsList = function pl() {
this._listRoot = null;
this._permissionsTable = null;
this._permissionTableHaveProcessed = false;
this._apps = null;
this._enabled = false;
};
PermissionsList.prototype = {
/**
* initialization
*
* @memberOf PermissionsList
* @param {HTMLElement} listRoot
* @access public
*/
init: function pl_init(listRoot) {
this._listRoot = listRoot;
this._boundOnAppChoose = this._onAppChoose.bind(this);
this._boundOnApplicationInstall = this._onApplicationInstall.bind(this);
this._boundOnApplicationUninstall =
this._onApplicationUninstall.bind(this);
},
set enabled(value) {
if (value !== this._enabled) {
this._enabled = value;
if (this._enabled) {
this._bindEvents();
} else {
this._unbindEvents();
}
}
},
_bindEvents: function pl__bindEvents() {
this._listRoot.addEventListener('click', this._boundOnAppChoose);
AppsCache.addEventListener('oninstall', this._boundOnApplicationInstall);
AppsCache.addEventListener('onuninstall',
this._boundOnApplicationUninstall);
},
_unbindEvents: function pl__unbindEvents() {
this._listRoot.removeEventListener('click', this._boundOnAppChoose);
AppsCache.removeEventListener('oninstall',
this._boundOnApplicationInstall);
AppsCache.removeEventListener('onuninstall',
this._boundOnApplicationUninstall);
},
/**
* Set /resources/permissions_table.json.
*
* @memberOf PermissionsList
* @param {Object} permissionTable
* @access public
*/
setPermissionsTable: function pl_setPermissionsTable(permissionTable) {
this._permissionTable = permissionTable;
},
/**
* Refresh the app list when we enter into panel.
*
* @memberOf PermissionsList
* @access public
* @return {Promise}
*/
refresh: function pl_refresh() {
var self = this;
this._apps = [];
if (this._permissionTableHaveProcessed) {
return this.loadApps();
} else {
return mozApps.getSelf().then(function(app) {
return self._initExplicitPermissionsTable(app);
}).then(function() {
return self.loadApps();
});
}
},
/**
* Go to app_permissions_detail panel when user select an app.
*
* @memberOf PermissionsList
* @param {Event} evt
* @access public
*/
_onAppChoose: function pl__onAppChoose(evt) {
var target = evt.target.closest('[data-app-index]');
if (target && this._listRoot.contains(target)) {
SettingsService.navigate('appPermissions-details', {
app: this._apps[target.dataset.appIndex],
permissionsTable: this._permissionTable
});
}
},
/**
* When new application is installed, we push the app to list, sort them and
* rerender the app list.
*
* @memberOf PermissionsList
* @param {Event} evt
* @access public
*/
_onApplicationInstall: function pl__onApplicationInstall(evt) {
var app = evt.application;
this._apps.push(app);
this._sortApps();
this.renderList();
},
/**
* When application is uninstalled, we remove it from list and rerender the
* app list.
*
* @memberOf PermissionsList
* @param {Event} evt
* @access public
*/
_onApplicationUninstall: function pl__onApplicationUninstall(evt) {
var app;
var appIndex;
this._apps.some(function findApp(anApp, index) {
if (anApp.origin === evt.application.origin) {
app = anApp;
appIndex = index;
return true;
}
return false;
});
if (!app) {
return;
}
SettingsService.navigate('appPermissions');
this._apps.splice(appIndex, 1);
this.renderList();
},
/**
* Sort the applist by the name of its manifest.
*
* @memberOf PermissionsList
* @access private
*/
_sortApps: function pl__sortApps() {
this._apps.sort(function alphabeticalSort(app, otherApp) {
var manifest = new ManifestHelper(app.manifest ?
app.manifest : app.updateManifest);
var otherManifest = new ManifestHelper(otherApp.manifest ?
otherApp.manifest : otherApp.updateManifest);
return manifest.name > otherManifest.name;
});
},
/**
* Genrate UI template of app item.
*
* @memberOf PermissionsList
* @access private
* @param {Object} itemData
* @return {HTMLDivElement}
*/
_genAppItemTemplate: function pl__genAppItemTemplate(itemData) {
var icon = document.createElement('img');
var item = document.createElement('li');
var link = document.createElement('a');
var span = document.createElement('span');
span.textContent = itemData.name;
span.dir = 'auto';
icon.src = itemData.iconSrc;
link.dataset.appIndex = itemData.index;
link.href = '#';
link.classList.add('menu-item');
link.appendChild(icon);
link.appendChild(span);
item.appendChild(link);
return item;
},
/**
* Genrate UI template of app item.
*
* @memberOf PermissionsList
* @access public
*/
renderList: function pl_renderList() {
this._listRoot.innerHTML = '';
var listFragment = document.createDocumentFragment();
this._apps.forEach(function appIterator(app, index) {
var manifest = new ManifestHelper(app.manifest ?
app.manifest : app.updateManifest);
var li = this._genAppItemTemplate({
name: manifest.displayName,
index: index,
iconSrc: this._getBestIconURL(app, manifest.icons)
});
listFragment.appendChild(li);
}.bind(this));
this._listRoot.appendChild(listFragment);
},
/**
* Genrate explicitCertifiedPermissions table from plainPermissions table
* table and "composedPermissions + accessModes" table.
*
* @memberOf PermissionsList
* @param {Event} evt
* @access private
*/
_initExplicitPermissionsTable:
function pl__initExplicitPermissionsTable(app) {
this._currentApp = app;
var table = this._permissionTable;
table.explicitCertifiedPermissions = [];
table.plainPermissions.forEach(function plainPermIterator(perm) {
if (this._isExplicitPerm(perm)) {
table.explicitCertifiedPermissions.push({
explicitPermission: perm,
permission: perm
});
}
}.bind(this));
table.composedPermissions.forEach(function permIterator(perm) {
table.accessModes.forEach(function modeIterator(mode) {
var composedPerm = perm + '-' + mode;
if (this._isExplicitPerm(composedPerm)) {
table.explicitCertifiedPermissions.push({
explicitPermission: composedPerm,
permission: perm
});
}
}.bind(this));
}.bind(this));
this._permissionTableHaveProcessed = true;
},
/**
* Identify the permission whether is explict or not.
*
* @memberOf PermissionsList
* @access private
* @return {Bool}
*/
_isExplicitPerm: function pl_isExplicitPerm(perm) {
return mozPerms && mozPerms.isExplicit(perm, this._currentApp.manifestURL,
this._currentApp.origin, false);
},
/**
* Filter explicit apps from moz apps, sort them, and render to screen.
*
* @memberOf PermissionsList
* @access public
* @return {Promise}
*/
loadApps: function pl_loadApps() {
var self = this;
return AppsCache.apps().then(function(apps) {
self._loadApps(apps);
});
},
/**
* Iterate internal apps and render them on UI.
*
* @memberOf PermissionsList
* @param {Object[]} apps
* @access private
*/
_loadApps: function pl__loadApps(apps) {
var table = this._permissionTable;
var internalRoles = ['system', 'input', 'homescreen', 'search', 'addon', 'langpack'];
apps.forEach(function(app) {
var manifest = app.manifest ? app.manifest : app.updateManifest;
if (manifest.role && internalRoles.indexOf(manifest.role) !== -1) {
return;
}
if (manifest.type != 'certified') {
this._apps.push(app);
return;
}
var display = table.explicitCertifiedPermissions
.some(function iterator(perm) {
var permInfo = mozPerms.get(perm.explicitPermission,
app.manifestURL, app.origin, false);
return permInfo != 'unknown';
}.bind(this));
if (display) {
this._apps.push(app);
}
}.bind(this));
this._sortApps();
this.renderList();
},
/**
* Get icon URL.
*
* @memberOf PermissionsList
* @param {Object} app
* @param {Object} icons
* @access private
*/
_getBestIconURL: function pl__getBestIconURL(app, icons) {
if (!icons || !Object.keys(icons).length) {
return '../style/images/default.png';
}
// The preferred size is 30 by the default. If we use HDPI device, we may
// use the image larger than 30 * 1.5 = 45 pixels.
var preferredIconSize = 30 * (window.devicePixelRatio || 1);
var preferredSize = Number.MAX_VALUE;
var max = 0;
for (var size in icons) {
size = parseInt(size, 10);
if (size > max) {
max = size;
}
if (size >= preferredIconSize && size < preferredSize) {
preferredSize = size;
}
}
// If there is an icon matching the preferred size, we return the result,
// if there isn't, we will return the maximum available size.
if (preferredSize === Number.MAX_VALUE) {
preferredSize = max;
}
var url = icons[preferredSize];
if (url) {
return !(/^(http|https|data):/.test(url)) ? app.origin + url : url;
} else {
return '../style/images/default.png';
}
}
};
return function ctor_permissions_list() {
return new PermissionsList();
};
});
define('panels/app_permissions_list/panel',['require','modules/settings_panel','panels/app_permissions_list/app_permissions_list','shared/lazy_loader'],function(require){'use strict';var SettingsPanel=require('modules/settings_panel');var PermissionList=require('panels/app_permissions_list/app_permissions_list');var LazyLoader=require('shared/lazy_loader');return function ctor_app_permissions_list_panel(){var permissionsTableHasBeenLoaded=false;var elements={};var permissionListModule=PermissionList();return SettingsPanel({onInit:function(panel){elements={list:panel.querySelector('.app-list')};permissionListModule.init(elements.list);},onBeforeShow:function(){if(permissionsTableHasBeenLoaded){permissionListModule.refresh();}else{LazyLoader.getJSON('/resources/permissions_table.json').then(function(data){permissionsTableHasBeenLoaded=true;permissionListModule.setPermissionsTable(data);permissionListModule.refresh();});}
permissionListModule.enabled=true;},onBeforeHide:function(){permissionListModule.enabled=false;}});};});

View file

@ -0,0 +1,75 @@
/**
* The battery panel displays battery information provided by Battery.
*/
define(function(require) {
'use strict';
var SettingsPanel = require('modules/settings_panel');
var Battery = require('modules/battery');
return function ctor_battery_panel() {
var _batteryLevelText = null;
var _batteryTimeText = null;
var _getBatteryTimeL10n = function(type) {
type = type === 'charging' ? 'charging': 'discharging';
var time = Battery[type + 'Time'];
var l10nAttrs = {
id: 'battery-' + type + '-calculating',
args: {}
};
if (time !== Infinity && time > 0) {
var timeLeft = new Date(0, 0, 0, 0, 0, time);
l10nAttrs.args.hours = timeLeft.getHours();
l10nAttrs.args.minutes = timeLeft.getMinutes();
if (l10nAttrs.args.hours > 0) {
l10nAttrs.id = 'battery-' + type + '-hours';
} else {
l10nAttrs.id = 'battery-' + type + '-minutes';
}
} else if (time !== Infinity && time === 0) {
l10nAttrs.id = 'battery-' + type + '-complete';
} else {
l10nAttrs.id = 'battery-' + type + '-unavailable';
}
return l10nAttrs;
};
var _refreshText = function() {
document.l10n.setAttributes(_batteryLevelText,
'battery-current-level', { level: Battery.level });
if (Battery.state === 'unplugged') {
var l10nDischarging = _getBatteryTimeL10n('discharging');
document.l10n.setAttributes(_batteryTimeText,
l10nDischarging.id, l10nDischarging.args);
}
else {
var l10nCharging = _getBatteryTimeL10n('charging');
document.l10n.setAttributes(_batteryTimeText,
l10nCharging.id, l10nCharging.args);
}
};
return SettingsPanel({
onInit: function bp_onInit(rootElement) {
_batteryLevelText = rootElement.querySelector(
'#battery-level');
_batteryTimeText = rootElement.querySelector(
'#battery-time');
},
onBeforeShow: function bp_onBeforeShow(rootElement) {
Battery.observe('level', _refreshText);
Battery.observe('state', _refreshText);
Battery.observe('chargingTime', _refreshText);
Battery.observe('dischargingTime', _refreshText);
_refreshText();
},
onBeforeHide: function bp_onBeforeHide() {
Battery.unobserve(_refreshText);
}
});
};
});

View file

@ -0,0 +1,240 @@
/* ----------------------------------
* Value selector (Single & Multiple)
* ---------------------------------- */
/* Main dialog setup */
[role="dialog"][data-type="value-selector"] {
background: #2d2d2d;
overflow: hidden;
position: absolute;
z-index: 100;
top: 0;
left: 0;
right: 0;
bottom: 0;
padding: 0 0 7rem;
color: #fff;
font-family: sans-serif;
}
[role="dialog"][data-type="value-selector"] > section {
padding: 0 1.5rem;
box-sizing: padding-box;
width: 100%;
height: 100%;
}
[role="dialog"][data-type="value-selector"] h1 {
font-weight: 400;
font-size: 1.9rem;
line-height: 4.8rem;
color: #fff;
border-bottom: 0.1rem solid #616262;
background-color: rgba(0, 0, 0, .2);
margin: 0 -1.5rem;
padding: 0 3rem 1rem;
height: 4.8rem;
box-sizing: border-box;
}
/* Specific component code */
[role="dialog"][data-type="value-selector"] [role="listbox"] {
position: relative;
padding: 0;
margin: 0 -1.5rem;
max-height: calc(100% - 5rem);
overflow: auto;
}
[role="dialog"][data-type="value-selector"] [role="listbox"] li {
margin: 0;
padding: 0 1.5rem;
height: auto;
list-style: none;
position: relative;
font-weight: 300;
font-size: 2.2rem;
line-height: normal;
color: #fff;
transition: background-color 0.2s ease;
}
html[dir="ltr"] [role="dialog"][data-type="value-selector"] [role="listbox"],
html[dir="ltr"] [role="dialog"][data-type="value-selector"] [role="listbox"] li {
text-align: left;
}
html[dir="rtl"] [role="dialog"][data-type="value-selector"] [role="listbox"],
html[dir="rtl"] [role="dialog"][data-type="value-selector"] [role="listbox"] li {
text-align: right;
}
[role="dialog"][data-type="value-selector"] [role="listbox"] li:first-child label {
border-top-color: transparent;
}
[role="dialog"][data-type="value-selector"] [role="listbox"] li label {
outline: none;
display: block;
color: #fff;
border-top: 0.1rem solid #666;
}
[role="dialog"][data-type="value-selector"] [role="listbox"] li label span {
display: block;
box-sizing: border-box;
min-height: 5.2rem;
padding: 1.2rem 1.5rem;
line-height: 2.8rem;
word-wrap: break-word;
}
/* Pressed status */
[role="dialog"][data-type="value-selector"] [role="listbox"] li:active {
background-color: #00ABCC;
}
[role="dialog"][data-type="value-selector"] [role="listbox"] li:active label,
[role="dialog"][data-type="value-selector"] [role="listbox"] li:active + li label {
border-top-color: transparent;
}
[role="dialog"][data-type="value-selector"] [role="listbox"] li:active label span {
color: #fff !important;
background-image: none;
}
/* Subheaders */
[role="dialog"][data-type="value-selector"] [role="listbox"] li[role="subheader"] {
border: none;
background-color: #242424;
pointer-events: none;
-moz-user-select: none;
}
[role="dialog"][data-type="value-selector"] [role="listbox"] li[role="subheader"] label {
color: #f4f4f4;
font-size: 1.4rem;
font-weight: 500;
}
[role="dialog"][data-type="value-selector"] [role="listbox"] li[role="subheader"] label,
[role="dialog"][data-type="value-selector"] [role="listbox"] li[role="subheader"] + li label {
border: none;
}
[role="dialog"][data-type="value-selector"] [role="listbox"] li[role="subheader"] label span {
line-height: 2rem;
}
/* Checked status */
[role="dialog"][data-type="value-selector"] [role="listbox"] li[aria-selected="true"] label {
background-image: url(value_selector/images/icons/checked.png);
background-color: transparent;
background-size: 2.4rem;
background-repeat: no-repeat;
}
html[dir="ltr"] [role="dialog"][data-type="value-selector"] [role="listbox"] li[aria-selected="true"] label {
background-position: right 1.2rem top 50%;
}
html[dir="rtl"] [role="dialog"][data-type="value-selector"] [role="listbox"] li[aria-selected="true"] label {
background-position: left 1.2rem top 50%;
}
[role="dialog"][data-type="value-selector"] [role="listbox"] li[aria-selected="true"] span {
color: #00abcd;
}
html[dir="ltr"] [role="dialog"][data-type="value-selector"] [role="listbox"] li[aria-selected="true"] span {
padding-right: 2.6rem;
margin-right: 1.2rem;
}
html[dir="rtl"] [role="dialog"][data-type="value-selector"] [role="listbox"] li[aria-selected="true"] span {
padding-left: 2.6rem;
margin-left: 1.2rem;
}
/* Menu & buttons setup */
.value-selector > [role="dialog"][data-type="value-selector"] menu.value-selector-buttons {
white-space: nowrap;
margin: 0;
padding: 1.5rem;
background: #4d4d4d;
display: block;
overflow: hidden;
position: absolute;
left: 0;
right: 0;
bottom: 0;
}
.value-selector > [role="dialog"][data-type="value-selector"] menu.value-selector-buttons button::-moz-focus-inner {
border: none;
outline: none;
}
.value-selector > [role="dialog"][data-type="value-selector"] menu.value-selector-buttons button {
width: calc((100% - 1rem) / 2);
font-family: sans-serif;
font-style: italic;
height: 4rem;
margin: 0 0 1rem;
padding: 0 1.2rem;
box-sizing: border-box;
display: inline-block;
vertical-align: middle;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
background: #d8d8d8;
border: none;
border-radius: 2rem;
font-weight: normal;
font-size: 1.6rem;
line-height: 4rem;
color: #333;
text-align: center;
text-shadow: none;
text-decoration: none;
outline: none;
}
/* Affirmative */
.value-selector > [role="dialog"][data-type="value-selector"] menu.value-selector-buttons button.affirmative,
.value-selector > [role="dialog"][data-type="value-selector"] menu.value-selector-buttons button.recommend {
background-color: #00caf2;
color: #fff;
}
/* Pressed */
.value-selector > [role="dialog"][data-type="value-selector"] menu.value-selector-buttons button:active {
background: #00aacc;
color: #fff;
}
/* Disabled */
[role="dialog"][data-type="value-selector"] > menu.value-selector-buttons > button[disabled] {
background-color: #565656;
color: rgba(255,255,255,0.4);
pointer-events: none;
}
[role="dialog"][data-type="value-selector"] > menu.value-selector-buttons > button[disabled].recommend {
background-color: #006579;
}
button[disabled]::-moz-selection {
-moz-user-select: none;
}
.value-selector > [role="dialog"][data-type="value-selector"] menu.value-selector-buttons button:last-child {
margin-inline-start: 1rem;
}
.value-selector > [role="dialog"][data-type="value-selector"] menu.value-selector-buttons button,
.value-selector > [role="dialog"][data-type="value-selector"] menu.value-selector-buttons button:first-child {
margin: 0;
}
.value-selector > [role="dialog"][data-type="value-selector"] menu.value-selector-buttons button.full {
width: 100%;
}

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

@ -0,0 +1,173 @@
/* dialog */
/* FIXME we have to see why we need such specific style here and why
* the general style in confirm.css isn't sufficient */
#setup-installed-app-dialog,
#ime-layout-dialog,
#app-install-dialog,
#app-install-cancel-dialog,
#app-download-cancel-dialog {
display: none;
top: 3rem;
pointer-events: none;
}
/* Shift the bottom of the prompt to account for the height of the
software home button */
#screen.software-button-enabled #setup-installed-app-dialog,
#screen.software-button-enabled #ime-layout-dialog,
#screen.software-button-enabled #app-install-dialog,
#screen.software-button-enabled #app-install-cancel-dialog,
#screen.software-button-enabled #app-download-cancel-dialog {
bottom: var(--software-home-button-height);
}
@media (orientation: landscape) {
#screen.software-button-enabled #setup-installed-app-dialog,
#screen.software-button-enabled #ime-layout-dialog,
#screen.software-button-enabled #app-install-dialog,
#screen.software-button-enabled #app-install-cancel-dialog,
#screen.software-button-enabled #app-download-cancel-dialog {
bottom: 0;
/* BiDi note: the software home button should not be mirrored in RTL. */
right: var(--software-home-button-height);
}
}
#setup-installed-app-dialog.visible,
#ime-layout-dialog.visible,
#app-install-dialog.visible,
#app-install-cancel-dialog.visible,
#app-download-cancel-dialog.visible {
display: inline-block;
pointer-events: auto;
}
#app-uninstall-dialog {
position: absolute;
}
#setup-installed-app-dialog section,
#ime-layout-dialog section,
#app-install-dialog section,
#app-install-cancel-dialog section,
#app-download-cancel-dialog section {
height: auto;
}
#setup-installed-app-dialog h1,
#ime-layout-dialog h1,
#app-install-dialog h1,
#app-install-cancel-dialog h1,
#app-download-cancel-dialog h1 {
border-bottom: none;
background: none;
}
#app-install-dialog dl > dt {
width: 8rem;
margin-inline-end: 1rem;
overflow: hidden;
text-overflow: ellipsis;
}
#app-install-dialog p {
font-size: 1.4rem;
font-weight: 400;
line-height: normal;
border-top: none;
}
#app-install-dialog #system-addon-warning p + p {
margin-top: 0;
}
#app-install-dialog span {
font-size: 1.4rem;
font-weight: 400;
line-height: normal;
color: inherit;
}
#app-install-dialog #system-addon-icon {
height: 3rem;
width: 3rem;
}
html[dir="ltr"] #app-install-dialog #system-addon-icon {
float: left;
margin: 1.5rem 0 0 1.5rem;
}
html[dir="rtl"] #app-install-dialog #system-addon-icon {
float: right;
margin: 1.5rem 1.5rem 0 0;
}
#app-install-author-url {
direction: ltr;
}
#app-install-cancel-dialog small:first-child {
margin-bottom: 1rem;
}
/* ... end of dialog */
/* notification */
#install-manager-notification-container > .fake-notification {
position: relative;
border-bottom: none;
}
#install-manager-notification-container > .fake-notification > * {
pointer-events: none;
}
#install-manager-notification-container [data-icon] {
color: #9ab0c0;
background-color: #edf1f4;
}
#install-manager-notification-container .title-container {
margin: 1.4rem 0 0;
line-height: 3rem;
}
html[dir="ltr"] #install-manager-notification-container .title-container {
margin-left: 3.7rem;
}
html[dir="rtl"] #install-manager-notification-container .title-container {
margin-right: 3.7rem;
}
#install-manager-notification-container progress {
position: absolute;
width: 100%;
height: 0.4rem;
left: 0;
top: 5.6rem;
border: none;
}
#install-manager-notification-container progress::-moz-progress-bar {
background: url(../shared/progress.gif) repeat-x;
}
/* ... end of notification */
/* ... start of keyboard layout */
ul#ime-list {
padding-inline-start: 1.5rem;
}
ul#ime-list > li {
/* Positioning */
position: relative;
/* Display & Box Model */
width: 100%;
height: 3rem;
padding-top: 2rem;
/* Other */
list-style: none;
}
ul#ime-list > li label {
text-transform: none;
}
/* ... end of keyboard layout */

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} }