Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/apps/system/js/window_manager.js
diff options
context:
space:
mode:
Diffstat (limited to 'apps/system/js/window_manager.js')
-rw-r--r--apps/system/js/window_manager.js2011
1 files changed, 0 insertions, 2011 deletions
diff --git a/apps/system/js/window_manager.js b/apps/system/js/window_manager.js
deleted file mode 100644
index e53605e..0000000
--- a/apps/system/js/window_manager.js
+++ /dev/null
@@ -1,2011 +0,0 @@
-/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
-/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
-
-//
-// This file calls getElementById without waiting for an onload event, so it
-// must have a defer attribute or be included at the end of the <body>.
-//
-// This module is responsible for launching apps and for allowing
-// the user to switch among apps and kill apps. Specifically, it handles:
-// launching apps,
-// killing apps
-// keeping track of the set of running apps (which we call tasks here)
-// keeping track of which task is displayed (the foreground task)
-// changing the foreground task
-// hiding all apps to display the homescreen
-// displaying the app switcher to allow the user to switch and kill apps
-// performing appropriate transition animations between:
-// the homescreen and an app
-// the homescreen and the switcher
-// an app and the homescreen
-// the switcher and the homescreen
-// the switcher and the current foreground task
-// the switcher and a different task
-// Handling Home key events to switch to the homescreen and the switcher
-//
-// The public API of the module is small. It defines an WindowManager object
-// with these methods:
-//
-// launch(origin): switch to the specified running app
-// kill(origin, callback): stop specified app
-// reload(origin): reload the given app
-// getDisplayedApp(): return the origin of the currently displayed app
-// setOrientationForApp(origin): set the phone to orientation to a given app
-// getAppFrame(origin): returns the iframe element for the specified origin
-// which is assumed to be running. This is only currently used
-// for tests and chrome stuff: see the end of the file
-// getRunningApps(): get the app references of the running apps.
-//
-// TODO
-// The "origin" does not actually refer to app's origin but rather a identifier
-// of the app reference that one gets from |getDisplayedApp()| or
-// iterates |getRunningApps|. The string is make up of the specified
-// launching entry point, origin, or the website url launched by wrapper.
-// It would be ideal if the variable get correctly named and it's rule is being
-// properly documented.
-// See https://bugzilla.mozilla.org/show_bug.cgi?id=796629
-//
-
-var WindowManager = (function() {
- 'use strict';
-
- function debug(str) {
- dump('WindowManager: ' + str + '\n');
- }
-
- // Holds the origin of the home screen, which should be the first
- // app we launch through web activity during boot
- var homescreen = null;
- var homescreenURL = '';
- var homescreenManifestURL = '';
- var ftu = null;
- var ftuManifestURL = '';
- var ftuURL = '';
- var isRunningFirstRunApp = false;
- // keep the reference of inline activity frame here
- var inlineActivityFrames = [];
- var activityCallerOrigin = '';
-
- // Some document elements we use
- var windows = document.getElementById('windows');
- var screenElement = document.getElementById('screen');
- var wrapperHeader = document.querySelector('#wrapper-activity-indicator');
- var wrapperFooter = document.querySelector('#wrapper-footer');
- var kTransitionTimeout = 1000;
-
- // Set this to true to debugging the transitions and state change
- var slowTransition = false;
- if (slowTransition) {
- windows.classList.add('slow-transition');
- }
-
- //
- // The set of running apps.
- // This is a map from app origin to an object like this:
- // {
- // name: the app's name
- // manifest: the app's manifest object
- // frame: the iframe element that the app is displayed in
- // launchTime: last time when app gets active
- // }
- //
- var runningApps = {};
- var numRunningApps = 0; // appendFrame() and removeFrame() maintain this count
- var nextAppId = 0; // to give each app's iframe a unique id attribute
-
- // The origin of the currently displayed app, or null if there isn't one
- var displayedApp = null;
-
- // Function to hide init starting logo
- function handleInitlogo(callback) {
- var initlogo = document.getElementById('initlogo');
- initlogo.classList.add('hide');
- initlogo.addEventListener('transitionend', function delInitlogo() {
- initlogo.removeEventListener('transitionend', delInitlogo);
- initlogo.parentNode.removeChild(initlogo);
- if (callback) {
- callback();
- }
- });
- };
-
- // Public function. Return the origin of the currently displayed app
- // or null if there is none.
- function getDisplayedApp() {
- return displayedApp || null;
- }
-
- function requireFullscreen(origin) {
- var app = runningApps[origin];
- if (!app)
- return false;
-
- var manifest = app.manifest;
- if (manifest.entry_points && manifest.type == 'certified') {
- var entryPoint = manifest.entry_points[origin.split('/')[3]];
- if (entryPoint)
- return entryPoint.fullscreen;
- return false;
- } else {
- return manifest.fullscreen;
- }
- }
-
- // Make the specified app the displayed app.
- // Public function. Pass null to make the homescreen visible
- function launch(origin) {
- // If the origin is indeed valid we make that app as the displayed app.
- if (isRunning(origin)) {
- setDisplayedApp(origin);
- return;
- }
-
- // If the origin is null, make the homescreen visible.
- if (origin == null) {
- setDisplayedApp(homescreen);
- return;
- }
-
- // At this point, we have no choice but to show the homescreen.
- // We cannot launch/relaunch a given app based on the "origin" because
- // we would need the manifest URL and the specific entry point.
- console.warn('No running app is being identified as "' + origin + '". ' +
- 'Showing home screen instead.');
- setDisplayedApp(homescreen);
- }
-
- function isRunning(origin) {
- return runningApps.hasOwnProperty(origin);
- }
-
- function getAppFrame(origin) {
- if (isRunning(origin))
- return runningApps[origin].frame;
- else
- return null;
- }
-
- // Set the size of the app's iframe to match the size of the screen.
- // We have to call this on resize events (which happen when the
- // phone orientation is changed). And also when an app is launched
- // and each time an app is brought to the front, since the
- // orientation could have changed since it was last displayed
- function setAppSize(origin, changeActivityFrame) {
- var app = runningApps[origin];
- if (!app)
- return;
-
- var frame = app.frame;
- var manifest = app.manifest;
-
- var cssWidth = window.innerWidth + 'px';
- var cssHeight = window.innerHeight - StatusBar.height;
- if ('wrapper' in frame.dataset) {
- cssHeight -= 10;
- }
- cssHeight += 'px';
-
- if (!screenElement.classList.contains('attention') &&
- requireFullscreen(origin)) {
- cssHeight = window.innerHeight + 'px';
- }
-
- frame.style.width = cssWidth;
- frame.style.height = cssHeight;
-
- // We will call setInlineActivityFrameSize()
- // if changeActivityFrame is not explicitly set to false.
- if (changeActivityFrame !== false)
- setInlineActivityFrameSize();
- }
-
- // App's height is relevant to keyboard height
- function setAppHeight(keyboardHeight) {
- var app = runningApps[displayedApp];
- if (!app)
- return;
-
- var frame = app.frame;
- var manifest = app.manifest;
-
- var cssHeight =
- window.innerHeight - StatusBar.height - keyboardHeight + 'px';
-
- if (!screenElement.classList.contains('attention') &&
- requireFullscreen(displayedApp)) {
- cssHeight = window.innerHeight - keyboardHeight + 'px';
- }
-
- frame.style.height = cssHeight;
-
- setInlineActivityFrameSize();
- }
-
- // Copy the dimension of the currently displayed app
- function setInlineActivityFrameSize() {
- if (!inlineActivityFrames.length)
- return;
-
- var app = runningApps[displayedApp];
- var appFrame = app.frame;
- var frame = inlineActivityFrames[inlineActivityFrames.length - 1];
-
- frame.style.width = appFrame.style.width;
-
- if (document.mozFullScreen) {
- frame.style.height = window.innerHeight + 'px';
- frame.style.top = '0px';
- } else {
- if ('wrapper' in appFrame.dataset) {
- frame.style.height = window.innerHeight - StatusBar.height + 'px';
- } else {
- frame.style.height = appFrame.style.height;
- }
- frame.style.top = appFrame.offsetTop + 'px';
- }
- }
-
- function setFrameBackgroundBlob(frame, blob, transparent) {
- URL.revokeObjectURL(frame.dataset.bgObjectURL);
- delete frame.dataset.bgObjectURL;
-
- var objectURL = URL.createObjectURL(blob);
- frame.dataset.bgObjectURL = objectURL;
- var backgroundCSS =
- '-moz-linear-gradient(top, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.5) 100%),' +
- 'url(' + objectURL + '),' +
- ((transparent) ? 'transparent' : '#fff');
-
- frame.style.background = backgroundCSS;
- }
-
- function clearFrameBackground(frame) {
- if (!('bgObjectURL' in frame.dataset))
- return;
-
- URL.revokeObjectURL(frame.dataset.bgObjectURL);
- delete frame.dataset.bgObjectURL;
- frame.style.background = '';
- }
-
- var openFrame = null;
- var closeFrame = null;
- var openCallback = null;
- var closeCallback = null;
- var transitionOpenCallback = null;
- var transitionCloseCallback = null;
-
- // Use setOpenFrame() to reset the CSS classes set
- // to the current openFrame (before overwriting the reference)
- function setOpenFrame(frame) {
- if (openFrame) {
- removeFrameClasses(openFrame);
- }
-
- openFrame = frame;
- }
-
- // Use setCloseFrame() to reset the CSS classes set
- // to the current closeFrame (before overwriting the reference)
- function setCloseFrame(frame) {
- if (closeFrame) {
- removeFrameClasses(closeFrame);
- // closeFrame should not be set to active
- closeFrame.classList.remove('active');
- }
-
- closeFrame = frame;
- }
-
- // Remove these visible className from frame so we will not ended
- // up having a frozen frame in the middle of the transition
- function removeFrameClasses(frame) {
- var classNames = ['opening', 'closing', 'opening-switching',
- 'opening-card', 'closing-card'];
-
- var classList = frame.classList;
-
- classNames.forEach(function removeClass(className) {
- classList.remove(className);
- });
- }
-
- windows.addEventListener('transitionend', function frameTransitionend(evt) {
- var prop = evt.propertyName;
- var frame = evt.target;
- if (prop !== 'transform')
- return;
-
- var classList = frame.classList;
-
- if (classList.contains('inlineActivity')) {
- if (classList.contains('active')) {
- if (openFrame)
- openFrame.firstChild.focus();
-
- setOpenFrame(null);
- } else {
- windows.removeChild(frame);
- }
-
- return;
- }
-
- if (screenElement.classList.contains('switch-app')) {
- if (classList.contains('closing')) {
- classList.remove('closing');
- classList.add('closing-card');
-
- if (openFrame) {
- if (openFrame.classList.contains('opening-card')) {
- openFrame.classList.remove('opening-card');
- openFrame.classList.add('opening-switching');
- } else {
- // Skip the opening-card and opening-switching transition
- // because the closing-card transition had already finished here.
- if (openFrame.classList.contains('fullscreen-app')) {
- screenElement.classList.add('fullscreen-app');
- }
- openFrame.classList.add('opening');
- }
- }
- } else if (classList.contains('closing-card')) {
- windowClosed(frame);
- setTimeout(closeCallback);
- closeCallback = null;
-
- } else if (classList.contains('opening-switching')) {
- // If the opening app need to be full screen, switch to full screen
- if (classList.contains('fullscreen-app')) {
- screenElement.classList.add('fullscreen-app');
- }
-
- classList.remove('opening-switching');
- classList.add('opening');
- } else if (classList.contains('opening')) {
- windowOpened(frame);
-
- setTimeout(openCallback);
- openCallback = null;
-
- setCloseFrame(null);
- setOpenFrame(null);
- screenElement.classList.remove('switch-app');
- }
-
- return;
- }
-
- if (classList.contains('opening')) {
- windowOpened(frame);
-
- setTimeout(openCallback);
- openCallback = null;
-
- setOpenFrame(null);
- } else if (classList.contains('closing')) {
- windowClosed(frame);
-
- setTimeout(closeCallback);
- closeCallback = null;
-
- setCloseFrame(null);
- }
- });
-
- // Executes when the opening transition scale the app
- // to full size.
- function windowOpened(frame) {
- var iframe = frame.firstChild;
-
- frame.classList.add('active');
- windows.classList.add('active');
-
- if ('wrapper' in frame.dataset) {
- wrapperFooter.classList.add('visible');
- }
-
- // Take the focus away from the currently displayed app
- var app = runningApps[displayedApp];
- if (app && app.iframe)
- app.iframe.blur();
-
- // Give the focus to the frame
- iframe.focus();
-
- if (!TrustedUIManager.isVisible() && !isRunningFirstRunApp) {
- // Set homescreen visibility to false
- toggleHomescreen(false);
- }
-
- // Set displayedApp to the new value
- displayedApp = iframe.dataset.frameOrigin;
-
- // Set orientation for the new app
- setOrientationForApp(displayedApp);
-
- // Dispatch an 'appopen' event.
- var manifestURL = runningApps[displayedApp].manifestURL;
- var evt = document.createEvent('CustomEvent');
- evt.initCustomEvent('appopen', true, false, {
- manifestURL: manifestURL,
- origin: displayedApp
- });
- frame.dispatchEvent(evt);
- }
-
- // Executes when app closing transition finishes.
- function windowClosed(frame) {
- var iframe = frame.firstChild;
-
- // If the FTU is closing, make sure we save this state
- if (iframe.src == ftuURL) {
- isRunningFirstRunApp = false;
- document.getElementById('screen').classList.remove('ftu');
- window.asyncStorage.setItem('ftu.enabled', false);
- // Done with FTU, letting everyone know
- var evt = document.createEvent('CustomEvent');
- evt.initCustomEvent('ftudone',
- /* canBubble */ true, /* cancelable */ false, {});
- window.dispatchEvent(evt);
- }
-
- frame.classList.remove('active');
- windows.classList.remove('active');
-
- // set the closed frame visibility to false
- if ('setVisible' in iframe)
- iframe.setVisible(false);
-
- screenElement.classList.remove('fullscreen-app');
- }
-
- // The following things needs to happen when firstpaint happens.
- // We centralize all that here but not all of them applies.
- windows.addEventListener('mozbrowserfirstpaint', function firstpaint(evt) {
- var iframe = evt.target;
- var frame = iframe.parentNode;
-
- // remove the unpainted flag
- delete iframe.dataset.unpainted;
-
- setTimeout(function firstpainted() {
- // Save the screenshot
- // Remove the background only until we actually got the screenshot,
- // because the getScreenshot() call will be pushed back by
- // painting/loading in the child process; when we got the screenshot,
- // that means the app is mostly loaded.
- // (as opposed to plain white firstpaint)
- saveAppScreenshot(frame, function screenshotTaken() {
- // Remove the default background
- frame.classList.remove('default-background');
-
- // Remove the screenshot from frame
- clearFrameBackground(frame);
- });
- });
- });
-
- // setFrameBackground() will attach the screenshot background to
- // the given frame.
- // The callback could be sync or async (depend on whether we need
- // the screenshot from database or not)
- function setFrameBackground(frame, callback, transparent) {
- var iframe = frame.firstChild;
- // If the frame is painted, or there is already background image present
- // start the transition right away.
- if (!('unpainted' in iframe.dataset) ||
- ('bgObjectURL' in frame.dataset)) {
- callback();
- return;
- }
-
- // Get the screenshot from the database
- getAppScreenshotFromDatabase(iframe.src || iframe.dataset.frameOrigin,
- function(screenshot) {
- // If firstpaint is faster than database, we will not transition
- // with screenshot.
- if (!('unpainted' in iframe.dataset)) {
- callback();
- return;
- }
-
- if (!screenshot) {
- // put a default background
- frame.classList.add('default-background');
- callback();
- return;
- }
-
- // set the screenshot as the background of the frame itself.
- // we are safe to do so since there is nothing on it yet.
- setFrameBackgroundBlob(frame, screenshot, transparent);
-
- // start the transition
- callback();
- });
- }
-
- // On-disk database for window manager.
- // It's only for app screenshots right now.
- var database = null;
- var DB_SCREENSHOT_OBJSTORE = 'screenshots';
-
- (function openDatabase() {
- var DB_VERSION = 2;
- var DB_NAME = 'window_manager';
-
- var req = window.indexedDB.open(DB_NAME, DB_VERSION);
- req.onerror = function() {
- console.error('Window Manager: opening database failed.');
- };
- req.onupgradeneeded = function databaseUpgradeneeded() {
- database = req.result;
-
- if (database.objectStoreNames.contains(DB_SCREENSHOT_OBJSTORE))
- database.deleteObjectStore(DB_SCREENSHOT_OBJSTORE);
-
- var store = database.createObjectStore(
- DB_SCREENSHOT_OBJSTORE, { keyPath: 'url' });
- };
-
- req.onsuccess = function databaseSuccess() {
- database = req.result;
- };
- })();
-
- function putAppScreenshotToDatabase(url, data) {
- if (!database)
- return;
-
- var txn = database.transaction(DB_SCREENSHOT_OBJSTORE, 'readwrite');
- txn.onerror = function() {
- console.warn(
- 'Window Manager: transaction error while trying to save screenshot.');
- };
- var store = txn.objectStore(DB_SCREENSHOT_OBJSTORE);
- var req = store.put({
- url: url,
- screenshot: data
- });
- req.onerror = function(evt) {
- console.warn(
- 'Window Manager: put error while trying to save screenshot.');
- };
- }
-
- function getAppScreenshotFromDatabase(url, callback) {
- if (!database) {
- console.warn(
- 'Window Manager: Neither database nor app frame is ' +
- 'ready for getting screenshot.');
-
- callback();
- return;
- }
-
- var req = database.transaction(DB_SCREENSHOT_OBJSTORE)
- .objectStore(DB_SCREENSHOT_OBJSTORE).get(url);
- req.onsuccess = function() {
- if (!req.result) {
- console.log('Window Manager: No screenshot in database. ' +
- 'This is expected from a fresh installed app.');
- callback();
-
- return;
- }
-
- callback(req.result.screenshot, true);
- }
- req.onerror = function(evt) {
- console.warn('Window Manager: get screenshot from database failed.');
- callback();
- };
- }
-
- function deleteAppScreenshotFromDatabase(url) {
- var txn = database.transaction(DB_SCREENSHOT_OBJSTORE);
- var store = txn.objectStore(DB_SCREENSHOT_OBJSTORE);
-
- store.delete(url);
- }
-
- function getAppScreenshotFromFrame(frame, callback) {
- if (!frame) {
- callback();
- return;
- }
-
- var iframe = frame.firstChild;
- var req = iframe.getScreenshot(iframe.offsetWidth, iframe.offsetHeight);
-
- req.onsuccess = function gotScreenshotFromFrame(evt) {
- var result = evt.target.result;
- callback(result, false);
- };
-
- req.onerror = function gotScreenshotFromFrameError(evt) {
- console.warn('Window Manager: getScreenshot failed.');
- callback();
- };
- }
-
- // Meta method for get the screenshot from the app frame,
- // and save it to database.
- function saveAppScreenshot(frame, callback) {
- getAppScreenshotFromFrame(frame, function gotScreenshot(screenshot) {
- if (callback)
- callback(screenshot);
-
- if (!screenshot)
- return;
-
- var iframe = frame.firstChild;
- putAppScreenshotToDatabase(iframe.src || iframe.dataset.frameOrigin,
- screenshot);
- });
- }
-
- // Perform an "open" animation for the app's iframe
- function openWindow(origin, callback) {
- var app = runningApps[origin];
- setOpenFrame(app.frame);
-
- openCallback = callback || function() {};
-
- // set the size of the opening app
- setAppSize(origin);
-
- if (origin === homescreen) {
- // We cannot apply background screenshot to home screen app since
- // the screenshot is encoded in JPEG and the alpha channel is
- // not perserved. See
- // https://bugzilla.mozilla.org/show_bug.cgi?id=801676#c33
- // If that resolves,
- // setFrameBackground(openFrame, gotBackground, true);
- // will simply work here.
-
- // Call the openCallback only once. We have to use tmp var as
- // openCallback can be a method calling the callback
- // (like the `removeFrame` callback in `kill()` ).
- var tmpCallback = openCallback;
- openCallback = null;
- tmpCallback();
-
- windows.classList.add('active');
- openFrame.classList.add('homescreen');
- openFrame.firstChild.focus();
- setOpenFrame(null);
- displayedApp = origin;
-
- return;
- }
-
- if (requireFullscreen(origin))
- screenElement.classList.add('fullscreen-app');
-
- transitionOpenCallback = function startOpeningTransition() {
- // We have been canceled by another transition.
- if (!openFrame || transitionOpenCallback != startOpeningTransition)
- return;
-
- // Make sure we're not called twice.
- transitionOpenCallback = null;
-
- if (!screenElement.classList.contains('switch-app')) {
- openFrame.classList.add('opening');
- } else if (!openFrame.classList.contains('opening')) {
- openFrame.classList.add('opening-card');
- }
- };
-
- if ('unpainted' in openFrame.firstChild.dataset) {
- waitForNextPaintOrBackground(openFrame, transitionOpenCallback);
- } else {
- waitForNextPaint(openFrame, transitionOpenCallback);
- }
-
- // Set the frame to be visible.
- if ('setVisible' in openFrame.firstChild) {
- if (!AttentionScreen.isFullyVisible()) {
- openFrame.firstChild.setVisible(true);
- } else {
- // If attention screen is fully visible now,
- // don't give the open frame visible.
- // This is the case that homescreen is restarted behind attention screen
- openFrame.firstChild.setVisible(false);
- }
- }
- }
-
- function waitForNextPaintOrBackground(frame, callback) {
- var waiting = true;
- function proceed() {
- if (waiting) {
- waiting = false;
- callback();
- }
- }
-
- waitForNextPaint(frame, proceed);
- setFrameBackground(frame, proceed);
- }
-
- function waitForNextPaint(frame, callback) {
- function onNextPaint() {
- clearTimeout(timeout);
- callback();
- }
-
- var iframe = frame.firstChild;
-
- // Register a timeout in case we don't receive
- // nextpaint in an acceptable time frame.
- var timeout = setTimeout(function() {
- if ('removeNextPaintListener' in iframe)
- iframe.removeNextPaintListener(onNextPaint);
- callback();
- }, kTransitionTimeout);
-
- if ('addNextPaintListener' in iframe)
- iframe.addNextPaintListener(onNextPaint);
- }
-
- // Perform a "close" animation for the app's iframe
- function closeWindow(origin, callback) {
- var app = runningApps[origin];
- setCloseFrame(app.frame);
- closeCallback = callback || function() {};
-
- // Animate the window close. Ensure the homescreen is in the
- // foreground since it will be shown during the animation.
- var homescreenFrame = ensureHomescreen();
-
- // invoke openWindow to show homescreen here
- openWindow(homescreen, null);
-
- // Take keyboard focus away from the closing window
- closeFrame.firstChild.blur();
-
- // set orientation for homescreen app
- setOrientationForApp(homescreen);
-
- // Set the size of both homescreen app and the closing app
- // since the orientation had changed.
- setAppSize(homescreen);
- setAppSize(origin);
-
- // Send a synthentic 'appwillclose' event.
- // The keyboard uses this and the appclose event to know when to close
- // See https://github.com/andreasgal/gaia/issues/832
- var evt = document.createEvent('CustomEvent');
- evt.initCustomEvent('appwillclose', true, false, { origin: origin });
- closeFrame.dispatchEvent(evt);
-
- if ('wrapper' in closeFrame.dataset) {
- wrapperHeader.classList.remove('visible');
- wrapperFooter.classList.remove('visible');
- }
-
- transitionCloseCallback = function startClosingTransition() {
- // We have been canceled by another transition.
- if (!closeFrame || transitionCloseCallback != startClosingTransition)
- return;
-
- // Make sure we're not called twice.
- transitionCloseCallback = null;
-
- // Start the transition
- closeFrame.classList.add('closing');
- closeFrame.classList.remove('active');
- };
-
- waitForNextPaint(homescreenFrame, transitionCloseCallback);
- }
-
- // Perform a "switching" animation for the closing frame and the opening frame
- function switchWindow(origin, callback) {
- // This will trigger different transition to both openWindow()
- // and closeWindow() transition.
- screenElement.classList.add('switch-app');
-
- // Ask closeWindow() to start closing the displayedApp
- closeWindow(displayedApp, callback);
-
- // Ask openWindow() to show a card on the right waiting to be opened
- openWindow(origin);
- }
-
- // Ensure the homescreen is loaded and return its frame. Restarts
- // the homescreen app if it was killed in the background.
- // Note: this function would not invoke openWindow(homescreen),
- // which should be handled in setDisplayedApp and in closeWindow()
- function ensureHomescreen(reset) {
- // If the url of the homescreen is not known at this point do nothing.
- if (!homescreen || !homescreenManifestURL) {
- return null;
- }
-
- if (!isRunning(homescreen)) {
- var app = Applications.getByManifestURL(homescreenManifestURL);
- appendFrame(null, homescreen, homescreenURL,
- app.manifest.name, app.manifest, app.manifestURL);
- runningApps[homescreen].iframe.dataset.start = Date.now();
- setAppSize(homescreen);
- } else if (reset) {
- runningApps[homescreen].iframe.src = homescreenURL;
- setAppSize(homescreen);
- }
-
- return runningApps[homescreen].frame;
- }
-
- function retrieveHomescreen(callback) {
- var lock = navigator.mozSettings.createLock();
- var setting = lock.get('homescreen.manifestURL');
- setting.onsuccess = function() {
- var app =
- Applications.getByManifestURL(this.result['homescreen.manifestURL']);
-
- // XXX This is a one-day workaround to not break everybody and make sure
- // work can continue.
- if (!app) {
- var tmpURL = document.location.toString()
- .replace('system', 'homescreen')
- .replace('index.html', 'manifest.webapp');
- app = Applications.getByManifestURL(tmpURL);
- }
-
- if (app) {
- homescreenManifestURL = app.manifestURL;
- homescreen = app.origin;
- homescreenURL = app.origin + '/index.html#root';
-
- callback(app);
- }
- }
- }
-
- function skipFTU() {
- document.getElementById('screen').classList.remove('ftuStarting');
- handleInitlogo();
- setDisplayedApp(homescreen);
- }
-
- // Check if the FTU was executed or not, if not, get a
- // reference to the app and launch it.
- function retrieveFTU() {
- window.asyncStorage.getItem('ftu.enabled', function getItem(launchFTU) {
- document.getElementById('screen').classList.add('ftuStarting');
- if (launchFTU === false) {
- skipFTU();
- return;
- }
- var lock = navigator.mozSettings.createLock();
- var req = lock.get('ftu.manifestURL');
- req.onsuccess = function() {
- ftuManifestURL = this.result['ftu.manifestURL'];
- if (!ftuManifestURL) {
- dump('FTU manifest cannot be found skipping.\n');
- skipFTU();
- return;
- }
- ftu = Applications.getByManifestURL(ftuManifestURL);
- if (!ftu) {
- dump('Opps, bogus FTU manifest.\n');
- skipFTU();
- return;
- }
- ftuURL = ftu.origin + ftu.manifest.entry_points['ftu'].launch_path;
- ftu.launch('ftu');
- };
- req.onerror = function() {
- dump('Couldn\'t get the ftu manifestURL.\n');
- skipFTU();
- };
- });
- }
-
- // Hide current app
- function hideCurrentApp(callback) {
- if (displayedApp == null || displayedApp == homescreen)
- return;
-
- toggleHomescreen(true);
- var frame = getAppFrame(displayedApp);
- frame.classList.add('back');
- frame.classList.remove('restored');
- if (callback) {
- frame.addEventListener('transitionend', function execCallback() {
- frame.style.visibility = 'hidden';
- frame.removeEventListener('transitionend', execCallback);
- callback();
- });
- }
- }
-
- // If app parameter is passed,
- // it means there's a specific app needs to be restored
- // instead of current app
- function restoreCurrentApp(app) {
- if (app) {
- // Restore app visibility immediately but don't open it.
- var frame = getAppFrame(app);
- frame.style.visibility = 'visible';
- frame.classList.remove('back');
- } else {
- app = displayedApp;
- toggleHomescreen(false);
- var frame = getAppFrame(app);
- frame.style.visibility = 'visible';
- frame.classList.remove('back');
- frame.classList.add('restored');
- frame.addEventListener('transitionend', function removeRestored() {
- frame.removeEventListener('transitionend', removeRestored);
- frame.classList.remove('restored');
- });
- }
- }
-
- function toggleHomescreen(visible) {
- var homescreenFrame = ensureHomescreen();
- if (homescreenFrame && 'setVisible' in homescreenFrame.firstChild)
- homescreenFrame.firstChild.setVisible(visible);
- }
-
- // Switch to a different app
- function setDisplayedApp(origin, callback) {
- var currentApp = displayedApp, newApp = origin || homescreen;
- var isFirstRunApplication = !currentApp && (origin == ftuURL);
-
- var homescreenFrame = null;
- if (!isFirstRunApplication) {
- // Returns the frame reference of the home screen app.
- // Restarts the homescreen app if it was killed in the background.
- homescreenFrame = ensureHomescreen();
- }
-
- // Cancel transitions waiting to be started.
- transitionOpenCallback = null;
- transitionCloseCallback = null;
-
- // Discard any existing activity
- stopInlineActivity(true);
-
- // Before starting a new transition, let's make sure current transitions
- // are stopped and the state classes are cleaned up.
- // visibility status should also be reset.
- if (openFrame && 'setVisible' in openFrame.firstChild)
- openFrame.firstChild.setVisible(false);
- if (closeFrame && 'setVisible' in closeFrame.firstChild)
- closeFrame.firstChild.setVisible(false);
-
- if (!isFirstRunApplication && newApp == homescreen && !AttentionScreen.isFullyVisible()) {
- toggleHomescreen(true);
- }
-
- setOpenFrame(null);
- setCloseFrame(null);
- screenElement.classList.remove('switch-app');
- screenElement.classList.remove('fullscreen-app');
-
- // Dispatch an appwillopen event only when we open an app
- if (newApp != currentApp) {
- var evt = document.createEvent('CustomEvent');
- evt.initCustomEvent('appwillopen', true, true, { origin: newApp });
-
- var app = runningApps[newApp];
- // Allows listeners to cancel app opening and so stay on homescreen
- if (!app.frame.dispatchEvent(evt)) {
- if (typeof(callback) == 'function')
- callback();
- return;
- }
-
- var iframe = app.iframe;
-
- // unpainted means that the app is cold booting
- // if it is, we're going to listen for Browser API's loadend event
- // which indicates that the iframe's document load is complete
- //
- // if the app is not cold booting (is in memory) we will listen
- // to appopen event, which is fired when the transition to the
- // app window is complete.
- //
- // [w] - warm boot (app is in memory, just transition to it)
- // [c] - cold boot (app has to be booted, we show it's document load
- // time)
- var type;
- if ('unpainted' in iframe.dataset) {
- type = 'mozbrowserloadend';
- } else {
- iframe.dataset.start = Date.now();
- type = 'appopen';
- }
-
- app.frame.addEventListener(type, function apploaded(e) {
- e.target.removeEventListener(e.type, apploaded);
-
- var evt = document.createEvent('CustomEvent');
- evt.initCustomEvent('apploadtime', true, false, {
- time: parseInt(Date.now() - iframe.dataset.start),
- type: (e.type == 'appopen') ? 'w' : 'c'
- });
- iframe.dispatchEvent(evt);
- });
- }
-
- // Case 1: the app is already displayed
- if (currentApp && currentApp == newApp) {
- if (newApp == homescreen) {
- // relaunch homescreen
- openWindow(homescreen, callback);
- } else if (callback) {
- // Just run the callback right away if it is not homescreen
- callback();
- }
- }
- // Case 2: null --> app
- else if (isFirstRunApplication) {
- isRunningFirstRunApp = true;
- openWindow(newApp, function windowOpened() {
- handleInitlogo(function() {
- var mainScreen = document.getElementById('screen');
- mainScreen.classList.add('ftu');
- mainScreen.classList.remove('ftuStarting');
- });
- });
- }
- // Case 3: null->homescreen || homescreen->app
- else if ((!currentApp && newApp == homescreen) ||
- (currentApp == homescreen && newApp)) {
- openWindow(newApp, callback);
- }
- // Case 4: app->homescreen
- else if (currentApp && currentApp != homescreen && newApp == homescreen) {
- // For screenshot to catch current window size
- closeWindow(currentApp, callback);
- }
- // Case 5: app-to-app transition
- else {
- switchWindow(newApp, callback);
- }
- // Set homescreen as active,
- // to control the z-index between homescreen & keyboard iframe
- if ((newApp == homescreen) && homescreenFrame) {
- homescreenFrame.classList.add('active');
- } else {
- homescreenFrame.classList.remove('active');
- }
-
- // Record the time when app was launched,
- // need this to display apps in proper order on CardsView.
- // We would also need this to determined the freshness of the frame
- // for making screenshots.
- if (newApp)
- runningApps[newApp].launchTime = Date.now();
-
- // If the app has a attention screen open, displaying it
- AttentionScreen.showForOrigin(newApp);
- }
-
- function setOrientationForApp(origin) {
- if (origin == null) { // No app is currently running.
- screen.mozLockOrientation('portrait-primary');
- return;
- }
-
- var app = runningApps[origin];
- if (!app)
- return;
- var manifest = app.manifest;
-
- if (manifest.orientation) {
- var rv = screen.mozLockOrientation(manifest.orientation);
- if (rv === false) {
- console.warn('screen.mozLockOrientation() returned false for',
- origin, 'orientation', manifest.orientation);
- }
- }
- else { // If no orientation was requested, then let it rotate
- screen.mozUnlockOrientation();
- }
- }
-
- var isOutOfProcessDisabled = false;
- SettingsListener.observe('debug.oop.disabled', false, function(value) {
- isOutOfProcessDisabled = value;
- });
-
- function createFrame(origFrame, origin, url, name, manifest, manifestURL) {
- var iframe = origFrame || document.createElement('iframe');
- iframe.setAttribute('mozallowfullscreen', 'true');
-
- var frame = document.createElement('div');
- frame.appendChild(iframe);
- frame.className = 'appWindow';
-
- iframe.dataset.frameOrigin = origin;
- // Save original frame URL in order to restore it on frame load error
- iframe.dataset.frameURL = url;
-
- // Note that we don't set the frame size here. That will happen
- // when we display the app in setDisplayedApp()
-
- // frames are began unpainted.
- iframe.dataset.unpainted = true;
-
- if (!manifestURL) {
- frame.setAttribute('data-wrapper', 'true');
- return frame;
- }
-
- // Most apps currently need to be hosted in a special 'mozbrowser' iframe.
- // They also need to be marked as 'mozapp' to be recognized as apps by the
- // platform.
- iframe.setAttribute('mozbrowser', 'true');
-
- // These apps currently have bugs preventing them from being
- // run out of process. All other apps will be run OOP.
- //
- var outOfProcessBlackList = [
- 'Browser'
- // Requires nested content processes (bug 761935). This is not
- // on the schedule for v1.
- ];
-
- if (!isOutOfProcessDisabled &&
- outOfProcessBlackList.indexOf(name) === -1) {
- // FIXME: content shouldn't control this directly
- iframe.setAttribute('remote', 'true');
- }
-
- iframe.setAttribute('mozapp', manifestURL);
- iframe.src = url;
- return frame;
- }
-
- function appendFrame(origFrame, origin, url, name, manifest, manifestURL) {
- // Create the <iframe mozbrowser mozapp> that hosts the app
- var frame =
- createFrame(origFrame, origin, url, name, manifest, manifestURL);
- var iframe = frame.firstChild;
- frame.id = 'appframe' + nextAppId++;
- iframe.dataset.frameType = 'window';
-
- // Give a name to the frame for differentiating between main frame and
- // inline frame. With the name we can get frames of the same app using the
- // window.open method.
- iframe.name = 'main';
-
- // If this frame corresponds to the homescreen, set mozapptype=homescreen
- // so we're less likely to kill this frame's process when we're running low
- // on memory.
- //
- // We must do this before we the appendChild() call below. Once
- // we add this frame to the document, we can't change its app type.
- if (origin === homescreen) {
- iframe.setAttribute('mozapptype', 'homescreen');
- }
-
- // Add the iframe to the document
- windows.appendChild(frame);
-
- // And map the app origin to the info we need for the app
- var app = new AppWindow({
- origin: origin,
- name: name,
- manifest: manifest,
- manifestURL: manifestURL,
- frame: frame,
- iframe: iframe,
- launchTime: 0
- });
- runningApps[origin] = app;
-
- if (requireFullscreen(origin)) {
- frame.classList.add('fullscreen-app');
- }
- if (origin === ftuURL) {
- // Add a way to identify ftu app
- // (Used by SimLock)
- frame.classList.add('ftu');
- }
-
- // A frame should start with visible false
- if ('setVisible' in iframe)
- iframe.setVisible(false);
-
- numRunningApps++;
-
- return app;
- }
-
- function startInlineActivity(origin, url, name, manifest, manifestURL) {
- // Create the <iframe mozbrowser mozapp> that hosts the app
- var frame = createFrame(null, origin, url, name, manifest, manifestURL);
- var iframe = frame.firstChild;
- frame.classList.add('inlineActivity');
- iframe.dataset.frameType = 'inline-activity';
-
- // Give a name to the frame for differentiating between main frame and
- // inline frame. With the name we can get frames of the same app using the
- // window.open method.
- iframe.name = 'inline';
-
- // Save the reference
- inlineActivityFrames.push(frame);
-
- // Set the size
- setInlineActivityFrameSize();
-
- // Add the iframe to the document
- windows.appendChild(frame);
-
- // Open the frame, first, store the reference
- openFrame = frame;
-
- // set the frame to visible state
- if ('setVisible' in iframe)
- iframe.setVisible(true);
-
- setFrameBackground(openFrame, function gotBackground() {
- // Start the transition when this async/sync callback is called.
- openFrame.classList.add('active');
- if (inlineActivityFrames.length == 1)
- activityCallerOrigin = displayedApp;
- if ('wrapper' in runningApps[displayedApp].frame.dataset) {
- wrapperFooter.classList.remove('visible');
- wrapperHeader.classList.remove('visible');
- }
- });
- }
-
- function removeFrame(origin) {
- var app = runningApps[origin];
- var frame = app.frame;
-
- if (frame) {
- windows.removeChild(frame);
- clearFrameBackground(frame);
- }
-
- if (openFrame == frame) {
- setOpenFrame(null);
- setTimeout(openCallback);
- openCallback = null;
- }
- if (closeFrame == frame) {
- setCloseFrame(null);
- setTimeout(closeCallback);
- closeCallback = null;
- }
-
- delete runningApps[origin];
- numRunningApps--;
- }
-
- function removeInlineFrame(frame) {
- // If frame is transitioning we should remove the reference
- if (openFrame == frame)
- setOpenFrame(null);
-
- // If frame is never set visible, we can remove the frame directly
- // without closing transition
- if (!frame.classList.contains('active')) {
- windows.removeChild(frame);
- return;
- }
- // Take keyboard focus away from the closing window
- frame.firstChild.blur();
- // Remove the active class and start the closing transition
- frame.classList.remove('active');
- }
-
- // If all is not specified,
- // remove the top most frame
- function stopInlineActivity(all) {
- if (!inlineActivityFrames.length)
- return;
-
- if (!all) {
- var frame = inlineActivityFrames.pop();
- removeInlineFrame(frame);
- } else {
- // stop all activity frames
- // Remore the inlineActivityFrame reference
- for (var frame of inlineActivityFrames) {
- removeInlineFrame(frame);
- }
- inlineActivityFrames = [];
- }
-
- if (!inlineActivityFrames.length) {
- // Give back focus to the displayed app
- var app = runningApps[displayedApp];
- if (app && app.iframe) {
- app.iframe.focus();
- if ('wrapper' in app.frame.dataset) {
- wrapperFooter.classList.add('visible');
- }
- }
- screenElement.classList.remove('inline-activity');
- }
- }
-
- // Watch activity completion here instead of activity.js
- // Because we know when and who to re-launch when activity ends.
- window.addEventListener('mozChromeEvent', function(e) {
- if (e.detail.type == 'activity-done') {
- // Remove the top most frame every time we get an 'activity-done' event.
- stopInlineActivity();
- if (!inlineActivityFrames.length) {
- setDisplayedApp(activityCallerOrigin);
- activityCallerOrigin = '';
- }
- }
- });
-
- // There are two types of mozChromeEvent we need to handle
- // in order to launch the app for Gecko
- window.addEventListener('mozChromeEvent', function(e) {
- var startTime = Date.now();
-
- var manifestURL = e.detail.manifestURL;
- if (!manifestURL)
- return;
-
- var app = Applications.getByManifestURL(manifestURL);
- if (!app)
- return;
-
- var manifest = app.manifest;
- var name = new ManifestHelper(manifest).name;
- var origin = app.origin;
-
- // Check if it's a virtual app from a entry point.
- // If so, change the app name and origin to the
- // entry point.
- var entryPoints = manifest.entry_points;
- if (entryPoints && manifest.type == 'certified') {
- var givenPath = e.detail.url.substr(origin.length);
-
- // Workaround here until the bug (to be filed) is fixed
- // Basicly, gecko is sending the URL without launch_path sometimes
- for (var ep in entryPoints) {
- var currentEp = entryPoints[ep];
- var path = givenPath;
- if (path.indexOf('?') != -1) {
- path = path.substr(0, path.indexOf('?'));
- }
-
- //Remove the origin and / to find if if the url is the entry point
- if (path.indexOf('/' + ep) == 0 &&
- (currentEp.launch_path == path)) {
- origin = origin + currentEp.launch_path;
- name = new ManifestHelper(currentEp).name;
- }
- }
- }
- switch (e.detail.type) {
- // mozApps API is asking us to launch the app
- // We will launch it in foreground
- case 'webapps-launch':
- if (origin == homescreen) {
- // No need to append a frame if is homescreen
- setDisplayedApp();
- } else {
- if (!isRunning(origin)) {
- appendFrame(null, origin, e.detail.url,
- name, app.manifest, app.manifestURL);
- }
- runningApps[origin].iframe.dataset.start = startTime;
- setDisplayedApp(origin, null, 'window');
- }
- break;
- // 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.
- case 'open-app':
- // If the system message goes to System app,
- // we should not be launching that in a frame.
- if (e.detail.url === window.location.href)
- return;
-
- if (e.detail.isActivity && e.detail.target.disposition &&
- e.detail.target.disposition == 'inline') {
- // Inline activities behaves more like a dialog,
- // let's deal them here.
-
- startInlineActivity(origin, e.detail.url,
- name, manifest, app.manifestURL);
-
- return;
- }
-
- if (isRunning(origin)) {
- // If the app is in foreground, it's too risky to change it's
- // URL. We'll ignore this request.
- if (displayedApp !== origin) {
- var iframe = getAppFrame(origin).firstChild;
-
- // If the app is opened and it is loaded to the correct page,
- // then there is nothing to do.
- if (iframe.src !== e.detail.url) {
- // Rewrite the URL of the app frame to the requested URL.
- // XXX: We could ended opening URls not for the app frame
- // in the app frame. But we don't care.
- iframe.src = e.detail.url;
- }
- }
- } else if (origin !== homescreen) {
- // XXX: We could ended opening URls not for the app frame
- // in the app frame. But we don't care.
- appendFrame(null, origin, e.detail.url,
- name, manifest, app.manifestURL);
-
- // set the size of the iframe
- // so Cards View will get a correct screenshot of the frame
- if (!e.detail.isActivity)
- setAppSize(origin, false);
- } else {
- ensureHomescreen();
- }
-
- // We will only bring web activity handling apps to the foreground
- if (!e.detail.isActivity)
- return;
-
- // XXX: the correct way would be for UtilityTray to close itself
- // when there is a appwillopen/appopen event.
- UtilityTray.hide();
-
- setDisplayedApp(origin);
-
- break;
- }
- });
-
- // If the application tried to close themselves by calling window.close()
- // we will handle that here.
- // XXX: this event is fired twice:
- // https://bugzilla.mozilla.org/show_bug.cgi?id=814583
- window.addEventListener('mozbrowserclose', function(e) {
- if (!'frameType' in e.target.dataset)
- return;
-
- switch (e.target.dataset.frameType) {
- case 'window':
- kill(e.target.dataset.frameOrigin);
- break;
-
- case 'inline-activity':
- stopInlineActivity(true);
- break;
- }
- });
-
- // Deal with locationchange
- window.addEventListener('mozbrowserlocationchange', function(e) {
- if (!'frameType' in e.target.dataset)
- return;
-
- e.target.dataset.url = e.detail;
- });
-
- // Deal with application uninstall event
- // if the application is being uninstalled, we ensure it stop running here.
- window.addEventListener('applicationuninstall', function(e) {
- kill(e.detail.application.origin);
-
- deleteAppScreenshotFromDatabase(e.detail.application.origin);
- });
-
- // When an UI layer is overlapping the current app,
- // WindowManager should set the visibility of app iframe to false
- // And reset to true when the layer is gone.
- // We may need to handle windowclosing, windowopened in the future.
- var attentionScreenTimer = null;
-
- var overlayEvents = [
- 'lock',
- 'will-unlock',
- 'attentionscreenshow',
- 'attentionscreenhide',
- 'status-active',
- 'status-inactive'
- ];
-
- function overlayEventHandler(evt) {
- if (attentionScreenTimer)
- clearTimeout(attentionScreenTimer);
- switch (evt.type) {
- case 'status-active':
- case 'attentionscreenhide':
- case 'will-unlock':
- if (LockScreen.locked)
- return;
- if (inlineActivityFrames.length) {
- setVisibilityForInlineActivity(true);
- } else {
- setVisibilityForCurrentApp(true);
- }
- break;
- case 'lock':
- setVisibilityForCurrentApp(false);
- break;
-
- /*
- * Because in-transition is needed in attention screen,
- * We set a timer here to deal with visibility change
- */
- case 'status-inactive':
- if (!AttentionScreen.isVisible())
- return;
- case 'attentionscreenshow':
- if (evt.detail && evt.detail.origin &&
- evt.detail.origin != displayedApp) {
- attentionScreenTimer = setTimeout(function setVisibility() {
- if (inlineActivityFrames.length) {
- setVisibilityForInlineActivity(false);
- } else {
- setVisibilityForCurrentApp(false);
- }
- }, 3000);
-
- // Immediatly blur the frame in order to ensure hiding the keyboard
- var app = runningApps[displayedApp];
- if (app)
- app.iframe.blur();
- }
- break;
- }
- }
-
- overlayEvents.forEach(function overlayEventIterator(event) {
- window.addEventListener(event, overlayEventHandler);
- });
-
- function setVisibilityForInlineActivity(visible) {
- if (!inlineActivityFrames.length)
- return;
-
- var topFrame = inlineActivityFrames[inlineActivityFrames.length - 1].firstChild;
- if ('setVisible' in topFrame) {
- topFrame.setVisible(visible);
- }
-
- // Restore/give away focus on visiblity change
- // so that the app can take back its focus
- if (visible) {
- topFrame.focus();
- } else {
- topFrame.blur();
- }
- }
-
- function setVisibilityForCurrentApp(visible) {
- var app = runningApps[displayedApp];
- if (!app)
- return;
- if ('setVisible' in app.iframe)
- app.iframe.setVisible(visible);
-
- // Restore/give away focus on visiblity change
- // so that the app can take back its focus
- if (visible)
- app.iframe.focus();
- else
- app.iframe.blur();
- }
-
- function handleAppCrash(origin, manifestURL) {
- if (origin && manifestURL) {
- // When inline activity frame crashes,
- // query the localized name from manifest
- var app = Applications.getByManifestURL(manifestURL);
- CrashReporter.setAppName(getAppName(origin, app.manifest));
- } else {
- var app = runningApps[displayedApp];
- CrashReporter.setAppName(app.name);
- }
- }
-
- function getAppName(origin, manifest) {
- if (!manifest)
- return '';
-
- if (manifest.entry_points && manifest.type == 'certified') {
- var entryPoint = manifest.entry_points[origin.split('/')[3]];
- return new ManifestHelper(entryPoint).name;
- }
- return new ManifestHelper(manifest).name;
- }
-
- // Deal with crashed apps
- window.addEventListener('mozbrowsererror', function(e) {
- if (!'frameType' in e.target.dataset)
- return;
-
- var origin = e.target.dataset.frameOrigin;
- var manifestURL = e.target.getAttribute('mozapp');
-
- if (e.target.dataset.frameType == 'inline-activity') {
- stopInlineActivity(true);
- handleAppCrash(origin, manifestURL);
- return;
- }
-
- if (e.target.dataset.frameType !== 'window')
- return;
-
- /*
- detail.type = error (Server Not Found case)
- is handled in Modal Dialog
- */
- if (e.detail.type !== 'fatal')
- return;
-
- // If the crashing app is currently displayed, we will present
- // the user with a banner notification.
- if (displayedApp == origin)
- handleAppCrash();
-
- // If the crashing app is the home screen app and it is the displaying app
- // we will need to relaunch it right away.
- // Alternatively, if home screen is not the displaying app,
- // we will not relaunch it until the foreground app is closed.
- // (to be dealt in setDisplayedApp(), not here)
- if (displayedApp == homescreen) {
- kill(origin, function relaunchHomescreen() {
- setDisplayedApp(homescreen);
- });
- return;
- }
-
- // Actually remove the frame, and trigger the closing transition
- // if the app is currently displaying
- kill(origin);
- });
-
-
- function hasPermission(app, permission) {
- var mozPerms = navigator.mozPermissionSettings;
- if (!mozPerms)
- return false;
-
- var value = mozPerms.get(permission, app.manifestURL, app.origin, false);
-
- return (value === 'allow');
- }
-
- // Use a setting in order to be "called" by settings app
- navigator.mozSettings.addObserver(
- 'clear.remote-windows.data',
- function clearRemoteWindowsData(setting) {
- var shouldClear = setting.settingValue;
- if (!shouldClear)
- return;
-
- // Delete all storage and cookies from our content processes
- var request = navigator.mozApps.getSelf();
- request.onsuccess = function() {
- request.result.clearBrowserData();
- };
-
- // Reset the setting value to false
- var lock = navigator.mozSettings.createLock();
- lock.set({'clear.remote-windows.data': false});
- });
-
- // Watch for window.open usages in order to open wrapper frames
- window.addEventListener('mozbrowseropenwindow', function handleWrapper(evt) {
- var detail = evt.detail;
- var features;
- try {
- features = JSON.parse(detail.features);
- } catch (e) {
- features = {};
- }
-
- // Handles only call to window.open with `{remote: true}` feature.
- if (!features.remote)
- return;
-
- // XXX bug 819882: for now, only allows homescreen to open oop windows
- var callerIframe = evt.target;
- var callerFrame = callerIframe.parentNode;
- var manifestURL = callerIframe.getAttribute('mozapp');
- var callerApp = Applications.getByManifestURL(manifestURL);
- if (!callerApp || !callerFrame.classList.contains('homescreen'))
- return;
- var callerOrigin = callerApp.origin;
-
- // So, we are going to open a remote window.
- // Now, avoid PopupManager listener to be fired.
- evt.stopImmediatePropagation();
-
- var name = detail.name;
- var url = detail.url;
-
- // 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;
- var app = null;
- if (name == '_blank') {
- origin = url;
-
- // Just bring on top if a wrapper window is already running with this url
- if (origin in runningApps &&
- runningApps[origin].windowName == '_blank') {
- setDisplayedApp(origin);
- return;
- }
- } else {
- origin = 'window:' + name + ',source:' + callerOrigin;
-
- var runningApp = runningApps[origin];
- if (runningApp && runningApp.windowName === name) {
- if (runningApp.iframe.src === url) {
- // If the url is already loaded, just display the app
- setDisplayedApp(origin);
- return;
- } else {
- // Wrapper context shouldn't be shared between two apps -> killing
- kill(origin);
- }
- }
- }
-
- var title = '', icon = '', remote = false, useAsyncPanZoom = false;
- var originName, originURL, searchName, searchURL;
-
- try {
- var features = JSON.parse(detail.features);
- var regExp = new RegExp('&nbsp;', 'g');
-
- title = features.name.replace(regExp, ' ') || url;
- icon = features.icon || '';
-
- if (features.origin) {
- originName = features.origin.name.replace(regExp, ' ');
- originURL = decodeURIComponent(features.origin.url);
- }
-
- if (features.search) {
- searchName = features.search.name.replace(regExp, ' ');
- searchURL = decodeURIComponent(features.search.url);
- }
-
- if (features.useAsyncPanZoom)
- useAsyncPanZoom = true;
- } catch (ex) { }
-
- // If we don't reuse an existing app, open a brand new one
- var iframe;
- if (!app) {
- // Bug 807438: Move new window document OOP
- // Ignore `event.detail.frameElement` for now in order
- // to create a remote system app frame.
- // So that new window documents are going to share
- // system app content processes data jar.
- iframe = document.createElement('iframe');
- iframe.setAttribute('mozbrowser', 'true');
- iframe.setAttribute('remote', 'true');
-
- iframe.addEventListener('mozbrowserloadstart', function start() {
- iframe.dataset.loading = true;
- wrapperHeader.classList.add('visible');
- });
-
- iframe.addEventListener('mozbrowserloadend', function end() {
- delete iframe.dataset.loading;
- wrapperHeader.classList.remove('visible');
- });
-
- // `mozasyncpanzoom` only works when added before attaching the iframe
- // node to the document.
- if (useAsyncPanZoom) {
- iframe.dataset.useAsyncPanZoom = true;
- iframe.setAttribute('mozasyncpanzoom', 'true');
- }
-
- var app = appendFrame(iframe, origin, url, title, {
- 'name': title
- }, null);
-
- // Set the window name in order to reuse this app if we try to open
- // a new window with same name
- app.windowName = name;
- } else {
- iframe = app.iframe;
-
- // Update app name for the card view
- app.manifest.name = title;
- }
-
- iframe.dataset.name = title;
- iframe.dataset.icon = icon;
-
- if (originName)
- iframe.dataset.originName = originName;
- if (originURL)
- iframe.dataset.originURL = originURL;
-
- if (searchName)
- iframe.dataset.searchName = searchName;
- if (searchURL)
- iframe.dataset.searchURL = searchURL;
-
- // First load blank page in order to hide previous website
- iframe.src = url;
-
- setDisplayedApp(origin);
- }, true); // Use capture in order to catch the event before PopupManager does
-
-
- // Stop running the app with the specified origin
- function kill(origin, callback) {
- if (!isRunning(origin))
- return;
-
- // As we can't immediatly remove runningApps entry,
- // we flag it as being killed in order to avoid trying to remove it twice.
- // (Check required because of bug 814583)
- if (runningApps[origin].killed)
- return;
- runningApps[origin].killed = true;
-
- // If the app is the currently displayed app, switch to the homescreen
- if (origin === displayedApp) {
- // when the homescreen is displayed and being
- // killed we need to forcibly restart it...
- if (origin === homescreen) {
- removeFrame(origin);
-
- // XXX workaround bug 810431.
- // we need this here and not in other situations
- // as it is expected that homescreen frame is available.
- setTimeout(function() {
- setDisplayedApp();
-
- if (callback) {
- callback();
- }
- });
- } else {
- setDisplayedApp(homescreen, function() {
- removeFrame(origin);
- if (callback)
- setTimeout(callback);
- });
- }
-
- } else {
- removeFrame(origin);
- }
-
- // Send a synthentic 'appterminated' event.
- // Let other system app module know an app is
- // being killed, removed or crashed.
- var evt = document.createEvent('CustomEvent');
- evt.initCustomEvent('appterminated', true, false, { origin: origin });
- window.dispatchEvent(evt);
- }
-
- // Reload the frame of the running app
- function reload(origin) {
- if (!isRunning(origin))
- return;
-
- var app = runningApps[origin];
- app.reload();
- }
-
- // When a resize event occurs, resize the running app, if there is one
- // When the status bar is active it doubles in height so we need a resize
- var appResizeEvents = ['resize', 'status-active', 'status-inactive',
- 'keyboardchange', 'keyboardhide',
- 'attentionscreenhide'];
- appResizeEvents.forEach(function eventIterator(event) {
- window.addEventListener(event, function on(evt) {
- if (event == 'keyboardchange') {
- // Cancel fullscreen if keyboard pops
- if (document.mozFullScreen)
- document.mozCancelFullScreen();
-
- setAppHeight(evt.detail.height);
- } else if (displayedApp) {
- setAppSize(displayedApp);
- }
- });
- });
-
- window.addEventListener('home', function(e) {
- // If the lockscreen is active, it will stop propagation on this event
- // and we'll never see it here. Similarly, other overlays may use this
- // event to hide themselves and may prevent the event from getting here.
- // Note that for this to work, the lockscreen and other overlays must
- // be included in index.html before this one, so they can register their
- // event handlers before we do.
-
- // If we are currently transitioning, the user would like to cancel
- // it instead of toggling homescreen panels.
- var inTransition = !!(openFrame || closeFrame);
-
- if (document.mozFullScreen) {
- document.mozCancelFullScreen();
- }
-
- if (displayedApp !== homescreen || inTransition) {
- if (displayedApp != ftuURL) {
- setDisplayedApp(homescreen);
- } else {
- e.preventDefault();
- }
- } else {
- stopInlineActivity(true);
- ensureHomescreen(true);
- }
- });
-
- // Cancel dragstart event to workaround
- // https://bugzilla.mozilla.org/show_bug.cgi?id=783076
- // which stops OOP home screen pannable with left mouse button on
- // B2G/Desktop.
- windows.addEventListener('dragstart', function(evt) {
- evt.preventDefault();
- }, true);
-
- // With all important event handlers in place, we can now notify
- // Gecko that we're ready for certain system services to send us
- // messages (e.g. the radio).
- // Note that shell.js starts listen for the mozContentEvent event at
- // mozbrowserloadstart, which sometimes does not happen till window.onload.
- window.addEventListener('load', function wm_loaded() {
- window.removeEventListener('load', wm_loaded);
-
- var evt = new CustomEvent('mozContentEvent',
- { bubbles: true, cancelable: false,
- detail: { type: 'system-message-listener-ready' } });
- window.dispatchEvent(evt);
- });
-
- // This is code copied from
- // http://dl.dropbox.com/u/8727858/physical-events/index.html
- // It appears to workaround the Nexus S bug where we're not
- // getting orientation data. See:
- // https://bugzilla.mozilla.org/show_bug.cgi?id=753245
- // It seems it needs to be in both window_manager.js and bootstrap.js.
- function dumbListener2(event) {}
- window.addEventListener('devicemotion', dumbListener2);
-
- window.setTimeout(function() {
- window.removeEventListener('devicemotion', dumbListener2);
- }, 2000);
-
- // Return the object that holds the public API
- return {
- isFtuRunning: function() {
- return isRunningFirstRunApp;
- },
- launch: launch,
- kill: kill,
- reload: reload,
- getDisplayedApp: getDisplayedApp,
- setOrientationForApp: setOrientationForApp,
- getAppFrame: getAppFrame,
- getRunningApps: function() {
- return runningApps;
- },
- setDisplayedApp: setDisplayedApp,
- getCurrentDisplayedApp: function() {
- return runningApps[displayedApp];
- },
- hideCurrentApp: hideCurrentApp,
- restoreCurrentApp: restoreCurrentApp,
- retrieveHomescreen: retrieveHomescreen,
- retrieveFTU: retrieveFTU
- };
-}());
-