зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to m-i
This commit is contained in:
Коммит
f26020beac
|
@ -222,6 +222,8 @@ function startup(data, reasonCode) {
|
||||||
resultFile: options.resultFile,
|
resultFile: options.resultFile,
|
||||||
// Arguments passed as --static-args
|
// Arguments passed as --static-args
|
||||||
staticArgs: options.staticArgs,
|
staticArgs: options.staticArgs,
|
||||||
|
// Add-on preferences branch name
|
||||||
|
preferencesBranch: options.preferencesBranch,
|
||||||
|
|
||||||
// Arguments related to test runner.
|
// Arguments related to test runner.
|
||||||
modules: {
|
modules: {
|
||||||
|
|
|
@ -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 ###
|
### J ###
|
||||||
|
|
||||||
|
* Tomislav Jovanovic
|
||||||
* Eric H. Jung
|
* Eric H. Jung
|
||||||
|
|
||||||
### K ###
|
### 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))
|
* Joe R. Nassimian ([placidrage](https://github.com/placidrage))
|
||||||
* Dương H. Nguyễn
|
* Dương H. Nguyễn
|
||||||
* Nick Nguyen
|
* Nick Nguyen
|
||||||
|
* nodeless
|
||||||
|
|
||||||
### O ###
|
### O ###
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ Then open "lib/main.js" and add the following code:
|
||||||
onItemVisited: function(aItemId, aVisitID, time) {
|
onItemVisited: function(aItemId, aVisitID, time) {
|
||||||
console.log("visited ", bookmarkService.getBookmarkURI(aItemId).spec);
|
console.log("visited ", bookmarkService.getBookmarkURI(aItemId).spec);
|
||||||
},
|
},
|
||||||
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsINavBookmarkObserver])
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver])
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.main = function() {
|
exports.main = function() {
|
||||||
|
|
|
@ -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}
|
* @returns {Error}
|
||||||
*/
|
*/
|
||||||
function incompatibility(module) {
|
function incompatibility(module) {
|
||||||
|
|
||||||
let { metadata, id } = module;
|
let { metadata, id } = module;
|
||||||
|
|
||||||
// if metadata or engines are not specified we assume compatibility is not
|
// 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 { when: unload } = require("./system/unload");
|
||||||
const { PrefsTarget } = require("./preferences/event-target");
|
const { PrefsTarget } = require("./preferences/event-target");
|
||||||
const { id } = require("./self");
|
const { id } = require("./self");
|
||||||
|
const { preferencesBranch } = require('@loader/options');
|
||||||
|
|
||||||
const observers = require("./deprecated/observer-service");
|
const observers = require("./deprecated/observer-service");
|
||||||
|
|
||||||
const ADDON_BRANCH = "extensions." + id + ".";
|
const ADDON_BRANCH = "extensions." + preferencesBranch + ".";
|
||||||
const BUTTON_PRESSED = id + "-cmdPressed";
|
const BUTTON_PRESSED = id + "-cmdPressed";
|
||||||
|
|
||||||
const target = PrefsTarget({ branchName: ADDON_BRANCH });
|
const target = PrefsTarget({ branchName: ADDON_BRANCH });
|
||||||
|
|
|
@ -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;
|
|
@ -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);
|
||||||
|
});
|
|
@ -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;
|
|
@ -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));
|
|
@ -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');
|
|
@ -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.'
|
||||||
|
}
|
||||||
|
});
|
|
@ -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);
|
|
@ -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');
|
|
@ -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;
|
||||||
|
}
|
|
@ -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,
|
{ WindowTrackerTrait } = windowUtils,
|
||||||
{ ns } = require('../core/namespace'),
|
{ ns } = require('../core/namespace'),
|
||||||
{ observer: windowObserver } = require('./observer'),
|
{ observer: windowObserver } = require('./observer'),
|
||||||
{ getOwnerWindow } = require('../private-browsing/window/utils'),
|
{ getOwnerWindow } = require('../private-browsing/window/utils');
|
||||||
viewNS = require('../core/namespace').ns(),
|
const { windowNS } = require('../window/namespace');
|
||||||
{ isPrivateBrowsingSupported } = require('../self');
|
const { isPrivateBrowsingSupported } = require('../self');
|
||||||
const { ignoreWindow } = require('sdk/private-browsing/utils');
|
const { ignoreWindow } = require('sdk/private-browsing/utils');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -74,7 +74,7 @@ const BrowserWindowTrait = Trait.compose(
|
||||||
|
|
||||||
this._load();
|
this._load();
|
||||||
|
|
||||||
viewNS(this._public).window = this._window;
|
windowNS(this._public).window = this._window;
|
||||||
getOwnerWindow.implement(this._public, getChromeWindow);
|
getOwnerWindow.implement(this._public, getChromeWindow);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
|
@ -256,7 +256,7 @@ const browserWindows = Trait.resolve({ toString: null }).compose(
|
||||||
)();
|
)();
|
||||||
|
|
||||||
function getChromeWindow(window) {
|
function getChromeWindow(window) {
|
||||||
return viewNS(window).window;
|
return windowNS(window).window;
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.browserWindows = browserWindows;
|
exports.browserWindows = browserWindows;
|
||||||
|
|
|
@ -234,6 +234,10 @@ parser_groups = (
|
||||||
default=False,
|
default=False,
|
||||||
cmds=['test', 'testpkgs', 'testaddons',
|
cmds=['test', 'testpkgs', 'testaddons',
|
||||||
'testall'])),
|
'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)
|
key,value = kv.split("=", 1)
|
||||||
extra_harness_options[key] = value
|
extra_harness_options[key] = value
|
||||||
# Generate xpi filepath
|
# 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
|
print >>stdout, "Exporting extension to %s." % xpi_path
|
||||||
build_xpi(template_root_dir=app_extension_dir,
|
build_xpi(template_root_dir=app_extension_dir,
|
||||||
manifest=manifest_rdf,
|
manifest=manifest_rdf,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
# 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/.
|
# 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
|
# this returns a unicode string
|
||||||
pref_list = []
|
pref_list = []
|
||||||
|
|
||||||
|
@ -21,6 +21,6 @@ def parse_options_defaults(options, jetpack_id):
|
||||||
else:
|
else:
|
||||||
value = str(pref["value"])
|
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"
|
return "\n".join(pref_list) + "\n"
|
||||||
|
|
|
@ -45,7 +45,7 @@ def validate_prefs(options):
|
||||||
|
|
||||||
# TODO: Check that pref["type"] matches default value type
|
# TODO: Check that pref["type"] matches default value type
|
||||||
|
|
||||||
def parse_options(options, jetpack_id):
|
def parse_options(options, jetpack_id, preferencesBranch):
|
||||||
doc = Document()
|
doc = Document()
|
||||||
root = doc.createElement("vbox")
|
root = doc.createElement("vbox")
|
||||||
root.setAttribute("xmlns", "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul")
|
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 = doc.createElement("setting")
|
||||||
setting.setAttribute("pref-name", pref["name"])
|
setting.setAttribute("pref-name", pref["name"])
|
||||||
setting.setAttribute("data-jetpack-id", jetpack_id)
|
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("type", pref["type"])
|
||||||
setting.setAttribute("title", pref["title"])
|
setting.setAttribute("title", pref["title"])
|
||||||
|
|
||||||
|
|
|
@ -396,6 +396,21 @@ def generate_build_for_target(pkg_cfg, target, deps,
|
||||||
if ('preferences' in target_cfg):
|
if ('preferences' in target_cfg):
|
||||||
build['preferences'] = target_cfg.preferences
|
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
|
return build
|
||||||
|
|
||||||
def _get_files_in_dir(path):
|
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(
|
build = packaging.generate_build_for_target(
|
||||||
pkg_cfg=pkg_cfg,
|
pkg_cfg=pkg_cfg,
|
||||||
target=pkg_name,
|
target=pkg_name,
|
||||||
deps=deps
|
deps=deps,
|
||||||
|
is_running_tests=True,
|
||||||
)
|
)
|
||||||
return Bunch(target_cfg=target_cfg, pkg_cfg=pkg_cfg, build=build)
|
return Bunch(target_cfg=target_cfg, pkg_cfg=pkg_cfg, build=build)
|
||||||
|
|
||||||
|
|
|
@ -40,10 +40,11 @@ class PrefsTests(unittest.TestCase):
|
||||||
|
|
||||||
def testPackageWithSimplePrefs(self):
|
def testPackageWithSimplePrefs(self):
|
||||||
self.makexpi('simple-prefs')
|
self.makexpi('simple-prefs')
|
||||||
|
packageName = 'jid1-fZHqN9JfrDBa8A@jetpack'
|
||||||
self.failUnless('options.xul' in self.xpi.namelist())
|
self.failUnless('options.xul' in self.xpi.namelist())
|
||||||
optsxul = self.xpi.read('options.xul').decode("utf-8")
|
optsxul = self.xpi.read('options.xul').decode("utf-8")
|
||||||
self.failUnlessEqual(self.xpi_harness_options["jetpackID"],
|
self.failUnlessEqual(self.xpi_harness_options["jetpackID"], packageName)
|
||||||
"jid1-fZHqN9JfrDBa8A@jetpack")
|
self.failUnlessEqual(self.xpi_harness_options["preferencesBranch"], packageName)
|
||||||
|
|
||||||
root = ElementTree.XML(optsxul.encode('utf-8'))
|
root = ElementTree.XML(optsxul.encode('utf-8'))
|
||||||
|
|
||||||
|
@ -53,7 +54,6 @@ class PrefsTests(unittest.TestCase):
|
||||||
settings = root.findall(xulNamespacePrefix + 'setting')
|
settings = root.findall(xulNamespacePrefix + 'setting')
|
||||||
|
|
||||||
def assertPref(setting, name, prefType, title):
|
def assertPref(setting, name, prefType, title):
|
||||||
packageName = 'jid1-fZHqN9JfrDBa8A@jetpack'
|
|
||||||
self.failUnlessEqual(setting.get('data-jetpack-id'), packageName)
|
self.failUnlessEqual(setting.get('data-jetpack-id'), packageName)
|
||||||
self.failUnlessEqual(setting.get('pref'),
|
self.failUnlessEqual(setting.get('pref'),
|
||||||
'extensions.' + packageName + '.' + name)
|
'extensions.' + packageName + '.' + name)
|
||||||
|
@ -88,6 +88,25 @@ class PrefsTests(unittest.TestCase):
|
||||||
]
|
]
|
||||||
self.failUnlessEqual(prefsjs, "\n".join(exp)+"\n")
|
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):
|
def testPackageWithNoPrefs(self):
|
||||||
self.makexpi('no-prefs')
|
self.makexpi('no-prefs')
|
||||||
self.failIf('options.xul' in self.xpi.namelist())
|
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")
|
prefsjs = self.xpi.read('defaults/preferences/prefs.js').decode("utf-8")
|
||||||
self.failUnlessEqual(prefsjs, "")
|
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):
|
class Bug588119Tests(unittest.TestCase):
|
||||||
def makexpi(self, pkg_name):
|
def makexpi(self, pkg_name):
|
||||||
|
|
|
@ -77,14 +77,15 @@ def build_xpi(template_root_dir, manifest, xpi_path,
|
||||||
validate_prefs(harness_options["preferences"])
|
validate_prefs(harness_options["preferences"])
|
||||||
|
|
||||||
opts_xul = parse_options(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"))
|
open('.options.xul', 'wb').write(opts_xul.encode("utf-8"))
|
||||||
zf.write('.options.xul', 'options.xul')
|
zf.write('.options.xul', 'options.xul')
|
||||||
os.remove('.options.xul')
|
os.remove('.options.xul')
|
||||||
|
|
||||||
from options_defaults import parse_options_defaults
|
from options_defaults import parse_options_defaults
|
||||||
prefs_js = parse_options_defaults(harness_options["preferences"],
|
prefs_js = parse_options_defaults(harness_options["preferences"],
|
||||||
harness_options["jetpackID"])
|
harness_options["preferencesBranch"])
|
||||||
open('.prefs.js', 'wb').write(prefs_js.encode("utf-8"))
|
open('.prefs.js', 'wb').write(prefs_js.encode("utf-8"))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -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);
|
|
@ -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
|
||||||
|
}]
|
||||||
|
}
|
|
@ -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);
|
|
@ -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-tabs'),
|
||||||
require('./test-page-mod'),
|
require('./test-page-mod'),
|
||||||
require('./test-private-browsing'),
|
require('./test-private-browsing'),
|
||||||
|
require('./test-sidebar'),
|
||||||
isGlobalPBSupported ? require('./test-global-private-browsing') : {}
|
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 app = require('sdk/system/xul-app');
|
||||||
const self = require('sdk/self');
|
const self = require('sdk/self');
|
||||||
const tabs = require('sdk/tabs');
|
const tabs = require('sdk/tabs');
|
||||||
|
const { preferencesBranch } = require('@loader/options');
|
||||||
|
|
||||||
const { AddonManager } = Cu.import('resource://gre/modules/AddonManager.jsm', {});
|
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);
|
require('sdk/test/runner').runTestsFromModule(module);
|
||||||
|
|
|
@ -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);
|
|
@ -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"
|
||||||
|
}
|
|
@ -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
|
// TEST: tab unloader
|
||||||
exports.testAutomaticDestroy = function(test) {
|
exports.testAutomaticDestroy = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let called = false;
|
let called = false;
|
||||||
|
|
||||||
let loader2 = Loader(module);
|
let loader2 = Loader(module);
|
||||||
|
@ -32,36 +30,36 @@ exports.testAutomaticDestroy = function(test) {
|
||||||
let tabs2Len = tabs2.length;
|
let tabs2Len = tabs2.length;
|
||||||
|
|
||||||
tabs2.on('open', function onOpen(tab) {
|
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;
|
called = true;
|
||||||
});
|
});
|
||||||
tabs2.on('ready', function onReady(tab) {
|
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;
|
called = true;
|
||||||
});
|
});
|
||||||
tabs2.on('select', function onSelect(tab) {
|
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;
|
called = true;
|
||||||
});
|
});
|
||||||
tabs2.on('close', function onClose(tab) {
|
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;
|
called = true;
|
||||||
});
|
});
|
||||||
loader2.unload();
|
loader2.unload();
|
||||||
|
|
||||||
tabs3.on('open', function onOpen(tab) {
|
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) {
|
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;
|
called = true;
|
||||||
});
|
});
|
||||||
tab.on('select', function onSelect(tab) {
|
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;
|
called = true;
|
||||||
});
|
});
|
||||||
tab.on('close', function onClose(tab) {
|
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;
|
called = true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -70,22 +68,22 @@ exports.testAutomaticDestroy = function(test) {
|
||||||
|
|
||||||
// Fire a tab event and ensure that the destroyed tab is inactive
|
// Fire a tab event and ensure that the destroyed tab is inactive
|
||||||
tabs.once('open', function(tab) {
|
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");
|
assert.equal(tabs2Len, tabs2.length, "tabs2 length was not changed");
|
||||||
test.assertEqual(tabs.length, (tabs2.length+2), "tabs.length > tabs2.length");
|
assert.equal(tabs.length, (tabs2.length+2), "tabs.length > tabs2.length");
|
||||||
|
|
||||||
tab.once('ready', function() {
|
tab.once('ready', function() {
|
||||||
test.pass('tab.once("ready") works!');
|
assert.pass('tab.once("ready") works!');
|
||||||
|
|
||||||
tab.once('close', function() {
|
tab.once('close', function() {
|
||||||
test.pass('tab.once("close") works!');
|
assert.pass('tab.once("close") works!');
|
||||||
|
|
||||||
timer.setTimeout(function () {
|
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
|
// end test
|
||||||
test.done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -97,8 +95,7 @@ exports.testAutomaticDestroy = function(test) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: tab properties
|
// TEST: tab properties
|
||||||
exports.testTabProperties = function(test) {
|
exports.testTabProperties = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
let { loader, messages } = LoaderWithHookedConsole();
|
let { loader, messages } = LoaderWithHookedConsole();
|
||||||
let tabs = loader.require('sdk/tabs');
|
let tabs = loader.require('sdk/tabs');
|
||||||
|
|
||||||
|
@ -107,39 +104,37 @@ exports.testTabProperties = function(test) {
|
||||||
tabs.open({
|
tabs.open({
|
||||||
url: url,
|
url: url,
|
||||||
onReady: function(tab) {
|
onReady: function(tab) {
|
||||||
test.assertEqual(tab.title, "foo", "title of the new tab matches");
|
assert.equal(tab.title, "foo", "title of the new tab matches");
|
||||||
test.assertEqual(tab.url, url, "URL of the new tab matches");
|
assert.equal(tab.url, url, "URL of the new tab matches");
|
||||||
test.assert(tab.favicon, "favicon of the new tab is not empty");
|
assert.ok(tab.favicon, "favicon of the new tab is not empty");
|
||||||
// TODO: remove need for this test by implementing the favicon feature
|
// 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 " +
|
"tab.favicon is deprecated, and " +
|
||||||
"currently favicon helpers are not yet supported " +
|
"currently favicon helpers are not yet supported " +
|
||||||
"by Fennec",
|
"by Fennec",
|
||||||
"favicon logs an error for now");
|
"favicon logs an error for now");
|
||||||
test.assertEqual(tab.style, null, "style of the new tab matches");
|
assert.equal(tab.style, null, "style of the new tab matches");
|
||||||
test.assertEqual(tab.index, tabsLen, "index of the new tab matches");
|
assert.equal(tab.index, tabsLen, "index of the new tab matches");
|
||||||
test.assertNotEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
|
assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
|
||||||
test.assertNotEqual(tab.id, null, "a tab object always has an id property");
|
assert.notEqual(tab.id, null, "a tab object always has an id property");
|
||||||
|
|
||||||
tab.close(function() {
|
tab.close(function() {
|
||||||
loader.unload();
|
loader.unload();
|
||||||
|
|
||||||
// end test
|
// end test
|
||||||
test.done();
|
done();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: tabs iterator and length property
|
// TEST: tabs iterator and length property
|
||||||
exports.testTabsIteratorAndLength = function(test) {
|
exports.testTabsIteratorAndLength = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let newTabs = [];
|
let newTabs = [];
|
||||||
let startCount = 0;
|
let startCount = 0;
|
||||||
for each (let t in tabs) startCount++;
|
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";
|
let url = "data:text/html;charset=utf-8,testTabsIteratorAndLength";
|
||||||
tabs.open({url: url, onOpen: function(tab) newTabs.push(tab)});
|
tabs.open({url: url, onOpen: function(tab) newTabs.push(tab)});
|
||||||
|
@ -149,26 +144,21 @@ exports.testTabsIteratorAndLength = function(test) {
|
||||||
onOpen: function(tab) {
|
onOpen: function(tab) {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
for each (let t in tabs) count++;
|
for each (let t in tabs) count++;
|
||||||
test.assertEqual(count, startCount + 3, "iterated tab count matches");
|
assert.equal(count, startCount + 3, "iterated tab count matches");
|
||||||
test.assertEqual(startCount + 3, tabs.length, "iterated tab count matches length property");
|
assert.equal(startCount + 3, tabs.length, "iterated tab count matches length property");
|
||||||
|
|
||||||
let newTabsLength = newTabs.length;
|
let newTabsLength = newTabs.length;
|
||||||
newTabs.forEach(function(t) t.close(function() {
|
newTabs.forEach(function(t) t.close(function() {
|
||||||
if (--newTabsLength > 0) return;
|
if (--newTabsLength > 0) return;
|
||||||
|
|
||||||
tab.close(function() {
|
tab.close(done);
|
||||||
// end test
|
|
||||||
test.done();
|
|
||||||
});
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: tab.url setter
|
// TEST: tab.url setter
|
||||||
exports.testTabLocation = function(test) {
|
exports.testTabLocation = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let url1 = "data:text/html;charset=utf-8,foo";
|
let url1 = "data:text/html;charset=utf-8,foo";
|
||||||
let url2 = "data:text/html;charset=utf-8,bar";
|
let url2 = "data:text/html;charset=utf-8,bar";
|
||||||
|
|
||||||
|
@ -177,12 +167,9 @@ exports.testTabLocation = function(test) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
tabs.removeListener('ready', onReady);
|
tabs.removeListener('ready', onReady);
|
||||||
test.pass("tab loaded the correct url");
|
assert.pass("tab loaded the correct url");
|
||||||
|
|
||||||
tab.close(function() {
|
tab.close(done);
|
||||||
// end test
|
|
||||||
test.done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
tabs.open({
|
tabs.open({
|
||||||
|
@ -194,9 +181,7 @@ exports.testTabLocation = function(test) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: tab.move()
|
// TEST: tab.move()
|
||||||
exports.testTabMove = function(test) {
|
exports.testTabMove = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let { loader, messages } = LoaderWithHookedConsole();
|
let { loader, messages } = LoaderWithHookedConsole();
|
||||||
let tabs = loader.require('sdk/tabs');
|
let tabs = loader.require('sdk/tabs');
|
||||||
|
|
||||||
|
@ -205,22 +190,22 @@ exports.testTabMove = function(test) {
|
||||||
tabs.open({
|
tabs.open({
|
||||||
url: url,
|
url: url,
|
||||||
onOpen: function(tab1) {
|
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({
|
tabs.open({
|
||||||
url: url,
|
url: url,
|
||||||
onOpen: function(tab) {
|
onOpen: function(tab) {
|
||||||
let i = tab.index;
|
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;
|
tab.index = 0;
|
||||||
test.assertEqual(tab.index, i, "tab index after move matches");
|
assert.equal(tab.index, i, "tab index after move matches");
|
||||||
test.assertEqual(JSON.stringify(messages),
|
assert.equal(JSON.stringify(messages),
|
||||||
JSON.stringify([ERR_FENNEC_MSG]),
|
JSON.stringify([ERR_FENNEC_MSG]),
|
||||||
"setting tab.index logs error");
|
"setting tab.index logs error");
|
||||||
// end test
|
// end test
|
||||||
tab1.close(function() tab.close(function() {
|
tab1.close(function() tab.close(function() {
|
||||||
loader.unload();
|
loader.unload();
|
||||||
test.done();
|
done();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -229,9 +214,7 @@ exports.testTabMove = function(test) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: open tab with default options
|
// TEST: open tab with default options
|
||||||
exports.testTabsOpen_alt = function(test) {
|
exports.testTabsOpen_alt = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let { loader, messages } = LoaderWithHookedConsole();
|
let { loader, messages } = LoaderWithHookedConsole();
|
||||||
let tabs = loader.require('sdk/tabs');
|
let tabs = loader.require('sdk/tabs');
|
||||||
let url = "data:text/html;charset=utf-8,default";
|
let url = "data:text/html;charset=utf-8,default";
|
||||||
|
@ -239,24 +222,22 @@ exports.testTabsOpen_alt = function(test) {
|
||||||
tabs.open({
|
tabs.open({
|
||||||
url: url,
|
url: url,
|
||||||
onReady: function(tab) {
|
onReady: function(tab) {
|
||||||
test.assertEqual(tab.url, url, "URL of the new tab matches");
|
assert.equal(tab.url, url, "URL of the new tab matches");
|
||||||
test.assertEqual(tabs.activeTab, tab, "URL of active tab in the current window matches");
|
assert.equal(tabs.activeTab, tab, "URL of active tab in the current window matches");
|
||||||
test.assertEqual(tab.isPinned, false, "The new tab is not pinned");
|
assert.equal(tab.isPinned, false, "The new tab is not pinned");
|
||||||
test.assertEqual(messages.length, 1, "isPinned logs error");
|
assert.equal(messages.length, 1, "isPinned logs error");
|
||||||
|
|
||||||
// end test
|
// end test
|
||||||
tab.close(function() {
|
tab.close(function() {
|
||||||
loader.unload();
|
loader.unload();
|
||||||
test.done();
|
done();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: open pinned tab
|
// TEST: open pinned tab
|
||||||
exports.testOpenPinned_alt = function(test) {
|
exports.testOpenPinned_alt = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let { loader, messages } = LoaderWithHookedConsole();
|
let { loader, messages } = LoaderWithHookedConsole();
|
||||||
let tabs = loader.require('sdk/tabs');
|
let tabs = loader.require('sdk/tabs');
|
||||||
let url = "about:blank";
|
let url = "about:blank";
|
||||||
|
@ -265,26 +246,24 @@ exports.testOpenPinned_alt = function(test) {
|
||||||
url: url,
|
url: url,
|
||||||
isPinned: true,
|
isPinned: true,
|
||||||
onOpen: function(tab) {
|
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
|
// We get two error message: one for tabs.open's isPinned argument
|
||||||
// and another one for tab.isPinned
|
// and another one for tab.isPinned
|
||||||
test.assertEqual(JSON.stringify(messages),
|
assert.equal(JSON.stringify(messages),
|
||||||
JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]),
|
JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]),
|
||||||
"isPinned logs error");
|
"isPinned logs error");
|
||||||
|
|
||||||
// end test
|
// end test
|
||||||
tab.close(function() {
|
tab.close(function() {
|
||||||
loader.unload();
|
loader.unload();
|
||||||
test.done();
|
done();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: pin/unpin opened tab
|
// TEST: pin/unpin opened tab
|
||||||
exports.testPinUnpin_alt = function(test) {
|
exports.testPinUnpin_alt = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let { loader, messages } = LoaderWithHookedConsole();
|
let { loader, messages } = LoaderWithHookedConsole();
|
||||||
let tabs = loader.require('sdk/tabs');
|
let tabs = loader.require('sdk/tabs');
|
||||||
let url = "data:text/html;charset=utf-8,default";
|
let url = "data:text/html;charset=utf-8,default";
|
||||||
|
@ -293,8 +272,8 @@ exports.testPinUnpin_alt = function(test) {
|
||||||
url: url,
|
url: url,
|
||||||
onOpen: function(tab) {
|
onOpen: function(tab) {
|
||||||
tab.pin();
|
tab.pin();
|
||||||
test.assertEqual(tab.isPinned, false, "The tab was pinned correctly");
|
assert.equal(tab.isPinned, false, "The tab was pinned correctly");
|
||||||
test.assertEqual(JSON.stringify(messages),
|
assert.equal(JSON.stringify(messages),
|
||||||
JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]),
|
JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]),
|
||||||
"tab.pin() logs error");
|
"tab.pin() logs error");
|
||||||
|
|
||||||
|
@ -302,35 +281,33 @@ exports.testPinUnpin_alt = function(test) {
|
||||||
messages.length = 0;
|
messages.length = 0;
|
||||||
|
|
||||||
tab.unpin();
|
tab.unpin();
|
||||||
test.assertEqual(tab.isPinned, false, "The tab was unpinned correctly");
|
assert.equal(tab.isPinned, false, "The tab was unpinned correctly");
|
||||||
test.assertEqual(JSON.stringify(messages),
|
assert.equal(JSON.stringify(messages),
|
||||||
JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]),
|
JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]),
|
||||||
"tab.unpin() logs error");
|
"tab.unpin() logs error");
|
||||||
|
|
||||||
// end test
|
// end test
|
||||||
tab.close(function() {
|
tab.close(function() {
|
||||||
loader.unload();
|
loader.unload();
|
||||||
test.done();
|
done();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: open tab in background
|
// TEST: open tab in background
|
||||||
exports.testInBackground = function(test) {
|
exports.testInBackground = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let activeUrl = tabs.activeTab.url;
|
let activeUrl = tabs.activeTab.url;
|
||||||
let url = "data:text/html;charset=utf-8,background";
|
let url = "data:text/html;charset=utf-8,background";
|
||||||
let window = windows.browserWindows.activeWindow;
|
let window = windows.browserWindows.activeWindow;
|
||||||
tabs.once('ready', function onReady(tab) {
|
tabs.once('ready', function onReady(tab) {
|
||||||
test.assertEqual(tabs.activeTab.url, activeUrl, "URL of active tab has not changed");
|
assert.equal(tabs.activeTab.url, activeUrl, "URL of active tab has not changed");
|
||||||
test.assertEqual(tab.url, url, "URL of the new background tab matches");
|
assert.equal(tab.url, url, "URL of the new background tab matches");
|
||||||
test.assertEqual(windows.browserWindows.activeWindow, window, "a new window was not opened");
|
assert.equal(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.notEqual(tabs.activeTab.url, url, "URL of active tab is not the new URL");
|
||||||
|
|
||||||
// end test
|
// end test
|
||||||
tab.close(function() test.done());
|
tab.close(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
tabs.open({
|
tabs.open({
|
||||||
|
@ -340,9 +317,7 @@ exports.testInBackground = function(test) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: open tab in new window
|
// TEST: open tab in new window
|
||||||
exports.testOpenInNewWindow = function(test) {
|
exports.testOpenInNewWindow = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let url = "data:text/html;charset=utf-8,newwindow";
|
let url = "data:text/html;charset=utf-8,newwindow";
|
||||||
let window = windows.browserWindows.activeWindow;
|
let window = windows.browserWindows.activeWindow;
|
||||||
|
|
||||||
|
@ -350,20 +325,18 @@ exports.testOpenInNewWindow = function(test) {
|
||||||
url: url,
|
url: url,
|
||||||
inNewWindow: true,
|
inNewWindow: true,
|
||||||
onReady: function(tab) {
|
onReady: function(tab) {
|
||||||
test.assertEqual(windows.browserWindows.length, 1, "a new window was not opened");
|
assert.equal(windows.browserWindows.length, 1, "a new window was not opened");
|
||||||
test.assertEqual(windows.browserWindows.activeWindow, window, "old window is active");
|
assert.equal(windows.browserWindows.activeWindow, window, "old window is active");
|
||||||
test.assertEqual(tab.url, url, "URL of the new tab matches");
|
assert.equal(tab.url, url, "URL of the new tab matches");
|
||||||
test.assertEqual(tabs.activeTab, tab, "tab is the activeTab");
|
assert.equal(tabs.activeTab, tab, "tab is the activeTab");
|
||||||
|
|
||||||
tab.close(function() test.done());
|
tab.close(done);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: onOpen event handler
|
// TEST: onOpen event handler
|
||||||
exports.testTabsEvent_onOpen = function(test) {
|
exports.testTabsEvent_onOpen = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let url = URL.replace('#title#', 'testTabsEvent_onOpen');
|
let url = URL.replace('#title#', 'testTabsEvent_onOpen');
|
||||||
let eventCount = 0;
|
let eventCount = 0;
|
||||||
|
|
||||||
|
@ -375,21 +348,19 @@ exports.testTabsEvent_onOpen = function(test) {
|
||||||
|
|
||||||
// add listener via collection add
|
// add listener via collection add
|
||||||
tabs.on('open', function listener2(tab) {
|
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', listener1);
|
||||||
tabs.removeListener('open', listener2);
|
tabs.removeListener('open', listener2);
|
||||||
|
|
||||||
// ends test
|
// ends test
|
||||||
tab.close(function() test.done());
|
tab.close(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
tabs.open(url);
|
tabs.open(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: onClose event handler
|
// TEST: onClose event handler
|
||||||
exports.testTabsEvent_onClose = function(test) {
|
exports.testTabsEvent_onClose = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let url = "data:text/html;charset=utf-8,onclose";
|
let url = "data:text/html;charset=utf-8,onclose";
|
||||||
let eventCount = 0;
|
let eventCount = 0;
|
||||||
|
|
||||||
|
@ -401,12 +372,12 @@ exports.testTabsEvent_onClose = function(test) {
|
||||||
|
|
||||||
// add listener via collection add
|
// add listener via collection add
|
||||||
tabs.on('close', function listener2(tab) {
|
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', listener1);
|
||||||
tabs.removeListener('close', listener2);
|
tabs.removeListener('close', listener2);
|
||||||
|
|
||||||
// end test
|
// end test
|
||||||
test.done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
tabs.on('ready', function onReady(tab) {
|
tabs.on('ready', function onReady(tab) {
|
||||||
|
@ -418,9 +389,7 @@ exports.testTabsEvent_onClose = function(test) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: onClose event handler when a window is closed
|
// TEST: onClose event handler when a window is closed
|
||||||
exports.testTabsEvent_onCloseWindow = function(test) {
|
exports.testTabsEvent_onCloseWindow = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let closeCount = 0, individualCloseCount = 0;
|
let closeCount = 0, individualCloseCount = 0;
|
||||||
function listener() {
|
function listener() {
|
||||||
closeCount++;
|
closeCount++;
|
||||||
|
@ -434,12 +403,12 @@ exports.testTabsEvent_onCloseWindow = function(test) {
|
||||||
if (++openTabs == 3) {
|
if (++openTabs == 3) {
|
||||||
tabs.removeListener("close", listener);
|
tabs.removeListener("close", listener);
|
||||||
|
|
||||||
test.assertEqual(closeCount, 3, "Correct number of close events received");
|
assert.equal(closeCount, 3, "Correct number of close events received");
|
||||||
test.assertEqual(individualCloseCount, 3,
|
assert.equal(individualCloseCount, 3,
|
||||||
"Each tab with an attached onClose listener received a close " +
|
"Each tab with an attached onClose listener received a close " +
|
||||||
"event when the window was closed");
|
"event when the window was closed");
|
||||||
|
|
||||||
test.done();
|
done();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -464,9 +433,7 @@ exports.testTabsEvent_onCloseWindow = function(test) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: onReady event handler
|
// TEST: onReady event handler
|
||||||
exports.testTabsEvent_onReady = function(test) {
|
exports.testTabsEvent_onReady = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let url = "data:text/html;charset=utf-8,onready";
|
let url = "data:text/html;charset=utf-8,onready";
|
||||||
let eventCount = 0;
|
let eventCount = 0;
|
||||||
|
|
||||||
|
@ -478,21 +445,19 @@ exports.testTabsEvent_onReady = function(test) {
|
||||||
|
|
||||||
// add listener via collection add
|
// add listener via collection add
|
||||||
tabs.on('ready', function listener2(tab) {
|
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', listener1);
|
||||||
tabs.removeListener('ready', listener2);
|
tabs.removeListener('ready', listener2);
|
||||||
|
|
||||||
// end test
|
// end test
|
||||||
tab.close(function() test.done());
|
tab.close(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
tabs.open(url);
|
tabs.open(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: onActivate event handler
|
// TEST: onActivate event handler
|
||||||
exports.testTabsEvent_onActivate = function(test) {
|
exports.testTabsEvent_onActivate = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let url = "data:text/html;charset=utf-8,onactivate";
|
let url = "data:text/html;charset=utf-8,onactivate";
|
||||||
let eventCount = 0;
|
let eventCount = 0;
|
||||||
|
|
||||||
|
@ -504,22 +469,20 @@ exports.testTabsEvent_onActivate = function(test) {
|
||||||
|
|
||||||
// add listener via collection add
|
// add listener via collection add
|
||||||
tabs.on('activate', function listener2(tab) {
|
tabs.on('activate', function listener2(tab) {
|
||||||
test.assertEqual(++eventCount, 2, "both listeners notified");
|
assert.equal(++eventCount, 2, "both listeners notified");
|
||||||
test.assertEqual(tab, tabs.activeTab, 'the active tab is correct');
|
assert.equal(tab, tabs.activeTab, 'the active tab is correct');
|
||||||
tabs.removeListener('activate', listener1);
|
tabs.removeListener('activate', listener1);
|
||||||
tabs.removeListener('activate', listener2);
|
tabs.removeListener('activate', listener2);
|
||||||
|
|
||||||
// end test
|
// end test
|
||||||
tab.close(function() test.done());
|
tab.close(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
tabs.open(url);
|
tabs.open(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: onDeactivate event handler
|
// TEST: onDeactivate event handler
|
||||||
exports.testTabsEvent_onDeactivate = function(test) {
|
exports.testTabsEvent_onDeactivate = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let url = "data:text/html;charset=utf-8,ondeactivate";
|
let url = "data:text/html;charset=utf-8,ondeactivate";
|
||||||
let eventCount = 0;
|
let eventCount = 0;
|
||||||
|
|
||||||
|
@ -531,13 +494,13 @@ exports.testTabsEvent_onDeactivate = function(test) {
|
||||||
|
|
||||||
// add listener via collection add
|
// add listener via collection add
|
||||||
tabs.on('deactivate', function listener2(tab) {
|
tabs.on('deactivate', function listener2(tab) {
|
||||||
test.assertEqual(++eventCount, 2, 'both listeners notified');
|
assert.equal(++eventCount, 2, 'both listeners notified');
|
||||||
test.assertNotEqual(tab, tabs.activeTab, 'the active tab is not the deactivated tab');
|
assert.notEqual(tab, tabs.activeTab, 'the active tab is not the deactivated tab');
|
||||||
tabs.removeListener('deactivate', listener1);
|
tabs.removeListener('deactivate', listener1);
|
||||||
tabs.removeListener('deactivate', listener2);
|
tabs.removeListener('deactivate', listener2);
|
||||||
|
|
||||||
// end test
|
// end test
|
||||||
tab.close(function() test.done());
|
tab.close(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
tabs.on('activate', function onActivate(tab) {
|
tabs.on('activate', function onActivate(tab) {
|
||||||
|
@ -550,9 +513,7 @@ exports.testTabsEvent_onDeactivate = function(test) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: per-tab event handlers
|
// TEST: per-tab event handlers
|
||||||
exports.testPerTabEvents = function(test) {
|
exports.testPerTabEvents = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let eventCount = 0;
|
let eventCount = 0;
|
||||||
|
|
||||||
tabs.open({
|
tabs.open({
|
||||||
|
@ -566,19 +527,18 @@ exports.testPerTabEvents = function(test) {
|
||||||
|
|
||||||
// add listener via collection add
|
// add listener via collection add
|
||||||
tab.on('ready', function listener2() {
|
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', listener1);
|
||||||
tab.removeListener('ready', listener2);
|
tab.removeListener('ready', listener2);
|
||||||
|
|
||||||
// end test
|
// end test
|
||||||
tab.close(function() test.done());
|
tab.close(done);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.testUniqueTabIds = function(test) {
|
exports.testUniqueTabIds = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
var tabs = require('sdk/tabs');
|
var tabs = require('sdk/tabs');
|
||||||
var tabIds = {};
|
var tabIds = {};
|
||||||
var steps = [
|
var steps = [
|
||||||
|
@ -601,8 +561,8 @@ exports.testUniqueTabIds = function(test) {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
function (index) {
|
function (index) {
|
||||||
test.assertNotEqual(tabIds.tab1, tabIds.tab2, "Tab ids should be unique.");
|
assert.notEqual(tabIds.tab1, tabIds.tab2, "Tab ids should be unique.");
|
||||||
test.done();
|
done();
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -617,3 +577,5 @@ exports.testUniqueTabIds = function(test) {
|
||||||
|
|
||||||
next(0);
|
next(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
require('sdk/test').run(exports);
|
||||||
|
|
|
@ -19,19 +19,17 @@ const base64png = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYA" +
|
||||||
"bWRR9AAAAABJRU5ErkJggg%3D%3D";
|
"bWRR9AAAAABJRU5ErkJggg%3D%3D";
|
||||||
|
|
||||||
// Bug 682681 - tab.title should never be empty
|
// Bug 682681 - tab.title should never be empty
|
||||||
exports.testBug682681_aboutURI = function(test) {
|
exports.testBug682681_aboutURI = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let tabStrings = StringBundle('chrome://browser/locale/tabbrowser.properties');
|
let tabStrings = StringBundle('chrome://browser/locale/tabbrowser.properties');
|
||||||
|
|
||||||
tabs.on('ready', function onReady(tab) {
|
tabs.on('ready', function onReady(tab) {
|
||||||
tabs.removeListener('ready', onReady);
|
tabs.removeListener('ready', onReady);
|
||||||
|
|
||||||
test.assertEqual(tab.title,
|
assert.equal(tab.title,
|
||||||
tabStrings.get('tabs.emptyTabTitle'),
|
tabStrings.get('tabs.emptyTabTitle'),
|
||||||
"title of about: tab is not blank");
|
"title of about: tab is not blank");
|
||||||
|
|
||||||
tab.close(function() test.done());
|
tab.close(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
// open a about: url
|
// open a about: url
|
||||||
|
@ -42,42 +40,36 @@ exports.testBug682681_aboutURI = function(test) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// related to Bug 682681
|
// related to Bug 682681
|
||||||
exports.testTitleForDataURI = function(test) {
|
exports.testTitleForDataURI = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
tabs.open({
|
tabs.open({
|
||||||
url: "data:text/html;charset=utf-8,<title>tab</title>",
|
url: "data:text/html;charset=utf-8,<title>tab</title>",
|
||||||
inBackground: true,
|
inBackground: true,
|
||||||
onReady: function(tab) {
|
onReady: function(tab) {
|
||||||
test.assertEqual(tab.title, "tab", "data: title is not Connecting...");
|
assert.equal(tab.title, "tab", "data: title is not Connecting...");
|
||||||
tab.close(function() test.done());
|
tab.close(done);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: 'BrowserWindow' instance creation on tab 'activate' event
|
// TEST: 'BrowserWindow' instance creation on tab 'activate' event
|
||||||
// See bug 648244: there was a infinite loop.
|
// See bug 648244: there was a infinite loop.
|
||||||
exports.testBrowserWindowCreationOnActivate = function(test) {
|
exports.testBrowserWindowCreationOnActivate = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let windows = require("sdk/windows").browserWindows;
|
let windows = require("sdk/windows").browserWindows;
|
||||||
let gotActivate = false;
|
let gotActivate = false;
|
||||||
|
|
||||||
tabs.once('activate', function onActivate(eventTab) {
|
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;
|
gotActivate = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
open().then(function(window) {
|
open().then(function(window) {
|
||||||
test.assert(gotActivate, "Received activate event before openBrowserWindow's callback is called");
|
assert.ok(gotActivate, "Received activate event before openBrowserWindow's callback is called");
|
||||||
closeBrowserWindow(window, function () test.done());
|
close(window).then(done);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TEST: tab unloader
|
// TEST: tab unloader
|
||||||
exports.testAutomaticDestroy = function(test) {
|
exports.testAutomaticDestroy = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
// Create a second tab instance that we will destroy
|
// Create a second tab instance that we will destroy
|
||||||
let called = false;
|
let called = false;
|
||||||
|
|
||||||
|
@ -92,20 +84,18 @@ exports.testAutomaticDestroy = function(test) {
|
||||||
// Fire a tab event and ensure that the destroyed tab is inactive
|
// Fire a tab event and ensure that the destroyed tab is inactive
|
||||||
tabs.once('open', function (tab) {
|
tabs.once('open', function (tab) {
|
||||||
timer.setTimeout(function () {
|
timer.setTimeout(function () {
|
||||||
test.assert(!called, "Unloaded tab module is destroyed and inactive");
|
assert.ok(!called, "Unloaded tab module is destroyed and inactive");
|
||||||
tab.close(test.done.bind(test));
|
tab.close(done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
tabs.open("data:text/html;charset=utf-8,foo");
|
tabs.open("data:text/html;charset=utf-8,foo");
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.testTabPropertiesInNewWindow = function(test) {
|
exports.testTabPropertiesInNewWindow = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
function onReadyOrLoad (tab) {
|
function onReadyOrLoad (tab) {
|
||||||
if (count++) {
|
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,
|
inNewWindow: true,
|
||||||
url: url,
|
url: url,
|
||||||
onReady: function(tab) {
|
onReady: function(tab) {
|
||||||
test.assertEqual(tab.title, "foo", "title of the new tab matches");
|
assert.equal(tab.title, "foo", "title of the new tab matches");
|
||||||
test.assertEqual(tab.url, url, "URL of the new tab matches");
|
assert.equal(tab.url, url, "URL of the new tab matches");
|
||||||
test.assert(tab.favicon, "favicon of the new tab is not empty");
|
assert.ok(tab.favicon, "favicon of the new tab is not empty");
|
||||||
test.assertEqual(tab.style, null, "style of the new tab matches");
|
assert.equal(tab.style, null, "style of the new tab matches");
|
||||||
test.assertEqual(tab.index, 0, "index of the new tab matches");
|
assert.equal(tab.index, 0, "index of the new tab matches");
|
||||||
test.assertNotEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
|
assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
|
||||||
test.assertNotEqual(tab.id, null, "a tab object always has an id property.");
|
assert.notEqual(tab.id, null, "a tab object always has an id property.");
|
||||||
|
|
||||||
onReadyOrLoad(tab);
|
onReadyOrLoad(tab);
|
||||||
},
|
},
|
||||||
onLoad: function(tab) {
|
onLoad: function(tab) {
|
||||||
test.assertEqual(tab.title, "foo", "title of the new tab matches");
|
assert.equal(tab.title, "foo", "title of the new tab matches");
|
||||||
test.assertEqual(tab.url, url, "URL of the new tab matches");
|
assert.equal(tab.url, url, "URL of the new tab matches");
|
||||||
test.assert(tab.favicon, "favicon of the new tab is not empty");
|
assert.ok(tab.favicon, "favicon of the new tab is not empty");
|
||||||
test.assertEqual(tab.style, null, "style of the new tab matches");
|
assert.equal(tab.style, null, "style of the new tab matches");
|
||||||
test.assertEqual(tab.index, 0, "index of the new tab matches");
|
assert.equal(tab.index, 0, "index of the new tab matches");
|
||||||
test.assertNotEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
|
assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
|
||||||
test.assertNotEqual(tab.id, null, "a tab object always has an id property.");
|
assert.notEqual(tab.id, null, "a tab object always has an id property.");
|
||||||
|
|
||||||
onReadyOrLoad(tab);
|
onReadyOrLoad(tab);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.testTabPropertiesInSameWindow = function(test) {
|
exports.testTabPropertiesInSameWindow = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
// Get current count of tabs so we know the index of the
|
// Get current count of tabs so we know the index of the
|
||||||
// new tab, bug 893846
|
// new tab, bug 893846
|
||||||
let tabCount = tabs.length;
|
let tabCount = tabs.length;
|
||||||
let count = 0;
|
let count = 0;
|
||||||
function onReadyOrLoad (tab) {
|
function onReadyOrLoad (tab) {
|
||||||
if (count++) {
|
if (count++) {
|
||||||
tab.close(test.done.bind(test));
|
tab.close(done);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,24 +143,24 @@ exports.testTabPropertiesInSameWindow = function(test) {
|
||||||
tabs.open({
|
tabs.open({
|
||||||
url: url,
|
url: url,
|
||||||
onReady: function(tab) {
|
onReady: function(tab) {
|
||||||
test.assertEqual(tab.title, "foo", "title of the new tab matches");
|
assert.equal(tab.title, "foo", "title of the new tab matches");
|
||||||
test.assertEqual(tab.url, url, "URL of the new tab matches");
|
assert.equal(tab.url, url, "URL of the new tab matches");
|
||||||
test.assert(tab.favicon, "favicon of the new tab is not empty");
|
assert.ok(tab.favicon, "favicon of the new tab is not empty");
|
||||||
test.assertEqual(tab.style, null, "style of the new tab matches");
|
assert.equal(tab.style, null, "style of the new tab matches");
|
||||||
test.assertEqual(tab.index, tabCount, "index of the new tab matches");
|
assert.equal(tab.index, tabCount, "index of the new tab matches");
|
||||||
test.assertNotEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
|
assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
|
||||||
test.assertNotEqual(tab.id, null, "a tab object always has an id property.");
|
assert.notEqual(tab.id, null, "a tab object always has an id property.");
|
||||||
|
|
||||||
onReadyOrLoad(tab);
|
onReadyOrLoad(tab);
|
||||||
},
|
},
|
||||||
onLoad: function(tab) {
|
onLoad: function(tab) {
|
||||||
test.assertEqual(tab.title, "foo", "title of the new tab matches");
|
assert.equal(tab.title, "foo", "title of the new tab matches");
|
||||||
test.assertEqual(tab.url, url, "URL of the new tab matches");
|
assert.equal(tab.url, url, "URL of the new tab matches");
|
||||||
test.assert(tab.favicon, "favicon of the new tab is not empty");
|
assert.ok(tab.favicon, "favicon of the new tab is not empty");
|
||||||
test.assertEqual(tab.style, null, "style of the new tab matches");
|
assert.equal(tab.style, null, "style of the new tab matches");
|
||||||
test.assertEqual(tab.index, tabCount, "index of the new tab matches");
|
assert.equal(tab.index, tabCount, "index of the new tab matches");
|
||||||
test.assertNotEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
|
assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
|
||||||
test.assertNotEqual(tab.id, null, "a tab object always has an id property.");
|
assert.notEqual(tab.id, null, "a tab object always has an id property.");
|
||||||
|
|
||||||
onReadyOrLoad(tab);
|
onReadyOrLoad(tab);
|
||||||
}
|
}
|
||||||
|
@ -180,9 +168,7 @@ exports.testTabPropertiesInSameWindow = function(test) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: tab properties
|
// TEST: tab properties
|
||||||
exports.testTabContentTypeAndReload = function(test) {
|
exports.testTabContentTypeAndReload = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
open().then(focus).then(function(window) {
|
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 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>";
|
let urlXML = "data:text/xml;charset=utf-8,<foo>bar</foo>";
|
||||||
|
@ -190,11 +176,12 @@ exports.testTabContentTypeAndReload = function(test) {
|
||||||
url: url,
|
url: url,
|
||||||
onReady: function(tab) {
|
onReady: function(tab) {
|
||||||
if (tab.url === url) {
|
if (tab.url === url) {
|
||||||
test.assertEqual(tab.contentType, "text/html");
|
assert.equal(tab.contentType, "text/html");
|
||||||
tab.url = urlXML;
|
tab.url = urlXML;
|
||||||
} else {
|
}
|
||||||
test.assertEqual(tab.contentType, "text/xml");
|
else {
|
||||||
closeBrowserWindow(window, function() test.done());
|
assert.equal(tab.contentType, "text/xml");
|
||||||
|
close(window).then(done);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -202,13 +189,11 @@ exports.testTabContentTypeAndReload = function(test) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: tabs iterator and length property
|
// TEST: tabs iterator and length property
|
||||||
exports.testTabsIteratorAndLength = function(test) {
|
exports.testTabsIteratorAndLength = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
open(null, { features: { chrome: true, toolbar: true } }).then(focus).then(function(window) {
|
open(null, { features: { chrome: true, toolbar: true } }).then(focus).then(function(window) {
|
||||||
let startCount = 0;
|
let startCount = 0;
|
||||||
for each (let t in tabs) startCount++;
|
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";
|
let url = "data:text/html;charset=utf-8,default";
|
||||||
|
|
||||||
tabs.open(url);
|
tabs.open(url);
|
||||||
|
@ -218,19 +203,17 @@ exports.testTabsIteratorAndLength = function(test) {
|
||||||
onOpen: function(tab) {
|
onOpen: function(tab) {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
for each (let t in tabs) count++;
|
for each (let t in tabs) count++;
|
||||||
test.assertEqual(count, startCount + 3, "iterated tab count matches");
|
assert.equal(count, startCount + 3, "iterated tab count matches");
|
||||||
test.assertEqual(startCount + 3, tabs.length, "iterated tab count matches length property");
|
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
|
// TEST: tab.url setter
|
||||||
exports.testTabLocation = function(test) {
|
exports.testTabLocation = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
open().then(focus).then(function(window) {
|
open().then(focus).then(function(window) {
|
||||||
let url1 = "data:text/html;charset=utf-8,foo";
|
let url1 = "data:text/html;charset=utf-8,foo";
|
||||||
let url2 = "data:text/html;charset=utf-8,bar";
|
let url2 = "data:text/html;charset=utf-8,bar";
|
||||||
|
@ -239,8 +222,8 @@ exports.testTabLocation = function(test) {
|
||||||
if (tab.url != url2)
|
if (tab.url != url2)
|
||||||
return;
|
return;
|
||||||
tabs.removeListener('ready', onReady);
|
tabs.removeListener('ready', onReady);
|
||||||
test.pass("tab.load() loaded the correct url");
|
assert.pass("tab.load() loaded the correct url");
|
||||||
closeBrowserWindow(window, function() test.done());
|
close(window).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
tabs.open({
|
tabs.open({
|
||||||
|
@ -253,119 +236,108 @@ exports.testTabLocation = function(test) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: tab.close()
|
// TEST: tab.close()
|
||||||
exports.testTabClose = function(test) {
|
exports.testTabClose = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let url = "data:text/html;charset=utf-8,foo";
|
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.on('ready', function onReady(tab) {
|
||||||
tabs.removeListener('ready', onReady);
|
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;
|
let secondOnCloseCalled = false;
|
||||||
|
|
||||||
// Bug 699450: Multiple calls to tab.close should not throw
|
// Bug 699450: Multiple calls to tab.close should not throw
|
||||||
tab.close(function() secondOnCloseCalled = true);
|
tab.close(function() secondOnCloseCalled = true);
|
||||||
try {
|
try {
|
||||||
tab.close(function () {
|
tab.close(function () {
|
||||||
test.assert(secondOnCloseCalled,
|
assert.ok(secondOnCloseCalled,
|
||||||
"The immediate second call to tab.close gots its callback fired");
|
"The immediate second call to tab.close gots its callback fired");
|
||||||
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");
|
||||||
test.done();
|
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch(e) {
|
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);
|
tabs.open(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: tab.move()
|
// TEST: tab.move()
|
||||||
exports.testTabMove = function(test) {
|
exports.testTabMove = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
open().then(focus).then(function(window) {
|
open().then(focus).then(function(window) {
|
||||||
let url = "data:text/html;charset=utf-8,foo";
|
let url = "data:text/html;charset=utf-8,foo";
|
||||||
|
|
||||||
tabs.open({
|
tabs.open({
|
||||||
url: url,
|
url: url,
|
||||||
onOpen: function(tab) {
|
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;
|
tab.index = 0;
|
||||||
test.assertEqual(tab.index, 0, "tab index after move matches");
|
assert.equal(tab.index, 0, "tab index after move matches");
|
||||||
closeBrowserWindow(window, function() test.done());
|
close(window).then(done);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: open tab with default options
|
// TEST: open tab with default options
|
||||||
exports.testOpen = function(test) {
|
exports.testOpen = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let url = "data:text/html;charset=utf-8,default";
|
let url = "data:text/html;charset=utf-8,default";
|
||||||
tabs.open({
|
tabs.open({
|
||||||
url: url,
|
url: url,
|
||||||
onReady: function(tab) {
|
onReady: function(tab) {
|
||||||
test.assertEqual(tab.url, url, "URL of the new tab matches");
|
assert.equal(tab.url, url, "URL of the new tab matches");
|
||||||
test.assertEqual(tab.isPinned, false, "The new tab is not pinned");
|
assert.equal(tab.isPinned, false, "The new tab is not pinned");
|
||||||
|
|
||||||
tab.close(function() test.done());
|
tab.close(done);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: opening a pinned tab
|
// TEST: opening a pinned tab
|
||||||
exports.testOpenPinned = function(test) {
|
exports.testOpenPinned = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let url = "data:text/html;charset=utf-8,default";
|
let url = "data:text/html;charset=utf-8,default";
|
||||||
tabs.open({
|
tabs.open({
|
||||||
url: url,
|
url: url,
|
||||||
isPinned: true,
|
isPinned: true,
|
||||||
onOpen: function(tab) {
|
onOpen: function(tab) {
|
||||||
test.assertEqual(tab.isPinned, true, "The new tab is pinned");
|
assert.equal(tab.isPinned, true, "The new tab is pinned");
|
||||||
tab.close(test.done.bind(test));
|
tab.close(done);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: pin/unpin opened tab
|
// TEST: pin/unpin opened tab
|
||||||
exports.testPinUnpin = function(test) {
|
exports.testPinUnpin = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let url = "data:text/html;charset=utf-8,default";
|
let url = "data:text/html;charset=utf-8,default";
|
||||||
tabs.open({
|
tabs.open({
|
||||||
url: url,
|
url: url,
|
||||||
inBackground: true,
|
inBackground: true,
|
||||||
onOpen: function(tab) {
|
onOpen: function(tab) {
|
||||||
tab.pin();
|
tab.pin();
|
||||||
test.assertEqual(tab.isPinned, true, "The tab was pinned correctly");
|
assert.equal(tab.isPinned, true, "The tab was pinned correctly");
|
||||||
tab.unpin();
|
tab.unpin();
|
||||||
test.assertEqual(tab.isPinned, false, "The tab was unpinned correctly");
|
assert.equal(tab.isPinned, false, "The tab was unpinned correctly");
|
||||||
tab.close(test.done.bind(test));
|
tab.close(done);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TEST: open tab in background
|
// TEST: open tab in background
|
||||||
exports.testInBackground = function(test) {
|
exports.testInBackground = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let window = getMostRecentBrowserWindow();
|
let window = getMostRecentBrowserWindow();
|
||||||
let activeUrl = tabs.activeTab.url;
|
let activeUrl = tabs.activeTab.url;
|
||||||
let url = "data:text/html;charset=utf-8,background";
|
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.on('ready', function onReady(tab) {
|
||||||
tabs.removeListener('ready', onReady);
|
tabs.removeListener('ready', onReady);
|
||||||
test.assertEqual(tabs.activeTab.url, activeUrl, "URL of active tab has not changed");
|
assert.equal(tabs.activeTab.url, activeUrl, "URL of active tab has not changed");
|
||||||
test.assertEqual(tab.url, url, "URL of the new background tab matches");
|
assert.equal(tab.url, url, "URL of the new background tab matches");
|
||||||
test.assertEqual(activeWindow, window, "a new window was not opened");
|
assert.equal(activeWindow, window, "a new window was not opened");
|
||||||
test.assertNotEqual(tabs.activeTab.url, url, "URL of active tab is not the new URL");
|
assert.notEqual(tabs.activeTab.url, url, "URL of active tab is not the new URL");
|
||||||
tab.close(test.done.bind(test));
|
tab.close(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
tabs.open({
|
tabs.open({
|
||||||
|
@ -375,9 +347,7 @@ exports.testInBackground = function(test) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TEST: open tab in new window
|
// TEST: open tab in new window
|
||||||
exports.testOpenInNewWindow = function(test) {
|
exports.testOpenInNewWindow = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let startWindowCount = windows().length;
|
let startWindowCount = windows().length;
|
||||||
|
|
||||||
let url = "data:text/html;charset=utf-8,testOpenInNewWindow";
|
let url = "data:text/html;charset=utf-8,testOpenInNewWindow";
|
||||||
|
@ -386,25 +356,23 @@ exports.testOpenInNewWindow = function(test) {
|
||||||
inNewWindow: true,
|
inNewWindow: true,
|
||||||
onReady: function(tab) {
|
onReady: function(tab) {
|
||||||
let newWindow = getOwnerWindow(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() {
|
onFocus(newWindow).then(function() {
|
||||||
test.assertEqual(activeWindow, newWindow, "new window is active");
|
assert.equal(activeWindow, newWindow, "new window is active");
|
||||||
test.assertEqual(tab.url, url, "URL of the new tab matches");
|
assert.equal(tab.url, url, "URL of the new tab matches");
|
||||||
test.assertEqual(newWindow.content.location, url, "URL of new tab in new window matches");
|
assert.equal(newWindow.content.location, url, "URL of new tab in new window matches");
|
||||||
test.assertEqual(tabs.activeTab.url, url, "URL of activeTab matches");
|
assert.equal(tabs.activeTab.url, url, "URL of activeTab matches");
|
||||||
|
|
||||||
closeBrowserWindow(newWindow, test.done.bind(test));
|
close(newWindow).then(done);
|
||||||
}, test.fail).then(null, test.fail);
|
}, assert.fail).then(null, assert.fail);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test tab.open inNewWindow + onOpen combination
|
// Test tab.open inNewWindow + onOpen combination
|
||||||
exports.testOpenInNewWindowOnOpen = function(test) {
|
exports.testOpenInNewWindowOnOpen = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let startWindowCount = windows().length;
|
let startWindowCount = windows().length;
|
||||||
|
|
||||||
let url = "data:text/html;charset=utf-8,newwindow";
|
let url = "data:text/html;charset=utf-8,newwindow";
|
||||||
|
@ -415,20 +383,17 @@ exports.testOpenInNewWindowOnOpen = function(test) {
|
||||||
let newWindow = getOwnerWindow(tab);
|
let newWindow = getOwnerWindow(tab);
|
||||||
|
|
||||||
onFocus(newWindow).then(function() {
|
onFocus(newWindow).then(function() {
|
||||||
test.assertEqual(windows().length, startWindowCount + 1, "a new window was opened");
|
assert.equal(windows().length, startWindowCount + 1, "a new window was opened");
|
||||||
test.assertEqual(activeWindow, newWindow, "new window is active");
|
assert.equal(activeWindow, newWindow, "new window is active");
|
||||||
|
|
||||||
closeBrowserWindow(newWindow, function() {
|
close(newWindow).then(done);
|
||||||
test.done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: onOpen event handler
|
// TEST: onOpen event handler
|
||||||
exports.testTabsEvent_onOpen = function(test) {
|
exports.testTabsEvent_onOpen = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
openBrowserWindow(function(window, browser) {
|
openBrowserWindow(function(window, browser) {
|
||||||
let url = "data:text/html;charset=utf-8,1";
|
let url = "data:text/html;charset=utf-8,1";
|
||||||
let eventCount = 0;
|
let eventCount = 0;
|
||||||
|
@ -441,10 +406,10 @@ exports.testTabsEvent_onOpen = function(test) {
|
||||||
|
|
||||||
// add listener via collection add
|
// add listener via collection add
|
||||||
tabs.on('open', function listener2(tab) {
|
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', listener1);
|
||||||
tabs.removeListener('open', listener2);
|
tabs.removeListener('open', listener2);
|
||||||
closeBrowserWindow(window, function() test.done());
|
close(window).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
tabs.open(url);
|
tabs.open(url);
|
||||||
|
@ -452,8 +417,7 @@ exports.testTabsEvent_onOpen = function(test) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: onClose event handler
|
// TEST: onClose event handler
|
||||||
exports.testTabsEvent_onClose = function(test) {
|
exports.testTabsEvent_onClose = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
openBrowserWindow(function(window, browser) {
|
openBrowserWindow(function(window, browser) {
|
||||||
let url = "data:text/html;charset=utf-8,onclose";
|
let url = "data:text/html;charset=utf-8,onclose";
|
||||||
let eventCount = 0;
|
let eventCount = 0;
|
||||||
|
@ -466,10 +430,10 @@ exports.testTabsEvent_onClose = function(test) {
|
||||||
|
|
||||||
// add listener via collection add
|
// add listener via collection add
|
||||||
tabs.on('close', function listener2(tab) {
|
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', listener1);
|
||||||
tabs.removeListener('close', listener2);
|
tabs.removeListener('close', listener2);
|
||||||
closeBrowserWindow(window, function() test.done());
|
close(window).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
tabs.on('ready', function onReady(tab) {
|
tabs.on('ready', function onReady(tab) {
|
||||||
|
@ -482,8 +446,7 @@ exports.testTabsEvent_onClose = function(test) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: onClose event handler when a window is closed
|
// TEST: onClose event handler when a window is closed
|
||||||
exports.testTabsEvent_onCloseWindow = function(test) {
|
exports.testTabsEvent_onCloseWindow = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
let closeCount = 0;
|
let closeCount = 0;
|
||||||
let individualCloseCount = 0;
|
let individualCloseCount = 0;
|
||||||
|
|
||||||
|
@ -499,12 +462,12 @@ exports.testTabsEvent_onCloseWindow = function(test) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
test.assertEqual(closeCount, 4, "Correct number of close events received");
|
assert.equal(closeCount, 4, "Correct number of close events received");
|
||||||
test.assertEqual(individualCloseCount, 3,
|
assert.equal(individualCloseCount, 3,
|
||||||
"Each tab with an attached onClose listener received a close " +
|
"Each tab with an attached onClose listener received a close " +
|
||||||
"event when the window was closed");
|
"event when the window was closed");
|
||||||
|
|
||||||
test.done();
|
done();
|
||||||
}
|
}
|
||||||
|
|
||||||
// One tab is already open with the window
|
// One tab is already open with the window
|
||||||
|
@ -536,8 +499,7 @@ exports.testTabsEvent_onCloseWindow = function(test) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TEST: onReady event handler
|
// TEST: onReady event handler
|
||||||
exports.testTabsEvent_onReady = function(test) {
|
exports.testTabsEvent_onReady = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
openBrowserWindow(function(window, browser) {
|
openBrowserWindow(function(window, browser) {
|
||||||
let url = "data:text/html;charset=utf-8,onready";
|
let url = "data:text/html;charset=utf-8,onready";
|
||||||
let eventCount = 0;
|
let eventCount = 0;
|
||||||
|
@ -550,10 +512,10 @@ exports.testTabsEvent_onReady = function(test) {
|
||||||
|
|
||||||
// add listener via collection add
|
// add listener via collection add
|
||||||
tabs.on('ready', function listener2(tab) {
|
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', listener1);
|
||||||
tabs.removeListener('ready', listener2);
|
tabs.removeListener('ready', listener2);
|
||||||
closeBrowserWindow(window, function() test.done());
|
close(window).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
tabs.open(url);
|
tabs.open(url);
|
||||||
|
@ -561,8 +523,7 @@ exports.testTabsEvent_onReady = function(test) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: onActivate event handler
|
// TEST: onActivate event handler
|
||||||
exports.testTabsEvent_onActivate = function(test) {
|
exports.testTabsEvent_onActivate = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
openBrowserWindow(function(window, browser) {
|
openBrowserWindow(function(window, browser) {
|
||||||
let url = "data:text/html;charset=utf-8,onactivate";
|
let url = "data:text/html;charset=utf-8,onactivate";
|
||||||
let eventCount = 0;
|
let eventCount = 0;
|
||||||
|
@ -575,10 +536,10 @@ exports.testTabsEvent_onActivate = function(test) {
|
||||||
|
|
||||||
// add listener via collection add
|
// add listener via collection add
|
||||||
tabs.on('activate', function listener2(tab) {
|
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', listener1);
|
||||||
tabs.removeListener('activate', listener2);
|
tabs.removeListener('activate', listener2);
|
||||||
closeBrowserWindow(window, function() test.done());
|
close(window).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
tabs.open(url);
|
tabs.open(url);
|
||||||
|
@ -586,8 +547,7 @@ exports.testTabsEvent_onActivate = function(test) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// onDeactivate event handler
|
// onDeactivate event handler
|
||||||
exports.testTabsEvent_onDeactivate = function(test) {
|
exports.testTabsEvent_onDeactivate = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
openBrowserWindow(function(window, browser) {
|
openBrowserWindow(function(window, browser) {
|
||||||
let url = "data:text/html;charset=utf-8,ondeactivate";
|
let url = "data:text/html;charset=utf-8,ondeactivate";
|
||||||
let eventCount = 0;
|
let eventCount = 0;
|
||||||
|
@ -600,10 +560,10 @@ exports.testTabsEvent_onDeactivate = function(test) {
|
||||||
|
|
||||||
// add listener via collection add
|
// add listener via collection add
|
||||||
tabs.on('deactivate', function listener2(tab) {
|
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', listener1);
|
||||||
tabs.removeListener('deactivate', listener2);
|
tabs.removeListener('deactivate', listener2);
|
||||||
closeBrowserWindow(window, function() test.done());
|
close(window).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
tabs.on('open', function onOpen(tab) {
|
tabs.on('open', function onOpen(tab) {
|
||||||
|
@ -616,8 +576,7 @@ exports.testTabsEvent_onDeactivate = function(test) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// pinning
|
// pinning
|
||||||
exports.testTabsEvent_pinning = function(test) {
|
exports.testTabsEvent_pinning = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
openBrowserWindow(function(window, browser) {
|
openBrowserWindow(function(window, browser) {
|
||||||
let url = "data:text/html;charset=utf-8,1";
|
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.on('pinned', function onPinned(tab) {
|
||||||
tabs.removeListener('pinned', onPinned);
|
tabs.removeListener('pinned', onPinned);
|
||||||
test.assert(tab.isPinned, "notified tab is pinned");
|
assert.ok(tab.isPinned, "notified tab is pinned");
|
||||||
tab.unpin();
|
tab.unpin();
|
||||||
});
|
});
|
||||||
|
|
||||||
tabs.on('unpinned', function onUnpinned(tab) {
|
tabs.on('unpinned', function onUnpinned(tab) {
|
||||||
tabs.removeListener('unpinned', onUnpinned);
|
tabs.removeListener('unpinned', onUnpinned);
|
||||||
test.assert(!tab.isPinned, "notified tab is not pinned");
|
assert.ok(!tab.isPinned, "notified tab is not pinned");
|
||||||
closeBrowserWindow(window, function() test.done());
|
close(window).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
tabs.open(url);
|
tabs.open(url);
|
||||||
|
@ -643,8 +602,7 @@ exports.testTabsEvent_pinning = function(test) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// TEST: per-tab event handlers
|
// TEST: per-tab event handlers
|
||||||
exports.testPerTabEvents = function(test) {
|
exports.testPerTabEvents = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
openBrowserWindow(function(window, browser) {
|
openBrowserWindow(function(window, browser) {
|
||||||
let eventCount = 0;
|
let eventCount = 0;
|
||||||
|
|
||||||
|
@ -659,19 +617,18 @@ exports.testPerTabEvents = function(test) {
|
||||||
|
|
||||||
// add listener via collection add
|
// add listener via collection add
|
||||||
tab.on('ready', function listener2() {
|
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', listener1);
|
||||||
tab.removeListener('ready', listener2);
|
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.
|
// Take care that attach has to be called on tab ready and not on tab open.
|
||||||
test.waitUntilDone();
|
|
||||||
openBrowserWindow(function(window, browser) {
|
openBrowserWindow(function(window, browser) {
|
||||||
tabs.open({
|
tabs.open({
|
||||||
url: "data:text/html;charset=utf-8,foobar",
|
url: "data:text/html;charset=utf-8,foobar",
|
||||||
|
@ -679,10 +636,10 @@ exports.testAttachOnOpen = function (test) {
|
||||||
let worker = tab.attach({
|
let worker = tab.attach({
|
||||||
contentScript: 'self.postMessage(document.location.href); ',
|
contentScript: 'self.postMessage(document.location.href); ',
|
||||||
onMessage: function (msg) {
|
onMessage: function (msg) {
|
||||||
test.assertEqual(msg, "about:blank",
|
assert.equal(msg, "about:blank",
|
||||||
"Worker document url is about:blank on open");
|
"Worker document url is about:blank on open");
|
||||||
worker.destroy();
|
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
|
// Example of attach that process multiple tab documents
|
||||||
test.waitUntilDone();
|
|
||||||
openBrowserWindow(function(window, browser) {
|
openBrowserWindow(function(window, browser) {
|
||||||
let firstLocation = "data:text/html;charset=utf-8,foobar";
|
let firstLocation = "data:text/html;charset=utf-8,foobar";
|
||||||
let secondLocation = "data:text/html;charset=utf-8,bar";
|
let secondLocation = "data:text/html;charset=utf-8,bar";
|
||||||
|
@ -702,6 +658,7 @@ exports.testAttachOnMultipleDocuments = function (test) {
|
||||||
let worker1 = null;
|
let worker1 = null;
|
||||||
let worker2 = null;
|
let worker2 = null;
|
||||||
let detachEventCount = 0;
|
let detachEventCount = 0;
|
||||||
|
|
||||||
tabs.open({
|
tabs.open({
|
||||||
url: firstLocation,
|
url: firstLocation,
|
||||||
onReady: function (tab) {
|
onReady: function (tab) {
|
||||||
|
@ -712,14 +669,14 @@ exports.testAttachOnMultipleDocuments = function (test) {
|
||||||
' function () self.postMessage(document.location.href)' +
|
' function () self.postMessage(document.location.href)' +
|
||||||
');',
|
');',
|
||||||
onMessage: function (msg) {
|
onMessage: function (msg) {
|
||||||
test.assertEqual(msg, firstLocation,
|
assert.equal(msg, firstLocation,
|
||||||
"Worker url is equal to the 1st document");
|
"Worker url is equal to the 1st document");
|
||||||
tab.url = secondLocation;
|
tab.url = secondLocation;
|
||||||
},
|
},
|
||||||
onDetach: function () {
|
onDetach: function () {
|
||||||
detachEventCount++;
|
detachEventCount++;
|
||||||
test.pass("Got worker1 detach event");
|
assert.pass("Got worker1 detach event");
|
||||||
test.assertRaises(function () {
|
assert.throws(function () {
|
||||||
worker1.postMessage("ex-1");
|
worker1.postMessage("ex-1");
|
||||||
},
|
},
|
||||||
/Couldn't find the worker/,
|
/Couldn't find the worker/,
|
||||||
|
@ -736,14 +693,14 @@ exports.testAttachOnMultipleDocuments = function (test) {
|
||||||
' function () self.postMessage(document.location.href)' +
|
' function () self.postMessage(document.location.href)' +
|
||||||
');',
|
');',
|
||||||
onMessage: function (msg) {
|
onMessage: function (msg) {
|
||||||
test.assertEqual(msg, secondLocation,
|
assert.equal(msg, secondLocation,
|
||||||
"Worker url is equal to the 2nd document");
|
"Worker url is equal to the 2nd document");
|
||||||
tab.url = thirdLocation;
|
tab.url = thirdLocation;
|
||||||
},
|
},
|
||||||
onDetach: function () {
|
onDetach: function () {
|
||||||
detachEventCount++;
|
detachEventCount++;
|
||||||
test.pass("Got worker2 detach event");
|
assert.pass("Got worker2 detach event");
|
||||||
test.assertRaises(function () {
|
assert.throws(function () {
|
||||||
worker2.postMessage("ex-2");
|
worker2.postMessage("ex-2");
|
||||||
},
|
},
|
||||||
/Couldn't find the worker/,
|
/Couldn't find the worker/,
|
||||||
|
@ -763,18 +720,17 @@ exports.testAttachOnMultipleDocuments = function (test) {
|
||||||
if (detachEventCount != 2)
|
if (detachEventCount != 2)
|
||||||
return;
|
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
|
// Check that content script has access to wrapped values by default
|
||||||
test.waitUntilDone();
|
|
||||||
openBrowserWindow(function(window, browser) {
|
openBrowserWindow(function(window, browser) {
|
||||||
let document = "data:text/html;charset=utf-8,<script>var globalJSVar = true; " +
|
let document = "data:text/html;charset=utf-8,<script>var globalJSVar = true; " +
|
||||||
" document.getElementById = 3;</script>";
|
" document.getElementById = 3;</script>";
|
||||||
|
@ -791,9 +747,9 @@ exports.testAttachWrappers = function (test) {
|
||||||
' self.postMessage(e.message);' +
|
' self.postMessage(e.message);' +
|
||||||
'}',
|
'}',
|
||||||
onMessage: function (msg) {
|
onMessage: function (msg) {
|
||||||
test.assertEqual(msg, true, "Worker has wrapped objects ("+count+")");
|
assert.equal(msg, true, "Worker has wrapped objects ("+count+")");
|
||||||
if (count++ == 1)
|
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
|
// We do not offer unwrapped access to DOM since bug 601295 landed
|
||||||
// See 660780 to track progress of unwrap feature
|
// 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
|
// Check that content script has access to unwrapped values through unsafeWindow
|
||||||
test.waitUntilDone();
|
|
||||||
openBrowserWindow(function(window, browser) {
|
openBrowserWindow(function(window, browser) {
|
||||||
let document = "data:text/html;charset=utf-8,<script>var globalJSVar=true;</script>";
|
let document = "data:text/html;charset=utf-8,<script>var globalJSVar=true;</script>";
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
@ -822,8 +777,8 @@ exports.testAttachUnwrapped = function (test) {
|
||||||
' self.postMessage(e.message);' +
|
' self.postMessage(e.message);' +
|
||||||
'}',
|
'}',
|
||||||
onMessage: function (msg) {
|
onMessage: function (msg) {
|
||||||
test.assertEqual(msg, true, "Worker has access to javascript content globals ("+count+")");
|
assert.equal(msg, true, "Worker has access to javascript content globals ("+count+")");
|
||||||
closeBrowserWindow(window, function() test.done());
|
close(window).then(done);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -833,27 +788,25 @@ exports.testAttachUnwrapped = function (test) {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
exports['test window focus changes active tab'] = function(test) {
|
exports['test window focus changes active tab'] = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
let url1 = "data:text/html;charset=utf-8," + encodeURIComponent("test window focus changes active tab</br><h1>Window #1");
|
let url1 = "data:text/html;charset=utf-8," + encodeURIComponent("test window focus changes active tab</br><h1>Window #1");
|
||||||
|
|
||||||
let win1 = openBrowserWindow(function() {
|
let win1 = openBrowserWindow(function() {
|
||||||
test.pass("window 1 is open");
|
assert.pass("window 1 is open");
|
||||||
|
|
||||||
let win2 = openBrowserWindow(function() {
|
let win2 = openBrowserWindow(function() {
|
||||||
test.pass("window 2 is open");
|
assert.pass("window 2 is open");
|
||||||
|
|
||||||
focus(win2).then(function() {
|
focus(win2).then(function() {
|
||||||
tabs.on("activate", function onActivate(tab) {
|
tabs.on("activate", function onActivate(tab) {
|
||||||
tabs.removeListener("activate", onActivate);
|
tabs.removeListener("activate", onActivate);
|
||||||
test.pass("activate was called on windows focus change.");
|
assert.pass("activate was called on windows focus change.");
|
||||||
test.assertEqual(tab.url, url1, 'the activated tab url is correct');
|
assert.equal(tab.url, url1, 'the activated tab url is correct');
|
||||||
|
|
||||||
close(win2).then(function() {
|
close(win2).then(function() {
|
||||||
test.pass('window 2 was closed');
|
assert.pass('window 2 was closed');
|
||||||
return close(win1);
|
return close(win1);
|
||||||
}).then(test.done.bind(test));
|
}).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
win1.focus();
|
win1.focus();
|
||||||
|
@ -862,24 +815,21 @@ exports['test window focus changes active tab'] = function(test) {
|
||||||
}, url1);
|
}, url1);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports['test ready event on new window tab'] = function(test) {
|
exports['test ready event on new window tab'] = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
let uri = encodeURI("data:text/html;charset=utf-8,Waiting for ready event!");
|
let uri = encodeURI("data:text/html;charset=utf-8,Waiting for ready event!");
|
||||||
|
|
||||||
require("sdk/tabs").on("ready", function onReady(tab) {
|
require("sdk/tabs").on("ready", function onReady(tab) {
|
||||||
if (tab.url === uri) {
|
if (tab.url === uri) {
|
||||||
require("sdk/tabs").removeListener("ready", onReady);
|
require("sdk/tabs").removeListener("ready", onReady);
|
||||||
test.pass("ready event was emitted");
|
assert.pass("ready event was emitted");
|
||||||
closeBrowserWindow(window, function() {
|
close(window).then(done);
|
||||||
test.done();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let window = openBrowserWindow(function(){}, uri);
|
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 windows = require('sdk/windows').browserWindows;
|
||||||
var { all, defer } = require('sdk/core/promise');
|
var { all, defer } = require('sdk/core/promise');
|
||||||
|
|
||||||
|
@ -891,9 +841,9 @@ exports['test unique tab ids'] = function(test) {
|
||||||
});
|
});
|
||||||
|
|
||||||
win.on('open', function(window) {
|
win.on('open', function(window) {
|
||||||
test.assert(window.tabs.length);
|
assert.ok(window.tabs.length);
|
||||||
test.assert(window.tabs.activeTab);
|
assert.ok(window.tabs.activeTab);
|
||||||
test.assert(window.tabs.activeTab.id);
|
assert.ok(window.tabs.activeTab.id);
|
||||||
deferred.resolve({
|
deferred.resolve({
|
||||||
id: window.tabs.activeTab.id,
|
id: window.tabs.activeTab.id,
|
||||||
win: win
|
win: win
|
||||||
|
@ -903,32 +853,29 @@ exports['test unique tab ids'] = function(test) {
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
test.waitUntilDone();
|
|
||||||
var one = openWindow(), two = openWindow();
|
var one = openWindow(), two = openWindow();
|
||||||
all([one, two]).then(function(results) {
|
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[0].win.close();
|
||||||
results[1].win.close();
|
results[1].win.close();
|
||||||
test.done();
|
done();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// related to Bug 671305
|
// related to Bug 671305
|
||||||
exports.testOnLoadEventWithDOM = function(test) {
|
exports.testOnLoadEventWithDOM = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
openBrowserWindow(function(window, browser) {
|
openBrowserWindow(function(window, browser) {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
tabs.on('load', function onLoad(tab) {
|
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++) {
|
if (!count++) {
|
||||||
tab.reload();
|
tab.reload();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// end of test
|
// end of test
|
||||||
tabs.removeListener('load', onLoad);
|
tabs.removeListener('load', onLoad);
|
||||||
test.pass('onLoad event called on reload');
|
assert.pass('onLoad event called on reload');
|
||||||
closeBrowserWindow(window, function() test.done());
|
close(window).then(done);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -941,9 +888,7 @@ exports.testOnLoadEventWithDOM = function(test) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// related to Bug 671305
|
// related to Bug 671305
|
||||||
exports.testOnLoadEventWithImage = function(test) {
|
exports.testOnLoadEventWithImage = function(assert, done) {
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
openBrowserWindow(function(window, browser) {
|
openBrowserWindow(function(window, browser) {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
tabs.on('load', function onLoad(tab) {
|
tabs.on('load', function onLoad(tab) {
|
||||||
|
@ -953,8 +898,8 @@ exports.testOnLoadEventWithImage = function(test) {
|
||||||
else {
|
else {
|
||||||
// end of test
|
// end of test
|
||||||
tabs.removeListener('load', onLoad);
|
tabs.removeListener('load', onLoad);
|
||||||
test.pass('onLoad event called on reload with image');
|
assert.pass('onLoad event called on reload with image');
|
||||||
closeBrowserWindow(window, function() test.done());
|
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");
|
const { LoaderWithHookedConsole } = require("sdk/test/loader");
|
||||||
let { loader, messages } = LoaderWithHookedConsole(module);
|
let { loader, messages } = LoaderWithHookedConsole(module);
|
||||||
let tabs = loader.require('sdk/tabs');
|
let tabs = loader.require('sdk/tabs');
|
||||||
test.waitUntilDone();
|
|
||||||
|
|
||||||
tabs.open({
|
tabs.open({
|
||||||
url: 'data:text/html;charset=utf-8,',
|
url: 'data:text/html;charset=utf-8,',
|
||||||
onOpen: function (tab) {
|
onOpen: function (tab) {
|
||||||
let favicon = tab.favicon;
|
let favicon = tab.favicon;
|
||||||
test.assert(messages.length === 1, 'only one error is dispatched');
|
assert.ok(messages.length === 1, 'only one error is dispatched');
|
||||||
test.assert(messages[0].type, 'error', 'the console message is an error');
|
assert.ok(messages[0].type, 'error', 'the console message is an error');
|
||||||
|
|
||||||
let msg = messages[0].msg;
|
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');
|
'message contains the given message');
|
||||||
tab.close(test.done.bind(test));
|
tab.close(done);
|
||||||
loader.unload();
|
loader.unload();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1027,11 +971,4 @@ function openBrowserWindow(callback, url) {
|
||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper for calling code at window close
|
require('sdk/test').run(exports);
|
||||||
function closeBrowserWindow(window, callback) {
|
|
||||||
window.addEventListener("unload", function unload() {
|
|
||||||
window.removeEventListener("unload", unload, false);
|
|
||||||
callback();
|
|
||||||
}, false);
|
|
||||||
window.close();
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
/* 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
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { Loader } = require("sdk/test/loader");
|
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 url2 = "data:text/html;charset=utf-8,page2";
|
||||||
let content =
|
let content =
|
||||||
"<script>" +
|
"<script>" +
|
||||||
"window.onload = function() {" +
|
"window.addEventListener('message', function() {"+
|
||||||
" setTimeout(function () {" +
|
" window.location = '" + url2 + "';" +
|
||||||
" window.location = '" + url2 + "';" +
|
'}, false);' +
|
||||||
" }, 0);" +
|
|
||||||
"}" +
|
|
||||||
"</script>";
|
"</script>";
|
||||||
let messageCount = 0;
|
let messageCount = 0;
|
||||||
let panel = Panel({
|
let panel = Panel({
|
||||||
// using URL here is intentional, see bug 859009
|
// using URL here is intentional, see bug 859009
|
||||||
contentURL: URL("data:text/html;charset=utf-8," + encodeURIComponent(content)),
|
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) {
|
onMessage: function (message) {
|
||||||
messageCount++;
|
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) {
|
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) {
|
else if (messageCount == 2) {
|
||||||
assert.equal(message, url2, "Second document too");
|
assert.equal(message, url2, "Second document too; " + message);
|
||||||
panel.destroy();
|
panel.destroy();
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,6 +87,9 @@ exports.testSearchURL = function (assert, done) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Disabling due to intermittent Bug 892619
|
||||||
|
// TODO solve this
|
||||||
|
/*
|
||||||
exports.testSearchTimeRange = function (assert, done) {
|
exports.testSearchTimeRange = function (assert, done) {
|
||||||
let firstTime, secondTime;
|
let firstTime, secondTime;
|
||||||
addVisits([
|
addVisits([
|
||||||
|
@ -120,7 +123,7 @@ exports.testSearchTimeRange = function (assert, done) {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
exports.testSearchQuery = function (assert, done) {
|
exports.testSearchQuery = function (assert, done) {
|
||||||
addVisits([
|
addVisits([
|
||||||
'http://mozilla.com', 'http://webaud.io', 'http://mozilla.com/webfwd'
|
'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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const app = require("sdk/system/xul-app");
|
module.metadata = {
|
||||||
|
'engines': {
|
||||||
|
'Firefox': '*',
|
||||||
|
'Fennec': '*'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (app.is("Firefox")) {
|
const app = require('sdk/system/xul-app');
|
||||||
module.exports = require("./tabs/test-firefox-tabs");
|
|
||||||
}
|
if (app.is('Fennec')) {
|
||||||
else if (app.is("Fennec")) {
|
module.exports = require('./tabs/test-fennec-tabs');
|
||||||
module.exports = require("./tabs/test-fennec-tabs");
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
require("test").run({
|
module.exports = require('./tabs/test-firefox-tabs');
|
||||||
"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."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,4 +57,4 @@ exports["test LoaderWithHookedConsole"] = function (assert) {
|
||||||
assert.equal(count, 6, "Called for all messages");
|
assert.equal(count, 6, "Called for all messages");
|
||||||
};
|
};
|
||||||
|
|
||||||
require("test").run(exports);
|
require("sdk/test").run(exports);
|
||||||
|
|
|
@ -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);
|
|
@ -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);
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -3,17 +3,11 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// Opening new windows in Fennec causes issues
|
|
||||||
module.metadata = {
|
|
||||||
engines: {
|
|
||||||
'Firefox': '*'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const { Loader } = require("sdk/test/loader");
|
const { Loader } = require("sdk/test/loader");
|
||||||
const { open, close } = require("sdk/window/helpers");
|
const { open, close } = require("sdk/window/helpers");
|
||||||
const { browserWindows: windows } = require("sdk/windows");
|
const { browserWindows: windows } = require("sdk/windows");
|
||||||
const { isBrowser } = require('sdk/window/utils');
|
const { isBrowser } = require('sdk/window/utils');
|
||||||
|
const app = require("sdk/system/xul-app");
|
||||||
|
|
||||||
exports["test unload window observer"] = function(assert, done) {
|
exports["test unload window observer"] = function(assert, done) {
|
||||||
// Hacky way to be able to create unloadable modules via makeSandboxedLoader.
|
// 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 closed = 0;
|
||||||
let windowsOpen = windows.length;
|
let windowsOpen = windows.length;
|
||||||
|
|
||||||
observer.on("open", function onOpen(window) {
|
observer.on("open", onOpen);
|
||||||
// Ignoring non-browser windows
|
observer.on("close", onClose);
|
||||||
if (isBrowser(window))
|
|
||||||
opened++;
|
// On Fennec, only test that the module does not throw an error
|
||||||
});
|
if (app.is("Fennec")) {
|
||||||
observer.on("close", function onClose(window) {
|
assert.pass("Windows observer did not throw on Fennec");
|
||||||
// Ignore non-browser windows & already opened `activeWindow` (unload will
|
return cleanUp();
|
||||||
// emit close on it even though it is not actually closed).
|
}
|
||||||
if (isBrowser(window))
|
|
||||||
closed++;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Open window and close it to trigger observers.
|
// Open window and close it to trigger observers.
|
||||||
open().
|
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(1, opened, "observer open was called before unload only");
|
||||||
assert.equal(windowsOpen + 1, closed, "observer close 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);
|
require("test").run(exports);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{
|
{
|
||||||
"revision": "6127157a7933badbe075f894743a09c1152afe58",
|
"revision": "a7d496de30d0a7612b31de99b01b5a4e2b749728",
|
||||||
"repo_path": "/integration/gaia-central"
|
"repo_path": "/integration/gaia-central"
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,14 @@
|
||||||
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
|
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
|
||||||
@namespace html url("http://www.w3.org/1999/xhtml");
|
@namespace html url("http://www.w3.org/1999/xhtml");
|
||||||
|
|
||||||
|
#main-window:not([chromehidden~="toolbar"]) {
|
||||||
|
%ifdef XP_MACOSX
|
||||||
|
min-width: 425px;
|
||||||
|
%else
|
||||||
|
min-width: 390px;
|
||||||
|
%endif
|
||||||
|
}
|
||||||
|
|
||||||
searchbar {
|
searchbar {
|
||||||
-moz-binding: url("chrome://browser/content/search/search.xml#searchbar");
|
-moz-binding: url("chrome://browser/content/search/search.xml#searchbar");
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,14 @@ XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||||
"resource://gre/modules/PlacesUtils.jsm");
|
"resource://gre/modules/PlacesUtils.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
|
XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
|
||||||
"resource://gre/modules/FormHistory.jsm");
|
"resource://gre/modules/FormHistory.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
|
||||||
|
"resource://gre/modules/Downloads.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||||
"resource://gre/modules/commonjs/sdk/core/promise.js");
|
"resource://gre/modules/commonjs/sdk/core/promise.js");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||||
|
"resource://gre/modules/Task.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
|
||||||
|
"resource:///modules/DownloadsCommon.jsm");
|
||||||
|
|
||||||
function Sanitizer() {}
|
function Sanitizer() {}
|
||||||
Sanitizer.prototype = {
|
Sanitizer.prototype = {
|
||||||
|
@ -303,47 +309,47 @@ Sanitizer.prototype = {
|
||||||
downloads: {
|
downloads: {
|
||||||
clear: function ()
|
clear: function ()
|
||||||
{
|
{
|
||||||
var dlMgr = Components.classes["@mozilla.org/download-manager;1"]
|
if (DownloadsCommon.useJSTransfer) {
|
||||||
.getService(Components.interfaces.nsIDownloadManager);
|
Task.spawn(function () {
|
||||||
|
let filterByTime = this.range ?
|
||||||
|
(download => download.startTime >= this.range[0] &&
|
||||||
|
download.startTime <= this.range[1]) : null;
|
||||||
|
|
||||||
var dlsToRemove = [];
|
// Clear all completed/cancelled downloads
|
||||||
if (this.range) {
|
let publicList = yield Downloads.getPublicDownloadList();
|
||||||
// First, remove the completed/cancelled downloads
|
publicList.removeFinished(filterByTime);
|
||||||
dlMgr.removeDownloadsByTimeframe(this.range[0], this.range[1]);
|
|
||||||
|
|
||||||
// Queue up any active downloads that started in the time span as well
|
let privateList = yield Downloads.getPrivateDownloadList();
|
||||||
for (let dlsEnum of [dlMgr.activeDownloads, dlMgr.activePrivateDownloads]) {
|
privateList.removeFinished(filterByTime);
|
||||||
while (dlsEnum.hasMoreElements()) {
|
}.bind(this)).then(null, Cu.reportError);
|
||||||
var dl = dlsEnum.next();
|
|
||||||
if (dl.startTime >= this.range[0])
|
|
||||||
dlsToRemove.push(dl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Clear all completed/cancelled downloads
|
var dlMgr = Components.classes["@mozilla.org/download-manager;1"]
|
||||||
dlMgr.cleanUp();
|
.getService(Components.interfaces.nsIDownloadManager);
|
||||||
dlMgr.cleanUpPrivate();
|
|
||||||
|
|
||||||
// Queue up all active ones as well
|
if (this.range) {
|
||||||
for (let dlsEnum of [dlMgr.activeDownloads, dlMgr.activePrivateDownloads]) {
|
// First, remove the completed/cancelled downloads
|
||||||
while (dlsEnum.hasMoreElements()) {
|
dlMgr.removeDownloadsByTimeframe(this.range[0], this.range[1]);
|
||||||
dlsToRemove.push(dlsEnum.next());
|
}
|
||||||
}
|
else {
|
||||||
|
// Clear all completed/cancelled downloads
|
||||||
|
dlMgr.cleanUp();
|
||||||
|
dlMgr.cleanUpPrivate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove any queued up active downloads
|
|
||||||
dlsToRemove.forEach(function (dl) {
|
|
||||||
dl.remove();
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
get canClear()
|
canClear : function(aCallback, aArg)
|
||||||
{
|
{
|
||||||
var dlMgr = Components.classes["@mozilla.org/download-manager;1"]
|
if (DownloadsCommon.useJSTransfer) {
|
||||||
.getService(Components.interfaces.nsIDownloadManager);
|
aCallback("downloads", true, aArg);
|
||||||
return dlMgr.canCleanUp || dlMgr.canCleanUpPrivate;
|
}
|
||||||
|
else {
|
||||||
|
var dlMgr = Components.classes["@mozilla.org/download-manager;1"]
|
||||||
|
.getService(Components.interfaces.nsIDownloadManager);
|
||||||
|
aCallback("downloads", dlMgr.canCleanUp || dlMgr.canCleanUpPrivate, aArg);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -45,13 +45,7 @@ const EXPECTED_REFLOWS = [
|
||||||
"ssi_getWindowDimension@resource:///modules/sessionstore/SessionStore.jsm|" +
|
"ssi_getWindowDimension@resource:///modules/sessionstore/SessionStore.jsm|" +
|
||||||
"@resource:///modules/sessionstore/SessionStore.jsm|" +
|
"@resource:///modules/sessionstore/SessionStore.jsm|" +
|
||||||
"ssi_updateWindowFeatures@resource:///modules/sessionstore/SessionStore.jsm|" +
|
"ssi_updateWindowFeatures@resource:///modules/sessionstore/SessionStore.jsm|" +
|
||||||
"ssi_collectWindowData@resource:///modules/sessionstore/SessionStore.jsm|" +
|
"ssi_collectWindowData@resource:///modules/sessionstore/SessionStore.jsm|",
|
||||||
"@resource:///modules/sessionstore/SessionStore.jsm|" +
|
|
||||||
"ssi_forEachBrowserWindow@resource:///modules/sessionstore/SessionStore.jsm|" +
|
|
||||||
"ssi_getCurrentState@resource:///modules/sessionstore/SessionStore.jsm|" +
|
|
||||||
"ssi_saveState@resource:///modules/sessionstore/SessionStore.jsm|" +
|
|
||||||
"ssi_onTimerCallback@resource:///modules/sessionstore/SessionStore.jsm|" +
|
|
||||||
"ssi_observe@resource:///modules/sessionstore/SessionStore.jsm|",
|
|
||||||
|
|
||||||
// tabPreviews.capture()
|
// tabPreviews.capture()
|
||||||
"tabPreviews_capture@chrome://browser/content/browser.js|" +
|
"tabPreviews_capture@chrome://browser/content/browser.js|" +
|
||||||
|
|
|
@ -53,6 +53,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
|
||||||
"resource://gre/modules/PluralForm.jsm");
|
"resource://gre/modules/PluralForm.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
|
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
|
||||||
"resource://gre/modules/Downloads.jsm");
|
"resource://gre/modules/Downloads.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "DownloadUIHelper",
|
||||||
|
"resource://gre/modules/DownloadUIHelper.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils",
|
XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils",
|
||||||
"resource://gre/modules/DownloadUtils.jsm");
|
"resource://gre/modules/DownloadUtils.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "OS",
|
XPCOMUtils.defineLazyModuleGetter(this, "OS",
|
||||||
|
@ -480,65 +482,44 @@ this.DownloadsCommon = {
|
||||||
if (!(aOwnerWindow instanceof Ci.nsIDOMWindow))
|
if (!(aOwnerWindow instanceof Ci.nsIDOMWindow))
|
||||||
throw new Error("aOwnerWindow must be a dom-window object");
|
throw new Error("aOwnerWindow must be a dom-window object");
|
||||||
|
|
||||||
// Confirm opening executable files if required.
|
let promiseShouldLaunch;
|
||||||
if (aFile.isExecutable()) {
|
if (aFile.isExecutable()) {
|
||||||
let showAlert = true;
|
// We get a prompter for the provided window here, even though anchoring
|
||||||
try {
|
// to the most recently active window should work as well.
|
||||||
showAlert = Services.prefs.getBoolPref(kPrefBdmAlertOnExeOpen);
|
promiseShouldLaunch =
|
||||||
} catch (ex) { }
|
DownloadUIHelper.getPrompter(aOwnerWindow)
|
||||||
|
.confirmLaunchExecutable(aFile.path);
|
||||||
// On Vista and above, we rely on native security prompting for
|
} else {
|
||||||
// downloaded content unless it's disabled.
|
promiseShouldLaunch = Promise.resolve(true);
|
||||||
if (DownloadsCommon.isWinVistaOrHigher) {
|
|
||||||
try {
|
|
||||||
if (Services.prefs.getBoolPref(kPrefBdmScanWhenDone)) {
|
|
||||||
showAlert = false;
|
|
||||||
}
|
|
||||||
} catch (ex) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showAlert) {
|
|
||||||
let name = aFile.leafName;
|
|
||||||
let message =
|
|
||||||
DownloadsCommon.strings.fileExecutableSecurityWarning(name, name);
|
|
||||||
let title =
|
|
||||||
DownloadsCommon.strings.fileExecutableSecurityWarningTitle;
|
|
||||||
let dontAsk =
|
|
||||||
DownloadsCommon.strings.fileExecutableSecurityWarningDontAsk;
|
|
||||||
|
|
||||||
let checkbox = { value: false };
|
|
||||||
let open = Services.prompt.confirmCheck(aOwnerWindow, title, message,
|
|
||||||
dontAsk, checkbox);
|
|
||||||
if (!open) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Services.prefs.setBoolPref(kPrefBdmAlertOnExeOpen,
|
|
||||||
!checkbox.value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually open the file.
|
promiseShouldLaunch.then(shouldLaunch => {
|
||||||
try {
|
if (!shouldLaunch) {
|
||||||
if (aMimeInfo && aMimeInfo.preferredAction == aMimeInfo.useHelperApp) {
|
|
||||||
aMimeInfo.launchWithFile(aFile);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch(ex) { }
|
|
||||||
|
|
||||||
// If either we don't have the mime info, or the preferred action failed,
|
// Actually open the file.
|
||||||
// attempt to launch the file directly.
|
try {
|
||||||
try {
|
if (aMimeInfo && aMimeInfo.preferredAction == aMimeInfo.useHelperApp) {
|
||||||
aFile.launch();
|
aMimeInfo.launchWithFile(aFile);
|
||||||
}
|
return;
|
||||||
catch(ex) {
|
}
|
||||||
// If launch fails, try sending it through the system's external "file:"
|
}
|
||||||
// URL handler.
|
catch(ex) { }
|
||||||
Cc["@mozilla.org/uriloader/external-protocol-service;1"]
|
|
||||||
.getService(Ci.nsIExternalProtocolService)
|
// If either we don't have the mime info, or the preferred action failed,
|
||||||
.loadUrl(NetUtil.newURI(aFile));
|
// attempt to launch the file directly.
|
||||||
}
|
try {
|
||||||
|
aFile.launch();
|
||||||
|
}
|
||||||
|
catch(ex) {
|
||||||
|
// If launch fails, try sending it through the system's external "file:"
|
||||||
|
// URL handler.
|
||||||
|
Cc["@mozilla.org/uriloader/external-protocol-service;1"]
|
||||||
|
.getService(Ci.nsIExternalProtocolService)
|
||||||
|
.loadUrl(NetUtil.newURI(aFile));
|
||||||
|
}
|
||||||
|
}).then(null, Cu.reportError);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -718,7 +699,7 @@ DownloadsDataCtor.prototype = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._downloadToDataItemMap.remove(aDownload);
|
this._downloadToDataItemMap.delete(aDownload);
|
||||||
this.dataItems[dataItem.downloadGuid] = null;
|
this.dataItems[dataItem.downloadGuid] = null;
|
||||||
for (let view of this._views) {
|
for (let view of this._views) {
|
||||||
view.onDataItemRemoved(dataItem);
|
view.onDataItemRemoved(dataItem);
|
||||||
|
@ -1344,7 +1325,7 @@ DownloadsDataItem.prototype = {
|
||||||
this.currBytes = this._download.currentBytes;
|
this.currBytes = this._download.currentBytes;
|
||||||
this.maxBytes = this._download.totalBytes;
|
this.maxBytes = this._download.totalBytes;
|
||||||
this.resumable = this._download.hasPartialData;
|
this.resumable = this._download.hasPartialData;
|
||||||
this.speed = 0;
|
this.speed = this._download.speed;
|
||||||
this.percentComplete = this._download.progress;
|
this.percentComplete = this._download.progress;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -741,12 +741,11 @@ let SessionStoreInternal = {
|
||||||
Services.obs.notifyObservers(null, NOTIFY_WINDOWS_RESTORED, "");
|
Services.obs.notifyObservers(null, NOTIFY_WINDOWS_RESTORED, "");
|
||||||
} else {
|
} else {
|
||||||
TelemetryTimestamps.add("sessionRestoreRestoring");
|
TelemetryTimestamps.add("sessionRestoreRestoring");
|
||||||
// make sure that the restored tabs are first in the window
|
|
||||||
aInitialState._firstTabs = true;
|
|
||||||
this._restoreCount = aInitialState.windows ? aInitialState.windows.length : 0;
|
this._restoreCount = aInitialState.windows ? aInitialState.windows.length : 0;
|
||||||
|
|
||||||
let overwrite = this._isCmdLineEmpty(aWindow, aInitialState);
|
let overwrite = this._isCmdLineEmpty(aWindow, aInitialState);
|
||||||
this.restoreWindow(aWindow, aInitialState, overwrite);
|
let options = {firstWindow: true, overwriteTabs: overwrite};
|
||||||
|
this.restoreWindow(aWindow, aInitialState, options);
|
||||||
|
|
||||||
// _loadState changed from "stopped" to "running". Save the session's
|
// _loadState changed from "stopped" to "running". Save the session's
|
||||||
// load state immediately so that crashes happening during startup
|
// load state immediately so that crashes happening during startup
|
||||||
|
@ -764,8 +763,9 @@ let SessionStoreInternal = {
|
||||||
}
|
}
|
||||||
// this window was opened by _openWindowWithState
|
// this window was opened by _openWindowWithState
|
||||||
else if (!this._isWindowLoaded(aWindow)) {
|
else if (!this._isWindowLoaded(aWindow)) {
|
||||||
let followUp = this._statesToRestore[aWindow.__SS_restoreID].windows.length == 1;
|
let state = this._statesToRestore[aWindow.__SS_restoreID];
|
||||||
this.restoreWindow(aWindow, this._statesToRestore[aWindow.__SS_restoreID], true, followUp);
|
let options = {overwriteTabs: true, isFollowUp: state.windows.length == 1};
|
||||||
|
this.restoreWindow(aWindow, state, options);
|
||||||
}
|
}
|
||||||
// The user opened another, non-private window after starting up with
|
// The user opened another, non-private window after starting up with
|
||||||
// a single private one. Let's restore the session we actually wanted to
|
// a single private one. Let's restore the session we actually wanted to
|
||||||
|
@ -773,10 +773,9 @@ let SessionStoreInternal = {
|
||||||
else if (this._deferredInitialState && !isPrivateWindow &&
|
else if (this._deferredInitialState && !isPrivateWindow &&
|
||||||
aWindow.toolbar.visible) {
|
aWindow.toolbar.visible) {
|
||||||
|
|
||||||
this._deferredInitialState._firstTabs = true;
|
|
||||||
this._restoreCount = this._deferredInitialState.windows ?
|
this._restoreCount = this._deferredInitialState.windows ?
|
||||||
this._deferredInitialState.windows.length : 0;
|
this._deferredInitialState.windows.length : 0;
|
||||||
this.restoreWindow(aWindow, this._deferredInitialState, false);
|
this.restoreWindow(aWindow, this._deferredInitialState, {firstWindow: true});
|
||||||
this._deferredInitialState = null;
|
this._deferredInitialState = null;
|
||||||
}
|
}
|
||||||
else if (this._restoreLastWindow && aWindow.toolbar.visible &&
|
else if (this._restoreLastWindow && aWindow.toolbar.visible &&
|
||||||
|
@ -837,7 +836,8 @@ let SessionStoreInternal = {
|
||||||
// Ensure that the window state isn't hidden
|
// Ensure that the window state isn't hidden
|
||||||
this._restoreCount = 1;
|
this._restoreCount = 1;
|
||||||
let state = { windows: [newWindowState] };
|
let state = { windows: [newWindowState] };
|
||||||
this.restoreWindow(aWindow, state, this._isCmdLineEmpty(aWindow, state));
|
let options = {overwriteTabs: this._isCmdLineEmpty(aWindow, state)};
|
||||||
|
this.restoreWindow(aWindow, state, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// we actually restored the session just now.
|
// we actually restored the session just now.
|
||||||
|
@ -1399,7 +1399,7 @@ let SessionStoreInternal = {
|
||||||
this._restoreCount = state.windows ? state.windows.length : 0;
|
this._restoreCount = state.windows ? state.windows.length : 0;
|
||||||
|
|
||||||
// restore to the given state
|
// restore to the given state
|
||||||
this.restoreWindow(window, state, true);
|
this.restoreWindow(window, state, {overwriteTabs: true});
|
||||||
},
|
},
|
||||||
|
|
||||||
getWindowState: function ssi_getWindowState(aWindow) {
|
getWindowState: function ssi_getWindowState(aWindow) {
|
||||||
|
@ -1419,7 +1419,7 @@ let SessionStoreInternal = {
|
||||||
if (!aWindow.__SSi)
|
if (!aWindow.__SSi)
|
||||||
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
|
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
|
||||||
|
|
||||||
this.restoreWindow(aWindow, aState, aOverwrite);
|
this.restoreWindow(aWindow, aState, {overwriteTabs: aOverwrite});
|
||||||
},
|
},
|
||||||
|
|
||||||
getTabState: function ssi_getTabState(aTab) {
|
getTabState: function ssi_getTabState(aTab) {
|
||||||
|
@ -1780,7 +1780,8 @@ let SessionStoreInternal = {
|
||||||
// in _preWindowToRestoreInto will prevent most (all?) Panorama
|
// in _preWindowToRestoreInto will prevent most (all?) Panorama
|
||||||
// weirdness but we will still merge other extData.
|
// weirdness but we will still merge other extData.
|
||||||
// Bug 588217 should make this go away by merging the group data.
|
// Bug 588217 should make this go away by merging the group data.
|
||||||
this.restoreWindow(windowToUse, { windows: [winState] }, canOverwriteTabs, true);
|
let options = {overwriteTabs: canOverwriteTabs, isFollowUp: true};
|
||||||
|
this.restoreWindow(windowToUse, { windows: [winState] }, options);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this._openWindowWithState({ windows: [winState] });
|
this._openWindowWithState({ windows: [winState] });
|
||||||
|
@ -2613,13 +2614,19 @@ let SessionStoreInternal = {
|
||||||
* Window reference
|
* Window reference
|
||||||
* @param aState
|
* @param aState
|
||||||
* JS object or its eval'able source
|
* JS object or its eval'able source
|
||||||
* @param aOverwriteTabs
|
* @param aOptions
|
||||||
* bool overwrite existing tabs w/ new ones
|
* {overwriteTabs: true} to overwrite existing tabs w/ new ones
|
||||||
* @param aFollowUp
|
* {isFollowUp: true} if this is not the restoration of the 1st window
|
||||||
* bool this isn't the restoration of the first window
|
* {firstWindow: true} if this is the first non-private window we're
|
||||||
|
* restoring in this session, that might open an
|
||||||
|
* external link as well
|
||||||
*/
|
*/
|
||||||
restoreWindow: function ssi_restoreWindow(aWindow, aState, aOverwriteTabs, aFollowUp) {
|
restoreWindow: function ssi_restoreWindow(aWindow, aState, aOptions = {}) {
|
||||||
if (!aFollowUp) {
|
let overwriteTabs = aOptions && aOptions.overwriteTabs;
|
||||||
|
let isFollowUp = aOptions && aOptions.isFollowUp;
|
||||||
|
let firstWindow = aOptions && aOptions.firstWindow;
|
||||||
|
|
||||||
|
if (isFollowUp) {
|
||||||
this.windowToFocus = aWindow;
|
this.windowToFocus = aWindow;
|
||||||
}
|
}
|
||||||
// initialize window if necessary
|
// initialize window if necessary
|
||||||
|
@ -2670,13 +2677,13 @@ let SessionStoreInternal = {
|
||||||
}
|
}
|
||||||
// don't restore a single blank tab when we've had an external
|
// don't restore a single blank tab when we've had an external
|
||||||
// URL passed in for loading at startup (cf. bug 357419)
|
// URL passed in for loading at startup (cf. bug 357419)
|
||||||
else if (root._firstTabs && !aOverwriteTabs && winData.tabs.length == 1 &&
|
else if (firstWindow && !overwriteTabs && winData.tabs.length == 1 &&
|
||||||
(!winData.tabs[0].entries || winData.tabs[0].entries.length == 0)) {
|
(!winData.tabs[0].entries || winData.tabs[0].entries.length == 0)) {
|
||||||
winData.tabs = [];
|
winData.tabs = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
var tabbrowser = aWindow.gBrowser;
|
var tabbrowser = aWindow.gBrowser;
|
||||||
var openTabCount = aOverwriteTabs ? tabbrowser.browsers.length : -1;
|
var openTabCount = overwriteTabs ? tabbrowser.browsers.length : -1;
|
||||||
var newTabCount = winData.tabs.length;
|
var newTabCount = winData.tabs.length;
|
||||||
var tabs = [];
|
var tabs = [];
|
||||||
|
|
||||||
|
@ -2686,14 +2693,14 @@ let SessionStoreInternal = {
|
||||||
tabstrip.smoothScroll = false;
|
tabstrip.smoothScroll = false;
|
||||||
|
|
||||||
// unpin all tabs to ensure they are not reordered in the next loop
|
// unpin all tabs to ensure they are not reordered in the next loop
|
||||||
if (aOverwriteTabs) {
|
if (overwriteTabs) {
|
||||||
for (let t = tabbrowser._numPinnedTabs - 1; t > -1; t--)
|
for (let t = tabbrowser._numPinnedTabs - 1; t > -1; t--)
|
||||||
tabbrowser.unpinTab(tabbrowser.tabs[t]);
|
tabbrowser.unpinTab(tabbrowser.tabs[t]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure that the selected tab won't be closed in order to
|
// make sure that the selected tab won't be closed in order to
|
||||||
// prevent unnecessary flickering
|
// prevent unnecessary flickering
|
||||||
if (aOverwriteTabs && tabbrowser.selectedTab._tPos >= newTabCount)
|
if (overwriteTabs && tabbrowser.selectedTab._tPos >= newTabCount)
|
||||||
tabbrowser.moveTabTo(tabbrowser.selectedTab, newTabCount - 1);
|
tabbrowser.moveTabTo(tabbrowser.selectedTab, newTabCount - 1);
|
||||||
|
|
||||||
let numVisibleTabs = 0;
|
let numVisibleTabs = 0;
|
||||||
|
@ -2703,7 +2710,7 @@ let SessionStoreInternal = {
|
||||||
tabbrowser.tabs[t] :
|
tabbrowser.tabs[t] :
|
||||||
tabbrowser.addTab("about:blank", {skipAnimation: true}));
|
tabbrowser.addTab("about:blank", {skipAnimation: true}));
|
||||||
// when resuming at startup: add additionally requested pages to the end
|
// when resuming at startup: add additionally requested pages to the end
|
||||||
if (!aOverwriteTabs && root._firstTabs) {
|
if (!overwriteTabs && firstWindow) {
|
||||||
tabbrowser.moveTabTo(tabs[t], t);
|
tabbrowser.moveTabTo(tabs[t], t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2730,7 +2737,7 @@ let SessionStoreInternal = {
|
||||||
// tabs will be rebuilt and marked if they need to be restored after loading
|
// tabs will be rebuilt and marked if they need to be restored after loading
|
||||||
// state (in restoreHistoryPrecursor).
|
// state (in restoreHistoryPrecursor).
|
||||||
// We also want to invalidate any cached information on the tab state.
|
// We also want to invalidate any cached information on the tab state.
|
||||||
if (aOverwriteTabs) {
|
if (overwriteTabs) {
|
||||||
for (let i = 0; i < tabbrowser.tabs.length; i++) {
|
for (let i = 0; i < tabbrowser.tabs.length; i++) {
|
||||||
let tab = tabbrowser.tabs[i];
|
let tab = tabbrowser.tabs[i];
|
||||||
TabStateCache.delete(tab);
|
TabStateCache.delete(tab);
|
||||||
|
@ -2746,7 +2753,7 @@ let SessionStoreInternal = {
|
||||||
// count in case there are still tabs restoring.
|
// count in case there are still tabs restoring.
|
||||||
if (!aWindow.__SS_tabsToRestore)
|
if (!aWindow.__SS_tabsToRestore)
|
||||||
aWindow.__SS_tabsToRestore = 0;
|
aWindow.__SS_tabsToRestore = 0;
|
||||||
if (aOverwriteTabs)
|
if (overwriteTabs)
|
||||||
aWindow.__SS_tabsToRestore = newTabCount;
|
aWindow.__SS_tabsToRestore = newTabCount;
|
||||||
else
|
else
|
||||||
aWindow.__SS_tabsToRestore += newTabCount;
|
aWindow.__SS_tabsToRestore += newTabCount;
|
||||||
|
@ -2759,12 +2766,12 @@ let SessionStoreInternal = {
|
||||||
aWindow.__SS_lastSessionWindowID = winData.__lastSessionWindowID;
|
aWindow.__SS_lastSessionWindowID = winData.__lastSessionWindowID;
|
||||||
|
|
||||||
// when overwriting tabs, remove all superflous ones
|
// when overwriting tabs, remove all superflous ones
|
||||||
if (aOverwriteTabs && newTabCount < openTabCount) {
|
if (overwriteTabs && newTabCount < openTabCount) {
|
||||||
Array.slice(tabbrowser.tabs, newTabCount, openTabCount)
|
Array.slice(tabbrowser.tabs, newTabCount, openTabCount)
|
||||||
.forEach(tabbrowser.removeTab, tabbrowser);
|
.forEach(tabbrowser.removeTab, tabbrowser);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aOverwriteTabs) {
|
if (overwriteTabs) {
|
||||||
this.restoreWindowFeatures(aWindow, winData);
|
this.restoreWindowFeatures(aWindow, winData);
|
||||||
delete this._windows[aWindow.__SSi].extData;
|
delete this._windows[aWindow.__SSi].extData;
|
||||||
}
|
}
|
||||||
|
@ -2779,12 +2786,12 @@ let SessionStoreInternal = {
|
||||||
this._windows[aWindow.__SSi].extData[key] = winData.extData[key];
|
this._windows[aWindow.__SSi].extData[key] = winData.extData[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (aOverwriteTabs || root._firstTabs) {
|
if (overwriteTabs || firstWindow) {
|
||||||
this._windows[aWindow.__SSi]._closedTabs = winData._closedTabs || [];
|
this._windows[aWindow.__SSi]._closedTabs = winData._closedTabs || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.restoreHistoryPrecursor(aWindow, tabs, winData.tabs,
|
this.restoreHistoryPrecursor(aWindow, tabs, winData.tabs,
|
||||||
(aOverwriteTabs ? (parseInt(winData.selected) || 1) : 0), 0, 0);
|
(overwriteTabs ? (parseInt(winData.selected) || 1) : 0), 0, 0);
|
||||||
|
|
||||||
if (aState.scratchpads) {
|
if (aState.scratchpads) {
|
||||||
ScratchpadManager.restoreSession(aState.scratchpads);
|
ScratchpadManager.restoreSession(aState.scratchpads);
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
function test() {
|
function test() {
|
||||||
waitForExplicitFinish();
|
waitForExplicitFinish();
|
||||||
newWindowWithTabView(onTabViewShown);
|
newWindowWithTabView(onTabViewShown, null, 850);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onTabViewShown(win) {
|
function onTabViewShown(win) {
|
||||||
|
|
|
@ -45,12 +45,8 @@ InspectorPanel.prototype = {
|
||||||
*/
|
*/
|
||||||
open: function InspectorPanel_open() {
|
open: function InspectorPanel_open() {
|
||||||
return this.target.makeRemote().then(() => {
|
return this.target.makeRemote().then(() => {
|
||||||
return this.target.inspector.getWalker();
|
return this._getWalker();
|
||||||
}).then(walker => {
|
}).then(() => {
|
||||||
if (this._destroyPromise) {
|
|
||||||
walker.release().then(null, console.error);
|
|
||||||
}
|
|
||||||
this.walker = walker;
|
|
||||||
return this._getDefaultNodeForSelection();
|
return this._getDefaultNodeForSelection();
|
||||||
}).then(defaultSelection => {
|
}).then(defaultSelection => {
|
||||||
return this._deferredOpen(defaultSelection);
|
return this._deferredOpen(defaultSelection);
|
||||||
|
@ -149,6 +145,16 @@ InspectorPanel.prototype = {
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getWalker: function() {
|
||||||
|
let inspector = this.target.inspector;
|
||||||
|
return inspector.getWalker().then(walker => {
|
||||||
|
this.walker = walker;
|
||||||
|
return inspector.getPageStyle();
|
||||||
|
}).then(pageStyle => {
|
||||||
|
this.pageStyle = pageStyle;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a promise that will resolve to the default node for selection.
|
* Return a promise that will resolve to the default node for selection.
|
||||||
*/
|
*/
|
||||||
|
@ -317,7 +323,7 @@ InspectorPanel.prototype = {
|
||||||
try {
|
try {
|
||||||
selfUpdate(selection);
|
selfUpdate(selection);
|
||||||
} catch(ex) {
|
} catch(ex) {
|
||||||
console.error(ex);
|
console.error(ex)
|
||||||
}
|
}
|
||||||
}, Ci.nsIThread.DISPATCH_NORMAL);
|
}, Ci.nsIThread.DISPATCH_NORMAL);
|
||||||
},
|
},
|
||||||
|
@ -403,6 +409,7 @@ InspectorPanel.prototype = {
|
||||||
if (this.walker) {
|
if (this.walker) {
|
||||||
this._destroyPromise = this.walker.release().then(null, console.error);
|
this._destroyPromise = this.walker.release().then(null, console.error);
|
||||||
delete this.walker;
|
delete this.walker;
|
||||||
|
delete this.pageStyle;
|
||||||
} else {
|
} else {
|
||||||
this._destroyPromise = promise.resolve(null);
|
this._destroyPromise = promise.resolve(null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ Selection.prototype = {
|
||||||
attributeChange = true;
|
attributeChange = true;
|
||||||
}
|
}
|
||||||
if (m.type == "childList") {
|
if (m.type == "childList") {
|
||||||
if (!detached && !this.isConnected()) {
|
if (!detached && this.isNode() && !this.isConnected()) {
|
||||||
parentNode = m.target;
|
parentNode = m.target;
|
||||||
detached = true;
|
detached = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ function test() {
|
||||||
testDiv.style.fontSize = "10px";
|
testDiv.style.fontSize = "10px";
|
||||||
|
|
||||||
// Start up the style inspector panel...
|
// Start up the style inspector panel...
|
||||||
Services.obs.addObserver(stylePanelTests, "StyleInspector-populated", false);
|
inspector.once("computed-view-refreshed", stylePanelTests);
|
||||||
|
|
||||||
inspector.selection.setNode(testDiv);
|
inspector.selection.setNode(testDiv);
|
||||||
});
|
});
|
||||||
|
@ -48,15 +48,13 @@ function test() {
|
||||||
|
|
||||||
function stylePanelTests()
|
function stylePanelTests()
|
||||||
{
|
{
|
||||||
Services.obs.removeObserver(stylePanelTests, "StyleInspector-populated");
|
|
||||||
|
|
||||||
let computedview = inspector.sidebar.getWindowForTab("computedview").computedview;
|
let computedview = inspector.sidebar.getWindowForTab("computedview").computedview;
|
||||||
ok(computedview, "Style Panel has a cssHtmlTree");
|
ok(computedview, "Style Panel has a cssHtmlTree");
|
||||||
|
|
||||||
let propView = getInspectorProp("font-size");
|
let propView = getInspectorProp("font-size");
|
||||||
is(propView.value, "10px", "Style inspector should be showing the correct font size.");
|
is(propView.value, "10px", "Style inspector should be showing the correct font size.");
|
||||||
|
|
||||||
Services.obs.addObserver(stylePanelAfterChange, "StyleInspector-populated", false);
|
inspector.once("computed-view-refreshed", stylePanelAfterChange);
|
||||||
|
|
||||||
testDiv.style.fontSize = "15px";
|
testDiv.style.fontSize = "15px";
|
||||||
inspector.emit("layout-change");
|
inspector.emit("layout-change");
|
||||||
|
@ -64,8 +62,6 @@ function test() {
|
||||||
|
|
||||||
function stylePanelAfterChange()
|
function stylePanelAfterChange()
|
||||||
{
|
{
|
||||||
Services.obs.removeObserver(stylePanelAfterChange, "StyleInspector-populated");
|
|
||||||
|
|
||||||
let propView = getInspectorProp("font-size");
|
let propView = getInspectorProp("font-size");
|
||||||
is(propView.value, "15px", "Style inspector should be showing the new font size.");
|
is(propView.value, "15px", "Style inspector should be showing the new font size.");
|
||||||
|
|
||||||
|
@ -78,7 +74,7 @@ function test() {
|
||||||
inspector.sidebar.select("ruleview");
|
inspector.sidebar.select("ruleview");
|
||||||
|
|
||||||
executeSoon(function() {
|
executeSoon(function() {
|
||||||
Services.obs.addObserver(stylePanelAfterSwitch, "StyleInspector-populated", false);
|
inspector.once("computed-view-refreshed", stylePanelAfterSwitch);
|
||||||
testDiv.style.fontSize = "20px";
|
testDiv.style.fontSize = "20px";
|
||||||
inspector.sidebar.select("computedview");
|
inspector.sidebar.select("computedview");
|
||||||
});
|
});
|
||||||
|
@ -86,8 +82,6 @@ function test() {
|
||||||
|
|
||||||
function stylePanelAfterSwitch()
|
function stylePanelAfterSwitch()
|
||||||
{
|
{
|
||||||
Services.obs.removeObserver(stylePanelAfterSwitch, "StyleInspector-populated");
|
|
||||||
|
|
||||||
let propView = getInspectorProp("font-size");
|
let propView = getInspectorProp("font-size");
|
||||||
is(propView.value, "20px", "Style inspector should be showing the newest font size.");
|
is(propView.value, "20px", "Style inspector should be showing the newest font size.");
|
||||||
|
|
||||||
|
|
|
@ -68,14 +68,12 @@ function performTests()
|
||||||
// inspector has been told of the pseudoclass lock change.
|
// inspector has been told of the pseudoclass lock change.
|
||||||
inspector.selection.once("pseudoclass", () => {
|
inspector.selection.once("pseudoclass", () => {
|
||||||
// Give the rule view time to update.
|
// Give the rule view time to update.
|
||||||
executeSoon(() => {
|
inspector.once("rule-view-refreshed", () => {
|
||||||
testAdded();
|
testAdded();
|
||||||
|
// Change the pseudo class and give the rule view time to update.
|
||||||
// toggle the lock off and wait for the pseudoclass event again.
|
|
||||||
inspector.togglePseudoClass(pseudo);
|
inspector.togglePseudoClass(pseudo);
|
||||||
inspector.selection.once("pseudoclass", () => {
|
inspector.selection.once("pseudoclass", () => {
|
||||||
// Give the rule view time to update.
|
inspector.once("rule-view-refreshed", () => {
|
||||||
executeSoon(() => {
|
|
||||||
testRemoved();
|
testRemoved();
|
||||||
testRemovedFromUI();
|
testRemovedFromUI();
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,8 @@
|
||||||
accesskey="&netmonitorUI.context.copyUrl.accesskey;"
|
accesskey="&netmonitorUI.context.copyUrl.accesskey;"
|
||||||
oncommand="NetMonitorView.RequestsMenu.copyUrl();"/>
|
oncommand="NetMonitorView.RequestsMenu.copyUrl();"/>
|
||||||
<menuitem id="request-menu-context-resend"
|
<menuitem id="request-menu-context-resend"
|
||||||
label="&netmonitorUI.summary.resend;"
|
label="&netmonitorUI.summary.editAndResend;"
|
||||||
accesskey="&netmonitorUI.summary.resend.accesskey;"
|
accesskey="&netmonitorUI.summary.editAndResend.accesskey;"
|
||||||
oncommand="NetMonitorView.RequestsMenu.cloneSelectedRequest();"/>
|
oncommand="NetMonitorView.RequestsMenu.cloneSelectedRequest();"/>
|
||||||
</menupopup>
|
</menupopup>
|
||||||
</popupset>
|
</popupset>
|
||||||
|
@ -314,7 +314,7 @@
|
||||||
crop="end"
|
crop="end"
|
||||||
flex="1"/>
|
flex="1"/>
|
||||||
<button id="headers-summary-resend"
|
<button id="headers-summary-resend"
|
||||||
label="&netmonitorUI.summary.resend;"
|
label="&netmonitorUI.summary.editAndResend;"
|
||||||
class="devtools-toolbarbutton"
|
class="devtools-toolbarbutton"
|
||||||
onclick="NetMonitorView.RequestsMenu.cloneSelectedRequest();"/>
|
onclick="NetMonitorView.RequestsMenu.cloneSelectedRequest();"/>
|
||||||
</hbox>
|
</hbox>
|
||||||
|
|
|
@ -53,45 +53,39 @@ function test() {
|
||||||
|
|
||||||
instance.setSize(500, 500);
|
instance.setSize(500, 500);
|
||||||
|
|
||||||
openInspector(onInspectorUIOpen);
|
openComputedView(onInspectorUIOpen);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onInspectorUIOpen(aInspector) {
|
function onInspectorUIOpen(aInspector, aComputedView) {
|
||||||
inspector = aInspector;
|
inspector = aInspector;
|
||||||
ok(inspector, "Got inspector instance");
|
ok(inspector, "Got inspector instance");
|
||||||
inspector.sidebar.select("computedview");
|
|
||||||
|
|
||||||
let div = content.document.getElementsByTagName("div")[0];
|
let div = content.document.getElementsByTagName("div")[0];
|
||||||
|
|
||||||
inspector.sidebar.once("computedview-ready", function() {
|
inspector.selection.setNode(div);
|
||||||
Services.obs.addObserver(testShrink, "StyleInspector-populated", false);
|
inspector.once("inspector-updated", testShrink);
|
||||||
inspector.selection.setNode(div);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function testShrink() {
|
function testShrink() {
|
||||||
Services.obs.removeObserver(testShrink, "StyleInspector-populated");
|
|
||||||
|
|
||||||
computedView = inspector.sidebar.getWindowForTab("computedview").computedview.view;
|
computedView = inspector.sidebar.getWindowForTab("computedview").computedview.view;
|
||||||
ok(computedView, "We have access to the Computed View object");
|
ok(computedView, "We have access to the Computed View object");
|
||||||
|
|
||||||
is(computedWidth(), "500px", "Should show 500px initially.");
|
is(computedWidth(), "500px", "Should show 500px initially.");
|
||||||
|
|
||||||
Services.obs.addObserver(function onShrink() {
|
inspector.once("computed-view-refreshed", function onShrink() {
|
||||||
Services.obs.removeObserver(onShrink, "StyleInspector-populated");
|
|
||||||
is(computedWidth(), "100px", "div should be 100px after shrinking.");
|
is(computedWidth(), "100px", "div should be 100px after shrinking.");
|
||||||
testGrow();
|
testGrow();
|
||||||
}, "StyleInspector-populated", false);
|
});
|
||||||
|
|
||||||
instance.setSize(100, 100);
|
instance.setSize(100, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testGrow() {
|
function testGrow() {
|
||||||
Services.obs.addObserver(function onGrow() {
|
inspector.once("computed-view-refreshed", function onGrow() {
|
||||||
Services.obs.removeObserver(onGrow, "StyleInspector-populated");
|
|
||||||
is(computedWidth(), "500px", "Should be 500px after growing.");
|
is(computedWidth(), "500px", "Should be 500px after growing.");
|
||||||
finishUp();
|
finishUp();
|
||||||
}, "StyleInspector-populated", false);
|
});
|
||||||
|
|
||||||
instance.setSize(500, 500);
|
instance.setSize(500, 500);
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,26 +48,20 @@ function test() {
|
||||||
|
|
||||||
instance.setSize(500, 500);
|
instance.setSize(500, 500);
|
||||||
|
|
||||||
openInspector(onInspectorUIOpen);
|
openRuleView(onInspectorUIOpen);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onInspectorUIOpen(aInspector) {
|
function onInspectorUIOpen(aInspector, aRuleView) {
|
||||||
inspector = aInspector;
|
inspector = aInspector;
|
||||||
|
ruleView = aRuleView;
|
||||||
ok(inspector, "Got inspector instance");
|
ok(inspector, "Got inspector instance");
|
||||||
inspector.sidebar.select("ruleview");
|
|
||||||
|
|
||||||
let div = content.document.getElementsByTagName("div")[0];
|
let div = content.document.getElementsByTagName("div")[0];
|
||||||
|
inspector.selection.setNode(div);
|
||||||
inspector.sidebar.once("ruleview-ready", function() {
|
inspector.once("inspector-updated", testShrink);
|
||||||
Services.obs.addObserver(testShrink, "StyleInspector-populated", false);
|
|
||||||
inspector.selection.setNode(div);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function testShrink() {
|
function testShrink() {
|
||||||
Services.obs.removeObserver(testShrink, "StyleInspector-populated");
|
|
||||||
|
|
||||||
ruleView = inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
|
|
||||||
|
|
||||||
is(numberOfRules(), 2, "Should have two rules initially.");
|
is(numberOfRules(), 2, "Should have two rules initially.");
|
||||||
|
|
||||||
|
|
|
@ -18,3 +18,23 @@ function openInspector(callback)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openComputedView(callback)
|
||||||
|
{
|
||||||
|
openInspector(inspector => {
|
||||||
|
inspector.sidebar.once("computedview-ready", () => {
|
||||||
|
inspector.sidebar.select("computedview");
|
||||||
|
let ruleView = inspector.sidebar.getWindowForTab("computedview").computedview.view;
|
||||||
|
callback(inspector, ruleView);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function openRuleView(callback)
|
||||||
|
{
|
||||||
|
openInspector(inspector => {
|
||||||
|
inspector.sidebar.once("ruleview-ready", () => {
|
||||||
|
inspector.sidebar.select("ruleview");
|
||||||
|
let ruleView = inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
|
||||||
|
callback(inspector, ruleView);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,9 @@ const {Cc, Ci, Cu} = require("chrome");
|
||||||
|
|
||||||
let ToolDefinitions = require("main").Tools;
|
let ToolDefinitions = require("main").Tools;
|
||||||
let {CssLogic} = require("devtools/styleinspector/css-logic");
|
let {CssLogic} = require("devtools/styleinspector/css-logic");
|
||||||
|
let {ELEMENT_STYLE} = require("devtools/server/actors/styles");
|
||||||
|
let promise = require("sdk/core/promise");
|
||||||
|
let {EventEmitter} = require("devtools/shared/event-emitter");
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
Cu.import("resource://gre/modules/PluralForm.jsm");
|
Cu.import("resource://gre/modules/PluralForm.jsm");
|
||||||
|
@ -90,6 +93,7 @@ UpdateProcess.prototype = {
|
||||||
this.onDone();
|
this.onDone();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
console.error(e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -115,14 +119,18 @@ UpdateProcess.prototype = {
|
||||||
* will generally only be one).
|
* will generally only be one).
|
||||||
*
|
*
|
||||||
* @params {StyleInspector} aStyleInspector The owner of this CssHtmlTree
|
* @params {StyleInspector} aStyleInspector The owner of this CssHtmlTree
|
||||||
|
* @param {PageStyleFront} aPageStyle
|
||||||
|
* Front for the page style actor that will be providing
|
||||||
|
* the style information.
|
||||||
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function CssHtmlTree(aStyleInspector)
|
function CssHtmlTree(aStyleInspector, aPageStyle)
|
||||||
{
|
{
|
||||||
this.styleWindow = aStyleInspector.window;
|
this.styleWindow = aStyleInspector.window;
|
||||||
this.styleDocument = aStyleInspector.window.document;
|
this.styleDocument = aStyleInspector.window.document;
|
||||||
this.styleInspector = aStyleInspector;
|
this.styleInspector = aStyleInspector;
|
||||||
this.cssLogic = aStyleInspector.cssLogic;
|
this.pageStyle = aPageStyle;
|
||||||
this.propertyViews = [];
|
this.propertyViews = [];
|
||||||
|
|
||||||
let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].
|
let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].
|
||||||
|
@ -144,6 +152,8 @@ function CssHtmlTree(aStyleInspector)
|
||||||
// No results text.
|
// No results text.
|
||||||
this.noResults = this.styleDocument.getElementById("noResults");
|
this.noResults = this.styleDocument.getElementById("noResults");
|
||||||
|
|
||||||
|
CssHtmlTree.processTemplate(this.templateRoot, this.root, this);
|
||||||
|
|
||||||
// The element that we're inspecting, and the document that it comes from.
|
// The element that we're inspecting, and the document that it comes from.
|
||||||
this.viewedElement = null;
|
this.viewedElement = null;
|
||||||
this.createStyleViews();
|
this.createStyleViews();
|
||||||
|
@ -207,8 +217,6 @@ CssHtmlTree.prototype = {
|
||||||
// Cache the list of properties that match the selected element.
|
// Cache the list of properties that match the selected element.
|
||||||
_matchedProperties: null,
|
_matchedProperties: null,
|
||||||
|
|
||||||
htmlComplete: false,
|
|
||||||
|
|
||||||
// Used for cancelling timeouts in the style filter.
|
// Used for cancelling timeouts in the style filter.
|
||||||
_filterChangedTimeout: null,
|
_filterChangedTimeout: null,
|
||||||
|
|
||||||
|
@ -227,6 +235,10 @@ CssHtmlTree.prototype = {
|
||||||
// Number of visible properties
|
// Number of visible properties
|
||||||
numVisibleProperties: 0,
|
numVisibleProperties: 0,
|
||||||
|
|
||||||
|
setPageStyle: function(pageStyle) {
|
||||||
|
this.pageStyle = pageStyle;
|
||||||
|
},
|
||||||
|
|
||||||
get includeBrowserStyles()
|
get includeBrowserStyles()
|
||||||
{
|
{
|
||||||
return this.includeBrowserStylesCheckbox.checked;
|
return this.includeBrowserStylesCheckbox.checked;
|
||||||
|
@ -236,65 +248,65 @@ CssHtmlTree.prototype = {
|
||||||
* Update the highlighted element. The CssHtmlTree panel will show the style
|
* Update the highlighted element. The CssHtmlTree panel will show the style
|
||||||
* information for the given element.
|
* information for the given element.
|
||||||
* @param {nsIDOMElement} aElement The highlighted node to get styles for.
|
* @param {nsIDOMElement} aElement The highlighted node to get styles for.
|
||||||
|
*
|
||||||
|
* @returns a promise that will be resolved when highlighting is complete.
|
||||||
*/
|
*/
|
||||||
highlight: function CssHtmlTree_highlight(aElement)
|
highlight: function(aElement) {
|
||||||
{
|
|
||||||
this.viewedElement = aElement;
|
|
||||||
this._matchedProperties = null;
|
|
||||||
|
|
||||||
if (!aElement) {
|
if (!aElement) {
|
||||||
if (this._refreshProcess) {
|
if (this._refreshProcess) {
|
||||||
this._refreshProcess.cancel();
|
this._refreshProcess.cancel();
|
||||||
}
|
}
|
||||||
return;
|
return promise.resolve(undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.htmlComplete) {
|
if (aElement === this.viewedElement) {
|
||||||
this.refreshSourceFilter();
|
return promise.resolve(undefined);
|
||||||
this.refreshPanel();
|
}
|
||||||
} else {
|
|
||||||
if (this._refreshProcess) {
|
this.viewedElement = aElement;
|
||||||
this._refreshProcess.cancel();
|
|
||||||
|
this.refreshSourceFilter();
|
||||||
|
return this.refreshPanel();
|
||||||
|
},
|
||||||
|
|
||||||
|
_createPropertyViews: function()
|
||||||
|
{
|
||||||
|
if (this._createViewsPromise) {
|
||||||
|
return this._createViewsPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
let deferred = promise.defer();
|
||||||
|
this._createViewsPromise = deferred.promise;
|
||||||
|
|
||||||
|
this.refreshSourceFilter();
|
||||||
|
this.numVisibleProperties = 0;
|
||||||
|
let fragment = this.styleDocument.createDocumentFragment();
|
||||||
|
|
||||||
|
this._createViewsProcess = new UpdateProcess(this.styleWindow, CssHtmlTree.propertyNames, {
|
||||||
|
onItem: (aPropertyName) => {
|
||||||
|
// Per-item callback.
|
||||||
|
let propView = new PropertyView(this, aPropertyName);
|
||||||
|
fragment.appendChild(propView.buildMain());
|
||||||
|
fragment.appendChild(propView.buildSelectorContainer());
|
||||||
|
|
||||||
|
if (propView.visible) {
|
||||||
|
this.numVisibleProperties++;
|
||||||
|
}
|
||||||
|
this.propertyViews.push(propView);
|
||||||
|
},
|
||||||
|
onCancel: () => {
|
||||||
|
deferred.reject("_createPropertyViews cancelled");
|
||||||
|
},
|
||||||
|
onDone: () => {
|
||||||
|
// Completed callback.
|
||||||
|
this.propertyContainer.appendChild(fragment);
|
||||||
|
this.noResults.hidden = this.numVisibleProperties > 0;
|
||||||
|
deferred.resolve(undefined);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
CssHtmlTree.processTemplate(this.templateRoot, this.root, this);
|
this._createViewsProcess.schedule();
|
||||||
|
return deferred.promise;
|
||||||
// Refresh source filter ... this must be done after templateRoot has been
|
|
||||||
// processed.
|
|
||||||
this.refreshSourceFilter();
|
|
||||||
this.numVisibleProperties = 0;
|
|
||||||
let fragment = this.styleDocument.createDocumentFragment();
|
|
||||||
this._refreshProcess = new UpdateProcess(this.styleWindow, CssHtmlTree.propertyNames, {
|
|
||||||
onItem: function(aPropertyName) {
|
|
||||||
// Per-item callback.
|
|
||||||
let propView = new PropertyView(this, aPropertyName);
|
|
||||||
fragment.appendChild(propView.buildMain());
|
|
||||||
fragment.appendChild(propView.buildSelectorContainer());
|
|
||||||
|
|
||||||
if (propView.visible) {
|
|
||||||
this.numVisibleProperties++;
|
|
||||||
}
|
|
||||||
propView.refreshMatchedSelectors();
|
|
||||||
this.propertyViews.push(propView);
|
|
||||||
}.bind(this),
|
|
||||||
onDone: function() {
|
|
||||||
// Completed callback.
|
|
||||||
this.htmlComplete = true;
|
|
||||||
this.propertyContainer.appendChild(fragment);
|
|
||||||
this.noResults.hidden = this.numVisibleProperties > 0;
|
|
||||||
this._refreshProcess = null;
|
|
||||||
|
|
||||||
// If a refresh was scheduled during the building, complete it.
|
|
||||||
if (this._needsRefresh) {
|
|
||||||
delete this._needsRefresh;
|
|
||||||
this.refreshPanel();
|
|
||||||
} else {
|
|
||||||
Services.obs.notifyObservers(null, "StyleInspector-populated", null);
|
|
||||||
}
|
|
||||||
}.bind(this)});
|
|
||||||
|
|
||||||
this._refreshProcess.schedule();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -302,39 +314,54 @@ CssHtmlTree.prototype = {
|
||||||
*/
|
*/
|
||||||
refreshPanel: function CssHtmlTree_refreshPanel()
|
refreshPanel: function CssHtmlTree_refreshPanel()
|
||||||
{
|
{
|
||||||
// If we're still in the process of creating the initial layout,
|
return promise.all([
|
||||||
// leave it alone.
|
this._createPropertyViews(),
|
||||||
if (!this.htmlComplete) {
|
this.pageStyle.getComputed(this.viewedElement, {
|
||||||
if (this._refreshProcess) {
|
filter: this._sourceFilter,
|
||||||
this._needsRefresh = true;
|
onlyMatched: !this.includeBrowserStyles,
|
||||||
|
markMatched: true
|
||||||
|
})
|
||||||
|
]).then(([createViews, computed]) => {
|
||||||
|
this._matchedProperties = new Set;
|
||||||
|
for (let name in computed) {
|
||||||
|
if (computed[name].matched) {
|
||||||
|
this._matchedProperties.add(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
this._computed = computed;
|
||||||
}
|
|
||||||
|
|
||||||
if (this._refreshProcess) {
|
if (this._refreshProcess) {
|
||||||
this._refreshProcess.cancel();
|
this._refreshProcess.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.noResults.hidden = true;
|
this.noResults.hidden = true;
|
||||||
|
|
||||||
// Reset visible property count
|
// Reset visible property count
|
||||||
this.numVisibleProperties = 0;
|
this.numVisibleProperties = 0;
|
||||||
|
|
||||||
// Reset zebra striping.
|
// Reset zebra striping.
|
||||||
this._darkStripe = true;
|
this._darkStripe = true;
|
||||||
|
|
||||||
let display = this.propertyContainer.style.display;
|
let display = this.propertyContainer.style.display;
|
||||||
this._refreshProcess = new UpdateProcess(this.styleWindow, this.propertyViews, {
|
|
||||||
onItem: function(aPropView) {
|
let deferred = promise.defer();
|
||||||
aPropView.refresh();
|
this._refreshProcess = new UpdateProcess(this.styleWindow, this.propertyViews, {
|
||||||
}.bind(this),
|
onItem: (aPropView) => {
|
||||||
onDone: function() {
|
aPropView.refresh();
|
||||||
this._refreshProcess = null;
|
},
|
||||||
this.noResults.hidden = this.numVisibleProperties > 0;
|
onCancel: () => {
|
||||||
Services.obs.notifyObservers(null, "StyleInspector-populated", null);
|
deferred.reject("refresh cancelled");
|
||||||
}.bind(this)
|
},
|
||||||
});
|
onDone: () => {
|
||||||
this._refreshProcess.schedule();
|
this._refreshProcess = null;
|
||||||
|
this.noResults.hidden = this.numVisibleProperties > 0;
|
||||||
|
this.styleInspector.inspector.emit("computed-view-refreshed");
|
||||||
|
deferred.resolve(undefined);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this._refreshProcess.schedule();
|
||||||
|
return deferred.promise;
|
||||||
|
}).then(null, (err) => console.error(err));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -377,7 +404,7 @@ CssHtmlTree.prototype = {
|
||||||
refreshSourceFilter: function CssHtmlTree_setSourceFilter()
|
refreshSourceFilter: function CssHtmlTree_setSourceFilter()
|
||||||
{
|
{
|
||||||
this._matchedProperties = null;
|
this._matchedProperties = null;
|
||||||
this.cssLogic.sourceFilter = this.includeBrowserStyles ?
|
this._sourceFilter = this.includeBrowserStyles ?
|
||||||
CssLogic.FILTER.UA :
|
CssLogic.FILTER.UA :
|
||||||
CssLogic.FILTER.USER;
|
CssLogic.FILTER.USER;
|
||||||
},
|
},
|
||||||
|
@ -409,21 +436,18 @@ CssHtmlTree.prototype = {
|
||||||
CssHtmlTree.propertyNames.sort();
|
CssHtmlTree.propertyNames.sort();
|
||||||
CssHtmlTree.propertyNames.push.apply(CssHtmlTree.propertyNames,
|
CssHtmlTree.propertyNames.push.apply(CssHtmlTree.propertyNames,
|
||||||
mozProps.sort());
|
mozProps.sort());
|
||||||
|
|
||||||
|
this._createPropertyViews();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of properties that have matched selectors.
|
* Get a set of properties that have matched selectors.
|
||||||
*
|
*
|
||||||
* @return {object} the object maps property names (keys) to booleans (values)
|
* @return {Set} If a property name is in the set, it has matching selectors.
|
||||||
* that tell if the given property has matched selectors or not.
|
|
||||||
*/
|
*/
|
||||||
get matchedProperties()
|
get matchedProperties()
|
||||||
{
|
{
|
||||||
if (!this._matchedProperties) {
|
return this._matchedProperties || new Set;
|
||||||
this._matchedProperties =
|
|
||||||
this.cssLogic.hasMatchedSelectors(CssHtmlTree.propertyNames);
|
|
||||||
}
|
|
||||||
return this._matchedProperties;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -473,6 +497,9 @@ CssHtmlTree.prototype = {
|
||||||
this.searchField.removeEventListener("command", this.filterChanged);
|
this.searchField.removeEventListener("command", this.filterChanged);
|
||||||
|
|
||||||
// Cancel tree construction
|
// Cancel tree construction
|
||||||
|
if (this._createViewsProcess) {
|
||||||
|
this._createViewsProcess.cancel();
|
||||||
|
}
|
||||||
if (this._refreshProcess) {
|
if (this._refreshProcess) {
|
||||||
this._refreshProcess.cancel();
|
this._refreshProcess.cancel();
|
||||||
}
|
}
|
||||||
|
@ -517,11 +544,18 @@ CssHtmlTree.prototype = {
|
||||||
delete this.propertyViews;
|
delete this.propertyViews;
|
||||||
delete this.styleWindow;
|
delete this.styleWindow;
|
||||||
delete this.styleDocument;
|
delete this.styleDocument;
|
||||||
delete this.cssLogic;
|
|
||||||
delete this.styleInspector;
|
delete this.styleInspector;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function PropertyInfo(aTree, aName) {
|
||||||
|
this.tree = aTree;
|
||||||
|
this.name = aName;
|
||||||
|
}
|
||||||
|
PropertyInfo.prototype = {
|
||||||
|
get value() this.tree._computed ? this.tree._computed[this.name].value : ""
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A container to give easy access to property data from the template engine.
|
* A container to give easy access to property data from the template engine.
|
||||||
*
|
*
|
||||||
|
@ -539,6 +573,7 @@ function PropertyView(aTree, aName)
|
||||||
this.link = "https://developer.mozilla.org/CSS/" + aName;
|
this.link = "https://developer.mozilla.org/CSS/" + aName;
|
||||||
|
|
||||||
this.templateMatchedSelectors = aTree.styleDocument.getElementById("templateMatchedSelectors");
|
this.templateMatchedSelectors = aTree.styleDocument.getElementById("templateMatchedSelectors");
|
||||||
|
this._propertyInfo = new PropertyInfo(aTree, aName);
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyView.prototype = {
|
PropertyView.prototype = {
|
||||||
|
@ -585,7 +620,7 @@ PropertyView.prototype = {
|
||||||
*/
|
*/
|
||||||
get propertyInfo()
|
get propertyInfo()
|
||||||
{
|
{
|
||||||
return this.tree.cssLogic.getPropertyInfo(this.name);
|
return this._propertyInfo;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -593,7 +628,7 @@ PropertyView.prototype = {
|
||||||
*/
|
*/
|
||||||
get hasMatchedSelectors()
|
get hasMatchedSelectors()
|
||||||
{
|
{
|
||||||
return this.name in this.tree.matchedProperties;
|
return this.tree.matchedProperties.has(this.name);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -738,15 +773,29 @@ PropertyView.prototype = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.matchedExpanded && hasMatchedSelectors) {
|
if (this.matchedExpanded && hasMatchedSelectors) {
|
||||||
CssHtmlTree.processTemplate(this.templateMatchedSelectors,
|
return this.tree.pageStyle.getMatchedSelectors(this.tree.viewedElement, this.name).then(matched => {
|
||||||
this.matchedSelectorsContainer, this);
|
if (!this.matchedExpanded) {
|
||||||
this.matchedExpander.setAttribute("open", "");
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._matchedSelectorResponse = matched;
|
||||||
|
CssHtmlTree.processTemplate(this.templateMatchedSelectors,
|
||||||
|
this.matchedSelectorsContainer, this);
|
||||||
|
this.matchedExpander.setAttribute("open", "");
|
||||||
|
this.tree.styleInspector.inspector.emit("computed-view-property-expanded");
|
||||||
|
}).then(null, console.error);
|
||||||
} else {
|
} else {
|
||||||
this.matchedSelectorsContainer.innerHTML = "";
|
this.matchedSelectorsContainer.innerHTML = "";
|
||||||
this.matchedExpander.removeAttribute("open");
|
this.matchedExpander.removeAttribute("open");
|
||||||
|
return promise.resolve(undefined);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get matchedSelectors()
|
||||||
|
{
|
||||||
|
return this._matchedSelectorResponse;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide access to the matched SelectorViews that we are currently
|
* Provide access to the matched SelectorViews that we are currently
|
||||||
* displaying.
|
* displaying.
|
||||||
|
@ -755,7 +804,7 @@ PropertyView.prototype = {
|
||||||
{
|
{
|
||||||
if (!this._matchedSelectorViews) {
|
if (!this._matchedSelectorViews) {
|
||||||
this._matchedSelectorViews = [];
|
this._matchedSelectorViews = [];
|
||||||
this.propertyInfo.matchedSelectors.forEach(
|
this._matchedSelectorResponse.forEach(
|
||||||
function matchedSelectorViews_convert(aSelectorInfo) {
|
function matchedSelectorViews_convert(aSelectorInfo) {
|
||||||
this._matchedSelectorViews.push(new SelectorView(this.tree, aSelectorInfo));
|
this._matchedSelectorViews.push(new SelectorView(this.tree, aSelectorInfo));
|
||||||
}, this);
|
}, this);
|
||||||
|
@ -802,6 +851,15 @@ function SelectorView(aTree, aSelectorInfo)
|
||||||
this.tree = aTree;
|
this.tree = aTree;
|
||||||
this.selectorInfo = aSelectorInfo;
|
this.selectorInfo = aSelectorInfo;
|
||||||
this._cacheStatusNames();
|
this._cacheStatusNames();
|
||||||
|
|
||||||
|
let rule = this.selectorInfo.rule;
|
||||||
|
if (rule && rule.parentStyleSheet) {
|
||||||
|
this.sheet = rule.parentStyleSheet;
|
||||||
|
this.source = CssLogic.shortSource(this.sheet) + ":" + rule.line;
|
||||||
|
} else {
|
||||||
|
this.source = CssLogic.l10n("rule.sourceElement");
|
||||||
|
this.href = "#";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -859,24 +917,25 @@ SelectorView.prototype = {
|
||||||
return SelectorView.CLASS_NAMES[this.selectorInfo.status - 1];
|
return SelectorView.CLASS_NAMES[this.selectorInfo.status - 1];
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
get href()
|
||||||
* A localized Get localized human readable info
|
{
|
||||||
*/
|
if (this._href) {
|
||||||
text: function SelectorView_text(aElement) {
|
return this._href;
|
||||||
let result = this.selectorInfo.selector.text;
|
|
||||||
if (this.selectorInfo.elementStyle) {
|
|
||||||
let source = this.selectorInfo.sourceElement;
|
|
||||||
let inspector = this.tree.styleInspector.inspector;
|
|
||||||
|
|
||||||
if (inspector.selection.node == source) {
|
|
||||||
result = "this";
|
|
||||||
} else {
|
|
||||||
result = CssLogic.getShortName(source);
|
|
||||||
}
|
|
||||||
result += ".style";
|
|
||||||
}
|
}
|
||||||
|
let sheet = this.selectorInfo.rule.parentStyleSheet;
|
||||||
|
this._href = sheet ? sheet.href : "#";
|
||||||
|
return this._href;
|
||||||
|
},
|
||||||
|
|
||||||
return result;
|
get sourceText()
|
||||||
|
{
|
||||||
|
return this.selectorInfo.sourceText;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
get value()
|
||||||
|
{
|
||||||
|
return this.selectorInfo.value;
|
||||||
},
|
},
|
||||||
|
|
||||||
maybeOpenStyleEditor: function(aEvent)
|
maybeOpenStyleEditor: function(aEvent)
|
||||||
|
@ -900,12 +959,8 @@ SelectorView.prototype = {
|
||||||
openStyleEditor: function(aEvent)
|
openStyleEditor: function(aEvent)
|
||||||
{
|
{
|
||||||
let inspector = this.tree.styleInspector.inspector;
|
let inspector = this.tree.styleInspector.inspector;
|
||||||
let contentDoc = inspector.selection.document;
|
let rule = this.selectorInfo.rule;
|
||||||
let cssSheet = this.selectorInfo.selector.cssRule._cssSheet;
|
let line = rule.line || 0;
|
||||||
let line = this.selectorInfo.ruleLine || 0;
|
|
||||||
let contentSheet = false;
|
|
||||||
let styleSheet;
|
|
||||||
let styleSheets;
|
|
||||||
|
|
||||||
// The style editor can only display stylesheets coming from content because
|
// The style editor can only display stylesheets coming from content because
|
||||||
// chrome stylesheets are not listed in the editor's stylesheet selector.
|
// chrome stylesheets are not listed in the editor's stylesheet selector.
|
||||||
|
@ -913,40 +968,28 @@ SelectorView.prototype = {
|
||||||
// If the stylesheet is a content stylesheet we send it to the style
|
// If the stylesheet is a content stylesheet we send it to the style
|
||||||
// editor else we display it in the view source window.
|
// editor else we display it in the view source window.
|
||||||
//
|
//
|
||||||
// We check if cssSheet exists in case of inline styles (which contain no
|
|
||||||
// sheet)
|
|
||||||
if (cssSheet) {
|
|
||||||
styleSheet = cssSheet.domSheet;
|
|
||||||
styleSheets = contentDoc.styleSheets;
|
|
||||||
|
|
||||||
// Array.prototype.indexOf always returns -1 here so we loop through
|
let href = rule.href;
|
||||||
// the styleSheets array instead.
|
let sheet = rule.parentStyleSheet;
|
||||||
for each (let sheet in styleSheets) {
|
if (sheet && href && !sheet.isSystem) {
|
||||||
if (sheet == styleSheet) {
|
|
||||||
contentSheet = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contentSheet) {
|
|
||||||
let target = inspector.target;
|
let target = inspector.target;
|
||||||
|
|
||||||
if (ToolDefinitions.styleEditor.isTargetSupported(target)) {
|
if (ToolDefinitions.styleEditor.isTargetSupported(target)) {
|
||||||
gDevTools.showToolbox(target, "styleeditor").then(function(toolbox) {
|
gDevTools.showToolbox(target, "styleeditor").then(function(toolbox) {
|
||||||
toolbox.getCurrentPanel().selectStyleSheet(styleSheet.href, line);
|
toolbox.getCurrentPanel().selectStyleSheet(href, line);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
return;
|
||||||
let href = styleSheet ? styleSheet.href : "";
|
|
||||||
let viewSourceUtils = inspector.viewSourceUtils;
|
|
||||||
|
|
||||||
if (this.selectorInfo.sourceElement) {
|
|
||||||
href = this.selectorInfo.sourceElement.ownerDocument.location.href;
|
|
||||||
}
|
|
||||||
viewSourceUtils.viewSource(href, null, contentDoc, line);
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
let contentDoc = null;
|
||||||
|
let rawNode = this.tree.viewedElement.rawNode();
|
||||||
|
if (rawNode) {
|
||||||
|
contentDoc = rawNode.ownerDocument;
|
||||||
|
}
|
||||||
|
|
||||||
|
let viewSourceUtils = inspector.viewSourceUtils;
|
||||||
|
viewSourceUtils.viewSource(href, null, contentDoc, line);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.CssHtmlTree = CssHtmlTree;
|
exports.CssHtmlTree = CssHtmlTree;
|
||||||
|
|
|
@ -98,12 +98,12 @@
|
||||||
<a target="_blank" class="link theme-link"
|
<a target="_blank" class="link theme-link"
|
||||||
onclick="${selector.openStyleEditor}"
|
onclick="${selector.openStyleEditor}"
|
||||||
onkeydown="${selector.maybeOpenStyleEditor}"
|
onkeydown="${selector.maybeOpenStyleEditor}"
|
||||||
title="${selector.selectorInfo.href}"
|
title="${selector.href}"
|
||||||
tabindex="0">${selector.selectorInfo.source}</a>
|
tabindex="0">${selector.source}</a>
|
||||||
</span>
|
</span>
|
||||||
<span dir="ltr" class="rule-text ${selector.statusClass} theme-fg-color3" title="${selector.statusText}">
|
<span dir="ltr" class="rule-text ${selector.statusClass} theme-fg-color3" title="${selector.statusText}">
|
||||||
${selector.text(__element)}
|
${selector.sourceText}
|
||||||
<span class="other-property-value theme-fg-color1">${selector.selectorInfo.value}</span>
|
<span class="other-property-value theme-fg-color1">${selector.value}</span>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</loop>
|
</loop>
|
||||||
|
|
|
@ -7,9 +7,11 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const {Cc, Ci, Cu} = require("chrome");
|
const {Cc, Ci, Cu} = require("chrome");
|
||||||
|
const promise = require("sdk/core/promise");
|
||||||
|
|
||||||
let {CssLogic} = require("devtools/styleinspector/css-logic");
|
let {CssLogic} = require("devtools/styleinspector/css-logic");
|
||||||
let {InplaceEditor, editableField, editableItem} = require("devtools/shared/inplace-editor");
|
let {InplaceEditor, editableField, editableItem} = require("devtools/shared/inplace-editor");
|
||||||
|
let {ELEMENT_STYLE} = require("devtools/server/actors/styles");
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
@ -34,6 +36,46 @@ const CSS_RESOURCE_RE = /url\([\'\"]?(.*?)[\'\"]?\)/;
|
||||||
const IOService = Cc["@mozilla.org/network/io-service;1"]
|
const IOService = Cc["@mozilla.org/network/io-service;1"]
|
||||||
.getService(Ci.nsIIOService);
|
.getService(Ci.nsIIOService);
|
||||||
|
|
||||||
|
function promiseWarn(err) {
|
||||||
|
console.error(err);
|
||||||
|
return promise.reject(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To figure out how shorthand properties are interpreted by the
|
||||||
|
* engine, we will set properties on a dummy element and observe
|
||||||
|
* how their .style attribute reflects them as computed values.
|
||||||
|
* This function creates the document in which those dummy elements
|
||||||
|
* will be created.
|
||||||
|
*/
|
||||||
|
var gDummyPromise;
|
||||||
|
function createDummyDocument() {
|
||||||
|
if (gDummyPromise) {
|
||||||
|
return gDummyPromise;
|
||||||
|
}
|
||||||
|
const { getDocShell, create: makeFrame } = require("sdk/frame/utils");
|
||||||
|
|
||||||
|
let frame = makeFrame(Services.appShell.hiddenDOMWindow.document, {
|
||||||
|
nodeName: "iframe",
|
||||||
|
namespaceURI: "http://www.w3.org/1999/xhtml",
|
||||||
|
allowJavascript: false,
|
||||||
|
allowPlugins: false,
|
||||||
|
allowAuth: false
|
||||||
|
});
|
||||||
|
let docShell = getDocShell(frame);
|
||||||
|
let eventTarget = docShell.chromeEventHandler;
|
||||||
|
docShell.createAboutBlankContentViewer(Cc["@mozilla.org/nullprincipal;1"].createInstance(Ci.nsIPrincipal));
|
||||||
|
let window = docShell.contentViewer.DOMDocument.defaultView;
|
||||||
|
window.location = "data:text/html,<html></html>";
|
||||||
|
let deferred = promise.defer()
|
||||||
|
eventTarget.addEventListener("DOMContentLoaded", function handler(event) {
|
||||||
|
eventTarget.removeEventListener("DOMContentLoaded", handler, false);
|
||||||
|
deferred.resolve(window.document);
|
||||||
|
}, false);
|
||||||
|
gDummyPromise = deferred.promise;
|
||||||
|
return gDummyPromise;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Our model looks like this:
|
* Our model looks like this:
|
||||||
*
|
*
|
||||||
|
@ -62,13 +104,17 @@ const IOService = Cc["@mozilla.org/network/io-service;1"]
|
||||||
* The ElementStyle can use this object to store metadata
|
* The ElementStyle can use this object to store metadata
|
||||||
* that might outlast the rule view, particularly the current
|
* that might outlast the rule view, particularly the current
|
||||||
* set of disabled properties.
|
* set of disabled properties.
|
||||||
|
* @param {PageStyleFront} aPageStyle
|
||||||
|
* Front for the page style actor that will be providing
|
||||||
|
* the style information.
|
||||||
*
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function ElementStyle(aElement, aStore)
|
function ElementStyle(aElement, aStore, aPageStyle)
|
||||||
{
|
{
|
||||||
this.element = aElement;
|
this.element = aElement;
|
||||||
this.store = aStore || {};
|
this.store = aStore || {};
|
||||||
|
this.pageStyle = aPageStyle;
|
||||||
|
|
||||||
// We don't want to overwrite this.store.userProperties so we only create it
|
// We don't want to overwrite this.store.userProperties so we only create it
|
||||||
// if it doesn't already exist.
|
// if it doesn't already exist.
|
||||||
|
@ -85,9 +131,12 @@ function ElementStyle(aElement, aStore)
|
||||||
// To figure out how shorthand properties are interpreted by the
|
// To figure out how shorthand properties are interpreted by the
|
||||||
// engine, we will set properties on a dummy element and observe
|
// engine, we will set properties on a dummy element and observe
|
||||||
// how their .style attribute reflects them as computed values.
|
// how their .style attribute reflects them as computed values.
|
||||||
this.dummyElement = doc.createElementNS(this.element.namespaceURI,
|
this.dummyElementPromise = createDummyDocument().then(document => {
|
||||||
this.element.tagName);
|
this.dummyElement = document.createElementNS(this.element.namespaceURI,
|
||||||
this.populate();
|
this.element.tagName);
|
||||||
|
document.documentElement.appendChild(this.dummyElement);
|
||||||
|
return this.dummyElement;
|
||||||
|
}).then(null, promiseWarn);
|
||||||
}
|
}
|
||||||
// We're exporting _ElementStyle for unit tests.
|
// We're exporting _ElementStyle for unit tests.
|
||||||
exports._ElementStyle = ElementStyle;
|
exports._ElementStyle = ElementStyle;
|
||||||
|
@ -101,6 +150,17 @@ ElementStyle.prototype = {
|
||||||
// to figure out how shorthand properties will be parsed.
|
// to figure out how shorthand properties will be parsed.
|
||||||
dummyElement: null,
|
dummyElement: null,
|
||||||
|
|
||||||
|
destroy: function()
|
||||||
|
{
|
||||||
|
this.dummyElement = null;
|
||||||
|
this.dummyElementPromise.then(dummyElement => {
|
||||||
|
if (dummyElement.parentNode) {
|
||||||
|
dummyElement.parentNode.removeChild(dummyElement);
|
||||||
|
}
|
||||||
|
this.dummyElementPromise = null;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by the Rule object when it has been changed through the
|
* Called by the Rule object when it has been changed through the
|
||||||
* setProperty* methods.
|
* setProperty* methods.
|
||||||
|
@ -115,62 +175,44 @@ ElementStyle.prototype = {
|
||||||
/**
|
/**
|
||||||
* Refresh the list of rules to be displayed for the active element.
|
* Refresh the list of rules to be displayed for the active element.
|
||||||
* Upon completion, this.rules[] will hold a list of Rule objects.
|
* Upon completion, this.rules[] will hold a list of Rule objects.
|
||||||
|
*
|
||||||
|
* Returns a promise that will be resolved when the elementStyle is
|
||||||
|
* ready.
|
||||||
*/
|
*/
|
||||||
populate: function ElementStyle_populate()
|
populate: function ElementStyle_populate()
|
||||||
{
|
{
|
||||||
// Store the current list of rules (if any) during the population
|
let populated = this.pageStyle.getApplied(this.element, {
|
||||||
// process. They will be reused if possible.
|
inherited: true,
|
||||||
this._refreshRules = this.rules;
|
matchedSelectors: true
|
||||||
|
}).then(entries => {
|
||||||
|
// Make sure the dummy element has been created before continuing...
|
||||||
|
return this.dummyElementPromise.then(() => {
|
||||||
|
if (this.populated != populated) {
|
||||||
|
// Don't care anymore.
|
||||||
|
return promise.reject("unused");
|
||||||
|
}
|
||||||
|
|
||||||
this.rules = [];
|
// Store the current list of rules (if any) during the population
|
||||||
|
// process. They will be reused if possible.
|
||||||
|
this._refreshRules = this.rules;
|
||||||
|
|
||||||
let element = this.element;
|
this.rules = [];
|
||||||
do {
|
|
||||||
this._addElementRules(element);
|
|
||||||
} while ((element = element.parentNode) &&
|
|
||||||
element.nodeType === Ci.nsIDOMNode.ELEMENT_NODE);
|
|
||||||
|
|
||||||
// Mark overridden computed styles.
|
for (let entry of entries) {
|
||||||
this.markOverridden();
|
this._maybeAddRule(entry);
|
||||||
|
}
|
||||||
|
|
||||||
// We're done with the previous list of rules.
|
// Mark overridden computed styles.
|
||||||
delete this._refreshRules;
|
this.markOverridden();
|
||||||
},
|
|
||||||
|
|
||||||
_addElementRules: function ElementStyle_addElementRules(aElement)
|
// We're done with the previous list of rules.
|
||||||
{
|
delete this._refreshRules;
|
||||||
let inherited = aElement !== this.element ? aElement : null;
|
|
||||||
|
|
||||||
// Include the element's style first.
|
return null;
|
||||||
this._maybeAddRule({
|
|
||||||
style: aElement.style,
|
|
||||||
selectorText: CssLogic.l10n("rule.sourceElement"),
|
|
||||||
inherited: inherited
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get the styles that apply to the element.
|
|
||||||
var domRules = domUtils.getCSSStyleRules(aElement);
|
|
||||||
|
|
||||||
// getCSStyleRules returns ordered from least-specific to
|
|
||||||
// most-specific.
|
|
||||||
for (let i = domRules.Count() - 1; i >= 0; i--) {
|
|
||||||
let domRule = domRules.GetElementAt(i);
|
|
||||||
|
|
||||||
// XXX: Optionally provide access to system sheets.
|
|
||||||
let contentSheet = CssLogic.isContentStylesheet(domRule.parentStyleSheet);
|
|
||||||
if (!contentSheet) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (domRule.type !== Ci.nsIDOMCSSRule.STYLE_RULE) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._maybeAddRule({
|
|
||||||
domRule: domRule,
|
|
||||||
inherited: inherited
|
|
||||||
});
|
});
|
||||||
}
|
}).then(null, promiseWarn);
|
||||||
|
this.populated = populated;
|
||||||
|
return this.populated;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -186,8 +228,12 @@ ElementStyle.prototype = {
|
||||||
{
|
{
|
||||||
// If we've already included this domRule (for example, when a
|
// If we've already included this domRule (for example, when a
|
||||||
// common selector is inherited), ignore it.
|
// common selector is inherited), ignore it.
|
||||||
if (aOptions.domRule &&
|
if (aOptions.rule &&
|
||||||
this.rules.some(function(rule) rule.domRule === aOptions.domRule)) {
|
this.rules.some(function(rule) rule.domRule === aOptions.rule)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aOptions.system) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,11 +241,13 @@ ElementStyle.prototype = {
|
||||||
|
|
||||||
// If we're refreshing and the rule previously existed, reuse the
|
// If we're refreshing and the rule previously existed, reuse the
|
||||||
// Rule object.
|
// Rule object.
|
||||||
for (let r of (this._refreshRules || [])) {
|
if (this._refreshRules) {
|
||||||
if (r.matches(aOptions)) {
|
for (let r of this._refreshRules) {
|
||||||
rule = r;
|
if (r.matches(aOptions)) {
|
||||||
rule.refresh();
|
rule = r;
|
||||||
break;
|
rule.refresh(aOptions);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,6 +262,7 @@ ElementStyle.prototype = {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.rules.push(rule);
|
this.rules.push(rule);
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -324,11 +373,7 @@ ElementStyle.prototype = {
|
||||||
* The ElementStyle to which this rule belongs.
|
* The ElementStyle to which this rule belongs.
|
||||||
* @param {object} aOptions
|
* @param {object} aOptions
|
||||||
* The information used to construct this rule. Properties include:
|
* The information used to construct this rule. Properties include:
|
||||||
* domRule: the nsIDOMCSSStyleRule to view, if any.
|
* rule: A StyleRuleActor
|
||||||
* style: the nsIDOMCSSStyleDeclaration to view. If omitted,
|
|
||||||
* the domRule's style will be used.
|
|
||||||
* selectorText: selector text to display. If omitted, the domRule's
|
|
||||||
* selectorText will be used.
|
|
||||||
* inherited: An element this rule was inherited from. If omitted,
|
* inherited: An element this rule was inherited from. If omitted,
|
||||||
* the rule applies directly to the current element.
|
* the rule applies directly to the current element.
|
||||||
* @constructor
|
* @constructor
|
||||||
|
@ -336,15 +381,17 @@ ElementStyle.prototype = {
|
||||||
function Rule(aElementStyle, aOptions)
|
function Rule(aElementStyle, aOptions)
|
||||||
{
|
{
|
||||||
this.elementStyle = aElementStyle;
|
this.elementStyle = aElementStyle;
|
||||||
this.domRule = aOptions.domRule || null;
|
this.domRule = aOptions.rule || null;
|
||||||
this.style = aOptions.style || this.domRule.style;
|
this.style = aOptions.rule;
|
||||||
this.selectorText = aOptions.selectorText || this.domRule.selectorText;
|
this.matchedSelectors = aOptions.matchedSelectors || [];
|
||||||
|
|
||||||
this.inherited = aOptions.inherited || null;
|
this.inherited = aOptions.inherited || null;
|
||||||
|
this._modificationDepth = 0;
|
||||||
|
|
||||||
if (this.domRule) {
|
if (this.domRule) {
|
||||||
let parentRule = this.domRule.parentRule;
|
let parentRule = this.domRule.parentRule;
|
||||||
if (parentRule && parentRule.type == Ci.nsIDOMCSSRule.MEDIA_RULE) {
|
if (parentRule && parentRule.type == Ci.nsIDOMCSSRule.MEDIA_RULE) {
|
||||||
this.mediaText = parentRule.media.mediaText;
|
this.mediaText = parentRule.mediaText;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,11 +410,12 @@ Rule.prototype = {
|
||||||
return this._title;
|
return this._title;
|
||||||
}
|
}
|
||||||
this._title = CssLogic.shortSource(this.sheet);
|
this._title = CssLogic.shortSource(this.sheet);
|
||||||
if (this.domRule) {
|
if (this.domRule.type !== ELEMENT_STYLE) {
|
||||||
this._title += ":" + this.ruleLine;
|
this._title += ":" + this.ruleLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._title + (this.mediaText ? " @media " + this.mediaText : "");
|
this._title = this._title + (this.mediaText ? " @media " + this.mediaText : "");
|
||||||
|
return this._title;
|
||||||
},
|
},
|
||||||
|
|
||||||
get inheritedSource()
|
get inheritedSource()
|
||||||
|
@ -387,6 +435,11 @@ Rule.prototype = {
|
||||||
return this._inheritedSource;
|
return this._inheritedSource;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get selectorText()
|
||||||
|
{
|
||||||
|
return this.domRule.selectors ? this.domRule.selectors.join(", ") : CssLogic.l10n("rule.sourceElement");
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The rule's stylesheet.
|
* The rule's stylesheet.
|
||||||
*/
|
*/
|
||||||
|
@ -400,11 +453,7 @@ Rule.prototype = {
|
||||||
*/
|
*/
|
||||||
get ruleLine()
|
get ruleLine()
|
||||||
{
|
{
|
||||||
if (!this.sheet) {
|
return this.domRule ? this.domRule.line : null;
|
||||||
// No stylesheet, no ruleLine
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return domUtils.getRuleLine(this.domRule);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -416,7 +465,7 @@ Rule.prototype = {
|
||||||
*/
|
*/
|
||||||
matches: function Rule_matches(aOptions)
|
matches: function Rule_matches(aOptions)
|
||||||
{
|
{
|
||||||
return (this.style === (aOptions.style || aOptions.domRule.style));
|
return this.style === aOptions.rule;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -447,12 +496,15 @@ Rule.prototype = {
|
||||||
* when calling from setPropertyValue & setPropertyName to signify
|
* when calling from setPropertyValue & setPropertyName to signify
|
||||||
* that the property should be saved in store.userProperties.
|
* that the property should be saved in store.userProperties.
|
||||||
*/
|
*/
|
||||||
applyProperties: function Rule_applyProperties(aName)
|
applyProperties: function Rule_applyProperties(aModifications, aName)
|
||||||
{
|
{
|
||||||
|
if (!aModifications) {
|
||||||
|
aModifications = this.style.startModifyingProperties();
|
||||||
|
}
|
||||||
let disabledProps = [];
|
let disabledProps = [];
|
||||||
let store = this.elementStyle.store;
|
let store = this.elementStyle.store;
|
||||||
|
|
||||||
for each (let prop in this.textProps) {
|
for (let prop of this.textProps) {
|
||||||
if (!prop.enabled) {
|
if (!prop.enabled) {
|
||||||
disabledProps.push({
|
disabledProps.push({
|
||||||
name: prop.name,
|
name: prop.name,
|
||||||
|
@ -462,21 +514,11 @@ Rule.prototype = {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.style.setProperty(prop.name, prop.value, prop.priority);
|
aModifications.setProperty(prop.name, prop.value, prop.priority);
|
||||||
|
|
||||||
if (aName && prop.name == aName) {
|
|
||||||
store.userProperties.setProperty(
|
|
||||||
this.style, prop.name,
|
|
||||||
this.style.getPropertyValue(prop.name),
|
|
||||||
prop.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh the property's priority from the style, to reflect
|
|
||||||
// any changes made during parsing.
|
|
||||||
prop.priority = this.style.getPropertyPriority(prop.name);
|
|
||||||
prop.updateComputed();
|
prop.updateComputed();
|
||||||
}
|
}
|
||||||
this.elementStyle._changed();
|
|
||||||
|
|
||||||
// Store disabled properties in the disabled store.
|
// Store disabled properties in the disabled store.
|
||||||
let disabled = this.elementStyle.store.disabled;
|
let disabled = this.elementStyle.store.disabled;
|
||||||
|
@ -486,7 +528,46 @@ Rule.prototype = {
|
||||||
disabled.delete(this.style);
|
disabled.delete(this.style);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.elementStyle.markOverridden();
|
let promise = aModifications.apply().then(() => {
|
||||||
|
let cssProps = {};
|
||||||
|
for (let cssProp of this._parseCSSText(this.style.cssText)) {
|
||||||
|
cssProps[cssProp.name] = cssProp;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let textProp of this.textProps) {
|
||||||
|
if (!textProp.enabled) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let cssProp = cssProps[textProp.name];
|
||||||
|
|
||||||
|
if (!cssProp) {
|
||||||
|
cssProp = {
|
||||||
|
name: textProp.name,
|
||||||
|
value: "",
|
||||||
|
priority: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aName && textProp.name == aName) {
|
||||||
|
store.userProperties.setProperty(
|
||||||
|
this.style, textProp.name,
|
||||||
|
null,
|
||||||
|
cssProp.value,
|
||||||
|
textProp.value);
|
||||||
|
}
|
||||||
|
textProp.priority = cssProp.priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.elementStyle.markOverridden();
|
||||||
|
|
||||||
|
if (promise === this._applyingModifications) {
|
||||||
|
this._applyingModifications = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.elementStyle._changed();
|
||||||
|
}).then(null, promiseWarn);
|
||||||
|
this._applyingModifications = promise;
|
||||||
|
return promise;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -502,9 +583,10 @@ Rule.prototype = {
|
||||||
if (aName === aProperty.name) {
|
if (aName === aProperty.name) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.style.removeProperty(aProperty.name);
|
let modifications = this.style.startModifyingProperties();
|
||||||
|
modifications.removeProperty(aProperty.name);
|
||||||
aProperty.name = aName;
|
aProperty.name = aName;
|
||||||
this.applyProperties(aName);
|
this.applyProperties(modifications, aName);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -524,7 +606,7 @@ Rule.prototype = {
|
||||||
}
|
}
|
||||||
aProperty.value = aValue;
|
aProperty.value = aValue;
|
||||||
aProperty.priority = aPriority;
|
aProperty.priority = aPriority;
|
||||||
this.applyProperties(aProperty.name);
|
this.applyProperties(null, aProperty.name);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -533,10 +615,11 @@ Rule.prototype = {
|
||||||
setPropertyEnabled: function Rule_enableProperty(aProperty, aValue)
|
setPropertyEnabled: function Rule_enableProperty(aProperty, aValue)
|
||||||
{
|
{
|
||||||
aProperty.enabled = !!aValue;
|
aProperty.enabled = !!aValue;
|
||||||
|
let modifications = this.style.startModifyingProperties();
|
||||||
if (!aProperty.enabled) {
|
if (!aProperty.enabled) {
|
||||||
this.style.removeProperty(aProperty.name);
|
modifications.removeProperty(aProperty.name);
|
||||||
}
|
}
|
||||||
this.applyProperties();
|
this.applyProperties(modifications);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -546,10 +629,32 @@ Rule.prototype = {
|
||||||
removeProperty: function Rule_removeProperty(aProperty)
|
removeProperty: function Rule_removeProperty(aProperty)
|
||||||
{
|
{
|
||||||
this.textProps = this.textProps.filter(function(prop) prop != aProperty);
|
this.textProps = this.textProps.filter(function(prop) prop != aProperty);
|
||||||
this.style.removeProperty(aProperty);
|
let modifications = this.style.startModifyingProperties();
|
||||||
|
modifications.removeProperty(aProperty.name);
|
||||||
// Need to re-apply properties in case removing this TextProperty
|
// Need to re-apply properties in case removing this TextProperty
|
||||||
// exposes another one.
|
// exposes another one.
|
||||||
this.applyProperties();
|
this.applyProperties(modifications);
|
||||||
|
},
|
||||||
|
|
||||||
|
_parseCSSText: function Rule_parseProperties(aCssText)
|
||||||
|
{
|
||||||
|
let lines = aCssText.match(CSS_LINE_RE);
|
||||||
|
let props = [];
|
||||||
|
|
||||||
|
for (let line of lines) {
|
||||||
|
dump("line: " + line + "\n");
|
||||||
|
let [, name, value, priority] = CSS_PROP_RE.exec(line) || []
|
||||||
|
if (!name || !value) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
props.push({
|
||||||
|
name: name,
|
||||||
|
value: value,
|
||||||
|
priority: priority || ""
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return props;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -560,19 +665,15 @@ Rule.prototype = {
|
||||||
{
|
{
|
||||||
let textProps = [];
|
let textProps = [];
|
||||||
let store = this.elementStyle.store;
|
let store = this.elementStyle.store;
|
||||||
let lines = this.style.cssText.match(CSS_LINE_RE);
|
let props = this._parseCSSText(this.style.cssText);
|
||||||
for each (let line in lines) {
|
for (let prop of props) {
|
||||||
let matches = CSS_PROP_RE.exec(line);
|
let name = prop.name;
|
||||||
if (!matches || !matches[2])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
let name = matches[1];
|
|
||||||
if (this.inherited && !domUtils.isInheritedProperty(name)) {
|
if (this.inherited && !domUtils.isInheritedProperty(name)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let value = store.userProperties.getProperty(this.style, name, matches[2]);
|
let value = store.userProperties.getProperty(this.style, name, prop.value);
|
||||||
let prop = new TextProperty(this, name, value, matches[3] || "");
|
let textProp = new TextProperty(this, name, value, prop.priority);
|
||||||
textProps.push(prop);
|
textProps.push(textProp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return textProps;
|
return textProps;
|
||||||
|
@ -607,8 +708,9 @@ Rule.prototype = {
|
||||||
* Reread the current state of the rules and rebuild text
|
* Reread the current state of the rules and rebuild text
|
||||||
* properties as needed.
|
* properties as needed.
|
||||||
*/
|
*/
|
||||||
refresh: function Rule_refresh()
|
refresh: function Rule_refresh(aOptions)
|
||||||
{
|
{
|
||||||
|
this.matchedSelectors = aOptions.matchedSelectors || [];
|
||||||
let newTextProps = this._getTextProperties();
|
let newTextProps = this._getTextProperties();
|
||||||
|
|
||||||
// Update current properties for each property present on the style.
|
// Update current properties for each property present on the style.
|
||||||
|
@ -861,14 +963,15 @@ TextProperty.prototype = {
|
||||||
* The CSS rule view can use this object to store metadata
|
* The CSS rule view can use this object to store metadata
|
||||||
* that might outlast the rule view, particularly the current
|
* that might outlast the rule view, particularly the current
|
||||||
* set of disabled properties.
|
* set of disabled properties.
|
||||||
* @param {<iframe>} aOuterIFrame
|
* @param {PageStyleFront} aPageStyle
|
||||||
* The iframe containing the ruleview.
|
* The PageStyleFront for communicating with the remote server.
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function CssRuleView(aDoc, aStore)
|
function CssRuleView(aDoc, aStore, aPageStyle)
|
||||||
{
|
{
|
||||||
this.doc = aDoc;
|
this.doc = aDoc;
|
||||||
this.store = aStore;
|
this.store = aStore;
|
||||||
|
this.pageStyle = aPageStyle;
|
||||||
this.element = this.doc.createElementNS(HTML_NS, "div");
|
this.element = this.doc.createElementNS(HTML_NS, "div");
|
||||||
this.element.className = "ruleview devtools-monospace";
|
this.element.className = "ruleview devtools-monospace";
|
||||||
this.element.flex = 1;
|
this.element.flex = 1;
|
||||||
|
@ -892,6 +995,10 @@ CssRuleView.prototype = {
|
||||||
// The element that we're inspecting.
|
// The element that we're inspecting.
|
||||||
_viewedElement: null,
|
_viewedElement: null,
|
||||||
|
|
||||||
|
setPageStyle: function(aPageStyle) {
|
||||||
|
this.pageStyle = aPageStyle;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return {bool} true if the rule view currently has an input editor visible.
|
* Return {bool} true if the rule view currently has an input editor visible.
|
||||||
*/
|
*/
|
||||||
|
@ -910,6 +1017,8 @@ CssRuleView.prototype = {
|
||||||
this.element.parentNode.removeChild(this.element);
|
this.element.parentNode.removeChild(this.element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.elementStyle.destroy();
|
||||||
|
|
||||||
this.popup.destroy();
|
this.popup.destroy();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -922,7 +1031,7 @@ CssRuleView.prototype = {
|
||||||
highlight: function CssRuleView_highlight(aElement)
|
highlight: function CssRuleView_highlight(aElement)
|
||||||
{
|
{
|
||||||
if (this._viewedElement === aElement) {
|
if (this._viewedElement === aElement) {
|
||||||
return;
|
return promise.resolve(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clear();
|
this.clear();
|
||||||
|
@ -934,15 +1043,15 @@ CssRuleView.prototype = {
|
||||||
this._viewedElement = aElement;
|
this._viewedElement = aElement;
|
||||||
if (!this._viewedElement) {
|
if (!this._viewedElement) {
|
||||||
this._showEmpty();
|
this._showEmpty();
|
||||||
return;
|
return promise.resolve(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._elementStyle = new ElementStyle(aElement, this.store);
|
this._elementStyle = new ElementStyle(aElement, this.store, this.pageStyle);
|
||||||
this._elementStyle.onChanged = function() {
|
return this._populate().then(() => {
|
||||||
this._changed();
|
this._elementStyle.onChanged = () => {
|
||||||
}.bind(this);
|
this._changed();
|
||||||
|
};
|
||||||
this._createEditors();
|
}).then(null, console.error);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -952,21 +1061,29 @@ CssRuleView.prototype = {
|
||||||
{
|
{
|
||||||
// Ignore refreshes during editing or when no element is selected.
|
// Ignore refreshes during editing or when no element is selected.
|
||||||
if (this.isEditing || !this._elementStyle) {
|
if (this.isEditing || !this._elementStyle) {
|
||||||
return;
|
return promise.resolve(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._clearRules();
|
this._clearRules();
|
||||||
|
|
||||||
// Repopulate the element style.
|
// Repopulate the element style.
|
||||||
this._elementStyle.populate();
|
return this._populate();
|
||||||
|
},
|
||||||
|
|
||||||
// Refresh the rule editors.
|
_populate: function() {
|
||||||
this._createEditors();
|
let elementStyle = this._elementStyle;
|
||||||
|
return this._elementStyle.populate().then(() => {
|
||||||
|
if (this._elementStyle != elementStyle) {
|
||||||
|
return promise.reject("element changed");
|
||||||
|
}
|
||||||
|
this._createEditors();
|
||||||
|
|
||||||
// Notify anyone that cares that we refreshed.
|
// Notify anyone that cares that we refreshed.
|
||||||
var evt = this.doc.createEvent("Events");
|
var evt = this.doc.createEvent("Events");
|
||||||
evt.initEvent("CssRuleViewRefreshed", true, false);
|
evt.initEvent("CssRuleViewRefreshed", true, false);
|
||||||
this.element.dispatchEvent(evt);
|
this.element.dispatchEvent(evt);
|
||||||
|
return undefined;
|
||||||
|
}).then(null, promiseWarn);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1023,7 +1140,10 @@ CssRuleView.prototype = {
|
||||||
// Run through the current list of rules, attaching
|
// Run through the current list of rules, attaching
|
||||||
// their editors in order. Create editors if needed.
|
// their editors in order. Create editors if needed.
|
||||||
let lastInheritedSource = "";
|
let lastInheritedSource = "";
|
||||||
for each (let rule in this._elementStyle.rules) {
|
for (let rule of this._elementStyle.rules) {
|
||||||
|
if (rule.domRule.system) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let inheritedSource = rule.inheritedSource;
|
let inheritedSource = rule.inheritedSource;
|
||||||
if (inheritedSource != lastInheritedSource) {
|
if (inheritedSource != lastInheritedSource) {
|
||||||
|
@ -1117,7 +1237,7 @@ RuleEditor.prototype = {
|
||||||
class: "ruleview-rule-source theme-link"
|
class: "ruleview-rule-source theme-link"
|
||||||
});
|
});
|
||||||
source.addEventListener("click", function() {
|
source.addEventListener("click", function() {
|
||||||
let rule = this.rule;
|
let rule = this.rule.domRule;
|
||||||
let evt = this.doc.createEvent("CustomEvent");
|
let evt = this.doc.createEvent("CustomEvent");
|
||||||
evt.initCustomEvent("CssRuleViewCSSLinkClicked", true, false, {
|
evt.initCustomEvent("CssRuleViewCSSLinkClicked", true, false, {
|
||||||
rule: rule,
|
rule: rule,
|
||||||
|
@ -1198,11 +1318,10 @@ RuleEditor.prototype = {
|
||||||
// If selector text comes from a css rule, highlight selectors that
|
// If selector text comes from a css rule, highlight selectors that
|
||||||
// actually match. For custom selector text (such as for the 'element'
|
// actually match. For custom selector text (such as for the 'element'
|
||||||
// style, just show the text directly.
|
// style, just show the text directly.
|
||||||
if (this.rule.domRule && this.rule.domRule.selectorText) {
|
if (this.rule.domRule.type === ELEMENT_STYLE) {
|
||||||
let selectors = CssLogic.getSelectors(this.rule.domRule);
|
this.selectorText.textContent = this.rule.selectorText;
|
||||||
let element = this.rule.inherited || this.ruleView._viewedElement;
|
} else {
|
||||||
for (let i = 0; i < selectors.length; i++) {
|
this.rule.domRule.selectors.forEach((selector, i) => {
|
||||||
let selector = selectors[i];
|
|
||||||
if (i != 0) {
|
if (i != 0) {
|
||||||
createChild(this.selectorText, "span", {
|
createChild(this.selectorText, "span", {
|
||||||
class: "ruleview-selector-separator",
|
class: "ruleview-selector-separator",
|
||||||
|
@ -1210,7 +1329,7 @@ RuleEditor.prototype = {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let cls;
|
let cls;
|
||||||
if (domUtils.selectorMatchesElement(element, this.rule.domRule, i)) {
|
if (this.rule.matchedSelectors.indexOf(selector) > -1) {
|
||||||
cls = "ruleview-selector-matched";
|
cls = "ruleview-selector-matched";
|
||||||
} else {
|
} else {
|
||||||
cls = "ruleview-selector-unmatched";
|
cls = "ruleview-selector-unmatched";
|
||||||
|
@ -1219,9 +1338,7 @@ RuleEditor.prototype = {
|
||||||
class: cls,
|
class: cls,
|
||||||
textContent: selector
|
textContent: selector
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
} else {
|
|
||||||
this.selectorText.textContent = this.rule.selectorText;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let prop of this.rule.textProps) {
|
for (let prop of this.rule.textProps) {
|
||||||
|
@ -1339,7 +1456,7 @@ function TextPropertyEditor(aRuleEditor, aProperty)
|
||||||
this.browserWindow = this.doc.defaultView.top;
|
this.browserWindow = this.doc.defaultView.top;
|
||||||
|
|
||||||
let sheet = this.prop.rule.sheet;
|
let sheet = this.prop.rule.sheet;
|
||||||
let href = sheet ? CssLogic.href(sheet) : null;
|
let href = sheet ? (sheet.href || sheet.nodeHref) : null;
|
||||||
if (href) {
|
if (href) {
|
||||||
this.sheetURI = IOService.newURI(href, null, null);
|
this.sheetURI = IOService.newURI(href, null, null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ loader.lazyGetter(this, "RuleView", () => require("devtools/styleinspector/rule-
|
||||||
loader.lazyGetter(this, "ComputedView", () => require("devtools/styleinspector/computed-view"));
|
loader.lazyGetter(this, "ComputedView", () => require("devtools/styleinspector/computed-view"));
|
||||||
loader.lazyGetter(this, "_strings", () => Services.strings
|
loader.lazyGetter(this, "_strings", () => Services.strings
|
||||||
.createBundle("chrome://browser/locale/devtools/styleinspector.properties"));
|
.createBundle("chrome://browser/locale/devtools/styleinspector.properties"));
|
||||||
loader.lazyGetter(this, "CssLogic", () => require("devtools/styleinspector/css-logic").CssLogic);
|
|
||||||
|
|
||||||
// This module doesn't currently export any symbols directly, it only
|
// This module doesn't currently export any symbols directly, it only
|
||||||
// registers inspector tools.
|
// registers inspector tools.
|
||||||
|
@ -29,19 +28,21 @@ function RuleViewTool(aInspector, aWindow, aIFrame)
|
||||||
this.view = new RuleView.CssRuleView(this.doc, null);
|
this.view = new RuleView.CssRuleView(this.doc, null);
|
||||||
this.doc.documentElement.appendChild(this.view.element);
|
this.doc.documentElement.appendChild(this.view.element);
|
||||||
|
|
||||||
this._changeHandler = function() {
|
this._changeHandler = () => {
|
||||||
this.inspector.markDirty();
|
this.inspector.markDirty();
|
||||||
}.bind(this);
|
};
|
||||||
|
|
||||||
this.view.element.addEventListener("CssRuleViewChanged", this._changeHandler)
|
this.view.element.addEventListener("CssRuleViewChanged", this._changeHandler);
|
||||||
|
|
||||||
this._cssLinkHandler = function(aEvent) {
|
this._refreshHandler = () => {
|
||||||
let contentDoc = this.inspector.selection.document;
|
this.inspector.emit("rule-view-refreshed");
|
||||||
|
};
|
||||||
|
|
||||||
|
this.view.element.addEventListener("CssRuleViewRefreshed", this._refreshHandler);
|
||||||
|
|
||||||
|
this._cssLinkHandler = (aEvent) => {
|
||||||
let rule = aEvent.detail.rule;
|
let rule = aEvent.detail.rule;
|
||||||
let line = rule.ruleLine || 0;
|
let line = rule.line || 0;
|
||||||
let styleSheet = rule.sheet;
|
|
||||||
let styleSheets = contentDoc.styleSheets;
|
|
||||||
let contentSheet = false;
|
|
||||||
|
|
||||||
// The style editor can only display stylesheets coming from content because
|
// The style editor can only display stylesheets coming from content because
|
||||||
// chrome stylesheets are not listed in the editor's stylesheet selector.
|
// chrome stylesheets are not listed in the editor's stylesheet selector.
|
||||||
|
@ -49,42 +50,34 @@ function RuleViewTool(aInspector, aWindow, aIFrame)
|
||||||
// If the stylesheet is a content stylesheet we send it to the style
|
// If the stylesheet is a content stylesheet we send it to the style
|
||||||
// editor else we display it in the view source window.
|
// editor else we display it in the view source window.
|
||||||
//
|
//
|
||||||
// Array.prototype.indexOf always returns -1 here so we loop through
|
let href = rule.href;
|
||||||
// the styleSheets object instead.
|
let sheet = rule.parentStyleSheet;
|
||||||
for each (let sheet in styleSheets) {
|
if (sheet && href && !sheet.isSystem) {
|
||||||
if (sheet == styleSheet) {
|
|
||||||
contentSheet = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contentSheet) {
|
|
||||||
let target = this.inspector.target;
|
let target = this.inspector.target;
|
||||||
|
|
||||||
if (ToolDefinitions.styleEditor.isTargetSupported(target)) {
|
if (ToolDefinitions.styleEditor.isTargetSupported(target)) {
|
||||||
gDevTools.showToolbox(target, "styleeditor").then(function(toolbox) {
|
gDevTools.showToolbox(target, "styleeditor").then(function(toolbox) {
|
||||||
toolbox.getCurrentPanel().selectStyleSheet(styleSheet.href, line);
|
toolbox.getCurrentPanel().selectStyleSheet(href, line);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
return;
|
||||||
let href = styleSheet ? styleSheet.href : "";
|
|
||||||
if (rule.elementStyle.element) {
|
|
||||||
href = rule.elementStyle.element.ownerDocument.location.href;
|
|
||||||
}
|
|
||||||
let viewSourceUtils = this.inspector.viewSourceUtils;
|
|
||||||
viewSourceUtils.viewSource(href, null, contentDoc, line);
|
|
||||||
}
|
}
|
||||||
}.bind(this);
|
|
||||||
|
let contentDoc = this.inspector.selection.document;
|
||||||
|
let viewSourceUtils = this.inspector.viewSourceUtils;
|
||||||
|
viewSourceUtils.viewSource(href, null, contentDoc, line);
|
||||||
|
}
|
||||||
|
|
||||||
this.view.element.addEventListener("CssRuleViewCSSLinkClicked",
|
this.view.element.addEventListener("CssRuleViewCSSLinkClicked",
|
||||||
this._cssLinkHandler);
|
this._cssLinkHandler);
|
||||||
|
|
||||||
this._onSelect = this.onSelect.bind(this);
|
this._onSelect = this.onSelect.bind(this);
|
||||||
this.inspector.selection.on("detached", this._onSelect);
|
this.inspector.selection.on("detached", this._onSelect);
|
||||||
this.inspector.selection.on("new-node", this._onSelect);
|
this.inspector.selection.on("new-node-front", this._onSelect);
|
||||||
this.refresh = this.refresh.bind(this);
|
this.refresh = this.refresh.bind(this);
|
||||||
this.inspector.on("layout-change", this.refresh);
|
this.inspector.on("layout-change", this.refresh);
|
||||||
this.inspector.sidebar.on("ruleview-selected", this.refresh);
|
|
||||||
|
this.panelSelected = this.panelSelected.bind(this);
|
||||||
|
this.inspector.sidebar.on("ruleview-selected", this.panelSelected);
|
||||||
this.inspector.selection.on("pseudoclass", this.refresh);
|
this.inspector.selection.on("pseudoclass", this.refresh);
|
||||||
if (this.inspector.highlighter) {
|
if (this.inspector.highlighter) {
|
||||||
this.inspector.highlighter.on("locked", this._onSelect);
|
this.inspector.highlighter.on("locked", this._onSelect);
|
||||||
|
@ -97,22 +90,30 @@ exports.RuleViewTool = RuleViewTool;
|
||||||
|
|
||||||
RuleViewTool.prototype = {
|
RuleViewTool.prototype = {
|
||||||
onSelect: function RVT_onSelect(aEvent) {
|
onSelect: function RVT_onSelect(aEvent) {
|
||||||
|
if (!this.isActive()) {
|
||||||
|
// We'll update when the panel is selected.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.view.setPageStyle(this.inspector.pageStyle);
|
||||||
|
|
||||||
if (!this.inspector.selection.isConnected() ||
|
if (!this.inspector.selection.isConnected() ||
|
||||||
!this.inspector.selection.isElementNode()) {
|
!this.inspector.selection.isElementNode()) {
|
||||||
this.view.highlight(null);
|
this.view.highlight(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!aEvent || aEvent == "new-node") {
|
if (!aEvent || aEvent == "new-node-front") {
|
||||||
if (this.inspector.selection.reason == "highlighter") {
|
if (this.inspector.selection.reason == "highlighter") {
|
||||||
this.view.highlight(null);
|
this.view.highlight(null);
|
||||||
} else {
|
} else {
|
||||||
this.view.highlight(this.inspector.selection.node);
|
let done = this.inspector.updating("rule-view");
|
||||||
|
this.view.highlight(this.inspector.selection.nodeFront).then(done, done);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aEvent == "locked") {
|
if (aEvent == "locked") {
|
||||||
this.view.highlight(this.inspector.selection.node);
|
let done = this.inspector.updating("rule-view");
|
||||||
|
this.view.highlight(this.inspector.selection.nodeFront).then(done, done);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -126,11 +127,19 @@ RuleViewTool.prototype = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
panelSelected: function() {
|
||||||
|
if (this.inspector.selection.nodeFront === this.view.viewedElement) {
|
||||||
|
this.view.nodeChanged();
|
||||||
|
} else {
|
||||||
|
this.onSelect();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
destroy: function RVT_destroy() {
|
destroy: function RVT_destroy() {
|
||||||
this.inspector.off("layout-change", this.refresh);
|
this.inspector.off("layout-change", this.refresh);
|
||||||
this.inspector.sidebar.off("ruleview-selected", this.refresh);
|
this.inspector.sidebar.off("ruleview-selected", this.refresh);
|
||||||
this.inspector.selection.off("pseudoclass", this.refresh);
|
this.inspector.selection.off("pseudoclass", this.refresh);
|
||||||
this.inspector.selection.off("new-node", this._onSelect);
|
this.inspector.selection.off("new-node-front", this._onSelect);
|
||||||
if (this.inspector.highlighter) {
|
if (this.inspector.highlighter) {
|
||||||
this.inspector.highlighter.off("locked", this._onSelect);
|
this.inspector.highlighter.off("locked", this._onSelect);
|
||||||
}
|
}
|
||||||
|
@ -141,6 +150,9 @@ RuleViewTool.prototype = {
|
||||||
this.view.element.removeEventListener("CssRuleViewChanged",
|
this.view.element.removeEventListener("CssRuleViewChanged",
|
||||||
this._changeHandler);
|
this._changeHandler);
|
||||||
|
|
||||||
|
this.view.element.removeEventListener("CssRuleViewRefreshed",
|
||||||
|
this._refreshHandler);
|
||||||
|
|
||||||
this.doc.documentElement.removeChild(this.view.element);
|
this.doc.documentElement.removeChild(this.view.element);
|
||||||
|
|
||||||
this.view.destroy();
|
this.view.destroy();
|
||||||
|
@ -158,21 +170,20 @@ function ComputedViewTool(aInspector, aWindow, aIFrame)
|
||||||
this.window = aWindow;
|
this.window = aWindow;
|
||||||
this.document = aWindow.document;
|
this.document = aWindow.document;
|
||||||
this.outerIFrame = aIFrame;
|
this.outerIFrame = aIFrame;
|
||||||
this.cssLogic = new CssLogic();
|
this.view = new ComputedView.CssHtmlTree(this, aInspector.pageStyle);
|
||||||
this.view = new ComputedView.CssHtmlTree(this);
|
|
||||||
|
|
||||||
this._onSelect = this.onSelect.bind(this);
|
this._onSelect = this.onSelect.bind(this);
|
||||||
this.inspector.selection.on("detached", this._onSelect);
|
this.inspector.selection.on("detached", this._onSelect);
|
||||||
this.inspector.selection.on("new-node", this._onSelect);
|
this.inspector.selection.on("new-node-front", this._onSelect);
|
||||||
if (this.inspector.highlighter) {
|
if (this.inspector.highlighter) {
|
||||||
this.inspector.highlighter.on("locked", this._onSelect);
|
this.inspector.highlighter.on("locked", this._onSelect);
|
||||||
}
|
}
|
||||||
this.refresh = this.refresh.bind(this);
|
this.refresh = this.refresh.bind(this);
|
||||||
this.inspector.on("layout-change", this.refresh);
|
this.inspector.on("layout-change", this.refresh);
|
||||||
this.inspector.sidebar.on("computedview-selected", this.refresh);
|
|
||||||
this.inspector.selection.on("pseudoclass", this.refresh);
|
this.inspector.selection.on("pseudoclass", this.refresh);
|
||||||
|
this.panelSelected = this.panelSelected.bind(this);
|
||||||
|
this.inspector.sidebar.on("computedview-selected", this.panelSelected);
|
||||||
|
|
||||||
this.cssLogic.highlight(null);
|
|
||||||
this.view.highlight(null);
|
this.view.highlight(null);
|
||||||
|
|
||||||
this.onSelect();
|
this.onSelect();
|
||||||
|
@ -183,24 +194,35 @@ exports.ComputedViewTool = ComputedViewTool;
|
||||||
ComputedViewTool.prototype = {
|
ComputedViewTool.prototype = {
|
||||||
onSelect: function CVT_onSelect(aEvent)
|
onSelect: function CVT_onSelect(aEvent)
|
||||||
{
|
{
|
||||||
|
if (!this.isActive()) {
|
||||||
|
// We'll try again when we're selected.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.view.setPageStyle(this.inspector.pageStyle);
|
||||||
|
|
||||||
if (!this.inspector.selection.isConnected() ||
|
if (!this.inspector.selection.isConnected() ||
|
||||||
!this.inspector.selection.isElementNode()) {
|
!this.inspector.selection.isElementNode()) {
|
||||||
this.view.highlight(null);
|
this.view.highlight(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!aEvent || aEvent == "new-node") {
|
if (!aEvent || aEvent == "new-node-front") {
|
||||||
if (this.inspector.selection.reason == "highlighter") {
|
if (this.inspector.selection.reason == "highlighter") {
|
||||||
// FIXME: We should hide view's content
|
// FIXME: We should hide view's content
|
||||||
} else {
|
} else {
|
||||||
this.cssLogic.highlight(this.inspector.selection.node);
|
let done = this.inspector.updating("computed-view");
|
||||||
this.view.highlight(this.inspector.selection.node);
|
this.view.highlight(this.inspector.selection.nodeFront).then(() => {
|
||||||
|
done();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aEvent == "locked") {
|
if (aEvent == "locked" && this.inspector.selection.nodeFront != this.view.viewedElement) {
|
||||||
this.cssLogic.highlight(this.inspector.selection.node);
|
let done = this.inspector.updating("computed-view");
|
||||||
this.view.highlight(this.inspector.selection.node);
|
this.view.highlight(this.inspector.selection.nodeFront).then(() => {
|
||||||
|
done();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -210,17 +232,24 @@ ComputedViewTool.prototype = {
|
||||||
|
|
||||||
refresh: function CVT_refresh() {
|
refresh: function CVT_refresh() {
|
||||||
if (this.isActive()) {
|
if (this.isActive()) {
|
||||||
this.cssLogic.highlight(this.inspector.selection.node);
|
|
||||||
this.view.refreshPanel();
|
this.view.refreshPanel();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
panelSelected: function() {
|
||||||
|
if (this.inspector.selection.nodeFront === this.view.viewedElement) {
|
||||||
|
this.view.refreshPanel();
|
||||||
|
} else {
|
||||||
|
this.onSelect();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
destroy: function CVT_destroy(aContext)
|
destroy: function CVT_destroy(aContext)
|
||||||
{
|
{
|
||||||
this.inspector.off("layout-change", this.refresh);
|
this.inspector.off("layout-change", this.refresh);
|
||||||
this.inspector.sidebar.off("computedview-selected", this.refresh);
|
this.inspector.sidebar.off("computedview-selected", this.refresh);
|
||||||
this.inspector.selection.off("pseudoclass", this.refresh);
|
this.inspector.selection.off("pseudoclass", this.refresh);
|
||||||
this.inspector.selection.off("new-node", this._onSelect);
|
this.inspector.selection.off("new-node-front", this._onSelect);
|
||||||
if (this.inspector.highlighter) {
|
if (this.inspector.highlighter) {
|
||||||
this.inspector.highlighter.off("locked", this._onSelect);
|
this.inspector.highlighter.off("locked", this._onSelect);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ relativesrcdir = @relativesrcdir@
|
||||||
include $(DEPTH)/config/autoconf.mk
|
include $(DEPTH)/config/autoconf.mk
|
||||||
|
|
||||||
MOCHITEST_BROWSER_FILES = \
|
MOCHITEST_BROWSER_FILES = \
|
||||||
browser_styleinspector.js \
|
|
||||||
browser_bug683672.js \
|
browser_bug683672.js \
|
||||||
browser_styleinspector_bug_672746_default_styles.js \
|
browser_styleinspector_bug_672746_default_styles.js \
|
||||||
browser_styleinspector_bug_672744_search_filter.js \
|
browser_styleinspector_bug_672744_search_filter.js \
|
||||||
|
|
|
@ -13,6 +13,7 @@ const TEST_URI = "http://example.com/browser/browser/devtools/styleinspector/tes
|
||||||
|
|
||||||
let tempScope = {};
|
let tempScope = {};
|
||||||
let {CssHtmlTree, PropertyView} = devtools.require("devtools/styleinspector/computed-view");
|
let {CssHtmlTree, PropertyView} = devtools.require("devtools/styleinspector/computed-view");
|
||||||
|
let {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
|
||||||
|
|
||||||
function test()
|
function test()
|
||||||
{
|
{
|
||||||
|
@ -25,49 +26,49 @@ function tabLoaded()
|
||||||
{
|
{
|
||||||
browser.removeEventListener("load", tabLoaded, true);
|
browser.removeEventListener("load", tabLoaded, true);
|
||||||
doc = content.document;
|
doc = content.document;
|
||||||
openInspector(selectNode);
|
openComputedView(selectNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectNode(aInspector)
|
function selectNode(aInspector, aComputedView)
|
||||||
{
|
{
|
||||||
inspector = aInspector;
|
inspector = aInspector;
|
||||||
|
computedView = aComputedView;
|
||||||
|
|
||||||
div = content.document.getElementById("test");
|
div = content.document.getElementById("test");
|
||||||
ok(div, "captain, we have the div");
|
ok(div, "captain, we have the div");
|
||||||
|
|
||||||
inspector.selection.setNode(div);
|
inspector.selection.setNode(div);
|
||||||
|
inspector.once("inspector-updated", runTests);
|
||||||
inspector.sidebar.once("computedview-ready", function() {
|
|
||||||
computedView = getComputedView(inspector);
|
|
||||||
|
|
||||||
inspector.sidebar.select("computedview");
|
|
||||||
runTests();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function runTests()
|
function runTests()
|
||||||
{
|
{
|
||||||
testMatchedSelectors();
|
testMatchedSelectors().then(() => {
|
||||||
|
info("finishing up");
|
||||||
info("finishing up");
|
finishUp();
|
||||||
finishUp();
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function testMatchedSelectors()
|
function testMatchedSelectors()
|
||||||
{
|
{
|
||||||
info("checking selector counts, matched rules and titles");
|
info("checking selector counts, matched rules and titles");
|
||||||
|
|
||||||
is(div, computedView.viewedElement,
|
is(div, computedView.viewedElement.rawNode(),
|
||||||
"style inspector node matches the selected node");
|
"style inspector node matches the selected node");
|
||||||
|
|
||||||
let propertyView = new PropertyView(computedView, "color");
|
let propertyView = new PropertyView(computedView, "color");
|
||||||
let numMatchedSelectors = propertyView.propertyInfo.matchedSelectors.length;
|
propertyView.buildMain();
|
||||||
|
propertyView.buildSelectorContainer();
|
||||||
|
propertyView.matchedExpanded = true;
|
||||||
|
return propertyView.refreshMatchedSelectors().then(() => {
|
||||||
|
let numMatchedSelectors = propertyView.matchedSelectors.length;
|
||||||
|
|
||||||
is(numMatchedSelectors, 6,
|
is(numMatchedSelectors, 6,
|
||||||
"CssLogic returns the correct number of matched selectors for div");
|
"CssLogic returns the correct number of matched selectors for div");
|
||||||
|
|
||||||
is(propertyView.hasMatchedSelectors, true,
|
is(propertyView.hasMatchedSelectors, true,
|
||||||
"hasMatchedSelectors returns true");
|
"hasMatchedSelectors returns true");
|
||||||
|
}).then(null, (err) => console.error(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
function finishUp()
|
function finishUp()
|
||||||
|
|
|
@ -11,6 +11,9 @@ let computedView;
|
||||||
const TEST_URI = "http://example.com/browser/browser/devtools/styleinspector/" +
|
const TEST_URI = "http://example.com/browser/browser/devtools/styleinspector/" +
|
||||||
"test/browser_bug722196_identify_media_queries.html";
|
"test/browser_bug722196_identify_media_queries.html";
|
||||||
|
|
||||||
|
let {PropertyView} = devtools.require("devtools/styleinspector/computed-view");
|
||||||
|
let {CssLogic} = devtools.require("devtools/styleinspector/css-logic");
|
||||||
|
|
||||||
function test()
|
function test()
|
||||||
{
|
{
|
||||||
waitForExplicitFinish();
|
waitForExplicitFinish();
|
||||||
|
@ -23,26 +26,24 @@ function docLoaded()
|
||||||
browser.removeEventListener("load", docLoaded, true);
|
browser.removeEventListener("load", docLoaded, true);
|
||||||
doc = content.document;
|
doc = content.document;
|
||||||
|
|
||||||
openInspector(selectNode);
|
openComputedView(selectNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectNode(aInspector)
|
function selectNode(aInspector, aComputedView)
|
||||||
{
|
{
|
||||||
|
computedView = aComputedView;
|
||||||
|
|
||||||
var div = doc.querySelector("div");
|
var div = doc.querySelector("div");
|
||||||
ok(div, "captain, we have the div");
|
ok(div, "captain, we have the div");
|
||||||
|
|
||||||
aInspector.selection.setNode(div);
|
aInspector.selection.setNode(div);
|
||||||
|
aInspector.once("inspector-updated", checkCssLogic);
|
||||||
aInspector.sidebar.once("computedview-ready", function() {
|
|
||||||
aInspector.sidebar.select("computedview");
|
|
||||||
computedView = getComputedView(aInspector);
|
|
||||||
checkSheets();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkSheets()
|
function checkCssLogic()
|
||||||
{
|
{
|
||||||
let cssLogic = computedView.cssLogic;
|
let cssLogic = new CssLogic();
|
||||||
|
cssLogic.highlight(doc.querySelector("div"));
|
||||||
cssLogic.processMatchedSelectors();
|
cssLogic.processMatchedSelectors();
|
||||||
|
|
||||||
let _strings = Services.strings
|
let _strings = Services.strings
|
||||||
|
@ -57,7 +58,26 @@ function checkSheets()
|
||||||
is(cssLogic._matchedRules[1][0].source, source2,
|
is(cssLogic._matchedRules[1][0].source, source2,
|
||||||
"rule.source gives correct output for rule 2");
|
"rule.source gives correct output for rule 2");
|
||||||
|
|
||||||
finishUp();
|
checkPropertyView();
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkPropertyView()
|
||||||
|
{
|
||||||
|
let propertyView = new PropertyView(computedView, "width");
|
||||||
|
propertyView.buildMain();
|
||||||
|
propertyView.buildSelectorContainer();
|
||||||
|
propertyView.matchedExpanded = true;
|
||||||
|
return propertyView.refreshMatchedSelectors().then(() => {
|
||||||
|
let numMatchedSelectors = propertyView.matchedSelectors.length;
|
||||||
|
|
||||||
|
is(numMatchedSelectors, 2,
|
||||||
|
"Property view has the correct number of matched selectors for div");
|
||||||
|
|
||||||
|
is(propertyView.hasMatchedSelectors, true,
|
||||||
|
"hasMatchedSelectors returns true");
|
||||||
|
|
||||||
|
finishUp();
|
||||||
|
}).then(null, (err) => console.error(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
function finishUp()
|
function finishUp()
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
// rule view.
|
// rule view.
|
||||||
|
|
||||||
let doc;
|
let doc;
|
||||||
|
let inspector = null;
|
||||||
|
|
||||||
const TEST_URI = "http://example.com/browser/browser/devtools/styleinspector/" +
|
const TEST_URI = "http://example.com/browser/browser/devtools/styleinspector/" +
|
||||||
"test/browser_bug722196_identify_media_queries.html";
|
"test/browser_bug722196_identify_media_queries.html";
|
||||||
|
@ -21,7 +22,15 @@ function docLoaded()
|
||||||
{
|
{
|
||||||
browser.removeEventListener("load", docLoaded, true);
|
browser.removeEventListener("load", docLoaded, true);
|
||||||
doc = content.document;
|
doc = content.document;
|
||||||
checkSheets();
|
openRuleView(selectNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectNode(aInspector, aRuleView)
|
||||||
|
{
|
||||||
|
inspector = aInspector;
|
||||||
|
|
||||||
|
inspector.selection.setNode(doc.querySelector("div"));
|
||||||
|
inspector.once("inspector-updated", checkSheets);
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkSheets()
|
function checkSheets()
|
||||||
|
@ -29,14 +38,14 @@ function checkSheets()
|
||||||
var div = doc.querySelector("div");
|
var div = doc.querySelector("div");
|
||||||
ok(div, "captain, we have the div");
|
ok(div, "captain, we have the div");
|
||||||
|
|
||||||
let elementStyle = new _ElementStyle(div);
|
let elementStyle = ruleView()._elementStyle;
|
||||||
is(elementStyle.rules.length, 3, "Should have 3 rules.");
|
|
||||||
|
|
||||||
let _strings = Services.strings
|
let _strings = Services.strings
|
||||||
.createBundle("chrome://browser/locale/devtools/styleinspector.properties");
|
.createBundle("chrome://browser/locale/devtools/styleinspector.properties");
|
||||||
|
|
||||||
let inline = _strings.GetStringFromName("rule.sourceInline");
|
let inline = _strings.GetStringFromName("rule.sourceInline");
|
||||||
|
|
||||||
|
is(elementStyle.rules.length, 3, "Should have 3 rules.");
|
||||||
is(elementStyle.rules[0].title, inline, "check rule 0 title");
|
is(elementStyle.rules[0].title, inline, "check rule 0 title");
|
||||||
is(elementStyle.rules[1].title, inline +
|
is(elementStyle.rules[1].title, inline +
|
||||||
":15 @media screen and (min-width: 1px)", "check rule 1 title");
|
":15 @media screen and (min-width: 1px)", "check rule 1 title");
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
// arrow keys works correctly.
|
// arrow keys works correctly.
|
||||||
|
|
||||||
let doc;
|
let doc;
|
||||||
let ruleDialog;
|
let view;
|
||||||
let ruleView;
|
let inspector;
|
||||||
|
|
||||||
function setUpTests()
|
function setUpTests()
|
||||||
{
|
{
|
||||||
|
@ -15,25 +15,22 @@ function setUpTests()
|
||||||
'margin-top:0px;' +
|
'margin-top:0px;' +
|
||||||
'padding-top: 0px;' +
|
'padding-top: 0px;' +
|
||||||
'color:#000000;' +
|
'color:#000000;' +
|
||||||
'background-color: #000000; >"'+
|
'background-color: #000000;" >'+
|
||||||
'</div>';
|
'</div>';
|
||||||
let testElement = doc.getElementById("test");
|
|
||||||
ruleDialog = openDialog("chrome://browser/content/devtools/cssruleview.xhtml",
|
openRuleView((aInspector, aRuleView) => {
|
||||||
"cssruleviewtest",
|
inspector = aInspector;
|
||||||
"width=350,height=350");
|
view = aRuleView;
|
||||||
ruleDialog.addEventListener("load", function onLoad(evt) {
|
inspector.selection.setNode(doc.getElementById("test"));
|
||||||
ruleDialog.removeEventListener("load", onLoad, true);
|
inspector.once("inspector-updated", () => {
|
||||||
let doc = ruleDialog.document;
|
runTests();
|
||||||
ruleView = new CssRuleView(doc);
|
})
|
||||||
doc.documentElement.appendChild(ruleView.element);
|
});
|
||||||
ruleView.highlight(testElement);
|
|
||||||
waitForFocus(runTests, ruleDialog);
|
|
||||||
}, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function runTests()
|
function runTests()
|
||||||
{
|
{
|
||||||
let idRuleEditor = ruleView.element.children[0]._ruleEditor;
|
let idRuleEditor = view.element.children[0]._ruleEditor;
|
||||||
let marginPropEditor = idRuleEditor.rule.textProps[0].editor;
|
let marginPropEditor = idRuleEditor.rule.textProps[0].editor;
|
||||||
let paddingPropEditor = idRuleEditor.rule.textProps[1].editor;
|
let paddingPropEditor = idRuleEditor.rule.textProps[1].editor;
|
||||||
let hexColorPropEditor = idRuleEditor.rule.textProps[2].editor;
|
let hexColorPropEditor = idRuleEditor.rule.textProps[2].editor;
|
||||||
|
@ -52,7 +49,7 @@ function runTests()
|
||||||
8: { pageDown: true, shift: true, start: "0px", end: "-100px", selectAll: true,
|
8: { pageDown: true, shift: true, start: "0px", end: "-100px", selectAll: true,
|
||||||
nextTest: test2 }
|
nextTest: test2 }
|
||||||
});
|
});
|
||||||
EventUtils.synthesizeMouse(marginPropEditor.valueSpan, 1, 1, {}, ruleDialog);
|
EventUtils.synthesizeMouse(marginPropEditor.valueSpan, 1, 1, {}, view.doc.defaultView);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
function test2() {
|
function test2() {
|
||||||
|
@ -69,7 +66,7 @@ function runTests()
|
||||||
9: { start: "0ex", end: "1ex", selectAll: true,
|
9: { start: "0ex", end: "1ex", selectAll: true,
|
||||||
nextTest: test3 }
|
nextTest: test3 }
|
||||||
});
|
});
|
||||||
EventUtils.synthesizeMouse(paddingPropEditor.valueSpan, 1, 1, {}, ruleDialog);
|
EventUtils.synthesizeMouse(paddingPropEditor.valueSpan, 1, 1, {}, view.doc.defaultView);
|
||||||
};
|
};
|
||||||
|
|
||||||
function test3() {
|
function test3() {
|
||||||
|
@ -83,7 +80,7 @@ function runTests()
|
||||||
6: { down: true, shift: true, start: "#000000", end: "#000000", selectAll: true,
|
6: { down: true, shift: true, start: "#000000", end: "#000000", selectAll: true,
|
||||||
nextTest: test4 }
|
nextTest: test4 }
|
||||||
});
|
});
|
||||||
EventUtils.synthesizeMouse(hexColorPropEditor.valueSpan, 1, 1, {}, ruleDialog);
|
EventUtils.synthesizeMouse(hexColorPropEditor.valueSpan, 1, 1, {}, view.doc.defaultView);
|
||||||
};
|
};
|
||||||
|
|
||||||
function test4() {
|
function test4() {
|
||||||
|
@ -97,7 +94,7 @@ function runTests()
|
||||||
6: { down: true, shift: true, start: "rgb(0,5,0)", end: "rgb(0,0,0)", selection: [6,7],
|
6: { down: true, shift: true, start: "rgb(0,5,0)", end: "rgb(0,0,0)", selection: [6,7],
|
||||||
nextTest: test5 }
|
nextTest: test5 }
|
||||||
});
|
});
|
||||||
EventUtils.synthesizeMouse(rgbColorPropEditor.valueSpan, 1, 1, {}, ruleDialog);
|
EventUtils.synthesizeMouse(rgbColorPropEditor.valueSpan, 1, 1, {}, view.doc.defaultView);
|
||||||
};
|
};
|
||||||
|
|
||||||
function test5() {
|
function test5() {
|
||||||
|
@ -111,7 +108,7 @@ function runTests()
|
||||||
6: { down: true, shift: true, start: "0px 0px 0px 0px", end: "-10px 0px 0px 0px", selectAll: true,
|
6: { down: true, shift: true, start: "0px 0px 0px 0px", end: "-10px 0px 0px 0px", selectAll: true,
|
||||||
nextTest: test6 }
|
nextTest: test6 }
|
||||||
});
|
});
|
||||||
EventUtils.synthesizeMouse(paddingPropEditor.valueSpan, 1, 1, {}, ruleDialog);
|
EventUtils.synthesizeMouse(paddingPropEditor.valueSpan, 1, 1, {}, view.doc.defaultView);
|
||||||
};
|
};
|
||||||
|
|
||||||
function test6() {
|
function test6() {
|
||||||
|
@ -133,7 +130,7 @@ function runTests()
|
||||||
14: { alt: true, start: "url('test--0.1.png')", end: "url('test-0.png')", selection: [10,14],
|
14: { alt: true, start: "url('test--0.1.png')", end: "url('test-0.png')", selection: [10,14],
|
||||||
endTest: true }
|
endTest: true }
|
||||||
});
|
});
|
||||||
EventUtils.synthesizeMouse(marginPropEditor.valueSpan, 1, 1, {}, ruleDialog);
|
EventUtils.synthesizeMouse(marginPropEditor.valueSpan, 1, 1, {}, view.doc.defaultView);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,14 +169,11 @@ function testIncrement( aEditor, aOptions )
|
||||||
key = ( aOptions.pageDown ) ? "VK_PAGE_DOWN" : ( aOptions.pageUp ) ? "VK_PAGE_UP" : key;
|
key = ( aOptions.pageDown ) ? "VK_PAGE_DOWN" : ( aOptions.pageUp ) ? "VK_PAGE_UP" : key;
|
||||||
EventUtils.synthesizeKey(key,
|
EventUtils.synthesizeKey(key,
|
||||||
{altKey: aOptions.alt, shiftKey: aOptions.shift},
|
{altKey: aOptions.alt, shiftKey: aOptions.shift},
|
||||||
ruleDialog);
|
view.doc.defaultView);
|
||||||
}
|
}
|
||||||
|
|
||||||
function finishTest()
|
function finishTest()
|
||||||
{
|
{
|
||||||
ruleView.clear();
|
|
||||||
ruleDialog.close();
|
|
||||||
ruleDialog = ruleView = null;
|
|
||||||
doc = null;
|
doc = null;
|
||||||
gBrowser.removeCurrentTab();
|
gBrowser.removeCurrentTab();
|
||||||
finish();
|
finish();
|
||||||
|
|
|
@ -62,7 +62,7 @@ function openRuleView()
|
||||||
let node = content.document.getElementsByTagName("h1")[0];
|
let node = content.document.getElementsByTagName("h1")[0];
|
||||||
inspector.selection.setNode(node);
|
inspector.selection.setNode(node);
|
||||||
|
|
||||||
inspector.sidebar.once("ruleview-ready", testCompletion);
|
inspector.once("inspector-updated", testCompletion);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ function openRuleView() {
|
||||||
let node = content.document.getElementsByTagName("h1")[0];
|
let node = content.document.getElementsByTagName("h1")[0];
|
||||||
inspector.selection.setNode(node);
|
inspector.selection.setNode(node);
|
||||||
|
|
||||||
inspector.sidebar.once("ruleview-ready", testCompletion);
|
inspector.once("inspector-updated", testCompletion);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ function openRuleView()
|
||||||
let node = content.document.getElementsByTagName("h1")[0];
|
let node = content.document.getElementsByTagName("h1")[0];
|
||||||
inspector.selection.setNode(node);
|
inspector.selection.setNode(node);
|
||||||
|
|
||||||
inspector.sidebar.once("ruleview-ready", testCompletion);
|
inspector.once("inspector-updated", testCompletion);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ function openRuleView() {
|
||||||
let node = content.document.getElementsByTagName("h1")[0];
|
let node = content.document.getElementsByTagName("h1")[0];
|
||||||
inspector.selection.setNode(node);
|
inspector.selection.setNode(node);
|
||||||
|
|
||||||
inspector.sidebar.once("ruleview-ready", testCompletion);
|
inspector.once("inspector-updated", testCompletion);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,29 +13,23 @@ function createDocument()
|
||||||
|
|
||||||
doc.title = "Style Inspector Selector Text Test";
|
doc.title = "Style Inspector Selector Text Test";
|
||||||
|
|
||||||
openInspector(openComputedView);
|
openComputedView(startTests);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function openComputedView(aInspector)
|
function startTests(aInspector, aComputedView)
|
||||||
{
|
{
|
||||||
|
computedView = aComputedView;
|
||||||
|
|
||||||
let div = doc.querySelector("div");
|
let div = doc.querySelector("div");
|
||||||
ok(div, "captain, we have the test div");
|
ok(div, "captain, we have the test div");
|
||||||
|
|
||||||
aInspector.selection.setNode(div);
|
aInspector.selection.setNode(div);
|
||||||
|
aInspector.once("inspector-updated", SI_checkText);
|
||||||
aInspector.sidebar.once("computedview-ready", function() {
|
|
||||||
aInspector.sidebar.select("computedview");
|
|
||||||
computedView = getComputedView(aInspector);
|
|
||||||
|
|
||||||
Services.obs.addObserver(SI_checkText, "StyleInspector-populated", false);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function SI_checkText()
|
function SI_checkText()
|
||||||
{
|
{
|
||||||
Services.obs.removeObserver(SI_checkText, "StyleInspector-populated");
|
|
||||||
|
|
||||||
let propertyView = null;
|
let propertyView = null;
|
||||||
computedView.propertyViews.some(function(aView) {
|
computedView.propertyViews.some(function(aView) {
|
||||||
if (aView.name == "color") {
|
if (aView.name == "color") {
|
||||||
|
@ -50,15 +44,16 @@ function SI_checkText()
|
||||||
is(propertyView.hasMatchedSelectors, true, "hasMatchedSelectors is true");
|
is(propertyView.hasMatchedSelectors, true, "hasMatchedSelectors is true");
|
||||||
|
|
||||||
propertyView.matchedExpanded = true;
|
propertyView.matchedExpanded = true;
|
||||||
propertyView.refreshMatchedSelectors();
|
propertyView.refreshMatchedSelectors().then(() => {
|
||||||
|
|
||||||
let span = propertyView.matchedSelectorsContainer.querySelector("span.rule-text");
|
let span = propertyView.matchedSelectorsContainer.querySelector("span.rule-text");
|
||||||
ok(span, "found the first table row");
|
ok(span, "found the first table row");
|
||||||
|
|
||||||
let selector = propertyView.matchedSelectorViews[0];
|
let selector = propertyView.matchedSelectorViews[0];
|
||||||
ok(selector, "found the first matched selector view");
|
ok(selector, "found the first matched selector view");
|
||||||
|
|
||||||
finishUp();
|
finishUp();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function finishUp()
|
function finishUp()
|
||||||
|
|
|
@ -42,29 +42,20 @@ const DOCUMENT_URL = "data:text/html,"+encodeURIComponent(
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function selectNode(aInspector)
|
function selectNode(aInspector, aComputedView)
|
||||||
{
|
{
|
||||||
inspector = aInspector;
|
inspector = aInspector;
|
||||||
|
computedView = aComputedView;
|
||||||
|
|
||||||
let span = doc.querySelector("span");
|
let span = doc.querySelector("span");
|
||||||
ok(span, "captain, we have the span");
|
ok(span, "captain, we have the span");
|
||||||
|
|
||||||
aInspector.selection.setNode(span);
|
inspector.selection.setNode(span);
|
||||||
|
inspector.once("inspector-updated", testInlineStyle);
|
||||||
aInspector.sidebar.once("computedview-ready", function() {
|
|
||||||
aInspector.sidebar.select("computedview");
|
|
||||||
|
|
||||||
computedView = getComputedView(aInspector);
|
|
||||||
|
|
||||||
Services.obs.addObserver(testInlineStyle, "StyleInspector-populated", false);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function testInlineStyle()
|
function testInlineStyle()
|
||||||
{
|
{
|
||||||
Services.obs.removeObserver(testInlineStyle, "StyleInspector-populated");
|
|
||||||
|
|
||||||
info("expanding property");
|
|
||||||
expandProperty(0, function propertyExpanded() {
|
expandProperty(0, function propertyExpanded() {
|
||||||
Services.ww.registerNotification(function onWindow(aSubject, aTopic) {
|
Services.ww.registerNotification(function onWindow(aSubject, aTopic) {
|
||||||
if (aTopic != "domwindowopened") {
|
if (aTopic != "domwindowopened") {
|
||||||
|
@ -137,12 +128,13 @@ function validateStyleEditorSheet(aEditor, aExpectedSheetIndex)
|
||||||
|
|
||||||
function expandProperty(aIndex, aCallback)
|
function expandProperty(aIndex, aCallback)
|
||||||
{
|
{
|
||||||
|
info("expanding property " + aIndex);
|
||||||
let contentDoc = computedView.styleDocument;
|
let contentDoc = computedView.styleDocument;
|
||||||
let expando = contentDoc.querySelectorAll(".expandable")[aIndex];
|
let expando = contentDoc.querySelectorAll(".expandable")[aIndex];
|
||||||
|
|
||||||
expando.click();
|
expando.click();
|
||||||
|
|
||||||
// We use executeSoon to give the property time to expand.
|
inspector.once("computed-view-property-expanded", aCallback);
|
||||||
executeSoon(aCallback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLinkByIndex(aIndex)
|
function getLinkByIndex(aIndex)
|
||||||
|
@ -168,7 +160,7 @@ function test()
|
||||||
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee,
|
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee,
|
||||||
true);
|
true);
|
||||||
doc = content.document;
|
doc = content.document;
|
||||||
waitForFocus(function () { openInspector(selectNode); }, content);
|
waitForFocus(function () { openComputedView(selectNode); }, content);
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
content.location = DOCUMENT_URL;
|
content.location = DOCUMENT_URL;
|
||||||
|
|
|
@ -32,33 +32,24 @@ function createDocument()
|
||||||
'</div>';
|
'</div>';
|
||||||
doc.title = "Computed view context menu test";
|
doc.title = "Computed view context menu test";
|
||||||
|
|
||||||
openInspector(selectNode)
|
openComputedView(selectNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectNode(aInspector)
|
function selectNode(aInspector, aComputedView)
|
||||||
{
|
{
|
||||||
|
computedView = aComputedView;
|
||||||
|
win = aInspector.sidebar.getWindowForTab("computedview");
|
||||||
|
|
||||||
let span = doc.querySelector("span");
|
let span = doc.querySelector("span");
|
||||||
ok(span, "captain, we have the span");
|
ok(span, "captain, we have the span");
|
||||||
|
|
||||||
aInspector.selection.setNode(span);
|
aInspector.selection.setNode(span);
|
||||||
|
aInspector.once("inspector-updated", runStyleInspectorTests);
|
||||||
aInspector.sidebar.once("computedview-ready", function() {
|
|
||||||
aInspector.sidebar.select("computedview");
|
|
||||||
|
|
||||||
computedView = getComputedView(aInspector);
|
|
||||||
win = aInspector.sidebar.getWindowForTab("computedview");
|
|
||||||
|
|
||||||
Services.obs.addObserver(runStyleInspectorTests,
|
|
||||||
"StyleInspector-populated", false);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function runStyleInspectorTests()
|
function runStyleInspectorTests()
|
||||||
{
|
{
|
||||||
Services.obs.removeObserver(runStyleInspectorTests,
|
|
||||||
"StyleInspector-populated", false);
|
|
||||||
|
|
||||||
let contentDocument = computedView.styleDocument;
|
let contentDocument = computedView.styleDocument;
|
||||||
let prop = contentDocument.querySelector(".property-view");
|
let prop = contentDocument.querySelector(".property-view");
|
||||||
ok(prop, "captain, we have the property-view node");
|
ok(prop, "captain, we have the property-view node");
|
||||||
|
|
|
@ -62,42 +62,38 @@ function highlightNode()
|
||||||
// Highlight a node.
|
// Highlight a node.
|
||||||
let div = content.document.getElementsByTagName("div")[0];
|
let div = content.document.getElementsByTagName("div")[0];
|
||||||
|
|
||||||
inspector.selection.once("new-node", function() {
|
inspector.selection.setNode(div);
|
||||||
|
inspector.once("inspector-updated", () => {
|
||||||
is(inspector.selection.node, div, "selection matches the div element");
|
is(inspector.selection.node, div, "selection matches the div element");
|
||||||
testInlineStyle();
|
testInlineStyle();
|
||||||
});
|
}).then(null, console.error);
|
||||||
executeSoon(function() {
|
|
||||||
inspector.selection.setNode(div);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function testInlineStyle()
|
function testInlineStyle()
|
||||||
{
|
{
|
||||||
executeSoon(function() {
|
info("clicking an inline style");
|
||||||
info("clicking an inline style");
|
|
||||||
|
|
||||||
Services.ww.registerNotification(function onWindow(aSubject, aTopic) {
|
Services.ww.registerNotification(function onWindow(aSubject, aTopic) {
|
||||||
if (aTopic != "domwindowopened") {
|
if (aTopic != "domwindowopened") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
win = aSubject.QueryInterface(Ci.nsIDOMWindow);
|
win = aSubject.QueryInterface(Ci.nsIDOMWindow);
|
||||||
win.addEventListener("load", function windowLoad() {
|
win.addEventListener("load", function windowLoad() {
|
||||||
win.removeEventListener("load", windowLoad);
|
win.removeEventListener("load", windowLoad);
|
||||||
let windowType = win.document.documentElement.getAttribute("windowtype");
|
let windowType = win.document.documentElement.getAttribute("windowtype");
|
||||||
is(windowType, "navigator:view-source", "view source window is open");
|
is(windowType, "navigator:view-source", "view source window is open");
|
||||||
win.close();
|
win.close();
|
||||||
Services.ww.unregisterNotification(onWindow);
|
Services.ww.unregisterNotification(onWindow);
|
||||||
executeSoon(() => {
|
executeSoon(() => {
|
||||||
testInlineStyleSheet();
|
testInlineStyleSheet();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
let link = getLinkByIndex(0);
|
|
||||||
link.scrollIntoView();
|
|
||||||
link.click();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let link = getLinkByIndex(0);
|
||||||
|
link.scrollIntoView();
|
||||||
|
link.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
function testInlineStyleSheet()
|
function testInlineStyleSheet()
|
||||||
|
|
|
@ -45,13 +45,12 @@ function highlightNode()
|
||||||
// Highlight a node.
|
// Highlight a node.
|
||||||
let div = content.document.getElementsByTagName("div")[0];
|
let div = content.document.getElementsByTagName("div")[0];
|
||||||
|
|
||||||
inspector.selection.once("new-node", function() {
|
inspector.once("inspector-updated", function() {
|
||||||
is(inspector.selection.node, div, "selection matches the div element");
|
is(inspector.selection.node, div, "selection matches the div element");
|
||||||
executeSoon(checkCopySelection);
|
executeSoon(checkCopySelection);
|
||||||
});
|
});
|
||||||
executeSoon(function() {
|
|
||||||
inspector.selection.setNode(div);
|
inspector.selection.setNode(div);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkCopySelection()
|
function checkCopySelection()
|
||||||
|
|
|
@ -3,20 +3,9 @@
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
let doc;
|
let doc;
|
||||||
let ruleDialog;
|
let ruleWindow;
|
||||||
let ruleView;
|
let ruleView;
|
||||||
|
let inspector;
|
||||||
var gRuleViewChanged = false;
|
|
||||||
function ruleViewChanged()
|
|
||||||
{
|
|
||||||
gRuleViewChanged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function expectChange()
|
|
||||||
{
|
|
||||||
ok(gRuleViewChanged, "Rule view should have fired a change event.");
|
|
||||||
gRuleViewChanged = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function startTest()
|
function startTest()
|
||||||
{
|
{
|
||||||
|
@ -32,18 +21,13 @@ function startTest()
|
||||||
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
|
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
|
||||||
let testElement = doc.getElementById("testid");
|
let testElement = doc.getElementById("testid");
|
||||||
|
|
||||||
ruleDialog = openDialog("chrome://browser/content/devtools/cssruleview.xhtml",
|
openRuleView((aInspector, aRuleView) => {
|
||||||
"cssruleviewtest",
|
inspector = aInspector;
|
||||||
"width=200,height=350");
|
ruleView = aRuleView;
|
||||||
ruleDialog.addEventListener("load", function onLoad(evt) {
|
ruleWindow = aRuleView.doc.defaultView;
|
||||||
ruleDialog.removeEventListener("load", onLoad, true);
|
inspector.selection.setNode(testElement);
|
||||||
let doc = ruleDialog.document;
|
inspector.once("inspector-updated", testCancelNew);
|
||||||
ruleView = new CssRuleView(doc);
|
});
|
||||||
doc.documentElement.appendChild(ruleView.element);
|
|
||||||
ruleView.element.addEventListener("CssRuleViewChanged", ruleViewChanged, false);
|
|
||||||
ruleView.highlight(testElement);
|
|
||||||
waitForFocus(testCancelNew, ruleDialog);
|
|
||||||
}, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function testCancelNew()
|
function testCancelNew()
|
||||||
|
@ -55,7 +39,7 @@ function testCancelNew()
|
||||||
is(inplaceEditor(elementRuleEditor.newPropSpan), aEditor, "Next focused editor should be the new property editor.");
|
is(inplaceEditor(elementRuleEditor.newPropSpan), aEditor, "Next focused editor should be the new property editor.");
|
||||||
let input = aEditor.input;
|
let input = aEditor.input;
|
||||||
waitForEditorBlur(aEditor, function () {
|
waitForEditorBlur(aEditor, function () {
|
||||||
ok(!gRuleViewChanged, "Shouldn't get a change event after a cancel.");
|
ok(!elementRuleEditor.rule._applyingModifications, "Shouldn't have an outstanding request after a cancel.");
|
||||||
is(elementRuleEditor.rule.textProps.length, 0, "Should have canceled creating a new text property.");
|
is(elementRuleEditor.rule.textProps.length, 0, "Should have canceled creating a new text property.");
|
||||||
ok(!elementRuleEditor.propertyList.hasChildNodes(), "Should not have any properties.");
|
ok(!elementRuleEditor.propertyList.hasChildNodes(), "Should not have any properties.");
|
||||||
testCreateNew();
|
testCreateNew();
|
||||||
|
@ -64,7 +48,7 @@ function testCancelNew()
|
||||||
});
|
});
|
||||||
EventUtils.synthesizeMouse(elementRuleEditor.closeBrace, 1, 1,
|
EventUtils.synthesizeMouse(elementRuleEditor.closeBrace, 1, 1,
|
||||||
{ },
|
{ },
|
||||||
ruleDialog);
|
ruleWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testCreateNew()
|
function testCreateNew()
|
||||||
|
@ -77,26 +61,28 @@ function testCreateNew()
|
||||||
input.value = "background-color";
|
input.value = "background-color";
|
||||||
|
|
||||||
waitForEditorFocus(elementRuleEditor.element, function onNewValue(aEditor) {
|
waitForEditorFocus(elementRuleEditor.element, function onNewValue(aEditor) {
|
||||||
expectChange();
|
promiseDone(expectRuleChange(elementRuleEditor.rule).then(() => {
|
||||||
is(elementRuleEditor.rule.textProps.length, 1, "Should have created a new text property.");
|
is(elementRuleEditor.rule.textProps.length, 1, "Should have created a new text property.");
|
||||||
is(elementRuleEditor.propertyList.children.length, 1, "Should have created a property editor.");
|
is(elementRuleEditor.propertyList.children.length, 1, "Should have created a property editor.");
|
||||||
let textProp = elementRuleEditor.rule.textProps[0];
|
let textProp = elementRuleEditor.rule.textProps[0];
|
||||||
is(aEditor, inplaceEditor(textProp.editor.valueSpan), "Should be editing the value span now.");
|
is(aEditor, inplaceEditor(textProp.editor.valueSpan), "Should be editing the value span now.");
|
||||||
aEditor.input.value = "#XYZ";
|
aEditor.input.value = "#XYZ";
|
||||||
waitForEditorBlur(aEditor, function() {
|
waitForEditorBlur(aEditor, function() {
|
||||||
expectChange();
|
promiseDone(expectRuleChange(elementRuleEditor.rule).then(() => {
|
||||||
is(textProp.value, "#XYZ", "Text prop should have been changed.");
|
is(textProp.value, "#XYZ", "Text prop should have been changed.");
|
||||||
is(textProp.editor._validate(), false, "#XYZ should not be a valid entry");
|
is(textProp.editor._validate(), false, "#XYZ should not be a valid entry");
|
||||||
testEditProperty();
|
testEditProperty();
|
||||||
});
|
}));
|
||||||
aEditor.input.blur();
|
});
|
||||||
|
aEditor.input.blur();
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
EventUtils.synthesizeKey("VK_RETURN", {}, ruleDialog);
|
EventUtils.synthesizeKey("VK_RETURN", {}, ruleWindow);
|
||||||
});
|
});
|
||||||
|
|
||||||
EventUtils.synthesizeMouse(elementRuleEditor.closeBrace, 1, 1,
|
EventUtils.synthesizeMouse(elementRuleEditor.closeBrace, 1, 1,
|
||||||
{ },
|
{ },
|
||||||
ruleDialog);
|
ruleWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testEditProperty()
|
function testEditProperty()
|
||||||
|
@ -107,38 +93,37 @@ function testEditProperty()
|
||||||
is(inplaceEditor(propEditor.nameSpan), aEditor, "Next focused editor should be the name editor.");
|
is(inplaceEditor(propEditor.nameSpan), aEditor, "Next focused editor should be the name editor.");
|
||||||
let input = aEditor.input;
|
let input = aEditor.input;
|
||||||
waitForEditorFocus(propEditor.element, function onNewName(aEditor) {
|
waitForEditorFocus(propEditor.element, function onNewName(aEditor) {
|
||||||
expectChange();
|
promiseDone(expectRuleChange(idRuleEditor.rule).then(() => {
|
||||||
input = aEditor.input;
|
input = aEditor.input;
|
||||||
is(inplaceEditor(propEditor.valueSpan), aEditor, "Focus should have moved to the value.");
|
is(inplaceEditor(propEditor.valueSpan), aEditor, "Focus should have moved to the value.");
|
||||||
|
|
||||||
waitForEditorBlur(aEditor, function() {
|
waitForEditorBlur(aEditor, function() {
|
||||||
expectChange();
|
promiseDone(expectRuleChange(idRuleEditor.rule).then(() => {
|
||||||
let value = idRuleEditor.rule.style.getPropertyValue("border-color");
|
let value = idRuleEditor.rule.domRule._rawStyle().getPropertyValue("border-color");
|
||||||
is(value, "red", "border-color should have been set.");
|
is(value, "red", "border-color should have been set.");
|
||||||
is(propEditor._validate(), true, "red should be a valid entry");
|
is(propEditor._validate(), true, "red should be a valid entry");
|
||||||
finishTest();
|
finishTest();
|
||||||
});
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
for each (let ch in "red;") {
|
for (let ch of "red;") {
|
||||||
EventUtils.sendChar(ch, ruleDialog);
|
EventUtils.sendChar(ch, ruleWindow);
|
||||||
}
|
}
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
for each (let ch in "border-color:") {
|
for (let ch of "border-color:") {
|
||||||
EventUtils.sendChar(ch, ruleDialog);
|
EventUtils.sendChar(ch, ruleWindow);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
EventUtils.synthesizeMouse(propEditor.nameSpan, 32, 1,
|
EventUtils.synthesizeMouse(propEditor.nameSpan, 32, 1,
|
||||||
{ },
|
{ },
|
||||||
ruleDialog);
|
ruleWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
function finishTest()
|
function finishTest()
|
||||||
{
|
{
|
||||||
ruleView.element.removeEventListener("CssRuleViewChanged", ruleViewChanged, false);
|
inspector = ruleWindow = ruleView = null;
|
||||||
ruleView.clear();
|
|
||||||
ruleDialog.close();
|
|
||||||
ruleDialog = ruleView = null;
|
|
||||||
doc = null;
|
doc = null;
|
||||||
gBrowser.removeCurrentTab();
|
gBrowser.removeCurrentTab();
|
||||||
finish();
|
finish();
|
||||||
|
|
|
@ -9,18 +9,12 @@ let doc;
|
||||||
let inspector;
|
let inspector;
|
||||||
let stylePanel;
|
let stylePanel;
|
||||||
|
|
||||||
function openRuleView()
|
function selectNode(aInspector, aRuleView)
|
||||||
{
|
{
|
||||||
var target = TargetFactory.forTab(gBrowser.selectedTab);
|
inspector = aInspector;
|
||||||
gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
|
let node = content.document.getElementsByTagName("h1")[0];
|
||||||
inspector = toolbox.getCurrentPanel();
|
inspector.selection.setNode(node);
|
||||||
inspector.sidebar.select("ruleview");
|
inspector.once("inspector-updated", testFocus);
|
||||||
|
|
||||||
// Highlight a node.
|
|
||||||
let node = content.document.getElementsByTagName("h1")[0];
|
|
||||||
|
|
||||||
inspector.sidebar.once("ruleview-ready", testFocus);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function testFocus()
|
function testFocus()
|
||||||
|
@ -64,7 +58,7 @@ function test()
|
||||||
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
|
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
|
||||||
doc = content.document;
|
doc = content.document;
|
||||||
doc.title = "Rule View Test";
|
doc.title = "Rule View Test";
|
||||||
waitForFocus(openRuleView, content);
|
waitForFocus(() => openRuleView(selectNode), content);
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
content.location = "data:text/html,<h1>Some header text</h1>";
|
content.location = "data:text/html,<h1>Some header text</h1>";
|
||||||
|
|
|
@ -4,8 +4,16 @@
|
||||||
|
|
||||||
let doc;
|
let doc;
|
||||||
|
|
||||||
function simpleInherit()
|
let inspector;
|
||||||
|
let view;
|
||||||
|
|
||||||
|
let {ELEMENT_STYLE} = devtools.require("devtools/server/actors/styles");
|
||||||
|
|
||||||
|
function simpleInherit(aInspector, aRuleView)
|
||||||
{
|
{
|
||||||
|
inspector = aInspector;
|
||||||
|
view = aRuleView;
|
||||||
|
|
||||||
let style = '' +
|
let style = '' +
|
||||||
'#test2 {' +
|
'#test2 {' +
|
||||||
' background-color: green;' +
|
' background-color: green;' +
|
||||||
|
@ -15,23 +23,26 @@ function simpleInherit()
|
||||||
let styleNode = addStyle(doc, style);
|
let styleNode = addStyle(doc, style);
|
||||||
doc.body.innerHTML = '<div id="test2"><div id="test1">Styled Node</div></div>';
|
doc.body.innerHTML = '<div id="test2"><div id="test1">Styled Node</div></div>';
|
||||||
|
|
||||||
let elementStyle = new _ElementStyle(doc.getElementById("test1"));
|
inspector.selection.setNode(doc.getElementById("test1"));
|
||||||
|
inspector.once("inspector-updated", () => {
|
||||||
|
let elementStyle = view._elementStyle;
|
||||||
|
|
||||||
is(elementStyle.rules.length, 2, "Should have 2 rules.");
|
is(elementStyle.rules.length, 2, "Should have 2 rules.");
|
||||||
|
|
||||||
let elementRule = elementStyle.rules[0];
|
let elementRule = elementStyle.rules[0];
|
||||||
ok(!elementRule.inherited, "Element style attribute should not consider itself inherited.");
|
ok(!elementRule.inherited, "Element style attribute should not consider itself inherited.");
|
||||||
|
|
||||||
let inheritRule = elementStyle.rules[1];
|
let inheritRule = elementStyle.rules[1];
|
||||||
is(inheritRule.selectorText, "#test2", "Inherited rule should be the one that includes inheritable properties.");
|
is(inheritRule.selectorText, "#test2", "Inherited rule should be the one that includes inheritable properties.");
|
||||||
ok(!!inheritRule.inherited, "Rule should consider itself inherited.");
|
ok(!!inheritRule.inherited, "Rule should consider itself inherited.");
|
||||||
is(inheritRule.textProps.length, 1, "Should only display one inherited style");
|
is(inheritRule.textProps.length, 1, "Should only display one inherited style");
|
||||||
let inheritProp = inheritRule.textProps[0];
|
let inheritProp = inheritRule.textProps[0];
|
||||||
is(inheritProp.name, "color", "color should have been inherited.");
|
is(inheritProp.name, "color", "color should have been inherited.");
|
||||||
|
|
||||||
styleNode.parentNode.removeChild(styleNode);
|
styleNode.parentNode.removeChild(styleNode);
|
||||||
|
|
||||||
emptyInherit();
|
emptyInherit();
|
||||||
|
}).then(null, console.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
function emptyInherit()
|
function emptyInherit()
|
||||||
|
@ -45,37 +56,43 @@ function emptyInherit()
|
||||||
let styleNode = addStyle(doc, style);
|
let styleNode = addStyle(doc, style);
|
||||||
doc.body.innerHTML = '<div id="test2"><div id="test1">Styled Node</div></div>';
|
doc.body.innerHTML = '<div id="test2"><div id="test1">Styled Node</div></div>';
|
||||||
|
|
||||||
let elementStyle = new _ElementStyle(doc.getElementById("test1"));
|
inspector.selection.setNode(doc.getElementById("test1"));
|
||||||
|
inspector.once("inspector-updated", () => {
|
||||||
|
let elementStyle = view._elementStyle;
|
||||||
|
|
||||||
is(elementStyle.rules.length, 1, "Should have 1 rule.");
|
is(elementStyle.rules.length, 1, "Should have 1 rule.");
|
||||||
|
|
||||||
let elementRule = elementStyle.rules[0];
|
let elementRule = elementStyle.rules[0];
|
||||||
ok(!elementRule.inherited, "Element style attribute should not consider itself inherited.");
|
ok(!elementRule.inherited, "Element style attribute should not consider itself inherited.");
|
||||||
|
|
||||||
styleNode.parentNode.removeChild(styleNode);
|
styleNode.parentNode.removeChild(styleNode);
|
||||||
|
|
||||||
elementStyleInherit();
|
elementStyleInherit();
|
||||||
|
}).then(null, console.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
function elementStyleInherit()
|
function elementStyleInherit()
|
||||||
{
|
{
|
||||||
doc.body.innerHTML = '<div id="test2" style="color: red"><div id="test1">Styled Node</div></div>';
|
doc.body.innerHTML = '<div id="test2" style="color: red"><div id="test1">Styled Node</div></div>';
|
||||||
|
|
||||||
let elementStyle = new _ElementStyle(doc.getElementById("test1"));
|
inspector.selection.setNode(doc.getElementById("test1"));
|
||||||
|
inspector.once("inspector-updated", () => {
|
||||||
|
let elementStyle = view._elementStyle;
|
||||||
|
|
||||||
is(elementStyle.rules.length, 2, "Should have 2 rules.");
|
is(elementStyle.rules.length, 2, "Should have 2 rules.");
|
||||||
|
|
||||||
let elementRule = elementStyle.rules[0];
|
let elementRule = elementStyle.rules[0];
|
||||||
ok(!elementRule.inherited, "Element style attribute should not consider itself inherited.");
|
ok(!elementRule.inherited, "Element style attribute should not consider itself inherited.");
|
||||||
|
|
||||||
let inheritRule = elementStyle.rules[1];
|
let inheritRule = elementStyle.rules[1];
|
||||||
ok(!inheritRule.domRule, "Inherited rule should be an element style, not a rule.");
|
is(inheritRule.domRule.type, ELEMENT_STYLE, "Inherited rule should be an element style, not a rule.");
|
||||||
ok(!!inheritRule.inherited, "Rule should consider itself inherited.");
|
ok(!!inheritRule.inherited, "Rule should consider itself inherited.");
|
||||||
is(inheritRule.textProps.length, 1, "Should only display one inherited style");
|
is(inheritRule.textProps.length, 1, "Should only display one inherited style");
|
||||||
let inheritProp = inheritRule.textProps[0];
|
let inheritProp = inheritRule.textProps[0];
|
||||||
is(inheritProp.name, "color", "color should have been inherited.");
|
is(inheritProp.name, "color", "color should have been inherited.");
|
||||||
|
|
||||||
finishTest();
|
finishTest();
|
||||||
|
}).then(null, console.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
function finishTest()
|
function finishTest()
|
||||||
|
@ -92,7 +109,7 @@ function test()
|
||||||
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
|
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
|
||||||
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
|
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
|
||||||
doc = content.document;
|
doc = content.document;
|
||||||
waitForFocus(simpleInherit, content);
|
waitForFocus(() => openRuleView(simpleInherit), content);
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
content.location = "data:text/html,basic style inspector tests";
|
content.location = "data:text/html,basic style inspector tests";
|
||||||
|
|
|
@ -4,47 +4,68 @@
|
||||||
|
|
||||||
let doc;
|
let doc;
|
||||||
|
|
||||||
function simpleOverride()
|
|
||||||
|
|
||||||
|
function simpleOverride(aInspector, aRuleView)
|
||||||
{
|
{
|
||||||
doc.body.innerHTML = '<div id="testid">Styled Node</div>';
|
doc.body.innerHTML = '<div id="testid">Styled Node</div>';
|
||||||
let element = doc.getElementById("testid");
|
let element = doc.getElementById("testid");
|
||||||
let elementStyle = new _ElementStyle(element);
|
|
||||||
|
|
||||||
let elementRule = elementStyle.rules[0];
|
aInspector.selection.setNode(element);
|
||||||
let firstProp = elementRule.createProperty("background-color", "green", "");
|
aInspector.once("inspector-updated", () => {
|
||||||
let secondProp = elementRule.createProperty("background-color", "blue", "");
|
let elementStyle = aRuleView._elementStyle;
|
||||||
is(elementRule.textProps[0], firstProp, "Rules should be in addition order.");
|
|
||||||
is(elementRule.textProps[1], secondProp, "Rules should be in addition order.");
|
|
||||||
|
|
||||||
is(element.style.getPropertyValue("background-color"), "blue", "Second property should have been used.");
|
let elementRule = elementStyle.rules[0];
|
||||||
|
let firstProp = elementRule.createProperty("background-color", "green", "");
|
||||||
|
let secondProp = elementRule.createProperty("background-color", "blue", "");
|
||||||
|
is(elementRule.textProps[0], firstProp, "Rules should be in addition order.");
|
||||||
|
is(elementRule.textProps[1], secondProp, "Rules should be in addition order.");
|
||||||
|
|
||||||
secondProp.remove();
|
promiseDone(elementRule._applyingModifications.then(() => {
|
||||||
is(element.style.getPropertyValue("background-color"), "green", "After deleting second property, first should be used.");
|
is(element.style.getPropertyValue("background-color"), "blue", "Second property should have been used.");
|
||||||
|
|
||||||
secondProp = elementRule.createProperty("background-color", "blue", "");
|
secondProp.remove();
|
||||||
is(element.style.getPropertyValue("background-color"), "blue", "New property should be used.");
|
return elementRule._applyingModifications;
|
||||||
|
}).then(() => {
|
||||||
|
is(element.style.getPropertyValue("background-color"), "green", "After deleting second property, first should be used.");
|
||||||
|
|
||||||
is(elementRule.textProps[0], firstProp, "Rules shouldn't have switched places.");
|
secondProp = elementRule.createProperty("background-color", "blue", "");
|
||||||
is(elementRule.textProps[1], secondProp, "Rules shouldn't have switched places.");
|
return elementRule._applyingModifications;
|
||||||
|
}).then(() => {
|
||||||
|
is(element.style.getPropertyValue("background-color"), "blue", "New property should be used.");
|
||||||
|
|
||||||
secondProp.setEnabled(false);
|
is(elementRule.textProps[0], firstProp, "Rules shouldn't have switched places.");
|
||||||
is(element.style.getPropertyValue("background-color"), "green", "After disabling second property, first value should be used");
|
is(elementRule.textProps[1], secondProp, "Rules shouldn't have switched places.");
|
||||||
|
|
||||||
firstProp.setEnabled(false);
|
secondProp.setEnabled(false);
|
||||||
is(element.style.getPropertyValue("background-color"), "", "After disabling both properties, value should be empty.");
|
return elementRule._applyingModifications;
|
||||||
|
}).then(() => {
|
||||||
|
is(element.style.getPropertyValue("background-color"), "green", "After disabling second property, first value should be used");
|
||||||
|
|
||||||
secondProp.setEnabled(true);
|
firstProp.setEnabled(false);
|
||||||
is(element.style.getPropertyValue("background-color"), "blue", "Value should be set correctly after re-enabling");
|
return elementRule._applyingModifications;
|
||||||
|
}).then(() => {
|
||||||
|
is(element.style.getPropertyValue("background-color"), "", "After disabling both properties, value should be empty.");
|
||||||
|
|
||||||
firstProp.setEnabled(true);
|
secondProp.setEnabled(true);
|
||||||
is(element.style.getPropertyValue("background-color"), "blue", "Re-enabling an earlier property shouldn't make it override a later property.");
|
return elementRule._applyingModifications;
|
||||||
is(elementRule.textProps[0], firstProp, "Rules shouldn't have switched places.");
|
}).then(() => {
|
||||||
is(elementRule.textProps[1], secondProp, "Rules shouldn't have switched places.");
|
is(element.style.getPropertyValue("background-color"), "blue", "Value should be set correctly after re-enabling");
|
||||||
|
|
||||||
firstProp.setValue("purple", "");
|
firstProp.setEnabled(true);
|
||||||
is(element.style.getPropertyValue("background-color"), "blue", "Modifying an earlier property shouldn't override a later property.");
|
return elementRule._applyingModifications;
|
||||||
|
}).then(() => {
|
||||||
|
is(element.style.getPropertyValue("background-color"), "blue", "Re-enabling an earlier property shouldn't make it override a later property.");
|
||||||
|
is(elementRule.textProps[0], firstProp, "Rules shouldn't have switched places.");
|
||||||
|
is(elementRule.textProps[1], secondProp, "Rules shouldn't have switched places.");
|
||||||
|
|
||||||
finishTest();
|
firstProp.setValue("purple", "");
|
||||||
|
return elementRule._applyingModifications;
|
||||||
|
}).then(() => {
|
||||||
|
is(element.style.getPropertyValue("background-color"), "blue", "Modifying an earlier property shouldn't override a later property.");
|
||||||
|
finishTest();
|
||||||
|
}));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function finishTest()
|
function finishTest()
|
||||||
|
@ -61,7 +82,7 @@ function test()
|
||||||
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
|
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
|
||||||
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
|
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
|
||||||
doc = content.document;
|
doc = content.document;
|
||||||
waitForFocus(simpleOverride, content);
|
waitForFocus(() => openRuleView(simpleOverride), content);
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
content.location = "data:text/html,basic style inspector tests";
|
content.location = "data:text/html,basic style inspector tests";
|
||||||
|
|
|
@ -3,9 +3,13 @@
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
let doc;
|
let doc;
|
||||||
|
let inspector;
|
||||||
|
let view;
|
||||||
|
|
||||||
function simpleOverride()
|
function simpleOverride(aInspector, aRuleView)
|
||||||
{
|
{
|
||||||
|
inspector = aInspector;
|
||||||
|
view = aRuleView;
|
||||||
let style = '' +
|
let style = '' +
|
||||||
'#testid {' +
|
'#testid {' +
|
||||||
' background-color: blue;' +
|
' background-color: blue;' +
|
||||||
|
@ -17,31 +21,34 @@ function simpleOverride()
|
||||||
let styleNode = addStyle(doc, style);
|
let styleNode = addStyle(doc, style);
|
||||||
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
|
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
|
||||||
|
|
||||||
let elementStyle = new _ElementStyle(doc.getElementById("testid"));
|
inspector.selection.setNode(doc.getElementById("testid"));
|
||||||
|
inspector.once("inspector-updated", () => {
|
||||||
|
let elementStyle = view._elementStyle;
|
||||||
|
|
||||||
let idRule = elementStyle.rules[1];
|
let idRule = elementStyle.rules[1];
|
||||||
let idProp = idRule.textProps[0];
|
let idProp = idRule.textProps[0];
|
||||||
is(idProp.name, "background-color", "First ID prop should be background-color");
|
is(idProp.name, "background-color", "First ID prop should be background-color");
|
||||||
ok(!idProp.overridden, "ID prop should not be overridden.");
|
ok(!idProp.overridden, "ID prop should not be overridden.");
|
||||||
|
|
||||||
let classRule = elementStyle.rules[2];
|
let classRule = elementStyle.rules[2];
|
||||||
let classProp = classRule.textProps[0];
|
let classProp = classRule.textProps[0];
|
||||||
is(classProp.name, "background-color", "First class prop should be background-color");
|
is(classProp.name, "background-color", "First class prop should be background-color");
|
||||||
ok(classProp.overridden, "Class property should be overridden.");
|
ok(classProp.overridden, "Class property should be overridden.");
|
||||||
|
|
||||||
// Override background-color by changing the element style.
|
// Override background-color by changing the element style.
|
||||||
let elementRule = elementStyle.rules[0];
|
let elementRule = elementStyle.rules[0];
|
||||||
elementRule.createProperty("background-color", "purple", "");
|
elementRule.createProperty("background-color", "purple", "");
|
||||||
let elementProp = elementRule.textProps[0];
|
promiseDone(elementRule._applyingModifications.then(() => {
|
||||||
is(classProp.name, "background-color", "First element prop should now be background-color");
|
let elementProp = elementRule.textProps[0];
|
||||||
|
is(classProp.name, "background-color", "First element prop should now be background-color");
|
||||||
|
ok(!elementProp.overridden, "Element style property should not be overridden");
|
||||||
|
ok(idProp.overridden, "ID property should be overridden");
|
||||||
|
ok(classProp.overridden, "Class property should be overridden");
|
||||||
|
|
||||||
ok(!elementProp.overridden, "Element style property should not be overridden");
|
styleNode.parentNode.removeChild(styleNode);
|
||||||
ok(idProp.overridden, "ID property should be overridden");
|
partialOverride();
|
||||||
ok(classProp.overridden, "Class property should be overridden");
|
}));
|
||||||
|
});
|
||||||
styleNode.parentNode.removeChild(styleNode);
|
|
||||||
|
|
||||||
partialOverride();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function partialOverride()
|
function partialOverride()
|
||||||
|
@ -59,22 +66,25 @@ function partialOverride()
|
||||||
let styleNode = addStyle(doc, style);
|
let styleNode = addStyle(doc, style);
|
||||||
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
|
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
|
||||||
|
|
||||||
let elementStyle = new _ElementStyle(doc.getElementById("testid"));
|
inspector.selection.setNode(doc.getElementById("testid"));
|
||||||
|
inspector.once("inspector-updated", () => {
|
||||||
|
let elementStyle = view._elementStyle;
|
||||||
|
|
||||||
let classRule = elementStyle.rules[2];
|
let classRule = elementStyle.rules[2];
|
||||||
let classProp = classRule.textProps[0];
|
let classProp = classRule.textProps[0];
|
||||||
ok(!classProp.overridden, "Class prop shouldn't be overridden, some props are still being used.");
|
ok(!classProp.overridden, "Class prop shouldn't be overridden, some props are still being used.");
|
||||||
for each (let computed in classProp.computed) {
|
for (let computed of classProp.computed) {
|
||||||
if (computed.name.indexOf("margin-left") == 0) {
|
if (computed.name.indexOf("margin-left") == 0) {
|
||||||
ok(computed.overridden, "margin-left props should be overridden.");
|
ok(computed.overridden, "margin-left props should be overridden.");
|
||||||
} else {
|
} else {
|
||||||
ok(!computed.overridden, "Non-margin-left props should not be overridden.");
|
ok(!computed.overridden, "Non-margin-left props should not be overridden.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
styleNode.parentNode.removeChild(styleNode);
|
styleNode.parentNode.removeChild(styleNode);
|
||||||
|
|
||||||
importantOverride();
|
importantOverride();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function importantOverride()
|
function importantOverride()
|
||||||
|
@ -91,24 +101,29 @@ function importantOverride()
|
||||||
let styleNode = addStyle(doc, style);
|
let styleNode = addStyle(doc, style);
|
||||||
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
|
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
|
||||||
|
|
||||||
let elementStyle = new _ElementStyle(doc.getElementById("testid"));
|
inspector.selection.setNode(doc.getElementById("testid"));
|
||||||
|
inspector.once("inspector-updated", () => {
|
||||||
|
let elementStyle = view._elementStyle;
|
||||||
|
|
||||||
let idRule = elementStyle.rules[1];
|
let idRule = elementStyle.rules[1];
|
||||||
let idProp = idRule.textProps[0];
|
let idProp = idRule.textProps[0];
|
||||||
ok(idProp.overridden, "Not-important rule should be overridden.");
|
ok(idProp.overridden, "Not-important rule should be overridden.");
|
||||||
|
|
||||||
let classRule = elementStyle.rules[2];
|
let classRule = elementStyle.rules[2];
|
||||||
let classProp = classRule.textProps[0];
|
let classProp = classRule.textProps[0];
|
||||||
ok(!classProp.overridden, "Important rule should not be overridden.");
|
ok(!classProp.overridden, "Important rule should not be overridden.");
|
||||||
|
|
||||||
styleNode.parentNode.removeChild(styleNode);
|
styleNode.parentNode.removeChild(styleNode);
|
||||||
|
|
||||||
let elementRule = elementStyle.rules[0];
|
let elementRule = elementStyle.rules[0];
|
||||||
let elementProp = elementRule.createProperty("background-color", "purple", "important");
|
let elementProp = elementRule.createProperty("background-color", "purple", "important");
|
||||||
ok(classProp.overridden, "New important prop should override class property.");
|
promiseDone(elementRule._applyingModifications.then(() => {
|
||||||
ok(!elementProp.overridden, "New important prop should not be overriden.");
|
ok(classProp.overridden, "New important prop should override class property.");
|
||||||
|
ok(!elementProp.overridden, "New important prop should not be overriden.");
|
||||||
|
|
||||||
disableOverride();
|
disableOverride();
|
||||||
|
}));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function disableOverride()
|
function disableOverride()
|
||||||
|
@ -123,19 +138,23 @@ function disableOverride()
|
||||||
let styleNode = addStyle(doc, style);
|
let styleNode = addStyle(doc, style);
|
||||||
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
|
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
|
||||||
|
|
||||||
let elementStyle = new _ElementStyle(doc.getElementById("testid"));
|
inspector.selection.setNode(doc.getElementById("testid"));
|
||||||
|
inspector.once("inspector-updated", () => {
|
||||||
|
let elementStyle = view._elementStyle;
|
||||||
|
|
||||||
let idRule = elementStyle.rules[1];
|
let idRule = elementStyle.rules[1];
|
||||||
let idProp = idRule.textProps[0];
|
let idProp = idRule.textProps[0];
|
||||||
idProp.setEnabled(false);
|
idProp.setEnabled(false);
|
||||||
|
promiseDone(idRule._applyingModifications.then(() => {
|
||||||
|
let classRule = elementStyle.rules[2];
|
||||||
|
let classProp = classRule.textProps[0];
|
||||||
|
ok(!classProp.overridden, "Class prop should not be overridden after id prop was disabled.");
|
||||||
|
|
||||||
let classRule = elementStyle.rules[2];
|
styleNode.parentNode.removeChild(styleNode);
|
||||||
let classProp = classRule.textProps[0];
|
|
||||||
ok(!classProp.overridden, "Class prop should not be overridden after id prop was disabled.");
|
|
||||||
|
|
||||||
styleNode.parentNode.removeChild(styleNode);
|
finishTest();
|
||||||
|
}));
|
||||||
finishTest();
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function finishTest()
|
function finishTest()
|
||||||
|
@ -152,7 +171,7 @@ function test()
|
||||||
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
|
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
|
||||||
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
|
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
|
||||||
doc = content.document;
|
doc = content.document;
|
||||||
waitForFocus(simpleOverride, content);
|
waitForFocus(() => openRuleView(simpleOverride), content);
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
content.location = "data:text/html,basic style inspector tests";
|
content.location = "data:text/html,basic style inspector tests";
|
||||||
|
|
|
@ -3,23 +3,15 @@
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
let doc;
|
let doc;
|
||||||
let ruleDialog;
|
let inspector;
|
||||||
|
let ruleWindow;
|
||||||
let ruleView;
|
let ruleView;
|
||||||
|
|
||||||
var gRuleViewChanged = false;
|
function startTest(aInspector, aRuleView)
|
||||||
function ruleViewChanged()
|
|
||||||
{
|
|
||||||
gRuleViewChanged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function expectChange()
|
|
||||||
{
|
|
||||||
ok(gRuleViewChanged, "Rule view should have fired a change event.");
|
|
||||||
gRuleViewChanged = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function startTest()
|
|
||||||
{
|
{
|
||||||
|
inspector = aInspector;
|
||||||
|
ruleWindow = aRuleView.doc.defaultView;
|
||||||
|
ruleView = aRuleView;
|
||||||
let style = '' +
|
let style = '' +
|
||||||
'#testid {' +
|
'#testid {' +
|
||||||
' background-color: blue;' +
|
' background-color: blue;' +
|
||||||
|
@ -30,30 +22,24 @@ function startTest()
|
||||||
|
|
||||||
let styleNode = addStyle(doc, style);
|
let styleNode = addStyle(doc, style);
|
||||||
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
|
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
|
||||||
|
|
||||||
let testElement = doc.getElementById("testid");
|
let testElement = doc.getElementById("testid");
|
||||||
|
inspector.selection.setNode(testElement);
|
||||||
ruleDialog = openDialog("chrome://browser/content/devtools/cssruleview.xhtml",
|
inspector.once("inspector-updated", () => {
|
||||||
"cssruleviewtest",
|
|
||||||
"width=200,height=350");
|
|
||||||
ruleDialog.addEventListener("load", function onLoad(evt) {
|
|
||||||
ruleDialog.removeEventListener("load", onLoad);
|
|
||||||
let doc = ruleDialog.document;
|
|
||||||
ruleView = new CssRuleView(doc);
|
|
||||||
doc.documentElement.appendChild(ruleView.element);
|
|
||||||
ruleView.element.addEventListener("CssRuleViewChanged", ruleViewChanged, false);
|
|
||||||
is(ruleView.element.querySelectorAll("#noResults").length, 1, "Has a no-results element.");
|
|
||||||
ruleView.highlight(testElement);
|
|
||||||
is(ruleView.element.querySelectorAll("#noResults").length, 0, "After a highlight, no longer has a no-results element.");
|
is(ruleView.element.querySelectorAll("#noResults").length, 0, "After a highlight, no longer has a no-results element.");
|
||||||
ruleView.highlight(null);
|
inspector.selection.setNode(null);
|
||||||
is(ruleView.element.querySelectorAll("#noResults").length, 1, "After highlighting null, has a no-results element again.");
|
inspector.once("inspector-updated", () => {
|
||||||
ruleView.highlight(testElement);
|
is(ruleView.element.querySelectorAll("#noResults").length, 1, "After highlighting null, has a no-results element again.");
|
||||||
|
inspector.selection.setNode(testElement)
|
||||||
|
inspector.once("inspector-updated", () => {
|
||||||
|
let classEditor = ruleView.element.children[2]._ruleEditor;
|
||||||
|
is(classEditor.selectorText.querySelector(".ruleview-selector-matched").textContent, ".testclass", ".textclass should be matched.");
|
||||||
|
is(classEditor.selectorText.querySelector(".ruleview-selector-unmatched").textContent, ".unmatched", ".unmatched should not be matched.");
|
||||||
|
|
||||||
let classEditor = ruleView.element.children[2]._ruleEditor;
|
testCancelNew();
|
||||||
is(classEditor.selectorText.querySelector(".ruleview-selector-matched").textContent, ".testclass", ".textclass should be matched.");
|
});
|
||||||
is(classEditor.selectorText.querySelector(".ruleview-selector-unmatched").textContent, ".unmatched", ".unmatched should not be matched.");
|
});
|
||||||
|
});
|
||||||
waitForFocus(testCancelNew, ruleDialog);
|
|
||||||
}, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function testCancelNew()
|
function testCancelNew()
|
||||||
|
@ -66,7 +52,7 @@ function testCancelNew()
|
||||||
is(inplaceEditor(elementRuleEditor.newPropSpan), aEditor, "Next focused editor should be the new property editor.");
|
is(inplaceEditor(elementRuleEditor.newPropSpan), aEditor, "Next focused editor should be the new property editor.");
|
||||||
let input = aEditor.input;
|
let input = aEditor.input;
|
||||||
waitForEditorBlur(aEditor, function () {
|
waitForEditorBlur(aEditor, function () {
|
||||||
ok(!gRuleViewChanged, "Shouldn't get a change event after a cancel.");
|
ok(!elementRuleEditor.rule._applyingModifications, "Shouldn't have an outstanding modification request after a cancel.");
|
||||||
is(elementRuleEditor.rule.textProps.length, 0, "Should have canceled creating a new text property.");
|
is(elementRuleEditor.rule.textProps.length, 0, "Should have canceled creating a new text property.");
|
||||||
ok(!elementRuleEditor.propertyList.hasChildNodes(), "Should not have any properties.");
|
ok(!elementRuleEditor.propertyList.hasChildNodes(), "Should not have any properties.");
|
||||||
testCreateNew();
|
testCreateNew();
|
||||||
|
@ -76,7 +62,7 @@ function testCancelNew()
|
||||||
|
|
||||||
EventUtils.synthesizeMouse(elementRuleEditor.closeBrace, 1, 1,
|
EventUtils.synthesizeMouse(elementRuleEditor.closeBrace, 1, 1,
|
||||||
{ },
|
{ },
|
||||||
ruleDialog);
|
ruleWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testCreateNew()
|
function testCreateNew()
|
||||||
|
@ -91,33 +77,35 @@ function testCreateNew()
|
||||||
ok(input.selectionStart === 0 && input.selectionEnd === input.value.length, "Editor contents are selected.");
|
ok(input.selectionStart === 0 && input.selectionEnd === input.value.length, "Editor contents are selected.");
|
||||||
|
|
||||||
// Try clicking on the editor's input again, shouldn't cause trouble (see bug 761665).
|
// Try clicking on the editor's input again, shouldn't cause trouble (see bug 761665).
|
||||||
EventUtils.synthesizeMouse(input, 1, 1, { }, ruleDialog);
|
EventUtils.synthesizeMouse(input, 1, 1, { }, ruleWindow);
|
||||||
input.select();
|
input.select();
|
||||||
|
|
||||||
input.value = "background-color";
|
input.value = "background-color";
|
||||||
|
|
||||||
waitForEditorFocus(elementRuleEditor.element, function onNewValue(aEditor) {
|
waitForEditorFocus(elementRuleEditor.element, function onNewValue(aEditor) {
|
||||||
expectChange();
|
promiseDone(expectRuleChange(elementRuleEditor.rule).then(() => {
|
||||||
is(elementRuleEditor.rule.textProps.length, 1, "Should have created a new text property.");
|
is(elementRuleEditor.rule.textProps.length, 1, "Should have created a new text property.");
|
||||||
is(elementRuleEditor.propertyList.children.length, 1, "Should have created a property editor.");
|
is(elementRuleEditor.propertyList.children.length, 1, "Should have created a property editor.");
|
||||||
let textProp = elementRuleEditor.rule.textProps[0];
|
let textProp = elementRuleEditor.rule.textProps[0];
|
||||||
is(aEditor, inplaceEditor(textProp.editor.valueSpan), "Should be editing the value span now.");
|
is(aEditor, inplaceEditor(textProp.editor.valueSpan), "Should be editing the value span now.");
|
||||||
|
|
||||||
aEditor.input.value = "purple";
|
aEditor.input.value = "purple";
|
||||||
waitForEditorBlur(aEditor, function() {
|
waitForEditorBlur(aEditor, function() {
|
||||||
expectChange();
|
expectRuleChange(elementRuleEditor.rule).then(() => {
|
||||||
is(textProp.value, "purple", "Text prop should have been changed.");
|
is(textProp.value, "purple", "Text prop should have been changed.");
|
||||||
testEditProperty();
|
testEditProperty();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
aEditor.input.blur();
|
aEditor.input.blur();
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
EventUtils.sendKey("return", ruleDialog);
|
EventUtils.sendKey("return", ruleWindow);
|
||||||
});
|
});
|
||||||
|
|
||||||
EventUtils.synthesizeMouse(elementRuleEditor.closeBrace, 1, 1,
|
EventUtils.synthesizeMouse(elementRuleEditor.closeBrace, 1, 1,
|
||||||
{ },
|
{ },
|
||||||
ruleDialog);
|
ruleWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testEditProperty()
|
function testEditProperty()
|
||||||
|
@ -133,48 +121,50 @@ function testEditProperty()
|
||||||
ok(input.selectionStart === 0 && input.selectionEnd === input.value.length, "Editor contents are selected.");
|
ok(input.selectionStart === 0 && input.selectionEnd === input.value.length, "Editor contents are selected.");
|
||||||
|
|
||||||
// Try clicking on the editor's input again, shouldn't cause trouble (see bug 761665).
|
// Try clicking on the editor's input again, shouldn't cause trouble (see bug 761665).
|
||||||
EventUtils.synthesizeMouse(input, 1, 1, { }, ruleDialog);
|
EventUtils.synthesizeMouse(input, 1, 1, { }, ruleWindow);
|
||||||
input.select();
|
input.select();
|
||||||
|
|
||||||
waitForEditorFocus(propEditor.element, function onNewName(aEditor) {
|
waitForEditorFocus(propEditor.element, function onNewName(aEditor) {
|
||||||
expectChange();
|
promiseDone(expectRuleChange(idRuleEditor.rule).then(() => {
|
||||||
is(inplaceEditor(propEditor.valueSpan), aEditor, "Focus should have moved to the value.");
|
is(inplaceEditor(propEditor.valueSpan), aEditor, "Focus should have moved to the value.");
|
||||||
|
|
||||||
input = aEditor.input;
|
input = aEditor.input;
|
||||||
|
|
||||||
ok(input.selectionStart === 0 && input.selectionEnd === input.value.length, "Editor contents are selected.");
|
ok(input.selectionStart === 0 && input.selectionEnd === input.value.length, "Editor contents are selected.");
|
||||||
|
|
||||||
// Try clicking on the editor's input again, shouldn't cause trouble (see bug 761665).
|
// Try clicking on the editor's input again, shouldn't cause trouble (see bug 761665).
|
||||||
EventUtils.synthesizeMouse(input, 1, 1, { }, ruleDialog);
|
EventUtils.synthesizeMouse(input, 1, 1, { }, ruleWindow);
|
||||||
input.select();
|
input.select();
|
||||||
|
|
||||||
waitForEditorBlur(aEditor, function() {
|
waitForEditorBlur(aEditor, function() {
|
||||||
expectChange();
|
promiseDone(expectRuleChange(idRuleEditor.rule).then(() => {;
|
||||||
is(idRuleEditor.rule.style.getPropertyValue("border-color"), "red",
|
is(idRuleEditor.rule.style._rawStyle().getPropertyValue("border-color"), "red",
|
||||||
"border-color should have been set.");
|
"border-color should have been set.");
|
||||||
|
|
||||||
let props = ruleView.element.querySelectorAll(".ruleview-property");
|
let props = ruleView.element.querySelectorAll(".ruleview-property");
|
||||||
for (let i = 0; i < props.length; i++) {
|
for (let i = 0; i < props.length; i++) {
|
||||||
is(props[i].hasAttribute("dirty"), i <= 1,
|
is(props[i].hasAttribute("dirty"), i <= 1,
|
||||||
"props[" + i + "] marked dirty as appropriate");
|
"props[" + i + "] marked dirty as appropriate");
|
||||||
|
}
|
||||||
|
testDisableProperty();
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let ch of "red;") {
|
||||||
|
EventUtils.sendChar(ch, ruleWindow);
|
||||||
|
is(propEditor.warning.hidden, ch == "d" || ch == ";",
|
||||||
|
"warning triangle is hidden or shown as appropriate");
|
||||||
}
|
}
|
||||||
testDisableProperty();
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
for each (let ch in "red;") {
|
|
||||||
EventUtils.sendChar(ch, ruleDialog);
|
|
||||||
is(propEditor.warning.hidden, ch == "d" || ch == ";",
|
|
||||||
"warning triangle is hidden or shown as appropriate");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
for each (let ch in "border-color:") {
|
for (let ch of "border-color:") {
|
||||||
EventUtils.sendChar(ch, ruleDialog);
|
EventUtils.sendChar(ch, ruleWindow);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
EventUtils.synthesizeMouse(propEditor.nameSpan, 32, 1,
|
EventUtils.synthesizeMouse(propEditor.nameSpan, 32, 1,
|
||||||
{ },
|
{ },
|
||||||
ruleDialog);
|
ruleWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testDisableProperty()
|
function testDisableProperty()
|
||||||
|
@ -183,22 +173,22 @@ function testDisableProperty()
|
||||||
let propEditor = idRuleEditor.rule.textProps[0].editor;
|
let propEditor = idRuleEditor.rule.textProps[0].editor;
|
||||||
|
|
||||||
propEditor.enable.click();
|
propEditor.enable.click();
|
||||||
is(idRuleEditor.rule.style.getPropertyValue("border-color"), "", "Border-color should have been unset.");
|
promiseDone(expectRuleChange(idRuleEditor.rule).then(() => {
|
||||||
expectChange();
|
is(idRuleEditor.rule.style._rawStyle().getPropertyValue("border-color"), "", "Border-color should have been unset.");
|
||||||
|
|
||||||
propEditor.enable.click();
|
propEditor.enable.click();
|
||||||
is(idRuleEditor.rule.style.getPropertyValue("border-color"), "red",
|
return expectRuleChange(idRuleEditor.rule);
|
||||||
"Border-color should have been reset.");
|
}).then(() => {
|
||||||
expectChange();
|
is(idRuleEditor.rule.style._rawStyle().getPropertyValue("border-color"), "red",
|
||||||
|
"Border-color should have been reset.");
|
||||||
|
|
||||||
finishTest();
|
finishTest();
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function finishTest()
|
function finishTest()
|
||||||
{
|
{
|
||||||
ruleView.clear();
|
inspector = ruleWindow = ruleView = null;
|
||||||
ruleDialog.close();
|
|
||||||
ruleDialog = ruleView = null;
|
|
||||||
doc = null;
|
doc = null;
|
||||||
gBrowser.removeCurrentTab();
|
gBrowser.removeCurrentTab();
|
||||||
finish();
|
finish();
|
||||||
|
@ -211,7 +201,7 @@ function test()
|
||||||
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
|
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
|
||||||
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
|
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
|
||||||
doc = content.document;
|
doc = content.document;
|
||||||
waitForFocus(startTest, content);
|
waitForFocus(() => openRuleView(startTest), content);
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
content.location = "data:text/html,basic style inspector tests";
|
content.location = "data:text/html,basic style inspector tests";
|
||||||
|
|
|
@ -3,12 +3,15 @@
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
let doc;
|
let doc;
|
||||||
let ruleDialog;
|
|
||||||
|
let inspector;
|
||||||
let ruleView;
|
let ruleView;
|
||||||
let testElement;
|
let testElement;
|
||||||
|
|
||||||
function startTest()
|
function startTest(aInspector, aRuleView)
|
||||||
{
|
{
|
||||||
|
inspector = aInspector;
|
||||||
|
ruleView = aRuleView;
|
||||||
let style = '' +
|
let style = '' +
|
||||||
'#testid {' +
|
'#testid {' +
|
||||||
' background-color: blue;' +
|
' background-color: blue;' +
|
||||||
|
@ -19,21 +22,15 @@ function startTest()
|
||||||
|
|
||||||
let styleNode = addStyle(doc, style);
|
let styleNode = addStyle(doc, style);
|
||||||
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
|
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
|
||||||
|
|
||||||
testElement = doc.getElementById("testid");
|
testElement = doc.getElementById("testid");
|
||||||
|
|
||||||
let elementStyle = 'margin-top: 1px; padding-top: 5px;'
|
let elementStyle = 'margin-top: 1px; padding-top: 5px;'
|
||||||
testElement.setAttribute("style", elementStyle);
|
testElement.setAttribute("style", elementStyle);
|
||||||
|
|
||||||
ruleDialog = openDialog("chrome://browser/content/devtools/cssruleview.xhtml",
|
inspector.selection.setNode(testElement);
|
||||||
"cssruleviewtest",
|
inspector.once("inspector-updated", () => {
|
||||||
"width=200,height=350");
|
testRuleChanges();
|
||||||
ruleDialog.addEventListener("load", function onLoad(evt) {
|
|
||||||
ruleDialog.removeEventListener("load", onLoad);
|
|
||||||
let doc = ruleDialog.document;
|
|
||||||
ruleView = new CssRuleView(doc);
|
|
||||||
doc.documentElement.appendChild(ruleView.element);
|
|
||||||
ruleView.highlight(testElement);
|
|
||||||
waitForFocus(testRuleChanges, ruleDialog);
|
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,24 +44,24 @@ function testRuleChanges()
|
||||||
|
|
||||||
// Change the id and refresh.
|
// Change the id and refresh.
|
||||||
testElement.setAttribute("id", "differentid");
|
testElement.setAttribute("id", "differentid");
|
||||||
ruleView.nodeChanged();
|
promiseDone(ruleView.nodeChanged().then(() => {
|
||||||
|
let selectors = ruleView.doc.querySelectorAll(".ruleview-selector");
|
||||||
|
is(selectors.length, 2, "Two rules visible.");
|
||||||
|
is(selectors[0].textContent.indexOf("element"), 0, "First item is inline style.");
|
||||||
|
is(selectors[1].textContent.indexOf(".testclass"), 0, "Second item is class rule.");
|
||||||
|
|
||||||
let selectors = ruleView.doc.querySelectorAll(".ruleview-selector");
|
testElement.setAttribute("id", "testid");
|
||||||
is(selectors.length, 2, "Three rules visible.");
|
return ruleView.nodeChanged();
|
||||||
is(selectors[0].textContent.indexOf("element"), 0, "First item is inline style.");
|
}).then(() => {
|
||||||
is(selectors[1].textContent.indexOf(".testclass"), 0, "Second item is class rule.");
|
// Put the id back.
|
||||||
|
let selectors = ruleView.doc.querySelectorAll(".ruleview-selector");
|
||||||
|
is(selectors.length, 3, "Three rules visible.");
|
||||||
|
is(selectors[0].textContent.indexOf("element"), 0, "First item is inline style.");
|
||||||
|
is(selectors[1].textContent.indexOf("#testid"), 0, "Second item is id rule.");
|
||||||
|
is(selectors[2].textContent.indexOf(".testclass"), 0, "Third item is class rule.");
|
||||||
|
|
||||||
testElement.setAttribute("id", "testid");
|
testPropertyChanges();
|
||||||
ruleView.nodeChanged();
|
}));
|
||||||
|
|
||||||
// Put the id back.
|
|
||||||
let selectors = ruleView.doc.querySelectorAll(".ruleview-selector");
|
|
||||||
is(selectors.length, 3, "Three rules visible.");
|
|
||||||
is(selectors[0].textContent.indexOf("element"), 0, "First item is inline style.");
|
|
||||||
is(selectors[1].textContent.indexOf("#testid"), 0, "Second item is id rule.");
|
|
||||||
is(selectors[2].textContent.indexOf(".testclass"), 0, "Third item is class rule.");
|
|
||||||
|
|
||||||
testPropertyChanges();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateTextProp(aProp, aEnabled, aName, aValue, aDesc)
|
function validateTextProp(aProp, aEnabled, aName, aValue, aDesc)
|
||||||
|
@ -81,59 +78,64 @@ function validateTextProp(aProp, aEnabled, aName, aValue, aDesc)
|
||||||
function testPropertyChanges()
|
function testPropertyChanges()
|
||||||
{
|
{
|
||||||
// Add a second margin-top value, just to make things interesting.
|
// Add a second margin-top value, just to make things interesting.
|
||||||
|
let rule = ruleView._elementStyle.rules[0];
|
||||||
let ruleEditor = ruleView._elementStyle.rules[0].editor;
|
let ruleEditor = ruleView._elementStyle.rules[0].editor;
|
||||||
ruleEditor.addProperty("margin-top", "5px", "");
|
ruleEditor.addProperty("margin-top", "5px", "");
|
||||||
|
promiseDone(expectRuleChange(rule).then(() => {
|
||||||
|
// Set the element style back to a 1px margin-top.
|
||||||
|
testElement.setAttribute("style", "margin-top: 1px; padding-top: 5px");
|
||||||
|
return ruleView.nodeChanged();
|
||||||
|
}).then(() => {
|
||||||
|
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
|
||||||
|
validateTextProp(rule.textProps[0], true, "margin-top", "1px", "First margin property re-enabled");
|
||||||
|
validateTextProp(rule.textProps[2], false, "margin-top", "5px", "Second margin property disabled");
|
||||||
|
|
||||||
let rule = ruleView._elementStyle.rules[0];
|
// Now set it back to 5px, the 5px value should be re-enabled.
|
||||||
|
testElement.setAttribute("style", "margin-top: 5px; padding-top: 5px;");
|
||||||
|
return ruleView.nodeChanged();
|
||||||
|
|
||||||
// Set the element style back to a 1px margin-top.
|
}).then(() => {
|
||||||
testElement.setAttribute("style", "margin-top: 1px; padding-top: 5px");
|
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
|
||||||
ruleView.nodeChanged();
|
validateTextProp(rule.textProps[0], false, "margin-top", "1px", "First margin property re-enabled");
|
||||||
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
|
validateTextProp(rule.textProps[2], true, "margin-top", "5px", "Second margin property disabled");
|
||||||
validateTextProp(rule.textProps[0], true, "margin-top", "1px", "First margin property re-enabled");
|
|
||||||
validateTextProp(rule.textProps[2], false, "margin-top", "5px", "Second margin property disabled");
|
|
||||||
|
|
||||||
// Now set it back to 5px, the 5px value should be re-enabled.
|
// Set the margin property to a value that doesn't exist in the editor.
|
||||||
testElement.setAttribute("style", "margin-top: 5px; padding-top: 5px;");
|
// Should reuse the currently-enabled element (the second one.)
|
||||||
ruleView.nodeChanged();
|
testElement.setAttribute("style", "margin-top: 15px; padding-top: 5px;");
|
||||||
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
|
return ruleView.nodeChanged();
|
||||||
validateTextProp(rule.textProps[0], false, "margin-top", "1px", "First margin property re-enabled");
|
}).then(() => {
|
||||||
validateTextProp(rule.textProps[2], true, "margin-top", "5px", "Second margin property disabled");
|
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
|
||||||
|
validateTextProp(rule.textProps[0], false, "margin-top", "1px", "First margin property re-enabled");
|
||||||
|
validateTextProp(rule.textProps[2], true, "margin-top", "15px", "Second margin property disabled");
|
||||||
|
|
||||||
// Set the margin property to a value that doesn't exist in the editor.
|
// Remove the padding-top attribute. Should disable the padding property but not remove it.
|
||||||
// Should reuse the currently-enabled element (the second one.)
|
testElement.setAttribute("style", "margin-top: 5px;");
|
||||||
testElement.setAttribute("style", "margin-top: 15px; padding-top: 5px;");
|
return ruleView.nodeChanged();
|
||||||
ruleView.nodeChanged();
|
}).then(() => {
|
||||||
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
|
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
|
||||||
validateTextProp(rule.textProps[0], false, "margin-top", "1px", "First margin property re-enabled");
|
validateTextProp(rule.textProps[1], false, "padding-top", "5px", "Padding property disabled");
|
||||||
validateTextProp(rule.textProps[2], true, "margin-top", "15px", "Second margin property disabled");
|
|
||||||
|
|
||||||
// Remove the padding-top attribute. Should disable the padding property but not remove it.
|
// Put the padding-top attribute back in, should re-enable the padding property.
|
||||||
testElement.setAttribute("style", "margin-top: 5px;");
|
testElement.setAttribute("style", "margin-top: 5px; padding-top: 25px");
|
||||||
ruleView.nodeChanged();
|
return ruleView.nodeChanged();
|
||||||
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
|
}).then(() => {
|
||||||
validateTextProp(rule.textProps[1], false, "padding-top", "5px", "Padding property disabled");
|
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
|
||||||
|
validateTextProp(rule.textProps[1], true, "padding-top", "25px", "Padding property enabled");
|
||||||
|
|
||||||
// Put the padding-top attribute back in, should re-enable the padding property.
|
// Add an entirely new property.
|
||||||
testElement.setAttribute("style", "margin-top: 5px; padding-top: 25px");
|
testElement.setAttribute("style", "margin-top: 5px; padding-top: 25px; padding-left: 20px;");
|
||||||
ruleView.nodeChanged();
|
return ruleView.nodeChanged();
|
||||||
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
|
}).then(() => {
|
||||||
validateTextProp(rule.textProps[1], true, "padding-top", "25px", "Padding property enabled");
|
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 4, "Added a property");
|
||||||
|
validateTextProp(rule.textProps[3], true, "padding-left", "20px", "Padding property enabled");
|
||||||
|
|
||||||
// Add an entirely new property.
|
finishTest();
|
||||||
testElement.setAttribute("style", "margin-top: 5px; padding-top: 25px; padding-left: 20px;");
|
}));
|
||||||
ruleView.nodeChanged();
|
|
||||||
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 4, "Added a property");
|
|
||||||
validateTextProp(rule.textProps[3], true, "padding-left", "20px", "Padding property enabled");
|
|
||||||
|
|
||||||
finishTest();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function finishTest()
|
function finishTest()
|
||||||
{
|
{
|
||||||
ruleView.clear();
|
inspector = ruleView = null;
|
||||||
ruleDialog.close();
|
|
||||||
ruleDialog = ruleView = null;
|
|
||||||
doc = null;
|
doc = null;
|
||||||
gBrowser.removeCurrentTab();
|
gBrowser.removeCurrentTab();
|
||||||
finish();
|
finish();
|
||||||
|
@ -146,7 +148,7 @@ function test()
|
||||||
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
|
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
|
||||||
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
|
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
|
||||||
doc = content.document;
|
doc = content.document;
|
||||||
waitForFocus(startTest, content);
|
waitForFocus(() => openRuleView(startTest), content);
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
content.location = "data:text/html,basic style inspector tests";
|
content.location = "data:text/html,basic style inspector tests";
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
/* vim: set ts=2 et sw=2 tw=80: */
|
|
||||||
/* Any copyright is dedicated to the Public Domain.
|
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
||||||
|
|
||||||
// Tests that the style inspector works properly
|
|
||||||
|
|
||||||
let doc;
|
|
||||||
let inspector;
|
|
||||||
let computedView;
|
|
||||||
|
|
||||||
function createDocument()
|
|
||||||
{
|
|
||||||
doc.body.innerHTML = '<style type="text/css"> ' +
|
|
||||||
'span { font-variant: small-caps; color: #000000; } ' +
|
|
||||||
'.nomatches {color: #ff0000;}</style> <div id="first" style="margin: 10em; ' +
|
|
||||||
'font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA">\n' +
|
|
||||||
'<h1>Some header text</h1>\n' +
|
|
||||||
'<p id="salutation" style="font-size: 12pt">hi.</p>\n' +
|
|
||||||
'<p id="body" style="font-size: 12pt">I am a test-case. This text exists ' +
|
|
||||||
'solely to provide some things to <span style="color: yellow">' +
|
|
||||||
'highlight</span> and <span style="font-weight: bold">count</span> ' +
|
|
||||||
'style list-items in the box at right. If you are reading this, ' +
|
|
||||||
'you should go do something else instead. Maybe read a book. Or better ' +
|
|
||||||
'yet, write some test-cases for another bit of code. ' +
|
|
||||||
'<span style="font-style: italic">Maybe more inspector test-cases!</span></p>\n' +
|
|
||||||
'<p id="closing">end transmission</p>\n' +
|
|
||||||
'<p>Inspect using inspectstyle(document.querySelectorAll("span")[0])</p>' +
|
|
||||||
'</div>';
|
|
||||||
doc.title = "Style Inspector Test";
|
|
||||||
|
|
||||||
openInspector(openComputedView);
|
|
||||||
}
|
|
||||||
|
|
||||||
function openComputedView(aInspector)
|
|
||||||
{
|
|
||||||
inspector = aInspector;
|
|
||||||
|
|
||||||
inspector.sidebar.once("computedview-ready", function() {
|
|
||||||
computedView = getComputedView(inspector);
|
|
||||||
|
|
||||||
inspector.sidebar.select("computedview");
|
|
||||||
runStyleInspectorTests();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function runStyleInspectorTests()
|
|
||||||
{
|
|
||||||
var spans = doc.querySelectorAll("span");
|
|
||||||
ok(spans, "captain, we have the spans");
|
|
||||||
|
|
||||||
for (var i = 0, numSpans = spans.length; i < numSpans; i++) {
|
|
||||||
inspector.selection.setNode(spans[i]);
|
|
||||||
|
|
||||||
is(spans[i], computedView.viewedElement,
|
|
||||||
"style inspector node matches the selected node");
|
|
||||||
is(computedView.viewedElement, computedView.cssLogic.viewedElement,
|
|
||||||
"cssLogic node matches the cssHtmlTree node");
|
|
||||||
}
|
|
||||||
|
|
||||||
SI_CheckProperty();
|
|
||||||
finishUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
function SI_CheckProperty()
|
|
||||||
{
|
|
||||||
let cssLogic = computedView.cssLogic;
|
|
||||||
let propertyInfo = cssLogic.getPropertyInfo("color");
|
|
||||||
ok(propertyInfo.matchedRuleCount > 0, "color property has matching rules");
|
|
||||||
}
|
|
||||||
|
|
||||||
function finishUp()
|
|
||||||
{
|
|
||||||
doc = computedView = null;
|
|
||||||
gBrowser.removeCurrentTab();
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
function test()
|
|
||||||
{
|
|
||||||
waitForExplicitFinish();
|
|
||||||
gBrowser.selectedTab = gBrowser.addTab();
|
|
||||||
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
|
|
||||||
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
|
|
||||||
doc = content.document;
|
|
||||||
waitForFocus(createDocument, content);
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
content.location = "data:text/html,basic style inspector tests";
|
|
||||||
}
|
|
|
@ -16,24 +16,14 @@ function createDocument()
|
||||||
'</div>';
|
'</div>';
|
||||||
doc.title = "Style Inspector Search Filter Test";
|
doc.title = "Style Inspector Search Filter Test";
|
||||||
|
|
||||||
openInspector(openComputedView);
|
openComputedView(runStyleInspectorTests);
|
||||||
}
|
}
|
||||||
|
|
||||||
function openComputedView(aInspector)
|
function runStyleInspectorTests(aInspector, aComputedView)
|
||||||
{
|
{
|
||||||
inspector = aInspector;
|
inspector = aInspector;
|
||||||
|
computedView = aComputedView;
|
||||||
|
|
||||||
inspector.sidebar.once("computedview-ready", function() {
|
|
||||||
inspector.sidebar.select("computedview");
|
|
||||||
computedView = getComputedView(inspector);
|
|
||||||
|
|
||||||
runStyleInspectorTests();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function runStyleInspectorTests()
|
|
||||||
{
|
|
||||||
Services.obs.addObserver(SI_toggleDefaultStyles, "StyleInspector-populated", false);
|
|
||||||
SI_inspectNode();
|
SI_inspectNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,32 +33,28 @@ function SI_inspectNode()
|
||||||
ok(span, "captain, we have the matches span");
|
ok(span, "captain, we have the matches span");
|
||||||
|
|
||||||
inspector.selection.setNode(span);
|
inspector.selection.setNode(span);
|
||||||
|
inspector.once("inspector-updated", () => {
|
||||||
is(span, computedView.viewedElement,
|
is(span, computedView.viewedElement.rawNode(),
|
||||||
"style inspector node matches the selected node");
|
"style inspector node matches the selected node");
|
||||||
is(computedView.viewedElement, computedView.cssLogic.viewedElement,
|
SI_toggleDefaultStyles();
|
||||||
"cssLogic node matches the cssHtmlTree node");
|
}).then(null, (err) => console.error(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
function SI_toggleDefaultStyles()
|
function SI_toggleDefaultStyles()
|
||||||
{
|
{
|
||||||
Services.obs.removeObserver(SI_toggleDefaultStyles, "StyleInspector-populated");
|
|
||||||
|
|
||||||
info("checking \"Browser styles\" checkbox");
|
info("checking \"Browser styles\" checkbox");
|
||||||
|
|
||||||
let doc = computedView.styleDocument;
|
let doc = computedView.styleDocument;
|
||||||
let checkbox = doc.querySelector(".includebrowserstyles");
|
let checkbox = doc.querySelector(".includebrowserstyles");
|
||||||
Services.obs.addObserver(SI_AddFilterText, "StyleInspector-populated", false);
|
inspector.once("computed-view-refreshed", SI_AddFilterText);
|
||||||
checkbox.click();
|
checkbox.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
function SI_AddFilterText()
|
function SI_AddFilterText()
|
||||||
{
|
{
|
||||||
Services.obs.removeObserver(SI_AddFilterText, "StyleInspector-populated");
|
|
||||||
|
|
||||||
let doc = computedView.styleDocument;
|
let doc = computedView.styleDocument;
|
||||||
let searchbar = doc.querySelector(".devtools-searchinput");
|
let searchbar = doc.querySelector(".devtools-searchinput");
|
||||||
Services.obs.addObserver(SI_checkFilter, "StyleInspector-populated", false);
|
inspector.once("computed-view-refreshed", SI_checkFilter);
|
||||||
info("setting filter text to \"color\"");
|
info("setting filter text to \"color\"");
|
||||||
searchbar.focus();
|
searchbar.focus();
|
||||||
|
|
||||||
|
@ -82,7 +68,6 @@ function SI_AddFilterText()
|
||||||
|
|
||||||
function SI_checkFilter()
|
function SI_checkFilter()
|
||||||
{
|
{
|
||||||
Services.obs.removeObserver(SI_checkFilter, "StyleInspector-populated");
|
|
||||||
let propertyViews = computedView.propertyViews;
|
let propertyViews = computedView.propertyViews;
|
||||||
|
|
||||||
info("check that the correct properties are visible");
|
info("check that the correct properties are visible");
|
||||||
|
|
|
@ -16,43 +16,27 @@ function createDocument()
|
||||||
'</div>';
|
'</div>';
|
||||||
doc.title = "Style Inspector Default Styles Test";
|
doc.title = "Style Inspector Default Styles Test";
|
||||||
|
|
||||||
openInspector(openComputedView);
|
openComputedView(SI_inspectNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
function openComputedView(aInspector)
|
function SI_inspectNode(aInspector, aComputedView)
|
||||||
{
|
{
|
||||||
inspector = aInspector;
|
inspector = aInspector;
|
||||||
|
computedView = aComputedView;
|
||||||
|
|
||||||
inspector.sidebar.once("computedview-ready", function() {
|
|
||||||
inspector.sidebar.select("computedview");
|
|
||||||
computedView = getComputedView(inspector);
|
|
||||||
|
|
||||||
runStyleInspectorTests();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function runStyleInspectorTests()
|
|
||||||
{
|
|
||||||
Services.obs.addObserver(SI_check, "StyleInspector-populated", false);
|
|
||||||
SI_inspectNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
function SI_inspectNode()
|
|
||||||
{
|
|
||||||
let span = doc.querySelector("#matches");
|
let span = doc.querySelector("#matches");
|
||||||
ok(span, "captain, we have the matches span");
|
ok(span, "captain, we have the matches span");
|
||||||
|
|
||||||
inspector.selection.setNode(span);
|
inspector.selection.setNode(span);
|
||||||
|
inspector.once("inspector-updated", () => {
|
||||||
is(span, computedView.viewedElement,
|
is(span, computedView.viewedElement.rawNode(),
|
||||||
"style inspector node matches the selected node");
|
"style inspector node matches the selected node");
|
||||||
is(computedView.viewedElement, computedView.cssLogic.viewedElement,
|
SI_check();
|
||||||
"cssLogic node matches the cssHtmlTree node");
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function SI_check()
|
function SI_check()
|
||||||
{
|
{
|
||||||
Services.obs.removeObserver(SI_check, "StyleInspector-populated");
|
|
||||||
is(propertyVisible("color"), true,
|
is(propertyVisible("color"), true,
|
||||||
"span #matches color property is visible");
|
"span #matches color property is visible");
|
||||||
is(propertyVisible("background-color"), false,
|
is(propertyVisible("background-color"), false,
|
||||||
|
@ -66,14 +50,13 @@ function SI_toggleDefaultStyles()
|
||||||
// Click on the checkbox.
|
// Click on the checkbox.
|
||||||
let doc = computedView.styleDocument;
|
let doc = computedView.styleDocument;
|
||||||
let checkbox = doc.querySelector(".includebrowserstyles");
|
let checkbox = doc.querySelector(".includebrowserstyles");
|
||||||
Services.obs.addObserver(SI_checkDefaultStyles, "StyleInspector-populated", false);
|
inspector.once("computed-view-refreshed", SI_checkDefaultStyles);
|
||||||
|
|
||||||
checkbox.click();
|
checkbox.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
function SI_checkDefaultStyles()
|
function SI_checkDefaultStyles()
|
||||||
{
|
{
|
||||||
Services.obs.removeObserver(SI_checkDefaultStyles, "StyleInspector-populated");
|
|
||||||
// Check that the default styles are now applied.
|
// Check that the default styles are now applied.
|
||||||
is(propertyVisible("color"), true,
|
is(propertyVisible("color"), true,
|
||||||
"span color property is visible");
|
"span color property is visible");
|
||||||
|
|
|
@ -15,22 +15,11 @@ const TEST_URI = BASE_URL +
|
||||||
const TEST_IMAGE = BASE_URL + "test-image.png";
|
const TEST_IMAGE = BASE_URL + "test-image.png";
|
||||||
const BASE_64_URL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
|
const BASE_64_URL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
|
||||||
|
|
||||||
function createDocument()
|
function selectNode(aInspector, aRuleView)
|
||||||
{
|
|
||||||
doc.title = "Style Inspector URL Clickable test";
|
|
||||||
|
|
||||||
openInspector(function(aInspector) {
|
|
||||||
inspector = aInspector;
|
|
||||||
executeSoon(selectNode);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function selectNode(aInspector)
|
|
||||||
{
|
{
|
||||||
|
inspector = aInspector;
|
||||||
let sidebar = inspector.sidebar;
|
let sidebar = inspector.sidebar;
|
||||||
let iframe = sidebar._tabbox.querySelector(".iframe-ruleview");
|
let contentDoc = aRuleView.doc;
|
||||||
let contentDoc = iframe.contentWindow.document;
|
|
||||||
|
|
||||||
let relative = doc.querySelector(".relative");
|
let relative = doc.querySelector(".relative");
|
||||||
let absolute = doc.querySelector(".absolute");
|
let absolute = doc.querySelector(".absolute");
|
||||||
|
@ -45,35 +34,44 @@ function selectNode(aInspector)
|
||||||
ok(noimage, "captain, we have the noimage div");
|
ok(noimage, "captain, we have the noimage div");
|
||||||
|
|
||||||
inspector.selection.setNode(relative);
|
inspector.selection.setNode(relative);
|
||||||
is(inspector.selection.node, relative, "selection matches the relative element");
|
inspector.once("inspector-updated", () => {
|
||||||
let relativeLink = contentDoc.querySelector(".ruleview-propertycontainer a");
|
is(inspector.selection.node, relative, "selection matches the relative element");
|
||||||
ok (relativeLink, "Link exists for relative node");
|
let relativeLink = contentDoc.querySelector(".ruleview-propertycontainer a");
|
||||||
ok (relativeLink.getAttribute("href"), TEST_IMAGE);
|
ok (relativeLink, "Link exists for relative node");
|
||||||
|
ok (relativeLink.getAttribute("href"), TEST_IMAGE);
|
||||||
|
|
||||||
inspector.selection.setNode(absolute);
|
inspector.selection.setNode(absolute);
|
||||||
is(inspector.selection.node, absolute, "selection matches the absolute element");
|
inspector.once("inspector-updated", () => {
|
||||||
let absoluteLink = contentDoc.querySelector(".ruleview-propertycontainer a");
|
is(inspector.selection.node, absolute, "selection matches the absolute element");
|
||||||
ok (absoluteLink, "Link exists for absolute node");
|
let absoluteLink = contentDoc.querySelector(".ruleview-propertycontainer a");
|
||||||
ok (absoluteLink.getAttribute("href"), TEST_IMAGE);
|
ok (absoluteLink, "Link exists for absolute node");
|
||||||
|
ok (absoluteLink.getAttribute("href"), TEST_IMAGE);
|
||||||
|
|
||||||
inspector.selection.setNode(inline);
|
inspector.selection.setNode(inline);
|
||||||
is(inspector.selection.node, inline, "selection matches the inline element");
|
inspector.once("inspector-updated", () => {
|
||||||
let inlineLink = contentDoc.querySelector(".ruleview-propertycontainer a");
|
is(inspector.selection.node, inline, "selection matches the inline element");
|
||||||
ok (inlineLink, "Link exists for inline node");
|
let inlineLink = contentDoc.querySelector(".ruleview-propertycontainer a");
|
||||||
ok (inlineLink.getAttribute("href"), TEST_IMAGE);
|
ok (inlineLink, "Link exists for inline node");
|
||||||
|
ok (inlineLink.getAttribute("href"), TEST_IMAGE);
|
||||||
|
|
||||||
inspector.selection.setNode(base64);
|
inspector.selection.setNode(base64);
|
||||||
is(inspector.selection.node, base64, "selection matches the base64 element");
|
inspector.once("inspector-updated", () => {
|
||||||
let base64Link = contentDoc.querySelector(".ruleview-propertycontainer a");
|
is(inspector.selection.node, base64, "selection matches the base64 element");
|
||||||
ok (base64Link, "Link exists for base64 node");
|
let base64Link = contentDoc.querySelector(".ruleview-propertycontainer a");
|
||||||
ok (base64Link.getAttribute("href"), BASE_64_URL);
|
ok (base64Link, "Link exists for base64 node");
|
||||||
|
ok (base64Link.getAttribute("href"), BASE_64_URL);
|
||||||
|
|
||||||
inspector.selection.setNode(noimage);
|
inspector.selection.setNode(noimage);
|
||||||
is(inspector.selection.node, noimage, "selection matches the inline element");
|
inspector.once("inspector-updated", () => {
|
||||||
let noimageLink = contentDoc.querySelector(".ruleview-propertycontainer a");
|
is(inspector.selection.node, noimage, "selection matches the inline element");
|
||||||
ok (!noimageLink, "There is no link for the node with no background image");
|
let noimageLink = contentDoc.querySelector(".ruleview-propertycontainer a");
|
||||||
|
ok (!noimageLink, "There is no link for the node with no background image");
|
||||||
finishUp();
|
finishUp();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function finishUp()
|
function finishUp()
|
||||||
|
@ -90,7 +88,7 @@ function test()
|
||||||
gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
|
gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
|
||||||
gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
|
gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
|
||||||
doc = content.document;
|
doc = content.document;
|
||||||
waitForFocus(createDocument, content);
|
waitForFocus(() => openRuleView(selectNode), content);
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
content.location = TEST_URI;
|
content.location = TEST_URI;
|
||||||
|
|
|
@ -15,45 +15,33 @@ function createDocument()
|
||||||
'<span id="matches" class="matches">Some styled text</span>';
|
'<span id="matches" class="matches">Some styled text</span>';
|
||||||
doc.title = "Tests that the no results placeholder works properly";
|
doc.title = "Tests that the no results placeholder works properly";
|
||||||
|
|
||||||
openInspector(openComputedView);
|
openComputedView(runStyleInspectorTests);
|
||||||
}
|
}
|
||||||
|
|
||||||
function openComputedView(aInspector)
|
function runStyleInspectorTests(aInspector, aComputedView)
|
||||||
{
|
{
|
||||||
inspector = aInspector;
|
inspector = aInspector;
|
||||||
|
computedView = aComputedView;
|
||||||
inspector.sidebar.once("computedview-ready", function() {
|
|
||||||
inspector.sidebar.select("computedview");
|
|
||||||
computedView = getComputedView(inspector);
|
|
||||||
|
|
||||||
runStyleInspectorTests();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function runStyleInspectorTests()
|
|
||||||
{
|
|
||||||
Services.obs.addObserver(SI_AddFilterText, "StyleInspector-populated", false);
|
|
||||||
|
|
||||||
let span = doc.querySelector("#matches");
|
let span = doc.querySelector("#matches");
|
||||||
ok(span, "captain, we have the matches span");
|
ok(span, "captain, we have the matches span");
|
||||||
|
|
||||||
inspector.selection.setNode(span);
|
inspector.selection.setNode(span);
|
||||||
|
inspector.once("inspector-updated", () => {
|
||||||
|
is(span, computedView.viewedElement.rawNode(),
|
||||||
|
"style inspector node matches the selected node");
|
||||||
|
SI_AddFilterText();
|
||||||
|
});
|
||||||
|
|
||||||
is(span, computedView.viewedElement,
|
|
||||||
"style inspector node matches the selected node");
|
|
||||||
is(computedView.viewedElement, computedView.cssLogic.viewedElement,
|
|
||||||
"cssLogic node matches the cssHtmlTree node");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function SI_AddFilterText()
|
function SI_AddFilterText()
|
||||||
{
|
{
|
||||||
Services.obs.removeObserver(SI_AddFilterText, "StyleInspector-populated");
|
|
||||||
|
|
||||||
let searchbar = computedView.searchField;
|
let searchbar = computedView.searchField;
|
||||||
let searchTerm = "xxxxx";
|
let searchTerm = "xxxxx";
|
||||||
|
|
||||||
Services.obs.addObserver(SI_checkPlaceholderVisible, "StyleInspector-populated", false);
|
inspector.once("computed-view-refreshed", SI_checkPlaceholderVisible);
|
||||||
|
|
||||||
info("setting filter text to \"" + searchTerm + "\"");
|
info("setting filter text to \"" + searchTerm + "\"");
|
||||||
searchbar.focus();
|
searchbar.focus();
|
||||||
for each (let c in searchTerm) {
|
for each (let c in searchTerm) {
|
||||||
|
@ -63,7 +51,6 @@ function SI_AddFilterText()
|
||||||
|
|
||||||
function SI_checkPlaceholderVisible()
|
function SI_checkPlaceholderVisible()
|
||||||
{
|
{
|
||||||
Services.obs.removeObserver(SI_checkPlaceholderVisible, "StyleInspector-populated");
|
|
||||||
info("SI_checkPlaceholderVisible called");
|
info("SI_checkPlaceholderVisible called");
|
||||||
let placeholder = computedView.noResults;
|
let placeholder = computedView.noResults;
|
||||||
let win = computedView.styleWindow;
|
let win = computedView.styleWindow;
|
||||||
|
@ -78,7 +65,7 @@ function SI_ClearFilterText()
|
||||||
{
|
{
|
||||||
let searchbar = computedView.searchField;
|
let searchbar = computedView.searchField;
|
||||||
|
|
||||||
Services.obs.addObserver(SI_checkPlaceholderHidden, "StyleInspector-populated", false);
|
inspector.once("computed-view-refreshed", SI_checkPlaceholderHidden);
|
||||||
info("clearing filter text");
|
info("clearing filter text");
|
||||||
searchbar.focus();
|
searchbar.focus();
|
||||||
searchbar.value = "";
|
searchbar.value = "";
|
||||||
|
@ -87,7 +74,6 @@ function SI_ClearFilterText()
|
||||||
|
|
||||||
function SI_checkPlaceholderHidden()
|
function SI_checkPlaceholderHidden()
|
||||||
{
|
{
|
||||||
Services.obs.removeObserver(SI_checkPlaceholderHidden, "StyleInspector-populated");
|
|
||||||
let placeholder = computedView.noResults;
|
let placeholder = computedView.noResults;
|
||||||
let win = computedView.styleWindow;
|
let win = computedView.styleWindow;
|
||||||
let display = win.getComputedStyle(placeholder).display;
|
let display = win.getComputedStyle(placeholder).display;
|
||||||
|
|
|
@ -3,7 +3,13 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
Services.prefs.setBoolPref("devtools.debugger.log", true);
|
||||||
|
SimpleTest.registerCleanupFunction(() => {
|
||||||
|
Services.prefs.clearUserPref("devtools.debugger.log");
|
||||||
|
});
|
||||||
|
|
||||||
let tempScope = {};
|
let tempScope = {};
|
||||||
|
|
||||||
Cu.import("resource:///modules/devtools/gDevTools.jsm", tempScope);
|
Cu.import("resource:///modules/devtools/gDevTools.jsm", tempScope);
|
||||||
let ConsoleUtils = tempScope.ConsoleUtils;
|
let ConsoleUtils = tempScope.ConsoleUtils;
|
||||||
let gDevTools = tempScope.gDevTools;
|
let gDevTools = tempScope.gDevTools;
|
||||||
|
@ -16,6 +22,8 @@ let {CssHtmlTree} = devtools.require("devtools/styleinspector/computed-view");
|
||||||
let {CssRuleView, _ElementStyle} = devtools.require("devtools/styleinspector/rule-view");
|
let {CssRuleView, _ElementStyle} = devtools.require("devtools/styleinspector/rule-view");
|
||||||
let {CssLogic, CssSelector} = devtools.require("devtools/styleinspector/css-logic");
|
let {CssLogic, CssSelector} = devtools.require("devtools/styleinspector/css-logic");
|
||||||
|
|
||||||
|
let promise = devtools.require("sdk/core/promise");
|
||||||
|
|
||||||
let {
|
let {
|
||||||
editableField,
|
editableField,
|
||||||
getInplaceEditorForSpan: inplaceEditor
|
getInplaceEditorForSpan: inplaceEditor
|
||||||
|
@ -40,6 +48,28 @@ function openInspector(callback)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openRuleView(callback)
|
||||||
|
{
|
||||||
|
openInspector(inspector => {
|
||||||
|
inspector.sidebar.once("ruleview-ready", () => {
|
||||||
|
inspector.sidebar.select("ruleview");
|
||||||
|
let ruleView = inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
|
||||||
|
callback(inspector, ruleView);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function openComputedView(callback)
|
||||||
|
{
|
||||||
|
openInspector(inspector => {
|
||||||
|
inspector.sidebar.once("computedview-ready", () => {
|
||||||
|
inspector.sidebar.select("computedview");
|
||||||
|
let ruleView = inspector.sidebar.getWindowForTab("computedview").computedview.view;
|
||||||
|
callback(inspector, ruleView);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function addStyle(aDocument, aString)
|
function addStyle(aDocument, aString)
|
||||||
{
|
{
|
||||||
let node = aDocument.createElement('style');
|
let node = aDocument.createElement('style');
|
||||||
|
@ -120,6 +150,21 @@ function contextMenuClick(element) {
|
||||||
element.dispatchEvent(evt);
|
element.dispatchEvent(evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function expectRuleChange(rule) {
|
||||||
|
return rule._applyingModifications;
|
||||||
|
}
|
||||||
|
|
||||||
|
function promiseDone(promise) {
|
||||||
|
promise.then(null, err => {
|
||||||
|
ok(false, "Promise failed: " + err);
|
||||||
|
if (err.stack) {
|
||||||
|
dump(err.stack);
|
||||||
|
}
|
||||||
|
SimpleTest.finish();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
registerCleanupFunction(tearDown);
|
registerCleanupFunction(tearDown);
|
||||||
|
|
||||||
waitForExplicitFinish();
|
waitForExplicitFinish();
|
||||||
|
|
|
@ -177,14 +177,14 @@
|
||||||
- for the Copy URL menu item displayed in the context menu for a request -->
|
- for the Copy URL menu item displayed in the context menu for a request -->
|
||||||
<!ENTITY netmonitorUI.context.copyUrl.accesskey "C">
|
<!ENTITY netmonitorUI.context.copyUrl.accesskey "C">
|
||||||
|
|
||||||
<!-- LOCALIZATION NOTE (debuggerUI.summary.resend): This is the label displayed
|
<!-- LOCALIZATION NOTE (debuggerUI.summary.editAndResend): This is the label displayed
|
||||||
- on the button in the headers tab that opens a form to resend the currently
|
- on the button in the headers tab that opens a form to edit and resend the currently
|
||||||
displayed request -->
|
displayed request -->
|
||||||
<!ENTITY netmonitorUI.summary.resend "Resend">
|
<!ENTITY netmonitorUI.summary.editAndResend "Edit and Resend">
|
||||||
|
|
||||||
<!-- LOCALIZATION NOTE (debuggerUI.summary.resend.accesskey): This is the access key
|
<!-- LOCALIZATION NOTE (debuggerUI.summary.editAndResend.accesskey): This is the access key
|
||||||
- for the Resend menu item displayed in the context menu for a request -->
|
- for the "Edit and Resend" menu item displayed in the context menu for a request -->
|
||||||
<!ENTITY netmonitorUI.summary.resend.accesskey "R">
|
<!ENTITY netmonitorUI.summary.editAndResend.accesskey "R">
|
||||||
|
|
||||||
<!-- LOCALIZATION NOTE (debuggerUI.custom.newRequest): This is the label displayed
|
<!-- LOCALIZATION NOTE (debuggerUI.custom.newRequest): This is the label displayed
|
||||||
- as the title of the new custom request form -->
|
- as the title of the new custom request form -->
|
||||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче