mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 13:51:41 +00:00
Bug 906173 - Uplift addon-sdk to Firefox r=me
This commit is contained in:
parent
d0ff61182b
commit
5c4a4d62bf
2
addon-sdk/source/app-extension/bootstrap.js
vendored
2
addon-sdk/source/app-extension/bootstrap.js
vendored
@ -222,6 +222,8 @@ function startup(data, reasonCode) {
|
||||
resultFile: options.resultFile,
|
||||
// Arguments passed as --static-args
|
||||
staticArgs: options.staticArgs,
|
||||
// Add-on preferences branch name
|
||||
preferencesBranch: options.preferencesBranch,
|
||||
|
||||
// Arguments related to test runner.
|
||||
modules: {
|
||||
|
10
addon-sdk/source/data/test-sidebar-addon-global.html
Normal file
10
addon-sdk/source/data/test-sidebar-addon-global.html
Normal file
@ -0,0 +1,10 @@
|
||||
<script>
|
||||
addon.port.on('X', function(msg) {
|
||||
// last message
|
||||
addon.port.emit('X', msg + '3');
|
||||
});
|
||||
|
||||
// start messaging chain
|
||||
addon.port.emit('Y', '1');
|
||||
</script>
|
||||
SIDEBAR TEST
|
@ -70,6 +70,7 @@ We'd like to thank our many Jetpack project contributors! They include:
|
||||
|
||||
### J ###
|
||||
|
||||
* Tomislav Jovanovic
|
||||
* Eric H. Jung
|
||||
|
||||
### K ###
|
||||
@ -100,6 +101,7 @@ We'd like to thank our many Jetpack project contributors! They include:
|
||||
* Joe R. Nassimian ([placidrage](https://github.com/placidrage))
|
||||
* Dương H. Nguyễn
|
||||
* Nick Nguyen
|
||||
* nodeless
|
||||
|
||||
### O ###
|
||||
|
||||
|
@ -46,7 +46,7 @@ Then open "lib/main.js" and add the following code:
|
||||
onItemVisited: function(aItemId, aVisitID, time) {
|
||||
console.log("visited ", bookmarkService.getBookmarkURI(aItemId).spec);
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsINavBookmarkObserver])
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver])
|
||||
};
|
||||
|
||||
exports.main = function() {
|
||||
|
214
addon-sdk/source/doc/module-source/sdk/ui.md
Normal file
214
addon-sdk/source/doc/module-source/sdk/ui.md
Normal file
@ -0,0 +1,214 @@
|
||||
<!-- 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/. -->
|
||||
|
||||
<!-- contributed by Erik Vold [evold@mozilla.com] -->
|
||||
|
||||
This module exports a two constructor functions `Button` which constructs a
|
||||
new toolbar button, and `Sidebar` which constructs a sidebar (with a button).
|
||||
|
||||
Sidebars are displayed on the left side of your browser. Its content is specified as
|
||||
local HTML, so the appearance and behaviour of the sidebar
|
||||
is limited only by what you can do using HTML, CSS and JavaScript.
|
||||
|
||||
The screenshot below shows a sidebar whose content is built from tweets:
|
||||
|
||||
<!-- add screen shot here -->
|
||||
|
||||
Sidebars are useful for presenting temporary interfaces to users in a way that is
|
||||
easier for users to ignore and dismiss than a modal dialog
|
||||
and easier for users to keep around than a Panel, since sidebars are
|
||||
displayed at the side of the browser until the user decides to close it.
|
||||
|
||||
A sidebar's content is loaded anew as soon as it is opened, and unloads
|
||||
when the user closes the sidebar.
|
||||
|
||||
Your add-on can receive notifications when a sidebar is shown or hidden by
|
||||
listening to its `show` and `hide` events.
|
||||
|
||||
Opening a sidebar in a window will close an already opened sidebar in that window.
|
||||
|
||||
## Sidebar Content ##
|
||||
|
||||
The sidebar's content is specified as HTML, which is loaded from the URL
|
||||
supplied in the `url` option to the sidebar's constructor.
|
||||
|
||||
You can load remote HTML into the sidebar:
|
||||
|
||||
var sidebar = require("sdk/ui").Sidebar({
|
||||
id: 'a-new-sidebar',
|
||||
title: 'A New Sidebar',
|
||||
icon: './icon.png',
|
||||
url: './index.html'
|
||||
});
|
||||
|
||||
sidebar.show();
|
||||
|
||||
This will load HTML that's been packaged with your add-on, and this is
|
||||
most probably how you will create sidebars. To do this, save
|
||||
the `index.html` HTML file in your add-on's `data` directory.
|
||||
|
||||
## Sidebar Positioning ##
|
||||
|
||||
By default the sidebars appears on the left side of the currently active browser window.
|
||||
|
||||
|
||||
## Updating Sidebar Content ##
|
||||
|
||||
You can update the sidebar's content simply by setting the sidebar's `url`
|
||||
property. Note this will change the sidebar's url for all windows.
|
||||
|
||||
## Scripting Sidebar Content ##
|
||||
|
||||
You can't directly access your sidebar's content from your main add-on code.
|
||||
To access the sidebar's content, you need to add a `<script>` into the sidebar.
|
||||
|
||||
The sidebar's scripts will have access to a `addon` global, with you can
|
||||
communicate with your main add-on code, like so:
|
||||
|
||||
`lib/main.js`:
|
||||
|
||||
let sidebar = Sidebar({
|
||||
id: 'a-new-sidebar',
|
||||
title: 'A New Sidebar',
|
||||
icon: './icon.png',
|
||||
url: './index.html',
|
||||
onAttach: function (worker) {
|
||||
worker.port.on('message', function() { // part 2
|
||||
// do things...
|
||||
worker.port.emit('message', 'I have information for you!'); // part 3
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
`data/index.html`
|
||||
|
||||
<pre class="brush: html">
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
...
|
||||
<script>
|
||||
addon.port.on('message', function(msg) { // part 4
|
||||
// msg will == 'I have information for you!'
|
||||
});
|
||||
// starting communication here..
|
||||
addon.port.emit('message'); // part 1
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
<api name="Sidebar">
|
||||
@class
|
||||
The Sidebar object.
|
||||
|
||||
Once a sidebar object has been created it can be shown and hidden,
|
||||
in the active window, using its
|
||||
`show()` and `hide()` methods. Once a sidebar is no longer needed it can be
|
||||
removed/destructed using `destroy()`.
|
||||
|
||||
<api name="Sidebar">
|
||||
@constructor
|
||||
Creates a sidebar.
|
||||
@param options {object}
|
||||
Options for the sidebar, with the following keys:
|
||||
@prop id {string}
|
||||
The `id` of the sidebar.
|
||||
@prop title {string}
|
||||
A title for the sidebar.
|
||||
@prop icon {string, object}
|
||||
The icon used for the sidebar's button.
|
||||
@prop url {string, URL}
|
||||
The URL of the content to load in the sidebar.
|
||||
@prop [onAttach] {function}
|
||||
Include this to listen to the sidebar's `attach` event.
|
||||
@prop [onShow] {function}
|
||||
Include this to listen to the sidebar's `show` event.
|
||||
@prop [onHide] {function}
|
||||
Include this to listen to the sidebar's `hide` event.
|
||||
</api>
|
||||
|
||||
<api name="id">
|
||||
@property {string}
|
||||
The `id` for the sidebar.
|
||||
</api>
|
||||
|
||||
<api name="title">
|
||||
@property {string}
|
||||
The `title` of the sidebar.
|
||||
</api>
|
||||
|
||||
<api name="icon">
|
||||
@property {string, object}
|
||||
The global icon for the sidebar.
|
||||
</api>
|
||||
|
||||
<api name="url">
|
||||
@property {string}
|
||||
The URL of content loaded into the sidebar.
|
||||
</api>
|
||||
|
||||
<api name="destroy">
|
||||
@method
|
||||
Destroys the sidebar, once destroyed, the sidebar can no longer be used.
|
||||
</api>
|
||||
|
||||
<api name="show">
|
||||
@method
|
||||
Displays the sidebar in the active window.
|
||||
</api>
|
||||
|
||||
<api name="hide">
|
||||
@method
|
||||
Hides the sidebar in the active window.
|
||||
</api>
|
||||
|
||||
<api name="on">
|
||||
@method
|
||||
Registers an event listener with the sidebar.
|
||||
@param type {string}
|
||||
The type of event to listen for.
|
||||
@param listener {function}
|
||||
The listener function that handles the event.
|
||||
</api>
|
||||
|
||||
<api name="once">
|
||||
@method
|
||||
Registers an event listener with the sidebar.
|
||||
The difference between `on` and `once` is that
|
||||
`on` will continue listening until it is
|
||||
removed, whereas `once` is removed automatically
|
||||
upon the first event it catches.
|
||||
@param type {string}
|
||||
The type of event to listen for.
|
||||
@param listener {function}
|
||||
The listener function that handles the event.
|
||||
</api>
|
||||
|
||||
<api name="removeListener">
|
||||
@method
|
||||
Unregisters/removes an event listener from the sidebar.
|
||||
@param type {string}
|
||||
The type of event for which `listener` was registered.
|
||||
@param listener {function}
|
||||
The listener function that was registered.
|
||||
</api>
|
||||
|
||||
<api name="attach">
|
||||
@event
|
||||
This event is emitted when the sidebar's window
|
||||
is created and the `addon` global was added.
|
||||
</api>
|
||||
|
||||
<api name="show">
|
||||
@event
|
||||
This event is emitted when the sidebar is shown.
|
||||
</api>
|
||||
|
||||
<api name="hide">
|
||||
@event
|
||||
This event is emitted when the sidebar is hidden.
|
||||
</api>
|
||||
|
||||
</api>
|
@ -48,7 +48,6 @@ const { override, load } = loaderModule;
|
||||
* @returns {Error}
|
||||
*/
|
||||
function incompatibility(module) {
|
||||
|
||||
let { metadata, id } = module;
|
||||
|
||||
// if metadata or engines are not specified we assume compatibility is not
|
||||
|
@ -11,9 +11,11 @@ const { emit, off } = require("./event/core");
|
||||
const { when: unload } = require("./system/unload");
|
||||
const { PrefsTarget } = require("./preferences/event-target");
|
||||
const { id } = require("./self");
|
||||
const { preferencesBranch } = require('@loader/options');
|
||||
|
||||
const observers = require("./deprecated/observer-service");
|
||||
|
||||
const ADDON_BRANCH = "extensions." + id + ".";
|
||||
const ADDON_BRANCH = "extensions." + preferencesBranch + ".";
|
||||
const BUTTON_PRESSED = id + "-cmdPressed";
|
||||
|
||||
const target = PrefsTarget({ branchName: ADDON_BRANCH });
|
||||
|
14
addon-sdk/source/lib/sdk/ui.js
Normal file
14
addon-sdk/source/lib/sdk/ui.js
Normal file
@ -0,0 +1,14 @@
|
||||
/* 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';
|
||||
|
||||
module.metadata = {
|
||||
'stability': 'experimental',
|
||||
'engines': {
|
||||
'Firefox': '> 24'
|
||||
}
|
||||
};
|
||||
|
||||
exports.Button = require('./ui/button').Button;
|
||||
exports.Sidebar = require('./ui/sidebar').Sidebar;
|
159
addon-sdk/source/lib/sdk/ui/button.js
Normal file
159
addon-sdk/source/lib/sdk/ui/button.js
Normal file
@ -0,0 +1,159 @@
|
||||
/* 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';
|
||||
|
||||
module.metadata = {
|
||||
'stability': 'experimental',
|
||||
'engines': {
|
||||
'Firefox': '> 24'
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
require('chrome').Cu.import('resource:///modules/CustomizableUI.jsm', {});
|
||||
}
|
||||
catch (e) {
|
||||
throw Error('Unsupported Application: The module ' + module.id + ' does not support this application.');
|
||||
}
|
||||
|
||||
const { Class } = require('../core/heritage');
|
||||
const { merge } = require('../util/object');
|
||||
const { properties, render, state, register, unregister } = require("./state");
|
||||
const { Disposable } = require('../core/disposable');
|
||||
const { contract } = require('../util/contract');
|
||||
const { on, off, emit, setListeners } = require('../event/core');
|
||||
const { EventTarget } = require('../event/target');
|
||||
|
||||
const { isNil, isObject, isString } = require('../lang/type');
|
||||
const { required, either, string, number, boolean, object } = require('../deprecated/api-utils');
|
||||
const { isLocalURL } = require('../url');
|
||||
|
||||
const { add, remove, has, clear, iterator } = require("../lang/weak-set");
|
||||
|
||||
const tabs = require("../tabs");
|
||||
const { browserWindows } = require("sdk/windows");
|
||||
|
||||
const view = require("./button/view");
|
||||
|
||||
const { data } = require("../self");
|
||||
|
||||
function isIconSet(icons) {
|
||||
return Object.keys(icons).
|
||||
every(size => String(size >>> 0) === size && isLocalURL(icons[size]))
|
||||
}
|
||||
|
||||
let iconSet = {
|
||||
is: either(object, string),
|
||||
ok: v => (isString(v) && isLocalURL(v)) || (isObject(v) && isIconSet(v)),
|
||||
msg: 'The option "icon" must be a local URL or an object with ' +
|
||||
'numeric keys / local URL values pair.'
|
||||
}
|
||||
|
||||
let buttonId = {
|
||||
is: string,
|
||||
ok: v => /^[a-z0-9-_]+$/i.test(v),
|
||||
msg: 'The option "id" must be a valid alphanumeric id (hyphens and ' +
|
||||
'underscores are allowed).'
|
||||
};
|
||||
|
||||
let buttonType = {
|
||||
is: string,
|
||||
ok: v => ~['button', 'checkbox'].indexOf(v),
|
||||
msg: 'The option "type" must be one of the following string values: ' +
|
||||
'"button", "checkbox".'
|
||||
}
|
||||
|
||||
let size = {
|
||||
is: string,
|
||||
ok: v => ~['small', 'medium', 'large'].indexOf(v),
|
||||
msg: 'The option "size" must be one of the following string values: ' +
|
||||
'"small", "medium", "large".'
|
||||
};
|
||||
|
||||
let label = {
|
||||
is: string,
|
||||
ok: v => isNil(v) || v.trim().length > 0,
|
||||
msg: 'The option "label" must be a non empty string'
|
||||
}
|
||||
|
||||
let stateContract = contract({
|
||||
label: label,
|
||||
icon: iconSet,
|
||||
disabled: boolean,
|
||||
checked: boolean
|
||||
});
|
||||
|
||||
let buttonContract = contract(merge({}, stateContract.rules, {
|
||||
id: required(buttonId),
|
||||
label: required(label),
|
||||
icon: required(iconSet),
|
||||
type: buttonType,
|
||||
size: size
|
||||
}));
|
||||
|
||||
const Button = Class({
|
||||
extends: EventTarget,
|
||||
implements: [
|
||||
properties(stateContract),
|
||||
state(stateContract),
|
||||
Disposable
|
||||
],
|
||||
setup: function setup(options) {
|
||||
let state = merge({
|
||||
type: 'button',
|
||||
disabled: false,
|
||||
checked: false,
|
||||
size: 'small',
|
||||
}, buttonContract(options));
|
||||
|
||||
// Setup listeners.
|
||||
setListeners(this, options);
|
||||
|
||||
// TODO: improve
|
||||
let viewEvents = view.create(state);
|
||||
|
||||
on(viewEvents, 'click', onViewClick.bind(this));
|
||||
on(viewEvents, 'moved', () => render(this));
|
||||
|
||||
register(this, state);
|
||||
},
|
||||
|
||||
dispose: function dispose() {
|
||||
off(this);
|
||||
|
||||
view.dispose(this);
|
||||
|
||||
unregister(this);
|
||||
},
|
||||
|
||||
get id() this.state().id,
|
||||
get size() this.state().size,
|
||||
get type() this.state().type,
|
||||
|
||||
click: function click() view.click(this)
|
||||
});
|
||||
exports.Button = Button;
|
||||
|
||||
function onViewClick() {
|
||||
let state = this.state(tabs.activeTab);
|
||||
|
||||
if (this.type === 'checkbox') {
|
||||
state = merge({}, state, { checked: !state.checked });
|
||||
|
||||
this.state(browserWindows.activeWindow, state);
|
||||
|
||||
emit(this, 'click', state);
|
||||
|
||||
emit(this, 'change', state);
|
||||
}
|
||||
else
|
||||
emit(this, 'click', state);
|
||||
}
|
||||
|
||||
on(Button, 'render', function(button, window, state) {
|
||||
view.setIcon(button, window, state.icon);
|
||||
view.setLabel(button, window, state.label);
|
||||
view.setDisabled(button, window, state.disabled);
|
||||
view.setChecked(button, window, state.checked);
|
||||
});
|
201
addon-sdk/source/lib/sdk/ui/button/view.js
Normal file
201
addon-sdk/source/lib/sdk/ui/button/view.js
Normal file
@ -0,0 +1,201 @@
|
||||
/* 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';
|
||||
|
||||
module.metadata = {
|
||||
'stability': 'experimental',
|
||||
'engines': {
|
||||
'Firefox': '> 24'
|
||||
}
|
||||
};
|
||||
|
||||
const { Cu } = require('chrome');
|
||||
const { on, off, emit } = require('../../event/core');
|
||||
|
||||
const { id: addonID, data } = require('sdk/self');
|
||||
const buttonPrefix = 'button--' + addonID.replace(/@/g, '-at-');
|
||||
|
||||
const { isObject } = require('../../lang/type');
|
||||
|
||||
const { getMostRecentBrowserWindow } = require('../../window/utils');
|
||||
const { ignoreWindow } = require('../../private-browsing/utils');
|
||||
const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
|
||||
const { AREA_PANEL, AREA_NAVBAR } = CustomizableUI;
|
||||
|
||||
const SIZE = {
|
||||
'small': 16,
|
||||
'medium': 32,
|
||||
'large': 64
|
||||
}
|
||||
|
||||
const XUL_NS = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
|
||||
|
||||
const toWidgetID = function(id) buttonPrefix + '-' + id;
|
||||
const toButtonID = function(id) id.substr(buttonPrefix.length + 1);
|
||||
|
||||
const views = {};
|
||||
|
||||
const buttonListener = {
|
||||
onWidgetAdded: function(widgetId, area, position) {
|
||||
let id = toButtonID(widgetId);
|
||||
|
||||
if (id in views && views[id].area !== area) {
|
||||
views[id].area = area;
|
||||
emit(views[id].events, 'moved', area === AREA_PANEL);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
CustomizableUI.addListener(buttonListener);
|
||||
|
||||
require('../../system/unload').when(function(){
|
||||
CustomizableUI.removeListener(buttonListener);
|
||||
});
|
||||
|
||||
function getNode(id, window) {
|
||||
return !(id in views) || ignoreWindow(window)
|
||||
? null
|
||||
: CustomizableUI.getWidget(toWidgetID(id)).forWindow(window).node
|
||||
};
|
||||
|
||||
function isInPanel(id) views[id] && views[id].area === AREA_PANEL;
|
||||
|
||||
function getImage(icon, areaIsPanel, pixelRatio) {
|
||||
let targetSize = (areaIsPanel ? 32 : 18) * pixelRatio;
|
||||
let bestSize = 0;
|
||||
let image = icon;
|
||||
|
||||
if (isObject(icon)) {
|
||||
for (let size of Object.keys(icon)) {
|
||||
size = +size;
|
||||
let offset = targetSize - size;
|
||||
|
||||
if (offset === 0) {
|
||||
bestSize = size;
|
||||
break;
|
||||
}
|
||||
|
||||
let delta = Math.abs(offset) - Math.abs(targetSize - bestSize);
|
||||
|
||||
if (delta < 0)
|
||||
bestSize = size;
|
||||
}
|
||||
|
||||
image = icon[bestSize];
|
||||
}
|
||||
|
||||
if (image.indexOf('./') === 0)
|
||||
return data.url(image.substr(2));
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
function create(options) {
|
||||
let { id, label, image, size, type } = options;
|
||||
let bus = {};
|
||||
|
||||
if (id in views)
|
||||
throw new Error('The ID "' + id + '" seems already used.');
|
||||
|
||||
CustomizableUI.createWidget({
|
||||
id: toWidgetID(id),
|
||||
type: 'custom',
|
||||
removable: true,
|
||||
defaultArea: AREA_NAVBAR,
|
||||
allowedAreas: [ AREA_PANEL, AREA_NAVBAR ],
|
||||
|
||||
onBuild: function(document) {
|
||||
let window = document.defaultView;
|
||||
|
||||
let node = document.createElementNS(XUL_NS, 'toolbarbutton');
|
||||
|
||||
if (ignoreWindow(window))
|
||||
node.style.display = 'none';
|
||||
|
||||
node.setAttribute('id', this.id);
|
||||
node.setAttribute('class', 'toolbarbutton-1 chromeclass-toolbar-additional');
|
||||
node.setAttribute('width', SIZE[size] || 16);
|
||||
node.setAttribute('type', type);
|
||||
|
||||
views[id] = {
|
||||
events: bus,
|
||||
area: this.currentArea
|
||||
};
|
||||
|
||||
node.addEventListener('command', function(event) {
|
||||
if (views[id])
|
||||
emit(views[id].events, 'click', event);
|
||||
});
|
||||
|
||||
return node;
|
||||
}
|
||||
});
|
||||
|
||||
return bus;
|
||||
};
|
||||
exports.create = create;
|
||||
|
||||
function dispose({id}) {
|
||||
if (!views[id]) return;
|
||||
|
||||
off(views[id].events);
|
||||
delete views[id];
|
||||
CustomizableUI.destroyWidget(toWidgetID(id));
|
||||
}
|
||||
exports.dispose = dispose;
|
||||
|
||||
function setIcon({id}, window, icon) {
|
||||
let node = getNode(id, window);
|
||||
|
||||
if (node) {
|
||||
let image = getImage(icon, isInPanel(id), window.devicePixelRatio);
|
||||
|
||||
node.setAttribute('image', image);
|
||||
}
|
||||
}
|
||||
exports.setIcon = setIcon;
|
||||
|
||||
function setLabel({id}, window, label) {
|
||||
let node = getNode(id, window);
|
||||
|
||||
if (node) {
|
||||
node.setAttribute('label', label);
|
||||
node.setAttribute('tooltiptext', label);
|
||||
}
|
||||
}
|
||||
exports.setLabel = setLabel;
|
||||
|
||||
function setDisabled({id}, window, disabled) {
|
||||
let node = getNode(id, window);
|
||||
|
||||
if (node) {
|
||||
if (disabled)
|
||||
node.setAttribute('disabled', disabled);
|
||||
else
|
||||
node.removeAttribute('disabled');
|
||||
}
|
||||
}
|
||||
exports.setDisabled = setDisabled;
|
||||
|
||||
function setChecked({id}, window, checked) {
|
||||
let node = getNode(id, window);
|
||||
|
||||
if (node) {
|
||||
if (checked)
|
||||
node.setAttribute('checked', checked);
|
||||
else
|
||||
node.removeAttribute('checked');
|
||||
}
|
||||
}
|
||||
exports.setChecked = setChecked;
|
||||
|
||||
function click({id}) {
|
||||
let window = getMostRecentBrowserWindow();
|
||||
|
||||
let node = getNode(id, window);
|
||||
|
||||
if (node)
|
||||
node.click();
|
||||
}
|
||||
exports.click = click;
|
330
addon-sdk/source/lib/sdk/ui/sidebar.js
Normal file
330
addon-sdk/source/lib/sdk/ui/sidebar.js
Normal file
@ -0,0 +1,330 @@
|
||||
/* 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';
|
||||
|
||||
module.metadata = {
|
||||
'stability': 'experimental',
|
||||
'engines': {
|
||||
'Firefox': '> 24'
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
require('chrome').Cu.import('resource:///modules/CustomizableUI.jsm', {});
|
||||
}
|
||||
catch (e) {
|
||||
throw Error('Unsupported Application: The module ' + module.id + ' does not support this application.');
|
||||
}
|
||||
|
||||
const { Class } = require('../core/heritage');
|
||||
const { merge } = require('../util/object');
|
||||
const { Disposable } = require('../core/disposable');
|
||||
const { off, emit, setListeners } = require('../event/core');
|
||||
const { EventTarget } = require('../event/target');
|
||||
const { URL } = require('../url');
|
||||
const { add, remove, has, clear, iterator } = require('../lang/weak-set');
|
||||
const { WindowTracker } = require('../deprecated/window-utils');
|
||||
const { isShowing } = require('./sidebar/utils');
|
||||
const { isBrowser, getMostRecentBrowserWindow, windows, isWindowPrivate } = require('../window/utils');
|
||||
const { ns } = require('../core/namespace');
|
||||
const { remove: removeFromArray } = require('../util/array');
|
||||
const { show, hide, toggle } = require('./sidebar/actions');
|
||||
const { Worker: WorkerTrait } = require('../content/worker');
|
||||
const { contract: sidebarContract } = require('./sidebar/contract');
|
||||
const { Button } = require('./button');
|
||||
const { setStateFor, getStateFor } = require('./state');
|
||||
const { create, dispose, updateTitle, updateURL, isSidebarShowing, showSidebar, hideSidebar } = require('./sidebar/view');
|
||||
const { defer } = require('../core/promise');
|
||||
const { models, buttons, views, viewsFor, modelFor } = require('./sidebar/namespace');
|
||||
const { isLocalURL } = require('../url');
|
||||
const { ensure } = require('../system/unload');
|
||||
|
||||
const Worker = WorkerTrait.resolve({
|
||||
_injectInDocument: '__injectInDocument'
|
||||
}).compose({
|
||||
get _injectInDocument() true
|
||||
});
|
||||
|
||||
const sidebarNS = ns();
|
||||
|
||||
const WEB_PANEL_BROWSER_ID = 'web-panels-browser';
|
||||
|
||||
let sidebars = {};
|
||||
|
||||
const Sidebar = Class({
|
||||
implements: [ Disposable ],
|
||||
extends: EventTarget,
|
||||
setup: function(options) {
|
||||
let model = sidebarContract(options);
|
||||
models.set(this, model);
|
||||
|
||||
validateTitleAndURLCombo({}, this.title, this.url);
|
||||
|
||||
// NOTE: delegating icon validation to the Button.
|
||||
// IMPORTANT: Make the button first since it has it's own
|
||||
// validation which we make use of.. (even if the sidebar
|
||||
// id is not a duplicate the button id could be..)
|
||||
let button = Button({
|
||||
id: model.id,
|
||||
icon: model.icon,
|
||||
label: model.title,
|
||||
type: 'checkbox',
|
||||
onChange: update.bind(null, 'button')
|
||||
});
|
||||
buttons.set(this, button);
|
||||
|
||||
const self = this;
|
||||
const internals = sidebarNS(self);
|
||||
const windowNS = internals.windowNS = ns();
|
||||
|
||||
// see bug https://bugzilla.mozilla.org/show_bug.cgi?id=886148
|
||||
ensure(this, 'destroy');
|
||||
|
||||
setListeners(this, options);
|
||||
|
||||
function update(source, state) {
|
||||
let wins = windows('navigator:browser', { includePrivate: true });
|
||||
|
||||
for (let window of wins) {
|
||||
let isShowing = isSidebarShowing(window, self);
|
||||
let isChecked = (source == 'button') ? getStateFor(button, window).checked : isShowing;
|
||||
|
||||
// update sidebar?
|
||||
if (isShowing != isChecked) {
|
||||
if (isChecked) {
|
||||
showSidebar(window, self);
|
||||
}
|
||||
else {
|
||||
hideSidebar(window, self);
|
||||
}
|
||||
}
|
||||
|
||||
// update the button
|
||||
setStateFor(button, window, { checked: isChecked });
|
||||
}
|
||||
}
|
||||
|
||||
let bars = [];
|
||||
internals.tracker = WindowTracker({
|
||||
onTrack: function(window) {
|
||||
if (!isBrowser(window))
|
||||
return;
|
||||
|
||||
let sidebar = window.document.getElementById('sidebar');
|
||||
let sidebarBox = window.document.getElementById('sidebar-box');
|
||||
|
||||
let bar = create(window, {
|
||||
id: self.id,
|
||||
title: self.title,
|
||||
sidebarurl: self.url
|
||||
});
|
||||
bars.push(bar);
|
||||
windowNS(window).bar = bar;
|
||||
|
||||
bar.addEventListener('command', function() {
|
||||
if (isSidebarShowing(window, self)) {
|
||||
hideSidebar(window, self);
|
||||
return;
|
||||
}
|
||||
|
||||
showSidebar(window, self);
|
||||
}, false);
|
||||
|
||||
function onSidebarLoad() {
|
||||
// check if the sidebar is ready
|
||||
let isReady = sidebar.docShell && sidebar.contentDocument;
|
||||
if (!isReady)
|
||||
return;
|
||||
|
||||
// check if it is a web panel
|
||||
let panelBrowser = sidebar.contentDocument.getElementById(WEB_PANEL_BROWSER_ID);
|
||||
if (!panelBrowser) {
|
||||
bar.removeAttribute('checked');
|
||||
return;
|
||||
}
|
||||
|
||||
let sbTitle = window.document.getElementById('sidebar-title');
|
||||
function onWebPanelSidebarCreated() {
|
||||
if (panelBrowser.contentWindow.location != model.url ||
|
||||
sbTitle.value != model.title) {
|
||||
return;
|
||||
}
|
||||
|
||||
let worker = windowNS(window).worker = Worker({
|
||||
window: panelBrowser.contentWindow
|
||||
});
|
||||
|
||||
function onWebPanelSidebarUnload() {
|
||||
windowNS(window).onWebPanelSidebarUnload = null;
|
||||
|
||||
// uncheck the associated menuitem
|
||||
bar.setAttribute('checked', 'false');
|
||||
setStateFor(button, window, { checked: false });
|
||||
|
||||
emit(self, 'hide', {});
|
||||
emit(self, 'detach', worker);
|
||||
}
|
||||
windowNS(window).onWebPanelSidebarUnload = onWebPanelSidebarUnload;
|
||||
panelBrowser.contentWindow.addEventListener('unload', onWebPanelSidebarUnload, true);
|
||||
|
||||
// check the associated menuitem
|
||||
bar.setAttribute('checked', 'true');
|
||||
|
||||
function onWebPanelSidebarLoad() {
|
||||
panelBrowser.contentWindow.removeEventListener('load', onWebPanelSidebarLoad, true);
|
||||
windowNS(window).onWebPanelSidebarLoad = null;
|
||||
|
||||
update();
|
||||
|
||||
// TODO: decide if returning worker is acceptable..
|
||||
//emit(self, 'show', { worker: worker });
|
||||
emit(self, 'show', {});
|
||||
}
|
||||
windowNS(window).onWebPanelSidebarLoad = onWebPanelSidebarLoad;
|
||||
panelBrowser.contentWindow.addEventListener('load', onWebPanelSidebarLoad, true);
|
||||
|
||||
emit(self, 'attach', worker);
|
||||
}
|
||||
windowNS(window).onWebPanelSidebarCreated = onWebPanelSidebarCreated;
|
||||
panelBrowser.addEventListener('DOMWindowCreated', onWebPanelSidebarCreated, true);
|
||||
}
|
||||
windowNS(window).onSidebarLoad = onSidebarLoad;
|
||||
sidebar.addEventListener('load', onSidebarLoad, true); // removed properly
|
||||
},
|
||||
onUntrack: function(window) {
|
||||
if (!isBrowser(window))
|
||||
return;
|
||||
|
||||
// hide the sidebar if it is showing
|
||||
hideSidebar(window, self);
|
||||
|
||||
// kill the menu item
|
||||
let { bar } = windowNS(window);
|
||||
if (bar) {
|
||||
removeFromArray(viewsFor(self), bar);
|
||||
dispose(bar);
|
||||
}
|
||||
|
||||
// kill listeners
|
||||
let sidebar = window.document.getElementById('sidebar');
|
||||
|
||||
if (windowNS(window).onSidebarLoad) {
|
||||
sidebar && sidebar.removeEventListener('load', windowNS(window).onSidebarLoad, true)
|
||||
windowNS(window).onSidebarLoad = null;
|
||||
}
|
||||
|
||||
let panelBrowser = sidebar && sidebar.contentDocument.getElementById(WEB_PANEL_BROWSER_ID);
|
||||
if (windowNS(window).onWebPanelSidebarCreated) {
|
||||
panelBrowser && panelBrowser.removeEventListener('DOMWindowCreated', windowNS(window).onWebPanelSidebarCreated, true);
|
||||
windowNS(window).onWebPanelSidebarCreated = null;
|
||||
}
|
||||
|
||||
if (windowNS(window).onWebPanelSidebarLoad) {
|
||||
panelBrowser && panelBrowser.contentWindow.removeEventListener('load', windowNS(window).onWebPanelSidebarLoad, true);
|
||||
windowNS(window).onWebPanelSidebarLoad = null;
|
||||
}
|
||||
|
||||
if (windowNS(window).onWebPanelSidebarUnload) {
|
||||
panelBrowser && panelBrowser.contentWindow.removeEventListener('unload', windowNS(window).onWebPanelSidebarUnload, true);
|
||||
windowNS(window).onWebPanelSidebarUnload = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
views.set(this, bars);
|
||||
|
||||
add(sidebars, this);
|
||||
},
|
||||
get id() (modelFor(this) || {}).id,
|
||||
get title() (modelFor(this) || {}).title,
|
||||
set title(v) {
|
||||
// destroyed?
|
||||
if (!modelFor(this))
|
||||
return;
|
||||
// validation
|
||||
if (typeof v != 'string')
|
||||
throw Error('title must be a string');
|
||||
validateTitleAndURLCombo(this, v, this.url);
|
||||
// do update
|
||||
updateTitle(this, v);
|
||||
return modelFor(this).title = v;
|
||||
},
|
||||
get url() (modelFor(this) || {}).url,
|
||||
set url(v) {
|
||||
// destroyed?
|
||||
if (!modelFor(this))
|
||||
return;
|
||||
// validation
|
||||
if (!isLocalURL(v))
|
||||
throw Error('the url must be a valid local url');
|
||||
validateTitleAndURLCombo(this, this.title, v);
|
||||
// do update
|
||||
updateURL(this, v);
|
||||
modelFor(this).url = v;
|
||||
},
|
||||
get icon() (buttons.get(this) || {}).icon,
|
||||
set icon(v) {
|
||||
let button = buttons.get(this);
|
||||
if (!button)
|
||||
return;
|
||||
button.icon = v;
|
||||
},
|
||||
show: function() {
|
||||
return showSidebar(null, this);
|
||||
},
|
||||
hide: function() {
|
||||
return hideSidebar(null, this);
|
||||
},
|
||||
dispose: function() {
|
||||
const internals = sidebarNS(this);
|
||||
|
||||
off(this);
|
||||
|
||||
remove(sidebars, this);
|
||||
|
||||
// stop tracking windows
|
||||
internals.tracker.unload();
|
||||
internals.tracker = null;
|
||||
|
||||
internals.windowNS = null;
|
||||
|
||||
views.delete(this);
|
||||
models.delete(this);
|
||||
|
||||
// kill the button
|
||||
let button = buttons.get(this);
|
||||
if (button)
|
||||
button.destroy();
|
||||
}
|
||||
});
|
||||
exports.Sidebar = Sidebar;
|
||||
|
||||
function validateTitleAndURLCombo(sidebar, title, url) {
|
||||
if (sidebar.title == title && sidebar.url == url) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let window of windows(null, { includePrivate: true })) {
|
||||
let sidebar = window.document.querySelector('menuitem[sidebarurl="' + url + '"][label="' + title + '"]');
|
||||
if (sidebar) {
|
||||
throw Error('The provided title and url combination is invalid (already used).');
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
isShowing.define(Sidebar, isSidebarShowing.bind(null, null));
|
||||
show.define(Sidebar, showSidebar.bind(null, null));
|
||||
hide.define(Sidebar, hideSidebar.bind(null, null));
|
||||
|
||||
function toggleSidebar(window, sidebar) {
|
||||
// TODO: make sure this is not private
|
||||
window = window || getMostRecentBrowserWindow();
|
||||
if (isSidebarShowing(window, sidebar)) {
|
||||
return hideSidebar(window, sidebar);
|
||||
}
|
||||
return showSidebar(window, sidebar);
|
||||
}
|
||||
toggle.define(Sidebar, toggleSidebar.bind(null, null));
|
10
addon-sdk/source/lib/sdk/ui/sidebar/actions.js
Normal file
10
addon-sdk/source/lib/sdk/ui/sidebar/actions.js
Normal file
@ -0,0 +1,10 @@
|
||||
/* 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 method = require('method/core');
|
||||
|
||||
exports.show = method('show');
|
||||
exports.hide = method('hide');
|
||||
exports.toggle = method('toggle');
|
38
addon-sdk/source/lib/sdk/ui/sidebar/contract.js
Normal file
38
addon-sdk/source/lib/sdk/ui/sidebar/contract.js
Normal file
@ -0,0 +1,38 @@
|
||||
/* 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 { contract } = require('../../util/contract');
|
||||
const { isValidURI, URL, isLocalURL } = require('../../url');
|
||||
const { isNil, isObject, isString } = require('../../lang/type');
|
||||
|
||||
function isIconSet(icons) {
|
||||
return Object.keys(icons).
|
||||
every(size => String(size >>> 0) === size && isLocalURL(icons[size]))
|
||||
}
|
||||
|
||||
exports.contract = contract({
|
||||
id: {
|
||||
is: [ 'string' ],
|
||||
ok: v => /^[a-z0-9-_]+$/i.test(v),
|
||||
msg: 'The option "id" must be a valid alphanumeric id (hyphens and ' +
|
||||
'underscores are allowed).'
|
||||
},
|
||||
title: {
|
||||
is: [ 'string' ],
|
||||
ok: v => v.length
|
||||
},
|
||||
icon: {
|
||||
is: ['string', 'object'],
|
||||
ok: v => (isString(v) && isLocalURL(v)) || (isObject(v) && isIconSet(v)),
|
||||
msg: 'The option "icon" must be a local URL or an object with ' +
|
||||
'numeric keys / local URL values pair.'
|
||||
},
|
||||
url: {
|
||||
is: [ 'string' ],
|
||||
ok: v => isLocalURL(v),
|
||||
map: function(v) v.toString(),
|
||||
msg: 'The option "url" must be a valid URI.'
|
||||
}
|
||||
});
|
11
addon-sdk/source/lib/sdk/ui/sidebar/namespace.js
Normal file
11
addon-sdk/source/lib/sdk/ui/sidebar/namespace.js
Normal file
@ -0,0 +1,11 @@
|
||||
/* 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 models = exports.models = new WeakMap();
|
||||
const views = exports.views = new WeakMap();
|
||||
exports.buttons = new WeakMap();
|
||||
|
||||
exports.viewsFor = function viewsFor(sidebar) views.get(sidebar);
|
||||
exports.modelFor = function modelFor(sidebar) models.get(sidebar);
|
8
addon-sdk/source/lib/sdk/ui/sidebar/utils.js
Normal file
8
addon-sdk/source/lib/sdk/ui/sidebar/utils.js
Normal file
@ -0,0 +1,8 @@
|
||||
/* 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 method = require('method/core');
|
||||
|
||||
exports.isShowing = method('isShowing');
|
193
addon-sdk/source/lib/sdk/ui/sidebar/view.js
Normal file
193
addon-sdk/source/lib/sdk/ui/sidebar/view.js
Normal file
@ -0,0 +1,193 @@
|
||||
/* 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';
|
||||
|
||||
module.metadata = {
|
||||
'stability': 'unstable',
|
||||
'engines': {
|
||||
'Firefox': '> 24'
|
||||
}
|
||||
};
|
||||
|
||||
const { models, buttons, views, viewsFor, modelFor } = require('./namespace');
|
||||
const { isBrowser, getMostRecentBrowserWindow, windows, isWindowPrivate } = require('../../window/utils');
|
||||
const { setStateFor } = require('../state');
|
||||
const { defer } = require('../../core/promise');
|
||||
const { isPrivateBrowsingSupported } = require('../../self');
|
||||
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
const WEB_PANEL_BROWSER_ID = 'web-panels-browser';
|
||||
|
||||
function create(window, details) {
|
||||
let { document } = window;
|
||||
|
||||
let menuitem = document.createElementNS(XUL_NS, 'menuitem');
|
||||
menuitem.setAttribute('id', makeID(details.id));
|
||||
menuitem.setAttribute('label', details.title);
|
||||
menuitem.setAttribute('sidebarurl', details.sidebarurl);
|
||||
menuitem.setAttribute('checked', 'false');
|
||||
menuitem.setAttribute('type', 'checkbox');
|
||||
menuitem.setAttribute('group', 'sidebar');
|
||||
menuitem.setAttribute('autoCheck', 'false');
|
||||
|
||||
document.getElementById('viewSidebarMenu').appendChild(menuitem);
|
||||
|
||||
return menuitem;
|
||||
}
|
||||
exports.create = create;
|
||||
|
||||
function dispose(menuitem) {
|
||||
menuitem.parentNode.removeChild(menuitem);
|
||||
}
|
||||
exports.dispose = dispose;
|
||||
|
||||
function updateTitle(sidebar, title) {
|
||||
let button = buttons.get(sidebar);
|
||||
|
||||
for (let window of windows(null, { includePrivate: true })) {
|
||||
let { document } = window;
|
||||
|
||||
// update the button
|
||||
if (button) {
|
||||
setStateFor(button, window, { label: title });
|
||||
}
|
||||
|
||||
// update the menuitem
|
||||
let mi = document.getElementById(makeID(sidebar.id));
|
||||
if (mi) {
|
||||
mi.setAttribute('label', title)
|
||||
}
|
||||
|
||||
// update sidebar, if showing
|
||||
if (isSidebarShowing(window, sidebar)) {
|
||||
document.getElementById('sidebar-title').setAttribute('value', title);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.updateTitle = updateTitle;
|
||||
|
||||
function updateURL(sidebar, url) {
|
||||
for (let window of windows(null, { includePrivate: true })) {
|
||||
// update sidebar, if showing
|
||||
if (isSidebarShowing(window, sidebar)) {
|
||||
showSidebar(window, sidebar, url);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.updateURL = updateURL;
|
||||
|
||||
function isSidebarShowing(window, sidebar) {
|
||||
let win = window || getMostRecentBrowserWindow();
|
||||
|
||||
// make sure there is a window
|
||||
if (!win) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure there is a sidebar for the window
|
||||
let sb = win.document.getElementById('sidebar');
|
||||
let sidebarTitle = win.document.getElementById('sidebar-title');
|
||||
if (!(sb && sidebarTitle)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// checks if the sidebar box is hidden
|
||||
let sbb = win.document.getElementById('sidebar-box');
|
||||
if (!sbb || sbb.hidden) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sidebarTitle.value == modelFor(sidebar).title) {
|
||||
// checks if the sidebar is loading
|
||||
if (win.gWebPanelURI == modelFor(sidebar).url) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// checks if the sidebar loaded already
|
||||
let ele = sb.contentDocument && sb.contentDocument.getElementById(WEB_PANEL_BROWSER_ID);
|
||||
if (!ele) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ele.getAttribute('cachedurl') == modelFor(sidebar).url) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ele && ele.contentWindow && ele.contentWindow.location == modelFor(sidebar).url) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// default
|
||||
return false;
|
||||
}
|
||||
exports.isSidebarShowing = isSidebarShowing;
|
||||
|
||||
function showSidebar(window, sidebar, newURL) {
|
||||
window = window || getMostRecentBrowserWindow();
|
||||
|
||||
let { promise, resolve, reject } = defer();
|
||||
let model = modelFor(sidebar);
|
||||
|
||||
if (!isPrivateBrowsingSupported && isWindowPrivate(window)) {
|
||||
reject(Error('You cannot show a sidebar on private windows'));
|
||||
}
|
||||
else {
|
||||
sidebar.once('show', resolve);
|
||||
|
||||
let menuitem = window.document.getElementById(makeID(model.id));
|
||||
menuitem.setAttribute('checked', true);
|
||||
|
||||
window.openWebPanel(model.title, newURL || model.url);
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
exports.showSidebar = showSidebar;
|
||||
|
||||
|
||||
function hideSidebar(window, sidebar) {
|
||||
window = window || getMostRecentBrowserWindow();
|
||||
|
||||
let { promise, resolve, reject } = defer();
|
||||
|
||||
if (!isSidebarShowing(window, sidebar)) {
|
||||
reject(Error('The sidebar is already hidden'));
|
||||
}
|
||||
else {
|
||||
sidebar.once('hide', resolve);
|
||||
|
||||
// Below was taken from http://mxr.mozilla.org/mozilla-central/source/browser/base/content/browser.js#4775
|
||||
// the code for window.todggleSideBar()..
|
||||
let { document } = window;
|
||||
let sidebarEle = document.getElementById('sidebar');
|
||||
let sidebarTitle = document.getElementById('sidebar-title');
|
||||
let sidebarBox = document.getElementById('sidebar-box');
|
||||
let sidebarSplitter = document.getElementById('sidebar-splitter');
|
||||
let commandID = sidebarBox.getAttribute('sidebarcommand');
|
||||
let sidebarBroadcaster = document.getElementById(commandID);
|
||||
|
||||
sidebarBox.hidden = true;
|
||||
sidebarSplitter.hidden = true;
|
||||
|
||||
sidebarEle.setAttribute('src', 'about:blank');
|
||||
//sidebarEle.docShell.createAboutBlankContentViewer(null);
|
||||
|
||||
sidebarBroadcaster.removeAttribute('checked');
|
||||
sidebarBox.setAttribute('sidebarcommand', '');
|
||||
sidebarTitle.value = '';
|
||||
sidebarBox.hidden = true;
|
||||
sidebarSplitter.hidden = true;
|
||||
|
||||
// TODO: perhaps this isn't necessary if the window is not most recent?
|
||||
window.gBrowser.selectedBrowser.focus();
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
exports.hideSidebar = hideSidebar;
|
||||
|
||||
function makeID(id) {
|
||||
return 'jetpack-sidebar-' + id;
|
||||
}
|
240
addon-sdk/source/lib/sdk/ui/state.js
Normal file
240
addon-sdk/source/lib/sdk/ui/state.js
Normal file
@ -0,0 +1,240 @@
|
||||
/* 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 Button module currently supports only Firefox.
|
||||
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=jetpack-panel-apps
|
||||
module.metadata = {
|
||||
'stability': 'experimental',
|
||||
'engines': {
|
||||
'Firefox': '*'
|
||||
}
|
||||
};
|
||||
|
||||
const { Ci } = require('chrome');
|
||||
|
||||
const events = require('../event/utils');
|
||||
const { events: browserEvents } = require('../browser/events');
|
||||
const { events: tabEvents } = require('../tab/events');
|
||||
|
||||
const { windows, isInteractive } = require('../window/utils');
|
||||
const { BrowserWindow, browserWindows } = require('../windows');
|
||||
const { windowNS } = require('../window/namespace');
|
||||
const { Tab } = require('../tabs/tab');
|
||||
const { getActiveTab, getOwnerWindow, getTabs, getTabId } = require('../tabs/utils');
|
||||
|
||||
const { ignoreWindow } = require('../private-browsing/utils');
|
||||
|
||||
const { freeze } = Object;
|
||||
const { merge } = require('../util/object');
|
||||
const { on, off, emit } = require('../event/core');
|
||||
|
||||
const { add, remove, has, clear, iterator } = require("../lang/weak-set");
|
||||
const { isNil, instanceOf } = require('../lang/type');
|
||||
|
||||
const components = new WeakMap();
|
||||
|
||||
const ERR_UNREGISTERED = 'The state cannot be set or get. ' +
|
||||
'The object may be not be registered, or may already have been unloaded.';
|
||||
|
||||
/**
|
||||
* temporary
|
||||
*/
|
||||
function getChromeWindow(sdkWindow) windowNS(sdkWindow).window;
|
||||
|
||||
/**
|
||||
* temporary
|
||||
*/
|
||||
function getChromeTab(sdkTab) {
|
||||
for (let tab of getTabs()) {
|
||||
if (sdkTab.id === getTabId(tab))
|
||||
return tab;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const isWindow = thing => thing instanceof Ci.nsIDOMWindow;
|
||||
const isTab = thing => thing.tagName && thing.tagName.toLowerCase() === "tab";
|
||||
const isActiveTab = thing => isTab(thing) && thing === getActiveTab(getOwnerWindow(thing));
|
||||
const isWindowEnumerable = window => !ignoreWindow(window);
|
||||
|
||||
function getStateFor(component, target) {
|
||||
if (!isRegistered(component))
|
||||
throw new Error(ERR_UNREGISTERED);
|
||||
|
||||
if (!components.has(component))
|
||||
return null;
|
||||
|
||||
let states = components.get(component);
|
||||
|
||||
let componentState = states.get(component);
|
||||
let windowState = null;
|
||||
let tabState = null;
|
||||
|
||||
if (target) {
|
||||
// has a target
|
||||
if (isTab(target)) {
|
||||
windowState = states.get(getOwnerWindow(target), null);
|
||||
|
||||
if (states.has(target)) {
|
||||
// we have a tab state
|
||||
tabState = states.get(target);
|
||||
}
|
||||
}
|
||||
else if (isWindow(target) && states.has(target)) {
|
||||
// we have a window state
|
||||
windowState = states.get(target);
|
||||
}
|
||||
}
|
||||
|
||||
return freeze(merge({}, componentState, windowState, tabState));
|
||||
}
|
||||
exports.getStateFor = getStateFor;
|
||||
|
||||
function setStateFor(component, target, state) {
|
||||
if (!isRegistered(component))
|
||||
throw new Error(ERR_UNREGISTERED);
|
||||
|
||||
let targetWindows = [];
|
||||
let isComponentState = target === component;
|
||||
|
||||
if (isWindow(target)) {
|
||||
targetWindows = [target];
|
||||
}
|
||||
else if (isActiveTab(target)) {
|
||||
targetWindows = [getOwnerWindow(target)];
|
||||
}
|
||||
else if (isComponentState) {
|
||||
targetWindows = windows('navigator:browser', { includePrivate: true}).filter(isInteractive);
|
||||
}
|
||||
else if (!isTab(target))
|
||||
throw new Error('target not allowed.');
|
||||
|
||||
// initialize the state's map
|
||||
if (!components.has(component))
|
||||
components.set(component, new WeakMap());
|
||||
|
||||
let states = components.get(component);
|
||||
|
||||
if (state === null && !isComponentState) // component state can't be deleted
|
||||
states.delete(target);
|
||||
else {
|
||||
let base = isComponentState ? states.get(target) : null;
|
||||
states.set(target, freeze(merge({}, base, state)));
|
||||
}
|
||||
|
||||
for (let window of targetWindows.filter(isWindowEnumerable)) {
|
||||
let tabState = getStateFor(component, getActiveTab(window));
|
||||
|
||||
emit(component.constructor, 'render', component, window, tabState);
|
||||
}
|
||||
}
|
||||
// Exporting `setStateFor` temporary for the sidebar / toolbar, until we do not
|
||||
// have an 'official' way to get an SDK Window from Chrome Window.
|
||||
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=695143
|
||||
//
|
||||
// Misuse of `setStateFor` could leads to side effects with the proper `state`
|
||||
// implementation.
|
||||
exports.setStateFor = setStateFor;
|
||||
|
||||
function render(component, targetWindows) {
|
||||
if (!targetWindows)
|
||||
targetWindows = windows('navigator:browser', { includePrivate: true}).filter(isInteractive);
|
||||
else
|
||||
targetWindows = [].concat(targetWindows);
|
||||
|
||||
for (let window of targetWindows.filter(isWindowEnumerable)) {
|
||||
let tabState = getStateFor(component, getActiveTab(window));
|
||||
|
||||
emit(component.constructor, 'render', component, window, tabState);
|
||||
}
|
||||
}
|
||||
exports.render = render;
|
||||
|
||||
function properties(contract) {
|
||||
let { rules } = contract;
|
||||
let descriptor = Object.keys(rules).reduce(function(descriptor, name) {
|
||||
descriptor[name] = {
|
||||
get: function() { return getStateFor(this)[name] },
|
||||
set: function(value) {
|
||||
let changed = {};
|
||||
changed[name] = value;
|
||||
|
||||
setStateFor(this, this, contract(changed));
|
||||
}
|
||||
}
|
||||
return descriptor;
|
||||
}, {});
|
||||
|
||||
return Object.create(Object.prototype, descriptor);
|
||||
}
|
||||
exports.properties = properties;
|
||||
|
||||
function state(contract) {
|
||||
return {
|
||||
state: function state(target, state) {
|
||||
// jquery style
|
||||
let isGet = arguments.length < 2;
|
||||
|
||||
if (instanceOf(target, BrowserWindow))
|
||||
target = getChromeWindow(target);
|
||||
else if (instanceOf(target, Tab))
|
||||
target = getChromeTab(target);
|
||||
else if (target !== this && !isNil(target))
|
||||
throw new Error('target not allowed.');
|
||||
|
||||
if (isGet)
|
||||
return getStateFor(this, target);
|
||||
|
||||
// contract?
|
||||
setStateFor(this, target, contract(state));
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.state = state;
|
||||
|
||||
function register(component, state) {
|
||||
add(components, component);
|
||||
setStateFor(component, component, state);
|
||||
}
|
||||
exports.register = register;
|
||||
|
||||
function unregister(component) remove(components, component);
|
||||
exports.unregister = unregister;
|
||||
|
||||
function isRegistered(component) has(components, component);
|
||||
exports.isRegistered = isRegistered;
|
||||
|
||||
let tabSelect = events.filter(tabEvents, function(e) e.type === 'TabSelect');
|
||||
let tabClose = events.filter(tabEvents, function(e) e.type === 'TabClose');
|
||||
let windowOpen = events.filter(browserEvents, function(e) e.type === 'load');
|
||||
let windowClose = events.filter(browserEvents, function(e) e.type === 'close');
|
||||
|
||||
let close = events.merge([tabClose, windowClose]);
|
||||
|
||||
on(windowOpen, 'data', function({target: window}) {
|
||||
if (ignoreWindow(window)) return;
|
||||
|
||||
let tab = getActiveTab(window);
|
||||
|
||||
for (let component of iterator(components)) {
|
||||
emit(component.constructor, 'render', component, window, getStateFor(component, tab));
|
||||
}
|
||||
});
|
||||
|
||||
on(tabSelect, 'data', function({target: tab}) {
|
||||
let window = getOwnerWindow(tab);
|
||||
|
||||
if (ignoreWindow(window)) return;
|
||||
|
||||
for (let component of iterator(components)) {
|
||||
emit(component.constructor, 'render', component, window, getStateFor(component, tab));
|
||||
}
|
||||
});
|
||||
|
||||
on(close, 'data', function({target}) {
|
||||
for (let component of iterator(components)) {
|
||||
components.get(component).delete(target);
|
||||
}
|
||||
});
|
@ -18,9 +18,9 @@ const { Cc, Ci, Cr } = require('chrome'),
|
||||
{ WindowTrackerTrait } = windowUtils,
|
||||
{ ns } = require('../core/namespace'),
|
||||
{ observer: windowObserver } = require('./observer'),
|
||||
{ getOwnerWindow } = require('../private-browsing/window/utils'),
|
||||
viewNS = require('../core/namespace').ns(),
|
||||
{ isPrivateBrowsingSupported } = require('../self');
|
||||
{ getOwnerWindow } = require('../private-browsing/window/utils');
|
||||
const { windowNS } = require('../window/namespace');
|
||||
const { isPrivateBrowsingSupported } = require('../self');
|
||||
const { ignoreWindow } = require('sdk/private-browsing/utils');
|
||||
|
||||
/**
|
||||
@ -74,7 +74,7 @@ const BrowserWindowTrait = Trait.compose(
|
||||
|
||||
this._load();
|
||||
|
||||
viewNS(this._public).window = this._window;
|
||||
windowNS(this._public).window = this._window;
|
||||
getOwnerWindow.implement(this._public, getChromeWindow);
|
||||
|
||||
return this;
|
||||
@ -256,7 +256,7 @@ const browserWindows = Trait.resolve({ toString: null }).compose(
|
||||
)();
|
||||
|
||||
function getChromeWindow(window) {
|
||||
return viewNS(window).window;
|
||||
return windowNS(window).window;
|
||||
}
|
||||
|
||||
exports.browserWindows = browserWindows;
|
||||
|
@ -234,6 +234,10 @@ parser_groups = (
|
||||
default=False,
|
||||
cmds=['test', 'testpkgs', 'testaddons',
|
||||
'testall'])),
|
||||
(("", "--output-file",), dict(dest="output_file",
|
||||
help="Where to put the finished .xpi",
|
||||
default=None,
|
||||
cmds=['xpi'])),
|
||||
]
|
||||
),
|
||||
|
||||
@ -892,7 +896,11 @@ def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None,
|
||||
key,value = kv.split("=", 1)
|
||||
extra_harness_options[key] = value
|
||||
# Generate xpi filepath
|
||||
xpi_path = XPI_FILENAME % target_cfg.name
|
||||
if options.output_file:
|
||||
xpi_path = options.output_file
|
||||
else:
|
||||
xpi_path = XPI_FILENAME % target_cfg.name
|
||||
|
||||
print >>stdout, "Exporting extension to %s." % xpi_path
|
||||
build_xpi(template_root_dir=app_extension_dir,
|
||||
manifest=manifest_rdf,
|
||||
|
@ -2,7 +2,7 @@
|
||||
# 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/.
|
||||
|
||||
def parse_options_defaults(options, jetpack_id):
|
||||
def parse_options_defaults(options, preferencesBranch):
|
||||
# this returns a unicode string
|
||||
pref_list = []
|
||||
|
||||
@ -21,6 +21,6 @@ def parse_options_defaults(options, jetpack_id):
|
||||
else:
|
||||
value = str(pref["value"])
|
||||
|
||||
pref_list.append("pref(\"extensions." + jetpack_id + "." + pref["name"] + "\", " + value + ");")
|
||||
pref_list.append("pref(\"extensions." + preferencesBranch + "." + pref["name"] + "\", " + value + ");")
|
||||
|
||||
return "\n".join(pref_list) + "\n"
|
||||
|
@ -45,7 +45,7 @@ def validate_prefs(options):
|
||||
|
||||
# TODO: Check that pref["type"] matches default value type
|
||||
|
||||
def parse_options(options, jetpack_id):
|
||||
def parse_options(options, jetpack_id, preferencesBranch):
|
||||
doc = Document()
|
||||
root = doc.createElement("vbox")
|
||||
root.setAttribute("xmlns", "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul")
|
||||
@ -58,7 +58,7 @@ def parse_options(options, jetpack_id):
|
||||
setting = doc.createElement("setting")
|
||||
setting.setAttribute("pref-name", pref["name"])
|
||||
setting.setAttribute("data-jetpack-id", jetpack_id)
|
||||
setting.setAttribute("pref", "extensions." + jetpack_id + "." + pref["name"])
|
||||
setting.setAttribute("pref", "extensions." + preferencesBranch + "." + pref["name"])
|
||||
setting.setAttribute("type", pref["type"])
|
||||
setting.setAttribute("title", pref["title"])
|
||||
|
||||
|
@ -396,6 +396,21 @@ def generate_build_for_target(pkg_cfg, target, deps,
|
||||
if ('preferences' in target_cfg):
|
||||
build['preferences'] = target_cfg.preferences
|
||||
|
||||
if 'id' in target_cfg:
|
||||
# NOTE: logic duplicated from buildJID()
|
||||
jid = target_cfg['id']
|
||||
if not ('@' in jid or jid.startswith('{')):
|
||||
jid += '@jetpack'
|
||||
build['preferencesBranch'] = jid
|
||||
|
||||
if 'preferences-branch' in target_cfg:
|
||||
# check it's a non-empty, valid branch name
|
||||
preferencesBranch = target_cfg['preferences-branch']
|
||||
if re.match('^[\w{@}-]+$', preferencesBranch):
|
||||
build['preferencesBranch'] = preferencesBranch
|
||||
elif not is_running_tests:
|
||||
print >>sys.stderr, "IGNORING preferences-branch (not a valid branch name)"
|
||||
|
||||
return build
|
||||
|
||||
def _get_files_in_dir(path):
|
||||
|
@ -0,0 +1,4 @@
|
||||
/* 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/. */
|
||||
|
@ -0,0 +1,14 @@
|
||||
{
|
||||
"id": "{34a1eae1-c20a-464f-9b0e-000000000000}",
|
||||
"fullName": "curly ID test",
|
||||
"author": "Tomislav Jovanovic",
|
||||
|
||||
"preferences": [{
|
||||
"name": "test13",
|
||||
"type": "integer",
|
||||
"title": "test13",
|
||||
"value": 26
|
||||
}],
|
||||
|
||||
"preferences-branch": "invalid^branch*name"
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
/* 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/. */
|
||||
|
@ -0,0 +1,14 @@
|
||||
{
|
||||
"id": "test-preferences-branch",
|
||||
"fullName": "preferences-branch test",
|
||||
"author": "Tomislav Jovanovic",
|
||||
|
||||
"preferences": [{
|
||||
"name": "test42",
|
||||
"type": "bool",
|
||||
"title": "test42",
|
||||
"value": true
|
||||
}],
|
||||
|
||||
"preferences-branch": "human-readable"
|
||||
}
|
@ -22,7 +22,8 @@ def get_configs(pkg_name, dirname='static-files'):
|
||||
build = packaging.generate_build_for_target(
|
||||
pkg_cfg=pkg_cfg,
|
||||
target=pkg_name,
|
||||
deps=deps
|
||||
deps=deps,
|
||||
is_running_tests=True,
|
||||
)
|
||||
return Bunch(target_cfg=target_cfg, pkg_cfg=pkg_cfg, build=build)
|
||||
|
||||
|
@ -40,10 +40,11 @@ class PrefsTests(unittest.TestCase):
|
||||
|
||||
def testPackageWithSimplePrefs(self):
|
||||
self.makexpi('simple-prefs')
|
||||
packageName = 'jid1-fZHqN9JfrDBa8A@jetpack'
|
||||
self.failUnless('options.xul' in self.xpi.namelist())
|
||||
optsxul = self.xpi.read('options.xul').decode("utf-8")
|
||||
self.failUnlessEqual(self.xpi_harness_options["jetpackID"],
|
||||
"jid1-fZHqN9JfrDBa8A@jetpack")
|
||||
self.failUnlessEqual(self.xpi_harness_options["jetpackID"], packageName)
|
||||
self.failUnlessEqual(self.xpi_harness_options["preferencesBranch"], packageName)
|
||||
|
||||
root = ElementTree.XML(optsxul.encode('utf-8'))
|
||||
|
||||
@ -53,7 +54,6 @@ class PrefsTests(unittest.TestCase):
|
||||
settings = root.findall(xulNamespacePrefix + 'setting')
|
||||
|
||||
def assertPref(setting, name, prefType, title):
|
||||
packageName = 'jid1-fZHqN9JfrDBa8A@jetpack'
|
||||
self.failUnlessEqual(setting.get('data-jetpack-id'), packageName)
|
||||
self.failUnlessEqual(setting.get('pref'),
|
||||
'extensions.' + packageName + '.' + name)
|
||||
@ -88,6 +88,25 @@ class PrefsTests(unittest.TestCase):
|
||||
]
|
||||
self.failUnlessEqual(prefsjs, "\n".join(exp)+"\n")
|
||||
|
||||
def testPackageWithPreferencesBranch(self):
|
||||
self.makexpi('preferences-branch')
|
||||
self.failUnless('options.xul' in self.xpi.namelist())
|
||||
optsxul = self.xpi.read('options.xul').decode("utf-8")
|
||||
self.failUnlessEqual(self.xpi_harness_options["preferencesBranch"],
|
||||
"human-readable")
|
||||
|
||||
root = ElementTree.XML(optsxul.encode('utf-8'))
|
||||
xulNamespacePrefix = \
|
||||
"{http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul}"
|
||||
|
||||
setting = root.find(xulNamespacePrefix + 'setting')
|
||||
self.failUnlessEqual(setting.get('pref'),
|
||||
'extensions.human-readable.test42')
|
||||
|
||||
prefsjs = self.xpi.read('defaults/preferences/prefs.js').decode("utf-8")
|
||||
self.failUnlessEqual(prefsjs,
|
||||
'pref("extensions.human-readable.test42", true);\n')
|
||||
|
||||
def testPackageWithNoPrefs(self):
|
||||
self.makexpi('no-prefs')
|
||||
self.failIf('options.xul' in self.xpi.namelist())
|
||||
@ -96,6 +115,33 @@ class PrefsTests(unittest.TestCase):
|
||||
prefsjs = self.xpi.read('defaults/preferences/prefs.js').decode("utf-8")
|
||||
self.failUnlessEqual(prefsjs, "")
|
||||
|
||||
def testPackageWithInvalidPreferencesBranch(self):
|
||||
self.makexpi('curly-id')
|
||||
self.failIfEqual(self.xpi_harness_options["preferencesBranch"],
|
||||
"invalid^branch*name")
|
||||
self.failUnlessEqual(self.xpi_harness_options["preferencesBranch"],
|
||||
"{34a1eae1-c20a-464f-9b0e-000000000000}")
|
||||
|
||||
def testPackageWithCurlyID(self):
|
||||
self.makexpi('curly-id')
|
||||
self.failUnlessEqual(self.xpi_harness_options["jetpackID"],
|
||||
"{34a1eae1-c20a-464f-9b0e-000000000000}")
|
||||
|
||||
self.failUnless('options.xul' in self.xpi.namelist())
|
||||
optsxul = self.xpi.read('options.xul').decode("utf-8")
|
||||
|
||||
root = ElementTree.XML(optsxul.encode('utf-8'))
|
||||
xulNamespacePrefix = \
|
||||
"{http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul}"
|
||||
|
||||
setting = root.find(xulNamespacePrefix + 'setting')
|
||||
self.failUnlessEqual(setting.get('pref'),
|
||||
'extensions.{34a1eae1-c20a-464f-9b0e-000000000000}.test13')
|
||||
|
||||
prefsjs = self.xpi.read('defaults/preferences/prefs.js').decode("utf-8")
|
||||
self.failUnlessEqual(prefsjs,
|
||||
'pref("extensions.{34a1eae1-c20a-464f-9b0e-000000000000}.test13", 26);\n')
|
||||
|
||||
|
||||
class Bug588119Tests(unittest.TestCase):
|
||||
def makexpi(self, pkg_name):
|
||||
|
@ -77,14 +77,15 @@ def build_xpi(template_root_dir, manifest, xpi_path,
|
||||
validate_prefs(harness_options["preferences"])
|
||||
|
||||
opts_xul = parse_options(harness_options["preferences"],
|
||||
harness_options["jetpackID"])
|
||||
harness_options["jetpackID"],
|
||||
harness_options["preferencesBranch"])
|
||||
open('.options.xul', 'wb').write(opts_xul.encode("utf-8"))
|
||||
zf.write('.options.xul', 'options.xul')
|
||||
os.remove('.options.xul')
|
||||
|
||||
from options_defaults import parse_options_defaults
|
||||
prefs_js = parse_options_defaults(harness_options["preferences"],
|
||||
harness_options["jetpackID"])
|
||||
harness_options["preferencesBranch"])
|
||||
open('.prefs.js', 'wb').write(prefs_js.encode("utf-8"))
|
||||
|
||||
else:
|
||||
|
45
addon-sdk/source/test/addons/curly-id/lib/main.js
Normal file
45
addon-sdk/source/test/addons/curly-id/lib/main.js
Normal file
@ -0,0 +1,45 @@
|
||||
/* 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 { id } = require('sdk/self');
|
||||
const simple = require('sdk/simple-prefs');
|
||||
const service = require('sdk/preferences/service');
|
||||
const { preferencesBranch } = require('@loader/options');
|
||||
const { AddonManager } = require('chrome').Cu.import('resource://gre/modules/AddonManager.jsm');
|
||||
|
||||
exports.testCurlyID = function(assert) {
|
||||
assert.equal(id, '{34a1eae1-c20a-464f-9b0e-000000000000}', 'curly ID is curly');
|
||||
|
||||
assert.equal(simple.prefs.test13, 26, 'test13 is 26');
|
||||
|
||||
simple.prefs.test14 = '15';
|
||||
assert.equal(service.get('extensions.{34a1eae1-c20a-464f-9b0e-000000000000}.test14'), '15', 'test14 is 15');
|
||||
|
||||
assert.equal(service.get('extensions.{34a1eae1-c20a-464f-9b0e-000000000000}.test14'), simple.prefs.test14, 'simple test14 also 15');
|
||||
|
||||
}
|
||||
|
||||
exports.testInvalidPreferencesBranch = function(assert) {
|
||||
assert.notEqual(preferencesBranch, 'invalid^branch*name', 'invalid preferences-branch value ignored');
|
||||
|
||||
assert.equal(preferencesBranch, '{34a1eae1-c20a-464f-9b0e-000000000000}', 'preferences-branch is {34a1eae1-c20a-464f-9b0e-000000000000}');
|
||||
|
||||
}
|
||||
|
||||
// from `/test/test-self.js`, adapted to `sdk/test/assert` API
|
||||
exports.testSelfID = function(assert, done) {
|
||||
|
||||
assert.equal(typeof(id), 'string', 'self.id is a string');
|
||||
assert.ok(id.length > 0, 'self.id not empty');
|
||||
|
||||
AddonManager.getAddonByID(id, function(addon) {
|
||||
assert.ok(addon, 'found addon with self.id');
|
||||
done();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
require('sdk/test/runner').runTestsFromModule(module);
|
14
addon-sdk/source/test/addons/curly-id/package.json
Normal file
14
addon-sdk/source/test/addons/curly-id/package.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"id": "{34a1eae1-c20a-464f-9b0e-000000000000}",
|
||||
"fullName": "curly ID test",
|
||||
"author": "Tomislav Jovanovic",
|
||||
|
||||
"preferences": [{
|
||||
"name": "test13",
|
||||
"type": "integer",
|
||||
"title": "test13",
|
||||
"value": 26
|
||||
}],
|
||||
|
||||
"preferences-branch": "invalid^branch*name"
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/* 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 { id } = require('sdk/self');
|
||||
const simple = require('sdk/simple-prefs');
|
||||
const service = require('sdk/preferences/service');
|
||||
const { preferencesBranch } = require('@loader/options');
|
||||
const { AddonManager } = require('chrome').Cu.import('resource://gre/modules/AddonManager.jsm', {});
|
||||
|
||||
const expected_id = 'predefined-id@test';
|
||||
|
||||
exports.testExpectedID = function(assert) {
|
||||
assert.equal(id, expected_id, 'ID is as expected');
|
||||
assert.equal(preferencesBranch, expected_id, 'preferences-branch is ' + expected_id);
|
||||
|
||||
assert.equal(simple.prefs.test, 5, 'test pref is 5');
|
||||
|
||||
simple.prefs.test2 = '25';
|
||||
assert.equal(service.get('extensions.'+expected_id+'.test2'), '25', 'test pref is 25');
|
||||
assert.equal(service.get('extensions.'+expected_id+'.test2'), simple.prefs.test2, 'test pref is 25');
|
||||
}
|
||||
|
||||
exports.testSelfID = function(assert, done) {
|
||||
assert.equal(typeof(id), 'string', 'self.id is a string');
|
||||
assert.ok(id.length > 0, 'self.id not empty');
|
||||
|
||||
AddonManager.getAddonByID(id, function(addon) {
|
||||
assert.equal(addon.id, id, 'found addon with self.id');
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
require('sdk/test/runner').runTestsFromModule(module);
|
@ -0,0 +1,11 @@
|
||||
{
|
||||
"id": "predefined-id@test",
|
||||
"fullName": "predefined ID test",
|
||||
"author": "Erik Vold",
|
||||
"preferences": [{
|
||||
"name": "test",
|
||||
"type": "integer",
|
||||
"title": "test",
|
||||
"value": 5
|
||||
}]
|
||||
}
|
36
addon-sdk/source/test/addons/preferences-branch/lib/main.js
Normal file
36
addon-sdk/source/test/addons/preferences-branch/lib/main.js
Normal file
@ -0,0 +1,36 @@
|
||||
/* 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 { id } = require('sdk/self');
|
||||
const simple = require('sdk/simple-prefs');
|
||||
const service = require('sdk/preferences/service');
|
||||
const { preferencesBranch } = require('@loader/options');
|
||||
const { AddonManager } = require('chrome').Cu.import('resource://gre/modules/AddonManager.jsm');
|
||||
|
||||
exports.testPreferencesBranch = function(assert) {
|
||||
assert.equal(preferencesBranch, 'human-readable', 'preferencesBranch is human-readable');
|
||||
|
||||
assert.equal(simple.prefs.test42, true, 'test42 is true');
|
||||
|
||||
simple.prefs.test43 = 'movie';
|
||||
assert.equal(service.get('extensions.human-readable.test43'), 'movie', 'test43 is a movie');
|
||||
|
||||
}
|
||||
|
||||
// from `/test/test-self.js`, adapted to `sdk/test/assert` API
|
||||
exports.testSelfID = function(assert, done) {
|
||||
|
||||
assert.equal(typeof(id), 'string', 'self.id is a string');
|
||||
assert.ok(id.length > 0, 'self.id not empty');
|
||||
|
||||
AddonManager.getAddonByID(id, function(addon) {
|
||||
assert.ok(addon, 'found addon with self.id');
|
||||
done();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
require('sdk/test/runner').runTestsFromModule(module);
|
14
addon-sdk/source/test/addons/preferences-branch/package.json
Normal file
14
addon-sdk/source/test/addons/preferences-branch/package.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"id": "test-preferences-branch",
|
||||
"fullName": "preferences-branch test",
|
||||
"author": "Tomislav Jovanovic",
|
||||
|
||||
"preferences": [{
|
||||
"name": "test42",
|
||||
"type": "bool",
|
||||
"title": "test42",
|
||||
"value": true
|
||||
}],
|
||||
|
||||
"preferences-branch": "human-readable"
|
||||
}
|
@ -11,6 +11,7 @@ merge(module.exports,
|
||||
require('./test-tabs'),
|
||||
require('./test-page-mod'),
|
||||
require('./test-private-browsing'),
|
||||
require('./test-sidebar'),
|
||||
isGlobalPBSupported ? require('./test-global-private-browsing') : {}
|
||||
);
|
||||
|
||||
|
@ -0,0 +1,86 @@
|
||||
/* 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');
|
||||
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
|
||||
const { fromIterator } = require('sdk/util/array');
|
||||
|
||||
const BLANK_IMG = exports.BLANK_IMG = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
|
||||
|
||||
const BUILTIN_SIDEBAR_MENUITEMS = exports.BUILTIN_SIDEBAR_MENUITEMS = [
|
||||
'menu_socialSidebar',
|
||||
'menu_historySidebar',
|
||||
'menu_bookmarksSidebar'
|
||||
];
|
||||
|
||||
function isSidebarShowing(window) {
|
||||
window = window || getMostRecentBrowserWindow();
|
||||
let sidebar = window.document.getElementById('sidebar-box');
|
||||
return !sidebar.hidden;
|
||||
}
|
||||
exports.isSidebarShowing = isSidebarShowing;
|
||||
|
||||
function getSidebarMenuitems(window) {
|
||||
window = window || getMostRecentBrowserWindow();
|
||||
return fromIterator(window.document.querySelectorAll('#viewSidebarMenu menuitem'));
|
||||
}
|
||||
exports.getSidebarMenuitems = getSidebarMenuitems;
|
||||
|
||||
function getExtraSidebarMenuitems() {
|
||||
let menuitems = getSidebarMenuitems();
|
||||
return menuitems.filter(function(mi) {
|
||||
return BUILTIN_SIDEBAR_MENUITEMS.indexOf(mi.getAttribute('id')) < 0;
|
||||
});
|
||||
}
|
||||
exports.getExtraSidebarMenuitems = getExtraSidebarMenuitems;
|
||||
|
||||
function makeID(id) {
|
||||
return 'jetpack-sidebar-' + id;
|
||||
}
|
||||
exports.makeID = makeID;
|
||||
|
||||
function simulateCommand(ele) {
|
||||
let window = ele.ownerDocument.defaultView;
|
||||
let { document } = window;
|
||||
var evt = document.createEvent('XULCommandEvent');
|
||||
evt.initCommandEvent('command', true, true, window,
|
||||
0, false, false, false, false, null);
|
||||
ele.dispatchEvent(evt);
|
||||
}
|
||||
exports.simulateCommand = simulateCommand;
|
||||
|
||||
function simulateClick(ele) {
|
||||
let window = ele.ownerDocument.defaultView;
|
||||
let { document } = window;
|
||||
let evt = document.createEvent('MouseEvents');
|
||||
evt.initMouseEvent('click', true, true, window,
|
||||
0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
||||
ele.dispatchEvent(evt);
|
||||
}
|
||||
exports.simulateClick = simulateClick;
|
||||
|
||||
function getWidget(buttonId, window = getMostRecentBrowserWindow()) {
|
||||
const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
|
||||
const { AREA_NAVBAR } = CustomizableUI;
|
||||
|
||||
let widgets = CustomizableUI.getWidgetsInArea(AREA_NAVBAR).
|
||||
filter(({id}) => id.startsWith('button--') && id.endsWith(buttonId));
|
||||
|
||||
if (widgets.length === 0)
|
||||
throw new Error('Widget with id `' + buttonId +'` not found.');
|
||||
|
||||
if (widgets.length > 1)
|
||||
throw new Error('Unexpected number of widgets: ' + widgets.length)
|
||||
|
||||
return widgets[0].forWindow(window);
|
||||
};
|
||||
exports.getWidget = getWidget;
|
||||
|
||||
// OSX and Windows exhibit different behaviors when 'checked' is false,
|
||||
// so compare against the consistent 'true'. See bug 894809.
|
||||
function isChecked(node) {
|
||||
return node.getAttribute('checked') === 'true';
|
||||
};
|
||||
exports.isChecked = isChecked;
|
@ -0,0 +1,217 @@
|
||||
/* 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 { Loader } = require('sdk/test/loader');
|
||||
const { show, hide } = require('sdk/ui/sidebar/actions');
|
||||
const { isShowing } = require('sdk/ui/sidebar/utils');
|
||||
const { getMostRecentBrowserWindow, isWindowPrivate } = require('sdk/window/utils');
|
||||
const { open, close, focus, promise: windowPromise } = require('sdk/window/helpers');
|
||||
const { setTimeout } = require('sdk/timers');
|
||||
const { isPrivate } = require('sdk/private-browsing');
|
||||
const { data } = require('sdk/self');
|
||||
const { URL } = require('sdk/url');
|
||||
|
||||
const { BLANK_IMG, BUILTIN_SIDEBAR_MENUITEMS, isSidebarShowing,
|
||||
getSidebarMenuitems, getExtraSidebarMenuitems, makeID, simulateCommand,
|
||||
simulateClick, getWidget, isChecked } = require('./sidebar/utils');
|
||||
|
||||
exports.testSideBarIsInNewPrivateWindows = function(assert, done) {
|
||||
const { Sidebar } = require('sdk/ui/sidebar');
|
||||
let testName = 'testSideBarIsInNewPrivateWindows';
|
||||
let sidebar = Sidebar({
|
||||
id: testName,
|
||||
title: testName,
|
||||
icon: BLANK_IMG,
|
||||
url: 'data:text/html;charset=utf-8,'+testName
|
||||
});
|
||||
|
||||
let startWindow = getMostRecentBrowserWindow();
|
||||
let ele = startWindow.document.getElementById(makeID(testName));
|
||||
assert.ok(ele, 'sidebar element was added');
|
||||
|
||||
open(null, { features: { private: true } }).then(function(window) {
|
||||
let ele = window.document.getElementById(makeID(testName));
|
||||
assert.ok(isPrivate(window), 'the new window is private');
|
||||
assert.ok(!!ele, 'sidebar element was added');
|
||||
|
||||
sidebar.destroy();
|
||||
assert.ok(!window.document.getElementById(makeID(testName)), 'sidebar id DNE');
|
||||
assert.ok(!startWindow.document.getElementById(makeID(testName)), 'sidebar id DNE');
|
||||
|
||||
close(window).then(done, assert.fail);
|
||||
})
|
||||
}
|
||||
|
||||
exports.testSidebarIsOpenInNewPrivateWindow = function(assert, done) {
|
||||
const { Sidebar } = require('sdk/ui/sidebar');
|
||||
let testName = 'testSidebarIsOpenInNewPrivateWindow';
|
||||
let window = getMostRecentBrowserWindow();
|
||||
|
||||
let sidebar = Sidebar({
|
||||
id: testName,
|
||||
title: testName,
|
||||
icon: BLANK_IMG,
|
||||
url: 'data:text/html;charset=utf-8,'+testName
|
||||
});
|
||||
|
||||
assert.equal(isPrivate(window), false, 'the window is not private');
|
||||
|
||||
sidebar.on('show', function() {
|
||||
assert.equal(isSidebarShowing(window), true, 'the sidebar is showing');
|
||||
assert.equal(isShowing(sidebar), true, 'the sidebar is showing');
|
||||
|
||||
windowPromise(window.OpenBrowserWindow({private: true}), 'DOMContentLoaded').then(function(window2) {
|
||||
assert.equal(isPrivate(window2), true, 'the new window is private');
|
||||
|
||||
let sidebarEle = window2.document.getElementById('sidebar');
|
||||
|
||||
// wait for the sidebar to load something
|
||||
function onSBLoad() {
|
||||
sidebarEle.contentDocument.getElementById('web-panels-browser').addEventListener('load', function() {
|
||||
assert.equal(isSidebarShowing(window), true, 'the sidebar is showing in old window still');
|
||||
assert.equal(isSidebarShowing(window2), true, 'the sidebar is showing in the new private window');
|
||||
assert.equal(isShowing(sidebar), true, 'the sidebar is showing');
|
||||
|
||||
sidebar.destroy();
|
||||
close(window2).then(done);
|
||||
}, true);
|
||||
}
|
||||
|
||||
sidebarEle.addEventListener('load', onSBLoad, true);
|
||||
|
||||
assert.pass('waiting for the sidebar to open...');
|
||||
}, assert.fail).then(null, assert.fail);
|
||||
});
|
||||
|
||||
sidebar.show();
|
||||
}
|
||||
|
||||
// TEST: edge case where web panel is destroyed while loading
|
||||
exports.testDestroyEdgeCaseBugWithPrivateWindow = function(assert, done) {
|
||||
const { Sidebar } = require('sdk/ui/sidebar');
|
||||
let testName = 'testDestroyEdgeCaseBug';
|
||||
let window = getMostRecentBrowserWindow();
|
||||
let sidebar = Sidebar({
|
||||
id: testName,
|
||||
title: testName,
|
||||
icon: BLANK_IMG,
|
||||
url: 'data:text/html;charset=utf-8,'+testName
|
||||
});
|
||||
|
||||
// NOTE: purposely not listening to show event b/c the event happens
|
||||
// between now and then.
|
||||
sidebar.show();
|
||||
|
||||
assert.equal(isPrivate(window), false, 'the new window is not private');
|
||||
assert.equal(isSidebarShowing(window), true, 'the sidebar is showing');
|
||||
|
||||
//assert.equal(isShowing(sidebar), true, 'the sidebar is showing');
|
||||
|
||||
open(null, { features: { private: true } }).then(focus).then(function(window2) {
|
||||
assert.equal(isPrivate(window2), true, 'the new window is private');
|
||||
assert.equal(isSidebarShowing(window2), false, 'the sidebar is not showing');
|
||||
assert.equal(isShowing(sidebar), false, 'the sidebar is not showing');
|
||||
|
||||
sidebar.destroy();
|
||||
assert.pass('destroying the sidebar');
|
||||
|
||||
close(window2).then(function() {
|
||||
let loader = Loader(module);
|
||||
|
||||
assert.equal(isPrivate(window), false, 'the current window is not private');
|
||||
|
||||
let sidebar = loader.require('sdk/ui/sidebar').Sidebar({
|
||||
id: testName,
|
||||
title: testName,
|
||||
icon: BLANK_IMG,
|
||||
url: 'data:text/html;charset=utf-8,'+ testName,
|
||||
onShow: function() {
|
||||
assert.pass('onShow works for Sidebar');
|
||||
loader.unload();
|
||||
|
||||
let sidebarMI = getSidebarMenuitems();
|
||||
for each (let mi in sidebarMI) {
|
||||
assert.ok(BUILTIN_SIDEBAR_MENUITEMS.indexOf(mi.getAttribute('id')) >= 0, 'the menuitem is for a built-in sidebar')
|
||||
assert.ok(!isChecked(mi), 'no sidebar menuitem is checked');
|
||||
}
|
||||
assert.ok(!window.document.getElementById(makeID(testName)), 'sidebar id DNE');
|
||||
assert.equal(isSidebarShowing(window), false, 'the sidebar is not showing');
|
||||
|
||||
done();
|
||||
}
|
||||
})
|
||||
|
||||
sidebar.show();
|
||||
assert.pass('showing the sidebar');
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
exports.testShowInPrivateWindow = function(assert, done) {
|
||||
const { Sidebar } = require('sdk/ui/sidebar');
|
||||
let testName = 'testShowInPrivateWindow';
|
||||
let window1 = getMostRecentBrowserWindow();
|
||||
let url = 'data:text/html;charset=utf-8,'+testName;
|
||||
|
||||
let sidebar1 = Sidebar({
|
||||
id: testName,
|
||||
title: testName,
|
||||
icon: BLANK_IMG,
|
||||
url: url
|
||||
});
|
||||
let menuitemID = makeID(sidebar1.id);
|
||||
|
||||
assert.equal(sidebar1.url, url, 'url getter works');
|
||||
assert.equal(isShowing(sidebar1), false, 'the sidebar is not showing');
|
||||
assert.ok(!isChecked(window1.document.getElementById(menuitemID)),
|
||||
'the menuitem is not checked');
|
||||
assert.equal(isSidebarShowing(window1), false, 'the new window sidebar is not showing');
|
||||
|
||||
windowPromise(window1.OpenBrowserWindow({ private: true }), 'load').then(function(window) {
|
||||
let { document } = window;
|
||||
assert.equal(isWindowPrivate(window), true, 'new window is private');
|
||||
assert.equal(isPrivate(window), true, 'new window is private');
|
||||
|
||||
sidebar1.show().then(
|
||||
function good() {
|
||||
assert.equal(isShowing(sidebar1), true, 'the sidebar is showing');
|
||||
assert.ok(!!document.getElementById(menuitemID),
|
||||
'the menuitem exists on the private window');
|
||||
assert.equal(isSidebarShowing(window), true, 'the new window sidebar is showing');
|
||||
|
||||
sidebar1.destroy();
|
||||
assert.equal(isSidebarShowing(window), false, 'the new window sidebar is showing');
|
||||
assert.ok(!window1.document.getElementById(menuitemID),
|
||||
'the menuitem on the new window dne');
|
||||
|
||||
// test old window state
|
||||
assert.equal(isSidebarShowing(window1), false, 'the old window sidebar is not showing');
|
||||
assert.equal(window1.document.getElementById(menuitemID),
|
||||
null,
|
||||
'the menuitem on the old window dne');
|
||||
|
||||
close(window).then(done);
|
||||
},
|
||||
function bad() {
|
||||
assert.fail('a successful show should not happen here..');
|
||||
});
|
||||
}, assert.fail);
|
||||
}
|
||||
|
||||
// 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.
|
||||
try {
|
||||
require('sdk/ui/sidebar');
|
||||
}
|
||||
catch (err) {
|
||||
if (!/^Unsupported Application/.test(err.message))
|
||||
throw err;
|
||||
|
||||
module.exports = {
|
||||
'test Unsupported Application': assert => assert.pass(err.message)
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ const sp = require('sdk/simple-prefs');
|
||||
const app = require('sdk/system/xul-app');
|
||||
const self = require('sdk/self');
|
||||
const tabs = require('sdk/tabs');
|
||||
const { preferencesBranch } = require('@loader/options');
|
||||
|
||||
const { AddonManager } = Cu.import('resource://gre/modules/AddonManager.jsm', {});
|
||||
|
||||
@ -89,4 +90,8 @@ if (app.is('Firefox')) {
|
||||
}
|
||||
}
|
||||
|
||||
exports.testDefaultPreferencesBranch = function(assert) {
|
||||
assert.equal(preferencesBranch, self.id, 'preferencesBranch default the same as self.id');
|
||||
}
|
||||
|
||||
require('sdk/test/runner').runTestsFromModule(module);
|
||||
|
45
addon-sdk/source/test/addons/standard-id/lib/main.js
Normal file
45
addon-sdk/source/test/addons/standard-id/lib/main.js
Normal file
@ -0,0 +1,45 @@
|
||||
/* 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 { id } = require('sdk/self');
|
||||
const simple = require('sdk/simple-prefs');
|
||||
const service = require('sdk/preferences/service');
|
||||
const { preferencesBranch } = require('@loader/options');
|
||||
const { AddonManager } = require('chrome').Cu.import('resource://gre/modules/AddonManager.jsm');
|
||||
|
||||
exports.testStandardID = function(assert) {
|
||||
assert.equal(id, 'standard-id@jetpack', 'standard ID is standard');
|
||||
|
||||
assert.equal(simple.prefs.test13, 26, 'test13 is 26');
|
||||
|
||||
simple.prefs.test14 = '15';
|
||||
assert.equal(service.get('extensions.standard-id@jetpack.test14'), '15', 'test14 is 15');
|
||||
|
||||
assert.equal(service.get('extensions.standard-id@jetpack.test14'), simple.prefs.test14, 'simple test14 also 15');
|
||||
|
||||
}
|
||||
|
||||
exports.testInvalidPreferencesBranch = function(assert) {
|
||||
assert.notEqual(preferencesBranch, 'invalid^branch*name', 'invalid preferences-branch value ignored');
|
||||
|
||||
assert.equal(preferencesBranch, 'standard-id@jetpack', 'preferences-branch is standard-id@jetpack');
|
||||
|
||||
}
|
||||
|
||||
// from `/test/test-self.js`, adapted to `sdk/test/assert` API
|
||||
exports.testSelfID = function(assert, done) {
|
||||
|
||||
assert.equal(typeof(id), 'string', 'self.id is a string');
|
||||
assert.ok(id.length > 0, 'self.id not empty');
|
||||
|
||||
AddonManager.getAddonByID(id, function(addon) {
|
||||
assert.ok(addon, 'found addon with self.id');
|
||||
done();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
require('sdk/test/runner').runTestsFromModule(module);
|
14
addon-sdk/source/test/addons/standard-id/package.json
Normal file
14
addon-sdk/source/test/addons/standard-id/package.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"id": "standard-id",
|
||||
"fullName": "standard ID test",
|
||||
"author": "Tomislav Jovanovic",
|
||||
|
||||
"preferences": [{
|
||||
"name": "test13",
|
||||
"type": "integer",
|
||||
"title": "test13",
|
||||
"value": 26
|
||||
}],
|
||||
|
||||
"preferences-branch": "invalid^branch*name"
|
||||
}
|
92
addon-sdk/source/test/sidebar/utils.js
Normal file
92
addon-sdk/source/test/sidebar/utils.js
Normal file
@ -0,0 +1,92 @@
|
||||
/* 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';
|
||||
|
||||
module.metadata = {
|
||||
'engines': {
|
||||
'Firefox': '> 24'
|
||||
}
|
||||
};
|
||||
|
||||
const { Cu } = require('chrome');
|
||||
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
|
||||
const { fromIterator } = require('sdk/util/array');
|
||||
|
||||
const BLANK_IMG = exports.BLANK_IMG = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
|
||||
|
||||
const BUILTIN_SIDEBAR_MENUITEMS = exports.BUILTIN_SIDEBAR_MENUITEMS = [
|
||||
'menu_socialSidebar',
|
||||
'menu_historySidebar',
|
||||
'menu_bookmarksSidebar'
|
||||
];
|
||||
|
||||
function isSidebarShowing(window) {
|
||||
window = window || getMostRecentBrowserWindow();
|
||||
let sidebar = window.document.getElementById('sidebar-box');
|
||||
return !sidebar.hidden;
|
||||
}
|
||||
exports.isSidebarShowing = isSidebarShowing;
|
||||
|
||||
function getSidebarMenuitems(window) {
|
||||
window = window || getMostRecentBrowserWindow();
|
||||
return fromIterator(window.document.querySelectorAll('#viewSidebarMenu menuitem'));
|
||||
}
|
||||
exports.getSidebarMenuitems = getSidebarMenuitems;
|
||||
|
||||
function getExtraSidebarMenuitems() {
|
||||
let menuitems = getSidebarMenuitems();
|
||||
return menuitems.filter(function(mi) {
|
||||
return BUILTIN_SIDEBAR_MENUITEMS.indexOf(mi.getAttribute('id')) < 0;
|
||||
});
|
||||
}
|
||||
exports.getExtraSidebarMenuitems = getExtraSidebarMenuitems;
|
||||
|
||||
function makeID(id) {
|
||||
return 'jetpack-sidebar-' + id;
|
||||
}
|
||||
exports.makeID = makeID;
|
||||
|
||||
function simulateCommand(ele) {
|
||||
let window = ele.ownerDocument.defaultView;
|
||||
let { document } = window;
|
||||
var evt = document.createEvent('XULCommandEvent');
|
||||
evt.initCommandEvent('command', true, true, window,
|
||||
0, false, false, false, false, null);
|
||||
ele.dispatchEvent(evt);
|
||||
}
|
||||
exports.simulateCommand = simulateCommand;
|
||||
|
||||
function simulateClick(ele) {
|
||||
let window = ele.ownerDocument.defaultView;
|
||||
let { document } = window;
|
||||
let evt = document.createEvent('MouseEvents');
|
||||
evt.initMouseEvent('click', true, true, window,
|
||||
0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
||||
ele.dispatchEvent(evt);
|
||||
}
|
||||
exports.simulateClick = simulateClick;
|
||||
|
||||
function getWidget(buttonId, window = getMostRecentBrowserWindow()) {
|
||||
const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
|
||||
const { AREA_NAVBAR } = CustomizableUI;
|
||||
|
||||
let widgets = CustomizableUI.getWidgetsInArea(AREA_NAVBAR).
|
||||
filter(({id}) => id.startsWith('button--') && id.endsWith(buttonId));
|
||||
|
||||
if (widgets.length === 0)
|
||||
throw new Error('Widget with id `' + buttonId +'` not found.');
|
||||
|
||||
if (widgets.length > 1)
|
||||
throw new Error('Unexpected number of widgets: ' + widgets.length)
|
||||
|
||||
return widgets[0].forWindow(window);
|
||||
};
|
||||
exports.getWidget = getWidget;
|
||||
|
||||
// OSX and Windows exhibit different behaviors when 'checked' is false,
|
||||
// so compare against the consistent 'true'. See bug 894809.
|
||||
function isChecked(node) {
|
||||
return node.getAttribute('checked') === 'true';
|
||||
};
|
||||
exports.isChecked = isChecked;
|
@ -20,9 +20,7 @@ const ERR_FENNEC_MSG = {
|
||||
};
|
||||
|
||||
// TEST: tab unloader
|
||||
exports.testAutomaticDestroy = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testAutomaticDestroy = function(assert, done) {
|
||||
let called = false;
|
||||
|
||||
let loader2 = Loader(module);
|
||||
@ -32,36 +30,36 @@ exports.testAutomaticDestroy = function(test) {
|
||||
let tabs2Len = tabs2.length;
|
||||
|
||||
tabs2.on('open', function onOpen(tab) {
|
||||
test.fail("an onOpen listener was called that should not have been");
|
||||
assert.fail("an onOpen listener was called that should not have been");
|
||||
called = true;
|
||||
});
|
||||
tabs2.on('ready', function onReady(tab) {
|
||||
test.fail("an onReady listener was called that should not have been");
|
||||
assert.fail("an onReady listener was called that should not have been");
|
||||
called = true;
|
||||
});
|
||||
tabs2.on('select', function onSelect(tab) {
|
||||
test.fail("an onSelect listener was called that should not have been");
|
||||
assert.fail("an onSelect listener was called that should not have been");
|
||||
called = true;
|
||||
});
|
||||
tabs2.on('close', function onClose(tab) {
|
||||
test.fail("an onClose listener was called that should not have been");
|
||||
assert.fail("an onClose listener was called that should not have been");
|
||||
called = true;
|
||||
});
|
||||
loader2.unload();
|
||||
|
||||
tabs3.on('open', function onOpen(tab) {
|
||||
test.pass("an onOpen listener was called for tabs3");
|
||||
assert.pass("an onOpen listener was called for tabs3");
|
||||
|
||||
tab.on('ready', function onReady(tab) {
|
||||
test.fail("an onReady listener was called that should not have been");
|
||||
assert.fail("an onReady listener was called that should not have been");
|
||||
called = true;
|
||||
});
|
||||
tab.on('select', function onSelect(tab) {
|
||||
test.fail("an onSelect listener was called that should not have been");
|
||||
assert.fail("an onSelect listener was called that should not have been");
|
||||
called = true;
|
||||
});
|
||||
tab.on('close', function onClose(tab) {
|
||||
test.fail("an onClose listener was called that should not have been");
|
||||
assert.fail("an onClose listener was called that should not have been");
|
||||
called = true;
|
||||
});
|
||||
});
|
||||
@ -70,22 +68,22 @@ exports.testAutomaticDestroy = function(test) {
|
||||
|
||||
// Fire a tab event and ensure that the destroyed tab is inactive
|
||||
tabs.once('open', function(tab) {
|
||||
test.pass('tabs.once("open") works!');
|
||||
assert.pass('tabs.once("open") works!');
|
||||
|
||||
test.assertEqual(tabs2Len, tabs2.length, "tabs2 length was not changed");
|
||||
test.assertEqual(tabs.length, (tabs2.length+2), "tabs.length > tabs2.length");
|
||||
assert.equal(tabs2Len, tabs2.length, "tabs2 length was not changed");
|
||||
assert.equal(tabs.length, (tabs2.length+2), "tabs.length > tabs2.length");
|
||||
|
||||
tab.once('ready', function() {
|
||||
test.pass('tab.once("ready") works!');
|
||||
assert.pass('tab.once("ready") works!');
|
||||
|
||||
tab.once('close', function() {
|
||||
test.pass('tab.once("close") works!');
|
||||
assert.pass('tab.once("close") works!');
|
||||
|
||||
timer.setTimeout(function () {
|
||||
test.assert(!called, "Unloaded tab module is destroyed and inactive");
|
||||
assert.ok(!called, "Unloaded tab module is destroyed and inactive");
|
||||
|
||||
// end test
|
||||
test.done();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@ -97,8 +95,7 @@ exports.testAutomaticDestroy = function(test) {
|
||||
};
|
||||
|
||||
// TEST: tab properties
|
||||
exports.testTabProperties = function(test) {
|
||||
test.waitUntilDone();
|
||||
exports.testTabProperties = function(assert, done) {
|
||||
let { loader, messages } = LoaderWithHookedConsole();
|
||||
let tabs = loader.require('sdk/tabs');
|
||||
|
||||
@ -107,39 +104,37 @@ exports.testTabProperties = function(test) {
|
||||
tabs.open({
|
||||
url: url,
|
||||
onReady: function(tab) {
|
||||
test.assertEqual(tab.title, "foo", "title of the new tab matches");
|
||||
test.assertEqual(tab.url, url, "URL of the new tab matches");
|
||||
test.assert(tab.favicon, "favicon of the new tab is not empty");
|
||||
assert.equal(tab.title, "foo", "title of the new tab matches");
|
||||
assert.equal(tab.url, url, "URL of the new tab matches");
|
||||
assert.ok(tab.favicon, "favicon of the new tab is not empty");
|
||||
// TODO: remove need for this test by implementing the favicon feature
|
||||
test.assertEqual(messages[0].msg,
|
||||
assert.equal(messages[0].msg,
|
||||
"tab.favicon is deprecated, and " +
|
||||
"currently favicon helpers are not yet supported " +
|
||||
"by Fennec",
|
||||
"favicon logs an error for now");
|
||||
test.assertEqual(tab.style, null, "style of the new tab matches");
|
||||
test.assertEqual(tab.index, tabsLen, "index of the new tab matches");
|
||||
test.assertNotEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
|
||||
test.assertNotEqual(tab.id, null, "a tab object always has an id property");
|
||||
assert.equal(tab.style, null, "style of the new tab matches");
|
||||
assert.equal(tab.index, tabsLen, "index of the new tab matches");
|
||||
assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
|
||||
assert.notEqual(tab.id, null, "a tab object always has an id property");
|
||||
|
||||
tab.close(function() {
|
||||
loader.unload();
|
||||
|
||||
// end test
|
||||
test.done();
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// TEST: tabs iterator and length property
|
||||
exports.testTabsIteratorAndLength = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testTabsIteratorAndLength = function(assert, done) {
|
||||
let newTabs = [];
|
||||
let startCount = 0;
|
||||
for each (let t in tabs) startCount++;
|
||||
|
||||
test.assertEqual(startCount, tabs.length, "length property is correct");
|
||||
assert.equal(startCount, tabs.length, "length property is correct");
|
||||
|
||||
let url = "data:text/html;charset=utf-8,testTabsIteratorAndLength";
|
||||
tabs.open({url: url, onOpen: function(tab) newTabs.push(tab)});
|
||||
@ -149,26 +144,21 @@ exports.testTabsIteratorAndLength = function(test) {
|
||||
onOpen: function(tab) {
|
||||
let count = 0;
|
||||
for each (let t in tabs) count++;
|
||||
test.assertEqual(count, startCount + 3, "iterated tab count matches");
|
||||
test.assertEqual(startCount + 3, tabs.length, "iterated tab count matches length property");
|
||||
assert.equal(count, startCount + 3, "iterated tab count matches");
|
||||
assert.equal(startCount + 3, tabs.length, "iterated tab count matches length property");
|
||||
|
||||
let newTabsLength = newTabs.length;
|
||||
newTabs.forEach(function(t) t.close(function() {
|
||||
if (--newTabsLength > 0) return;
|
||||
|
||||
tab.close(function() {
|
||||
// end test
|
||||
test.done();
|
||||
});
|
||||
tab.close(done);
|
||||
}));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// TEST: tab.url setter
|
||||
exports.testTabLocation = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testTabLocation = function(assert, done) {
|
||||
let url1 = "data:text/html;charset=utf-8,foo";
|
||||
let url2 = "data:text/html;charset=utf-8,bar";
|
||||
|
||||
@ -177,12 +167,9 @@ exports.testTabLocation = function(test) {
|
||||
return;
|
||||
|
||||
tabs.removeListener('ready', onReady);
|
||||
test.pass("tab loaded the correct url");
|
||||
assert.pass("tab loaded the correct url");
|
||||
|
||||
tab.close(function() {
|
||||
// end test
|
||||
test.done();
|
||||
});
|
||||
tab.close(done);
|
||||
});
|
||||
|
||||
tabs.open({
|
||||
@ -194,9 +181,7 @@ exports.testTabLocation = function(test) {
|
||||
};
|
||||
|
||||
// TEST: tab.move()
|
||||
exports.testTabMove = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testTabMove = function(assert, done) {
|
||||
let { loader, messages } = LoaderWithHookedConsole();
|
||||
let tabs = loader.require('sdk/tabs');
|
||||
|
||||
@ -205,22 +190,22 @@ exports.testTabMove = function(test) {
|
||||
tabs.open({
|
||||
url: url,
|
||||
onOpen: function(tab1) {
|
||||
test.assert(tab1.index >= 0, "opening a tab returns a tab w/ valid index");
|
||||
assert.ok(tab1.index >= 0, "opening a tab returns a tab w/ valid index");
|
||||
|
||||
tabs.open({
|
||||
url: url,
|
||||
onOpen: function(tab) {
|
||||
let i = tab.index;
|
||||
test.assert(tab.index > tab1.index, "2nd tab has valid index");
|
||||
assert.ok(tab.index > tab1.index, "2nd tab has valid index");
|
||||
tab.index = 0;
|
||||
test.assertEqual(tab.index, i, "tab index after move matches");
|
||||
test.assertEqual(JSON.stringify(messages),
|
||||
assert.equal(tab.index, i, "tab index after move matches");
|
||||
assert.equal(JSON.stringify(messages),
|
||||
JSON.stringify([ERR_FENNEC_MSG]),
|
||||
"setting tab.index logs error");
|
||||
// end test
|
||||
tab1.close(function() tab.close(function() {
|
||||
loader.unload();
|
||||
test.done();
|
||||
done();
|
||||
}));
|
||||
}
|
||||
});
|
||||
@ -229,9 +214,7 @@ exports.testTabMove = function(test) {
|
||||
};
|
||||
|
||||
// TEST: open tab with default options
|
||||
exports.testTabsOpen_alt = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testTabsOpen_alt = function(assert, done) {
|
||||
let { loader, messages } = LoaderWithHookedConsole();
|
||||
let tabs = loader.require('sdk/tabs');
|
||||
let url = "data:text/html;charset=utf-8,default";
|
||||
@ -239,24 +222,22 @@ exports.testTabsOpen_alt = function(test) {
|
||||
tabs.open({
|
||||
url: url,
|
||||
onReady: function(tab) {
|
||||
test.assertEqual(tab.url, url, "URL of the new tab matches");
|
||||
test.assertEqual(tabs.activeTab, tab, "URL of active tab in the current window matches");
|
||||
test.assertEqual(tab.isPinned, false, "The new tab is not pinned");
|
||||
test.assertEqual(messages.length, 1, "isPinned logs error");
|
||||
assert.equal(tab.url, url, "URL of the new tab matches");
|
||||
assert.equal(tabs.activeTab, tab, "URL of active tab in the current window matches");
|
||||
assert.equal(tab.isPinned, false, "The new tab is not pinned");
|
||||
assert.equal(messages.length, 1, "isPinned logs error");
|
||||
|
||||
// end test
|
||||
tab.close(function() {
|
||||
loader.unload();
|
||||
test.done();
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// TEST: open pinned tab
|
||||
exports.testOpenPinned_alt = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testOpenPinned_alt = function(assert, done) {
|
||||
let { loader, messages } = LoaderWithHookedConsole();
|
||||
let tabs = loader.require('sdk/tabs');
|
||||
let url = "about:blank";
|
||||
@ -265,26 +246,24 @@ exports.testOpenPinned_alt = function(test) {
|
||||
url: url,
|
||||
isPinned: true,
|
||||
onOpen: function(tab) {
|
||||
test.assertEqual(tab.isPinned, false, "The new tab is pinned");
|
||||
assert.equal(tab.isPinned, false, "The new tab is pinned");
|
||||
// We get two error message: one for tabs.open's isPinned argument
|
||||
// and another one for tab.isPinned
|
||||
test.assertEqual(JSON.stringify(messages),
|
||||
assert.equal(JSON.stringify(messages),
|
||||
JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]),
|
||||
"isPinned logs error");
|
||||
|
||||
// end test
|
||||
tab.close(function() {
|
||||
loader.unload();
|
||||
test.done();
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// TEST: pin/unpin opened tab
|
||||
exports.testPinUnpin_alt = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testPinUnpin_alt = function(assert, done) {
|
||||
let { loader, messages } = LoaderWithHookedConsole();
|
||||
let tabs = loader.require('sdk/tabs');
|
||||
let url = "data:text/html;charset=utf-8,default";
|
||||
@ -293,8 +272,8 @@ exports.testPinUnpin_alt = function(test) {
|
||||
url: url,
|
||||
onOpen: function(tab) {
|
||||
tab.pin();
|
||||
test.assertEqual(tab.isPinned, false, "The tab was pinned correctly");
|
||||
test.assertEqual(JSON.stringify(messages),
|
||||
assert.equal(tab.isPinned, false, "The tab was pinned correctly");
|
||||
assert.equal(JSON.stringify(messages),
|
||||
JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]),
|
||||
"tab.pin() logs error");
|
||||
|
||||
@ -302,35 +281,33 @@ exports.testPinUnpin_alt = function(test) {
|
||||
messages.length = 0;
|
||||
|
||||
tab.unpin();
|
||||
test.assertEqual(tab.isPinned, false, "The tab was unpinned correctly");
|
||||
test.assertEqual(JSON.stringify(messages),
|
||||
assert.equal(tab.isPinned, false, "The tab was unpinned correctly");
|
||||
assert.equal(JSON.stringify(messages),
|
||||
JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]),
|
||||
"tab.unpin() logs error");
|
||||
|
||||
// end test
|
||||
tab.close(function() {
|
||||
loader.unload();
|
||||
test.done();
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// TEST: open tab in background
|
||||
exports.testInBackground = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testInBackground = function(assert, done) {
|
||||
let activeUrl = tabs.activeTab.url;
|
||||
let url = "data:text/html;charset=utf-8,background";
|
||||
let window = windows.browserWindows.activeWindow;
|
||||
tabs.once('ready', function onReady(tab) {
|
||||
test.assertEqual(tabs.activeTab.url, activeUrl, "URL of active tab has not changed");
|
||||
test.assertEqual(tab.url, url, "URL of the new background tab matches");
|
||||
test.assertEqual(windows.browserWindows.activeWindow, window, "a new window was not opened");
|
||||
test.assertNotEqual(tabs.activeTab.url, url, "URL of active tab is not the new URL");
|
||||
assert.equal(tabs.activeTab.url, activeUrl, "URL of active tab has not changed");
|
||||
assert.equal(tab.url, url, "URL of the new background tab matches");
|
||||
assert.equal(windows.browserWindows.activeWindow, window, "a new window was not opened");
|
||||
assert.notEqual(tabs.activeTab.url, url, "URL of active tab is not the new URL");
|
||||
|
||||
// end test
|
||||
tab.close(function() test.done());
|
||||
tab.close(done);
|
||||
});
|
||||
|
||||
tabs.open({
|
||||
@ -340,9 +317,7 @@ exports.testInBackground = function(test) {
|
||||
};
|
||||
|
||||
// TEST: open tab in new window
|
||||
exports.testOpenInNewWindow = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testOpenInNewWindow = function(assert, done) {
|
||||
let url = "data:text/html;charset=utf-8,newwindow";
|
||||
let window = windows.browserWindows.activeWindow;
|
||||
|
||||
@ -350,20 +325,18 @@ exports.testOpenInNewWindow = function(test) {
|
||||
url: url,
|
||||
inNewWindow: true,
|
||||
onReady: function(tab) {
|
||||
test.assertEqual(windows.browserWindows.length, 1, "a new window was not opened");
|
||||
test.assertEqual(windows.browserWindows.activeWindow, window, "old window is active");
|
||||
test.assertEqual(tab.url, url, "URL of the new tab matches");
|
||||
test.assertEqual(tabs.activeTab, tab, "tab is the activeTab");
|
||||
assert.equal(windows.browserWindows.length, 1, "a new window was not opened");
|
||||
assert.equal(windows.browserWindows.activeWindow, window, "old window is active");
|
||||
assert.equal(tab.url, url, "URL of the new tab matches");
|
||||
assert.equal(tabs.activeTab, tab, "tab is the activeTab");
|
||||
|
||||
tab.close(function() test.done());
|
||||
tab.close(done);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// TEST: onOpen event handler
|
||||
exports.testTabsEvent_onOpen = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testTabsEvent_onOpen = function(assert, done) {
|
||||
let url = URL.replace('#title#', 'testTabsEvent_onOpen');
|
||||
let eventCount = 0;
|
||||
|
||||
@ -375,21 +348,19 @@ exports.testTabsEvent_onOpen = function(test) {
|
||||
|
||||
// add listener via collection add
|
||||
tabs.on('open', function listener2(tab) {
|
||||
test.assertEqual(++eventCount, 2, "both listeners notified");
|
||||
assert.equal(++eventCount, 2, "both listeners notified");
|
||||
tabs.removeListener('open', listener1);
|
||||
tabs.removeListener('open', listener2);
|
||||
|
||||
// ends test
|
||||
tab.close(function() test.done());
|
||||
tab.close(done);
|
||||
});
|
||||
|
||||
tabs.open(url);
|
||||
};
|
||||
|
||||
// TEST: onClose event handler
|
||||
exports.testTabsEvent_onClose = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testTabsEvent_onClose = function(assert, done) {
|
||||
let url = "data:text/html;charset=utf-8,onclose";
|
||||
let eventCount = 0;
|
||||
|
||||
@ -401,12 +372,12 @@ exports.testTabsEvent_onClose = function(test) {
|
||||
|
||||
// add listener via collection add
|
||||
tabs.on('close', function listener2(tab) {
|
||||
test.assertEqual(++eventCount, 2, "both listeners notified");
|
||||
assert.equal(++eventCount, 2, "both listeners notified");
|
||||
tabs.removeListener('close', listener1);
|
||||
tabs.removeListener('close', listener2);
|
||||
|
||||
// end test
|
||||
test.done();
|
||||
done();
|
||||
});
|
||||
|
||||
tabs.on('ready', function onReady(tab) {
|
||||
@ -418,9 +389,7 @@ exports.testTabsEvent_onClose = function(test) {
|
||||
};
|
||||
|
||||
// TEST: onClose event handler when a window is closed
|
||||
exports.testTabsEvent_onCloseWindow = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testTabsEvent_onCloseWindow = function(assert, done) {
|
||||
let closeCount = 0, individualCloseCount = 0;
|
||||
function listener() {
|
||||
closeCount++;
|
||||
@ -434,12 +403,12 @@ exports.testTabsEvent_onCloseWindow = function(test) {
|
||||
if (++openTabs == 3) {
|
||||
tabs.removeListener("close", listener);
|
||||
|
||||
test.assertEqual(closeCount, 3, "Correct number of close events received");
|
||||
test.assertEqual(individualCloseCount, 3,
|
||||
assert.equal(closeCount, 3, "Correct number of close events received");
|
||||
assert.equal(individualCloseCount, 3,
|
||||
"Each tab with an attached onClose listener received a close " +
|
||||
"event when the window was closed");
|
||||
|
||||
test.done();
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -464,9 +433,7 @@ exports.testTabsEvent_onCloseWindow = function(test) {
|
||||
};
|
||||
|
||||
// TEST: onReady event handler
|
||||
exports.testTabsEvent_onReady = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testTabsEvent_onReady = function(assert, done) {
|
||||
let url = "data:text/html;charset=utf-8,onready";
|
||||
let eventCount = 0;
|
||||
|
||||
@ -478,21 +445,19 @@ exports.testTabsEvent_onReady = function(test) {
|
||||
|
||||
// add listener via collection add
|
||||
tabs.on('ready', function listener2(tab) {
|
||||
test.assertEqual(++eventCount, 2, "both listeners notified");
|
||||
assert.equal(++eventCount, 2, "both listeners notified");
|
||||
tabs.removeListener('ready', listener1);
|
||||
tabs.removeListener('ready', listener2);
|
||||
|
||||
// end test
|
||||
tab.close(function() test.done());
|
||||
tab.close(done);
|
||||
});
|
||||
|
||||
tabs.open(url);
|
||||
};
|
||||
|
||||
// TEST: onActivate event handler
|
||||
exports.testTabsEvent_onActivate = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testTabsEvent_onActivate = function(assert, done) {
|
||||
let url = "data:text/html;charset=utf-8,onactivate";
|
||||
let eventCount = 0;
|
||||
|
||||
@ -504,22 +469,20 @@ exports.testTabsEvent_onActivate = function(test) {
|
||||
|
||||
// add listener via collection add
|
||||
tabs.on('activate', function listener2(tab) {
|
||||
test.assertEqual(++eventCount, 2, "both listeners notified");
|
||||
test.assertEqual(tab, tabs.activeTab, 'the active tab is correct');
|
||||
assert.equal(++eventCount, 2, "both listeners notified");
|
||||
assert.equal(tab, tabs.activeTab, 'the active tab is correct');
|
||||
tabs.removeListener('activate', listener1);
|
||||
tabs.removeListener('activate', listener2);
|
||||
|
||||
// end test
|
||||
tab.close(function() test.done());
|
||||
tab.close(done);
|
||||
});
|
||||
|
||||
tabs.open(url);
|
||||
};
|
||||
|
||||
// TEST: onDeactivate event handler
|
||||
exports.testTabsEvent_onDeactivate = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testTabsEvent_onDeactivate = function(assert, done) {
|
||||
let url = "data:text/html;charset=utf-8,ondeactivate";
|
||||
let eventCount = 0;
|
||||
|
||||
@ -531,13 +494,13 @@ exports.testTabsEvent_onDeactivate = function(test) {
|
||||
|
||||
// add listener via collection add
|
||||
tabs.on('deactivate', function listener2(tab) {
|
||||
test.assertEqual(++eventCount, 2, 'both listeners notified');
|
||||
test.assertNotEqual(tab, tabs.activeTab, 'the active tab is not the deactivated tab');
|
||||
assert.equal(++eventCount, 2, 'both listeners notified');
|
||||
assert.notEqual(tab, tabs.activeTab, 'the active tab is not the deactivated tab');
|
||||
tabs.removeListener('deactivate', listener1);
|
||||
tabs.removeListener('deactivate', listener2);
|
||||
|
||||
// end test
|
||||
tab.close(function() test.done());
|
||||
tab.close(done);
|
||||
});
|
||||
|
||||
tabs.on('activate', function onActivate(tab) {
|
||||
@ -550,9 +513,7 @@ exports.testTabsEvent_onDeactivate = function(test) {
|
||||
};
|
||||
|
||||
// TEST: per-tab event handlers
|
||||
exports.testPerTabEvents = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testPerTabEvents = function(assert, done) {
|
||||
let eventCount = 0;
|
||||
|
||||
tabs.open({
|
||||
@ -566,19 +527,18 @@ exports.testPerTabEvents = function(test) {
|
||||
|
||||
// add listener via collection add
|
||||
tab.on('ready', function listener2() {
|
||||
test.assertEqual(eventCount, 1, "both listeners notified");
|
||||
assert.equal(eventCount, 1, "both listeners notified");
|
||||
tab.removeListener('ready', listener1);
|
||||
tab.removeListener('ready', listener2);
|
||||
|
||||
// end test
|
||||
tab.close(function() test.done());
|
||||
tab.close(done);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.testUniqueTabIds = function(test) {
|
||||
test.waitUntilDone();
|
||||
exports.testUniqueTabIds = function(assert, done) {
|
||||
var tabs = require('sdk/tabs');
|
||||
var tabIds = {};
|
||||
var steps = [
|
||||
@ -601,8 +561,8 @@ exports.testUniqueTabIds = function(test) {
|
||||
});
|
||||
},
|
||||
function (index) {
|
||||
test.assertNotEqual(tabIds.tab1, tabIds.tab2, "Tab ids should be unique.");
|
||||
test.done();
|
||||
assert.notEqual(tabIds.tab1, tabIds.tab2, "Tab ids should be unique.");
|
||||
done();
|
||||
}
|
||||
];
|
||||
|
||||
@ -617,3 +577,5 @@ exports.testUniqueTabIds = function(test) {
|
||||
|
||||
next(0);
|
||||
}
|
||||
|
||||
require('sdk/test').run(exports);
|
||||
|
@ -19,19 +19,17 @@ const base64png = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYA" +
|
||||
"bWRR9AAAAABJRU5ErkJggg%3D%3D";
|
||||
|
||||
// Bug 682681 - tab.title should never be empty
|
||||
exports.testBug682681_aboutURI = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testBug682681_aboutURI = function(assert, done) {
|
||||
let tabStrings = StringBundle('chrome://browser/locale/tabbrowser.properties');
|
||||
|
||||
tabs.on('ready', function onReady(tab) {
|
||||
tabs.removeListener('ready', onReady);
|
||||
|
||||
test.assertEqual(tab.title,
|
||||
assert.equal(tab.title,
|
||||
tabStrings.get('tabs.emptyTabTitle'),
|
||||
"title of about: tab is not blank");
|
||||
|
||||
tab.close(function() test.done());
|
||||
tab.close(done);
|
||||
});
|
||||
|
||||
// open a about: url
|
||||
@ -42,42 +40,36 @@ exports.testBug682681_aboutURI = function(test) {
|
||||
};
|
||||
|
||||
// related to Bug 682681
|
||||
exports.testTitleForDataURI = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testTitleForDataURI = function(assert, done) {
|
||||
tabs.open({
|
||||
url: "data:text/html;charset=utf-8,<title>tab</title>",
|
||||
inBackground: true,
|
||||
onReady: function(tab) {
|
||||
test.assertEqual(tab.title, "tab", "data: title is not Connecting...");
|
||||
tab.close(function() test.done());
|
||||
assert.equal(tab.title, "tab", "data: title is not Connecting...");
|
||||
tab.close(done);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// TEST: 'BrowserWindow' instance creation on tab 'activate' event
|
||||
// See bug 648244: there was a infinite loop.
|
||||
exports.testBrowserWindowCreationOnActivate = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testBrowserWindowCreationOnActivate = function(assert, done) {
|
||||
let windows = require("sdk/windows").browserWindows;
|
||||
let gotActivate = false;
|
||||
|
||||
tabs.once('activate', function onActivate(eventTab) {
|
||||
test.assert(windows.activeWindow, "Is able to fetch activeWindow");
|
||||
assert.ok(windows.activeWindow, "Is able to fetch activeWindow");
|
||||
gotActivate = true;
|
||||
});
|
||||
|
||||
open().then(function(window) {
|
||||
test.assert(gotActivate, "Received activate event before openBrowserWindow's callback is called");
|
||||
closeBrowserWindow(window, function () test.done());
|
||||
assert.ok(gotActivate, "Received activate event before openBrowserWindow's callback is called");
|
||||
close(window).then(done);
|
||||
});
|
||||
}
|
||||
|
||||
// TEST: tab unloader
|
||||
exports.testAutomaticDestroy = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testAutomaticDestroy = function(assert, done) {
|
||||
// Create a second tab instance that we will destroy
|
||||
let called = false;
|
||||
|
||||
@ -92,20 +84,18 @@ exports.testAutomaticDestroy = function(test) {
|
||||
// Fire a tab event and ensure that the destroyed tab is inactive
|
||||
tabs.once('open', function (tab) {
|
||||
timer.setTimeout(function () {
|
||||
test.assert(!called, "Unloaded tab module is destroyed and inactive");
|
||||
tab.close(test.done.bind(test));
|
||||
assert.ok(!called, "Unloaded tab module is destroyed and inactive");
|
||||
tab.close(done);
|
||||
});
|
||||
});
|
||||
tabs.open("data:text/html;charset=utf-8,foo");
|
||||
};
|
||||
|
||||
exports.testTabPropertiesInNewWindow = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testTabPropertiesInNewWindow = function(assert, done) {
|
||||
let count = 0;
|
||||
function onReadyOrLoad (tab) {
|
||||
if (count++) {
|
||||
close(getOwnerWindow(tab)).then(test.done.bind(test));
|
||||
close(getOwnerWindow(tab)).then(done);
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,40 +104,38 @@ exports.testTabPropertiesInNewWindow = function(test) {
|
||||
inNewWindow: true,
|
||||
url: url,
|
||||
onReady: function(tab) {
|
||||
test.assertEqual(tab.title, "foo", "title of the new tab matches");
|
||||
test.assertEqual(tab.url, url, "URL of the new tab matches");
|
||||
test.assert(tab.favicon, "favicon of the new tab is not empty");
|
||||
test.assertEqual(tab.style, null, "style of the new tab matches");
|
||||
test.assertEqual(tab.index, 0, "index of the new tab matches");
|
||||
test.assertNotEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
|
||||
test.assertNotEqual(tab.id, null, "a tab object always has an id property.");
|
||||
assert.equal(tab.title, "foo", "title of the new tab matches");
|
||||
assert.equal(tab.url, url, "URL of the new tab matches");
|
||||
assert.ok(tab.favicon, "favicon of the new tab is not empty");
|
||||
assert.equal(tab.style, null, "style of the new tab matches");
|
||||
assert.equal(tab.index, 0, "index of the new tab matches");
|
||||
assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
|
||||
assert.notEqual(tab.id, null, "a tab object always has an id property.");
|
||||
|
||||
onReadyOrLoad(tab);
|
||||
},
|
||||
onLoad: function(tab) {
|
||||
test.assertEqual(tab.title, "foo", "title of the new tab matches");
|
||||
test.assertEqual(tab.url, url, "URL of the new tab matches");
|
||||
test.assert(tab.favicon, "favicon of the new tab is not empty");
|
||||
test.assertEqual(tab.style, null, "style of the new tab matches");
|
||||
test.assertEqual(tab.index, 0, "index of the new tab matches");
|
||||
test.assertNotEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
|
||||
test.assertNotEqual(tab.id, null, "a tab object always has an id property.");
|
||||
assert.equal(tab.title, "foo", "title of the new tab matches");
|
||||
assert.equal(tab.url, url, "URL of the new tab matches");
|
||||
assert.ok(tab.favicon, "favicon of the new tab is not empty");
|
||||
assert.equal(tab.style, null, "style of the new tab matches");
|
||||
assert.equal(tab.index, 0, "index of the new tab matches");
|
||||
assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
|
||||
assert.notEqual(tab.id, null, "a tab object always has an id property.");
|
||||
|
||||
onReadyOrLoad(tab);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.testTabPropertiesInSameWindow = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testTabPropertiesInSameWindow = function(assert, done) {
|
||||
// Get current count of tabs so we know the index of the
|
||||
// new tab, bug 893846
|
||||
let tabCount = tabs.length;
|
||||
let count = 0;
|
||||
function onReadyOrLoad (tab) {
|
||||
if (count++) {
|
||||
tab.close(test.done.bind(test));
|
||||
tab.close(done);
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,24 +143,24 @@ exports.testTabPropertiesInSameWindow = function(test) {
|
||||
tabs.open({
|
||||
url: url,
|
||||
onReady: function(tab) {
|
||||
test.assertEqual(tab.title, "foo", "title of the new tab matches");
|
||||
test.assertEqual(tab.url, url, "URL of the new tab matches");
|
||||
test.assert(tab.favicon, "favicon of the new tab is not empty");
|
||||
test.assertEqual(tab.style, null, "style of the new tab matches");
|
||||
test.assertEqual(tab.index, tabCount, "index of the new tab matches");
|
||||
test.assertNotEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
|
||||
test.assertNotEqual(tab.id, null, "a tab object always has an id property.");
|
||||
assert.equal(tab.title, "foo", "title of the new tab matches");
|
||||
assert.equal(tab.url, url, "URL of the new tab matches");
|
||||
assert.ok(tab.favicon, "favicon of the new tab is not empty");
|
||||
assert.equal(tab.style, null, "style of the new tab matches");
|
||||
assert.equal(tab.index, tabCount, "index of the new tab matches");
|
||||
assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
|
||||
assert.notEqual(tab.id, null, "a tab object always has an id property.");
|
||||
|
||||
onReadyOrLoad(tab);
|
||||
},
|
||||
onLoad: function(tab) {
|
||||
test.assertEqual(tab.title, "foo", "title of the new tab matches");
|
||||
test.assertEqual(tab.url, url, "URL of the new tab matches");
|
||||
test.assert(tab.favicon, "favicon of the new tab is not empty");
|
||||
test.assertEqual(tab.style, null, "style of the new tab matches");
|
||||
test.assertEqual(tab.index, tabCount, "index of the new tab matches");
|
||||
test.assertNotEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
|
||||
test.assertNotEqual(tab.id, null, "a tab object always has an id property.");
|
||||
assert.equal(tab.title, "foo", "title of the new tab matches");
|
||||
assert.equal(tab.url, url, "URL of the new tab matches");
|
||||
assert.ok(tab.favicon, "favicon of the new tab is not empty");
|
||||
assert.equal(tab.style, null, "style of the new tab matches");
|
||||
assert.equal(tab.index, tabCount, "index of the new tab matches");
|
||||
assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
|
||||
assert.notEqual(tab.id, null, "a tab object always has an id property.");
|
||||
|
||||
onReadyOrLoad(tab);
|
||||
}
|
||||
@ -180,9 +168,7 @@ exports.testTabPropertiesInSameWindow = function(test) {
|
||||
};
|
||||
|
||||
// TEST: tab properties
|
||||
exports.testTabContentTypeAndReload = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testTabContentTypeAndReload = function(assert, done) {
|
||||
open().then(focus).then(function(window) {
|
||||
let url = "data:text/html;charset=utf-8,<html><head><title>foo</title></head><body>foo</body></html>";
|
||||
let urlXML = "data:text/xml;charset=utf-8,<foo>bar</foo>";
|
||||
@ -190,11 +176,12 @@ exports.testTabContentTypeAndReload = function(test) {
|
||||
url: url,
|
||||
onReady: function(tab) {
|
||||
if (tab.url === url) {
|
||||
test.assertEqual(tab.contentType, "text/html");
|
||||
assert.equal(tab.contentType, "text/html");
|
||||
tab.url = urlXML;
|
||||
} else {
|
||||
test.assertEqual(tab.contentType, "text/xml");
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
}
|
||||
else {
|
||||
assert.equal(tab.contentType, "text/xml");
|
||||
close(window).then(done);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -202,13 +189,11 @@ exports.testTabContentTypeAndReload = function(test) {
|
||||
};
|
||||
|
||||
// TEST: tabs iterator and length property
|
||||
exports.testTabsIteratorAndLength = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testTabsIteratorAndLength = function(assert, done) {
|
||||
open(null, { features: { chrome: true, toolbar: true } }).then(focus).then(function(window) {
|
||||
let startCount = 0;
|
||||
for each (let t in tabs) startCount++;
|
||||
test.assertEqual(startCount, tabs.length, "length property is correct");
|
||||
assert.equal(startCount, tabs.length, "length property is correct");
|
||||
let url = "data:text/html;charset=utf-8,default";
|
||||
|
||||
tabs.open(url);
|
||||
@ -218,19 +203,17 @@ exports.testTabsIteratorAndLength = function(test) {
|
||||
onOpen: function(tab) {
|
||||
let count = 0;
|
||||
for each (let t in tabs) count++;
|
||||
test.assertEqual(count, startCount + 3, "iterated tab count matches");
|
||||
test.assertEqual(startCount + 3, tabs.length, "iterated tab count matches length property");
|
||||
assert.equal(count, startCount + 3, "iterated tab count matches");
|
||||
assert.equal(startCount + 3, tabs.length, "iterated tab count matches length property");
|
||||
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
close(window).then(done);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// TEST: tab.url setter
|
||||
exports.testTabLocation = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testTabLocation = function(assert, done) {
|
||||
open().then(focus).then(function(window) {
|
||||
let url1 = "data:text/html;charset=utf-8,foo";
|
||||
let url2 = "data:text/html;charset=utf-8,bar";
|
||||
@ -239,8 +222,8 @@ exports.testTabLocation = function(test) {
|
||||
if (tab.url != url2)
|
||||
return;
|
||||
tabs.removeListener('ready', onReady);
|
||||
test.pass("tab.load() loaded the correct url");
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
assert.pass("tab.load() loaded the correct url");
|
||||
close(window).then(done);
|
||||
});
|
||||
|
||||
tabs.open({
|
||||
@ -253,119 +236,108 @@ exports.testTabLocation = function(test) {
|
||||
};
|
||||
|
||||
// TEST: tab.close()
|
||||
exports.testTabClose = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testTabClose = function(assert, done) {
|
||||
let url = "data:text/html;charset=utf-8,foo";
|
||||
|
||||
test.assertNotEqual(tabs.activeTab.url, url, "tab is not the active tab");
|
||||
assert.notEqual(tabs.activeTab.url, url, "tab is not the active tab");
|
||||
tabs.on('ready', function onReady(tab) {
|
||||
tabs.removeListener('ready', onReady);
|
||||
test.assertEqual(tabs.activeTab.url, tab.url, "tab is now the active tab");
|
||||
assert.equal(tabs.activeTab.url, tab.url, "tab is now the active tab");
|
||||
let secondOnCloseCalled = false;
|
||||
|
||||
// Bug 699450: Multiple calls to tab.close should not throw
|
||||
tab.close(function() secondOnCloseCalled = true);
|
||||
try {
|
||||
tab.close(function () {
|
||||
test.assert(secondOnCloseCalled,
|
||||
assert.ok(secondOnCloseCalled,
|
||||
"The immediate second call to tab.close gots its callback fired");
|
||||
test.assertNotEqual(tabs.activeTab.url, url, "tab is no longer the active tab");
|
||||
test.done();
|
||||
assert.notEqual(tabs.activeTab.url, url, "tab is no longer the active tab");
|
||||
|
||||
done();
|
||||
});
|
||||
}
|
||||
catch(e) {
|
||||
test.fail("second call to tab.close() thrown an exception: " + e);
|
||||
assert.fail("second call to tab.close() thrown an exception: " + e);
|
||||
}
|
||||
test.assertNotEqual(tabs.activeTab.url, url, "tab is no longer the active tab");
|
||||
assert.notEqual(tabs.activeTab.url, url, "tab is no longer the active tab");
|
||||
});
|
||||
|
||||
tabs.open(url);
|
||||
};
|
||||
|
||||
// TEST: tab.move()
|
||||
exports.testTabMove = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testTabMove = function(assert, done) {
|
||||
open().then(focus).then(function(window) {
|
||||
let url = "data:text/html;charset=utf-8,foo";
|
||||
|
||||
tabs.open({
|
||||
url: url,
|
||||
onOpen: function(tab) {
|
||||
test.assertEqual(tab.index, 1, "tab index before move matches");
|
||||
assert.equal(tab.index, 1, "tab index before move matches");
|
||||
tab.index = 0;
|
||||
test.assertEqual(tab.index, 0, "tab index after move matches");
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
assert.equal(tab.index, 0, "tab index after move matches");
|
||||
close(window).then(done);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// TEST: open tab with default options
|
||||
exports.testOpen = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testOpen = function(assert, done) {
|
||||
let url = "data:text/html;charset=utf-8,default";
|
||||
tabs.open({
|
||||
url: url,
|
||||
onReady: function(tab) {
|
||||
test.assertEqual(tab.url, url, "URL of the new tab matches");
|
||||
test.assertEqual(tab.isPinned, false, "The new tab is not pinned");
|
||||
assert.equal(tab.url, url, "URL of the new tab matches");
|
||||
assert.equal(tab.isPinned, false, "The new tab is not pinned");
|
||||
|
||||
tab.close(function() test.done());
|
||||
tab.close(done);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// TEST: opening a pinned tab
|
||||
exports.testOpenPinned = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testOpenPinned = function(assert, done) {
|
||||
let url = "data:text/html;charset=utf-8,default";
|
||||
tabs.open({
|
||||
url: url,
|
||||
isPinned: true,
|
||||
onOpen: function(tab) {
|
||||
test.assertEqual(tab.isPinned, true, "The new tab is pinned");
|
||||
tab.close(test.done.bind(test));
|
||||
assert.equal(tab.isPinned, true, "The new tab is pinned");
|
||||
tab.close(done);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// TEST: pin/unpin opened tab
|
||||
exports.testPinUnpin = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testPinUnpin = function(assert, done) {
|
||||
let url = "data:text/html;charset=utf-8,default";
|
||||
tabs.open({
|
||||
url: url,
|
||||
inBackground: true,
|
||||
onOpen: function(tab) {
|
||||
tab.pin();
|
||||
test.assertEqual(tab.isPinned, true, "The tab was pinned correctly");
|
||||
assert.equal(tab.isPinned, true, "The tab was pinned correctly");
|
||||
tab.unpin();
|
||||
test.assertEqual(tab.isPinned, false, "The tab was unpinned correctly");
|
||||
tab.close(test.done.bind(test));
|
||||
assert.equal(tab.isPinned, false, "The tab was unpinned correctly");
|
||||
tab.close(done);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// TEST: open tab in background
|
||||
exports.testInBackground = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testInBackground = function(assert, done) {
|
||||
let window = getMostRecentBrowserWindow();
|
||||
let activeUrl = tabs.activeTab.url;
|
||||
let url = "data:text/html;charset=utf-8,background";
|
||||
test.assertEqual(activeWindow, window, "activeWindow matches this window");
|
||||
assert.equal(activeWindow, window, "activeWindow matches this window");
|
||||
tabs.on('ready', function onReady(tab) {
|
||||
tabs.removeListener('ready', onReady);
|
||||
test.assertEqual(tabs.activeTab.url, activeUrl, "URL of active tab has not changed");
|
||||
test.assertEqual(tab.url, url, "URL of the new background tab matches");
|
||||
test.assertEqual(activeWindow, window, "a new window was not opened");
|
||||
test.assertNotEqual(tabs.activeTab.url, url, "URL of active tab is not the new URL");
|
||||
tab.close(test.done.bind(test));
|
||||
assert.equal(tabs.activeTab.url, activeUrl, "URL of active tab has not changed");
|
||||
assert.equal(tab.url, url, "URL of the new background tab matches");
|
||||
assert.equal(activeWindow, window, "a new window was not opened");
|
||||
assert.notEqual(tabs.activeTab.url, url, "URL of active tab is not the new URL");
|
||||
tab.close(done);
|
||||
});
|
||||
|
||||
tabs.open({
|
||||
@ -375,9 +347,7 @@ exports.testInBackground = function(test) {
|
||||
}
|
||||
|
||||
// TEST: open tab in new window
|
||||
exports.testOpenInNewWindow = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testOpenInNewWindow = function(assert, done) {
|
||||
let startWindowCount = windows().length;
|
||||
|
||||
let url = "data:text/html;charset=utf-8,testOpenInNewWindow";
|
||||
@ -386,25 +356,23 @@ exports.testOpenInNewWindow = function(test) {
|
||||
inNewWindow: true,
|
||||
onReady: function(tab) {
|
||||
let newWindow = getOwnerWindow(tab);
|
||||
test.assertEqual(windows().length, startWindowCount + 1, "a new window was opened");
|
||||
assert.equal(windows().length, startWindowCount + 1, "a new window was opened");
|
||||
|
||||
onFocus(newWindow).then(function() {
|
||||
test.assertEqual(activeWindow, newWindow, "new window is active");
|
||||
test.assertEqual(tab.url, url, "URL of the new tab matches");
|
||||
test.assertEqual(newWindow.content.location, url, "URL of new tab in new window matches");
|
||||
test.assertEqual(tabs.activeTab.url, url, "URL of activeTab matches");
|
||||
assert.equal(activeWindow, newWindow, "new window is active");
|
||||
assert.equal(tab.url, url, "URL of the new tab matches");
|
||||
assert.equal(newWindow.content.location, url, "URL of new tab in new window matches");
|
||||
assert.equal(tabs.activeTab.url, url, "URL of activeTab matches");
|
||||
|
||||
closeBrowserWindow(newWindow, test.done.bind(test));
|
||||
}, test.fail).then(null, test.fail);
|
||||
close(newWindow).then(done);
|
||||
}, assert.fail).then(null, assert.fail);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Test tab.open inNewWindow + onOpen combination
|
||||
exports.testOpenInNewWindowOnOpen = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testOpenInNewWindowOnOpen = function(assert, done) {
|
||||
let startWindowCount = windows().length;
|
||||
|
||||
let url = "data:text/html;charset=utf-8,newwindow";
|
||||
@ -415,20 +383,17 @@ exports.testOpenInNewWindowOnOpen = function(test) {
|
||||
let newWindow = getOwnerWindow(tab);
|
||||
|
||||
onFocus(newWindow).then(function() {
|
||||
test.assertEqual(windows().length, startWindowCount + 1, "a new window was opened");
|
||||
test.assertEqual(activeWindow, newWindow, "new window is active");
|
||||
assert.equal(windows().length, startWindowCount + 1, "a new window was opened");
|
||||
assert.equal(activeWindow, newWindow, "new window is active");
|
||||
|
||||
closeBrowserWindow(newWindow, function() {
|
||||
test.done();
|
||||
});
|
||||
close(newWindow).then(done);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// TEST: onOpen event handler
|
||||
exports.testTabsEvent_onOpen = function(test) {
|
||||
test.waitUntilDone();
|
||||
exports.testTabsEvent_onOpen = function(assert, done) {
|
||||
openBrowserWindow(function(window, browser) {
|
||||
let url = "data:text/html;charset=utf-8,1";
|
||||
let eventCount = 0;
|
||||
@ -441,10 +406,10 @@ exports.testTabsEvent_onOpen = function(test) {
|
||||
|
||||
// add listener via collection add
|
||||
tabs.on('open', function listener2(tab) {
|
||||
test.assertEqual(++eventCount, 2, "both listeners notified");
|
||||
assert.equal(++eventCount, 2, "both listeners notified");
|
||||
tabs.removeListener('open', listener1);
|
||||
tabs.removeListener('open', listener2);
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
close(window).then(done);
|
||||
});
|
||||
|
||||
tabs.open(url);
|
||||
@ -452,8 +417,7 @@ exports.testTabsEvent_onOpen = function(test) {
|
||||
};
|
||||
|
||||
// TEST: onClose event handler
|
||||
exports.testTabsEvent_onClose = function(test) {
|
||||
test.waitUntilDone();
|
||||
exports.testTabsEvent_onClose = function(assert, done) {
|
||||
openBrowserWindow(function(window, browser) {
|
||||
let url = "data:text/html;charset=utf-8,onclose";
|
||||
let eventCount = 0;
|
||||
@ -466,10 +430,10 @@ exports.testTabsEvent_onClose = function(test) {
|
||||
|
||||
// add listener via collection add
|
||||
tabs.on('close', function listener2(tab) {
|
||||
test.assertEqual(++eventCount, 2, "both listeners notified");
|
||||
assert.equal(++eventCount, 2, "both listeners notified");
|
||||
tabs.removeListener('close', listener1);
|
||||
tabs.removeListener('close', listener2);
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
close(window).then(done);
|
||||
});
|
||||
|
||||
tabs.on('ready', function onReady(tab) {
|
||||
@ -482,8 +446,7 @@ exports.testTabsEvent_onClose = function(test) {
|
||||
};
|
||||
|
||||
// TEST: onClose event handler when a window is closed
|
||||
exports.testTabsEvent_onCloseWindow = function(test) {
|
||||
test.waitUntilDone();
|
||||
exports.testTabsEvent_onCloseWindow = function(assert, done) {
|
||||
let closeCount = 0;
|
||||
let individualCloseCount = 0;
|
||||
|
||||
@ -499,12 +462,12 @@ exports.testTabsEvent_onCloseWindow = function(test) {
|
||||
return;
|
||||
}
|
||||
|
||||
test.assertEqual(closeCount, 4, "Correct number of close events received");
|
||||
test.assertEqual(individualCloseCount, 3,
|
||||
"Each tab with an attached onClose listener received a close " +
|
||||
"event when the window was closed");
|
||||
assert.equal(closeCount, 4, "Correct number of close events received");
|
||||
assert.equal(individualCloseCount, 3,
|
||||
"Each tab with an attached onClose listener received a close " +
|
||||
"event when the window was closed");
|
||||
|
||||
test.done();
|
||||
done();
|
||||
}
|
||||
|
||||
// One tab is already open with the window
|
||||
@ -536,8 +499,7 @@ exports.testTabsEvent_onCloseWindow = function(test) {
|
||||
}
|
||||
|
||||
// TEST: onReady event handler
|
||||
exports.testTabsEvent_onReady = function(test) {
|
||||
test.waitUntilDone();
|
||||
exports.testTabsEvent_onReady = function(assert, done) {
|
||||
openBrowserWindow(function(window, browser) {
|
||||
let url = "data:text/html;charset=utf-8,onready";
|
||||
let eventCount = 0;
|
||||
@ -550,10 +512,10 @@ exports.testTabsEvent_onReady = function(test) {
|
||||
|
||||
// add listener via collection add
|
||||
tabs.on('ready', function listener2(tab) {
|
||||
test.assertEqual(++eventCount, 2, "both listeners notified");
|
||||
assert.equal(++eventCount, 2, "both listeners notified");
|
||||
tabs.removeListener('ready', listener1);
|
||||
tabs.removeListener('ready', listener2);
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
close(window).then(done);
|
||||
});
|
||||
|
||||
tabs.open(url);
|
||||
@ -561,8 +523,7 @@ exports.testTabsEvent_onReady = function(test) {
|
||||
};
|
||||
|
||||
// TEST: onActivate event handler
|
||||
exports.testTabsEvent_onActivate = function(test) {
|
||||
test.waitUntilDone();
|
||||
exports.testTabsEvent_onActivate = function(assert, done) {
|
||||
openBrowserWindow(function(window, browser) {
|
||||
let url = "data:text/html;charset=utf-8,onactivate";
|
||||
let eventCount = 0;
|
||||
@ -575,10 +536,10 @@ exports.testTabsEvent_onActivate = function(test) {
|
||||
|
||||
// add listener via collection add
|
||||
tabs.on('activate', function listener2(tab) {
|
||||
test.assertEqual(++eventCount, 2, "both listeners notified");
|
||||
assert.equal(++eventCount, 2, "both listeners notified");
|
||||
tabs.removeListener('activate', listener1);
|
||||
tabs.removeListener('activate', listener2);
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
close(window).then(done);
|
||||
});
|
||||
|
||||
tabs.open(url);
|
||||
@ -586,8 +547,7 @@ exports.testTabsEvent_onActivate = function(test) {
|
||||
};
|
||||
|
||||
// onDeactivate event handler
|
||||
exports.testTabsEvent_onDeactivate = function(test) {
|
||||
test.waitUntilDone();
|
||||
exports.testTabsEvent_onDeactivate = function(assert, done) {
|
||||
openBrowserWindow(function(window, browser) {
|
||||
let url = "data:text/html;charset=utf-8,ondeactivate";
|
||||
let eventCount = 0;
|
||||
@ -600,10 +560,10 @@ exports.testTabsEvent_onDeactivate = function(test) {
|
||||
|
||||
// add listener via collection add
|
||||
tabs.on('deactivate', function listener2(tab) {
|
||||
test.assertEqual(++eventCount, 2, "both listeners notified");
|
||||
assert.equal(++eventCount, 2, "both listeners notified");
|
||||
tabs.removeListener('deactivate', listener1);
|
||||
tabs.removeListener('deactivate', listener2);
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
close(window).then(done);
|
||||
});
|
||||
|
||||
tabs.on('open', function onOpen(tab) {
|
||||
@ -616,8 +576,7 @@ exports.testTabsEvent_onDeactivate = function(test) {
|
||||
};
|
||||
|
||||
// pinning
|
||||
exports.testTabsEvent_pinning = function(test) {
|
||||
test.waitUntilDone();
|
||||
exports.testTabsEvent_pinning = function(assert, done) {
|
||||
openBrowserWindow(function(window, browser) {
|
||||
let url = "data:text/html;charset=utf-8,1";
|
||||
|
||||
@ -628,14 +587,14 @@ exports.testTabsEvent_pinning = function(test) {
|
||||
|
||||
tabs.on('pinned', function onPinned(tab) {
|
||||
tabs.removeListener('pinned', onPinned);
|
||||
test.assert(tab.isPinned, "notified tab is pinned");
|
||||
assert.ok(tab.isPinned, "notified tab is pinned");
|
||||
tab.unpin();
|
||||
});
|
||||
|
||||
tabs.on('unpinned', function onUnpinned(tab) {
|
||||
tabs.removeListener('unpinned', onUnpinned);
|
||||
test.assert(!tab.isPinned, "notified tab is not pinned");
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
assert.ok(!tab.isPinned, "notified tab is not pinned");
|
||||
close(window).then(done);
|
||||
});
|
||||
|
||||
tabs.open(url);
|
||||
@ -643,8 +602,7 @@ exports.testTabsEvent_pinning = function(test) {
|
||||
};
|
||||
|
||||
// TEST: per-tab event handlers
|
||||
exports.testPerTabEvents = function(test) {
|
||||
test.waitUntilDone();
|
||||
exports.testPerTabEvents = function(assert, done) {
|
||||
openBrowserWindow(function(window, browser) {
|
||||
let eventCount = 0;
|
||||
|
||||
@ -659,19 +617,18 @@ exports.testPerTabEvents = function(test) {
|
||||
|
||||
// add listener via collection add
|
||||
tab.on('ready', function listener2() {
|
||||
test.assertEqual(eventCount, 1, "both listeners notified");
|
||||
assert.equal(eventCount, 1, "both listeners notified");
|
||||
tab.removeListener('ready', listener1);
|
||||
tab.removeListener('ready', listener2);
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
close(window).then(done);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
exports.testAttachOnOpen = function (test) {
|
||||
exports.testAttachOnOpen = function (assert, done) {
|
||||
// Take care that attach has to be called on tab ready and not on tab open.
|
||||
test.waitUntilDone();
|
||||
openBrowserWindow(function(window, browser) {
|
||||
tabs.open({
|
||||
url: "data:text/html;charset=utf-8,foobar",
|
||||
@ -679,10 +636,10 @@ exports.testAttachOnOpen = function (test) {
|
||||
let worker = tab.attach({
|
||||
contentScript: 'self.postMessage(document.location.href); ',
|
||||
onMessage: function (msg) {
|
||||
test.assertEqual(msg, "about:blank",
|
||||
assert.equal(msg, "about:blank",
|
||||
"Worker document url is about:blank on open");
|
||||
worker.destroy();
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
close(window).then(done);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -691,9 +648,8 @@ exports.testAttachOnOpen = function (test) {
|
||||
});
|
||||
}
|
||||
|
||||
exports.testAttachOnMultipleDocuments = function (test) {
|
||||
exports.testAttachOnMultipleDocuments = function (assert, done) {
|
||||
// Example of attach that process multiple tab documents
|
||||
test.waitUntilDone();
|
||||
openBrowserWindow(function(window, browser) {
|
||||
let firstLocation = "data:text/html;charset=utf-8,foobar";
|
||||
let secondLocation = "data:text/html;charset=utf-8,bar";
|
||||
@ -702,6 +658,7 @@ exports.testAttachOnMultipleDocuments = function (test) {
|
||||
let worker1 = null;
|
||||
let worker2 = null;
|
||||
let detachEventCount = 0;
|
||||
|
||||
tabs.open({
|
||||
url: firstLocation,
|
||||
onReady: function (tab) {
|
||||
@ -712,14 +669,14 @@ exports.testAttachOnMultipleDocuments = function (test) {
|
||||
' function () self.postMessage(document.location.href)' +
|
||||
');',
|
||||
onMessage: function (msg) {
|
||||
test.assertEqual(msg, firstLocation,
|
||||
assert.equal(msg, firstLocation,
|
||||
"Worker url is equal to the 1st document");
|
||||
tab.url = secondLocation;
|
||||
},
|
||||
onDetach: function () {
|
||||
detachEventCount++;
|
||||
test.pass("Got worker1 detach event");
|
||||
test.assertRaises(function () {
|
||||
assert.pass("Got worker1 detach event");
|
||||
assert.throws(function () {
|
||||
worker1.postMessage("ex-1");
|
||||
},
|
||||
/Couldn't find the worker/,
|
||||
@ -736,14 +693,14 @@ exports.testAttachOnMultipleDocuments = function (test) {
|
||||
' function () self.postMessage(document.location.href)' +
|
||||
');',
|
||||
onMessage: function (msg) {
|
||||
test.assertEqual(msg, secondLocation,
|
||||
assert.equal(msg, secondLocation,
|
||||
"Worker url is equal to the 2nd document");
|
||||
tab.url = thirdLocation;
|
||||
},
|
||||
onDetach: function () {
|
||||
detachEventCount++;
|
||||
test.pass("Got worker2 detach event");
|
||||
test.assertRaises(function () {
|
||||
assert.pass("Got worker2 detach event");
|
||||
assert.throws(function () {
|
||||
worker2.postMessage("ex-2");
|
||||
},
|
||||
/Couldn't find the worker/,
|
||||
@ -763,18 +720,17 @@ exports.testAttachOnMultipleDocuments = function (test) {
|
||||
if (detachEventCount != 2)
|
||||
return;
|
||||
|
||||
test.pass("Got all detach events");
|
||||
assert.pass("Got all detach events");
|
||||
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
close(window).then(done);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
exports.testAttachWrappers = function (test) {
|
||||
exports.testAttachWrappers = function (assert, done) {
|
||||
// Check that content script has access to wrapped values by default
|
||||
test.waitUntilDone();
|
||||
openBrowserWindow(function(window, browser) {
|
||||
let document = "data:text/html;charset=utf-8,<script>var globalJSVar = true; " +
|
||||
" document.getElementById = 3;</script>";
|
||||
@ -791,9 +747,9 @@ exports.testAttachWrappers = function (test) {
|
||||
' self.postMessage(e.message);' +
|
||||
'}',
|
||||
onMessage: function (msg) {
|
||||
test.assertEqual(msg, true, "Worker has wrapped objects ("+count+")");
|
||||
assert.equal(msg, true, "Worker has wrapped objects ("+count+")");
|
||||
if (count++ == 1)
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
close(window).then(done);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -805,9 +761,8 @@ exports.testAttachWrappers = function (test) {
|
||||
/*
|
||||
// We do not offer unwrapped access to DOM since bug 601295 landed
|
||||
// See 660780 to track progress of unwrap feature
|
||||
exports.testAttachUnwrapped = function (test) {
|
||||
exports.testAttachUnwrapped = function (assert, done) {
|
||||
// Check that content script has access to unwrapped values through unsafeWindow
|
||||
test.waitUntilDone();
|
||||
openBrowserWindow(function(window, browser) {
|
||||
let document = "data:text/html;charset=utf-8,<script>var globalJSVar=true;</script>";
|
||||
let count = 0;
|
||||
@ -822,8 +777,8 @@ exports.testAttachUnwrapped = function (test) {
|
||||
' self.postMessage(e.message);' +
|
||||
'}',
|
||||
onMessage: function (msg) {
|
||||
test.assertEqual(msg, true, "Worker has access to javascript content globals ("+count+")");
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
assert.equal(msg, true, "Worker has access to javascript content globals ("+count+")");
|
||||
close(window).then(done);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -833,27 +788,25 @@ exports.testAttachUnwrapped = function (test) {
|
||||
}
|
||||
*/
|
||||
|
||||
exports['test window focus changes active tab'] = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports['test window focus changes active tab'] = function(assert, done) {
|
||||
let url1 = "data:text/html;charset=utf-8," + encodeURIComponent("test window focus changes active tab</br><h1>Window #1");
|
||||
|
||||
let win1 = openBrowserWindow(function() {
|
||||
test.pass("window 1 is open");
|
||||
assert.pass("window 1 is open");
|
||||
|
||||
let win2 = openBrowserWindow(function() {
|
||||
test.pass("window 2 is open");
|
||||
assert.pass("window 2 is open");
|
||||
|
||||
focus(win2).then(function() {
|
||||
tabs.on("activate", function onActivate(tab) {
|
||||
tabs.removeListener("activate", onActivate);
|
||||
test.pass("activate was called on windows focus change.");
|
||||
test.assertEqual(tab.url, url1, 'the activated tab url is correct');
|
||||
assert.pass("activate was called on windows focus change.");
|
||||
assert.equal(tab.url, url1, 'the activated tab url is correct');
|
||||
|
||||
close(win2).then(function() {
|
||||
test.pass('window 2 was closed');
|
||||
assert.pass('window 2 was closed');
|
||||
return close(win1);
|
||||
}).then(test.done.bind(test));
|
||||
}).then(done);
|
||||
});
|
||||
|
||||
win1.focus();
|
||||
@ -862,24 +815,21 @@ exports['test window focus changes active tab'] = function(test) {
|
||||
}, url1);
|
||||
};
|
||||
|
||||
exports['test ready event on new window tab'] = function(test) {
|
||||
test.waitUntilDone();
|
||||
exports['test ready event on new window tab'] = function(assert, done) {
|
||||
let uri = encodeURI("data:text/html;charset=utf-8,Waiting for ready event!");
|
||||
|
||||
require("sdk/tabs").on("ready", function onReady(tab) {
|
||||
if (tab.url === uri) {
|
||||
require("sdk/tabs").removeListener("ready", onReady);
|
||||
test.pass("ready event was emitted");
|
||||
closeBrowserWindow(window, function() {
|
||||
test.done();
|
||||
});
|
||||
assert.pass("ready event was emitted");
|
||||
close(window).then(done);
|
||||
}
|
||||
});
|
||||
|
||||
let window = openBrowserWindow(function(){}, uri);
|
||||
};
|
||||
|
||||
exports['test unique tab ids'] = function(test) {
|
||||
exports['test unique tab ids'] = function(assert, done) {
|
||||
var windows = require('sdk/windows').browserWindows;
|
||||
var { all, defer } = require('sdk/core/promise');
|
||||
|
||||
@ -891,9 +841,9 @@ exports['test unique tab ids'] = function(test) {
|
||||
});
|
||||
|
||||
win.on('open', function(window) {
|
||||
test.assert(window.tabs.length);
|
||||
test.assert(window.tabs.activeTab);
|
||||
test.assert(window.tabs.activeTab.id);
|
||||
assert.ok(window.tabs.length);
|
||||
assert.ok(window.tabs.activeTab);
|
||||
assert.ok(window.tabs.activeTab.id);
|
||||
deferred.resolve({
|
||||
id: window.tabs.activeTab.id,
|
||||
win: win
|
||||
@ -903,32 +853,29 @@ exports['test unique tab ids'] = function(test) {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
test.waitUntilDone();
|
||||
var one = openWindow(), two = openWindow();
|
||||
all([one, two]).then(function(results) {
|
||||
test.assertNotEqual(results[0].id, results[1].id, "tab Ids should not be equal.");
|
||||
assert.notEqual(results[0].id, results[1].id, "tab Ids should not be equal.");
|
||||
results[0].win.close();
|
||||
results[1].win.close();
|
||||
test.done();
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
// related to Bug 671305
|
||||
exports.testOnLoadEventWithDOM = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testOnLoadEventWithDOM = function(assert, done) {
|
||||
openBrowserWindow(function(window, browser) {
|
||||
let count = 0;
|
||||
tabs.on('load', function onLoad(tab) {
|
||||
test.assertEqual(tab.title, 'tab', 'tab passed in as arg, load called');
|
||||
assert.equal(tab.title, 'tab', 'tab passed in as arg, load called');
|
||||
if (!count++) {
|
||||
tab.reload();
|
||||
}
|
||||
else {
|
||||
// end of test
|
||||
tabs.removeListener('load', onLoad);
|
||||
test.pass('onLoad event called on reload');
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
assert.pass('onLoad event called on reload');
|
||||
close(window).then(done);
|
||||
}
|
||||
});
|
||||
|
||||
@ -941,9 +888,7 @@ exports.testOnLoadEventWithDOM = function(test) {
|
||||
};
|
||||
|
||||
// related to Bug 671305
|
||||
exports.testOnLoadEventWithImage = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
exports.testOnLoadEventWithImage = function(assert, done) {
|
||||
openBrowserWindow(function(window, browser) {
|
||||
let count = 0;
|
||||
tabs.on('load', function onLoad(tab) {
|
||||
@ -953,8 +898,8 @@ exports.testOnLoadEventWithImage = function(test) {
|
||||
else {
|
||||
// end of test
|
||||
tabs.removeListener('load', onLoad);
|
||||
test.pass('onLoad event called on reload with image');
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
assert.pass('onLoad event called on reload with image');
|
||||
close(window).then(done);
|
||||
}
|
||||
});
|
||||
|
||||
@ -966,23 +911,22 @@ exports.testOnLoadEventWithImage = function(test) {
|
||||
});
|
||||
};
|
||||
|
||||
exports.testFaviconGetterDeprecation = function (test) {
|
||||
exports.testFaviconGetterDeprecation = function (assert, done) {
|
||||
const { LoaderWithHookedConsole } = require("sdk/test/loader");
|
||||
let { loader, messages } = LoaderWithHookedConsole(module);
|
||||
let tabs = loader.require('sdk/tabs');
|
||||
test.waitUntilDone();
|
||||
|
||||
tabs.open({
|
||||
url: 'data:text/html;charset=utf-8,',
|
||||
onOpen: function (tab) {
|
||||
let favicon = tab.favicon;
|
||||
test.assert(messages.length === 1, 'only one error is dispatched');
|
||||
test.assert(messages[0].type, 'error', 'the console message is an error');
|
||||
assert.ok(messages.length === 1, 'only one error is dispatched');
|
||||
assert.ok(messages[0].type, 'error', 'the console message is an error');
|
||||
|
||||
let msg = messages[0].msg;
|
||||
test.assert(msg.indexOf('tab.favicon is deprecated') !== -1,
|
||||
assert.ok(msg.indexOf('tab.favicon is deprecated') !== -1,
|
||||
'message contains the given message');
|
||||
tab.close(test.done.bind(test));
|
||||
tab.close(done);
|
||||
loader.unload();
|
||||
}
|
||||
});
|
||||
@ -1027,11 +971,4 @@ function openBrowserWindow(callback, url) {
|
||||
return window;
|
||||
}
|
||||
|
||||
// Helper for calling code at window close
|
||||
function closeBrowserWindow(window, callback) {
|
||||
window.addEventListener("unload", function unload() {
|
||||
window.removeEventListener("unload", unload, false);
|
||||
callback();
|
||||
}, false);
|
||||
window.close();
|
||||
}
|
||||
require('sdk/test').run(exports);
|
||||
|
@ -1,7 +1,6 @@
|
||||
/* 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 { Loader } = require("sdk/test/loader");
|
||||
|
@ -132,26 +132,27 @@ exports["test Document Reload"] = function(assert, done) {
|
||||
let url2 = "data:text/html;charset=utf-8,page2";
|
||||
let content =
|
||||
"<script>" +
|
||||
"window.onload = function() {" +
|
||||
" setTimeout(function () {" +
|
||||
" window.location = '" + url2 + "';" +
|
||||
" }, 0);" +
|
||||
"}" +
|
||||
"window.addEventListener('message', function() {"+
|
||||
" window.location = '" + url2 + "';" +
|
||||
'}, false);' +
|
||||
"</script>";
|
||||
let messageCount = 0;
|
||||
let panel = Panel({
|
||||
// using URL here is intentional, see bug 859009
|
||||
contentURL: URL("data:text/html;charset=utf-8," + encodeURIComponent(content)),
|
||||
contentScript: "self.postMessage(window.location.href)",
|
||||
contentScript: "self.postMessage(window.location.href);" +
|
||||
// initiate change to url2
|
||||
"self.port.once('move', function() document.defaultView.postMessage('move', '*'));",
|
||||
onMessage: function (message) {
|
||||
messageCount++;
|
||||
assert.notEqual(message, 'about:blank', 'about:blank is not a message ' + messageCount);
|
||||
assert.notEqual(message, "about:blank", "about:blank is not a message " + messageCount);
|
||||
|
||||
if (messageCount == 1) {
|
||||
assert.ok(/data:text\/html/.test(message), "First document had a content script " + message);
|
||||
assert.ok(/data:text\/html/.test(message), "First document had a content script; " + message);
|
||||
panel.port.emit('move');
|
||||
}
|
||||
else if (messageCount == 2) {
|
||||
assert.equal(message, url2, "Second document too");
|
||||
assert.equal(message, url2, "Second document too; " + message);
|
||||
panel.destroy();
|
||||
done();
|
||||
}
|
||||
|
@ -87,6 +87,9 @@ exports.testSearchURL = function (assert, done) {
|
||||
});
|
||||
};
|
||||
|
||||
// Disabling due to intermittent Bug 892619
|
||||
// TODO solve this
|
||||
/*
|
||||
exports.testSearchTimeRange = function (assert, done) {
|
||||
let firstTime, secondTime;
|
||||
addVisits([
|
||||
@ -120,7 +123,7 @@ exports.testSearchTimeRange = function (assert, done) {
|
||||
done();
|
||||
});
|
||||
};
|
||||
|
||||
*/
|
||||
exports.testSearchQuery = function (assert, done) {
|
||||
addVisits([
|
||||
'http://mozilla.com', 'http://webaud.io', 'http://mozilla.com/webfwd'
|
||||
|
@ -3,21 +3,18 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
'use strict';
|
||||
|
||||
const app = require("sdk/system/xul-app");
|
||||
module.metadata = {
|
||||
'engines': {
|
||||
'Firefox': '*',
|
||||
'Fennec': '*'
|
||||
}
|
||||
};
|
||||
|
||||
if (app.is("Firefox")) {
|
||||
module.exports = require("./tabs/test-firefox-tabs");
|
||||
}
|
||||
else if (app.is("Fennec")) {
|
||||
module.exports = require("./tabs/test-fennec-tabs");
|
||||
const app = require('sdk/system/xul-app');
|
||||
|
||||
if (app.is('Fennec')) {
|
||||
module.exports = require('./tabs/test-fennec-tabs');
|
||||
}
|
||||
else {
|
||||
require("test").run({
|
||||
"test Unsupported Application": function Unsupported (assert) {
|
||||
assert.pass(
|
||||
"The tabs module currently supports only Firefox and Fennec." +
|
||||
"In the future we would like it to support other applications, however."
|
||||
);
|
||||
}
|
||||
});
|
||||
module.exports = require('./tabs/test-firefox-tabs');
|
||||
}
|
||||
|
@ -57,4 +57,4 @@ exports["test LoaderWithHookedConsole"] = function (assert) {
|
||||
assert.equal(count, 6, "Called for all messages");
|
||||
};
|
||||
|
||||
require("test").run(exports);
|
||||
require("sdk/test").run(exports);
|
||||
|
998
addon-sdk/source/test/test-ui-button.js
Normal file
998
addon-sdk/source/test/test-ui-button.js
Normal file
@ -0,0 +1,998 @@
|
||||
/* 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';
|
||||
|
||||
module.metadata = {
|
||||
'engines': {
|
||||
'Firefox': '> 24'
|
||||
}
|
||||
};
|
||||
|
||||
const { Cu } = require('chrome');
|
||||
const { Loader } = require('sdk/test/loader');
|
||||
const { data } = require('sdk/self');
|
||||
const { open, focus, close } = require('sdk/window/helpers');
|
||||
const { setTimeout } = require('sdk/timers');
|
||||
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
|
||||
|
||||
function getWidget(buttonId, window = getMostRecentBrowserWindow()) {
|
||||
const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
|
||||
const { AREA_NAVBAR } = CustomizableUI;
|
||||
|
||||
let widgets = CustomizableUI.getWidgetsInArea(AREA_NAVBAR).
|
||||
filter(({id}) => id.startsWith('button--') && id.endsWith(buttonId));
|
||||
|
||||
if (widgets.length === 0)
|
||||
throw new Error('Widget with id `' + id +'` not found.');
|
||||
|
||||
if (widgets.length > 1)
|
||||
throw new Error('Unexpected number of widgets: ' + widgets.length)
|
||||
|
||||
return widgets[0].forWindow(window);
|
||||
};
|
||||
|
||||
exports['test basic constructor validation'] = function(assert) {
|
||||
let loader = Loader(module);
|
||||
let { Button } = loader.require('sdk/ui');
|
||||
|
||||
assert.throws(
|
||||
() => Button({}),
|
||||
/^The option/,
|
||||
'throws on no option given');
|
||||
|
||||
// Test no label
|
||||
assert.throws(
|
||||
() => Button({ id: 'my-button', icon: './icon.png'}),
|
||||
/^The option "label"/,
|
||||
'throws on no label given');
|
||||
|
||||
// Test no id
|
||||
assert.throws(
|
||||
() => Button({ label: 'my button', icon: './icon.png' }),
|
||||
/^The option "id"/,
|
||||
'throws on no id given');
|
||||
|
||||
// Test no icon
|
||||
assert.throws(
|
||||
() => Button({ id: 'my-button', label: 'my button' }),
|
||||
/^The option "icon"/,
|
||||
'throws on no icon given');
|
||||
|
||||
|
||||
// Test empty label
|
||||
assert.throws(
|
||||
() => Button({ id: 'my-button', label: '', icon: './icon.png' }),
|
||||
/^The option "label"/,
|
||||
'throws on no valid label given');
|
||||
|
||||
// Test invalid id
|
||||
assert.throws(
|
||||
() => Button({ id: 'my button', label: 'my button', icon: './icon.png' }),
|
||||
/^The option "id"/,
|
||||
'throws on no valid id given');
|
||||
|
||||
// Test empty id
|
||||
assert.throws(
|
||||
() => Button({ id: '', label: 'my button', icon: './icon.png' }),
|
||||
/^The option "id"/,
|
||||
'throws on no valid id given');
|
||||
|
||||
// Test remote icon
|
||||
assert.throws(
|
||||
() => Button({ id: 'my-button', label: 'my button', icon: 'http://www.mozilla.org/favicon.ico'}),
|
||||
/^The option "icon"/,
|
||||
'throws on no valid icon given');
|
||||
|
||||
// Test wrong icon: no absolute URI to local resource, neither relative './'
|
||||
assert.throws(
|
||||
() => Button({ id: 'my-button', label: 'my button', icon: 'icon.png'}),
|
||||
/^The option "icon"/,
|
||||
'throws on no valid icon given');
|
||||
|
||||
// Test wrong icon: no absolute URI to local resource, neither relative './'
|
||||
assert.throws(
|
||||
() => Button({ id: 'my-button', label: 'my button', icon: 'foo and bar'}),
|
||||
/^The option "icon"/,
|
||||
'throws on no valid icon given');
|
||||
|
||||
// Test wrong icon: '../' is not allowed
|
||||
assert.throws(
|
||||
() => Button({ id: 'my-button', label: 'my button', icon: '../icon.png'}),
|
||||
/^The option "icon"/,
|
||||
'throws on no valid icon given');
|
||||
|
||||
// Test wrong size: number
|
||||
assert.throws(
|
||||
() => Button({
|
||||
id:'my-button',
|
||||
label: 'my button',
|
||||
icon: './icon.png',
|
||||
size: 32
|
||||
}),
|
||||
/^The option "size"/,
|
||||
'throws on no valid size given');
|
||||
|
||||
// Test wrong size: string
|
||||
assert.throws(
|
||||
() => Button({
|
||||
id:'my-button',
|
||||
label: 'my button',
|
||||
icon: './icon.png',
|
||||
size: 'huge'
|
||||
}),
|
||||
/^The option "size"/,
|
||||
'throws on no valid size given');
|
||||
|
||||
// Test wrong type
|
||||
assert.throws(
|
||||
() => Button({
|
||||
id:'my-button',
|
||||
label: 'my button',
|
||||
icon: './icon.png',
|
||||
type: 'custom'
|
||||
}),
|
||||
/^The option "type"/,
|
||||
'throws on no valid type given');
|
||||
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
exports['test button added'] = function(assert) {
|
||||
let loader = Loader(module);
|
||||
let { Button } = loader.require('sdk/ui');
|
||||
|
||||
let button = Button({
|
||||
id: 'my-button-1',
|
||||
label: 'my button',
|
||||
icon: './icon.png'
|
||||
});
|
||||
|
||||
// check defaults
|
||||
assert.equal(button.size, 'small',
|
||||
'size is set to default "small" value');
|
||||
|
||||
assert.equal(button.disabled, false,
|
||||
'disabled is set to default `false` value');
|
||||
|
||||
assert.equal(button.checked, false,
|
||||
'checked is set to default `false` value');
|
||||
|
||||
assert.equal(button.type, 'button',
|
||||
'type is set to default "button" value');
|
||||
|
||||
let { node } = getWidget(button.id);
|
||||
|
||||
assert.ok(!!node, 'The button is in the navbar');
|
||||
|
||||
assert.equal(button.label, node.getAttribute('label'),
|
||||
'label is set');
|
||||
|
||||
assert.equal(button.label, node.getAttribute('tooltiptext'),
|
||||
'tooltip is set');
|
||||
|
||||
assert.equal(data.url(button.icon.substr(2)), node.getAttribute('image'),
|
||||
'icon is set');
|
||||
|
||||
assert.equal(button.type, node.getAttribute('type'),
|
||||
'type is set to default');
|
||||
|
||||
assert.equal(16, node.getAttribute('width'),
|
||||
'width is set to small');
|
||||
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
exports['test button added with resource URI'] = function(assert) {
|
||||
let loader = Loader(module);
|
||||
let { Button } = loader.require('sdk/ui');
|
||||
|
||||
let button = Button({
|
||||
id: 'my-button-1',
|
||||
label: 'my button',
|
||||
icon: data.url('icon.png')
|
||||
});
|
||||
|
||||
assert.equal(button.icon, data.url('icon.png'),
|
||||
'icon is set');
|
||||
|
||||
let { node } = getWidget(button.id);
|
||||
|
||||
assert.equal(button.icon, node.getAttribute('image'),
|
||||
'icon on node is set');
|
||||
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
exports['test button duplicate id'] = function(assert) {
|
||||
let loader = Loader(module);
|
||||
let { Button } = loader.require('sdk/ui');
|
||||
|
||||
let button = Button({
|
||||
id: 'my-button-2',
|
||||
label: 'my button',
|
||||
icon: './icon.png'
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
let doppelganger = Button({
|
||||
id: 'my-button-2',
|
||||
label: 'my button',
|
||||
icon: './icon.png'
|
||||
});
|
||||
},
|
||||
/^The ID/,
|
||||
'No duplicates allowed');
|
||||
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
exports['test button multiple destroy'] = function(assert) {
|
||||
let loader = Loader(module);
|
||||
let { Button } = loader.require('sdk/ui');
|
||||
|
||||
let button = Button({
|
||||
id: 'my-button-2',
|
||||
label: 'my button',
|
||||
icon: './icon.png'
|
||||
});
|
||||
|
||||
button.destroy();
|
||||
button.destroy();
|
||||
button.destroy();
|
||||
|
||||
assert.pass('multiple destroy doesn\'t matter');
|
||||
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
exports['test button removed on dispose'] = function(assert, done) {
|
||||
const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
|
||||
let loader = Loader(module);
|
||||
let { Button } = loader.require('sdk/ui');
|
||||
|
||||
let widgetId;
|
||||
|
||||
CustomizableUI.addListener({
|
||||
onWidgetDestroyed: function(id) {
|
||||
if (id === widgetId) {
|
||||
CustomizableUI.removeListener(this);
|
||||
|
||||
assert.pass('button properly removed');
|
||||
loader.unload();
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let button = Button({
|
||||
id: 'my-button-3',
|
||||
label: 'my button',
|
||||
icon: './icon.png'
|
||||
});
|
||||
|
||||
// Tried to use `getWidgetIdsInArea` but seems undefined, not sure if it
|
||||
// was removed or it's not in the UX build yet
|
||||
widgetId = getWidget(button.id).id;
|
||||
|
||||
button.destroy();
|
||||
};
|
||||
|
||||
exports['test button global state updated'] = function(assert) {
|
||||
let loader = Loader(module);
|
||||
let { Button } = loader.require('sdk/ui');
|
||||
|
||||
let button = Button({
|
||||
id: 'my-button-4',
|
||||
label: 'my button',
|
||||
icon: './icon.png'
|
||||
});
|
||||
|
||||
// Tried to use `getWidgetIdsInArea` but seems undefined, not sure if it
|
||||
// was removed or it's not in the UX build yet
|
||||
|
||||
let { node, id: widgetId } = getWidget(button.id);
|
||||
|
||||
// check read-only properties
|
||||
|
||||
assert.throws(() => button.id = 'another-id',
|
||||
/^setting a property that has only a getter/,
|
||||
'id cannot be set at runtime');
|
||||
|
||||
assert.equal(button.id, 'my-button-4',
|
||||
'id is unchanged');
|
||||
assert.equal(node.id, widgetId,
|
||||
'node id is unchanged');
|
||||
|
||||
assert.throws(() => button.type = 'checkbox',
|
||||
/^setting a property that has only a getter/,
|
||||
'type cannot be set at runtime');
|
||||
|
||||
assert.equal(button.type, 'button',
|
||||
'type is unchanged');
|
||||
assert.equal(node.getAttribute('type'), button.type,
|
||||
'node type is unchanged');
|
||||
|
||||
assert.throws(() => button.size = 'medium',
|
||||
/^setting a property that has only a getter/,
|
||||
'size cannot be set at runtime');
|
||||
|
||||
assert.equal(button.size, 'small',
|
||||
'size is unchanged');
|
||||
assert.equal(node.getAttribute('width'), 16,
|
||||
'node width is unchanged');
|
||||
|
||||
// check writable properties
|
||||
|
||||
button.label = 'New label';
|
||||
assert.equal(button.label, 'New label',
|
||||
'label is updated');
|
||||
assert.equal(node.getAttribute('label'), 'New label',
|
||||
'node label is updated');
|
||||
assert.equal(node.getAttribute('tooltiptext'), 'New label',
|
||||
'node tooltip is updated');
|
||||
|
||||
button.icon = './new-icon.png';
|
||||
assert.equal(button.icon, './new-icon.png',
|
||||
'icon is updated');
|
||||
assert.equal(node.getAttribute('image'), data.url('new-icon.png'),
|
||||
'node image is updated');
|
||||
|
||||
button.disabled = true;
|
||||
assert.equal(button.disabled, true,
|
||||
'disabled is updated');
|
||||
assert.equal(node.getAttribute('disabled'), 'true',
|
||||
'node disabled is updated');
|
||||
|
||||
// TODO: test validation on update
|
||||
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
exports['test button global state updated on multiple windows'] = function(assert, done) {
|
||||
let loader = Loader(module);
|
||||
let { Button } = loader.require('sdk/ui');
|
||||
|
||||
let button = Button({
|
||||
id: 'my-button-5',
|
||||
label: 'my button',
|
||||
icon: './icon.png'
|
||||
});
|
||||
|
||||
let nodes = [getWidget(button.id).node];
|
||||
|
||||
open(null, { features: { toolbar: true }}).then(window => {
|
||||
nodes.push(getWidget(button.id, window).node);
|
||||
|
||||
button.label = 'New label';
|
||||
button.icon = './new-icon.png';
|
||||
button.disabled = true;
|
||||
|
||||
for (let node of nodes) {
|
||||
assert.equal(node.getAttribute('label'), 'New label',
|
||||
'node label is updated');
|
||||
assert.equal(node.getAttribute('tooltiptext'), 'New label',
|
||||
'node tooltip is updated');
|
||||
|
||||
assert.equal(button.icon, './new-icon.png',
|
||||
'icon is updated');
|
||||
assert.equal(node.getAttribute('image'), data.url('new-icon.png'),
|
||||
'node image is updated');
|
||||
|
||||
assert.equal(button.disabled, true,
|
||||
'disabled is updated');
|
||||
assert.equal(node.getAttribute('disabled'), 'true',
|
||||
'node disabled is updated');
|
||||
};
|
||||
|
||||
return window;
|
||||
}).
|
||||
then(close).
|
||||
then(loader.unload).
|
||||
then(done, assert.fail);
|
||||
};
|
||||
|
||||
exports['test button window state'] = function(assert, done) {
|
||||
let loader = Loader(module);
|
||||
let { Button } = loader.require('sdk/ui');
|
||||
let { browserWindows } = loader.require('sdk/windows');
|
||||
|
||||
let button = Button({
|
||||
id: 'my-button-6',
|
||||
label: 'my button',
|
||||
icon: './icon.png'
|
||||
});
|
||||
|
||||
let mainWindow = browserWindows.activeWindow;
|
||||
let nodes = [getWidget(button.id).node];
|
||||
|
||||
open(null, { features: { toolbar: true }}).then(focus).then(window => {
|
||||
nodes.push(getWidget(button.id, window).node);
|
||||
|
||||
let { activeWindow } = browserWindows;
|
||||
|
||||
button.state(activeWindow, {
|
||||
label: 'New label',
|
||||
icon: './new-icon.png',
|
||||
disabled: true
|
||||
});
|
||||
|
||||
// check the states
|
||||
|
||||
assert.equal(button.label, 'my button',
|
||||
'global label unchanged');
|
||||
assert.equal(button.icon, './icon.png',
|
||||
'global icon unchanged');
|
||||
assert.equal(button.disabled, false,
|
||||
'global disabled unchanged');
|
||||
|
||||
let state = button.state(mainWindow);
|
||||
|
||||
assert.equal(state.label, 'my button',
|
||||
'previous window label unchanged');
|
||||
assert.equal(state.icon, './icon.png',
|
||||
'previous window icon unchanged');
|
||||
assert.equal(state.disabled, false,
|
||||
'previous window disabled unchanged');
|
||||
|
||||
let state = button.state(activeWindow);
|
||||
|
||||
assert.equal(state.label, 'New label',
|
||||
'active window label updated');
|
||||
assert.equal(state.icon, './new-icon.png',
|
||||
'active window icon updated');
|
||||
assert.equal(state.disabled, true,
|
||||
'active disabled updated');
|
||||
|
||||
// change the global state, only the windows without a state are affected
|
||||
|
||||
button.label = 'A good label';
|
||||
|
||||
assert.equal(button.label, 'A good label',
|
||||
'global label updated');
|
||||
assert.equal(button.state(mainWindow).label, 'A good label',
|
||||
'previous window label updated');
|
||||
assert.equal(button.state(activeWindow).label, 'New label',
|
||||
'active window label unchanged');
|
||||
|
||||
// delete the window state will inherits the global state again
|
||||
|
||||
button.state(activeWindow, null);
|
||||
|
||||
assert.equal(button.state(activeWindow).label, 'A good label',
|
||||
'active window label inherited');
|
||||
|
||||
// check the nodes properties
|
||||
let node = nodes[0];
|
||||
let state = button.state(mainWindow);
|
||||
|
||||
assert.equal(node.getAttribute('label'), state.label,
|
||||
'node label is correct');
|
||||
assert.equal(node.getAttribute('tooltiptext'), state.label,
|
||||
'node tooltip is correct');
|
||||
|
||||
assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
|
||||
'node image is correct');
|
||||
assert.equal(node.hasAttribute('disabled'), state.disabled,
|
||||
'disabled is correct');
|
||||
|
||||
let node = nodes[1];
|
||||
let state = button.state(activeWindow);
|
||||
|
||||
assert.equal(node.getAttribute('label'), state.label,
|
||||
'node label is correct');
|
||||
assert.equal(node.getAttribute('tooltiptext'), state.label,
|
||||
'node tooltip is correct');
|
||||
|
||||
assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
|
||||
'node image is correct');
|
||||
assert.equal(node.hasAttribute('disabled'), state.disabled,
|
||||
'disabled is correct');
|
||||
|
||||
return window;
|
||||
}).
|
||||
then(close).
|
||||
then(loader.unload).
|
||||
then(done, assert.fail);
|
||||
};
|
||||
|
||||
|
||||
exports['test button tab state'] = function(assert, done) {
|
||||
let loader = Loader(module);
|
||||
let { Button } = loader.require('sdk/ui');
|
||||
let { browserWindows } = loader.require('sdk/windows');
|
||||
let tabs = loader.require('sdk/tabs');
|
||||
|
||||
let button = Button({
|
||||
id: 'my-button-7',
|
||||
label: 'my button',
|
||||
icon: './icon.png'
|
||||
});
|
||||
|
||||
let mainTab = tabs.activeTab;
|
||||
let node = getWidget(button.id).node;
|
||||
|
||||
tabs.open({
|
||||
url: 'about:blank',
|
||||
onActivate: function onActivate(tab) {
|
||||
tab.removeListener('activate', onActivate);
|
||||
|
||||
let { activeWindow } = browserWindows;
|
||||
// set window state
|
||||
button.state(activeWindow, {
|
||||
label: 'Window label',
|
||||
icon: './window-icon.png'
|
||||
});
|
||||
|
||||
// set previous active tab state
|
||||
button.state(mainTab, {
|
||||
label: 'Tab label',
|
||||
icon: './tab-icon.png',
|
||||
});
|
||||
|
||||
// set current active tab state
|
||||
button.state(tab, {
|
||||
icon: './another-tab-icon.png',
|
||||
disabled: true
|
||||
});
|
||||
|
||||
// check the states
|
||||
|
||||
Cu.schedulePreciseGC(() => {
|
||||
assert.equal(button.label, 'my button',
|
||||
'global label unchanged');
|
||||
assert.equal(button.icon, './icon.png',
|
||||
'global icon unchanged');
|
||||
assert.equal(button.disabled, false,
|
||||
'global disabled unchanged');
|
||||
|
||||
let state = button.state(mainTab);
|
||||
|
||||
assert.equal(state.label, 'Tab label',
|
||||
'previous tab label updated');
|
||||
assert.equal(state.icon, './tab-icon.png',
|
||||
'previous tab icon updated');
|
||||
assert.equal(state.disabled, false,
|
||||
'previous tab disabled unchanged');
|
||||
|
||||
let state = button.state(tab);
|
||||
|
||||
assert.equal(state.label, 'Window label',
|
||||
'active tab inherited from window state');
|
||||
assert.equal(state.icon, './another-tab-icon.png',
|
||||
'active tab icon updated');
|
||||
assert.equal(state.disabled, true,
|
||||
'active disabled updated');
|
||||
|
||||
// change the global state
|
||||
button.icon = './good-icon.png';
|
||||
|
||||
// delete the tab state
|
||||
button.state(tab, null);
|
||||
|
||||
assert.equal(button.icon, './good-icon.png',
|
||||
'global icon updated');
|
||||
assert.equal(button.state(mainTab).icon, './tab-icon.png',
|
||||
'previous tab icon unchanged');
|
||||
assert.equal(button.state(tab).icon, './window-icon.png',
|
||||
'tab icon inherited from window');
|
||||
|
||||
// delete the window state
|
||||
button.state(activeWindow, null);
|
||||
|
||||
assert.equal(button.state(tab).icon, './good-icon.png',
|
||||
'tab icon inherited from global');
|
||||
|
||||
// check the node properties
|
||||
|
||||
let state = button.state(tabs.activeTab);
|
||||
|
||||
assert.equal(node.getAttribute('label'), state.label,
|
||||
'node label is correct');
|
||||
assert.equal(node.getAttribute('tooltiptext'), state.label,
|
||||
'node tooltip is correct');
|
||||
assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
|
||||
'node image is correct');
|
||||
assert.equal(node.hasAttribute('disabled'), state.disabled,
|
||||
'disabled is correct');
|
||||
|
||||
tabs.once('activate', () => {
|
||||
// This is made in order to avoid to check the node before it
|
||||
// is updated, need a better check
|
||||
setTimeout(() => {
|
||||
let state = button.state(mainTab);
|
||||
|
||||
assert.equal(node.getAttribute('label'), state.label,
|
||||
'node label is correct');
|
||||
assert.equal(node.getAttribute('tooltiptext'), state.label,
|
||||
'node tooltip is correct');
|
||||
assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
|
||||
'node image is correct');
|
||||
assert.equal(node.hasAttribute('disabled'), state.disabled,
|
||||
'disabled is correct');
|
||||
|
||||
tab.close(() => {
|
||||
loader.unload();
|
||||
done();
|
||||
});
|
||||
}, 500);
|
||||
});
|
||||
|
||||
mainTab.activate();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
exports['test button click'] = function(assert, done) {
|
||||
let loader = Loader(module);
|
||||
let { Button } = loader.require('sdk/ui');
|
||||
let { browserWindows } = loader.require('sdk/windows');
|
||||
|
||||
let labels = [];
|
||||
|
||||
let button = Button({
|
||||
id: 'my-button-8',
|
||||
label: 'my button',
|
||||
icon: './icon.png',
|
||||
onClick: ({label}) => labels.push(label)
|
||||
});
|
||||
|
||||
let mainWindow = browserWindows.activeWindow;
|
||||
let chromeWindow = getMostRecentBrowserWindow();
|
||||
|
||||
open(null, { features: { toolbar: true }}).then(focus).then(window => {
|
||||
button.state(mainWindow, { label: 'nothing' });
|
||||
button.state(mainWindow.tabs.activeTab, { label: 'foo'})
|
||||
button.state(browserWindows.activeWindow, { label: 'bar' });
|
||||
|
||||
button.click();
|
||||
|
||||
focus(chromeWindow).then(() => {
|
||||
button.click();
|
||||
|
||||
assert.deepEqual(labels, ['bar', 'foo'],
|
||||
'button click works');
|
||||
|
||||
close(window).
|
||||
then(loader.unload).
|
||||
then(done, assert.fail);
|
||||
});
|
||||
}).then(null, assert.fail);
|
||||
}
|
||||
|
||||
exports['test button type checkbox'] = function(assert, done) {
|
||||
let loader = Loader(module);
|
||||
let { Button } = loader.require('sdk/ui');
|
||||
let { browserWindows } = loader.require('sdk/windows');
|
||||
|
||||
let events = [];
|
||||
|
||||
let button = Button({
|
||||
id: 'my-button-9',
|
||||
label: 'my button',
|
||||
icon: './icon.png',
|
||||
type: 'checkbox',
|
||||
onClick: ({label}) => events.push('clicked:' + label),
|
||||
onChange: state => events.push('changed:' + state.label + ':' + state.checked)
|
||||
});
|
||||
|
||||
let { node } = getWidget(button.id);
|
||||
|
||||
assert.equal(button.type, 'checkbox',
|
||||
'button type is set');
|
||||
assert.equal(node.getAttribute('type'), 'checkbox',
|
||||
'node type is set');
|
||||
|
||||
let mainWindow = browserWindows.activeWindow;
|
||||
let chromeWindow = getMostRecentBrowserWindow();
|
||||
|
||||
open(null, { features: { toolbar: true }}).then(focus).then(window => {
|
||||
button.state(mainWindow, { label: 'nothing' });
|
||||
button.state(mainWindow.tabs.activeTab, { label: 'foo'})
|
||||
button.state(browserWindows.activeWindow, { label: 'bar' });
|
||||
|
||||
button.click();
|
||||
button.click();
|
||||
|
||||
focus(chromeWindow).then(() => {
|
||||
button.click();
|
||||
button.click();
|
||||
|
||||
assert.deepEqual(events, [
|
||||
'clicked:bar', 'changed:bar:true', 'clicked:bar', 'changed:bar:false',
|
||||
'clicked:foo', 'changed:foo:true', 'clicked:foo', 'changed:foo:false'
|
||||
],
|
||||
'button change events works');
|
||||
|
||||
close(window).
|
||||
then(loader.unload).
|
||||
then(done, assert.fail);
|
||||
})
|
||||
}).then(null, assert.fail);
|
||||
}
|
||||
|
||||
exports['test button icon set'] = function(assert) {
|
||||
const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
|
||||
let loader = Loader(module);
|
||||
let { Button } = loader.require('sdk/ui');
|
||||
|
||||
// Test remote icon set
|
||||
assert.throws(
|
||||
() => Button({
|
||||
id: 'my-button-10',
|
||||
label: 'my button',
|
||||
icon: {
|
||||
'16': 'http://www.mozilla.org/favicon.ico'
|
||||
}
|
||||
}),
|
||||
/^The option "icon"/,
|
||||
'throws on no valid icon given');
|
||||
|
||||
let button = Button({
|
||||
id: 'my-button-11',
|
||||
label: 'my button',
|
||||
icon: {
|
||||
'5': './icon5.png',
|
||||
'16': './icon16.png',
|
||||
'32': './icon32.png',
|
||||
'64': './icon64.png'
|
||||
}
|
||||
});
|
||||
|
||||
let { node, id: widgetId } = getWidget(button.id);
|
||||
let { devicePixelRatio } = node.ownerDocument.defaultView;
|
||||
|
||||
let size = 16 * devicePixelRatio;
|
||||
|
||||
assert.equal(node.getAttribute('image'), data.url(button.icon[size].substr(2)),
|
||||
'the icon is set properly in navbar');
|
||||
|
||||
let size = 32 * devicePixelRatio;
|
||||
|
||||
CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_PANEL);
|
||||
|
||||
assert.equal(node.getAttribute('image'), data.url(button.icon[size].substr(2)),
|
||||
'the icon is set properly in panel');
|
||||
|
||||
// Using `loader.unload` without move back the button to the original area
|
||||
// raises an error in the CustomizableUI. This is doesn't happen if the
|
||||
// button is moved manually from navbar to panel. I believe it has to do
|
||||
// with `addWidgetToArea` method, because even with a `timeout` the issue
|
||||
// persist.
|
||||
CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_NAVBAR);
|
||||
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
exports['test button icon se with only one option'] = function(assert) {
|
||||
const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
|
||||
let loader = Loader(module);
|
||||
let { Button } = loader.require('sdk/ui');
|
||||
|
||||
// Test remote icon set
|
||||
assert.throws(
|
||||
() => Button({
|
||||
id: 'my-button-10',
|
||||
label: 'my button',
|
||||
icon: {
|
||||
'16': 'http://www.mozilla.org/favicon.ico'
|
||||
}
|
||||
}),
|
||||
/^The option "icon"/,
|
||||
'throws on no valid icon given');
|
||||
|
||||
let button = Button({
|
||||
id: 'my-button-11',
|
||||
label: 'my button',
|
||||
icon: {
|
||||
'5': './icon5.png'
|
||||
}
|
||||
});
|
||||
|
||||
let { node, id: widgetId } = getWidget(button.id);
|
||||
|
||||
assert.equal(node.getAttribute('image'), data.url(button.icon['5'].substr(2)),
|
||||
'the icon is set properly in navbar');
|
||||
|
||||
CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_PANEL);
|
||||
|
||||
assert.equal(node.getAttribute('image'), data.url(button.icon['5'].substr(2)),
|
||||
'the icon is set properly in panel');
|
||||
|
||||
// Using `loader.unload` without move back the button to the original area
|
||||
// raises an error in the CustomizableUI. This is doesn't happen if the
|
||||
// button is moved manually from navbar to panel. I believe it has to do
|
||||
// with `addWidgetToArea` method, because even with a `timeout` the issue
|
||||
// persist.
|
||||
CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_NAVBAR);
|
||||
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
exports['test button state validation'] = function(assert) {
|
||||
let loader = Loader(module);
|
||||
let { Button } = loader.require('sdk/ui');
|
||||
let { browserWindows } = loader.require('sdk/windows');
|
||||
|
||||
let button = Button({
|
||||
id: 'my-button-12',
|
||||
label: 'my button',
|
||||
icon: './icon.png'
|
||||
})
|
||||
|
||||
button.state(button, {
|
||||
size: 'large'
|
||||
});
|
||||
|
||||
assert.equal(button.size, 'small',
|
||||
'button.size is unchanged');
|
||||
|
||||
let state = button.state(button);
|
||||
|
||||
assert.equal(button.size, 'small',
|
||||
'button state is unchanged');
|
||||
|
||||
assert.throws(
|
||||
() => button.state(button, { icon: 'http://www.mozilla.org/favicon.ico' }),
|
||||
/^The option "icon"/,
|
||||
'throws on remote icon given');
|
||||
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
exports['test button are not in private windows'] = function(assert, done) {
|
||||
let loader = Loader(module);
|
||||
let { Button } = loader.require('sdk/ui');
|
||||
let{ isPrivate } = loader.require('sdk/private-browsing');
|
||||
let { browserWindows } = loader.require('sdk/windows');
|
||||
|
||||
let button = Button({
|
||||
id: 'my-button-13',
|
||||
label: 'my button',
|
||||
icon: './icon.png'
|
||||
});
|
||||
|
||||
open(null, { features: { toolbar: true, private: true }}).then(window => {
|
||||
assert.ok(isPrivate(window),
|
||||
'the new window is private');
|
||||
|
||||
let { node } = getWidget(button.id, window);
|
||||
|
||||
assert.ok(!node || node.style.display === 'none',
|
||||
'the button is not added / is not visible on private window');
|
||||
|
||||
return window;
|
||||
}).
|
||||
then(close).
|
||||
then(loader.unload).
|
||||
then(done, assert.fail)
|
||||
}
|
||||
|
||||
exports['test button state are snapshot'] = function(assert) {
|
||||
let loader = Loader(module);
|
||||
let { Button } = loader.require('sdk/ui');
|
||||
let { browserWindows } = loader.require('sdk/windows');
|
||||
let tabs = loader.require('sdk/tabs');
|
||||
|
||||
let button = Button({
|
||||
id: 'my-button-14',
|
||||
label: 'my button',
|
||||
icon: './icon.png'
|
||||
});
|
||||
|
||||
let state = button.state(button);
|
||||
let windowState = button.state(browserWindows.activeWindow);
|
||||
let tabState = button.state(tabs.activeTab);
|
||||
|
||||
assert.deepEqual(windowState, state,
|
||||
'window state has the same properties of button state');
|
||||
|
||||
assert.deepEqual(tabState, state,
|
||||
'tab state has the same properties of button state');
|
||||
|
||||
assert.notEqual(windowState, state,
|
||||
'window state is not the same object of button state');
|
||||
|
||||
assert.notEqual(tabState, state,
|
||||
'tab state is not the same object of button state');
|
||||
|
||||
assert.deepEqual(button.state(button), state,
|
||||
'button state has the same content of previous button state');
|
||||
|
||||
assert.deepEqual(button.state(browserWindows.activeWindow), windowState,
|
||||
'window state has the same content of previous window state');
|
||||
|
||||
assert.deepEqual(button.state(tabs.activeTab), tabState,
|
||||
'tab state has the same content of previous tab state');
|
||||
|
||||
assert.notEqual(button.state(button), state,
|
||||
'button state is not the same object of previous button state');
|
||||
|
||||
assert.notEqual(button.state(browserWindows.activeWindow), windowState,
|
||||
'window state is not the same object of previous window state');
|
||||
|
||||
assert.notEqual(button.state(tabs.activeTab), tabState,
|
||||
'tab state is not the same object of previous tab state');
|
||||
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
exports['test button after destroy'] = function(assert) {
|
||||
let loader = Loader(module);
|
||||
let { Button } = loader.require('sdk/ui');
|
||||
let { browserWindows } = loader.require('sdk/windows');
|
||||
let { activeTab } = loader.require('sdk/tabs');
|
||||
|
||||
let button = Button({
|
||||
id: 'my-button-15',
|
||||
label: 'my button',
|
||||
icon: './icon.png',
|
||||
onClick: () => assert.fail('onClick should not be called')
|
||||
});
|
||||
|
||||
button.destroy();
|
||||
|
||||
assert.throws(
|
||||
() => button.click(),
|
||||
/^The state cannot be set or get/,
|
||||
'button.click() not executed');
|
||||
|
||||
assert.throws(
|
||||
() => button.label,
|
||||
/^The state cannot be set or get/,
|
||||
'button.label cannot be get after destroy');
|
||||
|
||||
assert.throws(
|
||||
() => button.label = 'my label',
|
||||
/^The state cannot be set or get/,
|
||||
'button.label cannot be set after destroy');
|
||||
|
||||
assert.throws(
|
||||
() => {
|
||||
button.state(browserWindows.activeWindow, {
|
||||
label: 'window label'
|
||||
});
|
||||
},
|
||||
/^The state cannot be set or get/,
|
||||
'window state label cannot be set after destroy');
|
||||
|
||||
assert.throws(
|
||||
() => button.state(browserWindows.activeWindow).label,
|
||||
/^The state cannot be set or get/,
|
||||
'window state label cannot be get after destroy');
|
||||
|
||||
assert.throws(
|
||||
() => {
|
||||
button.state(activeTab, {
|
||||
label: 'tab label'
|
||||
});
|
||||
},
|
||||
/^The state cannot be set or get/,
|
||||
'tab state label cannot be set after destroy');
|
||||
|
||||
assert.throws(
|
||||
() => button.state(activeTab).label,
|
||||
/^The state cannot be set or get/,
|
||||
'window state label cannot se get after destroy');
|
||||
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
// 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.
|
||||
try {
|
||||
require('sdk/ui/button');
|
||||
}
|
||||
catch (err) {
|
||||
if (!/^Unsupported Application/.test(err.message))
|
||||
throw err;
|
||||
|
||||
module.exports = {
|
||||
'test Unsupported Application': assert => assert.pass(err.message)
|
||||
}
|
||||
}
|
||||
|
||||
require('sdk/test').run(exports);
|
208
addon-sdk/source/test/test-ui-sidebar-private-browsing.js
Normal file
208
addon-sdk/source/test/test-ui-sidebar-private-browsing.js
Normal file
@ -0,0 +1,208 @@
|
||||
/* 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';
|
||||
|
||||
module.metadata = {
|
||||
'engines': {
|
||||
'Firefox': '> 24'
|
||||
}
|
||||
};
|
||||
|
||||
const { Loader } = require('sdk/test/loader');
|
||||
const { show, hide } = require('sdk/ui/sidebar/actions');
|
||||
const { isShowing } = require('sdk/ui/sidebar/utils');
|
||||
const { getMostRecentBrowserWindow, isWindowPrivate } = require('sdk/window/utils');
|
||||
const { open, close, focus, promise: windowPromise } = require('sdk/window/helpers');
|
||||
const { setTimeout } = require('sdk/timers');
|
||||
const { isPrivate } = require('sdk/private-browsing');
|
||||
const { data } = require('sdk/self');
|
||||
const { URL } = require('sdk/url');
|
||||
|
||||
const { BLANK_IMG, BUILTIN_SIDEBAR_MENUITEMS, isSidebarShowing,
|
||||
getSidebarMenuitems, getExtraSidebarMenuitems, makeID, simulateCommand,
|
||||
simulateClick, getWidget, isChecked } = require('./sidebar/utils');
|
||||
|
||||
exports.testSideBarIsNotInNewPrivateWindows = function(assert, done) {
|
||||
const { Sidebar } = require('sdk/ui/sidebar');
|
||||
let testName = 'testSideBarIsNotInNewPrivateWindows';
|
||||
let sidebar = Sidebar({
|
||||
id: testName,
|
||||
title: testName,
|
||||
icon: BLANK_IMG,
|
||||
url: 'data:text/html;charset=utf-8,'+testName
|
||||
});
|
||||
|
||||
let startWindow = getMostRecentBrowserWindow();
|
||||
let ele = startWindow.document.getElementById(makeID(testName));
|
||||
assert.ok(ele, 'sidebar element was added');
|
||||
|
||||
open(null, { features: { private: true } }).then(function(window) {
|
||||
let ele = window.document.getElementById(makeID(testName));
|
||||
assert.ok(isPrivate(window), 'the new window is private');
|
||||
assert.equal(ele, null, 'sidebar element was not added');
|
||||
|
||||
sidebar.destroy();
|
||||
assert.ok(!window.document.getElementById(makeID(testName)), 'sidebar id DNE');
|
||||
assert.ok(!startWindow.document.getElementById(makeID(testName)), 'sidebar id DNE');
|
||||
|
||||
close(window).then(done, assert.fail);
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
exports.testSidebarIsNotOpenInNewPrivateWindow = function(assert, done) {
|
||||
let testName = 'testSidebarIsNotOpenInNewPrivateWindow';
|
||||
let window = getMostRecentBrowserWindow();
|
||||
|
||||
let sidebar = Sidebar({
|
||||
id: testName,
|
||||
title: testName,
|
||||
icon: BLANK_IMG,
|
||||
url: 'data:text/html;charset=utf-8,'+testName
|
||||
});
|
||||
|
||||
sidebar.on('show', function() {
|
||||
assert.equal(isPrivate(window), false, 'the new window is not private');
|
||||
assert.equal(isSidebarShowing(window), true, 'the sidebar is showing');
|
||||
assert.equal(isShowing(sidebar), true, 'the sidebar is showing');
|
||||
|
||||
let window2 = window.OpenBrowserWindow({private: true});
|
||||
windowPromise(window2, 'load').then(focus).then(function() {
|
||||
// TODO: find better alt to setTimeout...
|
||||
setTimeout(function() {
|
||||
assert.equal(isPrivate(window2), true, 'the new window is private');
|
||||
assert.equal(isSidebarShowing(window), true, 'the sidebar is showing in old window still');
|
||||
assert.equal(isSidebarShowing(window2), false, 'the sidebar is not showing in the new private window');
|
||||
assert.equal(isShowing(sidebar), false, 'the sidebar is not showing');
|
||||
sidebar.destroy();
|
||||
close(window2).then(done);
|
||||
}, 500)
|
||||
})
|
||||
});
|
||||
|
||||
sidebar.show();
|
||||
}
|
||||
*/
|
||||
|
||||
// TEST: edge case where web panel is destroyed while loading
|
||||
exports.testDestroyEdgeCaseBugWithPrivateWindow = function(assert, done) {
|
||||
const { Sidebar } = require('sdk/ui/sidebar');
|
||||
let testName = 'testDestroyEdgeCaseBug';
|
||||
let window = getMostRecentBrowserWindow();
|
||||
|
||||
let sidebar = Sidebar({
|
||||
id: testName,
|
||||
title: testName,
|
||||
icon: BLANK_IMG,
|
||||
url: 'data:text/html;charset=utf-8,'+testName
|
||||
});
|
||||
|
||||
// NOTE: purposely not listening to show event b/c the event happens
|
||||
// between now and then.
|
||||
sidebar.show();
|
||||
|
||||
assert.equal(isPrivate(window), false, 'the new window is not private');
|
||||
assert.equal(isSidebarShowing(window), true, 'the sidebar is showing');
|
||||
|
||||
//assert.equal(isShowing(sidebar), true, 'the sidebar is showing');
|
||||
|
||||
open(null, { features: { private: true } }).then(focus).then(function(window2) {
|
||||
assert.equal(isPrivate(window2), true, 'the new window is private');
|
||||
assert.equal(isSidebarShowing(window2), false, 'the sidebar is not showing');
|
||||
assert.equal(isShowing(sidebar), false, 'the sidebar is not showing');
|
||||
|
||||
sidebar.destroy();
|
||||
assert.pass('destroying the sidebar');
|
||||
|
||||
close(window2).then(function() {
|
||||
let loader = Loader(module);
|
||||
|
||||
assert.equal(isPrivate(window), false, 'the current window is not private');
|
||||
|
||||
let sidebar = loader.require('sdk/ui/sidebar').Sidebar({
|
||||
id: testName,
|
||||
title: testName,
|
||||
icon: BLANK_IMG,
|
||||
url: 'data:text/html;charset=utf-8,'+ testName,
|
||||
onShow: function() {
|
||||
assert.pass('onShow works for Sidebar');
|
||||
loader.unload();
|
||||
|
||||
let sidebarMI = getSidebarMenuitems();
|
||||
for each (let mi in sidebarMI) {
|
||||
assert.ok(BUILTIN_SIDEBAR_MENUITEMS.indexOf(mi.getAttribute('id')) >= 0, 'the menuitem is for a built-in sidebar')
|
||||
assert.ok(!isChecked(mi), 'no sidebar menuitem is checked');
|
||||
}
|
||||
assert.ok(!window.document.getElementById(makeID(testName)), 'sidebar id DNE');
|
||||
assert.equal(isSidebarShowing(window), false, 'the sidebar is not showing');
|
||||
|
||||
done();
|
||||
}
|
||||
})
|
||||
|
||||
sidebar.show();
|
||||
assert.pass('showing the sidebar');
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
exports.testShowInPrivateWindow = function(assert, done) {
|
||||
const { Sidebar } = require('sdk/ui/sidebar');
|
||||
let testName = 'testShowInPrivateWindow';
|
||||
let window = getMostRecentBrowserWindow();
|
||||
let { document } = window;
|
||||
let url = 'data:text/html;charset=utf-8,'+testName;
|
||||
|
||||
let sidebar1 = Sidebar({
|
||||
id: testName,
|
||||
title: testName,
|
||||
icon: BLANK_IMG,
|
||||
url: url
|
||||
});
|
||||
|
||||
assert.equal(sidebar1.url, url, 'url getter works');
|
||||
assert.equal(isShowing(sidebar1), false, 'the sidebar is not showing');
|
||||
assert.ok(!isChecked(document.getElementById(makeID(sidebar1.id))),
|
||||
'the menuitem is not checked');
|
||||
assert.equal(isSidebarShowing(window), false, 'the new window sidebar is not showing');
|
||||
|
||||
windowPromise(window.OpenBrowserWindow({ private: true }), 'load').then(function(window) {
|
||||
let { document } = window;
|
||||
assert.equal(isWindowPrivate(window), true, 'new window is private');
|
||||
assert.equal(isPrivate(window), true, 'new window is private');
|
||||
|
||||
sidebar1.show().then(
|
||||
function bad() {
|
||||
assert.fail('a successful show should not happen here..');
|
||||
},
|
||||
function good() {
|
||||
assert.equal(isShowing(sidebar1), false, 'the sidebar is still not showing');
|
||||
assert.equal(document.getElementById(makeID(sidebar1.id)),
|
||||
null,
|
||||
'the menuitem dne on the private window');
|
||||
assert.equal(isSidebarShowing(window), false, 'the new window sidebar is not showing');
|
||||
|
||||
sidebar1.destroy();
|
||||
close(window).then(done);
|
||||
});
|
||||
}, assert.fail);
|
||||
}
|
||||
|
||||
// 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.
|
||||
try {
|
||||
require('sdk/ui/sidebar');
|
||||
}
|
||||
catch (err) {
|
||||
if (!/^Unsupported Application/.test(err.message))
|
||||
throw err;
|
||||
|
||||
module.exports = {
|
||||
'test Unsupported Application': assert => assert.pass(err.message)
|
||||
}
|
||||
}
|
||||
|
||||
require('sdk/test').run(exports);
|
1490
addon-sdk/source/test/test-ui-sidebar.js
Normal file
1490
addon-sdk/source/test/test-ui-sidebar.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -3,17 +3,11 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
// Opening new windows in Fennec causes issues
|
||||
module.metadata = {
|
||||
engines: {
|
||||
'Firefox': '*'
|
||||
}
|
||||
};
|
||||
|
||||
const { Loader } = require("sdk/test/loader");
|
||||
const { open, close } = require("sdk/window/helpers");
|
||||
const { browserWindows: windows } = require("sdk/windows");
|
||||
const { isBrowser } = require('sdk/window/utils');
|
||||
const app = require("sdk/system/xul-app");
|
||||
|
||||
exports["test unload window observer"] = function(assert, done) {
|
||||
// Hacky way to be able to create unloadable modules via makeSandboxedLoader.
|
||||
@ -23,17 +17,14 @@ exports["test unload window observer"] = function(assert, done) {
|
||||
let closed = 0;
|
||||
let windowsOpen = windows.length;
|
||||
|
||||
observer.on("open", function onOpen(window) {
|
||||
// Ignoring non-browser windows
|
||||
if (isBrowser(window))
|
||||
opened++;
|
||||
});
|
||||
observer.on("close", function onClose(window) {
|
||||
// Ignore non-browser windows & already opened `activeWindow` (unload will
|
||||
// emit close on it even though it is not actually closed).
|
||||
if (isBrowser(window))
|
||||
closed++;
|
||||
});
|
||||
observer.on("open", onOpen);
|
||||
observer.on("close", onClose);
|
||||
|
||||
// On Fennec, only test that the module does not throw an error
|
||||
if (app.is("Fennec")) {
|
||||
assert.pass("Windows observer did not throw on Fennec");
|
||||
return cleanUp();
|
||||
}
|
||||
|
||||
// Open window and close it to trigger observers.
|
||||
open().
|
||||
@ -46,7 +37,25 @@ exports["test unload window observer"] = function(assert, done) {
|
||||
assert.equal(1, opened, "observer open was called before unload only");
|
||||
assert.equal(windowsOpen + 1, closed, "observer close was called before unload only");
|
||||
}).
|
||||
then(done, assert.fail);
|
||||
then(cleanUp, assert.fail);
|
||||
|
||||
function cleanUp () {
|
||||
observer.removeListener("open", onOpen);
|
||||
observer.removeListener("close", onClose);
|
||||
done();
|
||||
}
|
||||
|
||||
function onOpen(window) {
|
||||
// Ignoring non-browser windows
|
||||
if (isBrowser(window))
|
||||
opened++;
|
||||
}
|
||||
function onClose(window) {
|
||||
// Ignore non-browser windows & already opened `activeWindow` (unload will
|
||||
// emit close on it even though it is not actually closed).
|
||||
if (isBrowser(window))
|
||||
closed++;
|
||||
}
|
||||
};
|
||||
|
||||
require("test").run(exports);
|
||||
|
Loading…
Reference in New Issue
Block a user