summaryrefslogtreecommitdiffstats
path: root/addon-sdk/source/lib/sdk/panel.js
diff options
context:
space:
mode:
Diffstat (limited to 'addon-sdk/source/lib/sdk/panel.js')
-rw-r--r--addon-sdk/source/lib/sdk/panel.js427
1 files changed, 0 insertions, 427 deletions
diff --git a/addon-sdk/source/lib/sdk/panel.js b/addon-sdk/source/lib/sdk/panel.js
deleted file mode 100644
index 4b625799d..000000000
--- a/addon-sdk/source/lib/sdk/panel.js
+++ /dev/null
@@ -1,427 +0,0 @@
-/* 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";
-
-// The panel module currently supports only Firefox and SeaMonkey.
-// See: https://bugzilla.mozilla.org/show_bug.cgi?id=jetpack-panel-apps
-module.metadata = {
- "stability": "stable",
- "engines": {
- "Firefox": "*",
- "SeaMonkey": "*"
- }
-};
-
-const { Cu, Ci } = require("chrome");
-const { setTimeout } = require('./timers');
-const { Class } = require("./core/heritage");
-const { merge } = require("./util/object");
-const { WorkerHost } = require("./content/utils");
-const { Worker } = require("./deprecated/sync-worker");
-const { Disposable } = require("./core/disposable");
-const { WeakReference } = require('./core/reference');
-const { contract: loaderContract } = require("./content/loader");
-const { contract } = require("./util/contract");
-const { on, off, emit, setListeners } = require("./event/core");
-const { EventTarget } = require("./event/target");
-const domPanel = require("./panel/utils");
-const { getDocShell } = require('./frame/utils');
-const { events } = require("./panel/events");
-const systemEvents = require("./system/events");
-const { filter, pipe, stripListeners } = require("./event/utils");
-const { getNodeView, getActiveView } = require("./view/core");
-const { isNil, isObject, isNumber } = require("./lang/type");
-const { getAttachEventType } = require("./content/utils");
-const { number, boolean, object } = require('./deprecated/api-utils');
-const { Style } = require("./stylesheet/style");
-const { attach, detach } = require("./content/mod");
-
-var isRect = ({top, right, bottom, left}) => [top, right, bottom, left].
- some(value => isNumber(value) && !isNaN(value));
-
-var isSDKObj = obj => obj instanceof Class;
-
-var rectContract = contract({
- top: number,
- right: number,
- bottom: number,
- left: number
-});
-
-var position = {
- is: object,
- map: v => (isNil(v) || isSDKObj(v) || !isObject(v)) ? v : rectContract(v),
- ok: v => isNil(v) || isSDKObj(v) || (isObject(v) && isRect(v)),
- msg: 'The option "position" must be a SDK object registered as anchor; ' +
- 'or an object with one or more of the following keys set to numeric ' +
- 'values: top, right, bottom, left.'
-}
-
-var displayContract = contract({
- width: number,
- height: number,
- focus: boolean,
- position: position
-});
-
-var panelContract = contract(merge({
- // contentStyle* / contentScript* are sharing the same validation constraints,
- // so they can be mostly reused, except for the messages.
- contentStyle: merge(Object.create(loaderContract.rules.contentScript), {
- msg: 'The `contentStyle` option must be a string or an array of strings.'
- }),
- contentStyleFile: merge(Object.create(loaderContract.rules.contentScriptFile), {
- msg: 'The `contentStyleFile` option must be a local URL or an array of URLs'
- }),
- contextMenu: boolean,
- allow: {
- is: ['object', 'undefined', 'null'],
- map: function (allow) { return { script: !allow || allow.script !== false }}
- },
-}, displayContract.rules, loaderContract.rules));
-
-function Allow(panel) {
- return {
- get script() { return getDocShell(viewFor(panel).backgroundFrame).allowJavascript; },
- set script(value) { return setScriptState(panel, value); },
- };
-}
-
-function setScriptState(panel, value) {
- let view = viewFor(panel);
- getDocShell(view.backgroundFrame).allowJavascript = value;
- getDocShell(view.viewFrame).allowJavascript = value;
- view.setAttribute("sdkscriptenabled", "" + value);
-}
-
-function isDisposed(panel) {
- return !views.has(panel);
-}
-
-var panels = new WeakMap();
-var models = new WeakMap();
-var views = new WeakMap();
-var workers = new WeakMap();
-var styles = new WeakMap();
-
-const viewFor = (panel) => views.get(panel);
-const modelFor = (panel) => models.get(panel);
-const panelFor = (view) => panels.get(view);
-const workerFor = (panel) => workers.get(panel);
-const styleFor = (panel) => styles.get(panel);
-
-function getPanelFromWeakRef(weakRef) {
- if (!weakRef) {
- return null;
- }
- let panel = weakRef.get();
- if (!panel) {
- return null;
- }
- if (isDisposed(panel)) {
- return null;
- }
- return panel;
-}
-
-var SinglePanelManager = {
- visiblePanel: null,
- enqueuedPanel: null,
- enqueuedPanelCallback: null,
- // Calls |callback| with no arguments when the panel may be shown.
- requestOpen: function(panelToOpen, callback) {
- let currentPanel = getPanelFromWeakRef(SinglePanelManager.visiblePanel);
- if (currentPanel || SinglePanelManager.enqueuedPanel) {
- SinglePanelManager.enqueuedPanel = Cu.getWeakReference(panelToOpen);
- SinglePanelManager.enqueuedPanelCallback = callback;
- if (currentPanel && currentPanel.isShowing) {
- currentPanel.hide();
- }
- } else {
- SinglePanelManager.notifyPanelCanOpen(panelToOpen, callback);
- }
- },
- notifyPanelCanOpen: function(panel, callback) {
- let view = viewFor(panel);
- // Can't pass an arrow function as the event handler because we need to be
- // able to call |removeEventListener| later.
- view.addEventListener("popuphidden", SinglePanelManager.onVisiblePanelHidden, true);
- view.addEventListener("popupshown", SinglePanelManager.onVisiblePanelShown, false);
- SinglePanelManager.enqueuedPanel = null;
- SinglePanelManager.enqueuedPanelCallback = null;
- SinglePanelManager.visiblePanel = Cu.getWeakReference(panel);
- callback();
- },
- onVisiblePanelShown: function(event) {
- let panel = panelFor(event.target);
- if (SinglePanelManager.enqueuedPanel) {
- // Another panel started waiting for |panel| to close before |panel| was
- // even done opening.
- panel.hide();
- }
- },
- onVisiblePanelHidden: function(event) {
- let view = event.target;
- let panel = panelFor(view);
- let currentPanel = getPanelFromWeakRef(SinglePanelManager.visiblePanel);
- if (currentPanel && currentPanel != panel) {
- return;
- }
- SinglePanelManager.visiblePanel = null;
- view.removeEventListener("popuphidden", SinglePanelManager.onVisiblePanelHidden, true);
- view.removeEventListener("popupshown", SinglePanelManager.onVisiblePanelShown, false);
- let nextPanel = getPanelFromWeakRef(SinglePanelManager.enqueuedPanel);
- let nextPanelCallback = SinglePanelManager.enqueuedPanelCallback;
- if (nextPanel) {
- SinglePanelManager.notifyPanelCanOpen(nextPanel, nextPanelCallback);
- }
- }
-};
-
-const Panel = Class({
- implements: [
- // Generate accessors for the validated properties that update model on
- // set and return values from model on get.
- panelContract.properties(modelFor),
- EventTarget,
- Disposable,
- WeakReference
- ],
- extends: WorkerHost(workerFor),
- setup: function setup(options) {
- let model = merge({
- defaultWidth: 320,
- defaultHeight: 240,
- focus: true,
- position: Object.freeze({}),
- contextMenu: false
- }, panelContract(options));
- model.ready = false;
- models.set(this, model);
-
- if (model.contentStyle || model.contentStyleFile) {
- styles.set(this, Style({
- uri: model.contentStyleFile,
- source: model.contentStyle
- }));
- }
-
- // Setup view
- let viewOptions = {allowJavascript: !model.allow || (model.allow.script !== false)};
- let view = domPanel.make(null, viewOptions);
- panels.set(view, this);
- views.set(this, view);
-
- // Load panel content.
- domPanel.setURL(view, model.contentURL);
-
- // Allow context menu
- domPanel.allowContextMenu(view, model.contextMenu);
-
- // Setup listeners.
- setListeners(this, options);
- let worker = new Worker(stripListeners(options));
- workers.set(this, worker);
-
- // pipe events from worker to a panel.
- pipe(worker, this);
- },
- dispose: function dispose() {
- this.hide();
- off(this);
-
- workerFor(this).destroy();
- detach(styleFor(this));
-
- domPanel.dispose(viewFor(this));
-
- // Release circular reference between view and panel instance. This
- // way view will be GC-ed. And panel as well once all the other refs
- // will be removed from it.
- views.delete(this);
- },
- /* Public API: Panel.width */
- get width() {
- return modelFor(this).width;
- },
- set width(value) {
- this.resize(value, this.height);
- },
- /* Public API: Panel.height */
- get height() {
- return modelFor(this).height;
- },
- set height(value) {
- this.resize(this.width, value);
- },
-
- /* Public API: Panel.focus */
- get focus() {
- return modelFor(this).focus;
- },
-
- /* Public API: Panel.position */
- get position() {
- return modelFor(this).position;
- },
-
- /* Public API: Panel.contextMenu */
- get contextMenu() {
- return modelFor(this).contextMenu;
- },
- set contextMenu(allow) {
- let model = modelFor(this);
- model.contextMenu = panelContract({ contextMenu: allow }).contextMenu;
- domPanel.allowContextMenu(viewFor(this), model.contextMenu);
- },
-
- get contentURL() {
- return modelFor(this).contentURL;
- },
- set contentURL(value) {
- let model = modelFor(this);
- model.contentURL = panelContract({ contentURL: value }).contentURL;
- domPanel.setURL(viewFor(this), model.contentURL);
- // Detach worker so that messages send will be queued until it's
- // reatached once panel content is ready.
- workerFor(this).detach();
- },
-
- get allow() { return Allow(this); },
- set allow(value) {
- let allowJavascript = panelContract({ allow: value }).allow.script;
- return setScriptState(this, value);
- },
-
- /* Public API: Panel.isShowing */
- get isShowing() {
- return !isDisposed(this) && domPanel.isOpen(viewFor(this));
- },
-
- /* Public API: Panel.show */
- show: function show(options={}, anchor) {
- SinglePanelManager.requestOpen(this, () => {
- if (options instanceof Ci.nsIDOMElement) {
- [anchor, options] = [options, null];
- }
-
- if (anchor instanceof Ci.nsIDOMElement) {
- console.warn(
- "Passing a DOM node to Panel.show() method is an unsupported " +
- "feature that will be soon replaced. " +
- "See: https://bugzilla.mozilla.org/show_bug.cgi?id=878877"
- );
- }
-
- let model = modelFor(this);
- let view = viewFor(this);
- let anchorView = getNodeView(anchor || options.position || model.position);
-
- options = merge({
- position: model.position,
- width: model.width,
- height: model.height,
- defaultWidth: model.defaultWidth,
- defaultHeight: model.defaultHeight,
- focus: model.focus,
- contextMenu: model.contextMenu
- }, displayContract(options));
-
- if (!isDisposed(this)) {
- domPanel.show(view, options, anchorView);
- }
- });
- return this;
- },
-
- /* Public API: Panel.hide */
- hide: function hide() {
- // Quit immediately if panel is disposed or there is no state change.
- domPanel.close(viewFor(this));
-
- return this;
- },
-
- /* Public API: Panel.resize */
- resize: function resize(width, height) {
- let model = modelFor(this);
- let view = viewFor(this);
- let change = panelContract({
- width: width || model.width || model.defaultWidth,
- height: height || model.height || model.defaultHeight
- });
-
- model.width = change.width
- model.height = change.height
-
- domPanel.resize(view, model.width, model.height);
-
- return this;
- }
-});
-exports.Panel = Panel;
-
-// Note must be defined only after value to `Panel` is assigned.
-getActiveView.define(Panel, viewFor);
-
-// Filter panel events to only panels that are create by this module.
-var panelEvents = filter(events, ({target}) => panelFor(target));
-
-// Panel events emitted after panel has being shown.
-var shows = filter(panelEvents, ({type}) => type === "popupshown");
-
-// Panel events emitted after panel became hidden.
-var hides = filter(panelEvents, ({type}) => type === "popuphidden");
-
-// Panel events emitted after content inside panel is ready. For different
-// panels ready may mean different state based on `contentScriptWhen` attribute.
-// Weather given event represents readyness is detected by `getAttachEventType`
-// helper function.
-var ready = filter(panelEvents, ({type, target}) =>
- getAttachEventType(modelFor(panelFor(target))) === type);
-
-// Panel event emitted when the contents of the panel has been loaded.
-var readyToShow = filter(panelEvents, ({type}) => type === "DOMContentLoaded");
-
-// Styles should be always added as soon as possible, and doesn't makes them
-// depends on `contentScriptWhen`
-var start = filter(panelEvents, ({type}) => type === "document-element-inserted");
-
-// Forward panel show / hide events to panel's own event listeners.
-on(shows, "data", ({target}) => {
- let panel = panelFor(target);
- if (modelFor(panel).ready)
- emit(panel, "show");
-});
-
-on(hides, "data", ({target}) => {
- let panel = panelFor(target);
- if (modelFor(panel).ready)
- emit(panel, "hide");
-});
-
-on(ready, "data", ({target}) => {
- let panel = panelFor(target);
- let window = domPanel.getContentDocument(target).defaultView;
-
- workerFor(panel).attach(window);
-});
-
-on(readyToShow, "data", ({target}) => {
- let panel = panelFor(target);
-
- if (!modelFor(panel).ready) {
- modelFor(panel).ready = true;
-
- if (viewFor(panel).state == "open")
- emit(panel, "show");
- }
-});
-
-on(start, "data", ({target}) => {
- let panel = panelFor(target);
- let window = domPanel.getContentDocument(target).defaultView;
-
- attach(styleFor(panel), window);
-});