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,379 @@
/* global MozActivity, IconsHelper, LazyLoader */
/* global BaseModule, Service */
(function(window) {
'use strict';
var _ = navigator.mozL10n.get;
const PINNING_PREF = 'dev.gaia.pinning_the_web';
const SITE_ICON_SIZE = 72;
/**
* The ContextMenu of the AppWindow.
*
* @class BrowserContextMenu
* @param {AppWindow} app The app window instance
* where this dialog should popup.
*/
function BrowserContextMenu(app) {
this.app = app;
this.containerElement = app.element;
this.app.element.addEventListener('mozbrowsercontextmenu', this);
return this;
}
BrowserContextMenu.SUB_MODULES = [
'ContextMenuView'
];
BaseModule.create(BrowserContextMenu, {
name: 'BrowserContextMenu',
handleEvent: function(evt) {
switch (evt.type) {
case 'mozbrowsercontextmenu':
if (!Service.query('isFtuRunning')) {
this.show(evt);
}
break;
}
},
show: function(evt) {
var detail = evt.detail;
var hasContextMenu = detail.contextmenu &&
detail.contextmenu.items.length > 0 &&
detail.contextmenu.customized;
var hasSystemTargets = detail.systemTargets &&
detail.systemTargets.length > 0;
// Nothing to show
if (!hasSystemTargets && !hasContextMenu) {
return;
}
// context menus in certified apps that only have system targets are
// currently disabled. https://bugzil.la/1010160 is tracking reenabling
if (!hasContextMenu && hasSystemTargets && this.app.isCertified()) {
return;
}
var items = this._listItems(detail);
if (!items.length) {
return;
}
evt.preventDefault();
evt.stopPropagation();
// This temporary check will be removed as soon as
// Pinning the Web lands without a Setting. We won't need
// the second call to _listItems anymore then.
this._getPinningEnabled(function(value) {
this.pinningEnabled = value;
items = this._listItems(detail);
// Notify the embedder we are handling the context menu
this.contextMenuView.show(items);
}.bind(this));
},
_listItems: function(detail) {
var items = [];
// contextmenu.items are specified by the web content via html5
// context menu api and a specific system command, copy-image.
if (detail.contextmenu && detail.contextmenu.items.length) {
detail.contextmenu.items.forEach(function(choice, index) {
var itemObj = null;
switch (choice.id) {
case 'copy-image':
case 'copy-link':
itemObj = {
id: choice.id,
label: _(choice.id),
};
break;
default:
// Customized menu items
itemObj = {
label: choice.label,
icon: choice.icon,
};
}
itemObj.callback = () => {
detail.contextMenuItemSelected(choice.id);
};
items.push(itemObj);
}, this);
}
if (detail.systemTargets) {
detail.systemTargets.forEach(function(item) {
this.generateSystemMenuItem(item).forEach(function(menuItem) {
items.push(menuItem);
});
}, this);
}
return items;
},
_getPinningEnabled: function(callback) {
var req = navigator.mozSettings.createLock().get(PINNING_PREF);
req.onsuccess = () => {
callback(req.result[PINNING_PREF]);
};
req.onerror = () => {
callback(false);
};
},
isShown: function() {
return this.contextMenuView.isShown();
},
hide: function() {
if (!this.contextMenuView.isShown()) {
return;
}
this.contextMenuView.hide();
},
openUrl: function(url, isPrivate) {
/* jshint nonew: false */
new MozActivity({
name: 'view',
data: {
type: 'url',
url: url,
isPrivate: isPrivate
}
});
},
shareUrl: function(url) {
/* jshint nonew: false */
new MozActivity({
name: 'share',
data: {
type: 'url',
url: url
}
});
},
_getSaveUrlItem: function(url, name) {
if (this.pinningEnabled) {
return {
id: 'pin-to-home-screen',
label: _('pin-to-home-screen'),
callback: this.pinUrl.bind(this, url, name)
};
}
return {
id: 'add-to-homescreen',
label: _('add-link-to-home-screen'),
callback: this.bookmarkUrl.bind(this, url, name)
};
},
bookmarkUrl: function(url, name) {
var favicons = this.app.favicons;
/* jshint nonew: false */
var data = {
type: 'url',
url: url,
name: name,
iconable: false
};
if (this.app.webManifestURL) {
data.manifestURL = this.app.webManifestURL;
}
LazyLoader.load('shared/js/icons_helper.js', (() => {
IconsHelper.getIcon(url, null, {icons: favicons}).then(icon => {
if (icon) {
data.icon = icon;
}
new MozActivity({
name: 'save-bookmark',
data: data
});
});
}));
},
pinUrl: function(url, name) {
var data = {
name: name,
type: 'url',
url: url,
iconable: false,
title: this.app.title,
meta: this.app.meta,
app: this.app
};
this.app.getSiteIconUrl(SITE_ICON_SIZE)
.then(iconObject => {
if (iconObject) {
data.icon = iconObject.blob;
}
this.app.getScreenshot(() => {
data.meta.screenshot = this.app._screenshotBlob;
this.publish('pin-page-dialog-requestopen', data);
});
})
.catch((err) => {
this.app.debug('bookmarkUrl, error from getSiteIcon: %s', err);
this.app.getScreenshot(() => {
data.meta.screenshot = this.app._screenshotBlob;
this.publish('pin-page-dialog-requestopen', data);
});
});
},
newWindow: function(manifest, isPrivate) {
// For private windows we create an empty private app window.
if (isPrivate) {
window.dispatchEvent(new CustomEvent('new-private-window'));
} else {
window.dispatchEvent(new CustomEvent('new-non-private-window'));
}
},
showWindows: function(manifest) {
window.dispatchEvent(
new CustomEvent('taskmanagershow',
{ detail: { filter: 'browser-only' }})
);
},
generateSystemMenuItem: function(item) {
var nodeName = item.nodeName.toUpperCase();
var documentURI = item.data.documentURI;
var uri = item.data.uri;
switch (nodeName) {
case 'A':
return [{
id: 'open-in-new-window',
label: _('open-in-new-window'),
callback: this.openUrl.bind(this, uri)
}, {
id: 'open-in-new-private-window',
label: _('open-in-new-private-window'),
callback: this.openUrl.bind(this, uri, true)
}, {
id: 'save-link',
label: _('save-link'),
callback: this.app.browser.element.download.bind(
this.app.browser.element, uri, { referrer: documentURI })
}, {
id: 'share-link',
label: _('share-link'),
callback: this.shareUrl.bind(this, uri)
}];
case 'IMG':
case 'VIDEO':
case 'AUDIO':
var typeMap = {
'IMG': 'image',
'VIDEO': 'video',
'AUDIO': 'audio'
};
var type = typeMap[nodeName];
if (nodeName === 'VIDEO' && !item.data.hasVideo) {
type = 'audio';
}
return [{
id: 'save-' + type,
label: _('save-' + type),
callback: this.app.browser.element.download.bind(
this.app.browser.element, uri, { referrer: documentURI })
}, {
id: 'share-' + type,
label: _('share-' + type),
callback: this.shareUrl.bind(this, uri)
}];
default:
return [];
}
},
showDefaultMenu: function(manifest, name) {
return new Promise((resolve) => {
this._getPinningEnabled(function(value) {
this.pinningEnabled = value;
var config = this.app.config;
var menuData = [];
var finish = () => {
this.contextMenuView.show(menuData);
resolve();
};
menuData.push({
id: 'new-window',
label: _('new-window'),
callback: this.newWindow.bind(this, manifest)
});
menuData.push({
id: 'new-private-window',
label: _('new-private-window'),
callback: this.newWindow.bind(this, manifest, true)
});
menuData.push({
id: 'show-windows',
label: _('show-windows'),
callback: this.showWindows.bind(this)
});
// Do not show the bookmark/share buttons if the url starts with app.
// This is because in some cases we use the app chrome to view system
// pages. E.g., private browsing.
if (config.url.startsWith('app')) {
finish();
return;
}
menuData.push(this._getSaveUrlItem(config.url, name));
menuData.push({
id: 'share',
label: _('share'),
callback: this.shareUrl.bind(this, config.url)
});
finish();
}.bind(this));
});
},
_stop: function() {
this.contextMenuView.destroy();
this.app.element.removeEventListener('mozbrowsercontextmenu', this);
this.containerElement = null;
this.app = null;
}
});
BrowserContextMenu.prototype.focus = function() {
this.contextMenuView.focus();
};
}(this));