Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2016-07-11 16:24:23 +02:00
commit 6139c05b2d
129 changed files with 1844 additions and 1346 deletions

View File

@ -699,7 +699,6 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
// previously set, revert to using an empty string by default.
this._cbTextbox.value = expr;
function openPopup() {
// Show the conditional expression panel. The popup arrow should be pointing
// at the line number node in the breakpoint item view.

View File

@ -9,6 +9,7 @@ support-files =
addon4.xpi
addon5.xpi
addon-webext-contentscript.xpi
addon-source/browser_dbg_addon5/*
code_binary_search.coffee
code_binary_search.js
code_binary_search.map

View File

@ -5,7 +5,8 @@
// Test that the we can see console messages from the add-on
const ADDON_URL = EXAMPLE_URL + "addon4.xpi";
const ADDON_ID = "browser_dbg_addon4@tests.mozilla.org";
const ADDON_PATH = "addon4.xpi";
function getCachedMessages(webConsole) {
let deferred = promise.defer();
@ -21,8 +22,8 @@ function getCachedMessages(webConsole) {
function test() {
Task.spawn(function* () {
let addon = yield addAddon(ADDON_URL);
let addonDebugger = yield initAddonDebugger(ADDON_URL);
let addon = yield addTemporaryAddon(ADDON_PATH);
let addonDebugger = yield initAddonDebugger(ADDON_ID);
let webConsole = addonDebugger.webConsole;
let messages = yield getCachedMessages(webConsole);

View File

@ -5,16 +5,19 @@
// Make sure the add-on actor can see loaded JS Modules from an add-on
const ADDON_URL = EXAMPLE_URL + "addon5.xpi";
const ADDON_ID = "browser_dbg_addon5@tests.mozilla.org";
const ADDON_PATH = "addon-source/browser_dbg_addon5/";
const ADDON_URL = getTemporaryAddonURLFromPath(ADDON_PATH);
function test() {
Task.spawn(function* () {
let addon = yield addAddon(ADDON_URL);
let addon = yield addTemporaryAddon(ADDON_PATH);
let tab1 = yield addTab("chrome://browser_dbg_addon5/content/test.xul");
let addonDebugger = yield initAddonDebugger(ADDON_URL);
let addonDebugger = yield initAddonDebugger(ADDON_ID);
is(addonDebugger.title, `Developer Tools - Test unpacked add-on with JS Modules - ${ADDON_URL}`,
is(addonDebugger.title,
`Developer Tools - Test unpacked add-on with JS Modules - ${ADDON_URL}`,
"Saw the right toolbox title.");
// Check the inital list of sources is correct
@ -25,7 +28,7 @@ function test() {
let sources = groups[0].sources;
is(sources.length, 3, "Should be three sources");
ok(sources[0].url.endsWith("/browser_dbg_addon5@tests.mozilla.org/bootstrap.js"), "correct url for bootstrap code");
ok(sources[0].url.endsWith("/browser_dbg_addon5/bootstrap.js"), "correct url for bootstrap code");
is(sources[0].label, "bootstrap.js", "correct label for bootstrap code");
is(sources[1].url, "resource://browser_dbg_addon5/test.jsm", "correct url for addon code");
is(sources[1].label, "test.jsm", "correct label for addon code");
@ -43,7 +46,7 @@ function test() {
sources = groups[0].sources;
is(sources.length, 5, "Should be five sources");
ok(sources[0].url.endsWith("/browser_dbg_addon5@tests.mozilla.org/bootstrap.js"), "correct url for bootstrap code");
ok(sources[0].url.endsWith("/browser_dbg_addon5/bootstrap.js"), "correct url for bootstrap code");
is(sources[0].label, "bootstrap.js", "correct label for bootstrap code");
is(sources[1].url, "resource://browser_dbg_addon5/test.jsm", "correct url for addon code");
is(sources[1].label, "test.jsm", "correct label for addon code");

View File

@ -5,14 +5,16 @@
// Make sure the add-on actor can see loaded JS Modules from an add-on
const ADDON_URL = EXAMPLE_URL + "addon4.xpi";
const ADDON_ID = "browser_dbg_addon4@tests.mozilla.org";
const ADDON_PATH = "addon4.xpi";
const ADDON_URL = getTemporaryAddonURLFromPath(ADDON_PATH);
function test() {
Task.spawn(function* () {
let addon = yield addAddon(ADDON_URL);
let addon = yield addTemporaryAddon(ADDON_PATH);
let tab1 = yield addTab("chrome://browser_dbg_addon4/content/test.xul");
let addonDebugger = yield initAddonDebugger(ADDON_URL);
let addonDebugger = yield initAddonDebugger(ADDON_ID);
is(addonDebugger.title, `Developer Tools - Test add-on with JS Modules - ${ADDON_URL}`,
"Saw the right toolbox title.");
@ -25,7 +27,7 @@ function test() {
let sources = groups[0].sources;
is(sources.length, 3, "Should be three sources");
ok(sources[0].url.endsWith("/browser_dbg_addon4@tests.mozilla.org.xpi!/bootstrap.js"), "correct url for bootstrap code");
ok(sources[0].url.endsWith("/addon4.xpi!/bootstrap.js"), "correct url for bootstrap code");
is(sources[0].label, "bootstrap.js", "correct label for bootstrap code");
is(sources[1].url, "resource://browser_dbg_addon4/test.jsm", "correct url for addon code");
is(sources[1].label, "test.jsm", "correct label for addon code");
@ -43,7 +45,7 @@ function test() {
sources = groups[0].sources;
is(sources.length, 5, "Should be five sources");
ok(sources[0].url.endsWith("/browser_dbg_addon4@tests.mozilla.org.xpi!/bootstrap.js"), "correct url for bootstrap code");
ok(sources[0].url.endsWith("/addon4.xpi!/bootstrap.js"), "correct url for bootstrap code");
is(sources[0].label, "bootstrap.js", "correct label for bootstrap code");
is(sources[1].url, "resource://browser_dbg_addon4/test.jsm", "correct url for addon code");
is(sources[1].label, "test.jsm", "correct label for addon code");

View File

@ -6,7 +6,8 @@
// Ensure that only panels that are relevant to the addon debugger
// display in the toolbox
const ADDON_URL = EXAMPLE_URL + "addon3.xpi";
const ADDON_ID = "jid1-ami3akps3baaeg@jetpack";
const ADDON_PATH = "addon3.xpi";
var gAddon, gClient, gThreadClient, gDebugger, gSources;
var PREFS = [
@ -21,8 +22,8 @@ function test() {
// Store and enable all optional dev tools panels
yield pushPrefs(...PREFS);
let addon = yield addAddon(ADDON_URL);
let addonDebugger = yield initAddonDebugger(ADDON_URL);
let addon = yield addTemporaryAddon(ADDON_PATH);
let addonDebugger = yield initAddonDebugger(ADDON_ID);
// Check only valid tabs are shown
let tabs = addonDebugger.frame.contentDocument.getElementById("toolbox-tabs").children;

View File

@ -6,13 +6,16 @@
// Ensure that the sources listed when debugging an addon are either from the
// addon itself, or the SDK, with proper groups and labels.
const ADDON_URL = EXAMPLE_URL + "addon3.xpi";
const ADDON_ID = "jid1-ami3akps3baaeg@jetpack";
const ADDON_PATH = "addon3.xpi";
const ADDON_URL = getTemporaryAddonURLFromPath(ADDON_PATH);
var gClient;
function test() {
Task.spawn(function* () {
let addon = yield addAddon(ADDON_URL);
let addonDebugger = yield initAddonDebugger(ADDON_URL);
let addon = yield addTemporaryAddon(ADDON_PATH);
let addonDebugger = yield initAddonDebugger(ADDON_ID);
is(addonDebugger.title, `Developer Tools - browser_dbg_addon3 - ${ADDON_URL}`,
"Saw the right toolbox title.");
@ -25,7 +28,7 @@ function test() {
let sources = groups[0].sources;
is(sources.length, 2, "Should be two sources");
ok(sources[0].url.endsWith("/jid1-ami3akps3baaeg@jetpack.xpi!/bootstrap.js"), "correct url for bootstrap code");
ok(sources[0].url.endsWith("/addon3.xpi!/bootstrap.js"), "correct url for bootstrap code");
is(sources[0].label, "bootstrap.js", "correct label for bootstrap code");
is(sources[1].url, "resource://jid1-ami3akps3baaeg-at-jetpack/browser_dbg_addon3/lib/main.js", "correct url for add-on code");
is(sources[1].label, "resources/browser_dbg_addon3/lib/main.js", "correct label for add-on code");

View File

@ -8,7 +8,9 @@
// Test that the Addon Debugger works when devtools.debugger.workers is enabled.
// Workers controller cannot be used when debugging an Addon actor.
const ADDON_URL = EXAMPLE_URL + "addon3.xpi";
const ADDON_ID = "jid1-ami3akps3baaeg@jetpack";
const ADDON_PATH = "addon3.xpi";
const ADDON_URL = getTemporaryAddonURLFromPath(ADDON_PATH);
function test() {
Task.spawn(function* () {
@ -19,8 +21,8 @@ function test() {
}, resolve);
});
let addon = yield addAddon(ADDON_URL);
let addonDebugger = yield initAddonDebugger(ADDON_URL);
let addon = yield addTemporaryAddon(ADDON_PATH);
let addonDebugger = yield initAddonDebugger(ADDON_ID);
is(addonDebugger.title,
`Developer Tools - browser_dbg_addon3 - ${ADDON_URL}`,

View File

@ -5,7 +5,8 @@
// Make sure we can attach to addon actors.
const ADDON3_URL = EXAMPLE_URL + "addon3.xpi";
const ADDON3_PATH = "addon3.xpi";
const ADDON3_ID = "jid1-ami3akps3baaeg@jetpack";
const ADDON_MODULE_URL = "resource://jid1-ami3akps3baaeg-at-jetpack/browser_dbg_addon3/lib/main.js";
var gAddon, gClient, gThreadClient;
@ -23,7 +24,7 @@ function test() {
"Root actor should identify itself as a browser.");
installAddon()
.then(attachAddonActorForUrl.bind(null, gClient, ADDON3_URL))
.then(attachAddonActorForId.bind(null, gClient, ADDON3_ID))
.then(attachAddonThread)
.then(testDebugger)
.then(testSources)
@ -37,13 +38,13 @@ function test() {
}
function installAddon() {
return addAddon(ADDON3_URL).then(aAddon => {
return addTemporaryAddon(ADDON3_PATH).then(aAddon => {
gAddon = aAddon;
});
}
function attachAddonThread([aGrip, aResponse]) {
info("attached addon actor for URL");
info("attached addon actor for Addon ID");
let deferred = promise.defer();
gClient.attachThread(aResponse.threadActor, (aResponse, aThreadClient) => {

View File

@ -7,7 +7,6 @@
* Check extension-added global actor API.
*/
const CHROME_URL = "chrome://mochitests/content/browser/devtools/client/debugger/test/mochitest/";
const ACTORS_URL = CHROME_URL + "testactors.js";
function test() {

View File

@ -6,8 +6,10 @@
/**
* Make sure the listAddons request works as specified.
*/
const ADDON1_URL = EXAMPLE_URL + "addon1.xpi";
const ADDON2_URL = EXAMPLE_URL + "addon2.xpi";
const ADDON1_ID = "jid1-oBAwBoE5rSecNg@jetpack";
const ADDON1_PATH = "addon1.xpi";
const ADDON2_ID = "jid1-qjtzNGV8xw5h2A@jetpack";
const ADDON2_PATH = "addon2.xpi";
var gAddon1, gAddon1Actor, gAddon2, gAddon2Actor, gClient;
@ -42,10 +44,10 @@ function testFirstAddon() {
addonListChanged = true;
});
return addAddon(ADDON1_URL).then(aAddon => {
return addTemporaryAddon(ADDON1_PATH).then(aAddon => {
gAddon1 = aAddon;
return getAddonActorForUrl(gClient, ADDON1_URL).then(aGrip => {
return getAddonActorForId(gClient, ADDON1_ID).then(aGrip => {
ok(!addonListChanged, "Should not yet be notified that list of addons changed.");
ok(aGrip, "Should find an addon actor for addon1.");
gAddon1Actor = aGrip.actor;
@ -59,11 +61,11 @@ function testSecondAddon() {
addonListChanged = true;
});
return addAddon(ADDON2_URL).then(aAddon => {
return addTemporaryAddon(ADDON2_PATH).then(aAddon => {
gAddon2 = aAddon;
return getAddonActorForUrl(gClient, ADDON1_URL).then(aFirstGrip => {
return getAddonActorForUrl(gClient, ADDON2_URL).then(aSecondGrip => {
return getAddonActorForId(gClient, ADDON1_ID).then(aFirstGrip => {
return getAddonActorForId(gClient, ADDON2_ID).then(aSecondGrip => {
ok(addonListChanged, "Should be notified that list of addons changed.");
is(aFirstGrip.actor, gAddon1Actor, "First addon's actor shouldn't have changed.");
ok(aSecondGrip, "Should find a addon actor for the second addon.");
@ -79,8 +81,8 @@ function testRemoveFirstAddon() {
addonListChanged = true;
});
removeAddon(gAddon1).then(() => {
return getAddonActorForUrl(gClient, ADDON1_URL).then(aGrip => {
return removeAddon(gAddon1).then(() => {
return getAddonActorForId(gClient, ADDON1_ID).then(aGrip => {
ok(addonListChanged, "Should be notified that list of addons changed.");
ok(!aGrip, "Shouldn't find a addon actor for the first addon anymore.");
});
@ -93,8 +95,8 @@ function testRemoveSecondAddon() {
addonListChanged = true;
});
removeAddon(gAddon2).then(() => {
return getAddonActorForUrl(gClient, ADDON2_URL).then(aGrip => {
return removeAddon(gAddon2).then(() => {
return getAddonActorForId(gClient, ADDON2_ID).then(aGrip => {
ok(addonListChanged, "Should be notified that list of addons changed.");
ok(!aGrip, "Shouldn't find a addon actor for the second addon anymore.");
});

View File

@ -7,6 +7,7 @@
* Make sure eval scripts appear in the source list
*/
const ADDON_PATH = "addon-webext-contentscript.xpi";
const TAB_URL = EXAMPLE_URL + "doc_script_webext_contentscript.html";
let {getExtensionUUID} = Cu.import("resource://gre/modules/Extension.jsm", {});
@ -30,7 +31,7 @@ function test() {
};
return Task.spawn(function* () {
gAddon = yield addAddon(EXAMPLE_URL + "/addon-webext-contentscript.xpi");
gAddon = yield addTemporaryAddon(ADDON_PATH);
let uuid = getExtensionUUID(gAddon.id);
let options = {

View File

@ -7,7 +7,6 @@
* Check extension-added tab actor lifetimes.
*/
const CHROME_URL = "chrome://mochitests/content/browser/devtools/client/debugger/test/mochitest/";
const ACTORS_URL = CHROME_URL + "testactors.js";
const TAB_URL = EXAMPLE_URL + "doc_empty-tab-01.html";

View File

@ -7,7 +7,6 @@
* Check extension-added tab actor lifetimes.
*/
const CHROME_URL = "chrome://mochitests/content/browser/devtools/client/debugger/test/mochitest/";
const ACTORS_URL = CHROME_URL + "testactors.js";
const TAB_URL = EXAMPLE_URL + "doc_empty-tab-01.html";

View File

@ -21,11 +21,15 @@ var { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
var EventEmitter = require("devtools/shared/event-emitter");
var { Toolbox } = require("devtools/client/framework/toolbox");
const chromeRegistry = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry);
// Override promise with deprecated-sync-thenables
promise = Cu.import("resource://devtools/shared/deprecated-sync-thenables.js", {}).Promise;
const EXAMPLE_URL = "http://example.com/browser/devtools/client/debugger/test/mochitest/";
const FRAME_SCRIPT_URL = getRootDirectory(gTestPath) + "code_frame-script.js";
const CHROME_URL = "chrome://mochitests/content/browser/devtools/client/debugger/test/mochitest/";
const CHROME_URI = Services.io.newURI(CHROME_URL, null, null);
registerCleanupFunction(function* () {
info("finish() was called, cleaning up...");
@ -111,27 +115,20 @@ this.removeTab = function removeTab(aTab, aWindow) {
return deferred.promise;
};
function addAddon(aUrl) {
info("Installing addon: " + aUrl);
function getAddonURIFromPath(aPath) {
let chromeURI = Services.io.newURI(aPath, null, CHROME_URI);
return chromeRegistry.convertChromeURL(chromeURI).QueryInterface(Ci.nsIFileURL);
}
let deferred = promise.defer();
function getTemporaryAddonURLFromPath(aPath) {
return getAddonURIFromPath(aPath).spec;
}
AddonManager.getInstallForURL(aUrl, aInstaller => {
aInstaller.install();
let listener = {
onInstallEnded: function (aAddon, aAddonInstall) {
aInstaller.removeListener(listener);
function addTemporaryAddon(aPath) {
let addonFile = getAddonURIFromPath(aPath).file;
info("Installing addon: " + addonFile.path);
// Wait for add-on's startup scripts to execute. See bug 997408
executeSoon(function () {
deferred.resolve(aAddonInstall);
});
}
};
aInstaller.addListener(listener);
}, "application/x-xpinstall");
return deferred.promise;
return AddonManager.installTemporaryAddon(addonFile);
}
function removeAddon(aAddon) {
@ -165,13 +162,13 @@ function getTabActorForUrl(aClient, aUrl) {
return deferred.promise;
}
function getAddonActorForUrl(aClient, aUrl) {
info("Get addon actor for URL: " + aUrl);
function getAddonActorForId(aClient, aAddonId) {
info("Get addon actor for ID: " + aAddonId);
let deferred = promise.defer();
aClient.listAddons(aResponse => {
let addonActor = aResponse.addons.filter(aGrip => aGrip.url == aUrl).pop();
info("got addon actor for URL: " + addonActor.actor);
let addonActor = aResponse.addons.filter(aGrip => aGrip.id == aAddonId).pop();
info("got addon actor for ID: " + aAddonId);
deferred.resolve(addonActor);
});
@ -614,9 +611,9 @@ let initDebugger = Task.async(function*(urlOrTab, options) {
// Creates an add-on debugger for a given add-on. The returned AddonDebugger
// object must be destroyed before finishing the test
function initAddonDebugger(aUrl) {
function initAddonDebugger(aAddonId) {
let addonDebugger = new AddonDebugger();
return addonDebugger.init(aUrl).then(() => addonDebugger);
return addonDebugger.init(aAddonId).then(() => addonDebugger);
}
function AddonDebugger() {
@ -626,7 +623,7 @@ function AddonDebugger() {
}
AddonDebugger.prototype = {
init: Task.async(function* (aUrl) {
init: Task.async(function* (aAddonId) {
info("Initializing an addon debugger panel.");
if (!DebuggerServer.initialized) {
@ -645,7 +642,7 @@ AddonDebugger.prototype = {
yield this.client.connect();
let addonActor = yield getAddonActorForUrl(this.client, aUrl);
let addonActor = yield getAddonActorForId(this.client, aAddonId);
let targetOptions = {
form: addonActor,
@ -951,10 +948,10 @@ function reopenVarPopup(...aArgs) {
return hideVarPopup.apply(this, aArgs).then(() => openVarPopup.apply(this, aArgs));
}
function attachAddonActorForUrl(aClient, aUrl) {
function attachAddonActorForId(aClient, aAddonId) {
let deferred = promise.defer();
getAddonActorForUrl(aClient, aUrl).then(aGrip => {
getAddonActorForId(aClient, aAddonId).then(aGrip => {
aClient.attachAddon(aGrip.actor, aResponse => {
deferred.resolve([aGrip, aResponse]);
});

View File

@ -79,7 +79,7 @@ InspectorSearch.prototype = {
let res = yield this.walker.search(query, { reverse });
// Value has changed since we started this request, we're done.
if (query != this.searchBox.value) {
if (query !== this.searchBox.value) {
return;
}
@ -142,21 +142,19 @@ function SelectorAutocompleter(inspector, inputNode) {
this.showSuggestions = this.showSuggestions.bind(this);
this._onSearchKeypress = this._onSearchKeypress.bind(this);
this._onListBoxKeypress = this._onListBoxKeypress.bind(this);
this._onSearchPopupClick = this._onSearchPopupClick.bind(this);
this._onMarkupMutation = this._onMarkupMutation.bind(this);
// Options for the AutocompletePopup.
let options = {
panelId: "inspector-searchbox-panel",
listBoxId: "searchbox-panel-listbox",
listId: "searchbox-panel-listbox",
autoSelect: true,
position: "before_start",
direction: "ltr",
position: "top",
theme: "auto",
onClick: this._onListBoxKeypress,
onKeypress: this._onListBoxKeypress
onClick: this._onSearchPopupClick,
};
this.searchPopup = new AutocompletePopup(this.panelDoc, options);
this.searchPopup = new AutocompletePopup(inspector._toolbox, options);
this.searchBox.addEventListener("input", this.showSuggestions, true);
this.searchBox.addEventListener("keypress", this._onSearchKeypress, true);
@ -231,11 +229,11 @@ SelectorAutocompleter.prototype = {
lastChar = secondLastChar;
case this.States.TAG: // eslint-disable-line
if (lastChar == ".") {
if (lastChar === ".") {
this._state = this.States.CLASS;
} else if (lastChar == "#") {
} else if (lastChar === "#") {
this._state = this.States.ID;
} else if (lastChar == "[") {
} else if (lastChar === "[") {
this._state = this.States.ATTRIBUTE;
} else {
this._state = this.States.TAG;
@ -246,11 +244,11 @@ SelectorAutocompleter.prototype = {
if (subQuery.match(/[\.]+[^\.]*$/)[0].length > 2) {
// Checks whether the subQuery has atleast one [a-zA-Z] after the
// '.'.
if (lastChar == " " || lastChar == ">") {
if (lastChar === " " || lastChar === ">") {
this._state = this.States.TAG;
} else if (lastChar == "#") {
} else if (lastChar === "#") {
this._state = this.States.ID;
} else if (lastChar == "[") {
} else if (lastChar === "[") {
this._state = this.States.ATTRIBUTE;
} else {
this._state = this.States.CLASS;
@ -262,11 +260,11 @@ SelectorAutocompleter.prototype = {
if (subQuery.match(/[#]+[^#]*$/)[0].length > 2) {
// Checks whether the subQuery has atleast one [a-zA-Z] after the
// '#'.
if (lastChar == " " || lastChar == ">") {
if (lastChar === " " || lastChar === ">") {
this._state = this.States.TAG;
} else if (lastChar == ".") {
} else if (lastChar === ".") {
this._state = this.States.CLASS;
} else if (lastChar == "[") {
} else if (lastChar === "[") {
this._state = this.States.ATTRIBUTE;
} else {
this._state = this.States.ID;
@ -275,13 +273,13 @@ SelectorAutocompleter.prototype = {
break;
case this.States.ATTRIBUTE:
if (subQuery.match(/[\[][^\]]+[\]]/) != null) {
if (subQuery.match(/[\[][^\]]+[\]]/) !== null) {
// Checks whether the subQuery has at least one ']' after the '['.
if (lastChar == " " || lastChar == ">") {
if (lastChar === " " || lastChar === ">") {
this._state = this.States.TAG;
} else if (lastChar == ".") {
} else if (lastChar === ".") {
this._state = this.States.CLASS;
} else if (lastChar == "#") {
} else if (lastChar === "#") {
this._state = this.States.ID;
} else {
this._state = this.States.ATTRIBUTE;
@ -311,20 +309,17 @@ SelectorAutocompleter.prototype = {
* Handles keypresses inside the input box.
*/
_onSearchKeypress: function (event) {
let query = this.searchBox.value;
let popup = this.searchPopup;
switch (event.keyCode) {
case event.DOM_VK_RETURN:
case event.DOM_VK_TAB:
if (popup.isOpen &&
popup.getItemAtIndex(popup.itemCount - 1)
.preLabel == query) {
popup.selectedIndex = popup.itemCount - 1;
this.searchBox.value = popup.selectedItem.label;
if (popup.isOpen) {
if (popup.selectedItem) {
this.searchBox.value = popup.selectedItem.label;
}
this.hidePopup();
} else if (!popup.isOpen &&
event.keyCode === event.DOM_VK_TAB) {
} else if (!popup.isOpen) {
// When tab is pressed with focus on searchbox and closed popup,
// do not prevent the default to avoid a keyboard trap and move focus
// to next/previous element.
@ -335,12 +330,10 @@ SelectorAutocompleter.prototype = {
case event.DOM_VK_UP:
if (popup.isOpen && popup.itemCount > 0) {
popup.focus();
if (popup.selectedIndex == popup.itemCount - 1) {
popup.selectedIndex =
Math.max(0, popup.itemCount - 2);
} else {
if (popup.selectedIndex === 0) {
popup.selectedIndex = popup.itemCount - 1;
} else {
popup.selectedIndex--;
}
this.searchBox.value = popup.selectedItem.label;
}
@ -348,12 +341,21 @@ SelectorAutocompleter.prototype = {
case event.DOM_VK_DOWN:
if (popup.isOpen && popup.itemCount > 0) {
popup.focus();
popup.selectedIndex = 0;
if (popup.selectedIndex === popup.itemCount - 1) {
popup.selectedIndex = 0;
} else {
popup.selectedIndex++;
}
this.searchBox.value = popup.selectedItem.label;
}
break;
case event.DOM_VK_ESCAPE:
if (popup.isOpen) {
this.hidePopup();
}
break;
default:
return;
}
@ -364,59 +366,17 @@ SelectorAutocompleter.prototype = {
},
/**
* Handles keypress and mouse click on the suggestions richlistbox.
* Handles click events from the autocomplete popup.
*/
_onListBoxKeypress: function (event) {
let popup = this.searchPopup;
switch (event.keyCode || event.button) {
case event.DOM_VK_RETURN:
case event.DOM_VK_TAB:
case 0:
// left mouse button
event.stopPropagation();
event.preventDefault();
this.searchBox.value = popup.selectedItem.label;
this.searchBox.focus();
this.hidePopup();
break;
case event.DOM_VK_UP:
if (popup.selectedIndex == 0) {
popup.selectedIndex = -1;
event.stopPropagation();
event.preventDefault();
this.searchBox.focus();
} else {
let index = popup.selectedIndex;
this.searchBox.value = popup.getItemAtIndex(index - 1).label;
}
break;
case event.DOM_VK_DOWN:
if (popup.selectedIndex == popup.itemCount - 1) {
popup.selectedIndex = -1;
event.stopPropagation();
event.preventDefault();
this.searchBox.focus();
} else {
let index = popup.selectedIndex;
this.searchBox.value = popup.getItemAtIndex(index + 1).label;
}
break;
case event.DOM_VK_BACK_SPACE:
event.stopPropagation();
event.preventDefault();
this.searchBox.focus();
if (this.searchBox.selectionStart > 0) {
this.searchBox.value = this.searchBox.value.substring(0,
this.searchBox.selectionStart - 1);
}
this.hidePopup();
break;
_onSearchPopupClick: function (event) {
let selectedItem = this.searchPopup.selectedItem;
if (selectedItem) {
this.searchBox.value = selectedItem.label;
}
this.emit("processing-done");
this.hidePopup();
event.preventDefault();
event.stopPropagation();
},
/**
@ -430,6 +390,9 @@ SelectorAutocompleter.prototype = {
/**
* Populates the suggestions list and show the suggestion popup.
*
* @return {Promise} promise that will resolve when the autocomplete popup is fully
* displayed or hidden.
*/
_showPopup: function (list, firstPart, popupState) {
let total = 0;
@ -473,21 +436,24 @@ SelectorAutocompleter.prototype = {
break;
}
}
if (total > 0) {
let onPopupOpened = this.searchPopup.once("popup-opened");
this.searchPopup.setItems(items);
this.searchPopup.openPopup(this.searchBox);
} else {
this.hidePopup();
return onPopupOpened;
}
return this.hidePopup();
},
/**
* Hide the suggestion popup if necessary.
*/
hidePopup: function () {
if (this.searchPopup.isOpen) {
this.searchPopup.hidePopup();
}
let onPopupClosed = this.searchPopup.once("popup-closed");
this.searchPopup.hidePopup();
return onPopupClosed;
},
/**
@ -534,7 +500,7 @@ SelectorAutocompleter.prototype = {
if (result.query !== query) {
// This means that this response is for a previous request and the user
// as since typed something extra leading to a new request.
return;
return promise.resolve(null);
}
if (state === this.States.CLASS) {
@ -550,7 +516,9 @@ SelectorAutocompleter.prototype = {
result.suggestions = [];
}
this._showPopup(result.suggestions, firstPart, state);
// Wait for the autocomplete-popup to fire its popup-opened event, to make sure
// the autoSelect item has been selected.
return this._showPopup(result.suggestions, firstPart, state);
});
return;

View File

@ -7,26 +7,6 @@
min-width: 250px;
}
#searchbox-panel-listbox {
width: 250px;
max-width: 250px;
overflow-x: hidden;
}
#searchbox-panel-listbox > richlistitem,
#searchbox-panel-listbox > richlistitem[selected] {
overflow-x: hidden;
}
#searchbox-panel-listbox > richlistitem > .initial-value {
max-width: 130px;
margin-left: 15px;
}
#searchbox-panel-listbox > richlistitem > .autocomplete-value {
max-width: 150px;
}
.inspector-tabpanel > * {
/*
* Override `-moz-user-focus:ignore;` from toolkit/content/minimal-xul.css

View File

@ -23,7 +23,6 @@ const DRAG_DROP_MIN_INITIAL_DISTANCE = 10;
const DRAG_DROP_HEIGHT_TO_SPEED = 500;
const DRAG_DROP_HEIGHT_TO_SPEED_MIN = 0.5;
const DRAG_DROP_HEIGHT_TO_SPEED_MAX = 1;
const AUTOCOMPLETE_POPUP_PANEL_ID = "markupview_autoCompletePopup";
const ATTR_COLLAPSE_ENABLED_PREF = "devtools.markup.collapseAttributes";
const ATTR_COLLAPSE_LENGTH_PREF = "devtools.markup.collapseAttributeLength";
const PREVIEW_MAX_DIM_PREF = "devtools.inspector.imagePreviewTooltipSize";
@ -109,12 +108,9 @@ function MarkupView(inspector, frame, controllerWindow) {
let options = {
autoSelect: true,
theme: "auto",
// panelId option prevents the markupView autocomplete popup from
// sharing XUL elements with other views, such as ruleView (see Bug 1191093)
panelId: AUTOCOMPLETE_POPUP_PANEL_ID
};
this.popup = new AutocompletePopup(this.doc.defaultView.parent.document,
options);
this.popup = new AutocompletePopup(inspector._toolbox, options);
this.undo = new UndoStack();
this.undo.installController(controllerWindow);

View File

@ -120,22 +120,13 @@ function* checkData(data, editor, inspector) {
if (selEnd != -1) {
is(editor.input.value, completion, "Completed value is correct");
is(editor.input.selectionStart, selStart,
"Selection start position is correct");
is(editor.input.selectionStart, selStart, "Selection start position is correct");
is(editor.input.selectionEnd, selEnd, "Selection end position is correct");
if (popupOpen) {
ok(editor.popup.isOpen, "Popup is open");
} else {
ok(editor.popup._panel.state != "open" &&
editor.popup._panel.state != "showing",
"Popup is closed");
}
is(editor.popup.isOpen, popupOpen, "Popup is " + (popupOpen ? "open" : "closed"));
} else {
let nodeFront = yield getNodeFront("#node14", inspector);
let container = getContainerForNodeFront(nodeFront, inspector);
let attr = container.editor.attrElements.get("style")
.querySelector(".editable");
is(attr.textContent, completion,
"Correct value is persisted after pressing Enter");
let attr = container.editor.attrElements.get("style").querySelector(".editable");
is(attr.textContent, completion, "Correct value is persisted after pressing Enter");
}
}

View File

@ -222,7 +222,7 @@ function CssRuleView(inspector, document, store, pageStyle) {
autoSelect: true,
theme: "auto"
};
this.popup = new AutocompletePopup(this.styleDocument, options);
this.popup = new AutocompletePopup(inspector._toolbox, options);
this._showEmpty();

View File

@ -47,7 +47,7 @@ add_task(function* () {
value: "rgb(0, 255, 0)"
});
let spectrum = yield cPicker.spectrum;
let spectrum = cPicker.spectrum;
let onHidden = cPicker.tooltip.once("hidden");
// Validating the color change ends up updating the rule view twice
let onRuleViewChanged = waitForNEvents(view, "ruleview-changed", 2);

View File

@ -52,7 +52,7 @@ function* basicTest(view, name, result) {
value: "rgb(0, 255, 0)"
});
let spectrum = yield cPicker.spectrum;
let spectrum = cPicker.spectrum;
let onHidden = cPicker.tooltip.once("hidden");
// Validating the color change ends up updating the rule view twice
let onRuleViewChanged = waitForNEvents(view, "ruleview-changed", 2);

View File

@ -44,7 +44,7 @@ function* testImageTooltipAfterColorChange(swatch, url, ruleView) {
value: 'url("chrome://global/skin/icons/warning-64.png"), linear-gradient(rgb(0, 0, 0), rgb(255, 0, 102) 400px)'
});
let spectrum = yield picker.spectrum;
let spectrum = picker.spectrum;
let onHidden = picker.tooltip.once("hidden");
let onModifications = ruleView.once("ruleview-changed");
EventUtils.sendKey("RETURN", spectrum.element.ownerDocument.defaultView);

View File

@ -42,7 +42,7 @@ function* testColorChangeIsntRevertedWhenOtherTooltipIsShown(ruleView) {
value: "rgb(0, 0, 0)"
});
let spectrum = yield picker.spectrum;
let spectrum = picker.spectrum;
let onModifications = waitForNEvents(ruleView, "ruleview-changed", 2);
let onHidden = picker.tooltip.once("hidden");

View File

@ -45,7 +45,7 @@ function* testPressingEnterCommitsChanges(swatch, ruleView) {
"The text of the border css property was updated");
let onModified = ruleView.once("ruleview-changed");
let spectrum = yield cPicker.spectrum;
let spectrum = cPicker.spectrum;
let onHidden = cPicker.tooltip.once("hidden");
EventUtils.sendKey("RETURN", spectrum.element.ownerDocument.defaultView);
yield onHidden;

View File

@ -17,7 +17,7 @@ add_task(function* () {
.querySelector(".ruleview-colorswatch");
let picker = yield openColorPickerForSwatch(cSwatch, view);
let spectrum = yield picker.spectrum;
let spectrum = picker.spectrum;
let change = spectrum.once("changed");
info("Pressing mouse down over color picker.");

View File

@ -35,7 +35,7 @@ function* testPressingEscapeRevertsChanges(view) {
is(propEditor.valueSpan.textContent, "#000",
"The text of the background-color css property was updated");
let spectrum = yield cPicker.spectrum;
let spectrum = cPicker.spectrum;
info("Pressing ESCAPE to close the tooltip");
let onHidden = cPicker.tooltip.once("hidden");
@ -76,7 +76,7 @@ function* testPressingEscapeRevertsChangesAndDisables(view) {
is(textProp.editor.enable.style.visibility, "hidden",
"property enable checkbox is hidden.");
let spectrum = yield cPicker.spectrum;
let spectrum = cPicker.spectrum;
info("Pressing ESCAPE to close the tooltip");
let onHidden = cPicker.tooltip.once("hidden");

View File

@ -85,21 +85,17 @@ function* runAutocompletionTest(toolbox, inspector, view) {
yield selectNode("h1", inspector);
info("Focusing the css property editable field");
let propertyName = view.styleDocument
.querySelectorAll(".ruleview-propertyname")[0];
let propertyName = view.styleDocument.querySelectorAll(".ruleview-propertyname")[0];
let editor = yield focusEditableField(view, propertyName);
info("Starting to test for css property completion");
let previousPopupSize = 0;
for (let i = 0; i < testData.length; i++) {
let expectPopupHiddenEvent = previousPopupSize > 0 && testData[3] === 0;
yield testCompletion(testData[i], expectPopupHiddenEvent, editor, view);
previousPopupSize = testData[3];
yield testCompletion(testData[i], editor, view);
}
}
function* testCompletion([key, completion, open, selected],
expectPopupHiddenEvent, editor, view) {
editor, view) {
info("Pressing key " + key);
info("Expecting " + completion);
info("Is popup opened: " + open);
@ -117,26 +113,24 @@ function* testCompletion([key, completion, open, selected],
onSuggest = editor.once("after-suggest");
}
// Also listening for popup hiding if needed.
let onMaybePopupHidden = expectPopupHiddenEvent
? once(editor.popup._panel, "hidden")
: null;
// Also listening for popup opened/closed events if needed.
let popupEvent = open ? "popup-opened" : "popup-closed";
let onPopupEvent = editor.popup.isOpen !== open ? once(editor.popup, popupEvent) : null;
info("Synthesizing key " + key);
EventUtils.synthesizeKey(key, {}, view.styleWindow);
yield onSuggest;
yield onMaybePopupHidden;
yield onPopupEvent;
info("Checking the state");
if (completion != null) {
if (completion !== null) {
is(editor.input.value, completion, "Correct value is autocompleted");
}
if (!open) {
ok(!(editor.popup && editor.popup.isOpen), "Popup is closed");
} else {
ok(editor.popup._panel.state == "open" ||
editor.popup._panel.state == "showing", "Popup is open");
is(editor.popup.selectedIndex != -1, selected, "An item is selected");
ok(editor.popup.isOpen, "Popup is open");
is(editor.popup.selectedIndex !== -1, selected, "An item is selected");
}
}

View File

@ -92,23 +92,28 @@ function* testCompletion([key, modifiers, completion, open, selected, change],
}
info("Synthesizing key " + key + ", modifiers: " + Object.keys(modifiers));
// Also listening for popup opened/closed events if needed.
let popupEvent = open ? "popup-opened" : "popup-closed";
let onPopupEvent = editor.popup.isOpen !== open ? once(editor.popup, popupEvent) : null;
EventUtils.synthesizeKey(key, modifiers, view.styleWindow);
yield onDone;
yield onPopupEvent;
// The key might have been a TAB or shift-TAB, in which case the editor will
// be a new one
editor = inplaceEditor(view.styleDocument.activeElement);
info("Checking the state");
if (completion != null) {
if (completion !== null) {
is(editor.input.value, completion, "Correct value is autocompleted");
}
if (!open) {
ok(!(editor.popup && editor.popup.isOpen), "Popup is closed");
} else {
ok(editor.popup._panel.state == "open" ||
editor.popup._panel.state == "showing", "Popup is open");
is(editor.popup.selectedIndex != -1, selected, "An item is selected");
ok(editor.popup.isOpen, "Popup is open");
is(editor.popup.selectedIndex !== -1, selected, "An item is selected");
}
}

View File

@ -63,10 +63,10 @@ function* runAutocompletionTest(toolbox, inspector, view) {
}
}
function* testCompletion([key, completion, isOpen, isSelected], editor, view) {
function* testCompletion([key, completion, open, isSelected], editor, view) {
info("Pressing key " + key);
info("Expecting " + completion);
info("Is popup opened: " + isOpen);
info("Is popup opened: " + open);
info("Is item selected: " + isSelected);
let onSuggest;
@ -79,21 +79,24 @@ function* testCompletion([key, completion, isOpen, isSelected], editor, view) {
onSuggest = editor.once("after-suggest");
}
// Also listening for popup opened/closed events if needed.
let popupEvent = open ? "popup-opened" : "popup-closed";
let onPopupEvent = editor.popup.isOpen !== open ? once(editor.popup, popupEvent) : null;
info("Synthesizing key " + key);
EventUtils.synthesizeKey(key, {}, view.styleWindow);
yield onSuggest;
yield waitForTick();
yield onPopupEvent;
info("Checking the state");
if (completion != null) {
if (completion !== null) {
is(editor.input.value, completion, "Correct value is autocompleted");
}
if (!isOpen) {
if (!open) {
ok(!(editor.popup && editor.popup.isOpen), "Popup is closed");
} else {
ok(editor.popup._panel.state == "open" ||
editor.popup._panel.state == "showing", "Popup is open");
is(editor.popup.selectedIndex != -1, isSelected, "An item is selected");
ok(editor.popup.isOpen, "Popup is open");
is(editor.popup.selectedIndex !== -1, isSelected, "An item is selected");
}
}

View File

@ -100,12 +100,17 @@ function* testCompletion([key, modifiers, completion, open, selected, change],
: null;
}
// Also listening for popup opened/closed events if needed.
let popupEvent = open ? "popup-opened" : "popup-closed";
let onPopupEvent = editor.popup.isOpen !== open ? once(editor.popup, popupEvent) : null;
info("Synthesizing key " + key + ", modifiers: " + Object.keys(modifiers));
EventUtils.synthesizeKey(key, modifiers, view.styleWindow);
yield onDone;
yield onPopupEvent;
info("Checking the state");
if (completion != null) {
if (completion !== null) {
// The key might have been a TAB or shift-TAB, in which case the editor will
// be a new one
editor = inplaceEditor(view.styleDocument.activeElement);
@ -114,8 +119,7 @@ function* testCompletion([key, modifiers, completion, open, selected, change],
if (!open) {
ok(!(editor.popup && editor.popup.isOpen), "Popup is closed");
} else {
ok(editor.popup._panel.state == "open" ||
editor.popup._panel.state == "showing", "Popup is open");
is(editor.popup.selectedIndex != -1, selected, "An item is selected");
ok(editor.popup.isOpen, "Popup is open");
is(editor.popup.selectedIndex !== -1, selected, "An item is selected");
}
}

View File

@ -41,7 +41,7 @@ function* runAutocompletionTest(toolbox, inspector, view) {
editor.popup.selectedIndex = itemIndex;
let node = editor.popup._list.childNodes[itemIndex];
EventUtils.synthesizeMouseAtCenter(node, {}, view.styleWindow);
EventUtils.synthesizeMouseAtCenter(node, {}, editor.popup._window);
is(editor.input.value, "background-color", "Correct value is autocompleted");
}

View File

@ -39,8 +39,8 @@ add_task(function* () {
info("Select the background-color suggestion with a mouse click.");
let onSuggest = editor.once("after-suggest");
let node = editor.popup._list.childNodes[itemIndex];
EventUtils.synthesizeMouseAtCenter(node, {}, view.styleWindow);
let node = editor.popup.elements.get(bgcItem);
EventUtils.synthesizeMouseAtCenter(node, {}, editor.popup._window);
yield onSuggest;
is(editor.input.value, "background-color", "Correct value is autocompleted");

View File

@ -60,7 +60,7 @@ add_task(function* () {
info("Check that UP/DOWN navigates in the input, even when next to a number");
EventUtils.synthesizeKey("VK_DOWN", {}, view.styleWindow);
ok(editor.input.selectionStart != pos, "Input caret moved");
ok(editor.input.selectionStart !== pos, "Input caret moved");
is(editor.input.value, LONG_CSS_VALUE, "Input value was not decremented.");
info("Move the caret to the end of the gradient definition.");
@ -95,8 +95,10 @@ add_task(function* () {
info("Select the background-color suggestion with a mouse click.");
let onRuleviewChanged = view.once("ruleview-changed");
let onSuggest = editor.once("after-suggest");
let node = editor.popup._list.childNodes[editor.popup.selectedIndex];
EventUtils.synthesizeMouseAtCenter(node, {}, view.styleWindow);
EventUtils.synthesizeMouseAtCenter(node, {}, editor.popup._window);
yield onSuggest;
yield onRuleviewChanged;

View File

@ -24,11 +24,13 @@ add_task(function* () {
info("Pressing key VK_DOWN");
let onSuggest = once(editor.input, "keypress");
let onPopupOpened = once(editor.popup, "popup-opened");
EventUtils.synthesizeKey("VK_DOWN", {}, view.styleWindow);
info("Waiting for autocomplete popup to be displayed");
yield onSuggest;
yield waitForTick();
yield onPopupOpened;
ok(view.popup && view.popup.isOpen, "Popup should be opened");

View File

@ -64,6 +64,6 @@ add_task(function* () {
ok(!inplaceEditor(propEditor.valueSpan),
"The inplace editor wasn't shown as a result of the color swatch click");
let spectrum = yield colorPicker.spectrum;
let spectrum = colorPicker.spectrum;
is(spectrum.rgb, "200,170,140,0.5", "The correct color picker was shown");
});

View File

@ -61,7 +61,7 @@ add_task(function* () {
let dropper = yield openEyedropper(view, swatch);
let tooltip = view.tooltips.colorPicker.tooltip;
ok(tooltip.isHidden(),
ok(!tooltip.isVisible(),
"color picker tooltip is closed after opening eyedropper");
yield testESC(swatch, dropper);
@ -134,8 +134,7 @@ function openEyedropper(view, swatch) {
let tooltip = view.tooltips.colorPicker.tooltip;
tooltip.once("shown", () => {
let tooltipDoc = tooltip.content.contentDocument;
let dropperButton = tooltipDoc.querySelector("#eyedropper-button");
let dropperButton = tooltip.doc.querySelector("#eyedropper-button");
tooltip.once("eyedropper-opened", (event, dropper) => {
deferred.resolve(dropper);

View File

@ -28,6 +28,5 @@ add_task(function* () {
ok(!inplaceEditor(swatch.parentNode),
"The inplace editor wasn't shown as a result of the filter swatch click");
yield filterTooltip.widget;
yield hideTooltipAndWaitForRuleViewChanged(filterTooltip, view);
});

View File

@ -25,7 +25,7 @@ add_task(function* () {
info("Get the cssfilter widget instance");
let filterTooltip = view.tooltips.filterEditor;
let widget = yield filterTooltip.widget;
let widget = filterTooltip.widget;
info("Set a new value in the cssfilter widget");
onRuleViewChanged = view.once("ruleview-changed");

View File

@ -38,7 +38,6 @@ function* testPressingEscapeRevertsChanges(view) {
function* testPressingEscapeRevertsChangesAndDisables(view) {
let ruleEditor = getRuleViewRuleEditor(view, 1);
let propEditor = ruleEditor.rule.textProps[0].editor;
let swatch = propEditor.valueSpan.querySelector(".ruleview-filterswatch");
info("Disabling filter property");
let onRuleViewChanged = view.once("ruleview-changed");
@ -56,6 +55,7 @@ function* testPressingEscapeRevertsChangesAndDisables(view) {
let newValue = yield getRulePropertyValue("filter");
is(newValue, "", "filter should have been unset.");
let swatch = propEditor.valueSpan.querySelector(".ruleview-filterswatch");
yield clickOnFilterSwatch(swatch, view);
ok(!propEditor.element.classList.contains("ruleview-overridden"),
@ -103,9 +103,8 @@ function* setValueInFilterWidget(value, view) {
info("Setting the CSS filter value in the tooltip");
let filterTooltip = view.tooltips.filterEditor;
let widget = yield filterTooltip.widget;
let onRuleViewChanged = view.once("ruleview-changed");
widget.setCssValue(value);
filterTooltip.widget.setCssValue(value);
yield onRuleViewChanged;
}
@ -113,8 +112,7 @@ function* pressEscapeToCloseTooltip(view) {
info("Pressing ESCAPE to close the tooltip");
let filterTooltip = view.tooltips.filterEditor;
let widget = yield filterTooltip.widget;
let onRuleViewChanged = view.once("ruleview-changed");
EventUtils.sendKey("ESCAPE", widget.styleWindow);
EventUtils.sendKey("ESCAPE", filterTooltip.widget.styleWindow);
yield onRuleViewChanged;
}

View File

@ -358,9 +358,14 @@ function getRuleViewSelectorHighlighterIcon(view, selectorText) {
*/
var simulateColorPickerChange = Task.async(function* (ruleView, colorPicker,
newRgba, expectedChange) {
let onComputedStyleChanged;
if (expectedChange) {
let {selector, name, value} = expectedChange;
onComputedStyleChanged = waitForComputedStyleProperty(selector, null, name, value);
}
let onRuleViewChanged = ruleView.once("ruleview-changed");
info("Getting the spectrum colorpicker object");
let spectrum = yield colorPicker.spectrum;
let spectrum = colorPicker.spectrum;
info("Setting the new color");
spectrum.rgb = newRgba;
info("Applying the change");
@ -371,8 +376,7 @@ var simulateColorPickerChange = Task.async(function* (ruleView, colorPicker,
if (expectedChange) {
info("Waiting for the style to be applied on the page");
let {selector, name, value} = expectedChange;
yield waitForComputedStyleProperty(selector, null, name, value);
yield onComputedStyleChanged;
}
});

View File

@ -261,10 +261,10 @@ exports.TooltipsOverlay = TooltipsOverlay;
TooltipsOverlay.prototype = {
get isEditing() {
return this.colorPicker.tooltip.isShown() ||
return this.colorPicker.tooltip.isVisible() ||
this.colorPicker.eyedropperOpen ||
this.cubicBezier.tooltip.isShown() ||
this.filterEditor.tooltip.isShown();
this.cubicBezier.tooltip.isVisible() ||
this.filterEditor.tooltip.isVisible();
},
/**
@ -290,11 +290,12 @@ TooltipsOverlay.prototype = {
if (this.isRuleView) {
// Color picker tooltip
this.colorPicker = new SwatchColorPickerTooltip(panelDoc);
let { toolbox } = this.view.inspector;
this.colorPicker = new SwatchColorPickerTooltip(toolbox);
// Cubic bezier tooltip
this.cubicBezier = new SwatchCubicBezierTooltip(panelDoc);
this.cubicBezier = new SwatchCubicBezierTooltip(toolbox);
// Filter editor tooltip
this.filterEditor = new SwatchFilterTooltip(panelDoc);
this.filterEditor = new SwatchFilterTooltip(toolbox);
}
this._isStarted = true;
@ -381,12 +382,12 @@ TooltipsOverlay.prototype = {
return false;
}
if (this.isRuleView && this.colorPicker.tooltip.isShown()) {
if (this.isRuleView && this.colorPicker.tooltip.isVisible()) {
this.colorPicker.revert();
this.colorPicker.hide();
}
if (this.isRuleView && this.cubicBezier.tooltip.isShown()) {
if (this.isRuleView && this.cubicBezier.tooltip.isVisible()) {
this.cubicBezier.revert();
this.cubicBezier.hide();
}
@ -395,7 +396,7 @@ TooltipsOverlay.prototype = {
this.cssDocs.hide();
}
if (this.isRuleView && this.filterEditor.tooltip.isShown()) {
if (this.isRuleView && this.filterEditor.tooltip.isVisible()) {
this.filterEditor.revert();
this.filterEdtior.hide();
}

View File

@ -34,7 +34,6 @@ skip-if = e10s # Bug 1111546 (e10s)
[browser_styleinspector_tooltip-multiple-background-images.js]
[browser_styleinspector_tooltip-shorthand-fontfamily.js]
[browser_styleinspector_tooltip-size.js]
skip-if = e10s || os == 'linux' # Bug 1111546 (e10s), bug 1093431 (linux)
[browser_styleinspector_transform-highlighter-01.js]
[browser_styleinspector_transform-highlighter-02.js]
[browser_styleinspector_transform-highlighter-03.js]

View File

@ -70,12 +70,13 @@ function* testPickerDimension(ruleView) {
// The colorpicker spectrum's iframe has a fixed width height, so let's
// make sure the tooltip is at least as big as that
let w = cPicker.tooltip.panel.querySelector("iframe").width;
let h = cPicker.tooltip.panel.querySelector("iframe").height;
let panelRect = cPicker.tooltip.panel.getBoundingClientRect();
let spectrumRect = cPicker.spectrum.element.getBoundingClientRect();
let panelRect = cPicker.tooltip.container.getBoundingClientRect();
ok(panelRect.width >= w, "The panel is wide enough to show the picker");
ok(panelRect.height >= h, "The panel is high enough to show the picker");
ok(panelRect.width >= spectrumRect.width,
"The panel is wide enough to show the picker");
ok(panelRect.height >= spectrumRect.height,
"The panel is high enough to show the picker");
let onHidden = cPicker.tooltip.once("hidden");
let onRuleViewChanged = ruleView.once("ruleview-changed");

View File

@ -14,11 +14,11 @@ const KEY_STATES = [
[".", "div."],
["VK_UP", "div.c1"],
["VK_DOWN", "div.l1"],
["VK_DOWN", "div.l1"],
["VK_BACK_SPACE", "div.l"],
["VK_TAB", "div.l1"],
[" ", "div.l1 "],
["VK_UP", "div.l1 div"],
["VK_UP", "div.l1 span"],
["VK_UP", "div.l1 div"],
[".", "div.l1 div."],
["VK_TAB", "div.l1 div.c1"],
@ -33,6 +33,7 @@ const KEY_STATES = [
["VK_BACK_SPACE", "div.l1 d"],
["VK_BACK_SPACE", "div.l1 "],
["VK_UP", "div.l1 div"],
["VK_UP", "div.l1 span"],
["VK_UP", "div.l1 div"],
["VK_TAB", "div.l1 div"],
["VK_BACK_SPACE", "div.l1 di"],
@ -40,7 +41,6 @@ const KEY_STATES = [
["VK_BACK_SPACE", "div.l1 "],
["VK_DOWN", "div.l1 div"],
["VK_DOWN", "div.l1 span"],
["VK_DOWN", "div.l1 span"],
["VK_BACK_SPACE", "div.l1 spa"],
["VK_BACK_SPACE", "div.l1 sp"],
["VK_BACK_SPACE", "div.l1 s"],
@ -68,6 +68,9 @@ add_task(function* () {
EventUtils.synthesizeKey(key, {}, inspector.panelWin);
yield done;
is(inspector.searchBox.value, query, "The searchbox value is correct.");
info("Waiting for search query to complete");
yield inspector.searchSuggestions._lastQuery;
is(inspector.searchBox.value, query, "The searchbox value is correct");
}
});

View File

@ -127,13 +127,11 @@ devtools.jar:
content/framework/connect/connect.css (framework/connect/connect.css)
content/framework/connect/connect.js (framework/connect/connect.js)
content/shared/widgets/graphs-frame.xhtml (shared/widgets/graphs-frame.xhtml)
content/shared/widgets/spectrum-frame.xhtml (shared/widgets/spectrum-frame.xhtml)
content/shared/widgets/cubic-bezier-frame.xhtml (shared/widgets/cubic-bezier-frame.xhtml)
content/shared/widgets/cubic-bezier.css (shared/widgets/cubic-bezier.css)
content/shared/widgets/mdn-docs-frame.xhtml (shared/widgets/mdn-docs-frame.xhtml)
content/shared/widgets/mdn-docs.css (shared/widgets/mdn-docs.css)
content/shared/widgets/filter-frame.xhtml (shared/widgets/filter-frame.xhtml)
content/shared/widgets/filter-widget.css (shared/widgets/filter-widget.css)
content/shared/widgets/spectrum.css (shared/widgets/spectrum.css)
content/eyedropper/eyedropper.xul (eyedropper/eyedropper.xul)
content/eyedropper/crosshairs.css (eyedropper/crosshairs.css)
content/eyedropper/nocursor.css (eyedropper/nocursor.css)
@ -195,7 +193,6 @@ devtools.jar:
skin/images/breadcrumbs-scrollbutton.png (themes/images/breadcrumbs-scrollbutton.png)
skin/images/breadcrumbs-scrollbutton@2x.png (themes/images/breadcrumbs-scrollbutton@2x.png)
skin/animationinspector.css (themes/animationinspector.css)
skin/spectrum.css (themes/spectrum.css)
skin/eyedropper.css (themes/eyedropper.css)
skin/canvasdebugger.css (themes/canvasdebugger.css)
skin/debugger.css (themes/debugger.css)

View File

@ -1,24 +0,0 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!-- LOCALIZATION NOTE : FILE These strings are used in the CSS Filter Editor Widget
- which can be found in a tooltip that appears in the Rule View when clicking
- on a filter swatch displayed next to CSS declarations like 'filter: blur(2px)'. -->
<!-- LOCALIZATION NOTE (filterListSelectPlaceholder): This string is used as
- a preview option in the list of possible filters <select> -->
<!ENTITY filterListSelectPlaceholder "Select a Filter">
<!-- LOCALIZATION NOTE (addNewFilterButton): This string is displayed on a button used to add new filters -->
<!ENTITY addNewFilterButton "Add">
<!-- LOCALIZATION NOTE (newPresetPlaceholder): This string is used as
- a placeholder in the list of presets which is used to save a new preset -->
<!ENTITY newPresetPlaceholder "Preset Name">
<!-- LOCALIZATION NOTE (savePresetButton): This string is displayed on a button used to save a new preset -->
<!ENTITY savePresetButton "Save">
<!-- LOCALIZATION NOTE(presetsToggleButton): This string is used in a button which toggles the presets list -->
<!ENTITY presetsToggleButton "Presets">

View File

@ -37,3 +37,25 @@ dragHandleTooltipText=Drag up or down to re-order filter
# filters' labels which can be dragged left/right to increase/decrease
# the filter's value (like photoshop)
labelDragTooltipText=Drag left or right to decrease or increase the value
# LOCALIZATION NOTE (filterListSelectPlaceholder):
# This string is used as a preview option in the list of possible filters
# <select>
filterListSelectPlaceholder=Select a Filter
# LOCALIZATION NOTE (addNewFilterButton):
# This string is displayed on a button used to add new filters
addNewFilterButton=Add
# LOCALIZATION NOTE (newPresetPlaceholder):
# This string is used as a placeholder in the list of presets which is used to
# save a new preset
newPresetPlaceholder=Preset Name
# LOCALIZATION NOTE (savePresetButton):
# This string is displayed on a button used to save a new preset
savePresetButton=Save
# LOCALIZATION NOTE(presetsToggleButton):
# This string is used in a button which toggles the presets list
presetsToggleButton=Presets

View File

@ -97,6 +97,7 @@ skip-if = (os == 'linux' && e10s && debug) # Bug 1242204
[browser_net_json_text_mime.js]
[browser_net_jsonp.js]
[browser_net_large-response.js]
[browser_net_leak_on_tab_close.js]
[browser_net_open_request_in_tab.js]
[browser_net_page-nav.js]
[browser_net_pane-collapse.js]

View File

@ -0,0 +1,18 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests that netmonitor doesn't leak windows on parent-side pages (bug 1285638)
*/
add_task(function* () {
// Tell initNetMonitor to enable cache. Otherwise it will assert that there were more
// than zero network requests during the page load. But when loading about:config,
// there are none.
let [,, monitor] = yield initNetMonitor("about:config", null, true);
ok(monitor, "The network monitor was opened");
yield teardown(monitor);
finish();
});

View File

@ -5,135 +5,106 @@
"use strict";
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const HTML_NS = "http://www.w3.org/1999/xhtml";
const Services = require("Services");
const {gDevTools} = require("devtools/client/framework/devtools");
const events = require("devtools/shared/event-emitter");
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const EventEmitter = require("devtools/shared/event-emitter");
let itemIdCounter = 0;
/**
* Autocomplete popup UI implementation.
*
* @constructor
* @param {nsIDOMDocument} document
* The document you want the popup attached to.
* @param {Toolbox} toolbox
* The devtools toolbox required to instanciate the HTMLTooltip.
* @param {Object} options
* An object consiting any of the following options:
* - panelId {String} The id for the popup panel.
* - listBoxId {String} The id for the richlistbox inside the panel.
* - position {String} The position for the popup panel.
* - theme {String} String related to the theme of the popup.
* - listId {String} The id for the list <LI> element.
* - position {String} The position for the tooltip ("top" or "bottom").
* - theme {String} String related to the theme of the popup
* - autoSelect {Boolean} Boolean to allow the first entry of the popup
* panel to be automatically selected when the popup shows.
* - direction {String} The direction of the text in the panel. rtl / ltr
* - onSelect {String} The select event handler for the richlistbox
* - onClick {String} The click event handler for the richlistbox.
* - onKeypress {String} The keypress event handler for richlistitems.
* panel to be automatically selected when the popup shows.
* - onSelect {String} Callback called when the selected index is updated.
* - onClick {String} Callback called when the autocomplete popup receives a click
* event. The selectedIndex will already be updated if need be.
*/
function AutocompletePopup(document, options = {}) {
this._document = document;
function AutocompletePopup(toolbox, options = {}) {
EventEmitter.decorate(this);
this._document = toolbox.doc;
this.autoSelect = options.autoSelect || false;
this.position = options.position || "after_start";
this.direction = options.direction || "ltr";
this.position = options.position || "bottom";
let theme = options.theme || "dark";
this.onSelectCallback = options.onSelect;
this.onClickCallback = options.onClick;
this.onKeypressCallback = options.onKeypress;
let id = options.panelId || "devtools_autoCompletePopup";
let theme = options.theme || "dark";
// If theme is auto, use the devtools.theme pref
if (theme == "auto") {
if (theme === "auto") {
theme = Services.prefs.getCharPref("devtools.theme");
this.autoThemeEnabled = true;
// Setup theme change listener.
this._handleThemeChange = this._handleThemeChange.bind(this);
gDevTools.on("pref-changed", this._handleThemeChange);
}
// Reuse the existing popup elements.
this._panel = this._document.getElementById(id);
if (!this._panel) {
this._panel = this._document.createElementNS(XUL_NS, "panel");
this._panel.setAttribute("id", id);
this._panel.className = "devtools-autocomplete-popup devtools-monospace "
+ theme + "-theme";
this._panel.setAttribute("noautofocus", "true");
this._panel.setAttribute("level", "top");
if (!options.onKeypress) {
this._panel.setAttribute("ignorekeys", "true");
}
// Stop this appearing as an alert to accessibility.
this._panel.setAttribute("role", "presentation");
let mainPopupSet = this._document.getElementById("mainPopupSet");
if (mainPopupSet) {
mainPopupSet.appendChild(this._panel);
} else {
this._document.documentElement.appendChild(this._panel);
}
} else {
this._list = this._panel.firstChild;
}
if (!this._list) {
this._list = this._document.createElementNS(XUL_NS, "richlistbox");
this._panel.appendChild(this._list);
// Open and hide the panel, so we initialize the API of the richlistbox.
this._panel.openPopup(null, this.position, 0, 0);
this._panel.hidePopup();
}
// Create HTMLTooltip instance
this._tooltip = new HTMLTooltip(toolbox);
this._tooltip.panel.classList.add(
"devtools-autocomplete-popup",
"devtools-monospace",
theme + "-theme");
// Stop this appearing as an alert to accessibility.
this._tooltip.panel.setAttribute("role", "presentation");
this._list = this._document.createElementNS(HTML_NS, "ul");
this._list.setAttribute("flex", "1");
this._list.setAttribute("seltype", "single");
if (options.listBoxId) {
this._list.setAttribute("id", options.listBoxId);
if (options.listId) {
this._list.setAttribute("id", options.listId);
}
this._list.className = "devtools-autocomplete-listbox " + theme + "-theme";
this.onSelect = this.onSelect.bind(this);
this._tooltip.setContent(this._list);
this.onClick = this.onClick.bind(this);
this.onKeypress = this.onKeypress.bind(this);
this._list.addEventListener("select", this.onSelect, false);
this._list.addEventListener("click", this.onClick, false);
this._list.addEventListener("keypress", this.onKeypress, false);
this._itemIdCounter = 0;
// Array of raw autocomplete items
this.items = [];
// Map of autocompleteItem to HTMLElement
this.elements = new WeakMap();
events.decorate(this);
this.selectedIndex = -1;
}
exports.AutocompletePopup = AutocompletePopup;
AutocompletePopup.prototype = {
_document: null,
_panel: null,
_tooltip: null,
_list: null,
__scrollbarWidth: null,
// Event handlers.
onSelect: function (e) {
this.emit("popup-select");
if (this.onSelectCallback) {
this.onSelectCallback(e);
}
},
onClick: function (e) {
let item = e.target.closest(".autocomplete-item");
if (item && typeof item.dataset.index !== "undefined") {
this.selectedIndex = parseInt(item.dataset.index, 10);
}
this.emit("popup-click");
if (this.onClickCallback) {
this.onClickCallback(e);
}
},
onKeypress: function (e) {
this.emit("popup-keypress");
if (this.onKeypressCallback) {
this.onKeypressCallback(e);
}
},
/**
* Open the autocomplete popup panel.
*
@ -151,13 +122,19 @@ AutocompletePopup.prototype = {
openPopup: function (anchor, xOffset = 0, yOffset = 0, index) {
this.__maxLabelLength = -1;
this._updateSize();
this._panel.openPopup(anchor, this.position, xOffset, yOffset);
this._tooltip.show(anchor, {
x: xOffset,
y: yOffset,
position: this.position,
});
if (this.autoSelect) {
this.selectItemAtIndex(index);
}
this._tooltip.once("shown", () => {
if (this.autoSelect) {
this.selectItemAtIndex(index);
}
this.emit("popup-opened");
this.emit("popup-opened");
});
},
/**
@ -167,30 +144,29 @@ AutocompletePopup.prototype = {
* The position of the item to select.
*/
selectItemAtIndex: function (index) {
if (typeof index != "number") {
if (typeof index !== "number") {
// If no index was provided, select the item closest to the input.
let isAboveInput = this.position.includes("before");
let isAboveInput = this.position === "top";
index = isAboveInput ? this.itemCount - 1 : 0;
}
this.selectedIndex = index;
this._list.ensureIndexIsVisible(this._list.selectedIndex);
},
/**
* Hide the autocomplete popup panel.
*/
hidePopup: function () {
// Return accessibility focus to the input.
this._document.activeElement.removeAttribute("aria-activedescendant");
this._panel.hidePopup();
this._tooltip.once("hidden", () => {
this.emit("popup-closed");
});
this._tooltip.hide();
},
/**
* Check if the autocomplete popup is open.
*/
get isOpen() {
return this._panel &&
(this._panel.state == "open" || this._panel.state == "showing");
return this._tooltip && this._tooltip.isVisible();
},
/**
@ -204,19 +180,17 @@ AutocompletePopup.prototype = {
this.hidePopup();
}
this._list.removeEventListener("select", this.onSelect, false);
this._list.removeEventListener("click", this.onClick, false);
this._list.removeEventListener("keypress", this.onKeypress, false);
if (this.autoThemeEnabled) {
gDevTools.off("pref-changed", this._handleThemeChange);
}
this._list.remove();
this._panel.remove();
this._tooltip.destroy();
this._document = null;
this._list = null;
this._panel = null;
this._tooltip = null;
},
/**
@ -228,7 +202,7 @@ AutocompletePopup.prototype = {
* @return {Object} The autocomplete item at index index.
*/
getItemAtIndex: function (index) {
return this._list.getItemAtIndex(index)._autocompleteItem;
return this.items[index];
},
/**
@ -237,13 +211,8 @@ AutocompletePopup.prototype = {
* @return {Array} The array of autocomplete items.
*/
getItems: function () {
let items = [];
Array.forEach(this._list.childNodes, function (item) {
items.push(item._autocompleteItem);
});
return items;
// Return a copy of the array to avoid side effects from the caller code.
return this.items.slice(0);
},
/**
@ -258,30 +227,24 @@ AutocompletePopup.prototype = {
this.clearItems();
items.forEach(this.appendItem, this);
// Make sure that the new content is properly fitted by the XUL richlistbox.
if (this.isOpen) {
if (this.autoSelect) {
this.selectItemAtIndex(index);
}
this._updateSize();
if (this.isOpen && this.autoSelect) {
this.selectItemAtIndex(index);
}
},
__maxLabelLength: -1,
get _maxLabelLength() {
if (this.__maxLabelLength != -1) {
if (this.__maxLabelLength !== -1) {
return this.__maxLabelLength;
}
let max = 0;
for (let i = 0; i < this._list.childNodes.length; i++) {
let item = this._list.childNodes[i]._autocompleteItem;
let str = item.label;
if (item.count) {
str += (item.count + "");
for (let {label, count} of this.items) {
if (count) {
label += count + "";
}
max = Math.max(str.length, max);
max = Math.max(label.length, max);
}
this.__maxLabelLength = max;
@ -292,26 +255,32 @@ AutocompletePopup.prototype = {
* Update the panel size to fit the content.
*/
_updateSize: function () {
if (!this._panel) {
if (!this._tooltip) {
return;
}
this._list.style.width = (this._maxLabelLength + 3) + "ch";
this._list.ensureIndexIsVisible(this._list.selectedIndex);
let selectedItem = this.selectedItem;
if (selectedItem) {
this._scrollElementIntoViewIfNeeded(this.elements.get(selectedItem));
}
},
/**
* Update accessibility appropriately when the selected item is changed.
*/
_updateAriaActiveDescendant: function () {
if (!this._list.selectedItem) {
// Return accessibility focus to the input.
this._document.activeElement.removeAttribute("aria-activedescendant");
_scrollElementIntoViewIfNeeded: function (element) {
let quads = element.getBoxQuads({relativeTo: this._tooltip.panel});
if (!quads || !quads[0]) {
return;
}
// Focus this for accessibility so users know about the selected item.
this._document.activeElement.setAttribute("aria-activedescendant",
this._list.selectedItem.id);
let {top, height} = quads[0].bounds;
let containerHeight = this._tooltip.panel.getBoundingClientRect().height;
if (top < 0) {
// Element is above container.
element.scrollIntoView(true);
} else if ((top + height) > containerHeight) {
// Element is beloew container.
element.scrollIntoView(false);
}
},
/**
@ -320,22 +289,10 @@ AutocompletePopup.prototype = {
clearItems: function () {
// Reset the selectedIndex to -1 before clearing the list
this.selectedIndex = -1;
while (this._list.hasChildNodes()) {
this._list.removeChild(this._list.firstChild);
}
this._list.innerHTML = "";
this.__maxLabelLength = -1;
// Reset the panel and list dimensions. New dimensions are calculated when
// a new set of items is added to the autocomplete popup.
this._list.width = "";
this._list.style.width = "";
this._list.height = "";
this._panel.width = "";
this._panel.height = "";
this._panel.top = "";
this._panel.left = "";
this.items = [];
this.elements = new WeakMap();
},
/**
@ -344,7 +301,7 @@ AutocompletePopup.prototype = {
* @type {Number}
*/
get selectedIndex() {
return this._list.selectedIndex;
return this._selectedIndex;
},
/**
@ -354,11 +311,24 @@ AutocompletePopup.prototype = {
* The number (index) of the item you want to select in the list.
*/
set selectedIndex(index) {
this._list.selectedIndex = index;
if (this.isOpen && this._list.ensureIndexIsVisible) {
this._list.ensureIndexIsVisible(this._list.selectedIndex);
let previousSelected = this._list.querySelector(".autocomplete-selected");
if (previousSelected) {
previousSelected.classList.remove("autocomplete-selected");
}
let item = this.items[index];
if (this.isOpen && item) {
let element = this.elements.get(item);
element.classList.add("autocomplete-selected");
this._scrollElementIntoViewIfNeeded(element);
}
this._selectedIndex = index;
if (this.isOpen && item && this.onSelectCallback) {
// Call the user-defined select callback if defined.
this.onSelectCallback();
}
this._updateAriaActiveDescendant();
},
/**
@ -366,8 +336,7 @@ AutocompletePopup.prototype = {
* @type Object
*/
get selectedItem() {
return this._list.selectedItem ?
this._list.selectedItem._autocompleteItem : null;
return this.items[this._selectedIndex];
},
/**
@ -377,11 +346,10 @@ AutocompletePopup.prototype = {
* The object you want selected in the list.
*/
set selectedItem(item) {
this._list.selectedItem = this._findListItem(item);
if (this.isOpen) {
this._list.ensureIndexIsVisible(this._list.selectedIndex);
let index = this.items.indexOf(item);
if (index !== -1 && this.isOpen) {
this.selectedIndex = index;
}
this._updateAriaActiveDescendant();
},
/**
@ -401,54 +369,36 @@ AutocompletePopup.prototype = {
* autocompleted label.
*/
appendItem: function (item) {
let listItem = this._document.createElementNS(XUL_NS, "richlistitem");
let listItem = this._document.createElementNS(HTML_NS, "li");
// Items must have an id for accessibility.
listItem.id = this._panel.id + "_item_" + this._itemIdCounter++;
listItem.setAttribute("id", "autocomplete-item-" + itemIdCounter++);
listItem.className = "autocomplete-item";
listItem.setAttribute("data-index", this.items.length);
if (this.direction) {
listItem.setAttribute("dir", this.direction);
}
let label = this._document.createElementNS(XUL_NS, "label");
label.setAttribute("value", item.label);
label.setAttribute("class", "autocomplete-value");
let label = this._document.createElementNS(HTML_NS, "span");
label.textContent = item.label;
label.className = "autocomplete-value";
if (item.preLabel) {
let preDesc = this._document.createElementNS(XUL_NS, "label");
preDesc.setAttribute("value", item.preLabel);
preDesc.setAttribute("class", "initial-value");
let preDesc = this._document.createElementNS(HTML_NS, "span");
preDesc.textContent = item.preLabel;
preDesc.className = "initial-value";
listItem.appendChild(preDesc);
label.setAttribute("value", item.label.slice(item.preLabel.length));
label.textContent = item.label.slice(item.preLabel.length);
}
listItem.appendChild(label);
if (item.count && item.count > 1) {
let countDesc = this._document.createElementNS(XUL_NS, "label");
countDesc.setAttribute("value", item.count);
let countDesc = this._document.createElementNS(HTML_NS, "span");
countDesc.textContent = item.count;
countDesc.setAttribute("flex", "1");
countDesc.setAttribute("class", "autocomplete-count");
countDesc.className = "autocomplete-count";
listItem.appendChild(countDesc);
}
listItem._autocompleteItem = item;
this._list.appendChild(listItem);
},
/**
* Find the richlistitem element that belongs to an item.
*
* @private
*
* @param {Object} item
* The object you want found in the list.
*
* @return {nsIDOMNode} The nsIDOMNode that belongs to the given item object.
* This node is the richlistitem element. Can be null.
*/
_findListItem: function (item) {
for (let i = 0; i < this._list.childNodes.length; i++) {
let child = this._list.childNodes[i];
if (child._autocompleteItem == item) {
return child;
}
}
return null;
this.items.push(item);
this.elements.set(item, listItem);
},
/**
@ -458,11 +408,26 @@ AutocompletePopup.prototype = {
* The item you want removed.
*/
removeItem: function (item) {
let listItem = this._findListItem(item);
if (!listItem) {
throw new Error("Item not found!");
if (!this.items.includes(item)) {
return;
}
let itemIndex = this.items.indexOf(item);
let selectedIndex = this.selectedIndex;
// Remove autocomplete item.
this.items.splice(itemIndex, 1);
// Remove corresponding DOM element from the elements WeakMap and from the DOM.
let elementToRemove = this.elements.get(item);
this.elements.delete(elementToRemove);
elementToRemove.remove();
if (itemIndex <= selectedIndex) {
// If the removed item index was before or equal to the selected index, shift the
// selected index by 1.
this.selectedIndex = Math.max(0, selectedIndex - 1);
}
this._list.removeChild(listItem);
},
/**
@ -470,7 +435,7 @@ AutocompletePopup.prototype = {
* @type {Number}
*/
get itemCount() {
return this._list.childNodes.length;
return this.items.length;
},
/**
@ -478,8 +443,14 @@ AutocompletePopup.prototype = {
*
* @type {Number}
*/
get _itemHeight() {
return this._list.selectedItem.clientHeight;
get _itemsPerPane() {
if (this.items.length) {
let listHeight = this._tooltip.panel.clientHeight;
let element = this.elements.get(this.items[0]);
let elementHeight = element.getBoundingClientRect().height;
return Math.floor(listHeight / elementHeight);
}
return 0;
},
/**
@ -489,12 +460,11 @@ AutocompletePopup.prototype = {
* The newly selected item object.
*/
selectNextItem: function () {
if (this.selectedIndex < (this.itemCount - 1)) {
if (this.selectedIndex < (this.items.length - 1)) {
this.selectedIndex++;
} else {
this.selectedIndex = 0;
}
return this.selectedItem;
},
@ -508,7 +478,7 @@ AutocompletePopup.prototype = {
if (this.selectedIndex > 0) {
this.selectedIndex--;
} else {
this.selectedIndex = this.itemCount - 1;
this.selectedIndex = this.items.length - 1;
}
return this.selectedItem;
@ -522,11 +492,8 @@ AutocompletePopup.prototype = {
* The newly-selected item object.
*/
selectNextPageItem: function () {
let itemsPerPane = Math.floor(this._list.scrollHeight / this._itemHeight);
let nextPageIndex = this.selectedIndex + itemsPerPane + 1;
this.selectedIndex = nextPageIndex > this.itemCount - 1 ?
this.itemCount - 1 : nextPageIndex;
let nextPageIndex = this.selectedIndex + this._itemsPerPane + 1;
this.selectedIndex = Math.min(nextPageIndex, this.itemCount - 1);
return this.selectedItem;
},
@ -538,20 +505,11 @@ AutocompletePopup.prototype = {
* The newly-selected item object.
*/
selectPreviousPageItem: function () {
let itemsPerPane = Math.floor(this._list.scrollHeight / this._itemHeight);
let prevPageIndex = this.selectedIndex - itemsPerPane - 1;
this.selectedIndex = prevPageIndex < 0 ? 0 : prevPageIndex;
let prevPageIndex = this.selectedIndex - this._itemsPerPane - 1;
this.selectedIndex = Math.max(prevPageIndex, 0);
return this.selectedItem;
},
/**
* Focuses the richlistbox.
*/
focus: function () {
this._list.focus();
},
/**
* Manages theme switching for the popup based on the devtools.theme pref.
*
@ -567,11 +525,25 @@ AutocompletePopup.prototype = {
* - oldValue {Object} The old value of the preference.
*/
_handleThemeChange: function (event, data) {
if (data.pref == "devtools.theme") {
this._panel.classList.toggle(data.oldValue + "-theme", false);
this._panel.classList.toggle(data.newValue + "-theme", true);
if (data.pref === "devtools.theme") {
this._tooltip.panel.classList.toggle(data.oldValue + "-theme", false);
this._tooltip.panel.classList.toggle(data.newValue + "-theme", true);
this._list.classList.toggle(data.oldValue + "-theme", false);
this._list.classList.toggle(data.newValue + "-theme", true);
}
},
/**
* Used by tests.
*/
get _panel() {
return this._tooltip.panel;
},
/**
* Used by tests.
*/
get _window() {
return this._document.defaultView;
},
};

View File

@ -93,18 +93,6 @@ define(function (require, exports, module) {
return items;
},
hasSpecialProperties: function (array) {
return false;
},
// Event Handlers
onToggleProperties: function (event) {
},
onClickBracket: function (event) {
},
render: function () {
let mode = this.props.mode || "short";
let object = this.props.object;
@ -119,12 +107,8 @@ define(function (require, exports, module) {
}
return (
ObjectBox({
className: "array",
onClick: this.onToggleProperties},
a({
className: "objectLink",
onclick: this.onClickBracket},
ObjectBox({className: "array"},
a({className: "objectLink"},
span({
className: "arrayLeftBracket",
role: "presentation"},
@ -132,9 +116,7 @@ define(function (require, exports, module) {
)
),
items,
a({
className: "objectLink",
onclick: this.onClickBracket},
a({className: "objectLink"},
span({
className: "arrayRightBracket",
role: "presentation"},

View File

@ -11,6 +11,7 @@ support-files =
[test_reps_date-time.html]
[test_reps_function.html]
[test_reps_grip.html]
[test_reps_grip-array.html]
[test_reps_null.html]
[test_reps_number.html]
[test_reps_object-with-text.html]

View File

@ -0,0 +1,262 @@
<!DOCTYPE HTML>
<html>
<!--
Test GripArray rep
-->
<head>
<meta charset="utf-8">
<title>Rep test - GripArray</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
</head>
<body>
<pre id="test">
<script src="head.js" type="application/javascript;version=1.8"></script>
<script type="application/javascript;version=1.8">
window.onload = Task.async(function* () {
let { Rep } = browserRequire("devtools/client/shared/components/reps/rep");
let { GripArray } = browserRequire("devtools/client/shared/components/reps/grip-array");
let componentUnderTest = GripArray;
try {
yield testBasic();
// Test property iterator
yield testMaxProps();
yield testMoreThanMaxProps();
yield testRecursiveArray();
} catch(e) {
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
} finally {
SimpleTest.finish();
}
function testBasic() {
// Test array: `[]`
const testName = "testBasic";
// Test that correct rep is chosen
const gripStub = getGripStub("testBasic");
const renderedRep = shallowRenderComponent(Rep, { object: gripStub });
is(renderedRep.type, GripArray.rep, `Rep correctly selects ${GripArray.rep.displayName}`);
// Test rendering
const defaultOutput = `[]`;
const modeTests = [
{
mode: undefined,
expectedOutput: defaultOutput,
},
{
mode: "tiny",
expectedOutput: `[0]`,
},
{
mode: "short",
expectedOutput: defaultOutput,
},
{
mode: "long",
expectedOutput: defaultOutput,
}
];
testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
}
function testMaxProps() {
// Test array: `[1, "foo", {}]`;
const testName = "testMaxProps";
const defaultOutput = `[1, "foo", Object]`;
const modeTests = [
{
mode: undefined,
expectedOutput: defaultOutput,
},
{
mode: "tiny",
expectedOutput: `[3]`,
},
{
mode: "short",
expectedOutput: defaultOutput,
},
{
mode: "long",
expectedOutput: defaultOutput,
}
];
testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
}
function testMoreThanMaxProps() {
// Test array = `["test string"...] //301 items`
const testName = "testMoreThanMaxProps";
const defaultOutput = `[${Array(3).fill("\"test string\"").join(", ")}, more...]`;
const modeTests = [
{
mode: undefined,
expectedOutput: defaultOutput,
},
{
mode: "tiny",
expectedOutput: `[302]`,
},
{
mode: "short",
expectedOutput: defaultOutput,
},
{
mode: "long",
expectedOutput: `[${Array(300).fill("\"test string\"").join(", ")}, more...]`,
}
];
testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
}
function testRecursiveArray() {
// @TODO This is not how this feature should actually work
// See Bug 1282465 - Reps: fix or remove recursive handling in grip-array
// Test array = `let a = []; a = [a]`
const testName = "testRecursiveArray";
const defaultOutput = `[[1]]`;
const modeTests = [
{
mode: undefined,
expectedOutput: defaultOutput,
},
{
mode: "tiny",
expectedOutput: `[1]`,
},
{
mode: "short",
expectedOutput: defaultOutput,
},
{
mode: "long",
expectedOutput: defaultOutput,
}
];
testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
}
function getGripStub(functionName) {
switch (functionName) {
case "testBasic":
return {
"type": "object",
"class": "Array",
"actor": "server1.conn0.obj35",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 1,
"preview": {
"kind": "ArrayLike",
"length": 0,
"items": []
}
};
case "testMaxProps":
return {
"type": "object",
"class": "Array",
"actor": "server1.conn1.obj35",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 4,
"preview": {
"kind": "ArrayLike",
"length": 3,
"items": [
1,
"foo",
{
"type": "object",
"class": "Object",
"actor": "server1.conn1.obj36",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 0
}
]
}
};
case "testMoreThanMaxProps":
let grip = {
"type": "object",
"class": "Array",
"actor": "server1.conn1.obj35",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 4,
"preview": {
"kind": "ArrayLike",
"length": 302,
"items": []
}
};
// Generate 101 properties, which is more that the maximum
// limit in case of the 'long' mode.
for (let i = 0; i < 302; i++) {
grip.preview.items.push("test string");
}
return grip;
case "testRecursiveArray":
return {
"type": "object",
"class": "Array",
"actor": "server1.conn3.obj42",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 2,
"preview": {
"kind": "ArrayLike",
"length": 1,
"items": [
{
"type": "object",
"class": "Array",
"actor": "server1.conn3.obj43",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 2,
"preview": {
"kind": "ArrayLike",
"length": 1
}
}
]
}
};
}
}
});
</script>
</pre>
</body>
</html>

View File

@ -33,7 +33,6 @@ const CONTENT_TYPES = {
CSS_MIXED: 2,
CSS_PROPERTY: 3,
};
const AUTOCOMPLETE_POPUP_CLASSNAME = "inplace-editor-autocomplete-popup";
// The limit of 500 autocomplete suggestions should not be reached but is kept
// for safety.
@ -989,13 +988,13 @@ InplaceEditor.prototype = {
let label, preLabel;
if (this._selectedIndex === undefined) {
({label, preLabel} =
this.popup.getItemAtIndex(this.popup.selectedIndex));
({label, preLabel} = this.popup.getItemAtIndex(this.popup.selectedIndex));
} else {
({label, preLabel} = this.popup.getItemAtIndex(this._selectedIndex));
}
let input = this.input;
let pre = "";
// CSS_MIXED needs special treatment here to make it so that
@ -1021,13 +1020,13 @@ InplaceEditor.prototype = {
// Wait for the popup to hide and then focus input async otherwise it does
// not work.
let onPopupHidden = () => {
this.popup._panel.removeEventListener("popuphidden", onPopupHidden);
this.popup.off("popup-closed", onPopupHidden);
this.doc.defaultView.setTimeout(()=> {
input.focus();
this.emit("after-suggest");
}, 0);
};
this.popup._panel.addEventListener("popuphidden", onPopupHidden);
this.popup.on("popup-closed", onPopupHidden);
this._hideAutocompletePopup();
},
@ -1174,7 +1173,6 @@ InplaceEditor.prototype = {
* item selected.
*/
_openAutocompletePopup: function (offset, selectedIndex) {
this.popup._panel.classList.add(AUTOCOMPLETE_POPUP_CLASSNAME);
this.popup.on("popup-click", this._onAutocompletePopupClick);
this.popup.openPopup(this.input, offset, 0, selectedIndex);
},
@ -1184,7 +1182,6 @@ InplaceEditor.prototype = {
* popup.
*/
_hideAutocompletePopup: function () {
this.popup._panel.classList.remove(AUTOCOMPLETE_POPUP_CLASSNAME);
this.popup.off("popup-click", this._onAutocompletePopupClick);
this.popup.hidePopup();
},

View File

@ -6,16 +6,16 @@
// Tests that the CubicBezierWidget generates content in a given parent node
const TEST_URI = "chrome://devtools/content/shared/widgets/cubic-bezier-frame.xhtml";
const {CubicBezierWidget} =
require("devtools/client/shared/widgets/CubicBezierWidget");
const TEST_URI = `data:text/html,<div id="cubic-bezier-container" />`;
add_task(function* () {
yield addTab("about:blank");
let [host, win, doc] = yield createHost("bottom", TEST_URI);
info("Checking that the graph markup is created in the parent");
let container = doc.querySelector("#container");
let container = doc.querySelector("#cubic-bezier-container");
let w = new CubicBezierWidget(container);
ok(container.querySelector(".display-wrap"),
@ -27,9 +27,7 @@ add_task(function* () {
is(buttons.length, 2,
"The 2 control points have been added");
is(buttons[0].className, "control-point");
is(buttons[0].id, "P1");
is(buttons[1].className, "control-point");
is(buttons[1].id, "P2");
ok(container.querySelector("canvas"), "The curve canvas has been added");
info("Destroying the widget");
@ -37,5 +35,4 @@ add_task(function* () {
is(container.children.length, 0, "All nodes have been removed");
host.destroy();
gBrowser.removeCurrentTab();
});

View File

@ -6,21 +6,26 @@
// Tests the CubicBezierWidget events
const TEST_URI = "chrome://devtools/content/shared/widgets/cubic-bezier-frame.xhtml";
const {CubicBezierWidget} =
require("devtools/client/shared/widgets/CubicBezierWidget");
const {PREDEFINED} = require("devtools/client/shared/widgets/CubicBezierPresets");
// In this test we have to use a slightly more complete HTML tree, with <body>
// in order to remove its margin and prevent shifted positions
const TEST_URI = `data:text/html,
<html><body>
<div id="cubic-bezier-container"/>
</body></html>`;
add_task(function* () {
yield addTab("about:blank");
let [host, win, doc] = yield createHost("bottom", TEST_URI);
// Required or widget will be clipped inside of 'bottom'
// host by -14. Setting `fixed` zeroes this which is needed for
// calculating offsets. Occurs in test env only.
doc.body.setAttribute("style", "position: fixed");
doc.body.setAttribute("style", "position: fixed; margin: 0;");
let container = doc.querySelector("#container");
let container = doc.querySelector("#cubic-bezier-container");
let w = new CubicBezierWidget(container, PREDEFINED.linear);
let rect = w.curve.getBoundingClientRect();
@ -34,7 +39,6 @@ add_task(function* () {
w.destroy();
host.destroy();
gBrowser.removeCurrentTab();
});
function* pointsCanBeDragged(widget, win, doc, offsets) {

View File

@ -6,16 +6,16 @@
// Tests that coordinates can be changed programatically in the CubicBezierWidget
const TEST_URI = "chrome://devtools/content/shared/widgets/cubic-bezier-frame.xhtml";
const {CubicBezierWidget} =
require("devtools/client/shared/widgets/CubicBezierWidget");
const {PREDEFINED} = require("devtools/client/shared/widgets/CubicBezierPresets");
const TEST_URI = `data:text/html,<div id="cubic-bezier-container" />`;
add_task(function* () {
yield addTab("about:blank");
let [host, win, doc] = yield createHost("bottom", TEST_URI);
let container = doc.querySelector("#container");
let container = doc.querySelector("#cubic-bezier-container");
let w = new CubicBezierWidget(container, PREDEFINED.linear);
yield coordinatesCanBeChangedByProvidingAnArray(w);
@ -23,7 +23,6 @@ add_task(function* () {
w.destroy();
host.destroy();
gBrowser.removeCurrentTab();
});
function* coordinatesCanBeChangedByProvidingAnArray(widget) {

View File

@ -6,16 +6,16 @@
// Tests that the CubicBezierPresetWidget generates markup.
const TEST_URI = "chrome://devtools/content/shared/widgets/cubic-bezier-frame.xhtml";
const {CubicBezierPresetWidget} =
require("devtools/client/shared/widgets/CubicBezierWidget");
const {PRESETS} = require("devtools/client/shared/widgets/CubicBezierPresets");
const TEST_URI = `data:text/html,<div id="cubic-bezier-container" />`;
add_task(function* () {
yield addTab("about:blank");
let [host, win, doc] = yield createHost("bottom", TEST_URI);
let container = doc.querySelector("#container");
let container = doc.querySelector("#cubic-bezier-container");
let w = new CubicBezierPresetWidget(container);
info("Checking that the presets are created in the parent");
@ -47,5 +47,4 @@ add_task(function* () {
w.destroy();
host.destroy();
gBrowser.removeCurrentTab();
});

View File

@ -6,17 +6,17 @@
// Tests that the CubicBezierPresetWidget cycles menus
const TEST_URI = "chrome://devtools/content/shared/widgets/cubic-bezier-frame.xhtml";
const {CubicBezierPresetWidget} =
require("devtools/client/shared/widgets/CubicBezierWidget");
const {PREDEFINED, PRESETS, DEFAULT_PRESET_CATEGORY} =
require("devtools/client/shared/widgets/CubicBezierPresets");
const TEST_URI = `data:text/html,<div id="cubic-bezier-container" />`;
add_task(function* () {
yield addTab("about:blank");
let [host, win, doc] = yield createHost("bottom", TEST_URI);
let container = doc.querySelector("#container");
let container = doc.querySelector("#cubic-bezier-container");
let w = new CubicBezierPresetWidget(container);
info("Checking that preset is selected if coordinates are known");
@ -45,5 +45,4 @@ add_task(function* () {
w.destroy();
host.destroy();
gBrowser.removeCurrentTab();
});

View File

@ -7,16 +7,16 @@
// Tests the integration between CubicBezierWidget and CubicBezierPresets
const TEST_URI = "chrome://devtools/content/shared/widgets/cubic-bezier-frame.xhtml";
const {CubicBezierWidget} =
require("devtools/client/shared/widgets/CubicBezierWidget");
const {PRESETS} = require("devtools/client/shared/widgets/CubicBezierPresets");
const TEST_URI = `data:text/html,<div id="cubic-bezier-container" />`;
add_task(function* () {
yield addTab("about:blank");
let [host, win, doc] = yield createHost("bottom", TEST_URI);
let container = doc.querySelector("#container");
let container = doc.querySelector("#cubic-bezier-container");
let w = new CubicBezierWidget(container,
PRESETS["ease-in"]["ease-in-sine"]);
w.presets.refreshMenu(PRESETS["ease-in"]["ease-in-sine"]);
@ -29,7 +29,6 @@ add_task(function* () {
w.destroy();
host.destroy();
gBrowser.removeCurrentTab();
});
function* adjustingBezierUpdatesPreset(widget, win, doc, rect) {

View File

@ -5,11 +5,12 @@
// Tests that the Filter Editor Widget parses filter values correctly (setCssValue)
const TEST_URI = "chrome://devtools/content/shared/widgets/filter-frame.xhtml";
const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
const DOMUtils =
Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
const TEST_URI = `data:text/html,<div id="filter-container" />`;
// Verify that the given string consists of a valid CSS URL token.
// Return true on success, false on error.
function verifyURL(string) {
@ -24,10 +25,9 @@ function verifyURL(string) {
}
add_task(function* () {
yield addTab("about:blank");
let [host, win, doc] = yield createHost("bottom", TEST_URI);
const container = doc.querySelector("#container");
const container = doc.querySelector("#filter-container");
let widget = new CSSFilterEditorWidget(container, "none");
info("Test parsing of a valid CSS Filter value");

View File

@ -5,15 +5,15 @@
// Tests that the Filter Editor Widget renders filters correctly
const TEST_URI = "chrome://devtools/content/shared/widgets/filter-frame.xhtml";
const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
const { LocalizationHelper } = require("devtools/client/shared/l10n");
const STRINGS_URI = "chrome://devtools/locale/filterwidget.properties";
const L10N = new LocalizationHelper(STRINGS_URI);
const TEST_URI = `data:text/html,<div id="filter-container" />`;
add_task(function* () {
yield addTab("about:blank");
let [host, win, doc] = yield createHost("bottom", TEST_URI);
const TEST_DATA = [
@ -68,7 +68,7 @@ add_task(function* () {
}
];
const container = doc.querySelector("#container");
const container = doc.querySelector("#filter-container");
let widget = new CSSFilterEditorWidget(container, "none");
info("Test rendering of different types");

View File

@ -5,17 +5,16 @@
// Tests the Filter Editor Widget add, removeAt, updateAt, getValueAt methods
const BASE_URI = "chrome://devtools/content/shared/widgets/";
const TEST_URI = BASE_URI + "filter-frame.xhtml";
const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
const GRAYSCALE_MAX = 100;
const INVERT_MIN = 0;
const TEST_URI = `data:text/html,<div id="filter-container" />`;
add_task(function* () {
yield addTab("about:blank");
let [host, win, doc] = yield createHost("bottom", TEST_URI);
const container = doc.querySelector("#container");
const container = doc.querySelector("#filter-container");
let widget = new CSSFilterEditorWidget(container, "none");
info("Test add method");

View File

@ -5,15 +5,15 @@
// Tests the Filter Editor Widget's drag-drop re-ordering
const TEST_URI = "chrome://devtools/content/shared/widgets/filter-frame.xhtml";
const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
const LIST_ITEM_HEIGHT = 32;
const TEST_URI = `data:text/html,<div id="filter-container" />`;
add_task(function* () {
yield addTab("about:blank");
let [host, win, doc] = yield createHost("bottom", TEST_URI);
const container = doc.querySelector("#container");
const container = doc.querySelector("#filter-container");
const initialValue = "blur(2px) contrast(200%) brightness(200%)";
let widget = new CSSFilterEditorWidget(container, initialValue);

View File

@ -7,7 +7,6 @@ requestLongerTimeout(2);
// Tests the Filter Editor Widget's label-dragging
const TEST_URI = "chrome://devtools/content/shared/widgets/filter-frame.xhtml";
const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
const FAST_VALUE_MULTIPLIER = 10;
@ -17,11 +16,12 @@ const DEFAULT_VALUE_MULTIPLIER = 1;
const GRAYSCALE_MAX = 100,
GRAYSCALE_MIN = 0;
const TEST_URI = `data:text/html,<div id="filter-container" />`;
add_task(function* () {
yield addTab("about:blank");
let [host, win, doc] = yield createHost("bottom", TEST_URI);
const container = doc.querySelector("#container");
const container = doc.querySelector("#filter-container");
let widget = new CSSFilterEditorWidget(container, "grayscale(0%) url(test.svg)");
const filters = widget.el.querySelector("#filters");

View File

@ -5,19 +5,18 @@
// Tests the Filter Editor Widget's add button
const TEST_URI = "chrome://devtools/content/shared/widgets/filter-frame.xhtml";
const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
const { LocalizationHelper } = require("devtools/client/shared/l10n");
const STRINGS_URI = "chrome://devtools/locale/filterwidget.properties";
const L10N = new LocalizationHelper(STRINGS_URI);
const TEST_URI = `data:text/html,<div id="filter-container" />`;
add_task(function* () {
yield addTab("about:blank");
let [host, win, doc] = yield createHost("bottom", TEST_URI);
const container = doc.querySelector("#container");
const container = doc.querySelector("#filter-container");
let widget = new CSSFilterEditorWidget(container, "none");
const select = widget.el.querySelector("select"),

View File

@ -5,19 +5,18 @@
// Tests the Filter Editor Widget's remove button
const TEST_URI = "chrome://devtools/content/shared/widgets/filter-frame.xhtml";
const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
const { LocalizationHelper } = require("devtools/client/shared/l10n");
const STRINGS_URI = "chrome://devtools/locale/filterwidget.properties";
const L10N = new LocalizationHelper(STRINGS_URI);
const TEST_URI = `data:text/html,<div id="filter-container" />`;
add_task(function* () {
yield addTab("about:blank");
let [host, win, doc] = yield createHost("bottom", TEST_URI);
const container = doc.querySelector("#container");
const container = doc.querySelector("#filter-container");
let widget = new CSSFilterEditorWidget(container, "blur(2px) contrast(200%)");
info("Test removing filters with remove button");

View File

@ -6,18 +6,18 @@
// Tests the Filter Editor Widget inputs increase/decrease value using
// arrow keys, applying multiplier using alt/shift on number-type filters
const TEST_URI = "chrome://devtools/content/shared/widgets/filter-frame.xhtml";
const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
const FAST_VALUE_MULTIPLIER = 10;
const SLOW_VALUE_MULTIPLIER = 0.1;
const DEFAULT_VALUE_MULTIPLIER = 1;
const TEST_URI = `data:text/html,<div id="filter-container" />`;
add_task(function* () {
yield addTab("about:blank");
let [host, win, doc] = yield createHost("bottom", TEST_URI);
const container = doc.querySelector("#container");
const container = doc.querySelector("#filter-container");
const initialValue = "blur(2px)";
let widget = new CSSFilterEditorWidget(container, initialValue);

View File

@ -6,18 +6,18 @@
// Tests the Filter Editor Widget inputs increase/decrease value when cursor is
// on a number using arrow keys, applying multiplier using alt/shift on strings
const TEST_URI = "chrome://devtools/content/shared/widgets/filter-frame.xhtml";
const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
const FAST_VALUE_MULTIPLIER = 10;
const SLOW_VALUE_MULTIPLIER = 0.1;
const DEFAULT_VALUE_MULTIPLIER = 1;
const TEST_URI = `data:text/html,<div id="filter-container" />`;
add_task(function* () {
yield addTab("about:blank");
let [host, win, doc] = yield createHost("bottom", TEST_URI);
const container = doc.querySelector("#container");
const container = doc.querySelector("#filter-container");
const initialValue = "drop-shadow(rgb(0, 0, 0) 1px 1px 0px)";
let widget = new CSSFilterEditorWidget(container, initialValue);
widget.el.querySelector("#filters input").setSelectionRange(13, 13);

View File

@ -6,18 +6,18 @@
// Tests the Filter Editor Widget inputs increase/decrease value when cursor is
// on a number using arrow keys if cursor is behind/mid/after the number strings
const TEST_URI = "chrome://devtools/content/shared/widgets/filter-frame.xhtml";
const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
const FAST_VALUE_MULTIPLIER = 10;
const SLOW_VALUE_MULTIPLIER = 0.1;
const DEFAULT_VALUE_MULTIPLIER = 1;
const TEST_URI = `data:text/html,<div id="filter-container" />`;
add_task(function* () {
yield addTab("about:blank");
let [host, win, doc] = yield createHost("bottom", TEST_URI);
const container = doc.querySelector("#container");
const container = doc.querySelector("#filter-container");
const initialValue = "drop-shadow(rgb(0, 0, 0) 10px 1px 0px)";
let widget = new CSSFilterEditorWidget(container, initialValue);
const input = widget.el.querySelector("#filters input");

View File

@ -5,15 +5,14 @@
// Tests saving presets
const TEST_URI = "chrome://devtools/content/shared/widgets/filter-frame.xhtml";
const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
add_task(function* () {
yield addTab("about:blank");
const TEST_URI = `data:text/html,<div id="filter-container" />`;
add_task(function* () {
let [host, win, doc] = yield createHost("bottom", TEST_URI);
const container = doc.querySelector("#container");
const container = doc.querySelector("#filter-container");
let widget = new CSSFilterEditorWidget(container, "none");
// First render
yield widget.once("render");

View File

@ -5,15 +5,14 @@
// Tests loading presets
const TEST_URI = "chrome://devtools/content/shared/widgets/filter-frame.xhtml";
const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
add_task(function* () {
yield addTab("about:blank");
const TEST_URI = `data:text/html,<div id="filter-container" />`;
add_task(function* () {
let [host, win, doc] = yield createHost("bottom", TEST_URI);
const container = doc.querySelector("#container");
const container = doc.querySelector("#filter-container");
let widget = new CSSFilterEditorWidget(container, "none");
// First render
yield widget.once("render");

View File

@ -5,15 +5,14 @@
// Tests deleting presets
const TEST_URI = "chrome://devtools/content/shared/widgets/filter-frame.xhtml";
const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
add_task(function* () {
yield addTab("about:blank");
const TEST_URI = `data:text/html,<div id="filter-container" />`;
add_task(function* () {
let [host, win, doc] = yield createHost("bottom", TEST_URI);
const container = doc.querySelector("#container");
const container = doc.querySelector("#filter-container");
let widget = new CSSFilterEditorWidget(container, "none");
// First render
yield widget.once("render");

View File

@ -48,7 +48,7 @@ add_task(function* () {
let [host, win, doc] = yield createHost();
let xulDocument = win.top.document;
let popup = new AutocompletePopup(xulDocument, { autoSelect: true });
let popup = new AutocompletePopup({ doc: xulDocument }, { autoSelect: true });
yield new Promise(resolve => {
createInplaceEditorAndClick({
start: runPropertyAutocompletionTest,
@ -58,6 +58,7 @@ add_task(function* () {
}, doc);
});
popup.destroy();
host.destroy();
gBrowser.removeCurrentTab();
});

View File

@ -49,7 +49,7 @@ add_task(function* () {
let [host, win, doc] = yield createHost();
let xulDocument = win.top.document;
let popup = new AutocompletePopup(xulDocument, { autoSelect: true });
let popup = new AutocompletePopup({ doc: xulDocument }, { autoSelect: true });
yield new Promise(resolve => {
createInplaceEditorAndClick({
@ -63,6 +63,7 @@ add_task(function* () {
}, doc);
});
popup.destroy();
host.destroy();
gBrowser.removeCurrentTab();
});

View File

@ -9,6 +9,14 @@ const { InplaceEditor } = require("devtools/client/shared/inplace-editor");
const { AutocompletePopup } = require("devtools/client/shared/autocomplete-popup");
loadHelperScript("helper_inplace_editor.js");
const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/global.css"?>
<?xml-stylesheet href="chrome://devtools/skin/common.css"?>
<?xml-stylesheet href="chrome://devtools/skin/tooltips.css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="Tooltip test">
</window>`;
// Test the inplace-editor autocomplete popup is aligned with the completed query.
// Which means when completing "style=display:flex; color:" the popup will aim to be
// aligned with the ":" next to "color".
@ -59,10 +67,9 @@ const mockGetCSSValuesForPropertyName = function (propertyName) {
add_task(function* () {
yield addTab("data:text/html;charset=utf-8,inplace editor CSS value autocomplete");
let [host, win, doc] = yield createHost();
let [host,, doc] = yield createHost("bottom", TEST_URI);
let xulDocument = win.top.document;
let popup = new AutocompletePopup(xulDocument, { autoSelect: true });
let popup = new AutocompletePopup({ doc }, { autoSelect: true });
info("Create a CSS_MIXED type autocomplete");
yield new Promise(resolve => {
@ -75,6 +82,7 @@ add_task(function* () {
}, doc);
});
popup.destroy();
host.destroy();
gBrowser.removeCurrentTab();
});

View File

@ -4,44 +4,42 @@
// Tests that the spectrum color picker works correctly
const TEST_URI = "chrome://devtools/content/shared/widgets/spectrum-frame.xhtml";
const {Spectrum} = require("devtools/client/shared/widgets/Spectrum");
add_task(function* () {
yield addTab("about:blank");
yield performTest();
gBrowser.removeCurrentTab();
});
const TEST_URI = `data:text/html,
<link rel="stylesheet" href="chrome://devtools/content/shared/widgets/spectrum.css" type="text/css"/>
<div id="spectrum-container" />`;
function* performTest() {
add_task(function* () {
let [host, win, doc] = yield createHost("bottom", TEST_URI);
yield testCreateAndDestroyShouldAppendAndRemoveElements(doc);
yield testPassingAColorAtInitShouldSetThatColor(doc);
yield testSettingAndGettingANewColor(doc);
yield testChangingColorShouldEmitEvents(doc);
yield testSettingColorShoudUpdateTheUI(doc);
let container = doc.getElementById("spectrum-container");
yield testCreateAndDestroyShouldAppendAndRemoveElements(container);
yield testPassingAColorAtInitShouldSetThatColor(container);
yield testSettingAndGettingANewColor(container);
yield testChangingColorShouldEmitEvents(container);
yield testSettingColorShoudUpdateTheUI(container);
host.destroy();
}
});
function testCreateAndDestroyShouldAppendAndRemoveElements(doc) {
let containerElement = doc.querySelector("#spectrum");
ok(containerElement, "We have the root node to append spectrum to");
is(containerElement.childElementCount, 0, "Root node is empty");
function testCreateAndDestroyShouldAppendAndRemoveElements(container) {
ok(container, "We have the root node to append spectrum to");
is(container.childElementCount, 0, "Root node is empty");
let s = new Spectrum(containerElement, [255, 126, 255, 1]);
let s = new Spectrum(container, [255, 126, 255, 1]);
s.show();
ok(containerElement.childElementCount > 0, "Spectrum has appended elements");
ok(container.childElementCount > 0, "Spectrum has appended elements");
s.destroy();
is(containerElement.childElementCount, 0, "Destroying spectrum removed all nodes");
is(container.childElementCount, 0, "Destroying spectrum removed all nodes");
}
function testPassingAColorAtInitShouldSetThatColor(doc) {
function testPassingAColorAtInitShouldSetThatColor(container) {
let initRgba = [255, 126, 255, 1];
let s = new Spectrum(doc.querySelector("#spectrum"), initRgba);
let s = new Spectrum(container, initRgba);
s.show();
let setRgba = s.rgb;
@ -54,8 +52,8 @@ function testPassingAColorAtInitShouldSetThatColor(doc) {
s.destroy();
}
function testSettingAndGettingANewColor(doc) {
let s = new Spectrum(doc.querySelector("#spectrum"), [0, 0, 0, 1]);
function testSettingAndGettingANewColor(container) {
let s = new Spectrum(container, [0, 0, 0, 1]);
s.show();
let colorToSet = [255, 255, 255, 1];
@ -70,9 +68,9 @@ function testSettingAndGettingANewColor(doc) {
s.destroy();
}
function testChangingColorShouldEmitEvents(doc) {
function testChangingColorShouldEmitEvents(container) {
return new Promise(resolve => {
let s = new Spectrum(doc.querySelector("#spectrum"), [255, 255, 255, 1]);
let s = new Spectrum(container, [255, 255, 255, 1]);
s.show();
s.once("changed", (event, rgba, color) => {
@ -92,8 +90,8 @@ function testChangingColorShouldEmitEvents(doc) {
});
}
function testSettingColorShoudUpdateTheUI(doc) {
let s = new Spectrum(doc.querySelector("#spectrum"), [255, 255, 255, 1]);
function testSettingColorShoudUpdateTheUI(container) {
let s = new Spectrum(container, [255, 255, 255, 1]);
s.show();
let dragHelperOriginalPos = [s.dragHelper.style.top, s.dragHelper.style.left];
let alphaHelperOriginalPos = s.alphaSliderHelper.style.left;

View File

@ -1,6 +1,7 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint no-unused-vars: [2, {"vars": "local", "args": "none"}] */
/* import-globals-from head.js */
"use strict";
@ -8,6 +9,7 @@
* Helper methods for the HTMLTooltip integration tests.
*/
const HTML_NS = "http://www.w3.org/1999/xhtml";
const { editableField } = require("devtools/client/shared/inplace-editor");
/**
@ -21,8 +23,7 @@ const { editableField } = require("devtools/client/shared/inplace-editor");
* @param {String} textContent
* (optional) String that will be used as the text content of the span.
*/
function createInplaceEditorAndClick(options, doc, textContent) {
doc.body.innerHTML = "";
const createInplaceEditorAndClick = Task.async(function* (options, doc, textContent) {
let span = options.element = createSpan(doc);
if (textContent) {
span.textContent = textContent;
@ -33,7 +34,7 @@ function createInplaceEditorAndClick(options, doc, textContent) {
info("Clicking on the inplace-editor field to turn to edit mode");
span.click();
}
});
/**
* Helper to create a span in the provided document.
@ -44,11 +45,21 @@ function createInplaceEditorAndClick(options, doc, textContent) {
*/
function createSpan(doc) {
info("Creating a new span element");
let span = doc.createElement("span");
let div = doc.createElementNS(HTML_NS, "div");
let span = doc.createElementNS(HTML_NS, "span");
span.setAttribute("tabindex", "0");
span.style.fontSize = "11px";
span.style.display = "inline-block";
span.style.width = "100px";
span.style.border = "1px solid red";
span.style.fontFamily = "monospace";
doc.body.appendChild(span);
div.style.height = "100%";
div.style.position = "absolute";
div.appendChild(span);
let parent = doc.querySelector("window") || doc.body;
parent.appendChild(div);
return span;
}
@ -68,8 +79,13 @@ function* testCompletion([key, completion, index, total], editor) {
info("Pressing key " + key);
info("Expecting " + completion);
let onSuggest;
let onVisibilityChange = null;
let open = total > 0;
if (editor.popup.isOpen != open) {
onVisibilityChange = editor.popup.once(open ? "popup-opened" : "popup-closed");
}
let onSuggest;
if (/(left|right|back_space|escape)/ig.test(key)) {
info("Adding event listener for right|back_space|escape keys");
onSuggest = once(editor.input, "keypress");
@ -82,17 +98,17 @@ function* testCompletion([key, completion, index, total], editor) {
EventUtils.synthesizeKey(key, {}, editor.input.defaultView);
yield onSuggest;
yield onVisibilityChange;
yield waitForTick();
info("Checking the state");
if (completion != null) {
if (completion !== null) {
is(editor.input.value, completion, "Correct value is autocompleted");
}
if (total === 0) {
ok(!(editor.popup && editor.popup.isOpen), "Popup is closed");
} else {
ok(editor.popup._panel.state == "open" ||
editor.popup._panel.state == "showing", "Popup is open");
ok(editor.popup.isOpen, "Popup is open");
is(editor.popup.getItems().length, total, "Number of suggestions match");
is(editor.popup.selectedIndex, index, "Expected item is selected");
}

View File

@ -32,6 +32,7 @@ const {
DEFAULT_PRESET_CATEGORY
} = require("devtools/client/shared/widgets/CubicBezierPresets");
const {getCSSLexer} = require("devtools/shared/css-lexer");
const XHTML_NS = "http://www.w3.org/1999/xhtml";
/**
* CubicBezier data structure helper
@ -253,26 +254,24 @@ CubicBezierWidget.prototype = {
_initMarkup: function () {
let doc = this.parent.ownerDocument;
let wrap = doc.createElement("div");
let wrap = doc.createElementNS(XHTML_NS, "div");
wrap.className = "display-wrap";
let plane = doc.createElement("div");
let plane = doc.createElementNS(XHTML_NS, "div");
plane.className = "coordinate-plane";
let p1 = doc.createElement("button");
let p1 = doc.createElementNS(XHTML_NS, "button");
p1.className = "control-point";
p1.id = "P1";
plane.appendChild(p1);
let p2 = doc.createElement("button");
let p2 = doc.createElementNS(XHTML_NS, "button");
p2.className = "control-point";
p2.id = "P2";
plane.appendChild(p2);
let curve = doc.createElement("canvas");
let curve = doc.createElementNS(XHTML_NS, "canvas");
curve.setAttribute("width", 150);
curve.setAttribute("height", 370);
curve.id = "curve";
curve.className = "curve";
plane.appendChild(curve);
wrap.appendChild(plane);
@ -280,14 +279,14 @@ CubicBezierWidget.prototype = {
this.parent.appendChild(wrap);
return {
p1: p1,
p2: p2,
curve: curve
p1,
p2,
curve
};
},
_removeMarkup: function () {
this.parent.ownerDocument.querySelector(".display-wrap").remove();
this.parent.querySelector(".display-wrap").remove();
},
_initEvents: function () {
@ -519,13 +518,13 @@ CubicBezierPresetWidget.prototype = {
_initMarkup: function () {
let doc = this.parent.ownerDocument;
let presetPane = doc.createElement("div");
let presetPane = doc.createElementNS(XHTML_NS, "div");
presetPane.className = "preset-pane";
let categoryList = doc.createElement("div");
let categoryList = doc.createElementNS(XHTML_NS, "div");
categoryList.id = "preset-categories";
let presetContainer = doc.createElement("div");
let presetContainer = doc.createElementNS(XHTML_NS, "div");
presetContainer.id = "preset-container";
Object.keys(PRESETS).forEach(categoryLabel => {
@ -554,7 +553,7 @@ CubicBezierPresetWidget.prototype = {
_createCategory: function (categoryLabel) {
let doc = this.parent.ownerDocument;
let category = doc.createElement("div");
let category = doc.createElementNS(XHTML_NS, "div");
category.id = categoryLabel;
category.classList.add("category");
@ -572,7 +571,7 @@ CubicBezierPresetWidget.prototype = {
_createPresetList: function (categoryLabel) {
let doc = this.parent.ownerDocument;
let presetList = doc.createElement("div");
let presetList = doc.createElementNS(XHTML_NS, "div");
presetList.id = "preset-category-" + categoryLabel;
presetList.classList.add("preset-list");
@ -587,12 +586,12 @@ CubicBezierPresetWidget.prototype = {
_createPreset: function (categoryLabel, presetLabel) {
let doc = this.parent.ownerDocument;
let preset = doc.createElement("div");
let preset = doc.createElementNS(XHTML_NS, "div");
preset.classList.add("preset");
preset.id = presetLabel;
preset.coordinates = PRESETS[categoryLabel][presetLabel];
// Create preset preview
let curve = doc.createElement("canvas");
let curve = doc.createElementNS(XHTML_NS, "canvas");
let bezier = new CubicBezier(preset.coordinates);
curve.setAttribute("height", 50);
curve.setAttribute("width", 50);
@ -604,7 +603,7 @@ CubicBezierPresetWidget.prototype = {
preset.appendChild(curve);
// Create preset label
let presetLabelElem = doc.createElement("p");
let presetLabelElem = doc.createElementNS(XHTML_NS, "p");
let presetDisplayLabel = this._normalizePresetLabel(categoryLabel,
presetLabel);
presetLabelElem.textContent = presetDisplayLabel;
@ -734,14 +733,14 @@ TimingFunctionPreviewWidget.prototype = {
_initMarkup: function () {
let doc = this.parent.ownerDocument;
let container = doc.createElement("div");
let container = doc.createElementNS(XHTML_NS, "div");
container.className = "timing-function-preview";
this.dot = doc.createElement("div");
this.dot = doc.createElementNS(XHTML_NS, "div");
this.dot.className = "dot";
container.appendChild(this.dot);
let scale = doc.createElement("div");
let scale = doc.createElementNS(XHTML_NS, "div");
scale.className = "scale";
container.appendChild(scale);
@ -780,12 +779,17 @@ TimingFunctionPreviewWidget.prototype = {
* Re-start the preview animation from the beginning
*/
restartAnimation: function () {
// Reset the animation duration in case it was changed
this.dot.style.animationDuration = (this.PREVIEW_DURATION * 2) + "ms";
// Just toggling the class won't do it unless there's a sync reflow
this.dot.classList.remove("animate");
this.dot.classList.add("animate");
this.dot.animate([
{ left: "-7px", offset: 0 },
{ left: "143px", offset: 0.25 },
{ left: "143px", offset: 0.5 },
{ left: "-7px", offset: 0.75 },
{ left: "-7px", offset: 1 }
], {
duration: (this.PREVIEW_DURATION * 2),
fill: "forwards"
});
// Restart it again after a while
this.autoRestartAnimation = setTimeout(this.restartAnimation.bind(this),

View File

@ -11,6 +11,7 @@
const EventEmitter = require("devtools/shared/event-emitter");
const { Cc, Ci } = require("chrome");
const XHTML_NS = "http://www.w3.org/1999/xhtml";
const { LocalizationHelper } = require("devtools/client/shared/l10n");
const STRINGS_URI = "chrome://devtools/locale/filterwidget.properties";
@ -151,6 +152,34 @@ exports.CSSFilterEditorWidget = CSSFilterEditorWidget;
CSSFilterEditorWidget.prototype = {
_initMarkup: function () {
let filterListSelectPlaceholder =
L10N.getStr("filterListSelectPlaceholder");
let addNewFilterButton = L10N.getStr("addNewFilterButton");
let presetsToggleButton = L10N.getStr("presetsToggleButton");
let newPresetPlaceholder = L10N.getStr("newPresetPlaceholder");
let savePresetButton = L10N.getStr("savePresetButton");
this.el.innerHTML = `
<div class="filters-list">
<div id="filters"></div>
<div class="footer">
<select value="">
<option value="">${filterListSelectPlaceholder}</option>
</select>
<button id="add-filter" class="add">${addNewFilterButton}</button>
<button id="toggle-presets">${presetsToggleButton}</button>
</div>
</div>
<div class="presets-list">
<div id="presets"></div>
<div class="footer">
<input value="" class="devtools-textinput"
placeholder="${newPresetPlaceholder}"></input>
<button class="add">${savePresetButton}</button>
</div>
</div>
`;
this.filtersList = this.el.querySelector("#filters");
this.presetsList = this.el.querySelector("#presets");
this.togglePresets = this.el.querySelector("#toggle-presets");
@ -183,7 +212,7 @@ CSSFilterEditorWidget.prototype = {
_populateFilterSelect: function () {
let select = this.filterSelect;
filterList.forEach(filter => {
let option = this.doc.createElement("option");
let option = this.doc.createElementNS(XHTML_NS, "option");
option.innerHTML = option.value = filter.name;
select.appendChild(option);
});
@ -193,31 +222,31 @@ CSSFilterEditorWidget.prototype = {
* Creates a template for filter elements which is cloned and used in render
*/
_buildFilterItemMarkup: function () {
let base = this.doc.createElement("div");
let base = this.doc.createElementNS(XHTML_NS, "div");
base.className = "filter";
let name = this.doc.createElement("div");
let name = this.doc.createElementNS(XHTML_NS, "div");
name.className = "filter-name";
let value = this.doc.createElement("div");
let value = this.doc.createElementNS(XHTML_NS, "div");
value.className = "filter-value";
let drag = this.doc.createElement("i");
let drag = this.doc.createElementNS(XHTML_NS, "i");
drag.title = L10N.getStr("dragHandleTooltipText");
let label = this.doc.createElement("label");
let label = this.doc.createElementNS(XHTML_NS, "label");
name.appendChild(drag);
name.appendChild(label);
let unitPreview = this.doc.createElement("span");
let input = this.doc.createElement("input");
let unitPreview = this.doc.createElementNS(XHTML_NS, "span");
let input = this.doc.createElementNS(XHTML_NS, "input");
input.classList.add("devtools-textinput");
value.appendChild(input);
value.appendChild(unitPreview);
let removeButton = this.doc.createElement("button");
let removeButton = this.doc.createElementNS(XHTML_NS, "button");
removeButton.className = "remove-button";
base.appendChild(name);
@ -228,16 +257,16 @@ CSSFilterEditorWidget.prototype = {
},
_buildPresetItemMarkup: function () {
let base = this.doc.createElement("div");
let base = this.doc.createElementNS(XHTML_NS, "div");
base.classList.add("preset");
let name = this.doc.createElement("label");
let name = this.doc.createElementNS(XHTML_NS, "label");
base.appendChild(name);
let value = this.doc.createElement("span");
let value = this.doc.createElementNS(XHTML_NS, "span");
base.appendChild(value);
let removeButton = this.doc.createElement("button");
let removeButton = this.doc.createElementNS(XHTML_NS, "button");
removeButton.classList.add("remove-button");
base.appendChild(removeButton);
@ -659,6 +688,11 @@ CSSFilterEditorWidget.prototype = {
renderPresets: function () {
this.getPresets().then(presets => {
// getPresets is async and the widget may be destroyed in between.
if (!this.presetsList) {
return;
}
if (!presets || !presets.length) {
this.presetsList.innerHTML = `<p>${L10N.getStr("emptyPresetList")}</p>`;
this.emit("render");

View File

@ -195,12 +195,15 @@ const getRelativeRect = function (node, relativeTo) {
* - {Boolean} useXulWrapper
* Defaults to false. If the tooltip is hosted in a XUL document, use a XUL panel
* in order to use all the screen viewport available.
* - {String} stylesheet
* Style sheet URL to apply to the tooltip content.
*/
function HTMLTooltip(toolbox, {
type = "normal",
autofocus = false,
consumeOutsideClicks = true,
useXulWrapper = false,
stylesheet = "",
} = {}) {
EventEmitter.decorate(this);
@ -223,6 +226,9 @@ function HTMLTooltip(toolbox, {
this.container = this._createContainer();
if (stylesheet) {
this._applyStylesheet(stylesheet);
}
if (this._isXUL() && this.useXulWrapper) {
// When using a XUL panel as the wrapper, the actual markup for the tooltip is as
// follows :
@ -420,6 +426,7 @@ HTMLTooltip.prototype = {
hide: Task.async(function* () {
this.doc.defaultView.clearTimeout(this.attachEventsTimer);
if (!this.isVisible()) {
this.emit("hidden");
return;
}
@ -500,7 +507,7 @@ HTMLTooltip.prototype = {
}
// Check if the node window is in the tooltip container.
while (win.parent && win.parent != win) {
while (win.parent && win.parent !== win) {
if (win.parent === tooltipWindow) {
// If the parent window is the tooltip window, check if the tooltip contains
// the current frame element.
@ -573,4 +580,16 @@ HTMLTooltip.prototype = {
top += this.doc.defaultView.mozInnerScreenY;
return {top, right: left + width, bottom: top + height, left, width, height};
},
/**
* Apply a scoped stylesheet to the container so that this css file only
* applies to it.
*/
_applyStylesheet: function (url) {
let style = this.doc.createElementNS(XHTML_NS, "style");
style.setAttribute("scoped", "true");
url = url.replace(/"/g, "\\\"");
style.textContent = `@import url("${url}");`;
this.container.appendChild(style);
}
};

View File

@ -5,6 +5,7 @@
"use strict";
const EventEmitter = require("devtools/shared/event-emitter");
const XHTML_NS = "http://www.w3.org/1999/xhtml";
/**
* Spectrum creates a color picker widget in any container you give it.
@ -32,7 +33,7 @@ const EventEmitter = require("devtools/shared/event-emitter");
function Spectrum(parentEl, rgb) {
EventEmitter.decorate(this);
this.element = parentEl.ownerDocument.createElement("div");
this.element = parentEl.ownerDocument.createElementNS(XHTML_NS, "div");
this.parentEl = parentEl;
this.element.className = "spectrum-container";
@ -131,21 +132,6 @@ Spectrum.rgbToHsv = function (r, g, b, a) {
return [h, s, v, a];
};
Spectrum.getOffset = function (el) {
let curleft = 0, curtop = 0;
if (el.offsetParent) {
while (el) {
curleft += el.offsetLeft;
curtop += el.offsetTop;
el = el.offsetParent;
}
}
return {
left: curleft,
top: curtop
};
};
Spectrum.draggable = function (element, onmove, onstart, onstop) {
onmove = onmove || function () {};
onstart = onstart || function () {};
@ -188,7 +174,7 @@ Spectrum.draggable = function (element, onmove, onstart, onstop) {
maxHeight = element.offsetHeight;
maxWidth = element.offsetWidth;
offset = Spectrum.getOffset(element);
offset = element.getBoundingClientRect();
move(e);

View File

@ -19,6 +19,8 @@ const {Eyedropper} = require("devtools/client/eyedropper/eyedropper");
const {gDevTools} = require("devtools/client/framework/devtools");
const Services = require("Services");
const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
loader.lazyRequireGetter(this, "beautify", "devtools/shared/jsbeautify/beautify");
loader.lazyRequireGetter(this, "setNamedTimeout", "devtools/client/shared/widgets/view-helpers", true);
@ -31,11 +33,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "VariablesViewController",
"resource://devtools/client/shared/widgets/VariablesViewController.jsm");
const XHTML_NS = "http://www.w3.org/1999/xhtml";
const SPECTRUM_FRAME = "chrome://devtools/content/shared/widgets/spectrum-frame.xhtml";
const CUBIC_BEZIER_FRAME =
"chrome://devtools/content/shared/widgets/cubic-bezier-frame.xhtml";
const MDN_DOCS_FRAME = "chrome://devtools/content/shared/widgets/mdn-docs-frame.xhtml";
const FILTER_FRAME = "chrome://devtools/content/shared/widgets/filter-frame.xhtml";
const ESCAPE_KEYCODE = Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE;
const RETURN_KEYCODE = Ci.nsIDOMKeyEvent.DOM_VK_RETURN;
const POPUP_EVENTS = ["shown", "hidden", "showing", "hiding"];
@ -557,99 +555,6 @@ Tooltip.prototype = {
return def.promise;
},
/**
* Fill the tooltip with a new instance of the spectrum color picker widget
* initialized with the given color, and return a promise that resolves to
* the instance of spectrum
*/
setColorPickerContent: function (color) {
let dimensions = {width: "210", height: "216"};
let panel = this.panel;
return this.setIFrameContent(dimensions, SPECTRUM_FRAME).then(onLoaded);
function onLoaded(iframe) {
let win = iframe.contentWindow.wrappedJSObject;
let def = defer();
let container = win.document.getElementById("spectrum");
let spectrum = new Spectrum(container, color);
function finalizeSpectrum() {
spectrum.show();
def.resolve(spectrum);
}
// Finalize spectrum's init when the tooltip becomes visible
if (panel.state == "open") {
finalizeSpectrum();
} else {
panel.addEventListener("popupshown", function shown() {
panel.removeEventListener("popupshown", shown, true);
finalizeSpectrum();
}, true);
}
return def.promise;
}
},
/**
* Fill the tooltip with a new instance of the cubic-bezier widget
* initialized with the given value, and return a promise that resolves to
* the instance of the widget
*/
setCubicBezierContent: function (bezier) {
let dimensions = {width: "500", height: "360"};
let panel = this.panel;
return this.setIFrameContent(dimensions, CUBIC_BEZIER_FRAME).then(onLoaded);
function onLoaded(iframe) {
let win = iframe.contentWindow.wrappedJSObject;
let def = defer();
let container = win.document.getElementById("container");
let widget = new CubicBezierWidget(container, bezier);
// Resolve to the widget instance whenever the popup becomes visible
if (panel.state == "open") {
def.resolve(widget);
} else {
panel.addEventListener("popupshown", function shown() {
panel.removeEventListener("popupshown", shown, true);
def.resolve(widget);
}, true);
}
return def.promise;
}
},
/**
* Fill the tooltip with a new instance of the CSSFilterEditorWidget
* widget initialized with the given filter value, and return a promise
* that resolves to the instance of the widget when ready.
*/
setFilterContent: function (filter) {
let dimensions = {width: "500", height: "200"};
let panel = this.panel;
return this.setIFrameContent(dimensions, FILTER_FRAME).then(onLoaded);
function onLoaded(iframe) {
let win = iframe.contentWindow.wrappedJSObject;
let def = defer();
let container = win.document.getElementById("container");
let widget = new CSSFilterEditorWidget(container, filter);
// Resolve to the widget instance whenever the popup becomes visible
if (panel.state === "open") {
def.resolve(widget);
} else {
panel.addEventListener("popupshown", function shown() {
panel.removeEventListener("popupshown", shown, true);
def.resolve(widget);
}, true);
}
return def.promise;
}
},
/**
* Set the content of this tooltip to the MDN docs widget.
*
@ -681,29 +586,44 @@ Tooltip.prototype = {
* Base class for all (color, gradient, ...)-swatch based value editors inside
* tooltips
*
* @param {XULDocument} doc
* @param {Toolbox} toolbox
* The devtools toolbox, needed to get the devtools main window.
*/
function SwatchBasedEditorTooltip(doc) {
function SwatchBasedEditorTooltip(toolbox, stylesheet) {
// Creating a tooltip instance
// This one will consume outside clicks as it makes more sense to let the user
// close the tooltip by clicking out
// It will also close on <escape> and <enter>
this.tooltip = new Tooltip(doc, {
consumeOutsideClick: true,
closeOnKeys: [ESCAPE_KEYCODE, RETURN_KEYCODE],
noAutoFocus: false
this.tooltip = new HTMLTooltip(toolbox, {
type: "arrow",
consumeOutsideClicks: true,
useXulWrapper: true,
stylesheet
});
// By default, swatch-based editor tooltips revert value change on <esc> and
// commit value change on <enter>
this._onTooltipKeypress = (event, code) => {
if (code === ESCAPE_KEYCODE) {
this.revert();
} else if (code === RETURN_KEYCODE) {
this.commit();
this.shortcuts = new KeyShortcuts({
window: this.tooltip.topWindow
});
this.shortcuts.on("Escape", (name, event) => {
if (!this.tooltip.isVisible()) {
return;
}
};
this.tooltip.on("keypress", this._onTooltipKeypress);
this.revert();
this.hide();
event.stopPropagation();
event.preventDefault();
});
this.shortcuts.on("Return", (name, event) => {
if (!this.tooltip.isVisible()) {
return;
}
this.commit();
this.hide();
event.stopPropagation();
event.preventDefault();
});
// All target swatches are kept in a map, indexed by swatch DOM elements
this.swatches = new Map();
@ -722,18 +642,14 @@ SwatchBasedEditorTooltip.prototype = {
this.tooltip.show(this.activeSwatch, "topcenter bottomleft");
// When the tooltip is closed by clicking outside the panel we want to
// commit any changes. Because the "hidden" event destroys the tooltip we
// need to do this before the tooltip is destroyed (in the "hiding"
// event).
this.tooltip.once("hiding", () => {
// commit any changes.
this.tooltip.once("hidden", () => {
if (!this._reverted && !this.eyedropperOpen) {
this.commit();
}
this._reverted = false;
});
// Once the tooltip is hidden we need to clean up any remaining objects.
this.tooltip.once("hidden", () => {
// Once the tooltip is hidden we need to clean up any remaining objects.
if (!this.eyedropperOpen) {
this.activeSwatch = null;
}
@ -825,7 +741,7 @@ SwatchBasedEditorTooltip.prototype = {
if (this.activeSwatch) {
this._reverted = true;
let swatch = this.swatches.get(this.activeSwatch);
this.tooltip.once("hiding", () => {
this.tooltip.once("hidden", () => {
swatch.callbacks.onRevert();
});
}
@ -846,6 +762,7 @@ SwatchBasedEditorTooltip.prototype = {
this.activeSwatch = null;
this.tooltip.off("keypress", this._onTooltipKeypress);
this.tooltip.destroy();
this.shortcuts.destroy();
}
};
@ -856,14 +773,16 @@ SwatchBasedEditorTooltip.prototype = {
* It just wraps a standard Tooltip and sets its content with an instance of a
* color picker.
*
* @param {XULDocument} doc
* @param {Toolbox} toolbox
* The devtools toolbox, needed to get the devtools main window.
*/
function SwatchColorPickerTooltip(doc) {
SwatchBasedEditorTooltip.call(this, doc);
function SwatchColorPickerTooltip(toolbox) {
let stylesheet = "chrome://devtools/content/shared/widgets/spectrum.css";
SwatchBasedEditorTooltip.call(this, toolbox, stylesheet);
// Creating a spectrum instance. this.spectrum will always be a promise that
// resolves to the spectrum instance
this.spectrum = this.tooltip.setColorPickerContent([0, 0, 0, 1]);
this.spectrum = this.setColorPickerContent([0, 0, 0, 1]);
this._onSpectrumColorChange = this._onSpectrumColorChange.bind(this);
this._openEyeDropper = this._openEyeDropper.bind(this);
}
@ -872,6 +791,36 @@ module.exports.SwatchColorPickerTooltip = SwatchColorPickerTooltip;
SwatchColorPickerTooltip.prototype =
Heritage.extend(SwatchBasedEditorTooltip.prototype, {
/**
* Fill the tooltip with a new instance of the spectrum color picker widget
* initialized with the given color, and return the instance of spectrum
*/
setColorPickerContent: function (color) {
let { doc } = this.tooltip;
let container = doc.createElementNS(XHTML_NS, "div");
container.id = "spectrum-tooltip";
let spectrumNode = doc.createElementNS(XHTML_NS, "div");
spectrumNode.id = "spectrum";
container.appendChild(spectrumNode);
let eyedropper = doc.createElementNS(XHTML_NS, "button");
eyedropper.id = "eyedropper-button";
eyedropper.className = "devtools-button";
container.appendChild(eyedropper);
this.tooltip.setContent(container, { width: 210, height: 216 });
let spectrum = new Spectrum(spectrumNode, color);
// Wait for the tooltip to be shown before calling spectrum.show
// as it expect to be visible in order to compute DOM element sizes.
this.tooltip.once("shown", () => {
spectrum.show();
});
return spectrum;
},
/**
* Overriding the SwatchBasedEditorTooltip.show function to set spectrum's
* color.
@ -884,16 +833,13 @@ Heritage.extend(SwatchBasedEditorTooltip.prototype, {
this.currentSwatchColor = this.activeSwatch.nextSibling;
this._originalColor = this.currentSwatchColor.textContent;
let color = this.activeSwatch.style.backgroundColor;
this.spectrum.then(spectrum => {
spectrum.off("changed", this._onSpectrumColorChange);
spectrum.rgb = this._colorToRgba(color);
spectrum.on("changed", this._onSpectrumColorChange);
spectrum.updateUI();
});
this.spectrum.off("changed", this._onSpectrumColorChange);
this.spectrum.rgb = this._colorToRgba(color);
this.spectrum.on("changed", this._onSpectrumColorChange);
this.spectrum.updateUI();
}
let tooltipDoc = this.tooltip.content.contentDocument;
let eyeButton = tooltipDoc.querySelector("#eyedropper-button");
let eyeButton = this.tooltip.doc.querySelector("#eyedropper-button");
eyeButton.addEventListener("click", this._openEyeDropper);
},
@ -967,10 +913,8 @@ Heritage.extend(SwatchBasedEditorTooltip.prototype, {
destroy: function () {
SwatchBasedEditorTooltip.prototype.destroy.call(this);
this.currentSwatchColor = null;
this.spectrum.then(spectrum => {
spectrum.off("changed", this._onSpectrumColorChange);
spectrum.destroy();
});
this.spectrum.off("changed", this._onSpectrumColorChange);
this.spectrum.destroy();
}
});
@ -981,14 +925,16 @@ Heritage.extend(SwatchBasedEditorTooltip.prototype, {
* It just wraps a standard Tooltip and sets its content with an instance of a
* CubicBezierWidget.
*
* @param {XULDocument} doc
* @param {Toolbox} toolbox
* The devtools toolbox, needed to get the devtools main window.
*/
function SwatchCubicBezierTooltip(doc) {
SwatchBasedEditorTooltip.call(this, doc);
function SwatchCubicBezierTooltip(toolbox) {
let stylesheet = "chrome://devtools/content/shared/widgets/cubic-bezier.css";
SwatchBasedEditorTooltip.call(this, toolbox, stylesheet);
// Creating a cubic-bezier instance.
// this.widget will always be a promise that resolves to the widget instance
this.widget = this.tooltip.setCubicBezierContent([0, 0, 1, 1]);
this.widget = this.setCubicBezierContent([0, 0, 1, 1]);
this._onUpdate = this._onUpdate.bind(this);
}
@ -996,6 +942,31 @@ module.exports.SwatchCubicBezierTooltip = SwatchCubicBezierTooltip;
SwatchCubicBezierTooltip.prototype =
Heritage.extend(SwatchBasedEditorTooltip.prototype, {
/**
* Fill the tooltip with a new instance of the cubic-bezier widget
* initialized with the given value, and return a promise that resolves to
* the instance of the widget
*/
setCubicBezierContent: function (bezier) {
let { doc } = this.tooltip;
let container = doc.createElementNS(XHTML_NS, "div");
container.className = "cubic-bezier-container";
this.tooltip.setContent(container, { width: 510, height: 370 });
let def = defer();
// Wait for the tooltip to be shown before calling instanciating the widget
// as it expect its DOM elements to be visible.
this.tooltip.once("shown", () => {
let widget = new CubicBezierWidget(container, bezier);
def.resolve(widget);
});
return def.promise;
},
/**
* Overriding the SwatchBasedEditorTooltip.show function to set the cubic
* bezier curve in the widget
@ -1079,14 +1050,15 @@ CssDocsTooltip.prototype = {
* It just wraps a standard Tooltip and sets its content with an instance of a
* CSSFilterEditorWidget.
*
* @param {XULDocument} doc
* @param {Toolbox} toolbox
* The devtools toolbox, needed to get the devtools main window.
*/
function SwatchFilterTooltip(doc) {
SwatchBasedEditorTooltip.call(this, doc);
function SwatchFilterTooltip(toolbox) {
let stylesheet = "chrome://devtools/content/shared/widgets/filter-widget.css";
SwatchBasedEditorTooltip.call(this, toolbox, stylesheet);
// Creating a filter editor instance.
// this.widget will always be a promise that resolves to the widget instance
this.widget = this.tooltip.setFilterContent("none");
this.widget = this.setFilterContent("none");
this._onUpdate = this._onUpdate.bind(this);
}
@ -1094,18 +1066,32 @@ exports.SwatchFilterTooltip = SwatchFilterTooltip;
SwatchFilterTooltip.prototype =
Heritage.extend(SwatchBasedEditorTooltip.prototype, {
/**
* Fill the tooltip with a new instance of the CSSFilterEditorWidget
* widget initialized with the given filter value, and return a promise
* that resolves to the instance of the widget when ready.
*/
setFilterContent: function (filter) {
let { doc } = this.tooltip;
let container = doc.createElementNS(XHTML_NS, "div");
container.id = "filter-container";
this.tooltip.setContent(container, { width: 510, height: 200 });
return new CSSFilterEditorWidget(container, filter);
},
show: function () {
// Call the parent class' show function
SwatchBasedEditorTooltip.prototype.show.call(this);
// Then set the filter value and listen to changes to preview them
if (this.activeSwatch) {
this.currentFilterValue = this.activeSwatch.nextSibling;
this.widget.then(widget => {
widget.off("updated", this._onUpdate);
widget.on("updated", this._onUpdate);
widget.setCssValue(this.currentFilterValue.textContent);
widget.render();
});
this.widget.off("updated", this._onUpdate);
this.widget.on("updated", this._onUpdate);
this.widget.setCssValue(this.currentFilterValue.textContent);
this.widget.render();
}
},
@ -1128,10 +1114,8 @@ Heritage.extend(SwatchBasedEditorTooltip.prototype, {
destroy: function () {
SwatchBasedEditorTooltip.prototype.destroy.call(this);
this.currentFilterValue = null;
this.widget.then(widget => {
widget.off("updated", this._onUpdate);
widget.destroy();
});
this.widget.off("updated", this._onUpdate);
this.widget.destroy();
},
/**

View File

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-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/. -->
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" href="chrome://devtools/content/shared/widgets/cubic-bezier.css" type="text/css"/>
<script type="application/javascript;version=1.8" src="chrome://devtools/content/shared/theme-switching.js"/>
<style>
html, body {
margin: 0;
padding: 0;
overflow: hidden;
width: 500px;
height: 370px;
}
</style>
</head>
<body role="application">
<div id="container"></div>
</body>
</html>

View File

@ -5,12 +5,13 @@
/* Based on Lea Verou www.cubic-bezier.com
See https://github.com/LeaVerou/cubic-bezier */
#container {
.cubic-bezier-container {
display: flex;
width: 500px;
height: 370px;
flex-direction: row-reverse;
overflow: hidden;
padding: 5px;
}
.display-wrap {
@ -64,13 +65,13 @@
-moz-user-select: none;
}
canvas#curve {
canvas.curve {
background: linear-gradient(-45deg, transparent 49.7%, rgba(0,0,0,.2) 49.7%, rgba(0,0,0,.2) 50.3%, transparent 50.3%) center no-repeat;
background-size: 100% 100%;
background-position: 0 0;
}
.theme-dark canvas#curve {
.theme-dark canvas.curve {
background: linear-gradient(-45deg, transparent 49.7%, #eee 49.7%, #eee 50.3%, transparent 50.3%) center no-repeat;
}
@ -109,30 +110,6 @@ canvas#curve {
background: #4C9ED9;
}
.timing-function-preview .dot.animate {
animation-duration: 2.5s;
animation-fill-mode: forwards;
animation-name: timing-function-preview;
}
@keyframes timing-function-preview {
0% {
left: -7px;
}
33% {
left: 143px;
}
50% {
left: 143px;
}
83% {
left: -7px;
}
100% {
left: -7px;
}
}
/* Preset Widget */
.preset-pane {

View File

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="UTF-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/. -->
<!DOCTYPE html [
<!ENTITY % filterwidgetDTD SYSTEM "chrome://devtools/locale/filterwidget.dtd" >
%filterwidgetDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" href="chrome://devtools/content/shared/widgets/filter-widget.css" type="text/css"/>
<script type="application/javascript;version=1.8" src="chrome://devtools/content/shared/theme-switching.js"></script>
</head>
<body>
<div id="container">
<div class="filters-list">
<div id="filters"></div>
<div class="footer">
<select value="">
<option value="">&filterListSelectPlaceholder;</option>
</select>
<button id="add-filter" class="add">&addNewFilterButton;</button>
<button id="toggle-presets">&presetsToggleButton;</button>
</div>
</div>
<div class="presets-list">
<div id="presets"></div>
<div class="footer">
<input value="" class="devtools-textinput"
placeholder="&newPresetPlaceholder;"></input>
<button class="add">&savePresetButton;</button>
</div>
</div>
</div>
</body>
</html>

View File

@ -2,23 +2,18 @@
* 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/. */
html, body {
height: 100%;
margin: 0;
overflow: hidden;
font: message-box;
color: var(--theme-body-color);
}
/* Main container: Displays the filters and presets in 2 columns */
#container {
#filter-container {
height: 100%;
display: flex;
position: relative;
padding: 5px;
/* when opened in a xul:panel, a gray color is applied to text */
color: var(--theme-body-color);
}
#container.dragging {
#filter-container.dragging {
-moz-user-select: none;
}
@ -49,13 +44,13 @@ html, body {
margin-top: 4px;
}
#container:not(.show-presets) .presets-list {
#filter-container:not(.show-presets) .presets-list {
width: 0;
border-left: none;
padding-left: 0;
}
#container.show-presets .filters-list {
#filter-container.show-presets .filters-list {
width: 300px;
}
@ -211,14 +206,14 @@ input {
display: none !important;
}
#container .dragging {
#filter-container .dragging {
position: relative;
z-index: 10;
cursor: grab;
}
/* message shown when there's no filter specified */
#container p {
#filter-container p {
text-align: center;
line-height: 20px;
}

View File

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-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/. -->
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" href="chrome://devtools/skin/spectrum.css" type="text/css"/>
<script type="application/javascript;version=1.8" src="chrome://devtools/content/shared/theme-switching.js"/>
<style>
body {
margin: 0;
padding: 0;
}
</style>
</head>
<body role="application">
<div id="spectrum"></div>
<button id="eyedropper-button" class="devtools-button"></button>
</body>
</html>

View File

@ -5,11 +5,8 @@
"use strict";
const { Cu } = require("chrome");
const CSSCompleter =
require("devtools/client/sourceeditor/css-autocompleter");
const { AutocompletePopup } =
require("devtools/client/shared/autocomplete-popup");
const CSSCompleter = require("devtools/client/sourceeditor/css-autocompleter");
const { AutocompletePopup } = require("devtools/client/shared/autocomplete-popup");
const CM_TERN_SCRIPTS = [
"chrome://devtools/content/sourceeditor/codemirror/addon/tern/tern.js",
@ -18,9 +15,6 @@ const CM_TERN_SCRIPTS = [
const autocompleteMap = new WeakMap();
// A simple way to give each popup its own panelId.
let autocompleteCounter = 0;
/**
* Prepares an editor instance for autocompletion.
*/
@ -124,23 +118,21 @@ function initializeAutoCompletion(ctx, options = {}) {
insertPopupItem(ed, popup.selectedItem);
}
popup.once("popup-closed", () => {
// This event is used in tests.
ed.emit("popup-hidden");
});
popup.hidePopup();
// This event is used in tests.
ed.emit("popup-hidden");
return true;
}
// Give each popup a new name to avoid sharing the elements.
let panelId = "devtools_sourceEditorCompletePopup" + autocompleteCounter;
++autocompleteCounter;
let popup = new AutocompletePopup(win.parent.document, {
position: "after_start",
fixedWidth: true,
let popup = new AutocompletePopup({ doc: win.parent.document }, {
position: "bottom",
theme: "auto",
autoSelect: true,
onClick: insertSelectedPopupItem,
panelId: panelId
onClick: insertSelectedPopupItem
});
let cycle = reverse => {
@ -217,30 +209,33 @@ function autoComplete({ ed, cm }) {
return;
}
let cur = ed.getCursor();
completer.complete(cm.getRange({line: 0, ch: 0}, cur), cur)
.then(suggestions => {
if (!suggestions || !suggestions.length ||
suggestions[0].preLabel == null) {
autocompleteOpts.suggestionInsertedOnce = false;
popup.hidePopup();
ed.emit("after-suggest");
return;
}
// The cursor is at the end of the currently entered part of the token,
// like "backgr|" but we need to open the popup at the beginning of the
// character "b". Thus we need to calculate the width of the entered part
// of the token ("backgr" here). 4 comes from the popup's left padding.
let cursorElement =
cm.display.cursorDiv.querySelector(".CodeMirror-cursor");
let left = suggestions[0].preLabel.length * cm.defaultCharWidth() + 4;
popup.hidePopup();
popup.setItems(suggestions);
popup.openPopup(cursorElement, -1 * left, 0);
completer.complete(cm.getRange({line: 0, ch: 0}, cur), cur).then(suggestions => {
if (!suggestions || !suggestions.length || suggestions[0].preLabel == null) {
autocompleteOpts.suggestionInsertedOnce = false;
popup.once("popup-closed", () => {
// This event is used in tests.
ed.emit("after-suggest");
});
popup.hidePopup();
return;
}
// The cursor is at the end of the currently entered part of the token,
// like "backgr|" but we need to open the popup at the beginning of the
// character "b". Thus we need to calculate the width of the entered part
// of the token ("backgr" here). 4 comes from the popup's left padding.
let cursorElement = cm.display.cursorDiv.querySelector(".CodeMirror-cursor");
let left = suggestions[0].preLabel.length * cm.defaultCharWidth() + 4;
popup.hidePopup();
popup.setItems(suggestions);
popup.once("popup-opened", () => {
// This event is used in tests.
ed.emit("after-suggest");
}).then(null, e => console.error(e));
});
popup.openPopup(cursorElement, -1 * left, 0);
autocompleteOpts.suggestionInsertedOnce = false;
}).then(null, e => console.error(e));
}
/**

View File

@ -45,6 +45,16 @@
max-height: 20rem;
}
/* Reset list styles. */
.devtools-autocomplete-popup ul {
list-style: none;
}
.devtools-autocomplete-popup ul,
.devtools-autocomplete-popup li {
margin: 0;
}
:root[platform="linux"] .devtools-autocomplete-popup {
/* Root font size is bigger on Linux, adjust rem-based values. */
max-height: 16rem;
@ -55,50 +65,50 @@
background-color: transparent;
border-width: 0px !important;
margin: 0;
padding: 2px;
}
.devtools-autocomplete-listbox > scrollbox {
padding: 2px;
}
.inplace-editor-autocomplete-popup .devtools-autocomplete-listbox {
/* Inplace editor closes the autocomplete popup on blur, the autocomplete
popup should not steal the focus here.*/
-moz-user-focus: ignore;
}
.devtools-autocomplete-listbox > richlistitem,
.devtools-autocomplete-listbox > richlistitem[selected] {
.devtools-autocomplete-listbox .autocomplete-item {
width: 100%;
background-color: transparent;
border-radius: 4px;
}
.devtools-autocomplete-listbox.dark-theme > richlistitem[selected],
.devtools-autocomplete-listbox.dark-theme > richlistitem:hover {
background-color: rgba(0,0,0,0.5);
}
.devtools-autocomplete-listbox.dark-theme > richlistitem[selected] > .autocomplete-value,
.devtools-autocomplete-listbox:focus.dark-theme > richlistitem[selected] > .initial-value {
color: hsl(208,100%,60%);
}
.devtools-autocomplete-listbox.dark-theme > richlistitem[selected] > label {
color: #eee;
}
.devtools-autocomplete-listbox.dark-theme > richlistitem > label {
color: #ccc;
}
.devtools-autocomplete-listbox > richlistitem > .initial-value,
.devtools-autocomplete-listbox > richlistitem > .autocomplete-value {
margin: 0;
padding: 1px 0;
}
.devtools-autocomplete-listbox > richlistitem > .autocomplete-count {
.devtools-autocomplete-listbox .autocomplete-selected {
background-color: rgba(0,0,0,0.2);
}
.devtools-autocomplete-listbox.dark-theme .autocomplete-selected,
.devtools-autocomplete-listbox.dark-theme .autocomplete-item:hover {
background-color: rgba(0,0,0,0.5);
}
.devtools-autocomplete-listbox.dark-theme .autocomplete-selected > .autocomplete-value,
.devtools-autocomplete-listbox:focus.dark-theme .autocomplete-selected > .initial-value {
color: hsl(208,100%,60%);
}
.devtools-autocomplete-listbox.dark-theme .autocomplete-selected > span {
color: #eee;
}
.devtools-autocomplete-listbox.dark-theme .autocomplete-item > span {
color: #ccc;
}
.devtools-autocomplete-listbox .autocomplete-item > .initial-value,
.devtools-autocomplete-listbox .autocomplete-item > .autocomplete-value {
margin: 0;
padding: 0;
cursor: default;
}
.devtools-autocomplete-listbox .autocomplete-item > .autocomplete-count {
text-align: end;
}
@ -132,22 +142,22 @@
background: var(--theme-body-background);
}
.devtools-autocomplete-listbox.firebug-theme > richlistitem[selected],
.devtools-autocomplete-listbox.firebug-theme > richlistitem:hover,
.devtools-autocomplete-listbox.light-theme > richlistitem[selected],
.devtools-autocomplete-listbox.light-theme > richlistitem:hover {
.devtools-autocomplete-listbox.firebug-theme .autocomplete-selected,
.devtools-autocomplete-listbox.firebug-theme .autocomplete-item:hover,
.devtools-autocomplete-listbox.light-theme .autocomplete-selected,
.devtools-autocomplete-listbox.light-theme .autocomplete-item:hover {
background-color: rgba(128,128,128,0.3);
}
.devtools-autocomplete-listbox.firebug-theme > richlistitem[selected] > .autocomplete-value,
.devtools-autocomplete-listbox:focus.firebug-theme > richlistitem[selected] > .initial-value,
.devtools-autocomplete-listbox.light-theme > richlistitem[selected] > .autocomplete-value,
.devtools-autocomplete-listbox:focus.light-theme > richlistitem[selected] > .initial-value {
.devtools-autocomplete-listbox.firebug-theme .autocomplete-selected > .autocomplete-value,
.devtools-autocomplete-listbox:focus.firebug-theme .autocomplete-selected > .initial-value,
.devtools-autocomplete-listbox.light-theme .autocomplete-selected > .autocomplete-value,
.devtools-autocomplete-listbox:focus.light-theme .autocomplete-selected > .initial-value {
color: #222;
}
.devtools-autocomplete-listbox.firebug-theme > richlistitem > label,
.devtools-autocomplete-listbox.light-theme > richlistitem > label {
.devtools-autocomplete-listbox.firebug-theme .autocomplete-item > span,
.devtools-autocomplete-listbox.light-theme .autocomplete-item > span {
color: #666;
}

View File

@ -3,7 +3,6 @@
* 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/. */
/* Tooltip widget (see devtools/client/shared/widgets/Tooltip.js) */
.devtools-tooltip .panel-arrowcontent {
@ -107,6 +106,7 @@
background: transparent;
pointer-events: none;
overflow: hidden;
filter: drop-shadow(0 3px 4px var(--theme-tooltip-shadow));
}
.tooltip-xul-wrapper {
@ -144,11 +144,7 @@
/* Tooltip : arrow style */
.tooltip-container[type="arrow"] {
filter: drop-shadow(0 3px 4px var(--theme-tooltip-shadow));
}
.tooltip-xul-wrapper .tooltip-container[type="arrow"] {
.tooltip-xul-wrapper .tooltip-container {
/* When displayed in a XUL panel the drop shadow would be abruptly cut by the panel */
filter: none;
}
@ -390,3 +386,25 @@
.stack-frame-line {
color: var(--theme-highlight-orange);
}
/* Tooltip: HTML Search */
#searchbox-panel-listbox {
width: 250px;
max-width: 250px;
overflow-x: hidden;
}
#searchbox-panel-listbox .autocomplete-item,
#searchbox-panel-listbox .autocomplete-item[selected] {
overflow-x: hidden;
}
#searchbox-panel-listbox .autocomplete-item > .initial-value {
max-width: 130px;
margin-left: 15px;
}
#searchbox-panel-listbox .autocomplete-item > .autocomplete-value {
max-width: 150px;
}

View File

@ -246,17 +246,21 @@ JSTerm.prototype = {
let autocompleteOptions = {
onSelect: this.onAutocompleteSelect.bind(this),
onClick: this.acceptProposedCompletion.bind(this),
panelId: "webConsole_autocompletePopup",
listBoxId: "webConsole_autocompletePopupListBox",
position: "before_start",
listId: "webConsole_autocompletePopupListBox",
position: "top",
theme: "auto",
direction: "ltr",
autoSelect: true
};
this.autocompletePopup = new AutocompletePopup(this.hud.document,
autocompleteOptions);
let doc = this.hud.document;
let toolbox = gDevTools.getToolbox(this.hud.owner.target);
if (!toolbox) {
// In some cases (e.g. Browser Console), there is no toolbox.
toolbox = { doc };
}
this.autocompletePopup = new AutocompletePopup(toolbox, autocompleteOptions);
let inputContainer = doc.querySelector(".jsterm-input-container");
this.completeNode = doc.querySelector(".jsterm-complete-node");
this.inputNode = doc.querySelector(".jsterm-input-node");
@ -1701,12 +1705,6 @@ JSTerm.prototype = {
this.autocompletePopup.destroy();
this.autocompletePopup = null;
let popup = this.hud.owner.chromeWindow.document
.getElementById("webConsole_autocompletePopup");
if (popup) {
popup.parentNode.removeChild(popup);
}
if (this._onPaste) {
this.inputNode.removeEventListener("paste", this._onPaste, false);
this.inputNode.removeEventListener("drop", this._onPaste, false);

View File

@ -14,7 +14,7 @@ add_task(function* () {
yield loadTab(TEST_URI);
let hud = yield openConsole();
let popup = hud.jsterm.autocompletePopup;
let popupShown = once(popup._panel, "popupshown");
let popupShown = once(popup, "popup-opened");
hud.jsterm.setInputValue("sc");
EventUtils.synthesizeKey("r", {});

View File

@ -30,9 +30,9 @@ add_task(function* () {
Services.prefs.setBoolPref(PREF_AUTO_MULTILINE, true);
});
var consoleOpened = Task.async(function* (aHud) {
var consoleOpened = Task.async(function* (hud) {
let deferred = promise.defer();
HUD = aHud;
HUD = hud;
info("web console opened");
jsterm = HUD.jsterm;
@ -51,9 +51,7 @@ var consoleOpened = Task.async(function* (aHud) {
ok(!popup.isOpen, "popup is not open");
popup._panel.addEventListener("popupshown", function onShown() {
popup._panel.removeEventListener("popupshown", onShown, false);
popup.once("popup-opened", () => {
ok(popup.isOpen, "popup is open");
// 4 values, and the following properties:
@ -65,6 +63,7 @@ var consoleOpened = Task.async(function* (aHud) {
let sameItems = popup.getItems().reverse().map(function (e) {
return e.label;
});
ok(sameItems.every(function (prop, index) {
return [
"__defineGetter__",
@ -134,14 +133,12 @@ var consoleOpened = Task.async(function* (aHud) {
is(popup.selectedIndex, 0, "index is first after Home");
info("press Tab and wait for popup to hide");
popup._panel.addEventListener("popuphidden", function popupHidden() {
popup._panel.removeEventListener("popuphidden", popupHidden, false);
popup.once("popup-closed", () => {
deferred.resolve();
}, false);
});
EventUtils.synthesizeKey("VK_TAB", {});
}, false);
});
info("wait for completion: window.foobarBug585991.");
jsterm.setInputValue("window.foobarBug585991");
EventUtils.synthesizeKey(".", {});
@ -159,9 +156,7 @@ function popupHideAfterTab() {
ok(!completeNode.value, "completeNode is empty");
popup._panel.addEventListener("popupshown", function onShown() {
popup._panel.removeEventListener("popupshown", onShown, false);
popup.once("popup-opened", function onShown() {
ok(popup.isOpen, "popup is open");
is(popup.itemCount, 19, "popup.itemCount is correct");
@ -176,9 +171,7 @@ function popupHideAfterTab() {
is(completeNode.value, prefix + "watch",
"completeNode.value holds watch");
popup._panel.addEventListener("popuphidden", function onHidden() {
popup._panel.removeEventListener("popuphidden", onHidden, false);
popup.once("popup-closed", function onHidden() {
ok(!popup.isOpen, "popup is not open after VK_ESCAPE");
is(jsterm.getInputValue(), "window.foobarBug585991.",
@ -207,9 +200,7 @@ function popupHideAfterTab() {
function testReturnKey() {
let deferred = promise.defer();
popup._panel.addEventListener("popupshown", function onShown() {
popup._panel.removeEventListener("popupshown", onShown, false);
popup.once("popup-opened", function onShown() {
ok(popup.isOpen, "popup is open");
is(popup.itemCount, 19, "popup.itemCount is correct");
@ -231,9 +222,7 @@ function testReturnKey() {
is(completeNode.value, prefix + "valueOf",
"completeNode.value holds valueOf");
popup._panel.addEventListener("popuphidden", function onHidden() {
popup._panel.removeEventListener("popuphidden", onHidden, false);
popup.once("popup-closed", function onHidden() {
ok(!popup.isOpen, "popup is not open after VK_RETURN");
is(jsterm.getInputValue(), "window.foobarBug585991.valueOf",
@ -271,9 +260,7 @@ function* dontShowArrayNumbers() {
jsterm = HUD.jsterm;
popup = jsterm.autocompletePopup;
popup._panel.addEventListener("popupshown", function onShown() {
popup._panel.removeEventListener("popupshown", onShown, false);
popup.once("popup-opened", function onShown() {
let sameItems = popup.getItems().map(function (e) {
return e.label;
});
@ -281,8 +268,7 @@ function* dontShowArrayNumbers() {
prop === "0";
}), "Completing on an array doesn't show numbers.");
popup._panel.addEventListener("popuphidden", function popupHidden() {
popup._panel.removeEventListener("popuphidden", popupHidden, false);
popup.once("popup-closed", function popupHidden() {
deferred.resolve();
}, false);
@ -304,16 +290,13 @@ function testReturnWithNoSelection() {
info("test pressing return with open popup, but no selection, see bug 873250");
popup._panel.addEventListener("popupshown", function onShown() {
popup._panel.removeEventListener("popupshown", onShown);
popup.once("popup-opened", function onShown() {
ok(popup.isOpen, "popup is open");
is(popup.itemCount, 2, "popup.itemCount is correct");
isnot(popup.selectedIndex, -1, "popup.selectedIndex is correct");
info("press Return and wait for popup to hide");
popup._panel.addEventListener("popuphidden", function popupHidden() {
popup._panel.removeEventListener("popuphidden", popupHidden);
popup.once("popup-closed", function popupHidden() {
deferred.resolve();
});
executeSoon(() => EventUtils.synthesizeKey("VK_RETURN", {}));
@ -344,9 +327,7 @@ function testCompletionInText() {
let deferred = promise.defer();
popup._panel.addEventListener("popupshown", function onShown() {
popup._panel.removeEventListener("popupshown", onShown);
popup.once("popup-opened", function onShown() {
ok(popup.isOpen, "popup is open");
is(popup.itemCount, 2, "popup.itemCount is correct");
@ -360,8 +341,7 @@ function testCompletionInText() {
ok(sameItems, "getItems returns the items we expect");
info("press Tab and wait for popup to hide");
popup._panel.addEventListener("popuphidden", function popupHidden() {
popup._panel.removeEventListener("popuphidden", popupHidden);
popup.once("popup-closed", function popupHidden() {
deferred.resolve();
});
EventUtils.synthesizeKey("VK_TAB", {});

View File

@ -26,67 +26,49 @@ function consoleOpened(HUD) {
let popup = HUD.jsterm.autocompletePopup;
let input = popup._document.activeElement;
function getActiveDescendant() {
return input.ownerDocument.getElementById(
input.getAttribute("aria-activedescendant"));
}
ok(!popup.isOpen, "popup is not open");
ok(!input.hasAttribute("aria-activedescendant"), "no aria-activedescendant");
popup._panel.addEventListener("popupshown", function onPopupPanel() {
popup._panel.removeEventListener("popupshown", onPopupPanel, false);
popup.once("popup-opened", () => {
ok(popup.isOpen, "popup is open");
is(popup.itemCount, 0, "no items");
ok(!input.hasAttribute("aria-activedescendant"),
"no aria-activedescendant");
popup.setItems(items);
is(popup.itemCount, items.length, "items added");
let sameItems = popup.getItems();
is(sameItems.every(function (aItem, aIndex) {
return aItem === items[aIndex];
is(sameItems.every(function (item, index) {
return item === items[index];
}), true, "getItems returns back the same items");
is(popup.selectedIndex, 2,
"Index of the first item from bottom is selected.");
is(popup.selectedIndex, 2, "Index of the first item from bottom is selected.");
is(popup.selectedItem, items[2], "First item from bottom is selected");
ok(getActiveDescendant().selected, "aria-activedescendant is correct");
popup.selectedIndex = 1;
is(popup.selectedIndex, 1, "index 1 is selected");
is(popup.selectedItem, items[1], "item1 is selected");
ok(getActiveDescendant().selected, "aria-activedescendant is correct");
popup.selectedItem = items[2];
is(popup.selectedIndex, 2, "index 2 is selected");
is(popup.selectedItem, items[2], "item2 is selected");
ok(getActiveDescendant().selected, "aria-activedescendant is correct");
is(popup.selectPreviousItem(), items[1], "selectPreviousItem() works");
is(popup.selectedIndex, 1, "index 1 is selected");
is(popup.selectedItem, items[1], "item1 is selected");
ok(getActiveDescendant().selected, "aria-activedescendant is correct");
is(popup.selectNextItem(), items[2], "selectPreviousItem() works");
is(popup.selectNextItem(), items[2], "selectNextItem() works");
is(popup.selectedIndex, 2, "index 2 is selected");
is(popup.selectedItem, items[2], "item2 is selected");
ok(getActiveDescendant().selected, "aria-activedescendant is correct");
ok(popup.selectNextItem(), "selectPreviousItem() works");
ok(popup.selectNextItem(), "selectNextItem() works");
is(popup.selectedIndex, 0, "index 0 is selected");
is(popup.selectedItem, items[0], "item0 is selected");
ok(getActiveDescendant().selected, "aria-activedescendant is correct");
items.push({label: "label3", value: "value3"});
popup.appendItem(items[3]);
@ -95,25 +77,23 @@ function consoleOpened(HUD) {
popup.selectedIndex = 3;
is(popup.selectedItem, items[3], "item3 is selected");
ok(getActiveDescendant().selected, "aria-activedescendant is correct");
popup.removeItem(items[2]);
is(popup.selectedIndex, 2, "index2 is selected");
is(popup.selectedItem, items[3], "item3 is still selected");
ok(getActiveDescendant().selected, "aria-activedescendant is correct");
is(popup.itemCount, items.length - 1, "item2 removed");
popup.clearItems();
is(popup.itemCount, 0, "items cleared");
ok(!input.hasAttribute("aria-activedescendant"),
"no aria-activedescendant");
popup.once("popup-closed", () => {
deferred.resolve();
});
popup.hidePopup();
deferred.resolve();
}, false);
});
popup.openPopup();
popup.openPopup(HUD.jsterm.inputNode);
return deferred.promise;
}

View File

@ -33,9 +33,7 @@ function consoleOpened() {
ok(!popup.isOpen, "popup is not open");
popup._panel.addEventListener("popupshown", function onShown() {
popup._panel.removeEventListener("popupshown", onShown, false);
popup.once("popup-opened", () => {
ok(popup.isOpen, "popup is open");
is(popup.itemCount, jsterm._autocompleteCache.length,
@ -47,10 +45,11 @@ function consoleOpened() {
isnot(jsterm._autocompleteCache.indexOf("ATTRIBUTE_NODE"), -1,
"ATTRIBUTE_NODE is in the list of suggestions");
popup._panel.addEventListener("popuphidden", deferred.resolve, false);
popup.once("popup-closed", () => {
deferred.resolve();
});
EventUtils.synthesizeKey("VK_ESCAPE", {});
}, false);
});
jsterm.setInputValue("document.body");
EventUtils.synthesizeKey(".", {});
@ -65,9 +64,6 @@ function autocompletePopupHidden() {
let popup = jsterm.autocompletePopup;
let completeNode = jsterm.completeNode;
popup._panel.removeEventListener("popuphidden", autocompletePopupHidden,
false);
ok(!popup.isOpen, "popup is not open");
jsterm.once("autocomplete-updated", function () {
@ -89,8 +85,8 @@ function testPropertyPanel() {
let jsterm = gHUD.jsterm;
jsterm.clearOutput();
jsterm.execute("document", (msg) => {
jsterm.once("variablesview-fetched", (aEvent, aView) => {
deferred.resolve(aView);
jsterm.once("variablesview-fetched", (evt, view) => {
deferred.resolve(view);
});
let anchor = msg.querySelector(".message-body a");
EventUtils.synthesizeMouse(anchor, 2, 2, {}, gHUD.iframeWindow);

View File

@ -25,12 +25,12 @@ function consoleOpened(HUD) {
ok(false, "popup shown");
};
jsterm.execute("window.foobarBug660806 = {\
'location': 'value0',\
'locationbar': 'value1'\
}");
jsterm.execute(`window.foobarBug660806 = {
'location': 'value0',
'locationbar': 'value1'
}`);
popup._panel.addEventListener("popupshown", onShown, false);
popup.on("popup-opened", onShown);
ok(!popup.isOpen, "popup is not open");
@ -47,7 +47,7 @@ function consoleOpened(HUD) {
executeSoon(function () {
ok(!popup.isOpen, "popup is not open");
popup._panel.removeEventListener("popupshown", onShown, false);
popup.off("popup-opened", onShown);
executeSoon(deferred.resolve);
});
return deferred.promise;

View File

@ -81,15 +81,14 @@
let deferred = promise.defer();
let popup = jsterm.autocompletePopup;
popup._panel.addEventListener("popuphidden", function popupHidden() {
popup._panel.removeEventListener("popuphidden", popupHidden, false);
popup.once("popup-closed", () => {
ok(!popup.isOpen,
"Auto complete popup is hidden.");
ok(toolbox.splitConsole,
"Split console is open after hiding the autocomplete popup.");
deferred.resolve();
}, false);
});
EventUtils.sendKey("ESCAPE", toolbox.win);
@ -136,19 +135,13 @@
}
function showAutoCompletePopoup() {
let deferred = promise.defer();
let popupPanel = jsterm.autocompletePopup._panel;
popupPanel.addEventListener("popupshown", function popupShown() {
popupPanel.removeEventListener("popupshown", popupShown, false);
deferred.resolve();
}, false);
let onPopupShown = jsterm.autocompletePopup.once("popup-opened");
jsterm.focus();
jsterm.setInputValue("document.location.");
EventUtils.sendKey("TAB", hud.iframeWindow);
return deferred.promise;
return onPopupShown;
}
function finish() {

Some files were not shown because too many files have changed in this diff Show More