b2g/components/Frames.jsm
author Mike Hommey <mh+mozilla@glandium.org>
Thu, 30 Oct 2014 13:06:12 +0900
changeset 213063 55d15b285436576e1c5979f753e433c50fd9db3a
parent 210303 7d7d64553c684fb913c0d53d7e49813da15d0c0b
child 233565 f63a2cf3fa11b5ee08b84e5d24dcfab51f56c90c
permissions -rw-r--r--
Bug 1077148 part 4 - Add and use new moz.build templates for Gecko programs and libraries. r=gps There are, sadly, many combinations of linkage in use throughout the tree. The main differentiator, though, is between program/libraries related to Gecko or not. Kind of. Some need mozglue, some don't. Some need dependent linkage, some standalone. Anyways, these new templates remove the need to manually define the right dependencies against xpcomglue, nspr, mozalloc and mozglue in most cases. Places that build programs and were resetting MOZ_GLUE_PROGRAM_LDFLAGS or that build libraries and were resetting MOZ_GLUE_LDFLAGS can now just not use those Gecko-specific templates.

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict';

this.EXPORTED_SYMBOLS = ['Frames'];

const Cu = Components.utils;
const Ci = Components.interfaces;

Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/SystemAppProxy.jsm');

const listeners = [];

const Observer = {
  // Save a map of (MessageManager => Frame) to be able to dispatch
  // the FrameDestroyed event with a frame reference.
  _frames: new Map(),

  // Also save current number of iframes opened by app
  _apps: new Map(),

  start: function () {
    Services.obs.addObserver(this, 'remote-browser-shown', false);
    Services.obs.addObserver(this, 'inprocess-browser-shown', false);
    Services.obs.addObserver(this, 'message-manager-disconnect', false);

    SystemAppProxy.getFrames().forEach(frame => {
      let mm = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager;
      this._frames.set(mm, frame);
      let mozapp = frame.getAttribute('mozapp');
      if (mozapp) {
        this._apps.set(mozapp, (this._apps.get(mozapp) || 0) + 1);
      }
    });
  },

  stop: function () {
    Services.obs.removeObserver(this, 'remote-browser-shown');
    Services.obs.removeObserver(this, 'inprocess-browser-shown');
    Services.obs.removeObserver(this, 'message-manager-disconnect');
    this._frames.clear();
    this._apps.clear();
  },

  observe: function (subject, topic, data) {
    switch(topic) {

      // Listen for frame creation in OOP (device) as well as in parent process (b2g desktop)
      case 'remote-browser-shown':
      case 'inprocess-browser-shown':
        let frameLoader = subject;

        // get a ref to the app <iframe>
        frameLoader.QueryInterface(Ci.nsIFrameLoader);
        let frame = frameLoader.ownerElement;
        let mm = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager;
        this.onMessageManagerCreated(mm, frame);
        break;

      // Every time an iframe is destroyed, its message manager also is
      case 'message-manager-disconnect':
        this.onMessageManagerDestroyed(subject);
        break;
    }
  },

  onMessageManagerCreated: function (mm, frame) {
    this._frames.set(mm, frame);

    let isFirstAppFrame = null;
    let mozapp = frame.getAttribute('mozapp');
    if (mozapp) {
      let count = (this._apps.get(mozapp) || 0) + 1;
      this._apps.set(mozapp, count);
      isFirstAppFrame = (count === 1);
    }

    listeners.forEach(function (listener) {
      try {
        listener.onFrameCreated(frame, isFirstAppFrame);
      } catch(e) {
        dump('Exception while calling Frames.jsm listener:' + e + '\n' +
             e.stack + '\n');
      }
    });
  },

  onMessageManagerDestroyed: function (mm) {
    let frame = this._frames.get(mm);
    if (!frame) {
      // We received an event for an unknown message manager
      return;
    }

    this._frames.delete(mm);

    let isLastAppFrame = null;
    let mozapp = frame.getAttribute('mozapp');
    if (mozapp) {
      let count = (this._apps.get(mozapp) || 0) - 1;
      this._apps.set(mozapp, count);
      isLastAppFrame = (count === 0);
    }

    listeners.forEach(function (listener) {
      try {
        listener.onFrameDestroyed(frame, isLastAppFrame);
      } catch(e) {
        dump('Exception while calling Frames.jsm listener:' + e + '\n' +
             e.stack + '\n');
      }
    });
  }

};

let Frames = this.Frames = {

  list: () => SystemAppProxy.getFrames(),

  addObserver: function (listener) {
    if (listeners.indexOf(listener) !== -1) {
      return;
    }

    listeners.push(listener);
    if (listeners.length == 1) {
      Observer.start();
    }
  },

  removeObserver: function (listener) {
    let idx = listeners.indexOf(listener);
    if (idx !== -1) {
      listeners.splice(idx, 1);
    }
    if (listeners.length === 0) {
      Observer.stop();
    }
  }

};