Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  Messaging.sys.mjs   Sprache: unbekannt

 
/* 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/. */

const IS_PARENT_PROCESS =
  Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_DEFAULT;

class ChildActorDispatcher {
  constructor(actor) {
    this._actor = actor;
  }

  // TODO: Bug 1658980
  registerListener() {
    throw new Error("Cannot registerListener in child actor");
  }
  unregisterListener() {
    throw new Error("Cannot registerListener in child actor");
  }

  /**
   * Sends a request to Java.
   *
   * @param aMsg      Message to send; must be an object with a "type" property
   */
  sendRequest(aMsg) {
    this._actor.sendAsyncMessage("DispatcherMessage", aMsg);
  }

  /**
   * Sends a request to Java, returning a Promise that resolves to the response.
   *
   * @param aMsg Message to send; must be an object with a "type" property
   * @return A Promise resolving to the response
   */
  sendRequestForResult(aMsg) {
    return this._actor.sendQuery("DispatcherQuery", aMsg);
  }
}

function DispatcherDelegate(aDispatcher, aMessageManager) {
  this._dispatcher = aDispatcher;
  this._messageManager = aMessageManager;

  if (!aDispatcher) {
    // Child process.
    // TODO: this doesn't work with Fission, remove this code path once every
    // consumer has been migrated. Bug 1569360.
    this._replies = new Map();
    (aMessageManager || Services.cpmm).addMessageListener(
      "GeckoView:MessagingReply",
      this
    );
  }
}

DispatcherDelegate.prototype = {
  /**
   * Register a listener to be notified of event(s).
   *
   * @param aListener Target listener implementing nsIGeckoViewEventListener.
   * @param aEvents   String or array of strings of events to listen to.
   */
  registerListener(aListener, aEvents) {
    if (!this._dispatcher) {
      throw new Error("Can only listen in parent process");
    }
    this._dispatcher.registerListener(aListener, aEvents);
  },

  /**
   * Unregister a previously-registered listener.
   *
   * @param aListener Registered listener implementing nsIGeckoViewEventListener.
   * @param aEvents   String or array of strings of events to stop listening to.
   */
  unregisterListener(aListener, aEvents) {
    if (!this._dispatcher) {
      throw new Error("Can only listen in parent process");
    }
    this._dispatcher.unregisterListener(aListener, aEvents);
  },

  /**
   * Dispatch an event to registered listeners for that event, and pass an
   * optional data object and/or a optional callback interface to the
   * listeners.
   *
   * @param aEvent     Name of event to dispatch.
   * @param aData      Optional object containing data for the event.
   * @param aCallback  Optional callback implementing nsIGeckoViewEventCallback.
   * @param aFinalizer Optional finalizer implementing nsIGeckoViewEventFinalizer.
   */
  dispatch(aEvent, aData, aCallback, aFinalizer) {
    if (this._dispatcher) {
      this._dispatcher.dispatch(aEvent, aData, aCallback, aFinalizer);
      return;
    }

    const mm = this._messageManager || Services.cpmm;
    const forwardData = {
      global: !this._messageManager,
      event: aEvent,
      data: aData,
    };

    if (aCallback) {
      const uuid = Services.uuid.generateUUID().toString();
      this._replies.set(uuid, {
        callback: aCallback,
        finalizer: aFinalizer,
      });
      forwardData.uuid = uuid;
    }

    mm.sendAsyncMessage("GeckoView:Messaging", forwardData);
  },

  /**
   * Sends a request to Java.
   *
   * @param aMsg      Message to send; must be an object with a "type" property
   * @param aCallback Optional callback implementing nsIGeckoViewEventCallback.
   */
  sendRequest(aMsg, aCallback) {
    const type = aMsg.type;
    aMsg.type = undefined;
    this.dispatch(type, aMsg, aCallback);
  },

  /**
   * Sends a request to Java, returning a Promise that resolves to the response.
   *
   * @param aMsg Message to send; must be an object with a "type" property
   * @return A Promise resolving to the response
   */
  sendRequestForResult(aMsg) {
    return new Promise((resolve, reject) => {
      const type = aMsg.type;
      aMsg.type = undefined;

      // Manually release the resolve/reject functions after one callback is
      // received, so the JS GC is not tied up with the Java GC.
      const onCallback = (callback, ...args) => {
        if (callback) {
          callback(...args);
        }
        resolve = undefined;
        reject = undefined;
      };
      const callback = {
        onSuccess: result => onCallback(resolve, result),
        onError: error => onCallback(reject, error),
        onFinalize: _ => onCallback(reject),
      };
      this.dispatch(type, aMsg, callback, callback);
    });
  },

  finalize() {
    if (!this._replies) {
      return;
    }
    this._replies.forEach(reply => {
      if (typeof reply.finalizer === "function") {
        reply.finalizer();
      } else if (reply.finalizer) {
        reply.finalizer.onFinalize();
      }
    });
    this._replies.clear();
  },

  receiveMessage(aMsg) {
    const { uuid, type } = aMsg.data;
    const reply = this._replies.get(uuid);
    if (!reply) {
      return;
    }

    if (type === "success") {
      reply.callback.onSuccess(aMsg.data.response);
    } else if (type === "error") {
      reply.callback.onError(aMsg.data.response);
    } else if (type === "finalize") {
      if (typeof reply.finalizer === "function") {
        reply.finalizer();
      } else if (reply.finalizer) {
        reply.finalizer.onFinalize();
      }
      this._replies.delete(uuid);
    } else {
      throw new Error("invalid reply type");
    }
  },
};

export var EventDispatcher = {
  instance: new DispatcherDelegate(
    IS_PARENT_PROCESS ? Services.androidBridge : undefined
  ),

  /**
   * Return an EventDispatcher instance for a chrome DOM window. In a content
   * process, return a proxy through the message manager that automatically
   * forwards events to the main process.
   *
   * To force using a message manager proxy (for example in a frame script
   * environment), call forMessageManager.
   *
   * @param aWindow a chrome DOM window.
   */
  for(aWindow) {
    const view =
      aWindow &&
      aWindow.arguments &&
      aWindow.arguments[0] &&
      aWindow.arguments[0].QueryInterface(Ci.nsIGeckoViewView);

    if (!view) {
      const mm = !IS_PARENT_PROCESS && aWindow && aWindow.messageManager;
      if (!mm) {
        throw new Error(
          "window is not a GeckoView-connected window and does" +
            " not have a message manager"
        );
      }
      return this.forMessageManager(mm);
    }

    return new DispatcherDelegate(view);
  },

  /**
   * Returns a named EventDispatcher, which can communicate with the
   * corresponding EventDispatcher on the java side.
   */
  byName(aName) {
    if (!IS_PARENT_PROCESS) {
      return undefined;
    }
    const dispatcher = Services.androidBridge.getDispatcherByName(aName);
    return new DispatcherDelegate(dispatcher);
  },

  /**
   * Return an EventDispatcher instance for a message manager associated with a
   * window.
   *
   * @param aWindow a message manager.
   */
  forMessageManager(aMessageManager) {
    return new DispatcherDelegate(null, aMessageManager);
  },

  /**
   * Return the EventDispatcher instance associated with an actor.
   *
   * @param aActor an actor
   */
  forActor(aActor) {
    return new ChildActorDispatcher(aActor);
  },

  receiveMessage(aMsg) {
    // aMsg.data includes keys: global, event, data, uuid
    let callback;
    if (aMsg.data.uuid) {
      const reply = (type, response) => {
        const mm = aMsg.data.global ? aMsg.target : aMsg.target.messageManager;
        if (!mm) {
          if (type === "finalize") {
            // It's normal for the finalize call to come after the browser has
            // been destroyed. We can gracefully handle that case despite
            // having no message manager.
            return;
          }
          throw Error(
            `No message manager for ${aMsg.data.event}:${type} reply`
          );
        }
        mm.sendAsyncMessage("GeckoView:MessagingReply", {
          type,
          response,
          uuid: aMsg.data.uuid,
        });
      };
      callback = {
        onSuccess: response => reply("success", response),
        onError: error => reply("error", error),
        onFinalize: () => reply("finalize"),
      };
    }

    try {
      if (aMsg.data.global) {
        this.instance.dispatch(
          aMsg.data.event,
          aMsg.data.data,
          callback,
          callback
        );
        return;
      }

      const win = aMsg.target.ownerGlobal;
      const dispatcher = win.WindowEventDispatcher || this.for(win);
      dispatcher.dispatch(aMsg.data.event, aMsg.data.data, callback, callback);
    } catch (e) {
      callback?.onError(`Error getting dispatcher: ${e}`);
      throw e;
    }
  },
};

if (IS_PARENT_PROCESS) {
  Services.mm.addMessageListener("GeckoView:Messaging", EventDispatcher);
  Services.ppmm.addMessageListener("GeckoView:Messaging", EventDispatcher);
}

[ Dauer der Verarbeitung: 0.2 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge