Source: js/lockscreen_window.js

/* globals LockScreenAgent, LazyLoader */
/* global Service */
'use strict';

(function(exports) {
  var AppWindow = window.AppWindow;

  /**
   * This window is inherit the AppWindow, and modifies some properties
   * different from the later.
   *
   * @constructor LockScreenWindow
   * @augments AppWindow
   */
  var LockScreenWindow = function() {
    // Before we make lockscreen as an app (Bug 898348 ), which would
    // own its own manifest, we must mock a manifest for him.
    this.configs = {
      url: window.location.href,
      manifest: {
        orientation: ['default']
      },
      name: 'Lockscreen',
      // No manifestURL + no chrome would cause a default chrome app
      manifestURL: window.location.href.replace('system', 'lockscreen') +
                  '/manifest.webapp',
      origin: window.location.origin.replace('system', 'lockscreen'),
      inputWindow: {
        resizeMode: false
      }
    };
    this.iframe = this.createFrame();
    if (!this.iframe || !this.iframe.parentNode) {
      // XXX: Lockscreen DOM elements are not copied.
      return;
    }

    LazyLoader.load(['js/lockscreen_agent.js']).then(() => {
      this.lockScreenAgent = new LockScreenAgent(this.iframe);
      this.lockScreenAgent.start();
    }).catch((err) => {
      console.error(err);
    });
    AppWindow.call(this, this.configs);
    window.dispatchEvent(new CustomEvent('lockscreen-frame-bootstrap', {
      detail: this
    }));
  };

  /**
   * @borrows AppWindow.prototype as LockScreenWindow.prototype
   * @memberof LockScreenWindow
   */
  LockScreenWindow.prototype = Object.create(AppWindow.prototype);

  LockScreenWindow.prototype.constructor = LockScreenWindow;

  LockScreenWindow.prototype.isLockscreen = true;

  LockScreenWindow.SUB_COMPONENTS = {
    'transitionController': 'AppTransitionController',
    'statusbar': 'AppStatusbar'
  };

  LockScreenWindow.REGISTERED_EVENTS = AppWindow.REGISTERED_EVENTS;

  /**
   * We still need this before we put the lockreen inside an iframe.
   *
   * @type LockScreen
   * @memberof LockScreenWindow
   */
  LockScreenWindow.prototype.lockscreen = null;

  LockScreenWindow.prototype.HIERARCHY_MANAGER = 'LockScreenWindowManager';

  /**
   * We would maintain our own events by other components.
   *
   * @type string
   * @memberof LockScreenWindow
   */
  LockScreenWindow.prototype.eventPrefix = 'lockscreen-app';

  /**
   * Different animation from the original window.
   *
   * @type string
   * @memberof LockScreenWindow
   */
  LockScreenWindow.prototype.openAnimation = 'immediate';

  /**
   * Different animation from the original window.
   *
   * @type string
   * @memberof LockScreenWindow
   */
  LockScreenWindow.prototype.closeAnimation = 'fade-out';

  LockScreenWindow.prototype._DEBUG = false;

  /**
   * LockScreenWindow has its own styles.
   *
   * @type string
   * @memberof LockScreenWindow
   */
  LockScreenWindow.prototype.CLASS_LIST = 'appWindow lockScreenWindow';
  LockScreenWindow.prototype.CLASS_NAME = 'LockScreenWindow';

  LockScreenWindow.prototype._resize = function aw__resize() {
    var height, width;

    // We want the lockscreen to go below the Statusbar
    height = Service.query('LayoutManager.height') || window.innerHeight;
    width = Service.query('LayoutManager.width') || window.innerWidth;

    this.width = width;
    this.height = height;
    this.element.style.width = this.width + 'px';
    this.element.style.height = this.height + 'px';

    this.resized = true;

    /**
     * Fired when the app is resized.
     *
     * @event LockScreenWindow#lockscreen-appresize
     */
    this.publish('resize');
  };

  /**
   * Create the iframe and load it.
   *
   * @this {LockScreenWindow}
   * @memberof LockScreenWindow
   */
  LockScreenWindow.prototype.createFrame =
    function lsw_createFrame() {
      // XXX: Before we can make LockScreen as a real app,
      // we need these.
      var frame = document.getElementById('lockscreen-frame');
      if (!frame) {
        // XXX: This means we failed to build the system app.
        // So from now on we will try to go without lockscreen.
        frame = document.createElement('div');
      }
      frame.setVisible = function() {};
      // XXX: real mozbrowser iframes would own these methods.
      frame.addNextPaintListener = function(cb) {
        cb();
      };
      frame.removeNextPaintListener = function() {};
      frame.getScreenshot = function() {
        // Mock the request.
        return {
          get onsuccess() {return null;},
          set onsuccess(cb) {
            var mockEvent = {
              target: {result: null}
            };
            cb(mockEvent);
          }
        };
      };
      frame.removeAttribute('hidden');
      return frame;
    };

  LockScreenWindow.prototype.getNotificationContainer =
    function lsw_getNotificationContainer() {
      // XXX: After we make LockScreen as an app, needn't this anymore.
      return document.getElementById(
        'notifications-lockscreen-container');
    };

  LockScreenWindow.prototype._resize =
    function lsw__resize() {
      var height, width;
      this.debug('force RESIZE...');
      if (this.inputWindow.isActive()) {
        /**
         * The event is dispatched on the app window only when keyboard is up.
         *
         * @access private
         * @event LockScreenWindow~_withkeyboard
         */
        this.broadcast('withkeyboard');
      } else {
        /**
         * The event is dispatched on the LockScreen window only
         * when keyboard is hidden.
         *
         * @access private
         * @event LockScreenWindow~_withoutkeyboard
         */
        this.broadcast('withoutkeyboard');
      }
      height = this.layoutHeight();
      width = this.layoutWidth();

      this.width = width;
      this.height = height;
      this.element.style.width = this.width + 'px';
      this.element.style.height = this.height + 'px';

      this.browser.element.style.width = '';
      this.browser.element.style.height = '';

      this.resized = true;
      if (this.screenshotOverlay) {
        this.screenshotOverlay.style.visibility = '';
      }

      /**
       * Fired when the app is resized.
       *
       * @event LockScreenWindow#lockscreen-appresize
       */
      this.publish('resize');
      this.debug('W:', this.width, 'H:', this.height);
    };

  // XXX Bug 1085226: Before we make LockScreen use real keyboard, we need this.
  LockScreenWindow.prototype.layoutHeight =
    function lwm_layoutHeight() {
      // Whether we can resize or not (depends on if the content
      // is inside an iframe or not).
      if (!this.configs.inputWindow.resizeMode) {
        return window.innerHeight;
      }
      var softwareButtonHeight = this.isActive()  ?
        0 : (Service.query('SoftwareButtonManager.height') || 0);
      var inputWindowHeight = 0;
      if (this.states.instance && this.states.instance.inputWindow.isActive()) {
        inputWindowHeight = this.configs.inputWindow.height;
      }
      var height = window.innerHeight -
        inputWindowHeight -
        softwareButtonHeight;

      // Normalizing the height so that it always translates to an integral
      // number of device pixels
      var dpx = window.devicePixelRatio;
      if ((height * dpx) % 1 !== 0) {
        height = Math.ceil(height * dpx) / dpx;
      }

      return height;
    };

  LockScreenWindow.prototype.layoutWidth =
    function() {
      return window.innerWidth;
    };

  LockScreenWindow.prototype.lockOrientation =
    function() {
      // XXX: When we turn the screen on, try to lock the orientation
      // until it works. It may fail at the moment the screenchange
      // event has been fired, so we may need to try it several times.
      var tryLockOrientation = () => {
        if (screen.mozLockOrientation('portrait-primary')) {
          if (!this.orientationLockID) {
            throw new Error('No orientation ID. This function should only' +
                'be invoked as a interval callback');
          }
          window.clearInterval(this.orientationLockID);
          this.orientationLockID = null;
        }
      };
      if (Service.query('isOnRealDevice')) {
        if (this.orientationLockID) {
          // The previous one still present and was not cleared,
          // so do nothing.
          return;
        }
        this.orientationLockID =
          window.setInterval(tryLockOrientation, 4);
        // 4ms is the minimum interval according to W3C#setTimeout standard.
      }
    };

  exports.LockScreenWindow = LockScreenWindow;
})(window);