mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 982379 - Uplift Add-on SDK to Firefox
This commit is contained in:
parent
35f2b81da7
commit
c3fd8f5dcd
40
addon-sdk/source/lib/sdk/input/customizable-ui.js
Normal file
40
addon-sdk/source/lib/sdk/input/customizable-ui.js
Normal file
@ -0,0 +1,40 @@
|
||||
/* 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";
|
||||
|
||||
const { Cu } = require("chrome");
|
||||
|
||||
// Because Firefox Holly, we still need to check if `CustomizableUI` is
|
||||
// available. Once Australis will officially land, we can safely remove it.
|
||||
// See Bug 959142
|
||||
try {
|
||||
Cu.import("resource:///modules/CustomizableUI.jsm", {});
|
||||
}
|
||||
catch (e) {
|
||||
throw Error("Unsupported Application: The module" + module.id +
|
||||
" does not support this application.");
|
||||
}
|
||||
|
||||
const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
|
||||
const { receive } = require("../event/utils");
|
||||
const { InputPort } = require("./system");
|
||||
const { object} = require("../util/sequence");
|
||||
const { getOuterId } = require("../window/utils");
|
||||
|
||||
const Input = function() {};
|
||||
Input.prototype = Object.create(InputPort.prototype);
|
||||
|
||||
Input.prototype.onCustomizeStart = function (window) {
|
||||
receive(this, object([getOuterId(window), true]));
|
||||
}
|
||||
|
||||
Input.prototype.onCustomizeEnd = function (window) {
|
||||
receive(this, object([getOuterId(window), null]));
|
||||
}
|
||||
|
||||
Input.prototype.addListener = input => CustomizableUI.addListener(input);
|
||||
|
||||
Input.prototype.removeListener = input => CustomizableUI.removeListener(input);
|
||||
|
||||
exports.CustomizationInput = Input;
|
@ -3,7 +3,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const { Cc, Ci, Cr } = require("chrome");
|
||||
const { Cc, Ci, Cr, Cu } = require("chrome");
|
||||
const { Input, start, stop, end, receive, outputs } = require("../event/utils");
|
||||
const { once, off } = require("../event/core");
|
||||
const { id: addonID } = require("../self");
|
||||
@ -14,6 +14,7 @@ const { addObserver, removeObserver } = Cc['@mozilla.org/observer-service;1'].
|
||||
|
||||
const addonUnloadTopic = "sdk:loader:destroy";
|
||||
|
||||
const isXrayWrapper = Cu.isXrayWrapper;
|
||||
// In the past SDK used to double-wrap notifications dispatched, which
|
||||
// made them awkward to use outside of SDK. At present they no longer
|
||||
// do that, although we still supported for legacy reasons.
|
||||
@ -48,23 +49,29 @@ InputPort.prototype.constructor = InputPort;
|
||||
// When port is started (which is when it's subgraph get's
|
||||
// first subscriber) actual observer is registered.
|
||||
InputPort.start = input => {
|
||||
addObserver(input, input.topic, false);
|
||||
input.addListener(input);
|
||||
// Also register add-on unload observer to end this signal
|
||||
// when that happens.
|
||||
addObserver(input, addonUnloadTopic, false);
|
||||
};
|
||||
InputPort.prototype[start] = InputPort.start;
|
||||
|
||||
InputPort.addListener = input => addObserver(input, input.topic, false);
|
||||
InputPort.prototype.addListener = InputPort.addListener;
|
||||
|
||||
// When port is stopped (which is when it's subgraph has no
|
||||
// no subcribers left) an actual observer unregistered.
|
||||
// Note that port stopped once it ends as well (which is when
|
||||
// add-on is unloaded).
|
||||
InputPort.stop = input => {
|
||||
removeObserver(input, input.topic);
|
||||
input.removeListener(input);
|
||||
removeObserver(input, addonUnloadTopic);
|
||||
};
|
||||
InputPort.prototype[stop] = InputPort.stop;
|
||||
|
||||
InputPort.removeListener = input => removeObserver(input, input.topic);
|
||||
InputPort.prototype.removeListener = InputPort.removeListener;
|
||||
|
||||
// `InputPort` also implements `nsIObserver` interface and
|
||||
// `nsISupportsWeakReference` interfaces as it's going to be used as such.
|
||||
InputPort.prototype.QueryInterface = function(iid) {
|
||||
@ -80,9 +87,11 @@ InputPort.prototype.QueryInterface = function(iid) {
|
||||
InputPort.prototype.observe = function(subject, topic, data) {
|
||||
// Unwrap message from the subject. SDK used to have it's own version of
|
||||
// wrappedJSObjects which take precedence, if subject has `wrappedJSObject`
|
||||
// use it as message. Otherwise use subject as is.
|
||||
// and it's not an XrayWrapper use it as message. Otherwise use subject as
|
||||
// is.
|
||||
const message = subject === null ? null :
|
||||
isLegacyWrapper(subject) ? unwrapLegacy(subject) :
|
||||
isXrayWrapper(subject) ? subject :
|
||||
subject.wrappedJSObject ? subject.wrappedJSObject :
|
||||
subject;
|
||||
|
||||
|
@ -13,7 +13,6 @@ module.metadata = {
|
||||
};
|
||||
|
||||
const { Ci } = require("chrome");
|
||||
const { validateOptions: valid } = require('./deprecated/api-utils');
|
||||
const { setTimeout } = require('./timers');
|
||||
const { isPrivateBrowsingSupported } = require('./self');
|
||||
const { isWindowPBSupported } = require('./private-browsing/utils');
|
||||
@ -31,11 +30,14 @@ 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 } = require("./lang/type");
|
||||
const { isNil, isObject, isNumber } = require("./lang/type");
|
||||
const { getAttachEventType } = require("./content/utils");
|
||||
const { number, boolean, object } = require('./deprecated/api-utils');
|
||||
|
||||
let number = { is: ['number', 'undefined', 'null'] };
|
||||
let boolean = { is: ['boolean', 'undefined', 'null'] };
|
||||
let isRect = ({top, right, bottom, left}) => [top, right, bottom, left].
|
||||
some(value => isNumber(value) && !isNaN(value));
|
||||
|
||||
let isSDKObj = obj => obj instanceof Class;
|
||||
|
||||
let rectContract = contract({
|
||||
top: number,
|
||||
@ -44,16 +46,20 @@ let rectContract = contract({
|
||||
left: number
|
||||
});
|
||||
|
||||
let rect = {
|
||||
is: ['object', 'undefined', 'null'],
|
||||
map: function(v) isNil(v) || !isObject(v) ? v : rectContract(v)
|
||||
let 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.'
|
||||
}
|
||||
|
||||
let displayContract = contract({
|
||||
width: number,
|
||||
height: number,
|
||||
focus: boolean,
|
||||
position: rect
|
||||
position: position
|
||||
});
|
||||
|
||||
let panelContract = contract(merge({}, displayContract.rules, loaderContract.rules));
|
||||
@ -176,7 +182,7 @@ const Panel = Class({
|
||||
get isShowing() !isDisposed(this) && domPanel.isOpen(viewFor(this)),
|
||||
|
||||
/* Public API: Panel.show */
|
||||
show: function show(options, anchor) {
|
||||
show: function show(options={}, anchor) {
|
||||
if (options instanceof Ci.nsIDOMElement) {
|
||||
[anchor, options] = [options, null];
|
||||
}
|
||||
@ -191,7 +197,7 @@ const Panel = Class({
|
||||
|
||||
let model = modelFor(this);
|
||||
let view = viewFor(this);
|
||||
let anchorView = getNodeView(anchor);
|
||||
let anchorView = getNodeView(anchor || options.position);
|
||||
|
||||
options = merge({
|
||||
position: model.position,
|
||||
@ -239,24 +245,25 @@ exports.Panel = Panel;
|
||||
getActiveView.define(Panel, viewFor);
|
||||
|
||||
// Filter panel events to only panels that are create by this module.
|
||||
let panelEvents = filter(events, function({target}) panelFor(target));
|
||||
let panelEvents = filter(events, ({target}) => panelFor(target));
|
||||
|
||||
// Panel events emitted after panel has being shown.
|
||||
let shows = filter(panelEvents, function({type}) type === "popupshown");
|
||||
let shows = filter(panelEvents, ({type}) => type === "popupshown");
|
||||
|
||||
// Panel events emitted after panel became hidden.
|
||||
let hides = filter(panelEvents, function({type}) type === "popuphidden");
|
||||
let 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.
|
||||
let ready = filter(panelEvents, function({type, target})
|
||||
let ready = filter(panelEvents, ({type, target}) =>
|
||||
getAttachEventType(modelFor(panelFor(target))) === type);
|
||||
|
||||
// Forward panel show / hide events to panel's own event listeners.
|
||||
on(shows, "data", function({target}) emit(panelFor(target), "show"));
|
||||
on(hides, "data", function({target}) emit(panelFor(target), "hide"));
|
||||
on(shows, "data", ({target}) => emit(panelFor(target), "show"));
|
||||
|
||||
on(hides, "data", ({target}) => emit(panelFor(target), "hide"));
|
||||
|
||||
on(ready, "data", function({target}) {
|
||||
let worker = workerFor(panelFor(target));
|
||||
|
@ -23,6 +23,8 @@ const events = require("../system/events");
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
|
||||
function calculateRegion({ position, width, height, defaultWidth, defaultHeight }, rect) {
|
||||
position = position || {};
|
||||
|
||||
let x, y;
|
||||
|
||||
let hasTop = !isNil(position.top);
|
||||
@ -127,14 +129,32 @@ function display(panel, options, anchor) {
|
||||
({x, y, width, height}) = calculateRegion(options, viewportRect);
|
||||
}
|
||||
else {
|
||||
// The XUL Panel has an arrow, so the margin needs to be reset
|
||||
// to the default value.
|
||||
panel.style.margin = "";
|
||||
let { CustomizableUI, window } = anchor.ownerDocument.defaultView;
|
||||
|
||||
// In Australis, widgets may be positioned in an overflow panel or the
|
||||
// menu panel.
|
||||
// In such cases clicking this widget will hide the overflow/menu panel,
|
||||
// and the widget's panel will show instead.
|
||||
if (CustomizableUI) {
|
||||
let node = anchor;
|
||||
({anchor}) = CustomizableUI.getWidget(anchor.id).forWindow(window);
|
||||
|
||||
// if `node` is not the `anchor` itself, it means the widget is
|
||||
// positioned in a panel, therefore we have to hide it before show
|
||||
// the widget's panel in the same anchor
|
||||
if (node !== anchor)
|
||||
CustomizableUI.hidePanelForNode(anchor);
|
||||
}
|
||||
|
||||
width = width || defaultWidth;
|
||||
height = height || defaultHeight;
|
||||
|
||||
// Open the popup by the anchor.
|
||||
let rect = anchor.getBoundingClientRect();
|
||||
|
||||
let window = anchor.ownerDocument.defaultView;
|
||||
|
||||
let zoom = getScreenPixelsPerCSSPixel(window);
|
||||
let screenX = rect.left + window.mozInnerScreenX * zoom;
|
||||
let screenY = rect.top + window.mozInnerScreenY * zoom;
|
||||
|
@ -12,7 +12,6 @@ const { Cc, Ci, CC } = require('chrome');
|
||||
const options = require('@loader/options');
|
||||
const file = require('./io/file');
|
||||
const runtime = require("./system/runtime");
|
||||
var cfxArgs = require("@test/options");
|
||||
|
||||
const appStartup = Cc['@mozilla.org/toolkit/app-startup;1'].
|
||||
getService(Ci.nsIAppStartup);
|
||||
@ -70,13 +69,12 @@ exports.exit = function exit(code) {
|
||||
stream.write(status, status.length);
|
||||
stream.flush();
|
||||
stream.close();
|
||||
if (cfxArgs.parseable) {
|
||||
console.log('wrote to resultFile');
|
||||
}
|
||||
}
|
||||
|
||||
forcedExit = true;
|
||||
appStartup.quit(E_FORCE);
|
||||
if (code == 0) {
|
||||
forcedExit = true;
|
||||
}
|
||||
appStartup.quit(code ? E_ATTEMPT : E_FORCE);
|
||||
};
|
||||
|
||||
// Adapter for nodejs's stdout & stderr:
|
||||
|
@ -7,7 +7,6 @@ module.metadata = {
|
||||
"stability": "experimental"
|
||||
};
|
||||
|
||||
var { setTimeout } = require("../timers");
|
||||
var { exit, stdout } = require("../system");
|
||||
var cfxArgs = require("@test/options");
|
||||
|
||||
@ -20,16 +19,9 @@ function runTests(findAndRunTests) {
|
||||
stdout.write(tests.passed + " of " + total + " tests passed.\n");
|
||||
|
||||
if (tests.failed == 0) {
|
||||
if (tests.passed === 0) {
|
||||
if (tests.passed === 0)
|
||||
stdout.write("No tests were run\n");
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
if (cfxArgs.parseable) {
|
||||
console.log('calling exit(0)');
|
||||
}
|
||||
exit(0);
|
||||
}, 0);
|
||||
exit(0);
|
||||
} else {
|
||||
if (cfxArgs.verbose || cfxArgs.parseable)
|
||||
printFailedTests(tests, stdout.write);
|
||||
|
@ -26,6 +26,7 @@ const { merge } = require('../../util/object');
|
||||
const { Disposable } = require('../../core/disposable');
|
||||
const { on, off, emit, setListeners } = require('../../event/core');
|
||||
const { EventTarget } = require('../../event/target');
|
||||
const { getNodeView } = require('../../view/core');
|
||||
|
||||
const view = require('./view');
|
||||
const { buttonContract, stateContract } = require('./contract');
|
||||
@ -89,6 +90,10 @@ exports.ActionButton = ActionButton;
|
||||
|
||||
identify.define(ActionButton, ({id}) => toWidgetId(id));
|
||||
|
||||
getNodeView.define(ActionButton, button =>
|
||||
view.nodeFor(toWidgetId(button.id))
|
||||
);
|
||||
|
||||
let actionButtonStateEvents = events.filter(stateEvents,
|
||||
e => e.target instanceof ActionButton);
|
||||
|
||||
|
@ -26,6 +26,7 @@ const { merge } = require('../../util/object');
|
||||
const { Disposable } = require('../../core/disposable');
|
||||
const { on, off, emit, setListeners } = require('../../event/core');
|
||||
const { EventTarget } = require('../../event/target');
|
||||
const { getNodeView } = require('../../view/core');
|
||||
|
||||
const view = require('./view');
|
||||
const { toggleButtonContract, toggleStateContract } = require('./contract');
|
||||
@ -90,6 +91,10 @@ exports.ToggleButton = ToggleButton;
|
||||
|
||||
identify.define(ToggleButton, ({id}) => toWidgetId(id));
|
||||
|
||||
getNodeView.define(ToggleButton, button =>
|
||||
view.nodeFor(toWidgetId(button.id))
|
||||
);
|
||||
|
||||
let toggleButtonStateEvents = events.filter(stateEvents,
|
||||
e => e.target instanceof ToggleButton);
|
||||
|
||||
|
@ -108,6 +108,11 @@ function getImage(icon, isInToolbar, pixelRatio) {
|
||||
return image;
|
||||
}
|
||||
|
||||
function nodeFor(id, window=getMostRecentBrowserWindow()) {
|
||||
return customizedWindows.has(window) ? null : getNode(id, window);
|
||||
};
|
||||
exports.nodeFor = nodeFor;
|
||||
|
||||
function create(options) {
|
||||
let { id, label, icon, type } = options;
|
||||
|
||||
@ -183,7 +188,7 @@ function setIcon(id, window, icon) {
|
||||
exports.setIcon = setIcon;
|
||||
|
||||
function setLabel(id, window, label) {
|
||||
let node = customizedWindows.has(window) ? null : getNode(id, window);
|
||||
let node = nodeFor(id, window);
|
||||
|
||||
if (node) {
|
||||
node.setAttribute('label', label);
|
||||
@ -193,7 +198,7 @@ function setLabel(id, window, label) {
|
||||
exports.setLabel = setLabel;
|
||||
|
||||
function setDisabled(id, window, disabled) {
|
||||
let node = customizedWindows.has(window) ? null : getNode(id, window);
|
||||
let node = nodeFor(id, window);
|
||||
|
||||
if (node)
|
||||
node.disabled = disabled;
|
||||
@ -201,7 +206,7 @@ function setDisabled(id, window, disabled) {
|
||||
exports.setDisabled = setDisabled;
|
||||
|
||||
function setChecked(id, window, checked) {
|
||||
let node = customizedWindows.has(window) ? null : getNode(id, window);
|
||||
let node = nodeFor(id, window);
|
||||
|
||||
if (node)
|
||||
node.checked = checked;
|
||||
@ -209,8 +214,7 @@ function setChecked(id, window, checked) {
|
||||
exports.setChecked = setChecked;
|
||||
|
||||
function click(id) {
|
||||
let window = getMostRecentBrowserWindow();
|
||||
let node = customizedWindows.has(window) ? null : getNode(id, window);
|
||||
let node = nodeFor(id);
|
||||
|
||||
if (node)
|
||||
node.click();
|
||||
|
@ -32,17 +32,17 @@ const mailbox = new OutputPort({ id: "frame-mailbox" });
|
||||
const input = Frames;
|
||||
|
||||
|
||||
const urlToId = url =>
|
||||
const makeID = url =>
|
||||
("frame-" + addonID + "-" + url).
|
||||
split("/").join("-").
|
||||
split(".").join("-").
|
||||
replace(/[^A-Za-z0-9_\-]/g, "");
|
||||
|
||||
const validate = contract({
|
||||
id: {
|
||||
name: {
|
||||
is: ["string", "undefined"],
|
||||
ok: x => /^[a-z][a-z0-9-_]+$/i.test(x),
|
||||
msg: "The `option.id` must be a valid alphanumeric string (hyphens and " +
|
||||
msg: "The `option.name` must be a valid alphanumeric string (hyphens and " +
|
||||
"underscores are allowed) starting with letter."
|
||||
},
|
||||
url: {
|
||||
@ -88,7 +88,7 @@ const Frame = Class({
|
||||
implements: [Disposable, Source],
|
||||
initialize: function(params={}) {
|
||||
const options = validate(params);
|
||||
const id = options.id || urlToId(options.url);
|
||||
const id = makeID(options.name || options.url);
|
||||
|
||||
if (frames.has(id))
|
||||
throw Error("Frame with this id already exists: " + id);
|
||||
|
@ -19,7 +19,7 @@ const { events: browserEvents } = require('../browser/events');
|
||||
const { events: tabEvents } = require('../tab/events');
|
||||
const { events: stateEvents } = require('./state/events');
|
||||
|
||||
const { windows, isInteractive, getMostRecentBrowserWindow } = require('../window/utils');
|
||||
const { windows, isInteractive, getFocusedBrowser } = require('../window/utils');
|
||||
const { getActiveTab, getOwnerWindow } = require('../tabs/utils');
|
||||
|
||||
const { ignoreWindow } = require('../private-browsing/utils');
|
||||
@ -47,7 +47,7 @@ const isActiveTab = thing => isTab(thing) && thing === getActiveTab(getOwnerWind
|
||||
const isEnumerable = window => !ignoreWindow(window);
|
||||
const browsers = _ =>
|
||||
windows('navigator:browser', { includePrivate: true }).filter(isInteractive);
|
||||
const getMostRecentTab = _ => getActiveTab(getMostRecentBrowserWindow());
|
||||
const getMostRecentTab = _ => getActiveTab(getFocusedBrowser());
|
||||
|
||||
function getStateFor(component, target) {
|
||||
if (!isRegistered(component))
|
||||
@ -172,7 +172,7 @@ exports.properties = properties;
|
||||
function state(contract) {
|
||||
return {
|
||||
state: function state(target, state) {
|
||||
let nativeTarget = target === 'window' ? getMostRecentBrowserWindow()
|
||||
let nativeTarget = target === 'window' ? getFocusedBrowser()
|
||||
: target === 'tab' ? getMostRecentTab()
|
||||
: viewFor(target);
|
||||
|
||||
|
@ -16,11 +16,13 @@ const { subscribe, send, Reactor, foldp, lift, merges } = require("../../event/u
|
||||
const { InputPort } = require("../../input/system");
|
||||
const { OutputPort } = require("../../output/system");
|
||||
const { Interactive } = require("../../input/browser");
|
||||
const { CustomizationInput } = require("../../input/customizable-ui");
|
||||
const { pairs, map, isEmpty, object,
|
||||
each, keys, values } = require("../../util/sequence");
|
||||
const { curry, flip } = require("../../lang/functional");
|
||||
const { patch, diff } = require("diffpatcher/index");
|
||||
const prefs = require("../../preferences/service");
|
||||
const { getByOuterId } = require("../../window/utils");
|
||||
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
const PREF_ROOT = "extensions.sdk-toolbar-collapsed.";
|
||||
@ -38,8 +40,9 @@ const syncoutput = new OutputPort({ id: "toolbar-change", sync: true });
|
||||
// date.
|
||||
const Toolbars = foldp(patch, {}, merges([new InputPort({ id: "toolbar-changed" }),
|
||||
new InputPort({ id: "toolbar-change" })]));
|
||||
const State = lift((toolbars, windows) => ({windows: windows, toolbars: toolbars}),
|
||||
Toolbars, Interactive);
|
||||
const State = lift((toolbars, windows, customizable) =>
|
||||
({windows: windows, toolbars: toolbars, customizable: customizable}),
|
||||
Toolbars, Interactive, new CustomizationInput());
|
||||
|
||||
// Shared event handler that makes `event.target.parent` collapsed.
|
||||
// Used as toolbar's close buttons click handler.
|
||||
@ -88,12 +91,17 @@ const addView = curry((options, {document}) => {
|
||||
view.setAttribute("collapsed", options.collapsed);
|
||||
view.setAttribute("toolbarname", options.title);
|
||||
view.setAttribute("pack", "end");
|
||||
view.setAttribute("defaultset", options.items.join(","));
|
||||
view.setAttribute("customizable", true);
|
||||
view.setAttribute("style", "max-height: 40px;");
|
||||
view.setAttribute("customizable", "false");
|
||||
view.setAttribute("style", "padding: 2px 0; max-height: 40px;");
|
||||
view.setAttribute("mode", "icons");
|
||||
view.setAttribute("iconsize", "small");
|
||||
view.setAttribute("context", "toolbar-context-menu");
|
||||
view.setAttribute("class", "toolbar-primary chromeclass-toolbar");
|
||||
|
||||
let label = document.createElementNS(XUL_NS, "label");
|
||||
label.setAttribute("value", options.title);
|
||||
label.setAttribute("collapsed", "true");
|
||||
view.appendChild(label);
|
||||
|
||||
let closeButton = document.createElementNS(XUL_NS, "toolbarbutton");
|
||||
closeButton.setAttribute("id", "close-" + options.id);
|
||||
@ -103,6 +111,24 @@ const addView = curry((options, {document}) => {
|
||||
|
||||
view.appendChild(closeButton);
|
||||
|
||||
// In order to have a close button not costumizable, aligned on the right,
|
||||
// leaving the customizable capabilities of Australis, we need to create
|
||||
// a toolbar inside a toolbar.
|
||||
// This is should be a temporary hack, we should have a proper XBL for toolbar
|
||||
// instead. See:
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=982005
|
||||
let toolbar = document.createElementNS(XUL_NS, "toolbar");
|
||||
toolbar.setAttribute("id", "inner-" + options.id);
|
||||
toolbar.setAttribute("defaultset", options.items.join(","));
|
||||
toolbar.setAttribute("customizable", "true");
|
||||
toolbar.setAttribute("style", "-moz-appearance: none; overflow: hidden");
|
||||
toolbar.setAttribute("mode", "icons");
|
||||
toolbar.setAttribute("iconsize", "small");
|
||||
toolbar.setAttribute("context", "toolbar-context-menu");
|
||||
toolbar.setAttribute("flex", "1");
|
||||
|
||||
view.insertBefore(toolbar, closeButton);
|
||||
|
||||
const observer = new document.defaultView.MutationObserver(attributesChanged);
|
||||
observer.observe(view, { attributes: true,
|
||||
attributeFilter: ["collapsed", "toolbarname"] });
|
||||
@ -117,23 +143,34 @@ const removeView = curry((id, {document}) => {
|
||||
if (view) view.remove();
|
||||
});
|
||||
|
||||
const updateView = curry((id, {title, collapsed}, {document}) => {
|
||||
const updateView = curry((id, {title, collapsed, isCustomizing}, {document}) => {
|
||||
const view = document.getElementById(id);
|
||||
if (view && title)
|
||||
|
||||
if (!view)
|
||||
return;
|
||||
|
||||
if (title)
|
||||
view.setAttribute("toolbarname", title);
|
||||
if (view && collapsed !== void(0))
|
||||
|
||||
if (collapsed !== void(0))
|
||||
view.setAttribute("collapsed", Boolean(collapsed));
|
||||
|
||||
if (isCustomizing !== void(0)) {
|
||||
view.querySelector("label").collapsed = !isCustomizing;
|
||||
view.querySelector("toolbar").style.visibility = isCustomizing
|
||||
? "hidden" : "visible";
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const viewUpdate = curry(flip(updateView));
|
||||
|
||||
// Utility function used to register toolbar into CustomizableUI.
|
||||
const registerToolbar = state => {
|
||||
// If it's first additon register toolbar as customizableUI component.
|
||||
CustomizableUI.registerArea(state.id, {
|
||||
CustomizableUI.registerArea("inner-" + state.id, {
|
||||
type: CustomizableUI.TYPE_TOOLBAR,
|
||||
legacy: true,
|
||||
defaultPlacements: [...state.items, "close-" + state.id]
|
||||
defaultPlacements: [...state.items]
|
||||
});
|
||||
};
|
||||
// Utility function used to unregister toolbar from the CustomizableUI.
|
||||
@ -148,7 +185,7 @@ const reactor = new Reactor({
|
||||
// we unregister toolbar and remove it from each window
|
||||
// it was added to.
|
||||
if (update === null) {
|
||||
unregisterToolbar(id);
|
||||
unregisterToolbar("inner-" + id);
|
||||
each(removeView(id), values(past.windows));
|
||||
|
||||
send(output, object([id, null]));
|
||||
@ -190,10 +227,16 @@ const reactor = new Reactor({
|
||||
if (window)
|
||||
each(viewAdd(window), values(past.toolbars));
|
||||
}, values(delta.windows));
|
||||
|
||||
each(([id, isCustomizing]) => {
|
||||
each(viewUpdate(getByOuterId(id), {isCustomizing: !!isCustomizing}),
|
||||
keys(present.toolbars));
|
||||
|
||||
}, pairs(delta.customizable))
|
||||
},
|
||||
onEnd: state => {
|
||||
each(id => {
|
||||
unregisterToolbar(id);
|
||||
unregisterToolbar("inner-" + id);
|
||||
each(removeView(id), values(state.windows));
|
||||
}, keys(state.toolbars));
|
||||
}
|
||||
|
@ -444,28 +444,10 @@ const WidgetViewTrait = LightTrait.compose(EventEmitterTrait, LightTrait({
|
||||
// Special case for click events: if the widget doesn't have a click
|
||||
// handler, but it does have a panel, display the panel.
|
||||
if ("click" == type && !this._listeners("click").length && this.panel) {
|
||||
// In Australis, widgets may be positioned in an overflow panel or the
|
||||
// menu panel.
|
||||
// In such cases clicking this widget will hide the overflow/menu panel,
|
||||
// and the widget's panel will show instead.
|
||||
|
||||
let anchor = domNode;
|
||||
let { CustomizableUI, window } = domNode.ownerDocument.defaultView;
|
||||
|
||||
if (CustomizableUI) {
|
||||
({anchor}) = CustomizableUI.getWidget(domNode.id).forWindow(window);
|
||||
|
||||
// if `anchor` is not the `domNode` itself, it means the widget is
|
||||
// positioned in a panel, therefore we have to hide it before show
|
||||
// the widget's panel in the same anchor
|
||||
if (anchor !== domNode)
|
||||
CustomizableUI.hidePanelForNode(domNode);
|
||||
}
|
||||
|
||||
// This kind of ugly workaround, instead we should implement
|
||||
// `getNodeView` for the `Widget` class itself, but that's kind of
|
||||
// hard without cleaning things up.
|
||||
this.panel.show(null, getNodeView.implement({}, () => anchor));
|
||||
this.panel.show(null, getNodeView.implement({}, () => domNode));
|
||||
}
|
||||
},
|
||||
|
||||
@ -795,8 +777,10 @@ WidgetChrome.prototype._createNode = function WC__createNode() {
|
||||
|
||||
// Initial population of a widget's content.
|
||||
WidgetChrome.prototype.fill = function WC_fill() {
|
||||
let { node, _doc: document } = this;
|
||||
|
||||
// Create element
|
||||
var iframe = this._doc.createElement("iframe");
|
||||
let iframe = document.createElement("iframe");
|
||||
iframe.setAttribute("type", "content");
|
||||
iframe.setAttribute("transparent", "transparent");
|
||||
iframe.style.overflow = "hidden";
|
||||
@ -809,14 +793,23 @@ WidgetChrome.prototype.fill = function WC_fill() {
|
||||
|
||||
// Do this early, because things like contentWindow are null
|
||||
// until the node is attached to a document.
|
||||
this.node.appendChild(iframe);
|
||||
node.appendChild(iframe);
|
||||
|
||||
var label = this._doc.createElement("label");
|
||||
let label = document.createElement("label");
|
||||
label.setAttribute("value", this._widget.label);
|
||||
label.className = "toolbarbutton-text";
|
||||
label.setAttribute("crop", "right");
|
||||
label.setAttribute("flex", "1");
|
||||
this.node.appendChild(label);
|
||||
node.appendChild(label);
|
||||
|
||||
// This toolbarbutton is created to provide a more consistent user experience
|
||||
// during customization, see:
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=959640
|
||||
let button = document.createElement("toolbarbutton");
|
||||
button.setAttribute("label", this._widget.label);
|
||||
button.setAttribute("crop", "right");
|
||||
button.className = "toolbarbutton-1 chromeclass-toolbar-additional";
|
||||
node.appendChild(button);
|
||||
|
||||
// add event handlers
|
||||
this.addEventHandlers();
|
||||
|
@ -19,6 +19,8 @@ const WM = Cc['@mozilla.org/appshell/window-mediator;1'].
|
||||
getService(Ci.nsIWindowMediator);
|
||||
const io = Cc['@mozilla.org/network/io-service;1'].
|
||||
getService(Ci.nsIIOService);
|
||||
const FM = Cc["@mozilla.org/focus-manager;1"].
|
||||
getService(Ci.nsIFocusManager);
|
||||
|
||||
const XUL_NS = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
|
||||
|
||||
@ -356,6 +358,18 @@ function getFocusedWindow() {
|
||||
}
|
||||
exports.getFocusedWindow = getFocusedWindow;
|
||||
|
||||
/**
|
||||
* Returns the focused browser window if any, or the most recent one.
|
||||
* Opening new window, updates most recent window, but focus window
|
||||
* changes later; so most recent window and focused window are not always
|
||||
* the same.
|
||||
*/
|
||||
function getFocusedBrowser() {
|
||||
let window = FM.activeWindow;
|
||||
return isBrowser(window) ? window : getMostRecentBrowserWindow()
|
||||
}
|
||||
exports.getFocusedBrowser = getFocusedBrowser;
|
||||
|
||||
/**
|
||||
* Returns the focused element in the most recent focused window
|
||||
*/
|
||||
|
@ -543,11 +543,9 @@ def initializer(env_root, args, out=sys.stdout, err=sys.stderr):
|
||||
if existing:
|
||||
print >>err, 'This command must be run in an empty directory.'
|
||||
return {"result":1}
|
||||
for d in ['lib','data','test','doc']:
|
||||
for d in ['lib','data','test']:
|
||||
os.mkdir(os.path.join(path,d))
|
||||
print >>out, '*', d, 'directory created'
|
||||
open(os.path.join(path,'README.md'),'w').write('')
|
||||
print >>out, '* README.md written'
|
||||
jid = create_jid()
|
||||
print >>out, '* generated jID automatically:', jid
|
||||
open(os.path.join(path,'package.json'),'w').write(PACKAGE_JSON % {'name':addon.lower(),
|
||||
@ -558,8 +556,6 @@ def initializer(env_root, args, out=sys.stdout, err=sys.stderr):
|
||||
print >>out, '* test/test-main.js written'
|
||||
open(os.path.join(path,'lib','main.js'),'w').write('')
|
||||
print >>out, '* lib/main.js written'
|
||||
open(os.path.join(path,'doc','main.md'),'w').write('')
|
||||
print >>out, '* doc/main.md written'
|
||||
if len(args) == 1:
|
||||
print >>out, '\nYour sample add-on is now ready.'
|
||||
print >>out, 'Do "cfx test" to test it and "cfx run" to try it. Have fun!'
|
||||
|
@ -736,7 +736,6 @@ def run_app(harness_root_dir, manifest_rdf, harness_options,
|
||||
if os.path.exists(resultfile):
|
||||
result = open(resultfile).read()
|
||||
if result:
|
||||
sys.stderr.write("resultfile contained " + "'" + result + "'\n")
|
||||
if result in ['OK', 'FAIL']:
|
||||
done = True
|
||||
else:
|
||||
@ -755,9 +754,7 @@ def run_app(harness_root_dir, manifest_rdf, harness_options,
|
||||
else:
|
||||
runner.wait(10)
|
||||
finally:
|
||||
sys.stderr.write("Done.\n")
|
||||
outf.close()
|
||||
sys.stderr.write("Clean the profile.\n")
|
||||
if profile:
|
||||
profile.cleanup()
|
||||
|
||||
|
@ -3,18 +3,12 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
'use strict';
|
||||
|
||||
module.metadata = {
|
||||
engines: {
|
||||
'Firefox': '*'
|
||||
}
|
||||
};
|
||||
|
||||
const { isTabOpen, activateTab, openTab,
|
||||
closeTab, getTabURL, getWindowHoldingTab } = require('sdk/tabs/utils');
|
||||
const windows = require('sdk/deprecated/window-utils');
|
||||
const { LoaderWithHookedConsole } = require('sdk/test/loader');
|
||||
const { setTimeout } = require('sdk/timers');
|
||||
const { is } = require('sdk/system/xul-app');
|
||||
const app = require("sdk/system/xul-app");
|
||||
const tabs = require('sdk/tabs');
|
||||
const isAustralis = "gCustomizeMode" in windows.activeBrowserWindow;
|
||||
const { set: setPref } = require("sdk/preferences/service");
|
||||
@ -44,6 +38,10 @@ function isChromeVisible(window) {
|
||||
return x !== 'true';
|
||||
}
|
||||
|
||||
// Once Bug 903018 is resolved, just move the application testing to
|
||||
// module.metadata.engines
|
||||
if (app.is('Firefox')) {
|
||||
|
||||
exports['test add-on page deprecation message'] = function(assert) {
|
||||
let { loader, messages } = LoaderWithHookedConsole(module);
|
||||
loader.require('sdk/addon-page');
|
||||
@ -74,7 +72,7 @@ exports['test that add-on page has no chrome'] = function(assert, done) {
|
||||
setTimeout(function() {
|
||||
activateTab(tab);
|
||||
|
||||
assert.equal(isChromeVisible(window), is('Fennec') || isAustralis,
|
||||
assert.equal(isChromeVisible(window), app.is('Fennec') || isAustralis,
|
||||
'chrome is not visible for addon page');
|
||||
|
||||
closeTabPromise(tab).then(function() {
|
||||
@ -100,7 +98,7 @@ exports['test that add-on page with hash has no chrome'] = function(assert, done
|
||||
setTimeout(function() {
|
||||
activateTab(tab);
|
||||
|
||||
assert.equal(isChromeVisible(window), is('Fennec') || isAustralis,
|
||||
assert.equal(isChromeVisible(window), app.is('Fennec') || isAustralis,
|
||||
'chrome is not visible for addon page');
|
||||
|
||||
closeTabPromise(tab).then(function() {
|
||||
@ -126,7 +124,7 @@ exports['test that add-on page with querystring has no chrome'] = function(asser
|
||||
setTimeout(function() {
|
||||
activateTab(tab);
|
||||
|
||||
assert.equal(isChromeVisible(window), is('Fennec') || isAustralis,
|
||||
assert.equal(isChromeVisible(window), app.is('Fennec') || isAustralis,
|
||||
'chrome is not visible for addon page');
|
||||
|
||||
closeTabPromise(tab).then(function() {
|
||||
@ -152,7 +150,7 @@ exports['test that add-on page with hash and querystring has no chrome'] = funct
|
||||
setTimeout(function() {
|
||||
activateTab(tab);
|
||||
|
||||
assert.equal(isChromeVisible(window), is('Fennec') || isAustralis,
|
||||
assert.equal(isChromeVisible(window), app.is('Fennec') || isAustralis,
|
||||
'chrome is not visible for addon page');
|
||||
|
||||
closeTabPromise(tab).then(function() {
|
||||
@ -185,4 +183,8 @@ exports['test that malformed uri is not an addon-page'] = function(assert, done)
|
||||
});
|
||||
};
|
||||
|
||||
} else {
|
||||
exports['test unsupported'] = (assert) => assert.pass('This application is unsupported.');
|
||||
}
|
||||
|
||||
require('sdk/test/runner').runTestsFromModule(module);
|
||||
|
@ -4,29 +4,36 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const { exec } = require("sdk/system/child_process");
|
||||
const { platform, pathFor } = require("sdk/system");
|
||||
const PROFILE_DIR = pathFor("ProfD");
|
||||
const isWindows = platform.toLowerCase().indexOf("win") === 0;
|
||||
|
||||
/**
|
||||
* Ensures using child_process and underlying subprocess.jsm
|
||||
* works within an addon
|
||||
*/
|
||||
exports["test child_process in an addon"] = (assert, done) => {
|
||||
exec(isWindows ? "DIR /A-D" : "ls -al", {
|
||||
cwd: PROFILE_DIR
|
||||
}, (err, stdout, stderr) => {
|
||||
assert.ok(!err, "no errors");
|
||||
assert.equal(stderr, "", "stderr is empty");
|
||||
assert.ok(/extensions\.ini/.test(stdout), "stdout output of `ls -al` finds files");
|
||||
|
||||
if (isWindows)
|
||||
assert.ok(!/<DIR>/.test(stdout), "passing args works");
|
||||
else
|
||||
assert.ok(/d(r[-|w][-|x]){3}/.test(stdout), "passing args works");
|
||||
done();
|
||||
});
|
||||
};
|
||||
const { exec } = require("sdk/system/child_process");
|
||||
const { platform, pathFor } = require("sdk/system");
|
||||
const PROFILE_DIR = pathFor("ProfD");
|
||||
const isWindows = platform.toLowerCase().indexOf("win") === 0;
|
||||
const app = require("sdk/system/xul-app");
|
||||
|
||||
// Once Bug 903018 is resolved, just move the application testing to
|
||||
// module.metadata.engines
|
||||
if (app.is("Firefox")) {
|
||||
exports["test child_process in an addon"] = (assert, done) => {
|
||||
exec(isWindows ? "DIR /A-D" : "ls -al", {
|
||||
cwd: PROFILE_DIR
|
||||
}, (err, stdout, stderr) => {
|
||||
assert.ok(!err, "no errors");
|
||||
assert.equal(stderr, "", "stderr is empty");
|
||||
assert.ok(/extensions\.ini/.test(stdout), "stdout output of `ls -al` finds files");
|
||||
|
||||
if (isWindows)
|
||||
assert.ok(!/<DIR>/.test(stdout), "passing args works");
|
||||
else
|
||||
assert.ok(/d(r[-|w][-|x]){3}/.test(stdout), "passing args works");
|
||||
done();
|
||||
});
|
||||
};
|
||||
} else {
|
||||
exports["test unsupported"] = (assert) => assert.pass("This application is unsupported.");
|
||||
}
|
||||
require("sdk/test/runner").runTestsFromModule(module);
|
||||
|
@ -4,21 +4,24 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
module.metadata = {
|
||||
'engines': {
|
||||
'Firefox': '*'
|
||||
}
|
||||
};
|
||||
|
||||
const { safeMerge: merge } = require('sdk/util/object');
|
||||
const app = require("sdk/system/xul-app");
|
||||
|
||||
merge(module.exports,
|
||||
require('./tests/test-places-bookmarks'),
|
||||
require('./tests/test-places-events'),
|
||||
require('./tests/test-places-favicon'),
|
||||
require('./tests/test-places-history'),
|
||||
require('./tests/test-places-host'),
|
||||
require('./tests/test-places-utils')
|
||||
);
|
||||
// Once Bug 903018 is resolved, just move the application testing to
|
||||
// module.metadata.engines
|
||||
if (app.is('Firefox')) {
|
||||
merge(module.exports,
|
||||
require('./tests/test-places-bookmarks'),
|
||||
require('./tests/test-places-events'),
|
||||
require('./tests/test-places-favicon'),
|
||||
require('./tests/test-places-history'),
|
||||
require('./tests/test-places-host'),
|
||||
require('./tests/test-places-utils')
|
||||
);
|
||||
} else {
|
||||
exports['test unsupported'] = (assert) => {
|
||||
assert.pass('This application is unsupported.');
|
||||
};
|
||||
}
|
||||
|
||||
require('sdk/test/runner').runTestsFromModule(module);
|
||||
|
@ -12,46 +12,54 @@ const { preferencesBranch } = require('sdk/self');
|
||||
|
||||
const { AddonManager } = Cu.import('resource://gre/modules/AddonManager.jsm', {});
|
||||
|
||||
exports.testAOMLocalization = function(assert, done) {
|
||||
tabs.open({
|
||||
url: 'about:addons',
|
||||
onReady: function(tab) {
|
||||
tab.attach({
|
||||
contentScriptWhen: 'end',
|
||||
contentScript: 'function onLoad() {\n' +
|
||||
'unsafeWindow.removeEventListener("load", onLoad, false);\n' +
|
||||
'AddonManager.getAddonByID("' + self.id + '", function(aAddon) {\n' +
|
||||
'unsafeWindow.gViewController.viewObjects.detail.node.addEventListener("ViewChanged", function whenViewChanges() {\n' +
|
||||
'unsafeWindow.gViewController.viewObjects.detail.node.removeEventListener("ViewChanged", whenViewChanges, false);\n' +
|
||||
'setTimeout(function() {\n' + // TODO: figure out why this is necessary..
|
||||
'self.postMessage({\n' +
|
||||
'somePreference: getAttributes(unsafeWindow.document.querySelector("setting[data-jetpack-id=\'' + self.id + '\']"))\n' +
|
||||
'});\n' +
|
||||
'}, 250);\n' +
|
||||
'}, false);\n' +
|
||||
'unsafeWindow.gViewController.commands.cmd_showItemDetails.doCommand(aAddon, true);\n' +
|
||||
'});\n' +
|
||||
'function getAttributes(ele) {\n' +
|
||||
'if (!ele) return {};\n' +
|
||||
'return {\n' +
|
||||
'title: ele.getAttribute("title")\n' +
|
||||
// Once Bug 903018 is resolved, just move the application testing to
|
||||
// module.metadata.engines
|
||||
//
|
||||
// This should work in Fennec, needs to be refactored to work, via bug 979645
|
||||
if (app.is('Firefox')) {
|
||||
exports.testAOMLocalization = function(assert, done) {
|
||||
tabs.open({
|
||||
url: 'about:addons',
|
||||
onReady: function(tab) {
|
||||
tab.attach({
|
||||
contentScriptWhen: 'end',
|
||||
contentScript: 'function onLoad() {\n' +
|
||||
'unsafeWindow.removeEventListener("load", onLoad, false);\n' +
|
||||
'AddonManager.getAddonByID("' + self.id + '", function(aAddon) {\n' +
|
||||
'unsafeWindow.gViewController.viewObjects.detail.node.addEventListener("ViewChanged", function whenViewChanges() {\n' +
|
||||
'unsafeWindow.gViewController.viewObjects.detail.node.removeEventListener("ViewChanged", whenViewChanges, false);\n' +
|
||||
'setTimeout(function() {\n' + // TODO: figure out why this is necessary..
|
||||
'self.postMessage({\n' +
|
||||
'somePreference: getAttributes(unsafeWindow.document.querySelector("setting[data-jetpack-id=\'' + self.id + '\']"))\n' +
|
||||
'});\n' +
|
||||
'}, 250);\n' +
|
||||
'}, false);\n' +
|
||||
'unsafeWindow.gViewController.commands.cmd_showItemDetails.doCommand(aAddon, true);\n' +
|
||||
'});\n' +
|
||||
'function getAttributes(ele) {\n' +
|
||||
'if (!ele) return {};\n' +
|
||||
'return {\n' +
|
||||
'title: ele.getAttribute("title")\n' +
|
||||
'}\n' +
|
||||
'}\n' +
|
||||
'}\n' +
|
||||
'}\n' +
|
||||
// Wait for the load event ?
|
||||
'if (document.readyState == "complete") {\n' +
|
||||
'onLoad()\n' +
|
||||
'} else {\n' +
|
||||
'unsafeWindow.addEventListener("load", onLoad, false);\n' +
|
||||
'}\n',
|
||||
onMessage: function(msg) {
|
||||
// test somePreference
|
||||
assert.equal(msg.somePreference.title, 'A', 'somePreference title is correct');
|
||||
tab.close(done);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// Wait for the load event ?
|
||||
'if (document.readyState == "complete") {\n' +
|
||||
'onLoad()\n' +
|
||||
'} else {\n' +
|
||||
'unsafeWindow.addEventListener("load", onLoad, false);\n' +
|
||||
'}\n',
|
||||
onMessage: function(msg) {
|
||||
// test somePreference
|
||||
assert.equal(msg.somePreference.title, 'A', 'somePreference title is correct');
|
||||
tab.close(done);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
exports['test unsupported'] = (assert) => assert.pass('This test is unsupported.');
|
||||
}
|
||||
|
||||
require('sdk/test/runner').runTestsFromModule(module);
|
||||
|
@ -46,9 +46,9 @@ exports["test frame API"] = function*(assert) {
|
||||
/The `options.url`/,
|
||||
"options.url must be local url");
|
||||
|
||||
assert.throws(() => new Frame({url: url, id: "4you" }),
|
||||
/The `option.id` must be a valid/,
|
||||
"can only take valid ID's");
|
||||
assert.throws(() => new Frame({url: url, name: "4you" }),
|
||||
/The `option.name` must be a valid/,
|
||||
"can only take valid names");
|
||||
|
||||
const f1 = new Frame({ url: url });
|
||||
|
||||
@ -64,8 +64,8 @@ exports["test frame API"] = function*(assert) {
|
||||
"can't have two identical frames");
|
||||
|
||||
|
||||
const f2 = new Frame({ id: "frame-2", url: url });
|
||||
assert.pass("can create frame with same url but diff id");
|
||||
const f2 = new Frame({ name: "frame-2", url: url });
|
||||
assert.pass("can create frame with same url but diff name");
|
||||
const p2 = wait(f2, "register");
|
||||
|
||||
yield p1;
|
||||
@ -145,7 +145,7 @@ exports["test host to content messaging"] = function*(assert) {
|
||||
event.source.postMessage("pong!", event.origin);
|
||||
});
|
||||
} + "</script>";
|
||||
const f1 = new Frame({ id: "mailbox", url: url });
|
||||
const f1 = new Frame({ name: "mailbox", url: url });
|
||||
const t1 = new Toolbar({ title: "mailbox", items: [f1] });
|
||||
|
||||
const e1 = yield wait(f1, "ready");
|
||||
@ -169,7 +169,7 @@ exports["test content to host messaging"] = function*(assert) {
|
||||
window.parent.postMessage("ping!", "*");
|
||||
} + "</script>";
|
||||
|
||||
const f1 = new Frame({ id: "inbox", url: url });
|
||||
const f1 = new Frame({ name: "inbox", url: url });
|
||||
const t1 = new Toolbar({ title: "inbox", items: [f1] });
|
||||
|
||||
const e1 = yield wait(f1, "message");
|
||||
@ -197,7 +197,7 @@ exports["test direct messaging"] = function*(assert) {
|
||||
} + "</script>";
|
||||
|
||||
const w1 = getMostRecentBrowserWindow();
|
||||
const f1 = new Frame({ url: url, id: "mail-cluster" });
|
||||
const f1 = new Frame({ url: url, name: "mail-cluster" });
|
||||
const t1 = new Toolbar({ title: "claster", items: [f1] });
|
||||
|
||||
yield wait(f1, "ready");
|
||||
|
@ -1020,6 +1020,44 @@ exports['test button click do not messing up states'] = function(assert) {
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
exports['test buttons can have anchored panels'] = function(assert, done) {
|
||||
let loader = Loader(module);
|
||||
let { ToggleButton } = loader.require('sdk/ui');
|
||||
let { Panel } = loader.require('sdk/panel');
|
||||
let { identify } = loader.require('sdk/ui/id');
|
||||
let { getActiveView } = loader.require('sdk/view/core');
|
||||
|
||||
let button = ToggleButton({
|
||||
id: 'my-button-22',
|
||||
label: 'my button',
|
||||
icon: './icon.png',
|
||||
onChange: ({checked}) => checked && panel.show({position: button})
|
||||
});
|
||||
|
||||
let panel = Panel();
|
||||
|
||||
panel.once('show', () => {
|
||||
let { document } = getMostRecentBrowserWindow();
|
||||
let buttonNode = document.getElementById(identify(button));
|
||||
let panelNode = getActiveView(panel);
|
||||
|
||||
assert.ok(button.state('window').checked,
|
||||
'button is checked');
|
||||
|
||||
assert.equal(panelNode.getAttribute('type'), 'arrow',
|
||||
'the panel is a arrow type');
|
||||
|
||||
assert.strictEqual(buttonNode, panelNode.anchorNode,
|
||||
'the panel is anchored properly to the button');
|
||||
|
||||
loader.unload();
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
button.click();
|
||||
}
|
||||
|
||||
// If the module doesn't support the app we're being run in, require() will
|
||||
// throw. In that case, remove all tests above from exports, and add one dummy
|
||||
// test that passes.
|
||||
|
@ -12,11 +12,12 @@ module.metadata = {
|
||||
const { Toolbar } = require("sdk/ui/toolbar");
|
||||
const { Loader } = require("sdk/test/loader");
|
||||
const { identify } = require("sdk/ui/id");
|
||||
const { getMostRecentBrowserWindow, open } = require("sdk/window/utils");
|
||||
const { getMostRecentBrowserWindow, open, getOuterId } = require("sdk/window/utils");
|
||||
const { ready, close } = require("sdk/window/helpers");
|
||||
const { defer } = require("sdk/core/promise");
|
||||
const { send } = require("sdk/event/utils");
|
||||
const { send, stop, Reactor } = require("sdk/event/utils");
|
||||
const { object } = require("sdk/util/sequence");
|
||||
const { CustomizationInput } = require("sdk/input/customizable-ui");
|
||||
const { OutputPort } = require("sdk/output/system");
|
||||
const output = new OutputPort({ id: "toolbar-change" });
|
||||
|
||||
@ -357,4 +358,120 @@ exports["test title change"] = function*(assert) {
|
||||
yield close(w2);
|
||||
};
|
||||
|
||||
exports["test toolbar is not customizable"] = function*(assert, done) {
|
||||
const { window, document, gCustomizeMode } = getMostRecentBrowserWindow();
|
||||
const outerId = getOuterId(window);
|
||||
const input = new CustomizationInput();
|
||||
const customized = defer();
|
||||
const customizedEnd = defer();
|
||||
|
||||
new Reactor({ onStep: value => {
|
||||
if (value[outerId] === true)
|
||||
customized.resolve();
|
||||
if (value[outerId] === null)
|
||||
customizedEnd.resolve();
|
||||
}}).run(input);
|
||||
|
||||
const toolbar = new Toolbar({ title: "foo" });
|
||||
|
||||
yield wait(toolbar, "attach");
|
||||
|
||||
let view = document.getElementById(toolbar.id);
|
||||
let label = view.querySelector("label");
|
||||
let inner = view.querySelector("toolbar");
|
||||
|
||||
assert.equal(view.getAttribute("customizable"), "false",
|
||||
"The outer toolbar is not customizable.");
|
||||
|
||||
assert.ok(label.collapsed,
|
||||
"The label is not displayed.")
|
||||
|
||||
assert.equal(inner.getAttribute("customizable"), "true",
|
||||
"The inner toolbar is customizable.");
|
||||
|
||||
assert.equal(window.getComputedStyle(inner).visibility, "visible",
|
||||
"The inner toolbar is visible.");
|
||||
|
||||
// Enter in customization mode
|
||||
gCustomizeMode.toggle();
|
||||
|
||||
yield customized.promise;
|
||||
|
||||
assert.equal(view.getAttribute("customizable"), "false",
|
||||
"The outer toolbar is not customizable.");
|
||||
|
||||
assert.equal(label.collapsed, false,
|
||||
"The label is displayed.")
|
||||
|
||||
assert.equal(inner.getAttribute("customizable"), "true",
|
||||
"The inner toolbar is customizable.");
|
||||
|
||||
assert.equal(window.getComputedStyle(inner).visibility, "hidden",
|
||||
"The inner toolbar is hidden.");
|
||||
|
||||
// Exit from customization mode
|
||||
gCustomizeMode.toggle();
|
||||
|
||||
yield customizedEnd.promise;
|
||||
|
||||
assert.equal(view.getAttribute("customizable"), "false",
|
||||
"The outer toolbar is not customizable.");
|
||||
|
||||
assert.ok(label.collapsed,
|
||||
"The label is not displayed.")
|
||||
|
||||
assert.equal(inner.getAttribute("customizable"), "true",
|
||||
"The inner toolbar is customizable.");
|
||||
|
||||
assert.equal(window.getComputedStyle(inner).visibility, "visible",
|
||||
"The inner toolbar is visible.");
|
||||
|
||||
toolbar.destroy();
|
||||
};
|
||||
|
||||
exports["test button are attached to toolbar"] = function*(assert) {
|
||||
const { document } = getMostRecentBrowserWindow();
|
||||
const { ActionButton, ToggleButton } = require("sdk/ui");
|
||||
const { identify } = require("sdk/ui/id");
|
||||
|
||||
let action = ActionButton({
|
||||
id: "btn-1",
|
||||
label: "action",
|
||||
icon: "./placeholder.png"
|
||||
});
|
||||
|
||||
let toggle = ToggleButton({
|
||||
id: "btn-2",
|
||||
label: "toggle",
|
||||
icon: "./placeholder.png"
|
||||
});
|
||||
|
||||
const toolbar = new Toolbar({
|
||||
title: "foo",
|
||||
items: [action, toggle]
|
||||
});
|
||||
|
||||
yield wait(toolbar, "attach");
|
||||
|
||||
let actionNode = document.getElementById(identify(action));
|
||||
let toggleNode = document.getElementById(identify(toggle));
|
||||
|
||||
assert.notEqual(actionNode, null,
|
||||
"action button exists in the document");
|
||||
|
||||
assert.notEqual(actionNode, null,
|
||||
"action button exists in the document");
|
||||
|
||||
assert.notEqual(toggleNode, null,
|
||||
"toggle button exists in the document");
|
||||
|
||||
assert.equal(actionNode.nextElementSibling, toggleNode,
|
||||
"action button is placed before toggle button");
|
||||
|
||||
assert.equal(actionNode.parentNode.parentNode.id, toolbar.id,
|
||||
"buttons are placed in the correct toolbar");
|
||||
|
||||
toolbar.destroy();
|
||||
};
|
||||
|
||||
require("sdk/test").run(exports);
|
||||
|
Loading…
Reference in New Issue
Block a user