toolkit/actors/UAWidgetsChild.jsm
author Kris Maglione <maglione.k@gmail.com>
Thu, 14 Feb 2019 17:54:00 -0800
changeset 517406 7436c0f5b8b1583d20d5ea2d1d9d3b2c665bdf33
parent 514637 4d21e1285e2a334414eb1d45446fdea9c4922072
child 528189 aab68db4131baab15a3c4850d73b1fa9ed4a51df
permissions -rw-r--r--
Fix botched backout (bug 1524687). r=bustage

/* vim: set ts=2 sw=2 sts=2 et tw=80: */
/* 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";

var EXPORTED_SYMBOLS = ["UAWidgetsChild"];

const {ActorChild} = ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");

class UAWidgetsChild extends ActorChild {
  constructor(dispatcher) {
    super(dispatcher);

    this.widgets = new WeakMap();
  }

  handleEvent(aEvent) {
    switch (aEvent.type) {
      case "UAWidgetSetupOrChange":
        this.setupOrNotifyWidget(aEvent.target);
        break;
      case "UAWidgetTeardown":
        this.teardownWidget(aEvent.target);
        break;
    }

    // In case we are a nested frame, prevent the message manager of the
    // parent frame from receving the event.
    aEvent.stopPropagation();
  }

  setupOrNotifyWidget(aElement) {
    let widget = this.widgets.get(aElement);
    if (!widget) {
      this.setupWidget(aElement);
      return;
    }
    if (typeof widget.onchange == "function") {
      try {
        widget.onchange();
      } catch (ex) {
        Cu.reportError(ex);
      }
    }
  }

  setupWidget(aElement) {
    let uri;
    let widgetName;
    switch (aElement.localName) {
      case "video":
      case "audio":
        uri = "chrome://global/content/elements/videocontrols.js";
        widgetName = "VideoControlsWidget";
        break;
      case "input":
        uri = "chrome://global/content/elements/datetimebox.js";
        widgetName = "DateTimeBoxWidget";
        break;
      case "embed":
      case "object":
        uri = "chrome://global/content/elements/pluginProblem.js";
        widgetName = "PluginProblemWidget";
        break;
      case "marquee":
        uri = "chrome://global/content/elements/marquee.js";
        widgetName = "MarqueeWidget";
        break;
    }

    if (!uri || !widgetName) {
      Cu.reportError("Getting a UAWidgetSetupOrChange event on undefined element.");
      return;
    }

    let shadowRoot = aElement.openOrClosedShadowRoot;
    if (!shadowRoot) {
      Cu.reportError("Getting a UAWidgetSetupOrChange event without the Shadow Root.");
      return;
    }

    let isSystemPrincipal = aElement.nodePrincipal.isSystemPrincipal;
    let sandbox = isSystemPrincipal ?
      Object.create(null) : Cu.getUAWidgetScope(aElement.nodePrincipal);

    if (!sandbox[widgetName]) {
      Services.scriptloader.loadSubScript(uri, sandbox);
    }

    let widget = new sandbox[widgetName](shadowRoot);
    if (!isSystemPrincipal) {
      widget = widget.wrappedJSObject;
    }
    this.widgets.set(aElement, widget);
    try {
      widget.onsetup();
    } catch (ex) {
      Cu.reportError(ex);
    }
  }

  teardownWidget(aElement) {
    let widget = this.widgets.get(aElement);
    if (!widget) {
      return;
    }
    if (typeof widget.destructor == "function") {
      try {
        widget.destructor();
      } catch (ex) {
        Cu.reportError(ex);
      }
    }
    this.widgets.delete(aElement);
  }
}