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,298 @@
'use strict';
/* global applications, BrowserConfigHelper, AppWindow, Service */
/* jshint nonew: false */
(function(exports) {
/**
* AppWindowFactory handle the launch request from gecko and
* wrap the config with properer parameters.
*
* If gecko is asking us to open a webapp,
* AppWindowFactory would do the instantiation and let
* AppWindowManager to do the following app opening control via
* event <code>launchapp</code>.
*
* If gecko is asking us to open an inline activity page,
* AppWindowFactory would wrap the configuration and sent it to
* AppWindowFactory for it to do instantiation via event
* <code>launchactivity</code>.
*
* ![app and activity launch flow](http://i.imgur.com/ZyMcgft.png)
*
* @class AppWindowFactory
*/
function AppWindowFactory() {
this.preHandleEvent = this.preHandleEvent.bind(this);
}
AppWindowFactory.prototype = {
name: 'AppWindowFactory',
/**
* Indicate whether this class is started or not.
* @access private
* @type {Boolean}
* @memberof AppWindowFactory.prototype
*/
_started: false,
/**
* Register all event handlers.
* @memberof AppWindowFactory.prototype
*/
start: function awf_start() {
if (this._started) {
return;
}
this._started = true;
window.addEventListener('webapps-launch', this.preHandleEvent);
window.addEventListener('webapps-close', this.preHandleEvent);
window.addEventListener('open-app', this.preHandleEvent);
window.addEventListener('openwindow', this.preHandleEvent);
window.addEventListener('appopenwindow', this.preHandleEvent);
window.addEventListener('applicationready', (function appReady(e) {
window.removeEventListener('applicationready', appReady);
this._handlePendingEvents();
}).bind(this));
Service.registerState('isLaunchingWindow', this);
},
/**
* Unregister all event handlers.
* @memberof AppWindowFactory.prototype
*/
stop: function awf_stop() {
if (!this._started) {
return;
}
this._started = false;
window.removeEventListener('webapps-launch', this.preHandleEvent);
window.removeEventListener('webapps-close', this.preHandleEvent);
window.removeEventListener('open-app', this.preHandleEvent);
window.removeEventListener('openwindow', this.preHandleEvent);
window.removeEventListener('appopenwindow', this.preHandleEvent);
},
/**
* Queue events until AppWindowFactory is ready to handle them.
*/
_queueEvents: [],
_queuePendingEvent: function(evt) {
this._queueEvents.push(evt);
},
_handlePendingEvents: function() {
this._queueEvents.forEach((function(evt) {
this.handleEvent(evt);
}).bind(this));
this._queueEvents = [];
},
preHandleEvent: function(evt) {
if (applications.ready) {
this.handleEvent(evt);
} else {
this._queuePendingEvent(evt);
}
},
handleEvent: function awf_handleEvent(evt) {
var detail = evt.detail;
if (evt.type === '_opened' || evt.type === '_terminated') {
if (this._launchingApp === detail) {
this.forgetLastLaunchingWindow();
}
return;
}
if (!detail.url && !detail.manifestURL) {
return;
}
var config = new BrowserConfigHelper(detail);
config.evtType = evt.type;
switch (evt.type) {
case 'openwindow':
case 'appopenwindow':
case 'webapps-launch':
config.timestamp = detail.timestamp;
// TODO: Look up current opened window list,
// and then create a new instance here.
this.launch(config);
break;
case 'open-app':
// System Message Handler API is asking us to open the specific URL
// that handles the pending system message.
// We will launch it in background if it's not handling an activity.
config.isSystemMessage = true;
if (detail.isActivity) {
config.isActivity = true;
if (detail.target.disposition &&
detail.target.disposition == 'inline') {
config.inline = true;
}
}
config.changeURL = !detail.onlyShowApp;
config.stayBackground = !detail.showApp;
if (detail.extra && detail.extra.manifestURL) {
config.parentApp = detail.extra.manifestURL;
}
// TODO: Create activity window instance
// or background app window instance for system message here.
this.launch(config);
break;
case 'webapps-close':
this.publish('killapp', config);
break;
}
},
/**
* Browser Configuration
* @typedef {Object} BrowserConfig
* @property {String} origin the same as appURL.
* @property {String} manifestURL the same as manifestURL.
* @property {Object} manifest the parsed manifest object.
* If the app is not an entry point app,
* the manifest would be the reference of application
* manifest stored in Applications module. But if the app
* is an entry point app, we will do deep clone to
* generate a new object and replace the properties of
* entry point to proper position.
* @property {String} name the name of the app, retrieved from manifest.
* @property {Boolean} oop Indicate it's running out of process or in
* process.
*/
/**
* Launch an app window.
* @param {BrowserConfig} config Generated by BrowserConfigHelper.
* @memberof AppWindowFactory.prototype
*/
launch: function awf_launch(config) {
if (config.url === window.location.href) {
return;
}
if (config.isActivity && config.inline) {
this.publish('launchactivity', config);
return;
}
// The rocketbar currently handles the management of normal search app
// launches. Requests for the 'newtab' page will continue to filter
// through and publish the launchapp event.
if (this._isSearch(config)) {
if (config.url.indexOf('newtab.html') === -1) {
return;
}
// In case of the Browser, oppen an already open
// instance instead of creating a new one
var openInstance = this._findBrowserInstance(config);
if (openInstance) {
openInstance.requestOpen();
return;
}
}
var app = Service.query('getApp', config.origin, config.manifestURL);
if (app) {
if (config.evtType == 'appopenwindow') {
app.browser.element.src = config.url;
}
app.reviveBrowser();
// Always relaunch background app locally
this.publish('launchapp', config);
} else {
var launchApp = () => {
// homescreenWindowManager already listens webapps-launch and
// open-app. We don't need to check if the launched app is homescreen.
this.forgetLastLaunchingWindow();
this.trackLauchingWindow(config);
this.publish('launchapp', config);
};
if (Service.query('MultiScreenController.enabled')) {
Service.request('chooseDisplay', config).catch(launchApp);
} else {
launchApp();
}
}
},
trackLauchingWindow: function(config) {
var app = new AppWindow(config);
if (config.stayBackground) {
return;
}
this._launchingApp = app;
this._launchingApp.element.addEventListener('_opened', this);
this._launchingApp.element.addEventListener('_terminated', this);
},
forgetLastLaunchingWindow: function() {
if (this._launchingApp && this._launchingApp.element) {
this._launchingApp.element.removeEventListener('_opened', this);
this._launchingApp.element.removeEventListener('_terminated', this);
}
this._launchingApp = null;
},
isLaunchingWindow: function() {
return !!this._launchingApp;
},
/**
* Returns an appWindow instace that matches
* the criteria given by the config
* @param {Object} detail The data passed when initializing the event.
* @memberof AppWindowFactory.prototype
*/
_findBrowserInstance: function(config) {
// Special case for the browser. Openning the last instance
// of any unpinned window, if it exists.
var activeApp = Service.query('AppWindowManager.getActiveApp');
if (!this._isSearch(config) || activeApp.isBrowser()) {
return;
}
var unpinned = Service.query('AppWindowManager.getUnpinnedWindows');
if (unpinned.length) {
return unpinned.sort(function(app1, app2) {
return app2.launchTime - app1.launchTime;
})[0];
}
},
/**
* Determines if the current app to be opened
* is the Browser app
* @param {Object} detail The data passed when initializing the event.
* @memberof AppWindowFactory.prototype
*/
_isSearch: function(config) {
return (config.manifest && config.manifest.role === 'search');
},
/**
* Publish a CustomEvent.
* @param {String} event The name of the event.
* @param {Object} detail The data passed when initializing the event.
* @memberof AppWindowFactory.prototype
*/
publish: function awf_publish(event, detail, scope) {
scope = scope || window;
var evt = document.createEvent('CustomEvent');
evt.initCustomEvent(event, true, false, detail);
scope.dispatchEvent(evt);
}
};
exports.AppWindowFactory = AppWindowFactory;
}(window));