mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge fx-team to central, a=merge CLOSED TREE
This commit is contained in:
commit
a241fb67ab
@ -112,7 +112,6 @@ devtools/client/shadereditor/**
|
||||
devtools/client/shared/**
|
||||
devtools/client/sourceeditor/**
|
||||
devtools/client/storage/**
|
||||
devtools/client/styleeditor/**
|
||||
devtools/client/tilt/**
|
||||
devtools/client/webaudioeditor/**
|
||||
devtools/client/webconsole/**
|
||||
|
@ -410,15 +410,20 @@ AnimationsTimeline.prototype = {
|
||||
|
||||
drawHeaderAndBackground: function() {
|
||||
let width = this.timeHeaderEl.offsetWidth;
|
||||
let scale = width / (TimeScale.maxEndTime - TimeScale.minStartTime);
|
||||
let animationDuration = TimeScale.maxEndTime - TimeScale.minStartTime;
|
||||
let minTimeInterval = TIME_GRADUATION_MIN_SPACING * animationDuration / width;
|
||||
let intervalLength = findOptimalTimeInterval(minTimeInterval);
|
||||
let intervalWidth = intervalLength * width / animationDuration;
|
||||
|
||||
drawGraphElementBackground(this.win.document, "time-graduations",
|
||||
width, scale);
|
||||
width, intervalWidth);
|
||||
|
||||
// And the time graduation header.
|
||||
this.timeHeaderEl.innerHTML = "";
|
||||
let interval = findOptimalTimeInterval(scale, TIME_GRADUATION_MIN_SPACING);
|
||||
for (let i = 0; i < width; i += interval) {
|
||||
let pos = 100 * i / width;
|
||||
|
||||
for (let i = 0; i <= width / intervalWidth; i++) {
|
||||
let pos = 100 * i * intervalWidth / width;
|
||||
|
||||
createNode({
|
||||
parent: this.timeHeaderEl,
|
||||
nodeType: "span",
|
||||
|
@ -24,11 +24,14 @@ add_task(function*() {
|
||||
|
||||
info("Find out how many time graduations should there be");
|
||||
let width = headerEl.offsetWidth;
|
||||
let scale = width / (TimeScale.maxEndTime - TimeScale.minStartTime);
|
||||
|
||||
let animationDuration = TimeScale.maxEndTime - TimeScale.minStartTime;
|
||||
let minTimeInterval = TIME_GRADUATION_MIN_SPACING * animationDuration / width;
|
||||
|
||||
// Note that findOptimalTimeInterval is tested separately in xpcshell test
|
||||
// test_findOptimalTimeInterval.js, so we assume that it works here.
|
||||
let interval = findOptimalTimeInterval(scale, TIME_GRADUATION_MIN_SPACING);
|
||||
let nb = Math.ceil(width / interval);
|
||||
let interval = findOptimalTimeInterval(minTimeInterval);
|
||||
let nb = Math.ceil(animationDuration / interval);
|
||||
|
||||
is(headerEl.querySelectorAll(".time-tick").length, nb,
|
||||
"The expected number of time ticks were found");
|
||||
@ -36,9 +39,9 @@ add_task(function*() {
|
||||
info("Make sure graduations are evenly distributed and show the right times");
|
||||
[...headerEl.querySelectorAll(".time-tick")].forEach((tick, i) => {
|
||||
let left = parseFloat(tick.style.left);
|
||||
let expectedPos = i * interval * 100 / width;
|
||||
let expectedPos = i * interval * 100 / animationDuration;
|
||||
is(Math.round(left), Math.round(expectedPos),
|
||||
"Graduation " + i + " is positioned correctly");
|
||||
`Graduation ${i} is positioned correctly`);
|
||||
|
||||
// Note that the distancetoRelativeTime and formatTime functions are tested
|
||||
// separately in xpcshell test test_timeScale.js, so we assume that they
|
||||
@ -46,6 +49,6 @@ add_task(function*() {
|
||||
let formattedTime = TimeScale.formatTime(
|
||||
TimeScale.distanceToRelativeTime(expectedPos, width));
|
||||
is(tick.textContent, formattedTime,
|
||||
"Graduation " + i + " has the right text content");
|
||||
`Graduation ${i} has the right text content`);
|
||||
});
|
||||
});
|
||||
|
@ -14,66 +14,64 @@ const {findOptimalTimeInterval} = require("devtools/client/animationinspector/ut
|
||||
// findOptimalTimeInterval function. Each object should have the following
|
||||
// properties:
|
||||
// - desc: an optional string that will be printed out
|
||||
// - timeScale: a number that represents how many pixels is 1ms
|
||||
// - minSpacing: an optional number that represents the minim space between 2
|
||||
// time graduations
|
||||
// - minTimeInterval: a number that represents the minimum time in ms
|
||||
// that should be displayed in one interval
|
||||
// - expectedInterval: a number that you expect the findOptimalTimeInterval
|
||||
// function to return as a result.
|
||||
// Optionally you can pass a string where `interval` is the calculated
|
||||
// interval, this string will be eval'd and tested to be truthy.
|
||||
const TEST_DATA = [{
|
||||
desc: "With 1px being 1ms and no minSpacing, expect the interval to be the " +
|
||||
"interval multiple",
|
||||
timeScale: 1,
|
||||
minSpacing: undefined,
|
||||
desc: "With no minTimeInterval, expect the interval to be 0",
|
||||
minTimeInterval: null,
|
||||
expectedInterval: 0
|
||||
}, {
|
||||
desc: "With a minTimeInterval of 0 ms, expect the interval to be 0",
|
||||
minTimeInterval: 0,
|
||||
expectedInterval: 0
|
||||
}, {
|
||||
desc: "With a minInterval of 1ms, expect the interval to be the 1ms too",
|
||||
minTimeInterval: 1,
|
||||
expectedInterval: 1
|
||||
}, {
|
||||
desc: "With a very small minTimeInterval, expect the interval to be 1ms",
|
||||
minTimeInterval: 1e-31,
|
||||
expectedInterval: 1
|
||||
}, {
|
||||
desc: "With a minInterval of 2.5ms, expect the interval to be 2.5ms too",
|
||||
minTimeInterval: 2.5,
|
||||
expectedInterval: 2.5
|
||||
}, {
|
||||
desc: "With a minInterval of 5ms, expect the interval to be 5ms too",
|
||||
minTimeInterval: 5,
|
||||
expectedInterval: 5
|
||||
}, {
|
||||
desc: "With a minInterval of 7ms, expect the interval to be the next " +
|
||||
"multiple of 5",
|
||||
minTimeInterval: 7,
|
||||
expectedInterval: 10
|
||||
}, {
|
||||
minTimeInterval: 20,
|
||||
expectedInterval: 25
|
||||
}, {
|
||||
desc: "With 1px being 1ms and a custom minSpacing being a multiple of 25 " +
|
||||
"expect the interval to be the custom min spacing",
|
||||
timeScale: 1,
|
||||
minSpacing: 50,
|
||||
minTimeInterval: 33,
|
||||
expectedInterval: 50
|
||||
}, {
|
||||
desc: "With 1px being 1ms and a custom minSpacing not being multiple of 25 " +
|
||||
"expect the interval to be the next multiple of 10",
|
||||
timeScale: 1,
|
||||
minSpacing: 26,
|
||||
expectedInterval: 50
|
||||
minTimeInterval: 987,
|
||||
expectedInterval: 1000
|
||||
}, {
|
||||
desc: "If 1ms corresponds to a distance that is greater than the min " +
|
||||
"spacing then, expect the interval to be this distance",
|
||||
timeScale: 20,
|
||||
minSpacing: undefined,
|
||||
expectedInterval: 20
|
||||
minTimeInterval: 1234,
|
||||
expectedInterval: 2500
|
||||
}, {
|
||||
desc: "If 1ms corresponds to a distance that is greater than the min " +
|
||||
"spacing then, expect the interval to be this distance, even if it " +
|
||||
"isn't a multiple of 25",
|
||||
timeScale: 33,
|
||||
minSpacing: undefined,
|
||||
expectedInterval: 33
|
||||
}, {
|
||||
desc: "If 1ms is a very small distance, then expect this distance to be " +
|
||||
"multiplied by 25, 50, 100, 200, etc... until it goes over the min " +
|
||||
"spacing",
|
||||
timeScale: 0.001,
|
||||
minSpacing: undefined,
|
||||
expectedInterval: 12.8
|
||||
}, {
|
||||
desc: "If the time scale is such that we need to iterate more than the " +
|
||||
"maximum allowed number of iterations, then expect an interval lower " +
|
||||
"than the minimum one",
|
||||
timeScale: 1e-31,
|
||||
minSpacing: undefined,
|
||||
expectedInterval: "interval < 10"
|
||||
minTimeInterval: 9800,
|
||||
expectedInterval: 10000
|
||||
}];
|
||||
|
||||
function run_test() {
|
||||
for (let {timeScale, desc, minSpacing, expectedInterval} of TEST_DATA) {
|
||||
do_print("Testing timeScale: " + timeScale + " and minSpacing: " +
|
||||
minSpacing + ". Expecting " + expectedInterval + ".");
|
||||
for (let {minTimeInterval, desc, expectedInterval} of TEST_DATA) {
|
||||
do_print(`Testing minTimeInterval: ${minTimeInterval}.
|
||||
Expecting ${expectedInterval}.`);
|
||||
|
||||
let interval = findOptimalTimeInterval(timeScale, minSpacing);
|
||||
let interval = findOptimalTimeInterval(minTimeInterval);
|
||||
if (typeof expectedInterval == "string") {
|
||||
ok(eval(expectedInterval), desc);
|
||||
} else {
|
||||
|
@ -18,17 +18,15 @@ const L10N = new ViewHelpers.L10N(STRINGS_URI);
|
||||
// How many times, maximum, can we loop before we find the optimal time
|
||||
// interval in the timeline graph.
|
||||
const OPTIMAL_TIME_INTERVAL_MAX_ITERS = 100;
|
||||
// Background time graduations should be multiple of this number of millis.
|
||||
const TIME_INTERVAL_MULTIPLE = 25;
|
||||
const TIME_INTERVAL_SCALES = 3;
|
||||
// The default minimum spacing between time graduations in px.
|
||||
const TIME_GRADUATION_MIN_SPACING = 10;
|
||||
// Time graduations should be multiple of one of these number.
|
||||
const OPTIMAL_TIME_INTERVAL_MULTIPLES = [1, 2.5, 5];
|
||||
|
||||
// RGB color for the time interval background.
|
||||
const TIME_INTERVAL_COLOR = [128, 136, 144];
|
||||
// byte
|
||||
const TIME_INTERVAL_OPACITY_MIN = 32;
|
||||
const TIME_INTERVAL_OPACITY_MIN = 64;
|
||||
// byte
|
||||
const TIME_INTERVAL_OPACITY_ADD = 32;
|
||||
const TIME_INTERVAL_OPACITY_MAX = 96;
|
||||
|
||||
const MILLIS_TIME_FORMAT_MAX_DURATION = 4000;
|
||||
|
||||
@ -72,9 +70,9 @@ exports.createNode = createNode;
|
||||
* @param {Document} document The document where the image-element should be set.
|
||||
* @param {String} id The ID for the image-element.
|
||||
* @param {Number} graphWidth The width of the graph.
|
||||
* @param {Number} timeScale How many px is 1ms in the graph.
|
||||
* @param {Number} intervalWidth The width of one interval
|
||||
*/
|
||||
function drawGraphElementBackground(document, id, graphWidth, timeScale) {
|
||||
function drawGraphElementBackground(document, id, graphWidth, intervalWidth) {
|
||||
let canvas = document.createElement("canvas");
|
||||
let ctx = canvas.getContext("2d");
|
||||
|
||||
@ -93,17 +91,19 @@ function drawGraphElementBackground(document, id, graphWidth, timeScale) {
|
||||
|
||||
// Build new millisecond tick lines...
|
||||
let [r, g, b] = TIME_INTERVAL_COLOR;
|
||||
let alphaComponent = TIME_INTERVAL_OPACITY_MIN;
|
||||
let interval = findOptimalTimeInterval(timeScale);
|
||||
let opacities = [TIME_INTERVAL_OPACITY_MAX, TIME_INTERVAL_OPACITY_MIN];
|
||||
|
||||
// Insert one pixel for each division on each scale.
|
||||
for (let i = 1; i <= TIME_INTERVAL_SCALES; i++) {
|
||||
let increment = interval * Math.pow(2, i);
|
||||
for (let x = 0; x < canvas.width; x += increment) {
|
||||
let position = x | 0;
|
||||
view32bit[position] = (alphaComponent << 24) | (b << 16) | (g << 8) | r;
|
||||
// Insert one tick line on each interval
|
||||
for (let i = 0; i <= graphWidth / intervalWidth; i++) {
|
||||
let x = i * intervalWidth;
|
||||
// Ensure the last line is drawn on canvas
|
||||
if (x >= graphWidth) {
|
||||
x = graphWidth - 0.5;
|
||||
}
|
||||
alphaComponent += TIME_INTERVAL_OPACITY_ADD;
|
||||
let position = x | 0;
|
||||
let alphaComponent = opacities[i % opacities.length];
|
||||
|
||||
view32bit[position] = (alphaComponent << 24) | (b << 16) | (g << 8) | r;
|
||||
}
|
||||
|
||||
// Flush the image data and cache the waterfall background.
|
||||
@ -116,31 +116,30 @@ exports.drawGraphElementBackground = drawGraphElementBackground;
|
||||
|
||||
/**
|
||||
* Find the optimal interval between time graduations in the animation timeline
|
||||
* graph based on a time scale and a minimum spacing.
|
||||
* @param {Number} timeScale How many px is 1ms in the graph.
|
||||
* @param {Number} minSpacing The minimum spacing between 2 graduations,
|
||||
* defaults to TIME_GRADUATION_MIN_SPACING.
|
||||
* @return {Number} The optimal interval, in pixels.
|
||||
* graph based on a minimum time interval
|
||||
* @param {Number} minTimeInterval Minimum time in ms in one interval
|
||||
* @return {Number} The optimal interval time in ms
|
||||
*/
|
||||
function findOptimalTimeInterval(timeScale,
|
||||
minSpacing=TIME_GRADUATION_MIN_SPACING) {
|
||||
let timingStep = TIME_INTERVAL_MULTIPLE;
|
||||
function findOptimalTimeInterval(minTimeInterval) {
|
||||
let numIters = 0;
|
||||
let multiplier = 1;
|
||||
|
||||
if (timeScale > minSpacing) {
|
||||
return timeScale;
|
||||
if (!minTimeInterval) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let interval;
|
||||
while (true) {
|
||||
let scaledStep = timeScale * timingStep;
|
||||
for (let i = 0; i < OPTIMAL_TIME_INTERVAL_MULTIPLES.length; i++) {
|
||||
interval = OPTIMAL_TIME_INTERVAL_MULTIPLES[i] * multiplier;
|
||||
if (minTimeInterval <= interval) {
|
||||
return interval;
|
||||
}
|
||||
}
|
||||
if (++numIters > OPTIMAL_TIME_INTERVAL_MAX_ITERS) {
|
||||
return scaledStep;
|
||||
return interval;
|
||||
}
|
||||
if (scaledStep < minSpacing) {
|
||||
timingStep *= 2;
|
||||
continue;
|
||||
}
|
||||
return scaledStep;
|
||||
multiplier *= 10;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["StyleEditorUI"];
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
@ -18,11 +17,13 @@ const {OS} = Cu.import("resource://gre/modules/osfile.jsm", {});
|
||||
const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const {gDevTools} = require("resource://devtools/client/framework/gDevTools.jsm");
|
||||
/* import-globals-from StyleEditorUtil.jsm */
|
||||
Cu.import("resource://devtools/client/styleeditor/StyleEditorUtil.jsm");
|
||||
const {SplitView} = Cu.import("resource://devtools/client/shared/SplitView.jsm", {});
|
||||
const {StyleSheetEditor} = Cu.import("resource://devtools/client/styleeditor/StyleSheetEditor.jsm");
|
||||
loader.lazyImporter(this, "PluralForm", "resource://gre/modules/PluralForm.jsm");
|
||||
const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/client/styleeditor/utils");
|
||||
const {PrefObserver, PREF_ORIG_SOURCES} =
|
||||
require("devtools/client/styleeditor/utils");
|
||||
const csscoverage = require("devtools/server/actors/csscoverage");
|
||||
const {console} = require("resource://gre/modules/Console.jsm");
|
||||
const promise = require("promise");
|
||||
@ -173,7 +174,8 @@ StyleEditorUI.prototype = {
|
||||
this._contextMenu.addEventListener("popupshowing",
|
||||
this._updateOpenLinkItem);
|
||||
|
||||
this._optionsMenu = this._panelDoc.getElementById("style-editor-options-popup");
|
||||
this._optionsMenu =
|
||||
this._panelDoc.getElementById("style-editor-options-popup");
|
||||
this._optionsMenu.addEventListener("popupshowing",
|
||||
this._onOptionsPopupShowing);
|
||||
this._optionsMenu.addEventListener("popuphiding",
|
||||
@ -187,7 +189,8 @@ StyleEditorUI.prototype = {
|
||||
this._mediaItem.addEventListener("command",
|
||||
this._toggleMediaSidebar);
|
||||
|
||||
this._openLinkNewTabItem = this._panelDoc.getElementById("context-openlinknewtab");
|
||||
this._openLinkNewTabItem =
|
||||
this._panelDoc.getElementById("context-openlinknewtab");
|
||||
this._openLinkNewTabItem.addEventListener("command",
|
||||
this._openLinkNewTab);
|
||||
|
||||
@ -358,13 +361,13 @@ StyleEditorUI.prototype = {
|
||||
* Optional parent window for the file picker.
|
||||
*/
|
||||
_importFromFile: function(file, parentWindow) {
|
||||
let onFileSelected = (file) => {
|
||||
if (!file) {
|
||||
let onFileSelected = (selectedFile) => {
|
||||
if (!selectedFile) {
|
||||
// nothing selected
|
||||
return;
|
||||
}
|
||||
NetUtil.asyncFetch({
|
||||
uri: NetUtil.newURI(file),
|
||||
uri: NetUtil.newURI(selectedFile),
|
||||
loadingNode: this._window.document,
|
||||
contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER
|
||||
}, (stream, status) => {
|
||||
@ -372,11 +375,12 @@ StyleEditorUI.prototype = {
|
||||
this.emit("error", { key: LOAD_ERROR });
|
||||
return;
|
||||
}
|
||||
let source = NetUtil.readInputStreamToString(stream, stream.available());
|
||||
let source =
|
||||
NetUtil.readInputStreamToString(stream, stream.available());
|
||||
stream.close();
|
||||
|
||||
this._debuggee.addStyleSheet(source).then((styleSheet) => {
|
||||
this._onStyleSheetCreated(styleSheet, file);
|
||||
this._onStyleSheetCreated(styleSheet, selectedFile);
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -384,7 +388,6 @@ StyleEditorUI.prototype = {
|
||||
showFilePicker(file, false, parentWindow, onFileSelected);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* When a new or imported stylesheet has been added to the document.
|
||||
* Add an editor for it.
|
||||
@ -429,16 +432,22 @@ StyleEditorUI.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* This method handles the following cases related to the context menu item "_openLinkNewTabItem":
|
||||
* This method handles the following cases related to the context
|
||||
* menu item "_openLinkNewTabItem":
|
||||
*
|
||||
* 1) There was a stylesheet clicked on and it is external: show and enable the context menu item
|
||||
* 2) There was a stylesheet clicked on and it is inline: show and disable the context menu item
|
||||
* 3) There was no stylesheet clicked on (the right click happened below the list): hide the context menu
|
||||
* 1) There was a stylesheet clicked on and it is external: show and
|
||||
* enable the context menu item
|
||||
* 2) There was a stylesheet clicked on and it is inline: show and
|
||||
* disable the context menu item
|
||||
* 3) There was no stylesheet clicked on (the right click happened
|
||||
* below the list): hide the context menu
|
||||
*/
|
||||
_updateOpenLinkItem: function() {
|
||||
this._openLinkNewTabItem.setAttribute("hidden", !this._contextMenuStyleSheet);
|
||||
this._openLinkNewTabItem.setAttribute("hidden",
|
||||
!this._contextMenuStyleSheet);
|
||||
if (this._contextMenuStyleSheet) {
|
||||
this._openLinkNewTabItem.setAttribute("disabled", !this._contextMenuStyleSheet.href);
|
||||
this._openLinkNewTabItem.setAttribute("disabled",
|
||||
!this._contextMenuStyleSheet.href);
|
||||
}
|
||||
},
|
||||
|
||||
@ -460,15 +469,14 @@ StyleEditorUI.prototype = {
|
||||
_removeStyleSheetEditor: function(editor) {
|
||||
if (editor.summary) {
|
||||
this._view.removeItem(editor.summary);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
let self = this;
|
||||
this.on("editor-added", function onAdd(event, added) {
|
||||
if (editor == added) {
|
||||
self.off("editor-added", onAdd);
|
||||
self._view.removeItem(editor.summary);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
editor.destroy();
|
||||
@ -503,21 +511,21 @@ StyleEditorUI.prototype = {
|
||||
disableAnimations: this._alwaysDisableAnimations,
|
||||
ordinal: ordinal,
|
||||
onCreate: function(summary, details, data) {
|
||||
let editor = data.editor;
|
||||
editor.summary = summary;
|
||||
editor.details = details;
|
||||
let createdEditor = data.editor;
|
||||
createdEditor.summary = summary;
|
||||
createdEditor.details = details;
|
||||
|
||||
wire(summary, ".stylesheet-enabled", function onToggleDisabled(event) {
|
||||
event.stopPropagation();
|
||||
event.target.blur();
|
||||
|
||||
editor.toggleDisabled();
|
||||
createdEditor.toggleDisabled();
|
||||
});
|
||||
|
||||
wire(summary, ".stylesheet-name", {
|
||||
events: {
|
||||
"keypress": (aEvent) => {
|
||||
if (aEvent.keyCode == aEvent.DOM_VK_RETURN) {
|
||||
"keypress": (event) => {
|
||||
if (event.keyCode == event.DOM_VK_RETURN) {
|
||||
this._view.activeSummary = summary;
|
||||
}
|
||||
}
|
||||
@ -528,13 +536,13 @@ StyleEditorUI.prototype = {
|
||||
event.stopPropagation();
|
||||
event.target.blur();
|
||||
|
||||
editor.saveToFile(editor.savedFile);
|
||||
createdEditor.saveToFile(createdEditor.savedFile);
|
||||
});
|
||||
|
||||
this._updateSummaryForEditor(editor, summary);
|
||||
this._updateSummaryForEditor(createdEditor, summary);
|
||||
|
||||
summary.addEventListener("contextmenu", (event) => {
|
||||
this._contextMenuStyleSheet = editor.styleSheet;
|
||||
summary.addEventListener("contextmenu", () => {
|
||||
this._contextMenuStyleSheet = createdEditor.styleSheet;
|
||||
}, false);
|
||||
|
||||
summary.addEventListener("focus", function onSummaryFocus(event) {
|
||||
@ -554,44 +562,46 @@ StyleEditorUI.prototype = {
|
||||
Services.prefs.setIntPref(PREF_SIDEBAR_WIDTH, sidebarWidth);
|
||||
|
||||
// update all @media sidebars for consistency
|
||||
let sidebars = [...this._panelDoc.querySelectorAll(".stylesheet-sidebar")];
|
||||
let sidebars =
|
||||
[...this._panelDoc.querySelectorAll(".stylesheet-sidebar")];
|
||||
for (let mediaSidebar of sidebars) {
|
||||
mediaSidebar.setAttribute("width", sidebarWidth);
|
||||
}
|
||||
});
|
||||
|
||||
// autofocus if it's a new user-created stylesheet
|
||||
if (editor.isNew) {
|
||||
this._selectEditor(editor);
|
||||
if (createdEditor.isNew) {
|
||||
this._selectEditor(createdEditor);
|
||||
}
|
||||
|
||||
if (this._isEditorToSelect(editor)) {
|
||||
if (this._isEditorToSelect(createdEditor)) {
|
||||
this.switchToSelectedSheet();
|
||||
}
|
||||
|
||||
// If this is the first stylesheet and there is no pending request to
|
||||
// select a particular style sheet, select this sheet.
|
||||
if (!this.selectedEditor && !this._styleSheetBoundToSelect
|
||||
&& editor.styleSheet.styleSheetIndex == 0) {
|
||||
this._selectEditor(editor);
|
||||
&& createdEditor.styleSheet.styleSheetIndex == 0) {
|
||||
this._selectEditor(createdEditor);
|
||||
}
|
||||
this.emit("editor-added", editor);
|
||||
this.emit("editor-added", createdEditor);
|
||||
}.bind(this),
|
||||
|
||||
onShow: function(summary, details, data) {
|
||||
let editor = data.editor;
|
||||
this.selectedEditor = editor;
|
||||
let showEditor = data.editor;
|
||||
this.selectedEditor = showEditor;
|
||||
|
||||
Task.spawn(function* () {
|
||||
if (!editor.sourceEditor) {
|
||||
if (!showEditor.sourceEditor) {
|
||||
// only initialize source editor when we switch to this view
|
||||
let inputElement = details.querySelector(".stylesheet-editor-input");
|
||||
yield editor.load(inputElement);
|
||||
let inputElement =
|
||||
details.querySelector(".stylesheet-editor-input");
|
||||
yield showEditor.load(inputElement);
|
||||
}
|
||||
|
||||
editor.onShow();
|
||||
showEditor.onShow();
|
||||
|
||||
this.emit("editor-selected", editor);
|
||||
this.emit("editor-selected", showEditor);
|
||||
|
||||
// Is there any CSS coverage markup to include?
|
||||
let usage = yield csscoverage.getUsage(this._target);
|
||||
@ -599,21 +609,20 @@ StyleEditorUI.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
let href = csscoverage.sheetToUrl(editor.styleSheet);
|
||||
let data = yield usage.createEditorReport(href)
|
||||
let href = csscoverage.sheetToUrl(showEditor.styleSheet);
|
||||
let reportData = yield usage.createEditorReport(href);
|
||||
|
||||
editor.removeAllUnusedRegions();
|
||||
showEditor.removeAllUnusedRegions();
|
||||
|
||||
if (data.reports.length > 0) {
|
||||
if (reportData.reports.length > 0) {
|
||||
// Only apply if this file isn't compressed. We detect a
|
||||
// compressed file if there are more rules than lines.
|
||||
let text = editor.sourceEditor.getText();
|
||||
let text = showEditor.sourceEditor.getText();
|
||||
let lineCount = text.split("\n").length;
|
||||
let ruleCount = editor.styleSheet.ruleCount;
|
||||
let ruleCount = showEditor.styleSheet.ruleCount;
|
||||
if (lineCount >= ruleCount) {
|
||||
editor.addUnusedRegions(data.reports);
|
||||
}
|
||||
else {
|
||||
showEditor.addUnusedRegions(reportData.reports);
|
||||
} else {
|
||||
this.emit("error", { key: "error-compressed", level: "info" });
|
||||
}
|
||||
}
|
||||
@ -733,13 +742,14 @@ StyleEditorUI.prototype = {
|
||||
/**
|
||||
* Returns an identifier for the given style sheet.
|
||||
*
|
||||
* @param {StyleSheet} aStyleSheet
|
||||
* @param {StyleSheet} styleSheet
|
||||
* The style sheet to be identified.
|
||||
*/
|
||||
getStyleSheetIdentifier: function (aStyleSheet) {
|
||||
// Identify inline style sheets by their host page URI and index at the page.
|
||||
return aStyleSheet.href ? aStyleSheet.href :
|
||||
"inline-" + aStyleSheet.styleSheetIndex + "-at-" + aStyleSheet.nodeHref;
|
||||
getStyleSheetIdentifier: function(styleSheet) {
|
||||
// Identify inline style sheets by their host page URI and index
|
||||
// at the page.
|
||||
return styleSheet.href ? styleSheet.href :
|
||||
"inline-" + styleSheet.styleSheetIndex + "-at-" + styleSheet.nodeHref;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -767,7 +777,6 @@ StyleEditorUI.prototype = {
|
||||
return this.switchToSelectedSheet();
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Handler for an editor's 'property-changed' event.
|
||||
* Update the summary in the UI.
|
||||
@ -784,8 +793,8 @@ StyleEditorUI.prototype = {
|
||||
*
|
||||
* @param {StyleSheetEditor} editor
|
||||
* @param {DOMElement} summary
|
||||
* Optional item's summary element to update. If none, item corresponding
|
||||
* to passed editor is used.
|
||||
* Optional item's summary element to update. If none, item
|
||||
* corresponding to passed editor is used.
|
||||
*/
|
||||
_updateSummaryForEditor: function(editor, summary) {
|
||||
summary = summary || editor.summary;
|
||||
@ -801,7 +810,7 @@ StyleEditorUI.prototype = {
|
||||
ruleCount = "-";
|
||||
}
|
||||
|
||||
var flags = [];
|
||||
let flags = [];
|
||||
if (editor.styleSheet.disabled) {
|
||||
flags.push("disabled");
|
||||
}
|
||||
@ -873,7 +882,8 @@ StyleEditorUI.prototype = {
|
||||
|
||||
let div = this._panelDoc.createElement("div");
|
||||
div.className = "media-rule-label";
|
||||
div.addEventListener("click", this._jumpToLocation.bind(this, location));
|
||||
div.addEventListener("click",
|
||||
this._jumpToLocation.bind(this, location));
|
||||
|
||||
let cond = this._panelDoc.createElement("div");
|
||||
cond.textContent = rule.conditionText;
|
||||
@ -882,8 +892,13 @@ StyleEditorUI.prototype = {
|
||||
cond.classList.add("media-condition-unmatched");
|
||||
}
|
||||
if (this._target.tab.tagName == "tab") {
|
||||
cond.innerHTML = cond.textContent.replace(/(min\-|max\-)(width|height):\s\d+(px)/ig, "<a href='#' class='media-responsive-mode-toggle'>$&</a>");
|
||||
cond.addEventListener("click", this._onMediaConditionClick.bind(this));
|
||||
const minMaxPattern = /(min\-|max\-)(width|height):\s\d+(px)/ig;
|
||||
const replacement =
|
||||
"<a href='#' class='media-responsive-mode-toggle'>$&</a>";
|
||||
|
||||
cond.innerHTML = cond.textContent.replace(minMaxPattern, replacement);
|
||||
cond.addEventListener("click",
|
||||
this._onMediaConditionClick.bind(this));
|
||||
}
|
||||
div.appendChild(cond);
|
||||
|
||||
@ -916,7 +931,7 @@ StyleEditorUI.prototype = {
|
||||
}
|
||||
let conditionText = e.target.textContent;
|
||||
let isWidthCond = conditionText.toLowerCase().indexOf("width") > -1;
|
||||
let mediaVal = parseInt(/\d+/.exec(conditionText));
|
||||
let mediaVal = parseInt(/\d+/.exec(conditionText), 10);
|
||||
|
||||
let options = isWidthCond ? {width: mediaVal} : {height: mediaVal};
|
||||
this._launchResponsiveMode(options);
|
||||
@ -936,12 +951,11 @@ StyleEditorUI.prototype = {
|
||||
|
||||
ResponsiveUIManager.runIfNeeded(win, tab);
|
||||
if (options.width && options.height) {
|
||||
ResponsiveUIManager.getResponsiveUIForTab(tab).setSize(options.width, options.height);
|
||||
}
|
||||
else if (options.width) {
|
||||
ResponsiveUIManager.getResponsiveUIForTab(tab).setSize(options.width,
|
||||
options.height);
|
||||
} else if (options.width) {
|
||||
ResponsiveUIManager.getResponsiveUIForTab(tab).setWidth(options.width);
|
||||
}
|
||||
else if (options.height) {
|
||||
} else if (options.height) {
|
||||
ResponsiveUIManager.getResponsiveUIForTab(tab).setHeight(options.height);
|
||||
}
|
||||
},
|
||||
@ -978,4 +992,4 @@ StyleEditorUI.prototype = {
|
||||
this._prefObserver.off(PREF_MEDIA_SIDEBAR, this._onMediaPrefChanged);
|
||||
this._prefObserver.destroy();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -3,6 +3,9 @@
|
||||
* 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/. */
|
||||
|
||||
/* All top-level definitions here are exports. */
|
||||
/* eslint no-unused-vars: [2, {"vars": "local"}] */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
@ -26,87 +29,82 @@ const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const console = require("resource://gre/modules/Console.jsm").console;
|
||||
const gStringBundle = Services.strings.createBundle(PROPERTIES_URL);
|
||||
|
||||
|
||||
/**
|
||||
* Returns a localized string with the given key name from the string bundle.
|
||||
*
|
||||
* @param aName
|
||||
* @param name
|
||||
* @param ...rest
|
||||
* Optional arguments to format in the string.
|
||||
* @return string
|
||||
*/
|
||||
this._ = function _(aName)
|
||||
{
|
||||
function _(name) {
|
||||
try {
|
||||
if (arguments.length == 1) {
|
||||
return gStringBundle.GetStringFromName(aName);
|
||||
return gStringBundle.GetStringFromName(name);
|
||||
}
|
||||
let rest = Array.prototype.slice.call(arguments, 1);
|
||||
return gStringBundle.formatStringFromName(aName, rest, rest.length);
|
||||
}
|
||||
catch (ex) {
|
||||
return gStringBundle.formatStringFromName(name, rest, rest.length);
|
||||
} catch (ex) {
|
||||
console.error(ex);
|
||||
throw new Error("L10N error. '" + aName + "' is missing from " + PROPERTIES_URL);
|
||||
throw new Error("L10N error. '" + name + "' is missing from " +
|
||||
PROPERTIES_URL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert an expression is true or throw if false.
|
||||
*
|
||||
* @param aExpression
|
||||
* @param aMessage
|
||||
* @param expression
|
||||
* @param message
|
||||
* Optional message.
|
||||
* @return aExpression
|
||||
* @return expression
|
||||
*/
|
||||
this.assert = function assert(aExpression, aMessage)
|
||||
{
|
||||
if (!!!(aExpression)) {
|
||||
let msg = aMessage ? "ASSERTION FAILURE:" + aMessage : "ASSERTION FAILURE";
|
||||
function assert(expression, message) {
|
||||
if (!expression) {
|
||||
let msg = message ? "ASSERTION FAILURE:" + message : "ASSERTION FAILURE";
|
||||
log(msg);
|
||||
throw new Error(msg);
|
||||
}
|
||||
return aExpression;
|
||||
return expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve or set the text content of an element.
|
||||
*
|
||||
* @param DOMElement aRoot
|
||||
* @param DOMElement root
|
||||
* The element to use for querySelector.
|
||||
* @param string aSelector
|
||||
* @param string selector
|
||||
* Selector string for the element to get/set the text content.
|
||||
* @param string aText
|
||||
* @param string textContent
|
||||
* Optional text to set.
|
||||
* @return string
|
||||
* Text content of matching element or null if there were no element
|
||||
* matching aSelector.
|
||||
* matching selector.
|
||||
*/
|
||||
this.text = function text(aRoot, aSelector, aText)
|
||||
{
|
||||
let element = aRoot.querySelector(aSelector);
|
||||
function text(root, selector, textContent) {
|
||||
let element = root.querySelector(selector);
|
||||
if (!element) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (aText === undefined) {
|
||||
if (textContent === undefined) {
|
||||
return element.textContent;
|
||||
}
|
||||
element.textContent = aText;
|
||||
return aText;
|
||||
element.textContent = textContent;
|
||||
return textContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates _own_ properties of an object.
|
||||
*
|
||||
* @param aObject
|
||||
* @param object
|
||||
* The object to iterate.
|
||||
* @param function aCallback(aKey, aValue)
|
||||
* @param function callback(aKey, aValue)
|
||||
*/
|
||||
function forEach(aObject, aCallback)
|
||||
{
|
||||
for (let key in aObject) {
|
||||
if (aObject.hasOwnProperty(key)) {
|
||||
aCallback(key, aObject[key]);
|
||||
function forEach(object, callback) {
|
||||
for (let key in object) {
|
||||
if (object.hasOwnProperty(key)) {
|
||||
callback(key, object[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -116,52 +114,54 @@ function forEach(aObject, aCallback)
|
||||
*
|
||||
* @param ...rest
|
||||
* One or multiple arguments to log.
|
||||
* If multiple arguments are given, they will be joined by " " in the log.
|
||||
* If multiple arguments are given, they will be joined by " "
|
||||
* in the log.
|
||||
*/
|
||||
this.log = function log()
|
||||
{
|
||||
function log() {
|
||||
console.logStringMessage(Array.prototype.slice.call(arguments).join(" "));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wire up element(s) matching selector with attributes, event listeners, etc.
|
||||
*
|
||||
* @param DOMElement aRoot
|
||||
* @param DOMElement root
|
||||
* The element to use for querySelectorAll.
|
||||
* Can be null if aSelector is a DOMElement.
|
||||
* @param string|DOMElement aSelectorOrElement
|
||||
* Can be null if selector is a DOMElement.
|
||||
* @param string|DOMElement selectorOrElement
|
||||
* Selector string or DOMElement for the element(s) to wire up.
|
||||
* @param object aDescriptor
|
||||
* An object describing how to wire matching selector, supported properties
|
||||
* are "events" and "attributes" taking objects themselves.
|
||||
* @param object descriptor
|
||||
* An object describing how to wire matching selector,
|
||||
* supported properties are "events" and "attributes" taking
|
||||
* objects themselves.
|
||||
* Each key of properties above represents the name of the event or
|
||||
* attribute, with the value being a function used as an event handler or
|
||||
* string to use as attribute value.
|
||||
* If aDescriptor is a function, the argument is equivalent to :
|
||||
* {events: {'click': aDescriptor}}
|
||||
* If descriptor is a function, the argument is equivalent to :
|
||||
* {events: {'click': descriptor}}
|
||||
*/
|
||||
this.wire = function wire(aRoot, aSelectorOrElement, aDescriptor)
|
||||
{
|
||||
function wire(root, selectorOrElement, descriptor) {
|
||||
let matches;
|
||||
if (typeof(aSelectorOrElement) == "string") { // selector
|
||||
matches = aRoot.querySelectorAll(aSelectorOrElement);
|
||||
if (typeof selectorOrElement == "string") {
|
||||
// selector
|
||||
matches = root.querySelectorAll(selectorOrElement);
|
||||
if (!matches.length) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
matches = [aSelectorOrElement]; // element
|
||||
// element
|
||||
matches = [selectorOrElement];
|
||||
}
|
||||
|
||||
if (typeof(aDescriptor) == "function") {
|
||||
aDescriptor = {events: {click: aDescriptor}};
|
||||
if (typeof descriptor == "function") {
|
||||
descriptor = {events: {click: descriptor}};
|
||||
}
|
||||
|
||||
for (let i = 0; i < matches.length; i++) {
|
||||
let element = matches[i];
|
||||
forEach(aDescriptor.events, function (aName, aHandler) {
|
||||
element.addEventListener(aName, aHandler, false);
|
||||
forEach(descriptor.events, function(name, handler) {
|
||||
element.addEventListener(name, handler, false);
|
||||
});
|
||||
forEach(aDescriptor.attributes, element.setAttribute);
|
||||
forEach(descriptor.attributes, element.setAttribute);
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,10 +181,9 @@ this.wire = function wire(aRoot, aSelectorOrElement, aDescriptor)
|
||||
* @param AString suggestedFilename
|
||||
* The suggested filename when toSave is true.
|
||||
*/
|
||||
this.showFilePicker = function showFilePicker(path, toSave, parentWindow,
|
||||
callback, suggestedFilename)
|
||||
{
|
||||
if (typeof(path) == "string") {
|
||||
function showFilePicker(path, toSave, parentWindow, callback,
|
||||
suggestedFilename) {
|
||||
if (typeof path == "string") {
|
||||
try {
|
||||
if (Services.io.extractScheme(path) == "file") {
|
||||
let uri = Services.io.newURI(path, null, null);
|
||||
@ -197,7 +196,8 @@ this.showFilePicker = function showFilePicker(path, toSave, parentWindow,
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
|
||||
let file =
|
||||
Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
|
||||
file.initWithPath(path);
|
||||
callback(file);
|
||||
return;
|
||||
@ -206,7 +206,8 @@ this.showFilePicker = function showFilePicker(path, toSave, parentWindow,
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (path) { // "path" is an nsIFile
|
||||
if (path) {
|
||||
// "path" is an nsIFile
|
||||
callback(path);
|
||||
return;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const Editor = require("devtools/client/sourceeditor/editor");
|
||||
const Editor = require("devtools/client/sourceeditor/editor");
|
||||
const promise = require("promise");
|
||||
const {CssLogic} = require("devtools/shared/inspector/css-logic");
|
||||
const {console} = require("resource://gre/modules/Console.jsm");
|
||||
@ -20,9 +20,10 @@ const {console} = require("resource://gre/modules/Console.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
const { TextDecoder, OS } = Cu.import("resource://gre/modules/osfile.jsm", {});
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://devtools/shared/event-emitter.js");
|
||||
/* import-globals-from StyleEditorUtil.jsm */
|
||||
Cu.import("resource://devtools/client/styleeditor/StyleEditorUtil.jsm");
|
||||
|
||||
const LOAD_ERROR = "error-load";
|
||||
@ -40,10 +41,10 @@ const TRANSITION_PREF = "devtools.styleeditor.transitions";
|
||||
|
||||
// How long to wait to update linked CSS file after original source was saved
|
||||
// to disk. Time in ms.
|
||||
const CHECK_LINKED_SHEET_DELAY=500;
|
||||
const CHECK_LINKED_SHEET_DELAY = 500;
|
||||
|
||||
// How many times to check for linked file changes
|
||||
const MAX_CHECK_COUNT=10;
|
||||
const MAX_CHECK_COUNT = 10;
|
||||
|
||||
// The classname used to show a line that is not used
|
||||
const UNUSED_CLASS = "cm-unused-line";
|
||||
@ -92,7 +93,8 @@ function StyleSheetEditor(styleSheet, win, file, isNew, walker, highlighter) {
|
||||
// event from the StyleSheetActor.
|
||||
this._justSetText = false;
|
||||
|
||||
this._state = { // state to use when inputElement attaches
|
||||
// state to use when inputElement attaches
|
||||
this._state = {
|
||||
text: "",
|
||||
selection: {
|
||||
start: {line: 0, ch: 0},
|
||||
@ -123,7 +125,8 @@ function StyleSheetEditor(styleSheet, win, file, isNew, walker, highlighter) {
|
||||
this.styleSheet.on("error", this._onError);
|
||||
this.mediaRules = [];
|
||||
if (this.cssSheet.getMediaRules) {
|
||||
this.cssSheet.getMediaRules().then(this._onMediaRulesChanged, Cu.reportError);
|
||||
this.cssSheet.getMediaRules().then(this._onMediaRulesChanged,
|
||||
Cu.reportError);
|
||||
}
|
||||
this.cssSheet.on("media-rules-changed", this._onMediaRulesChanged);
|
||||
this.cssSheet.on("style-applied", this._onStyleApplied);
|
||||
@ -195,6 +198,7 @@ StyleSheetEditor.prototype = {
|
||||
try {
|
||||
this._friendlyName = decodeURI(this._friendlyName);
|
||||
} catch (ex) {
|
||||
// Ignore.
|
||||
}
|
||||
}
|
||||
return this._friendlyName;
|
||||
@ -226,13 +230,11 @@ StyleSheetEditor.prototype = {
|
||||
if (uri.scheme == "file") {
|
||||
let file = uri.QueryInterface(Ci.nsIFileURL).file;
|
||||
path = file.path;
|
||||
}
|
||||
else if (this.savedFile) {
|
||||
} else if (this.savedFile) {
|
||||
let origHref = removeQuery(this.styleSheet.href);
|
||||
let origUri = NetUtil.newURI(origHref);
|
||||
path = findLinkedFilePath(uri, origUri, this.savedFile);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// we can't determine path to generated file on disk
|
||||
return;
|
||||
}
|
||||
@ -529,7 +531,8 @@ StyleSheetEditor.prototype = {
|
||||
*/
|
||||
_updateStyleSheet: function() {
|
||||
if (this.styleSheet.disabled) {
|
||||
return; // TODO: do we want to do this?
|
||||
// TODO: do we want to do this?
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._justSetText) {
|
||||
@ -537,10 +540,11 @@ StyleSheetEditor.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
this._updateTask = null; // reset only if we actually perform an update
|
||||
// (stylesheet is enabled) so that 'missed' updates
|
||||
// while the stylesheet is disabled can be performed
|
||||
// when it is enabled back. @see enableStylesheet
|
||||
// reset only if we actually perform an update
|
||||
// (stylesheet is enabled) so that 'missed' updates
|
||||
// while the stylesheet is disabled can be performed
|
||||
// when it is enabled back. @see enableStylesheet
|
||||
this._updateTask = null;
|
||||
|
||||
if (this.sourceEditor) {
|
||||
this._state.text = this.sourceEditor.getText();
|
||||
@ -582,7 +586,8 @@ StyleSheetEditor.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
let node = yield this.walker.getStyleSheetOwnerNode(this.styleSheet.actorID);
|
||||
let node =
|
||||
yield this.walker.getStyleSheetOwnerNode(this.styleSheet.actorID);
|
||||
yield this.highlighter.show(node, {
|
||||
selector: info.selector,
|
||||
hideInfoBar: true,
|
||||
@ -601,7 +606,8 @@ StyleSheetEditor.prototype = {
|
||||
* Optional nsIFile or string representing the filename to save in the
|
||||
* background, no UI will be displayed.
|
||||
* If not specified, the original style sheet URI is used.
|
||||
* To implement 'Save' instead of 'Save as', you can pass savedFile here.
|
||||
* To implement 'Save' instead of 'Save as', you can pass
|
||||
* savedFile here.
|
||||
* @param function(nsIFile aFile) callback
|
||||
* Optional callback called when the operation has finished.
|
||||
* aFile has the nsIFile object for saved file or null if the operation
|
||||
@ -715,7 +721,7 @@ StyleSheetEditor.prototype = {
|
||||
this.emit("linked-css-file-error");
|
||||
|
||||
error += " querying " + this.linkedCSSFile +
|
||||
" original source location: " + this.savedFile.path
|
||||
" original source location: " + this.savedFile.path;
|
||||
Cu.reportError(error);
|
||||
},
|
||||
|
||||
@ -750,7 +756,7 @@ StyleSheetEditor.prototype = {
|
||||
this.saveToFile();
|
||||
};
|
||||
|
||||
bindings["Esc"] = false;
|
||||
bindings.Esc = false;
|
||||
|
||||
return bindings;
|
||||
},
|
||||
|
@ -29,40 +29,40 @@ exports.items = [{
|
||||
description: l10n.lookup("editDesc"),
|
||||
manual: l10n.lookup("editManual2"),
|
||||
params: [
|
||||
{
|
||||
name: 'resource',
|
||||
type: {
|
||||
name: 'resource',
|
||||
include: 'text/css'
|
||||
},
|
||||
description: l10n.lookup("editResourceDesc")
|
||||
},
|
||||
{
|
||||
name: "line",
|
||||
defaultValue: 1,
|
||||
type: {
|
||||
name: "number",
|
||||
min: 1,
|
||||
step: 10
|
||||
},
|
||||
description: l10n.lookup("editLineToJumpToDesc")
|
||||
}
|
||||
],
|
||||
returnType: "editArgs",
|
||||
exec: args => {
|
||||
return { href: args.resource.name, line: args.line };
|
||||
}
|
||||
{
|
||||
name: "resource",
|
||||
type: {
|
||||
name: "resource",
|
||||
include: "text/css"
|
||||
},
|
||||
description: l10n.lookup("editResourceDesc")
|
||||
},
|
||||
{
|
||||
name: "line",
|
||||
defaultValue: 1,
|
||||
type: {
|
||||
name: "number",
|
||||
min: 1,
|
||||
step: 10
|
||||
},
|
||||
description: l10n.lookup("editLineToJumpToDesc")
|
||||
}
|
||||
],
|
||||
returnType: "editArgs",
|
||||
exec: args => {
|
||||
return { href: args.resource.name, line: args.line };
|
||||
}
|
||||
}, {
|
||||
item: "converter",
|
||||
from: "editArgs",
|
||||
to: "dom",
|
||||
exec: function(args, context) {
|
||||
let target = context.environment.target;
|
||||
let gDevTools = require("resource://devtools/client/framework/gDevTools.jsm").gDevTools;
|
||||
return gDevTools.showToolbox(target, "styleeditor").then(function(toolbox) {
|
||||
let styleEditor = toolbox.getCurrentPanel();
|
||||
styleEditor.selectStyleSheet(args.href, args.line);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
exec: function(args, context) {
|
||||
let target = context.environment.target;
|
||||
let gDevTools = require("resource://devtools/client/framework/gDevTools.jsm").gDevTools;
|
||||
return gDevTools.showToolbox(target, "styleeditor").then(function(toolbox) {
|
||||
let styleEditor = toolbox.getCurrentPanel();
|
||||
styleEditor.selectStyleSheet(args.href, args.line);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}];
|
||||
|
@ -4,7 +4,9 @@
|
||||
* 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/. */
|
||||
|
||||
const {Cc, Ci, Cu, Cr} = require("chrome");
|
||||
"use strict";
|
||||
|
||||
const {Cu} = require("chrome");
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
@ -13,6 +15,7 @@ var promise = require("promise");
|
||||
var EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
Cu.import("resource://devtools/client/styleeditor/StyleEditorUI.jsm");
|
||||
/* import-globals-from StyleEditorUtil.jsm */
|
||||
Cu.import("resource://devtools/client/styleeditor/StyleEditorUtil.jsm");
|
||||
|
||||
loader.lazyGetter(this, "StyleSheetsFront",
|
||||
@ -21,7 +24,7 @@ loader.lazyGetter(this, "StyleSheetsFront",
|
||||
loader.lazyGetter(this, "StyleEditorFront",
|
||||
() => require("devtools/server/actors/styleeditor").StyleEditorFront);
|
||||
|
||||
this.StyleEditorPanel = function StyleEditorPanel(panelWin, toolbox) {
|
||||
var StyleEditorPanel = function StyleEditorPanel(panelWin, toolbox) {
|
||||
EventEmitter.decorate(this);
|
||||
|
||||
this._toolbox = toolbox;
|
||||
@ -31,7 +34,7 @@ this.StyleEditorPanel = function StyleEditorPanel(panelWin, toolbox) {
|
||||
|
||||
this.destroy = this.destroy.bind(this);
|
||||
this._showError = this._showError.bind(this);
|
||||
}
|
||||
};
|
||||
|
||||
exports.StyleEditorPanel = StyleEditorPanel;
|
||||
|
||||
@ -57,8 +60,7 @@ StyleEditorPanel.prototype = {
|
||||
|
||||
if (this.target.form.styleSheetsActor) {
|
||||
this._debuggee = StyleSheetsFront(this.target.client, this.target.form);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
/* We're talking to a pre-Firefox 29 server-side */
|
||||
this._debuggee = StyleEditorFront(this.target.client, this.target.form);
|
||||
}
|
||||
@ -94,7 +96,8 @@ StyleEditorPanel.prototype = {
|
||||
}
|
||||
|
||||
let notificationBox = this._toolbox.getNotificationBox();
|
||||
let notification = notificationBox.getNotificationWithValue("styleeditor-error");
|
||||
let notification =
|
||||
notificationBox.getNotificationWithValue("styleeditor-error");
|
||||
let level = (data.level === "info") ?
|
||||
notificationBox.PRIORITY_INFO_LOW :
|
||||
notificationBox.PRIORITY_CRITICAL_LOW;
|
||||
@ -120,7 +123,7 @@ StyleEditorPanel.prototype = {
|
||||
*/
|
||||
selectStyleSheet: function(href, line, col) {
|
||||
if (!this._debuggee || !this.UI) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
return this.UI.selectStyleSheet(href, line - 1, col ? col - 1 : 0);
|
||||
},
|
||||
@ -146,10 +149,10 @@ StyleEditorPanel.prototype = {
|
||||
|
||||
return promise.resolve(null);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyGetter(StyleEditorPanel.prototype, "strings",
|
||||
function () {
|
||||
function() {
|
||||
return Services.strings.createBundle(
|
||||
"chrome://devtools/locale/styleeditor.properties");
|
||||
});
|
||||
|
@ -44,6 +44,7 @@ support-files =
|
||||
sourcemaps-watching.html
|
||||
test_private.css
|
||||
test_private.html
|
||||
doc_frame_script.js
|
||||
doc_long.css
|
||||
doc_uncached.css
|
||||
doc_uncached.html
|
||||
|
@ -5,14 +5,16 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Resources</title>
|
||||
<script type="text/javascript" id="script1">
|
||||
window.addEventListener('load', function() {
|
||||
var pid = document.getElementById('pid');
|
||||
var div = document.createElement('div');
|
||||
div.id = 'divid';
|
||||
div.classList.add('divclass');
|
||||
div.appendChild(document.createTextNode('div'));
|
||||
div.setAttribute('data-a1', 'div');
|
||||
<script type="text/javascript;version=1.8" id="script1">
|
||||
"use strict";
|
||||
|
||||
window.addEventListener("load", function() {
|
||||
let pid = document.getElementById("pid");
|
||||
let div = document.createElement("div");
|
||||
div.id = "divid";
|
||||
div.classList.add("divclass");
|
||||
div.appendChild(document.createTextNode("div"));
|
||||
div.setAttribute("data-a1", "div");
|
||||
pid.parentNode.appendChild(div);
|
||||
});
|
||||
</script>
|
||||
@ -37,12 +39,13 @@
|
||||
<p class="pclass" id="pid" data-a1="p">paragraph</p>
|
||||
|
||||
<script>
|
||||
var pid = document.getElementById('pid');
|
||||
var h4 = document.createElement('h4');
|
||||
h4.id = 'h4id';
|
||||
h4.classList.add('h4class');
|
||||
h4.appendChild(document.createTextNode('h4'));
|
||||
h4.setAttribute('data-a1', 'h4');
|
||||
"use strict";
|
||||
let pid = document.getElementById("pid");
|
||||
let h4 = document.createElement("h4");
|
||||
h4.id = "h4id";
|
||||
h4.classList.add("h4class");
|
||||
h4.appendChild(document.createTextNode("h4"));
|
||||
h4.setAttribute("data-a1", "h4");
|
||||
pid.parentNode.appendChild(h4);
|
||||
</script>
|
||||
|
||||
|
@ -5,6 +5,12 @@
|
||||
|
||||
// Tests that the edit command works
|
||||
|
||||
// Import the GCLI test helper
|
||||
/* import-globals-from ../../commandline/test/helpers.js */
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/commandline/test/helpers.js",
|
||||
this);
|
||||
|
||||
const TEST_URI = "http://example.com/browser/devtools/client/styleeditor/" +
|
||||
"test/browser_styleeditor_cmd_edit.html";
|
||||
|
||||
@ -82,9 +88,9 @@ add_task(function* () {
|
||||
resource: {
|
||||
arg: " page1",
|
||||
status: "INCOMPLETE",
|
||||
message: 'Value required for \'resource\'.'
|
||||
message: "Value required for \'resource\'."
|
||||
},
|
||||
line: { status: 'VALID' },
|
||||
line: { status: "VALID" },
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -10,7 +10,8 @@ const MEDIA_PREF = "devtools.styleeditor.showMediaSidebar";
|
||||
|
||||
const RESIZE = 300;
|
||||
const LABELS = ["not all", "all", "(max-width: 400px)",
|
||||
"(min-height: 200px) and (max-height: 250px)", "(max-width: 600px)"];
|
||||
"(min-height: 200px) and (max-height: 250px)",
|
||||
"(max-width: 600px)"];
|
||||
const LINE_NOS = [1, 7, 19, 25, 30];
|
||||
const NEW_RULE = "\n@media (max-width: 600px) { div { color: blue; } }";
|
||||
|
||||
|
@ -2,6 +2,8 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/* Tests responsive mode links for
|
||||
* @media sidebar width and height related conditions */
|
||||
|
||||
@ -20,7 +22,7 @@ add_task(function*() {
|
||||
yield testLinkifiedConditions(mediaEditor, gBrowser.selectedTab, ui);
|
||||
});
|
||||
|
||||
function testLinkifiedConditions(editor, tab, ui) {
|
||||
function* testLinkifiedConditions(editor, tab, ui) {
|
||||
let sidebar = editor.details.querySelector(".stylesheet-sidebar");
|
||||
let conditions = sidebar.querySelectorAll(".media-rule-condition");
|
||||
let responsiveModeToggleClass = ".media-responsive-mode-toggle";
|
||||
@ -29,7 +31,7 @@ function testLinkifiedConditions(editor, tab, ui) {
|
||||
ok(!conditions[0].querySelector(responsiveModeToggleClass),
|
||||
"There should be no links in the first media rule.");
|
||||
ok(!conditions[1].querySelector(responsiveModeToggleClass),
|
||||
"There should be no links in the second media rule.")
|
||||
"There should be no links in the second media rule.");
|
||||
ok(conditions[2].querySelector(responsiveModeToggleClass),
|
||||
"There should be 1 responsive mode link in the media rule");
|
||||
is(conditions[3].querySelectorAll(responsiveModeToggleClass).length, 2,
|
||||
|
@ -13,7 +13,7 @@ add_task(function*() {
|
||||
let { panel, ui } = yield openStyleEditorForURL(TESTCASE_URI);
|
||||
|
||||
let editor = yield createNew(ui, panel.panelWindow);
|
||||
testInitialState(editor);
|
||||
yield testInitialState(editor);
|
||||
|
||||
let originalHref = editor.styleSheet.href;
|
||||
let waitForPropertyChange = onPropertyChange(editor);
|
||||
@ -45,14 +45,14 @@ function createNew(ui, panelWindow) {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function onPropertyChange(aEditor) {
|
||||
function onPropertyChange(editor) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
aEditor.styleSheet.on("property-change", function onProp(property) {
|
||||
editor.styleSheet.on("property-change", function onProp(property) {
|
||||
// wait for text to be entered fully
|
||||
let text = aEditor.sourceEditor.getText();
|
||||
let text = editor.sourceEditor.getText();
|
||||
if (property == "ruleCount" && text == TESTCASE_CSS_SOURCE + "}") {
|
||||
aEditor.styleSheet.off("property-change", onProp);
|
||||
editor.styleSheet.off("property-change", onProp);
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
@ -60,33 +60,33 @@ function onPropertyChange(aEditor) {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function testInitialState(aEditor) {
|
||||
function* testInitialState(editor) {
|
||||
info("Testing the initial state of the new editor");
|
||||
|
||||
let summary = aEditor.summary;
|
||||
let summary = editor.summary;
|
||||
|
||||
ok(aEditor.sourceLoaded, "new editor is loaded when attached");
|
||||
ok(aEditor.isNew, "new editor has isNew flag");
|
||||
ok(editor.sourceLoaded, "new editor is loaded when attached");
|
||||
ok(editor.isNew, "new editor has isNew flag");
|
||||
|
||||
ok(aEditor.sourceEditor.hasFocus(), "new editor has focus");
|
||||
ok(editor.sourceEditor.hasFocus(), "new editor has focus");
|
||||
|
||||
summary = aEditor.summary;
|
||||
summary = editor.summary;
|
||||
let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
|
||||
is(parseInt(ruleCount, 10), 0, "new editor initially shows 0 rules");
|
||||
|
||||
let computedStyle = content.getComputedStyle(content.document.body, null);
|
||||
is(computedStyle.backgroundColor, "rgb(255, 255, 255)",
|
||||
let color = yield getComputedStyleProperty("body", null, "background-color");
|
||||
is(color, "rgb(255, 255, 255)",
|
||||
"content's background color is initially white");
|
||||
}
|
||||
|
||||
function typeInEditor(aEditor, panelWindow) {
|
||||
function typeInEditor(editor, panelWindow) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
waitForFocus(function() {
|
||||
for (let c of TESTCASE_CSS_SOURCE) {
|
||||
EventUtils.synthesizeKey(c, {}, panelWindow);
|
||||
}
|
||||
ok(aEditor.unsaved, "new editor has unsaved flag");
|
||||
ok(editor.unsaved, "new editor has unsaved flag");
|
||||
|
||||
deferred.resolve();
|
||||
}, panelWindow);
|
||||
@ -94,17 +94,17 @@ function typeInEditor(aEditor, panelWindow) {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function testUpdated(aEditor, originalHref) {
|
||||
function testUpdated(editor, originalHref) {
|
||||
info("Testing the state of the new editor after editing it");
|
||||
|
||||
is(aEditor.sourceEditor.getText(), TESTCASE_CSS_SOURCE + "}",
|
||||
is(editor.sourceEditor.getText(), TESTCASE_CSS_SOURCE + "}",
|
||||
"rule bracket has been auto-closed");
|
||||
|
||||
let ruleCount = aEditor.summary.querySelector(".stylesheet-rule-count")
|
||||
let ruleCount = editor.summary.querySelector(".stylesheet-rule-count")
|
||||
.textContent;
|
||||
is(parseInt(ruleCount, 10), 1,
|
||||
"new editor shows 1 rule after modification");
|
||||
|
||||
is(aEditor.styleSheet.href, originalHref,
|
||||
is(editor.styleSheet.href, originalHref,
|
||||
"style sheet href did not change");
|
||||
}
|
||||
|
@ -53,10 +53,8 @@ add_task(function*() {
|
||||
|
||||
yield editor.getSourceEditor();
|
||||
|
||||
let element = content.document.querySelector("div");
|
||||
let style = content.getComputedStyle(element, null);
|
||||
|
||||
is(style.color, "rgb(255, 0, 102)", "div is red before saving file");
|
||||
let color = yield getComputedStyleProperty("div", null, "color");
|
||||
is(color, "rgb(255, 0, 102)", "div is red before saving file");
|
||||
|
||||
// let styleApplied = promise.defer();
|
||||
let styleApplied = editor.once("style-applied");
|
||||
@ -75,7 +73,8 @@ add_task(function*() {
|
||||
|
||||
yield styleApplied;
|
||||
|
||||
is(style.color, "rgb(0, 0, 255)", "div is blue after saving file");
|
||||
color = yield getComputedStyleProperty("div", null, "color");
|
||||
is(color, "rgb(0, 0, 255)", "div is blue after saving file");
|
||||
});
|
||||
|
||||
function editSCSS(editor) {
|
||||
@ -125,7 +124,6 @@ function copy(srcChromeURL, destFilePath) {
|
||||
function read(srcChromeURL) {
|
||||
let scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
|
||||
.getService(Ci.nsIScriptableInputStream);
|
||||
let principal = Services.scriptSecurityManager.getSystemPrincipal();
|
||||
|
||||
let channel = NetUtil.newChannel({
|
||||
uri: srcChromeURL,
|
||||
@ -144,7 +142,7 @@ function read(srcChromeURL) {
|
||||
return data;
|
||||
}
|
||||
|
||||
function write(aData, aFile) {
|
||||
function write(data, file) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
@ -152,15 +150,15 @@ function write(aData, aFile) {
|
||||
|
||||
converter.charset = "UTF-8";
|
||||
|
||||
let istream = converter.convertToInputStream(aData);
|
||||
let ostream = FileUtils.openSafeFileOutputStream(aFile);
|
||||
let istream = converter.convertToInputStream(data);
|
||||
let ostream = FileUtils.openSafeFileOutputStream(file);
|
||||
|
||||
NetUtil.asyncCopy(istream, ostream, function(status) {
|
||||
if (!Components.isSuccessCode(status)) {
|
||||
info("Coudln't write to " + aFile.path);
|
||||
info("Coudln't write to " + file.path);
|
||||
return;
|
||||
}
|
||||
deferred.resolve(aFile);
|
||||
deferred.resolve(file);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
|
@ -47,14 +47,14 @@ const contents = {
|
||||
"jZjA2O1xuXG4jaGVhZGVyIHtcbiAgY29sb3I6ICRwaW5rO1xufSJdfQ==*/"
|
||||
].join("\n"),
|
||||
"test-stylus.styl": [
|
||||
"paulrougetpink = #f06;",
|
||||
"",
|
||||
"div",
|
||||
" color: paulrougetpink",
|
||||
"",
|
||||
"span",
|
||||
" background-color: #EEE",
|
||||
""
|
||||
"paulrougetpink = #f06;",
|
||||
"",
|
||||
"div",
|
||||
" color: paulrougetpink",
|
||||
"",
|
||||
"span",
|
||||
" background-color: #EEE",
|
||||
""
|
||||
].join("\n"),
|
||||
"test-stylus.css": [
|
||||
"div {",
|
||||
|
@ -6,6 +6,7 @@
|
||||
// Test that changes in the style inspector are synchronized into the
|
||||
// style editor.
|
||||
|
||||
/* import-globals-from ../../inspector/shared/test/head.js */
|
||||
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/inspector/shared/test/head.js", this);
|
||||
|
||||
const TESTCASE_URI = TEST_BASE_HTTP + "sync.html";
|
||||
@ -65,10 +66,6 @@ add_task(function*() {
|
||||
|
||||
// For the time being, the actor does not update the style's owning
|
||||
// node's textContent. See bug 1205380.
|
||||
yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() {
|
||||
let style = content.document.querySelector("style");
|
||||
return style.textContent;
|
||||
}).then((textContent) => {
|
||||
isnot(textContent, expectedText, "changes not written back to style node");
|
||||
});
|
||||
let textContent = yield executeInContent("Test:GetStyleContent", null, null);
|
||||
isnot(textContent, expectedText, "changes not written back to style node");
|
||||
});
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
// Test that adding a new rule is synced to the style editor.
|
||||
|
||||
/* import-globals-from ../../inspector/shared/test/head.js */
|
||||
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/inspector/shared/test/head.js", this);
|
||||
|
||||
const TESTCASE_URI = TEST_BASE_HTTP + "sync.html";
|
||||
|
@ -6,6 +6,7 @@
|
||||
// Test that changes in the style inspector are synchronized into the
|
||||
// style editor.
|
||||
|
||||
/* import-globals-from ../../inspector/shared/test/head.js */
|
||||
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/inspector/shared/test/head.js", this);
|
||||
|
||||
const TESTCASE_URI = TEST_BASE_HTTP + "sync.html";
|
||||
|
@ -6,6 +6,7 @@
|
||||
// Test that changes in the style inspector are synchronized into the
|
||||
// style editor.
|
||||
|
||||
/* import-globals-from ../../inspector/shared/test/head.js */
|
||||
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/inspector/shared/test/head.js", this);
|
||||
|
||||
const TESTCASE_URI = TEST_BASE_HTTP + "sync.html";
|
||||
|
@ -6,6 +6,7 @@
|
||||
// Test that changes in the style editor are synchronized into the
|
||||
// style inspector.
|
||||
|
||||
/* import-globals-from ../../inspector/shared/test/head.js */
|
||||
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/inspector/shared/test/head.js", this);
|
||||
|
||||
const TEST_URI = `
|
||||
@ -35,14 +36,14 @@ add_task(function*() {
|
||||
is(value, "chartreuse", "check that edits were synced to rule view");
|
||||
});
|
||||
|
||||
function typeInEditor(aEditor, panelWindow) {
|
||||
function typeInEditor(editor, panelWindow) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
waitForFocus(function() {
|
||||
for (let c of TESTCASE_CSS_SOURCE) {
|
||||
EventUtils.synthesizeKey(c, {}, panelWindow);
|
||||
}
|
||||
ok(aEditor.unsaved, "new editor has unsaved flag");
|
||||
ok(editor.unsaved, "new editor has unsaved flag");
|
||||
|
||||
deferred.resolve();
|
||||
}, panelWindow);
|
||||
|
@ -24,12 +24,13 @@ add_task(function*() {
|
||||
|
||||
yield styleChanges;
|
||||
|
||||
let sheet = content.document.styleSheets[0];
|
||||
let rules = yield executeInContent("Test:cssRules", {
|
||||
num: 0
|
||||
});
|
||||
|
||||
// Test that we removed the transition rule, but kept the rule we added
|
||||
is(sheet.cssRules.length, 1, "only one rule in stylesheet");
|
||||
is(sheet.cssRules[0].cssText, NEW_RULE,
|
||||
"stylesheet only contains rule we added");
|
||||
is(rules.length, 1, "only one rule in stylesheet");
|
||||
is(rules[0], NEW_RULE, "stylesheet only contains rule we added");
|
||||
});
|
||||
|
||||
/* Helpers */
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
// Test that the style-editor initializes correctly for XUL windows.
|
||||
|
||||
"use strict";
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
const TEST_URL = TEST_BASE + "doc_xulpage.xul";
|
||||
@ -15,5 +17,6 @@ add_task(function*() {
|
||||
let toolbox = yield gDevTools.showToolbox(target, "styleeditor");
|
||||
let panel = toolbox.getCurrentPanel();
|
||||
|
||||
ok(panel, "The style-editor panel did initialize correctly for the XUL window");
|
||||
ok(panel,
|
||||
"The style-editor panel did initialize correctly for the XUL window");
|
||||
});
|
||||
|
37
devtools/client/styleeditor/test/doc_frame_script.js
Normal file
37
devtools/client/styleeditor/test/doc_frame_script.js
Normal file
@ -0,0 +1,37 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/* globals addMessageListener, sendAsyncMessage */
|
||||
|
||||
addMessageListener("Test:cssRules", function(msg) {
|
||||
let {num} = msg.data;
|
||||
let sheet = content.document.styleSheets[num];
|
||||
let result = [];
|
||||
for (let i = 0; i < sheet.cssRules.length; ++i) {
|
||||
result.push(sheet.cssRules[i].cssText);
|
||||
}
|
||||
sendAsyncMessage("Test:cssRules", result);
|
||||
});
|
||||
|
||||
/**
|
||||
* Get the property value from the computed style for an element.
|
||||
* @param {Object} data Expects a data object with the following properties
|
||||
* - {String} selector: The selector used to obtain the element.
|
||||
* - {String} pseudo: pseudo id to query, or null.
|
||||
* - {String} name: name of the property
|
||||
* @return {String} The value, if found, null otherwise
|
||||
*/
|
||||
addMessageListener("Test:GetComputedStylePropertyValue", function(msg) {
|
||||
let {selector, pseudo, name} = msg.data;
|
||||
let element = content.document.querySelector(selector);
|
||||
let style = content.document.defaultView.getComputedStyle(element, pseudo);
|
||||
let value = style.getPropertyValue(name);
|
||||
sendAsyncMessage("Test:GetComputedStylePropertyValue", value);
|
||||
});
|
||||
|
||||
addMessageListener("Test:GetStyleContent", function() {
|
||||
sendAsyncMessage("Test:GetStyleContent",
|
||||
content.document.querySelector("style").textContent);
|
||||
});
|
@ -1,23 +1,28 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const FRAME_SCRIPT_UTILS_URL = "chrome://devtools/content/shared/frame-script-utils.js"
|
||||
const TEST_BASE = "chrome://mochitests/content/browser/devtools/client/styleeditor/test/";
|
||||
const TEST_BASE_HTTP = "http://example.com/browser/devtools/client/styleeditor/test/";
|
||||
const TEST_BASE_HTTPS = "https://example.com/browser/devtools/client/styleeditor/test/";
|
||||
const TEST_HOST = 'mochi.test:8888';
|
||||
/* All top-level definitions here are exports. */
|
||||
/* eslint no-unused-vars: [2, {"vars": "local"}] */
|
||||
|
||||
"use strict";
|
||||
|
||||
const FRAME_SCRIPT_UTILS_URL =
|
||||
"chrome://devtools/content/shared/frame-script-utils.js";
|
||||
const TEST_BASE =
|
||||
"chrome://mochitests/content/browser/devtools/client/styleeditor/test/";
|
||||
const TEST_BASE_HTTP =
|
||||
"http://example.com/browser/devtools/client/styleeditor/test/";
|
||||
const TEST_BASE_HTTPS =
|
||||
"https://example.com/browser/devtools/client/styleeditor/test/";
|
||||
const TEST_HOST = "mochi.test:8888";
|
||||
|
||||
const EDITOR_FRAME_SCRIPT = getRootDirectory(gTestPath) + "doc_frame_script.js";
|
||||
|
||||
var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
var {TargetFactory} = require("devtools/client/framework/target");
|
||||
var {console} = Cu.import("resource://gre/modules/Console.jsm", {});
|
||||
var promise = require("promise");
|
||||
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
|
||||
// Import the GCLI test helper
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/commandline/test/helpers.js",
|
||||
this);
|
||||
|
||||
DevToolsUtils.testing = true;
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
DevToolsUtils.testing = false;
|
||||
@ -61,8 +66,7 @@ function navigateTo(url) {
|
||||
return navigating.promise;
|
||||
}
|
||||
|
||||
function* cleanup()
|
||||
{
|
||||
function* cleanup() {
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
yield gDevTools.closeToolbox(target);
|
||||
@ -92,6 +96,10 @@ var openStyleEditor = Task.async(function*(tab) {
|
||||
*/
|
||||
var openStyleEditorForURL = Task.async(function* (url, win) {
|
||||
let tab = yield addTab(url, win);
|
||||
|
||||
gBrowser.selectedBrowser.messageManager.loadFrameScript(EDITOR_FRAME_SCRIPT,
|
||||
false);
|
||||
|
||||
let result = yield openStyleEditor(tab);
|
||||
result.tab = tab;
|
||||
return result;
|
||||
@ -128,15 +136,15 @@ function loadCommonFrameScript(tab) {
|
||||
* Resolves to the response data if a response is expected, immediately
|
||||
* resolves otherwise
|
||||
*/
|
||||
function executeInContent(name, data={}, objects={}, expectResponse=true) {
|
||||
function executeInContent(name, data = {}, objects = {},
|
||||
expectResponse = true) {
|
||||
let mm = gBrowser.selectedBrowser.messageManager;
|
||||
|
||||
mm.sendAsyncMessage(name, data, objects);
|
||||
if (expectResponse) {
|
||||
return waitForContentMessage(name);
|
||||
} else {
|
||||
return promise.resolve();
|
||||
}
|
||||
return promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -152,9 +160,27 @@ function waitForContentMessage(name) {
|
||||
let def = promise.defer();
|
||||
mm.addMessageListener(name, function onMessage(msg) {
|
||||
mm.removeMessageListener(name, onMessage);
|
||||
def.resolve(msg);
|
||||
def.resolve(msg.data);
|
||||
});
|
||||
return def.promise;
|
||||
}
|
||||
|
||||
registerCleanupFunction(cleanup);
|
||||
|
||||
/**
|
||||
* Send an async message to the frame script and get back the requested
|
||||
* computed style property.
|
||||
*
|
||||
* @param {String} selector
|
||||
* The selector used to obtain the element.
|
||||
* @param {String} pseudo
|
||||
* pseudo id to query, or null.
|
||||
* @param {String} name
|
||||
* name of the property.
|
||||
*/
|
||||
function* getComputedStyleProperty(selector, pseudo, propName) {
|
||||
return yield executeInContent("Test:GetComputedStylePropertyValue",
|
||||
{selector,
|
||||
pseudo,
|
||||
name: propName});
|
||||
}
|
||||
|
@ -19,8 +19,9 @@
|
||||
</head>
|
||||
<body>
|
||||
Time passes:
|
||||
<script>
|
||||
for (i = 0; i < 5000; i++) {
|
||||
<script type="application/javascript;version=1.8">
|
||||
"use strict";
|
||||
for (let i = 0; i < 5000; i++) {
|
||||
document.write("<br>...");
|
||||
}
|
||||
</script>
|
||||
|
@ -4,7 +4,9 @@
|
||||
* 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/. */
|
||||
|
||||
const {Cc, Ci, Cu, Cr} = require("chrome");
|
||||
"use strict";
|
||||
|
||||
const {Cu} = require("chrome");
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://devtools/shared/event-emitter.js");
|
||||
@ -34,7 +36,7 @@ PrefObserver.prototype = {
|
||||
|
||||
destroy: function() {
|
||||
if (this.branch) {
|
||||
this.branch.removeObserver('', this);
|
||||
this.branch.removeObserver("", this);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -8,8 +8,10 @@ package org.mozilla.gecko.dlc.catalog;
|
||||
import android.support.v4.util.AtomicFile;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.background.testhelpers.TestRunner;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
@ -57,7 +59,11 @@ public class TestDownloadContentCatalog {
|
||||
* * Catalog is bootstrapped with items.
|
||||
*/
|
||||
@Test
|
||||
public void testCatalogIsBootstrapedIfFileDoesNotExist() throws Exception {
|
||||
public void testCatalogIsBootstrappedIfFileDoesNotExist() throws Exception {
|
||||
// The catalog is only bootstrapped if fonts are excluded from the build. If this is a build
|
||||
// with fonts included then ignore this test.
|
||||
Assume.assumeTrue("Fonts are excluded from build", AppConstants.MOZ_ANDROID_EXCLUDE_FONTS);
|
||||
|
||||
AtomicFile file = mock(AtomicFile.class);
|
||||
doThrow(FileNotFoundException.class).when(file).readFully();
|
||||
|
||||
|
@ -29,6 +29,9 @@ if so, checks for possible CPOW usage.
|
||||
"Cu.importGlobalProperties". Use of this function is undesirable in
|
||||
some parts of the tree.
|
||||
|
||||
``this-top-level-scope`` This rule treats top-level assignments like
|
||||
``this.mumble = value`` as declaring a global.
|
||||
|
||||
Note: These are string matches so we will miss situations where the parent
|
||||
object is assigned to another variable e.g.::
|
||||
|
||||
@ -73,4 +76,5 @@ Example configuration::
|
||||
no-aArgs
|
||||
no-cpows-in-tests
|
||||
reject-importGlobalProperties
|
||||
this-top-level-scope
|
||||
var-only-at-top-level
|
||||
|
11
testing/eslint-plugin-mozilla/docs/this-top-level-scope.rst
Normal file
11
testing/eslint-plugin-mozilla/docs/this-top-level-scope.rst
Normal file
@ -0,0 +1,11 @@
|
||||
.. _this-top-level-scope:
|
||||
|
||||
====================
|
||||
this-top-level-scope
|
||||
====================
|
||||
|
||||
Rule Details
|
||||
------------
|
||||
|
||||
Treat top-level assignments like ``this.mumble = value`` as declaring
|
||||
a global.
|
@ -26,7 +26,6 @@
|
||||
"is": false,
|
||||
"isnot": false,
|
||||
"ok": false,
|
||||
"promise": false,
|
||||
"registerCleanupFunction": false,
|
||||
"requestLongerTimeout": false,
|
||||
"SimpleTest": false,
|
||||
|
Loading…
Reference in New Issue
Block a user