Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
230 lines
7.4 KiB
JavaScript
230 lines
7.4 KiB
JavaScript
'use strict';
|
|
/*global applications, Service, AppWindow, Browser */
|
|
|
|
(function(window) {
|
|
/**
|
|
* WrapperFactory deals with opening a window for bookmarked web page.
|
|
* Usually the request is coming from homescreen app.
|
|
* @module WrapperFactory
|
|
*/
|
|
var WrapperFactory = {
|
|
name: 'WrapperFactory',
|
|
start: function() {
|
|
window.addEventListener('mozbrowseropenwindow', this, true);
|
|
Service.registerState('isLaunchingWindow', this);
|
|
},
|
|
stop: function() {
|
|
window.removeEventListener('mozbrowseropenwindow', this, true);
|
|
},
|
|
|
|
isLaunchingWindow: function() {
|
|
return !!this._launchingApp;
|
|
},
|
|
|
|
forgetLastLaunchingWindow: function() {
|
|
if (this._launchingApp && this._launchingApp.element) {
|
|
this._launchingApp.element.removeEventListener('_opened', this);
|
|
this._launchingApp.element.removeEventListener('_terminated', this);
|
|
}
|
|
this._launchingApp = null;
|
|
},
|
|
|
|
handleEvent: function wf_handleEvent(evt) {
|
|
// Prevent Gecko's default handler from opening the window.
|
|
evt.preventDefault();
|
|
|
|
if (evt.type === '_opened' || evt.type === '_terminated') {
|
|
if (this._launchingApp === evt.detail) {
|
|
this.forgetLastLaunchingWindow();
|
|
}
|
|
return;
|
|
}
|
|
var detail = evt.detail;
|
|
|
|
// If it's a normal window.open request, ignore.
|
|
if (typeof detail.features !== 'string') {
|
|
return;
|
|
}
|
|
|
|
// Turn ',' separated 'key=value' string into object for easy access
|
|
var features = detail.features
|
|
.split(',')
|
|
.reduce(function(acc, feature) {
|
|
feature = feature
|
|
.split('=')
|
|
.map(function(featureElem) { return featureElem.trim(); });
|
|
if (feature.length !== 2) {
|
|
return acc;
|
|
}
|
|
|
|
acc[decodeURIComponent(feature[0])] = decodeURIComponent(feature[1]);
|
|
return acc;
|
|
}, {});
|
|
|
|
// Handles only call to window.open with `remote=true` feature.
|
|
if (!('remote' in features) || features.remote !== 'true') {
|
|
return;
|
|
}
|
|
|
|
var callerOrigin;
|
|
|
|
// Examine permission
|
|
// XXX: Ask app window about this.
|
|
// We can skip the permission check for events against the system window.
|
|
if (evt.target !== window) {
|
|
var callerIframe = evt.target;
|
|
var manifestURL = callerIframe.getAttribute('mozapp');
|
|
|
|
var callerApp = applications.getByManifestURL(manifestURL);
|
|
if (!this.hasPermission(callerApp, 'open-remote-window') &&
|
|
!this.hasPermission(callerApp, 'homescreen-webapps-manage')) {
|
|
return;
|
|
}
|
|
|
|
callerOrigin = callerApp.origin;
|
|
} else {
|
|
callerOrigin = location.origin;
|
|
}
|
|
|
|
// So, we are going to open a remote window.
|
|
evt.stopImmediatePropagation();
|
|
|
|
var name = detail.name;
|
|
var url = detail.url;
|
|
var app;
|
|
|
|
// Use fake origin for named windows in order to be able to reuse them,
|
|
// otherwise always open a new window for '_blank'.
|
|
var origin = null;
|
|
switch (name) {
|
|
case '_samescope':
|
|
var scope = features.scope || false;
|
|
var appName = features.name;
|
|
var inScopeMethod = 'AppWindowManager.getAppInScope';
|
|
|
|
origin = new URL(url).origin;
|
|
features.pinned = true;
|
|
app = Service.query(inScopeMethod, scope, origin, appName);
|
|
if (app) {
|
|
app.requestOpen();
|
|
return;
|
|
}
|
|
/* falls through */
|
|
case '_blank':
|
|
// If we already have a browser and we receive an open request,
|
|
// display it in the current browser frame.
|
|
var activeApp = Service.query('AppWindowManager.getActiveWindow');
|
|
var isManuallyRegular = activeApp && activeApp.url &&
|
|
activeApp.url.includes('private=0');
|
|
if (activeApp && (activeApp.isBrowser() || activeApp.isSearch())) {
|
|
activeApp.isPrivate = activeApp.hasOwnProperty('isPrivate') ?
|
|
activeApp.isPrivate :
|
|
(Browser.privateByDefault && !isManuallyRegular);
|
|
activeApp.navigate(url);
|
|
return;
|
|
}
|
|
|
|
origin = url;
|
|
app = Service.query('AppWindowManager.getApp', origin);
|
|
// Just bring on top if a wrapper window is
|
|
// already running with this url.
|
|
if (app && app.windowName == '_blank') {
|
|
this.publish('launchapp', { origin: origin });
|
|
return;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
origin = 'window:' + name + ',source:' + callerOrigin;
|
|
app = Service.query('AppWindowManager.getApp', origin);
|
|
if (app && app.windowName === name) {
|
|
if (app.iframe.src === url) {
|
|
// If the url is already loaded, just display the app
|
|
this.publish('launchapp', { origin: origin });
|
|
return;
|
|
} else {
|
|
// Wrapper context shouldn't be shared between two apps -> killing
|
|
this.publish('killapp', { origin: origin });
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: Put this into browser_config_helper.
|
|
var browser_config = this.generateBrowserConfig(features);
|
|
|
|
// If we don't reuse an existing app, open a brand new one
|
|
browser_config.url = url;
|
|
browser_config.origin = origin;
|
|
browser_config.windowName = name;
|
|
browser_config.isPrivate = Browser.privateByDefault;
|
|
if (!browser_config.title) {
|
|
browser_config.title = url;
|
|
}
|
|
|
|
if (Service.query('MultiScreenController.enabled')) {
|
|
Service.request('chooseDisplay', browser_config)
|
|
.catch(this.launchWrapper.bind(this, browser_config));
|
|
} else {
|
|
this.launchWrapper(browser_config);
|
|
}
|
|
},
|
|
|
|
launchWrapper: function wf_launchWrapper(config) {
|
|
var app = Service.query('AppWindowManager.getApp', config.origin);
|
|
if (!app) {
|
|
config.chrome.scrollable = true;
|
|
this.forgetLastLaunchingWindow();
|
|
this.trackLauchingWindow(config);
|
|
}
|
|
|
|
this.publish('launchapp', { origin: config.origin });
|
|
},
|
|
|
|
trackLauchingWindow: function(config) {
|
|
this._launchingApp = new AppWindow(config);
|
|
this._launchingApp.element.addEventListener('_opened', this);
|
|
this._launchingApp.element.addEventListener('_terminated', this);
|
|
},
|
|
|
|
hasPermission: function wf_hasPermission(app, permission) {
|
|
var mozPerms = navigator.mozPermissionSettings;
|
|
if (!mozPerms) {
|
|
return false;
|
|
}
|
|
|
|
var value = mozPerms.get(permission, app.manifestURL, app.origin, false);
|
|
|
|
return (value === 'allow');
|
|
},
|
|
|
|
generateBrowserConfig: function wf_generateBrowserConfig(features) {
|
|
var config = {};
|
|
config.chrome = {};
|
|
config.title = features.name;
|
|
config.icon = features.icon || '';
|
|
config.chrome.pinned = features.pinned || false;
|
|
|
|
if ('originName' in features) {
|
|
config.originName = features.originName;
|
|
config.originURL = features.originUrl;
|
|
}
|
|
|
|
if ('searchName' in features) {
|
|
config.searchName = features.searchName;
|
|
config.searchURL = features.searchUrl;
|
|
}
|
|
|
|
if ('remote' in features) {
|
|
config.oop = true;
|
|
}
|
|
|
|
return config;
|
|
},
|
|
|
|
publish: function wf_publish(event, detail) {
|
|
var evt = new CustomEvent(event, { detail: detail });
|
|
window.dispatchEvent(evt);
|
|
}
|
|
};
|
|
window.WrapperFactory = WrapperFactory;
|
|
}(window));
|